Linux FilesystemLesson 3 of 8
Files: create, read, copy, move, remove
Create files, read them without an editor, handle many at once with globs, and avoid the ways these commands destroy data.
touchcatlessheadtailwcfilecpmvrm
After this lesson you can
- Create and fill files from the command line, and avoid overwriting by accident.
- Read any file, or any part of a large one, with the right tool.
- Select groups of files with globs and preview the match before acting.
- Copy, move and remove files with the safety options switched on.
Create files
touch creates an empty file, or, if the file exists, updates its modification time:
cd ~/lab
touch notes.txt
ls -l notes.txt
-rw-rw-r-- 1 alex alex 0 Nov 3 10:12 notes.txt
To put text in a file, redirect a command’s output into it. > creates the file, or replaces it if it exists. >> appends:
echo "first" > notes.txt
echo "second" >> notes.txt
cat notes.txt
first
second
echo adds a newline. printf does not, and understands \n, so it is the better tool for multi-line text:
printf 'name=web\nport=8080\nmode=prod\n' > config.ini
cat config.ini
name=web
port=8080
mode=prod
Read files
| Command | Use it to |
|---|---|
cat file | Print a short file |
cat -n file | Print with line numbers |
less file | Page through a long file |
head -n 5 file | First lines |
tail -n 5 file | Last lines |
tail -f file | Follow a file as it grows |
wc -l file | Count lines |
Generate a 1000-line sample and try them:
seq -f "entry %g" 1 1000 > big.log
wc -l big.log
1000 big.log
tail -n 3 big.log
entry 998
entry 999
entry 1000
head and tail combine to cut out any range. Lines 10 to 12 are the last three of the first twelve:
head -n 12 big.log | tail -n 3
entry 10
entry 11
entry 12
| (pipe) sends the output of one command into the next. It is the main way Linux commands work together.
Page with less
less opens a file without loading it all, and lets you move around:
| Key | Action |
|---|---|
Space / b | Page down / up |
/text then n | Search forward, jump to next match |
g / G | Start / end of the file |
q | Quit |
Follow a growing file
tail -f keeps the file open and prints new lines as they are written. It is how you watch a log live. Press Ctrl+C to stop:
tail -f big.log
Count with wc
wc counts lines, words and bytes (-l, -w, -c). Give it a redirected file and it prints only the number:
wc -l < big.log
1000
Ask a file what it is
Names lie. file reads the content:
file /etc/passwd /usr /usr/bin/ls
/etc/passwd: ASCII text
/usr: directory
/usr/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, ...
Run file before you cat something you do not recognise. Printing a binary can garble your terminal; if that happens, type reset.
Copy, move, remove
The same three commands as for directories, with the same traps:
cp config.ini config.ini.bak
mv config.ini.bak config.ini.old
rm config.ini.old
Each has options that turn silent overwriting into a question or a refusal:
| Option | Effect |
|---|---|
-i | Ask before overwriting or removing |
-n | Never overwrite |
-v | Print what was done |
-a (cp) | Copy and keep permissions, owner and times |
The first copy creates config.ini.bak silently. Run the same command again and it asks:
cp -i config.ini config.ini.bak
cp -i config.ini config.ini.bak
cp: overwrite 'config.ini.bak'?
Answer n to keep the existing file.
mv -v shows the rename:
mv -v config.ini.bak config.old
renamed 'config.ini.bak' -> 'config.old'
Compare two files
diff old new prints only the difference. Here is a change to config.ini:
cp -a config.ini config.ini.bak
echo 'debug=true' >> config.ini
diff config.ini.bak config.ini
3a4
> debug=true
Read 3a4 as “after line 3 of the old file, add line 4 of the new”. > marks added lines and < removed ones. diff prints nothing when the files are identical.
Patterns: globs
Before a command runs, the shell expands patterns into matching names. The command never sees the pattern.
| Pattern | Matches |
|---|---|
* | Any run of characters, including none |
? | Exactly one character |
[abc], [0-9] | One character from the set or range |
{a,b} | Not a glob: expands to each word in turn |
touch a1.log a2.log b1.log c.txt notes.md
ls *.log
a1.log a2.log b1.log
ls ?1.log
a1.log b1.log
ls {a,c}*
a1.log a2.log c.txt
A pattern with no match stays as typed, so the command gets the literal text:
echo *.zip
*.zip
Globs do not match names that start with a dot, so rm * leaves hidden files behind. They also do not cross /: * never matches a path separator.
echo *.log
a1.log a2.log b1.log
Only then:
rm *.log
Timestamps
ls -l shows the modification time. Use --time-style=long-iso for an unambiguous format, and touch -t YYYYMMDDhhmm to set one:
touch -t 202401151030 old.txt
ls -l --time-style=long-iso old.txt
-rw-rw-r-- 1 alex alex 0 2024-01-15 10:30 old.txtPractice
0 of 5 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
Create, append, count
In
~/lab, createlist.txtcontaining the linesone,twoandthree(one command). Append the linefour. Then print the file with line numbers and report its line count.Check your result
cat -n ~/lab/list.txt wc -l ~/lab/list.txt1 one 2 two 3 three 4 four 4 /home/alex/lab/list.txtHint
>creates or replaces.>>appends.printfunderstands\n.Solution
printf 'one\ntwo\nthree\n' > ~/lab/list.txt echo four >> ~/lab/list.txt cat -n ~/lab/list.txt wc -l ~/lab/list.txt - Warm-up
Select files with a pattern
Create the five files below. Then list, with one
lseach, (1) only the.logfiles, (2) only the.logfiles whose name is a letteraorbfollowed by one digit.cd ~/lab && touch a1.log a2.log b1.log c.txt notes.mdCheck your result
a1.log a2.log b1.log a1.log a2.log b1.logBoth patterns match the same three files here. Add
a10.logand run the second pattern again: it must not appear.Hint
*matches any run of characters.[ab]matches one of the listed characters and[0-9]one digit.Solution
ls *.log ls [ab][0-9].log - Practice
Back up, change, compare
Create
config.iniwith three lines (name=web,port=8080,mode=prod). Make a backup that keeps the original timestamp, appenddebug=trueto the original, and show exactly what changed with a command that prints only the difference.Check your result
diff config.ini.bak config.ini3a4 > debug=trueHint
cp -apreserves metadata.diff old newshows what the new file added or removed.Solution
cd ~/lab printf 'name=web\nport=8080\nmode=prod\n' > config.ini cp -a config.ini config.ini.bak echo 'debug=true' >> config.ini diff config.ini.bak config.ini - Practice
Ask the file what it is
Use one command on all three paths to learn what each is:
/etc/passwd,/usrand/usr/bin/ls. In one line each, say what kind of thing it is and whether you could read it withcat.Check your result
/etc/passwd: ASCII text /usr: directory /usr/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), ...You can
catthe first. The second is a directory. The third is a compiled program andcatwould print unreadable bytes.Hint
The command is called
file, and it looks at content, not at the name.Solution
file /etc/passwd /usr /usr/bin/ls - Challenge
Triage a big file without an editor
Generate a 1000-line log, then answer four questions about it using only the tools from this lesson: how many lines, what are the last three, what is line 5, and what are lines 10 to 12.
seq -f "entry %g" 1 1000 > ~/lab/big.logCheck your result
Total
1000. Last threeentry 998,entry 999,entry 1000. Line 5 isentry 5. Lines 10 to 12 areentry 10,entry 11,entry 12.Hint
Line 5 is the last line of the first five. Lines 10 to 12 are the last three of the first twelve. Chain two commands with
|.Solution
cd ~/lab wc -l big.log tail -n 3 big.log head -n 5 big.log | tail -n 1 head -n 12 big.log | tail -n 3