Files, directories and where things live
The commands in this guide are the ones you will type most and get wrong least - until a filename has a space in it, or a cp -r puts a directory somewhere you did not expect. This covers ls -l field by field, what stat knows that ls does not, the five file types, and the two shell behaviours - globbing and quoting - that make the difference between a command that works and one that half works.
Essential Commands Guide 5 of 38 Beginner
- OSUbuntu 26.04 LTS
- LVM2.03.31 (Ubuntu) / 2.03.36 (AlmaLinux)
- nftables1.1.6 (Ubuntu) / 1.1.5 (AlmaLinux)
- TimeAbout 18 min
GNU coreutils. cp -T and --strip-components are GNU extensions and are what the exam images have. The globbing behaviour differs by SHELL rather than by distribution, and the login shell here is zsh.
- Firewallufw 0.36.2 enabled but reporting inactive / firewalld active
- Network confignetplan + systemd-networkd / NetworkManager 1.56.0
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| LFCS-A01 | 192.168.0.70 | Ubuntu 26.04 LTS | Primary host - most guides run only here | 2 Core | 4 GB | 50 GB |
This guide includes
Use this for the commands you will type most and get wrong quietly. This matters because cp -r changes meaning depending on whether the destination already exists - which is how a script that worked yesterday nests a directory inside itself.
- reading everything
ls -ltells you, and the flags worth adding - finding what
statknows thatlsdoes not, and the five file types - meeting the destination that changes what
cp -rmeans, and using-Twhen a script depends on it - seeing that globbing is the shell's work and the command never sees it
- quoting a filename with a space, and removing things with the two safeties
Before you start
- guide 4 - the shell you are typing into.
-
What ls -l actually tells you
One directory containing a file, two directories, a symlink and a hidden file.
bash Example session mkdir -p ~/ess/files && cd ~/ess/files && printf 'one\ntwo\nthree\n' > report.txt && mkdir -p logs archive && ln -s report.txt current.txt && touch .hidden && lsarchivecurrent.txtlogsreport.txtcd ~/ess/files && ls -ltotal 12drwxrwxr-x 2 sysadmin sysadmin 4096 Aug 29 09:40 archivelrwxrwxrwx 1 sysadmin sysadmin 10 Aug 29 09:40 current.txt -> report.txtdrwxrwxr-x 2 sysadmin sysadmin 4096 Aug 29 09:40 logs-rw-rw-r-- 1 sysadmin sysadmin 14 Aug 29 09:40 report.txtcd ~/ess/files && ls -la --time-style=long-isototal 20drwxrwxr-x 4 sysadmin sysadmin 4096 2026-08-29 09:40 .drwxrwxr-x 3 sysadmin sysadmin 4096 2026-08-29 09:40 ..-rw-rw-r-- 1 sysadmin sysadmin 0 2026-08-29 09:40 .hiddendrwxrwxr-x 2 sysadmin sysadmin 4096 2026-08-29 09:40 archivelrwxrwxrwx 1 sysadmin sysadmin 10 2026-08-29 09:40 current.txt -> report.txtdrwxrwxr-x 2 sysadmin sysadmin 4096 2026-08-29 09:40 logs-rw-rw-r-- 1 sysadmin sysadmin 14 2026-08-29 09:40 report.txtExpected result
lsomitting.hiddenentirely;ls -lshowing the symlink ascurrent.txt -> report.txt;ls -laadding.,..and the hidden file.Success conditionYou can read a directory listing field by field.
-
The other flags worth having
-d,-h,-Fand-i.bash Example session cd ~/ess/files && ls -ldh logs; echo "---"; ls -1F; echo "---"; ls -i report.txtdrwxrwxr-x 2 sysadmin sysadmin 4.0K Aug 29 09:40 logs---archive/current.txt@logs/report.txt---789196 report.txtExpected result
4.0Kfor the directory itself, thenarchive/,current.txt@,logs/with type suffixes, then the inode number.Success conditionYou can list a directory without listing its contents.
-
What stat knows that ls does not
The same file, fully described.
bash Example session cd ~/ess/files && stat report.txt File: report.txt size: 14 Blocks: 8 IO Block: 4096 regular fileDevice: 252,0 Inode: 789196 Links: 1Access: (0664/-rw-rw-r--) Uid: ( 1000/sysadmin) Gid: ( 1000/sysadmin)Access: 2026-08-29 09:40:52.878729254 +0000Modify: 2026-08-29 09:40:52.878729254 +0000Change: 2026-08-29 09:40:52.878729254 +0000 Birth: 2026-08-29 09:40:52.878729254 +0000cd ~/ess/files && stat -c '%A %a %h %U:%G %s %y %n' report.txt logs current.txt-rw-rw-r-- 664 1 sysadmin:sysadmin 14 2026-08-29 09:40:52.878729254 +0000 report.txtdrwxrwxr-x 775 2 sysadmin:sysadmin 4096 2026-08-29 09:40:52.879729251 +0000 logslrwxrwxrwx 777 1 sysadmin:sysadmin 10 2026-08-29 09:40:52.880729247 +0000 current.txtExpected resultInode, blocks, device, the mode as both
0664and-rw-rw-r--, and four timestamps - Access, Modify, Change and Birth.Success conditionYou can get any single fact about a file, scriptably.
-
The five file types
The first character of the mode, and what each one means.
bash Example session cd ~/ess/files && for f in report.txt logs current.txt /dev/null /dev/sda; do printf '%-14s %s\n' "$(basename $f)" "$(stat -c '%F mode=%A' $f)"; donereport.txt regular file mode=-rw-rw-r--logs directory mode=drwxrwxr-xcurrent.txt symbolic link mode=lrwxrwxrwxnull character special file mode=crw-rw-rw-sda block special file mode=brw-rw----mkdir -p ~/ess/files && cd ~/ess/files && printf 'one\ntwo\nthree\n' > report.txt && mkdir -p logs archive && ln -s report.txt current.txt && touch .hidden && lsarchivecurrent.txtlogsreport.txtExpected result
regular file,directory,symbolic link,character special file(/dev/null),block special file(/dev/sda) - andfileidentifying a gzip header and an ELF binary.Success conditionYou can tell what kind of thing you are looking at.
-
cp, and the destination that changes the meaning
The same
cp -rcommand, run against a destination that does and does not already exist.Read the
findoutput each time.bash Example session mkdir -p ~/ess/n/logs && cd ~/ess/n && printf 'x\n' > logs/report.txt && mkdir -p dest && cp -r logs dest && find dest | sortdestdest/logsdest/logs/report.txtcd ~/ess/n && cp -r logs dest && find dest | sort; echo "--- identical: the second copy MERGED into dest/logs, it did not nest"destdest/logsdest/logs/report.txt--- identical: the second copy MERGED into dest/logs, it did not nestcd ~/ess/n && rm -rf dest && cp -r logs dest && find dest | sort; echo "--- dest did NOT exist, so it BECAME the copy of logs"destdest/report.txtdest/second.txt--- dest did NOT exist, so it BECAME the copy of logsExpected resultWith
destexisting:dest/logs/report.txt. Run again: identical - it merged. Withdestabsent:dest/report.txt- dest became the copy.Success conditionYou can predict where a recursive copy lands.
-
So use -T when a script depends on it
cp -Tsays "the destination is the thing itself", whether or not it exists.bash Example session cd ~/ess/n && rm -rf dest && mkdir dest && cp -rT logs dest && find dest | sort; echo "--- cp -T ignores whether dest exists and always means 'dest IS the copy'"destdest/report.txtdest/second.txt--- cp -T ignores whether dest exists and always means 'dest IS the copy'cd ~/ess/n && cp -rT logs dest && find dest | sort; echo "--- so -T is repeatable, which is what you want in a script"destdest/report.txtdest/second.txt--- so -T is repeatable, which is what you want in a scriptExpected result
dest/report.txtboth times - the same result whether it is the first run or the fifth.Success conditionYou have a copy that is safe to repeat.
-
Globbing, which the shell does and the command never sees
The expansion happens before the command runs - and what happens when nothing matches depends on the shell.
zsh Example session cd ~/ess/files && bash -c 'echo *.txt; echo "---"; echo *.nomatch; echo "---"; echo logs/*'current.txt---*.nomatch---logs/renamed.txt logs/report-backup.txt logs/report.txtcd ~/ess/files && bash -c 'ls -d [cm]*; echo "---"; ls -d ??????.bin 2>&1; echo "---"; shopt -s nullglob; echo "with nullglob: [$(echo *.nomatch)]"'current.txtmaybe.gz---random.bin---with nullglob: []cd ~/ess/files && echo *.nomatch 2>&1 | head -2; echo "--- zsh errors where bash passes the pattern through"zsh:2: no matches found: *.nomatch--- zsh errors where bash passes the pattern throughExpected resultbash printing
*.nomatchunchanged when nothing matches;nullglobmaking it empty; and zsh giving **no matches found: *.nomatch**.Success conditionYou know who expands your wildcards.
-
Quoting, and the space in a filename
One file called
two words.txt, and the difference one pair of quotes makes.bash Example session cd ~/ess/files && touch 'two words.txt' && bash -c 'for f in *.txt; do printf "[%s]\n" "$f"; done'[current.txt][two words.txt]cd ~/ess/files && bash -c 'f="two words.txt"; ls -l $f 2>&1 | tail -2; echo "--- quoted:"; ls -l "$f"'ls: cannot access 'two': No such file or directoryls: cannot access 'words.txt': No such file or directory--- quoted:-rw-rw-r-- 1 sysadmin sysadmin 0 Aug 29 09:40 two words.txtcd ~/ess/files && bash -c 'v=world; echo "double: $v"; echo "escaped: \$v"'double: worldescaped: $vExpected resultUnquoted
$fproducing two errors -cannot access 'two'andcannot access 'words.txt'- and the quoted form working.Success conditionYou can handle filenames you did not choose.
-
Removing things, and the two safeties
rmdir,rm -r, and the file whose name looks like a flag.bash Example session cd ~/ess/files && ls; echo "--- rmdir only removes empty:"; rmdir dest 2>&1 | tail -1; rmdir archive/logs-copy 2>&1 | tail -1archivecurrent.txtdestlogsmaybe.gzprograndom.bintwo words.txt--- rmdir only removes empty:rmdir: failed to remove 'dest': Directory not emptyrmdir: failed to remove 'archive/logs-copy': Directory not emptycd ~/ess/files && rm -r dest && echo "dest gone" && ls -d dest 2>&1 | tail -1dest gonels: cannot access 'dest': No such file or directorycd ~/ess/files && touch -- -weird-name && ls -l -- -weird-name && rm -- -weird-name && echo "removed a file whose name starts with a dash"-rw-rw-r-- 1 sysadmin sysadmin 0 Aug 29 09:40 -weird-nameremoved a file whose name starts with a dashExpected result
rmdir: failed to remove 'dest': Directory not emptytwice, thenrm -rworking, then a file called-weird-namecreated and removed.Success conditionYou can remove what you meant to remove.
Troubleshooting
No such file or directoryfor a file you can see inls.Why: The name has a space or a special character and the variable was unquoted.
Fix:Quote it:
"$f".ls -bshows escapes,ls -Qshows names in quotes.cp -rput the directory one level deeper than expected.Why: The destination already existed, so the source went inside it.
Fix:
cp -rT src dest, or remove the destination first.Argument list too long.Why: A glob expanded to more filenames than the kernel allows in one exec.
Fix:
find ... -exec ... +orxargs- see guide 7.A loop over
*.logran once with a literal*.log.Why: bash passes an unmatched pattern through unchanged.
Fix:
shopt -s nullglob, or test the file exists inside the loop.no matches foundand the command did not run at all.Why: That is zsh refusing an unmatched glob.
Fix:Quote the pattern if the command should receive it literally, or run it under
bash -c.rmtreats a filename as an option.Why: The name starts with a dash.
Fix:
rm -- -name, orrm ./-name.duandls -ldisagree about a file's size.Why:
lsreports the logical size andduthe blocks allocated.Fix:Both are right.
statshows size and blocks together.