UNIX Shell Scripting Talk
UNIX Shell Scripting Talk
Netsoc
Stephen Shaw <[email protected]>
2011
Getting started
SSH to one of our servers PuTTY: Enter login.netsoc.tcd.ie as the hostname Real operating systems: $ ssh [email protected] NX to cube if you want - all you need is a shell though No netsoc account? CS: macneill.scss.tcd.ie Maths servers? Talk to an admin before you leave so you have an account for next time
UNIX
Multi-user, multi-tasking operating system Origins in the late 60s - UNICS The ancestor of many modern operating systems: BSD AIX Solaris Mac OS X
Kernels
Shells
bash is one of the more popular shells This talk will be based on
bash
chsh
Your prompt
You should see something like
1
stesh@cube :~$
This is called the prompt stesh - username cube - hostname ~ - current working directory $ - privilege level The format of the prompt is maintained in a variable called
PS1:
1 2
Well use
Variables
All variables in bash are strings This is both a blessing and a curse Variables are assigned values with Variables are evaluated with
1 2 3 4 5 6 7
$ foo=bar $ echo $foo bar $ echo zanzi${foo} zanzibar $ echo "zanzi$foo" zanzibar
(how?)
Special variables
random integer
current PID exit status of last process exited PID of last fork argv 0th to 9th argument current shell current user number of arguments
$0$9: $#:
$SHELL: $USER:
Quotes
Quotes are very important in shell scripts Single quotes mean literally:
1 2 3
Backticks
return a result:
1 2 3 4
echo $(whoami) stesh echo `uptime ` 05:07:59 up 120 days , 16:17 , 115 users , 0.46, 0.47, 0.42 $()
load average:
Data going in (buered) Data coming out (buered) Warnings coming out (not buered)
cat
concatenate Copy
stdin
to
stdout
printed on
1 2 3
stdout
tac
is like
cat,
Pipes
stdout
of one process to
stdin
of another
$ ls /home | sort | head -n 5 # First five home folders by alphabetical order alxsoky andyrew arboroia at_god baran $ ps -ef | grep emacs | grep -v grep | wc -l # How many emacs users are there ? 4
Redirects
1 2
feeds
stdin
from
redirects
stdout
to
redirects
stderr
to
redirects
stderr
to
stdout
$ cat <<EOF > myscript.sh echo 'Hello netsoc!' EOF $ chmod +x myscript .sh $ ./ myscript.sh Hello netsoc!
Conditional execution
&&
||
for disjunction
$ t r u e && echo "Hi$USER" Hi stesh $ f a l s e && echo "Hi$USER" $ t r u e || echo "Hi$USER" $ f a l s e || echo "Hi$USER" Hi stesh $ ./ configure && make
if
Processes have exit codes They tell you something about the status of the process when
it ended
Success? Failure? You exit a script with
1 2 3
exit
exit exit
Traditional-style conditionals
Programs have exit codes So why not write a program which turns condition tests into
exit codes?
1 2 3 4 5
[ and logic
true if $p is not true $p is true and $q is true $p is true or $q is true length of $str is zero length of $str is greater than zero $a and $b are equal $a and $b dier
[ and les
condition -e file -f file -d file -r file -w file -x file -p file true if file exists file exists file exists file exists file exists file exists file exists
is is is is is is
You have to be careful using these le tests The condition is true as of when it was evaluated Race conditions
Looping
1 2 3
use
1 2 3 4 5 6 7 8
f o r i in 1 2 3; do > echo $i > done 1 2 f o r file in $(ls); do > du -sh $i > done
Vocabulary
who
who is logged in, and from where
1 2 3 4 5 6 7 8
2011 -10 -26 2011 -09 -27 2011 -07 -10 2011 -10 -26 2011 -08 -30 2011 -10 -25
$ w stesh pts /199 89.100.25.137 20:12 0.00s 0.06s 0.00s tmux a stesh pts /228 :1026.0 Tue23 24:14m 0.67s 0.61s ssh spoon stesh pts /230 :1026.0 Tue23 24:31m 0.06s 0.06s zsh
$ w USER TTY FROM LOGIN@ IDLE WHAT stesh console - Mer18 6:53 stesh s000 - Mer19 1 ssh cube w -h
last
Login histories
1 2 3 4 5 6 7
$ who mloc pts /129 104.76.534.53 Thu Oct 27 00:01 still logged in bunburya pts /222 88.151.27.232 Wed Oct 26 23:17 still logged in mloc pts /193 202.17.56.53 Wed Oct 26 21:35 gone - no logout scott pts /58 89.116.2.54 Wed Oct 26 21:12 - 00:30 (02:12) t1 pts /129 109.76.162.99 Wed Oct 26 22:16 - 00:06 (01:50) ... /var/log/wtmp
If
root
finger
Look up information about a user
1 2 3 4 5 6 7 8 9 10
$ finger stesh Login: stesh Name: Stephen Shaw Directory: /home/stesh Shell: /usr/bin/ zsh ... $ finger finger Login: finger Name: Kieran Manning Directory: /home/finger Shell: /bin/bash ... $ finger stephen # finger everyone called 'Stephen ' $ finger -m stesh # finger stesh in more detail touch ~/.nofinger
run
What
Idle
TTY
Host
uptime
How long weve been up, and what the load averages are
1 2
$ uptime 00:44:03 up 121 days , 11:53 , 130 users , 0.71, 0.62, 0.56
load average:
ps
Get information about the processes that are currently running
ps
GNU ps:
1 2 3
BSD ps:
1 2
xargs
stdin
$ ls ~ | xargs du -h # calculate sizes for my files $ find /srv/webspace/$USER - type d | xargs chmod 755 # fix webspace permissions $ find /srv/webspace/$USER - type f | xargs chmod 644 # fix webspace permissions
cp
Copy a le
1 2 3 4
$ cp /etc/motd.tail /etc/motd $ cp -r /etc /var/backups/etc # recursively copy a directory $ cp -a ~/ Docs mnt/spoon # preserve access times and ownership $ cp -v /home /mnt/backupdrive # notify on stderr when a copy is made
mv
Move a le
1 2 3 4
$ $ $ $
mv mv mv mv
/var/log/auth.log /var/log/auth.log.1 -i /etc/profile /etc/passwd # confirm before moving -n new.txt old.txt # don 't move if old.txt exists -v # notify on stderr when a move is made
rm, rmdir
remove a le or directory
1 2 3 4
$ $ $ $
rm /bin/rm # oops rm -r ~/. Trash # recursively remove a directory rmdir ~/. Trash # remove a directory , fails if non -empty rm -rf --preserve -root / # Refuse to destroy slash
grep,fgrep
$ grep root /etc/passwd root:x:0:0: root :/ root :/bin/bash $ ps -e | grep tmux 3279 ? 00:06:15 tmux 4888 pts /183 00:00:00 tmux $ fgrep -i fail /var/log/auth.log # ignore case $ last | grep -v netsoc # reverse the match $ last | grep -e '(\d+) \.(\d+) \.(\d+) \.(\d+)' # use extended regexes
wc
Count things in a le
1 2 3 4 5
$ $ $ $ $
wc -l /var/log/sshd.log # count lines wc -m myfile.txt # count characters wc -b myfile.txt # count bytes mv -w myfile.txt # count tokens grep ":0:0" /etc/passwd | wc -l # toor?
1 2 3 4 5 6 7
tar
- tape archive
$ tar -cf homebackup.tar /home/stesh # archive my home directory $ tar -czf homebackup.tar /home/stesh # same , but with compression $ tar -xf homebackup.tar # restore from an archive $ gzip access.log # compress a file $ gzip -9 access.log # highest compression level ( between 1 and 9) $ gunzip access.log.gz # decompress $ zcat access.log.gz # decompress and output to stdout
pv
stderr
sed and tr
sed - Stream editor modify input line-by-line a silly example: replace all the colons in
/etc/passwd
with
hyphens:
1
$ cat ls /home | tr '\n' ' ' # replace newlines with spaces $ finger stephen | tr -s ' ' # 'squeeze ' multiple spaces into one
$ $ $ $
man ssh | head head -n 5 /etc/shadow # first 5 lines last | tail -n 10 # last 10 lines tail -f /var/log/userweb.log # watch for new writes
sort
$ $ $ $ $
who | sort sort -g myfile # sort numerically sort -r myfile # reverse order sort -u myfile # don 't print duplicates df -h | sort -h # sort human - readable quantities (1G, 2K , etc .)
shuf
$ who | sort $ shuf /etc/passwd | head -n 1 | cut -d ':' -f 1 | # a random user $ shuf /usr/share/dict/words | head -n 1 # a random word from the dictionary
cut
$ cut -d ':' -f 1 /etc/passwd # list the usernames in /etc / passwd $ ps -ef | cut -d ' ' -f 2,3,4 # the second , third , and forth space - delimited tokens $ cut -c 100 ~/. plan # the first 100 characters
1 2 3
prints lines common to two les shows the dierences between two les shows the unique lines in a le
$ ps -ef | cut -d ' ' -f 1 | sort | uniq $ comm /etc/ssh/ssh_config ~/. ssh/config $ diff myfile.txt myfile.txt.old comm
and
diff
You get unexpected results if the input lines are not sorted
perl
Perl is a general-purpose, interpreted programming language It is used a lot in text processing and system administration Very powerful regular expressions
nite-state automaton
This is usually irrelevant for the purpose of shell scripting Use to match patterns in text Can also perform limited amounts of parsing
Recognizes a single occurrence of a a single occurrence of any character zero or more occurrences of a one or more occurrences of a a single occurrence a or of b (but not both) a single a followed by a single b a single a, optionally followed by a single b
cron
1 2 3 4 5 6 7 8
you schedule tasks to run at particular times -l to view your cron table -e to edit your cron table -lu user to view users cron table (requires root)
command
# hourly backups to spoon @hourly /home/stesh/bin/hourly -backups # daily backups from CS 30 4 * * * /home/stesh/bin/daily -backups
Its often good to end a cron entry with 2>&1 >/dev/null Otherwise cron daemon will send emails about your cronjob It is good manners not to schedule a big cron job during peak
hours
Notice how my big daily backup job runs at 4:30 in the
morning
nc
netcat copy
1 2 3 4
stdin
to
stdout
over a network
$ cat myfile.txt | nc -lp 9999 # serve myfile .txt on port 9999 $ nc localhost 9999 > myfile.txt.copy $ nc -z spoon.netsoc.tcd.ie 22 # is port 22 open on spoon? $ nc -z spoon 1 -1000 # which ports between 1 and 1000 are open on spoon? nc
Example: backup
a backup of
/etc/
I need to store the backup on a remote machine The remote machine isnt as physically secure as spoon.
Example: backup
Use
/etc
Encrypt the archive using the GNU privacy guard (gpg) Use
ssh
Example: backup
1 2 3 4 5 6 7 8 9 10 11
#!/ bin/bash s e t -e # die if any call exists with an exception ln -s $$ lock || e x i t 1 i f [ ! -e etcbackup.tgz ]; then tar -czf etcbackup.tgz /etc gpg -c etcbackup.tgz scp etcbackup.tgz.gpg prime.netsoc.tcd.ie: fi rm lock
thoughts?
Example: backup
thoughts? locking is important in admin-style scripts, especially cronjobs make sure at most one instance of the script can run at any
one time
Be careful when using
[
le tests
This implementation creates a few unnecessary les We can condense it down to one line:
1 2
#!/ bin/bash tar -c /etc | gzip --best | gpg -c | ssh prime.netsoc.tcd. ie ">etcbackup.tgz.gpg" stdin
remote end