The purpose of this tutorial is to get you familiarized and comfortable with the command line. For this reason you should avoid using a file manager or similar program during this tutorial, and stick to the text terminal exclusively.
Launch a terminal emulator window
(why is it called a terminal “emulator”?)
or switch to the text console
Type some stuff and press Enter. See what happens.
Note how you can retrieve the previous command(s) using the arrow-up key.
Note what happens if you press Tab.
(What is the name of the program reading and interpreting your input?)
Use pwd
to find your place in the filesystem.
Type ls
to see what’s there.
Use cd ..
to go one level upward,
and cd name
to enter a directory called name
(substitute directory names you find in your filesystem for name).
See if you can find any interesting files or directories this way.
What does cd ..
actually mean?
(What does cd .
mean?)
Type ls -al
and see if you can figure it out.
What do all the items in the output mean?
What does -al
mean?
Type man ls
and find out.
(What does man
do?)
If your output is very long and you want to view it one page at a time,
you can “pipe” the output into a “pager” such as
more
or less
:
First, try ls -alF /usr/bin
.
Compare this to ls -alF /usr/bin | less
.
In fact, many command-line utilities can be strung together into “pipelines”
where the output of one program is the input to another program.
For example, to see all the shells running on the system except ssh:
ps ax | grep '[ /][a-z]*sh\( \|$\)' | grep -v ssh
(What does ps
do? What about grep
?)
Note the quotes! See the difference:
ps ax | grep '.*'
ps ax | grep .*
What is actually happening in this case?
The quotes prevent the pattern (intended for grep
)
from being “expanded” by the shell
(the shell looks at all existing files and replaces the pattern
with a list of those filenames that match the pattern).
Read all about quoting in the bash manual page. (What is the command for displaying the bash manual page?)
Go back to your home directory using cd
(without arguments).
Create a working directory for this tutorial with mkdir tutorial
.
Change into this directory.
Create (empty) files
HD_66811
,
SN_1994D
,
He2-131
,
M_31
,
NGC_221
,
He2-108
,
NGC_224
,
HD_30614
,
NGC_6543
,
HD_93129A
,
SN_1987A
, and
NGC_XXX
.
(How do you create a file?)
Rename NGC_XXX
to NGC_104
using the program mv
.
Create directories stars
,
galaxies
,
nebulae
,
supernovae
, and
globular_clusters
.
Use mv
to move the files into the corresponding directories.
What happens if you make a mistake?
Can you move more than one file at a time? How?
Remove NGC_224
(because it is actually the same object as M 31) using the program rm
.
Can you do this without first changing into the directory the file is in?
Then create a recursive listing of the directory and save it in a file my_objects
.
(How do you save a program’s output in a file
instead of having it printed to the terminal?)
Output redirection:
ls -R > my_objects
This is like a pipe, except that the destination is a file and not the input of another program.
(See the manual page of tee
for a program
that acts like a “pipe fitting”.)
By default, programs actually have two outputs, one for “normal” output (“stdout”) and one for error messages and diagnostics (“stderr”). Both can be redirected:
command >file
(output into file, errors to terminal)
command >>file
(output appended to file, errors to terminal)
command >file 2>&1
(output into file, errors into the same file)
command >file1 2>file2
(output into file, errors into different file)
>
alone is the same as 1>
:
command 1>file1 2>file2
(output into file, errors into different file)
Example for redirecting stdout and stderr (note that, for demonstration purposes, we’re deliberately trying to list a file that does not exist):
touch a b c
ls a b c d >x
cat x
ls a b c d >x 2>y
cat x
cat y
ls a b c d >z 2>&1
cat z
Try the command seq -w 100
.
(MacOS users: try jot
if you don’t have seq
. See manpage for options.)
Now create a sequence of files with:
touch `seq -w 100`
(backquotes, not normal quotes!!!)
or
touch $(seq -w 100)
.
This useful feature is known as “command substitution”.
Note: there is a difference between
mydir=`pwd`; echo $mydir
(backquotes, command substitution)
and
mydir='pwd'; echo $mydir
(normal quotes, stuff taken literally)
Rename
001
to exp_001.fits
,
002
to exp_002.fits
, etc.
Is there an easy way to accomplish this?
Why doesn’t mv * exp_*.fits
work?
How many arguments does mv
get?
How does mv
interpret its arguments when there are more than two?
(Discuss: filename “globbing”.)
Answer to “easy way”: two “obvious” methods:
Using a (trivial) shell script:
Create a text file rename_my_files
containing
mv 001 exp_001.fits
mv 002 exp_002.fits
mv 003 exp_003.fits
(etc.)
Then let a shell execute these commands with bash rename_my_files
.
Using a “for” loop:
for f in [0-9][0-9][0-9]; do mv $f exp_$f.fits; done
(Note pattern for the shell to match the existing filenames against.)
Now rename all exp_*.fits
files to Wendelstein-*.jpg
(assume that we made a mistake, and the files aren’t actually FITS files but rather JPEG files).
One possible way:
for f in exp_*.fits; do mv $f `echo $f | sed -e 's/exp_\(.*\)\.fits/Wendelstein-\1.jpg/'`; done
or
for f in exp_*.fits; do mv $f $(echo $f | sed -e 's/exp_\(.*\)\.fits/Wendelstein-\1.jpg/'); done
Note the use of command substitution.
To understand how this works, experiment a bit with these commands:
echo exp_hallo.fits | sed -e 's/exp_\(.*\)\.fits/Wendelstein-\1.jpg/'
echo abc_xyz | sed -e 's/\(.*\)_\(.*\)/\2_\1/'
echo abc_xyz | sed -e 's=\(.*\)_\(.*\)=\2_\1='
Other useful utilities for working with filenames are
basename
and dirname
:
basename filename [extension]
dirname filename
Example:
basename /etc/passwd
basename /etc/passwd wd
dirname /etc/passwd
What makes the shell so useful is that it is both a user interface for interactive work
as well as a programming language you can use to automatize complex tasks.
For this, it provides the usual control structures:
for name in list; do command1; command2; command3; done
if command0; then command1; command2; command3; fi
while command0; do command1; command2; command3; done
Examples:
var1=foo
var2=bar
if test "$var1" = "$var2"; then echo "Yes!"; else echo "No!"; fi
if test -f myfile; then echo "myfile exists"; fi
i=77
if test $i -gt 10; then echo "too large"; fi
while true; do date; sleep 1; done
(The last one is an endless loop. Terminate this with Ctrl-C
.)
You can also write these with newlines (this looks nicer in shell scripts):
if command0
then
command1
command2
command3
else
command4
command5
fi
Modern shells can also handle (integer) numerical expressions using $(( ))
:
echo $((1+5))
for i in {1..10}; do j=$((i+5)); echo "$i -> $j"; done
Older shells that don’t understand arithmetic expressions can use the program expr
:
for i in `seq 1 10`; do j=`expr $i + 5`; echo "$i -> $j"; done
If you also need fractional numbers, use a utility such as dc
:
for i in `seq 100`; do j=`dc -e "50k $i v100+p"`; echo "$i -> $j"; done
(Can you figure out what this does?)