On day one you made a lab folder, and ended up with a second copy nested inside the first - no error, no warning. And there is no trash can on a server: a wrong rm is gone for good. This lesson is creating, copying and deleting files without those surprises.
What you need to know already: 1.3 The tree (absolute vs relative paths, ~, ls).
mkdir -p and the relative-path trap
mkdir DIR (make directory) creates one directory. mkdir a/b/c fails if a does not exist yet. mkdir -p a/b/c (-p for parents) creates the whole chain, and does not complain if part of it is already there. That also makes it safe to run twice - the second run changes nothing and still succeeds.
Your day-one bug: you ran mkdir with a relative path while standing in the wrong directory, and got a second oncall-lab/labs nested inside the first. Nothing was broken and nothing warned you; the path was simply relative to where you happened to be.
Two habits that prevent it forever:
pwdbefore any relativemkdir.- Or don't be relative: start the path with
~/or/.
Brace expansion
mkdir -p ~/lab/{systemd,processes,bash}
Before mkdir even starts, the shell rewrites {systemd,processes,bash} into three separate words: ~/lab/systemd ~/lab/processes ~/lab/bash. This is brace expansion. mkdir never sees a brace. Prove it with echo, which just prints its arguments back:
$ echo ~/lab/{a,b,c}
/home/learner/lab/a /home/learner/lab/b /home/learner/lab/c
Braces combine: {dev,prod}/{app,db} gives four paths. No spaces inside the braces - a space ends the word and the expansion does not happen.
Making files
touch f- create an empty filef, or, if it exists, just update its "last modified" time.echo hi > f- writehiintof, replacing whatever was there.echo hi >> f- addhito the end off(append).cat f- print a file's contents.
> and >> are redirection: instead of printing to the screen, the output goes into a file. The shell does the redirecting, not the command - it opens the file as you, before the command starts. Remember that; it explains a famous trap in lesson 1.11.
Copying, moving, deleting
cp a b- copy fileatob.cp -r dir/ dest/for a directory:-rmeans recursive - the directory and everything inside it, all the way down.mv a b- move or rename. They are the same operation: renaming is moving to a new name in the same directory.rm f- remove (delete) a file.rm -r dirdeletes a directory and everything inside it.
There is no trash. rm -rf (-f = force: never ask, ignore missing files) on the wrong path is the classic career moment. Two guards: look at what you are about to delete with ls first, and never type rm -rf $VAR/ unless you are certain the variable $VAR (a named value the shell fills in - see lesson 1.9) is set. If it is empty, that command becomes rm -rf /.
The errors you will hit, on purpose
$ mkdir a/b/c
mkdir: cannot create directory 'a/b/c': No such file or directory
$ mkdir -p a/b/c # fine, and fine again if re-run
$ rmdir a
rmdir: failed to remove 'a': Directory not empty
$ rm a
rm: cannot remove 'a': Is a directory
$ cp a /tmp/
cp: -r not specified; omitting directory 'a'
$ mv nothere x
mv: cannot stat 'nothere': No such file or directory
(Anything after # on a line is a comment: the shell ignores it.)
Every one of those is the tool refusing to guess. rmdir removes only empty directories, which makes it the safe choice when you expect something to be empty. "cannot stat" means "cannot look up the details of" - the file is not there.
Wildcards are expanded by the shell too
A glob (wildcard pattern) like *.log matches file names: * means "any characters", so *.log is every name ending in .log. Like braces, the shell replaces it with the matching names before the command runs.
$ ls *.log the shell turns *.log into the matching names
$ echo *.nothing
*.nothing no match: bash passes the pattern through as-is
$ rm -i *.tmp -i (interactive) asks before each delete
Before any rm with a wildcard, run the same pattern with ls or echo and read the list. It costs one second.
Seeing what you made
$ echo hello > f.txt; echo world >> f.txt
$ cat f.txt
hello
world
$ wc -l f.txt
2 f.txt
$ head -n 1 f.txt; tail -n 1 f.txt
hello
world
;separates two commands on one line: run the first, then the second.wc -l- word count, but-lcounts lines only.head -n 1- the first 1 line.tail -n 1- the last 1 line.
What you can now do
- Build a nested layout in one
mkdir -pwith braces, from an anchored path. - Create, copy, rename and delete files and directories.
- Check a wildcard before you delete with it.