EDV-Zentrum der Universität für Bodenkultur Bourne-Shell cron sed awk Kursunterlage H. Partl Juli 1995 |
U n i x Shell-Programmierung |
vi filename erstellen
chmod 700 filename
sh -vx filename parameter 2>&1 | more (sh)
sh -vx
filename parameter |& more (csh)
testen
filename parameter aufrufen
#!/bin/sh magic number (1. Zeile)
# Kommentar
zumindest: Name
Zweck
Autor
Datum
(Version)
if [ $# -lt 2 -o $# -gt 3 ]
then echo "Usage: filename par1 par2
[par3]"
exit 1
fi
$1 1. Parameter
$9 9. Parameter
oder Leerstring
"$1" 1. Parameter mit ""
$* alle Parameter
"$@" alle Parameter, jeder mit ""
$# Anzahl der Parameter
shift
name=${1-defaultwert}
name=wert
$name
${name}
${name-defaultwert}
export name
if command
then commands
fi
if command
then commands
else commands
fi
if command
then commands
elif command
then commands
else
commands
fi
if test expression
if [ expression ]
-d file Directory
-f file File
-s file nicht leeres File (size)
-r file Read-Permission
-w file Write-Permission
-x file Execute-Permission
-z "string" Leerstring (zero)
= Strings gleich
!= Strings ungleich
-eq Zahlen gleich (equal)
-ne Zahlen ungleich
-gt größer (greater than)
-ge größer gleich
-lt kleiner (less than)
-le kleiner gleich
! nicht
-a und (and)
-o oder (or)
\( \) Teil-Expressions
while command
do
commands
done
while read feld1 feld2 rest
do
commands mit $feld1 $feld2
done <
filename
i=1
n=10
while [ $i -le $n ]
do
commands mit $i
i='expr $i +
1'
done
for name in wert1 wert2 wert3
do
commands mit $name
done
case wert in
muster1) commands ;;
muster2) commands ;;
*) commands
;;
esac
exit 0 erfolgreich beenden
exit 1 mit Fehler beenden
exit Exit-Status des letzten Befehls
command <infile
command <<eofstring
... Eingabe-Zeilen ...
eofstring
command >outfile
command >>outfile
command >outfile 2>errfile
command >outfile 2>&1
echo "Fehlermeldung" 1>&2
command1 | command2 | command3
command1 | tee outfile | command2
* 0 bis n beliebige Zeichen
? 1 beliebiges Zeichen
[abc] 1 Zeichen aus Menge
[a-z] 1 Zeichen aus Menge (Bereich)
![abc] 1 Zeichen nicht aus Menge
\x 1 Zeichen
'text' ganzer Text
"text" Text bis auf $ und '
'command' Ergebnis des Befehls
(date; who) >file
(cd sub; ls -l | more)
(cd old; tar -cf - .) | (cd new; tar -xvf - .)
crontab -l
crontab -l > file
crontab < file
Minute Stunde Tag Monat Wochentag Befehl
1 0 1 * * command
jeden Monatsersten um 0:01 Uhr
30 19 8 3 * command
am 8. März um 19:30 Uhr
1 0 1 1,4,7,10 * command
an jedem Quartalsbeginn
0 5 * * 1 command
jeden Montag um 5 Uhr
0 5 * * 0,6 command
jeden Sonntag und Samstag
0 5 * * 1-5 command
jeden Montag bis Freitag
Stream Editor
sed -e 'command' textfile
sed -f commandfile textfile
sed -e 'command' inputfile >outputfile
Editor-Befehle wie bei ed und ex
^ Zeilenanfang
$ Zeilenende
. 1 beliebiges Zeichen
[abc] 1 Zeichen aus Menge
[a-z] 1 Zeichen aus Menge (Bereich)
[^abc] 1 Zeichen nicht aus Menge
* 0 bis n solche Zeichen
.* 0 bis n beliebige Zeichen
Alfred V. Aho
Peter J. Weinberger
Brian W. Kernighan
awk 'command' textfile
awk -f commandfile textfile
awk 'command' inputfile >outputfile
bedingung { aktion }
$1 1. Feld der Zeile
$99 99. Feld der Zeile
$0 Zeile
"wert" Text-String
wert Zahl
NF Feld-Nummer
NR Zeilen-Nummer (Record)
FS Feld-Separator
== gleich
< kleiner
<= kleiner gleich
> größer
>= größer gleich
~ enthält Teilstring
! nicht
&& und
|| oder
BEGIN vor der ersten Zeile
END nach der letzten Zeile
print Ausgabe
man -k keyword
man commandname
man chapter commandname
man ... | col -bx | lp -ddruckername
1 Befehle für Benutzer
1M Befehle für Systemadmin.
2 C-System-Calls
3 C-Library-Funktionen
3F Fortran-Library-Funktionen
4 File-Formate
5 verschiedenes
7 Special-Files (Devices)
8 Befehle für Systemadmin.
#! /bin/sh
#
# ll = ls -l (like under HP-UX)
#
# by Hubert Partl (EDV-Zentrum Boku Wien)
# last change: 1993-01-07
ls -l $*
exit
#!/bin/sh
#
# tel - lookup BOKU phone numbers
#
# by Partl, BOKU Wien
# last change: Partl 92-11-13
#
# Usage:
# tel pattern
# prints all lines of $FILE that match the search pattern (name, part
# of name, number, part of number, regular expression as understood
# by egrep).
# $FILE contains the phone list (lastname, initial, extension).
FILE=/usr/local/lib/telefon.liste
if [ $# != 1 ]
then echo "Usage: tel searchpattern" 1>&2
exit 1
fi
egrep -i "$1" $FILE
exit
#! /bin/sh
#
# clean-tmp woechentliche Verkleinerung von /tmp
# (Loeschen aller Files aelter als 1 Woche)
#
# last change Partl 93-01-27
#
# Aufruf als User root, jeden Montag frueh mittels crontab.
for dir in /tmp /usr/tmp /opt/tmp /var/tmp
do
if [ -d $dir ]
then find $dir -type f -mtime +7 -exec rm {} \;
fi
done
exit
#! /bin/sh
#
# vi-crontab Prozedur zum Veraendern der crontab mittels vi
#
# Autor: Hubert Partl, BOKU Wien
# letzte Aenderung: 1992-03-08
# verwendete Files:
FILE=$HOME/crontab.copy
# behaelt den neuen Zustand der crontab des Usernames
# alte crontab holen:
crontab -l >$FILE
# Aenderungen durchfuehren:
vi $FILE
# wird mit :wq oder :q! beendet
crontab <$FILE
exit
#!/bin/sh
#
# newlogin
#
# by Hubert Partl, BOKU Wien
# last change: Partl 1993-05-28
#
# Usage: newlogin [-help] [-host hostname] [-user username]
# Standard-Werte:
hostname='hostname'
username='logname'
help=false
okay=true
HELP="Help:
Newlogin startet eine neue Login-Session.
Bei Angabe des Parameters -host hostname
auf diesem Unix-Rechner, sonst am eigenen Rechner.
Bei Angabe des Parameters -user username
unter diesem Username, sonst unter dem eigenen Username.
Anschliessend wird man nach dem Passwort gefragt.
Bei Angabe des Parameters -help erhaelt man Erklaerungen
und es wird keine Session gestartet."
USAGE="Usage: newlogin [-help] [-host hostname] [-user username]"
# Optionen verarbeiten:
while [ $# -gt 0 ]
do case "$1" in
-help) help=true
shift;;
-host) shift
if [ $# -eq 0 ]
then echo "Hostname fehlt."
okay=false
else hostname="$1"
shift
fi;;
-user) shift
if [ $# -eq 0 ]
then echo "Username fehlt."
okay=false
else username="$1"
shift
fi;;
*) echo "Falscher Parameter $1"
okay=false
shift;;
esac
done
# Ausfuehrung:
if $help
then echo "$HELP"
elif $okay
then echo "Login auf Rechner $hostname unter Username $username"
rlogin $hostname -l $username
exit # with exit status of rlogin
else echo "$USAGE"
exit 1
fi
Aufgabe: Automatische Bestimmung des DISPLAY-Variablen für X-Window in der Form
DISPLAY=ipadresse:0 (Bourne-Shell)
export DISPLAY
bzw.
setenv DISPLAY ipadresse:0 (C-Shell)
An Stardent-Rechnern liefert who am i
eine Zeile der Form username tty monat tag zeit ipadresse
DISPLAY='who am i | awk '{print $6}'':0An HP-Rechnern liefert who am i -R
eine Zeile der Form username tty monat tag zeit (ipadresse)
DISPLAY='who am i -R | sed -e 's/^.*(//' -e 's/)/:0/''
Aufgabe: Ausgabe der Directory-Hierarchie in der Form
directory
|---- file
|---- directory
| |---- directory
|
| |---- file
| | |---- file
| |---- directory
| |
|---- file
| | |---- file
|---- file
|---- file
Ablaufskizze:
find subdir/subdir/subdir/file
1. sed-Schritt |---- |---- |---- file
2. sed-Schritt | | |---- file
Shell-Script:
#!/bin/sh
#
# tree print directory tree
#
# by Hubert Partl, BOKU Wien
# using ideas from
# Helmut Herold: "AWK und SED", Unix und seine Werkzeuge,
# Addison Wesley 1991
#
# last change: Partl 1993-03-18
#
# Usage:
# tree directory or
# tree (current directory)
if [ $# -gt 1 ]
then echo "Usage: tree [directory]"
exit 1
fi
dir=${1-.} # Default for directory is . (current directory)
if [ ! -d $dir ]
then echo "$dir is no directory."
exit 1
fi
find $dir -print | sort | \
sed -e 's:[^/]*/:|---- :g' -e 's/---- |/ |/g'
# first replace subdir/
# by |----
# with subdir = [not /]*
# then replace |---- |----
# by | |----
exit
Grundlagen:
Jeder Benutzer hat in seinem Home-Directory Startup-Files mit Namen der Form ".xxx". Das EDV-Zentrum legt unter "/etc/d.xxx" Standard-Versionen dieser Startup-Files an.
Beim Anlegen eines neuen Username werden die Standard-Versionen in sein Home-Directory kopiert. Der Benutzer kann die Files dann nach seinen Bedürfnissen verändern.
Wenn das EDV-Zentrum später neue oder verbesserte /etc/d.xxx-Files zur Verfügung stellt, kann jeder Benutzer diese mit "getdotfiles" in sein Home-Directory kopieren. Seine alten Versionen werden dabei unter anderen Namen "gerettet", damit er die Neuerungen mit "diff" kontrollieren und seine eigenen Modifikationen nach Bedarf in die neuen Files einfügen kann.
Shell-Script:
#!/bin/sh
#
# getdotfiles copy new default startup files into home directory
#
# by Bauer and Partl, BOKU Wien
# last change: Partl 93-01-07
#
# Source files: /etc/d.xxxx
# (usually symoblic links to /usr/local/etc/d.xxx)
# Destination files: $HOME/.xxx
# Backup files: $HOME/OLD.xxx
#
# For each .xxx in a predefined set (.login, .Xdefaults, .exrc etc.) do
# if source file exists
# if destination file exists already
# if destination file is different from source file
# copy old destination file to backup file
# copy source file to (new) destination file
# else do nothing
# fi
# else (destination file does not exist yet)
# copy source file to (new) destination file
# fi
# fi done
cd $HOME
cflag=false
for dfile in .profile .login .cshrc .logout \
.xsession .mwmrc .Xdefaults .vueprofile .Xdefaults.pc .xpc \
.exrc .mailrc
do
sfile=/etc/d$dfile
if [ -r $sfile ]
then if [ -f $dfile ]
then if cmp -s $dfile $sfile
then echo "No changes in $dfile."
else cp $dfile OLD$dfile
cp $sfile $dfile
echo "Old $dfile saved as OLD$dfile, new $dfile copied."
cflag=true
fi
else # $dfile does not exist
cp $sfile $dfile
chmod 700 $dfile
echo "New $dfile created."
cflag=true
fi
fi
done
if $cflag
then echo "New startup files may need a new login to become active."
fi
exit 0
Typische Ausgabe:
No changes in .profile.
Old .login saved as OLD.login, new .login
copied.
Old .cshrc saved as OLD.cshrc, new .cshrc copied.
No changes in
.logout.
New .Xdefaults created.
New .Xdefaults.pc created.
New .xpc
created.
New startup files may need a new login to become active.
Grundlagen:
Mail-Adressen sind Usernames.
Institute sind Unix-Gruppen mit der Institutsbezeichnung (z.B. h999) als Groupname.
/etc/passwd enthält Zeilen der Form username:passwort:uid:gid:gecos:home:shell
/etc/group enthält Zeilen der Form groupname:passwort:gid:kommentar
Ablauf-Skizze (ohne Fehler-Überprüfungen etc.):
file=$1
subject="$2"
gid='awk -F: '$1 == "h999" {print $3}'
/etc/group'
adrlist='awk -F: '$4 == "'$gid'" {print $1}'
/etc/passwd'
mail -s "$subject" $adrlist < $file
Komplettes Shell-Script:
#!/bin/sh
#
# mailall Mail an alle Benutzer eines Instituts senden
#
# Autor: Partl, BOKU Wien
# letzte Aenderung: 93-03-08# Aufruf: mailall file [subject]# verwendete Files:
# /etc/group fuer Zuordnung Institut (Groupname) -> GID
# /etc/passwd fuer Zuordnung GID -> Usernames
inst=h999 # Institutsnummer = Unix-Groupname
defaultsubject="mailall-Message fuer $inst"
if [ -x /usr/ucb/mail ]
then mailprog=/usr/ucb/mail
elif [ -x /usr/bin/mailx ]
then mailprog=/usr/bin/mailx
elif [ -x /bin/mail ]
then mailprog=/bin/mail
else echo "Kein Mail-Programm gefunden."
exit 1
fi
if [ $# -lt 1 -o $# -gt 2 ]
then echo "Usage: mailall file [subject]"
exit 1
fi
file=$1
subject=${2-$defaultsubject}
if [ ! -r $file ]
then echo "Ich kann File $file nicht lesen."
exit 1
fi
if file $file | grep text >/dev/null
then : # okay
else echo "$file ist kein Text-File."
exit 1
fi
gid='awk -F: '$1 == "'$inst'" {print $3}' /etc/group'
if [ -z "$gid" ]
then echo "Institut bzw. Unix-Gruppe $inst existiert nicht."
exit 1
fi
adrlist='awk -F: '$4 == "'$gid'" {print $1}' /etc/passwd'
if [ -z "$adrlist" ]
then echo "Es gibt keine 'hostname'-Benutzer in der Gruppe $inst."
exit 1
fi
echo "Soll File $file mit Subject $subject"
echo "an $adrlist" | tr "\012" " "
echo ""
echo "gesendet werden? (Return fuer ja, Ctrl-c fuer nein)"
read antwort
$mailprog -s "$subject" $adrlist < $file
exit # return exit status of $mailprog
Typische Ausgabe:
Soll File willkomm mit Subject Willkommen im Kurs
an kurs kurs1 kurs2
kurs3 kurs4 xkurs
gesendet werden? (Return fuer ja, Ctrl-c fuer nein)
schlecht:
find . -type f -exec grep text {} /dev/null ';' (zu viele Prozesse)
grep text 'find . -type f -print' (Gefahr von "command too long")
gut:
find . -type f -print | xargs grep text
falsch:
size='wc -c $file' (wc liefert Länge und Filename)
wc -c $file | read size rest (read liest nicht aus Pipe)
wc -c $file | ( read size rest ) (size nur in Subshell gesetzt)
size='wc -c $file | cut -d" " -f1' (mehrere Blanks vor der ersten Ziffer)
richtig, aber nicht gut :
size='wc -c $file | awk '{print $1}'' (zu viele Prozesse)
size='ls -l $file | awk '{print $5}'' (zu viele Prozesse)
set 'wc -c $file' (Parameter werden zerstört)
size=$1
set 'ls -l $file' (Parameter werden zerstört)
size=$5
size='cat $file | wc -c' (zu viele Prozesse)
gut:
size='wc -c <$file'