Re: Skriptfrage - Datendatei Zeile für Zeile abarbeiten
Jürgen Vollmer wrote:
Hallo,
[ ... ]
anbei einige Kommentare zum Skript, und ein Verbersserungsvorschlag.
sort -t: -k2 -o /root/verwaltung/user/userdaten.csv \ /root/verwaltung/user/userdaten.csv
Das funktioniert nur in den seltensten Fällen, da versucht wird eine Datei zu lesen und zu schreiben. Das geht unter Unix nicht. Korrekt wäre:
sort -t: -k2 -o /root/verwaltung/user/userdaten.csv.1 \ /root/verwaltung/user/userdaten.csv mv /root/verwaltung/user/userdaten.csv.1 \ /root/verwaltung/user/userdaten.csv
(Achtung die \ am Zeilenende sind "Fortsetzungszeichen", und sagt der Shell, das das Kommando in der nächsten Zeile weitergeht.
Hallo Liste, nachdem ich heute mittag "meine" Version des User-Anlegen Skriptes an die Liste gepostet habe möchte ich Euch eine bessere Variante von Jürgen Vollmer nicht vorenthalten - Jürgen, vielen Dank.
Falls es noch Fragen gibt: nur zu. Falls Du willst kannst Du die Lösung ja de Mailingliste posten.
mach' ich gerne, hier das Skript. Ist noch ungetestet. Viele Grüsse Joachim
------------------------------------------------------------------------
#!/bin/bash # Liest die Datei $USERS # und erzeugt die entsprechenden User- und Mail Accounts. # Author: Dr. Jürgen Vollmer
# set -x CMD=`basename $0` USERS=/root/verwaltung/user/userdaten.csv
# Basisverzeichnis aller User USER_HOME_BASE=/export/home/1
# Benutzer-Shell USER_LOGIN_SHELL=/bin/false
# setquota: QUOTA_BLOCK_SOFT=100000 QUOTA_BLOCK_HARD=100000 QUOTA_INODE_SOFT=0 QUOTA_INODE_HARD=0
############################################################################# usage() { cat <
Format der Datei: Vorname:Name:Login:Passwort:MatrNr:Expires:Angelegt:Gruppe1:Gruppe2:Gruppe3 Alle Felder mit Ausname von Name, Login, Passwort können leer sein.
Optionen: -h : help
Author: Dr. Jürgen Vollmer
EOF } ############################################################################# # Einige Funktionen: #############################################################################
# Gib Fehlermeldung aus, und Abbruch error() { echo "$CMD: Fehler: $*" echo "$CMD: Fehler: $*" | mail -s "$CMD: Fehler" root exit 1; }
############################################################################# # Führe Kommando $1 mit den angegeben Argumenten aus und prüfe den # Exit-code der Kommands. # Im Fehlerfalle: gib Fehlermeldung aus (error) do_cmd() { cmd=$1 shift
$CMD "@*" || error "$cmd " "@*" }
############################################################################# # Analysiere Argumente while getopts hv opt $* do case $opt in h) usage; exit;; *) exit 1;; esac done
shift `expr $OPTIND - 1`
# Keine weiteren Argumente angegeben? [ $OPTIND -eq 1 ] || { usage; exit 1;}
#############################################################################
# IFS The Internal Field Separator that is used for word # splitting after expansion and to split lines into # words with the read builtin command. The default # value is ``<space><tab><newline>''. OLD_IFS=$IFS
sort -t: -k2 $USERS | # sortiere nach Nachnamen (2. Feld) while read line do # entferne alle Leerzeichen aus der Zeile line=`echo $line | sed -e's/ //g'`
# spalte Zeile auf am ":" IFS=":" echo "$line" set -- $line U_VORNAME=$1 U_NAME=$2 U_LOGIN=$3 U_PASSWORT=$4 U_MATRNR=$5 U_EXPIRES=$6 U_ANGELEGT=$7 U_GRUPPE1=$8 U_GRUPPE2=$9 U_GRUPPE3=${10} # bash specific!
IFS=$OLDIFS
# Prüfe ob Pflichtfelder angegeben wurden: [ -z "$U_NAME" ] && error "NAME nicht angegeben: $line" [ -z "$U_LOGIN" ] && error "LOGIN nicht angegeben: $line" [ -z "$U_PASSWORT" ] && error "PASSWORT nicht angegeben: $line"
# Führe Kommandos aus do_cmd useradd -c "$U_VORNAME $U_NAMEs" -d $USER_HOME_BASE/$U_LOGIN \ -e $E_EXPIRES -G$U_GRUPPE1 -m -s $USER_LOGIN_SHELL $U_LOGIN do_cmd smbpasswd -a $U_LOGIN $U_PASSWORT do_cmd setquota $U_LOGIN \ $QUOTA_BLOCK_SOFT $QUOTA_BLOCK_HARD \ $QUOTA_INODE_SOFT $QUOTA_INODE_HARD \ -a
# informiere Root ( echo "Vorname: $U_VORNAME" echo "Nachname: $U_NAME" echo "------------------------" echo "Login: $U_LOGIN" echo "Passwort: $U_PASSWORT" echo "------------------------" echo "Matr. Nr.: $U_MATRNR" echo "gueltig bis: $U_EXPIRES" echo "Gruppe 1: $U_GRUPPE1" echo "Gruppe 2: $U_GRUPPE2" ) | mail -s "Account fuer $U_NAME, $U_VORNAME angelegt" root done
##############################################################################
------------------------------------------------------------------------
Dr.rer.nat. Juergen Vollmer, Viktoriastrasse 15, D-76133 Karlsruhe Tel: +49(721) 9204871 Fax: +49(721) 24874 juergen@informatik-vollmer.de,vollmer@cocolab.de,Juergen.Vollmer@acm.org www.informatik-vollmer.de
Eine kleine Korrektur zu meinem Skript noch ('s war halt Sonntag aben, und
sollte auch nur eine Art Demo sein)
do_cmd()
{
cmd=$1
shift
### $CMD "@*" || error "$cmd " "@*"
### ^^^^ falsch, richtig ist:
$cmd "@*" || error "$cmd " "@*"
}
Joachim Kieferle
Allerdings würde ich auch nach wie vor nicht gleich das Skript "losrennen" lassen, da es z.B. keinen Usercheck gibt (schon vorhanden),
Einfach folgendes einfügen (testet ob das Home-Verzeichnis bereits exisitiert) [ -d $USER_HOME_BASE/$U_LOGIN ] && error "Benutzer bereits angelegt" Es kann aber auch sein, daß useradd einen Fehler-code ausgibt, wenn es den Benutzer bereits gibt. Jürgen -- Dr.rer.nat. Juergen Vollmer, Viktoriastrasse 15, D-76133 Karlsruhe Tel: +49(721) 9204871 Fax: +49(721) 24874 juergen@informatik-vollmer.de,vollmer@cocolab.de,Juergen.Vollmer@acm.org www.informatik-vollmer.de
On Mon, 18 Nov 2002 at 08:37 (+0100), J|rgen Vollmer wrote: [...]
Joachim Kieferle
: Allerdings würde ich auch nach wie vor nicht gleich das Skript "losrennen" lassen, da es z.B. keinen Usercheck gibt (schon vorhanden),
Einfach folgendes einfügen (testet ob das Home-Verzeichnis bereits exisitiert) [ -d $USER_HOME_BASE/$U_LOGIN ] && error "Benutzer bereits angelegt"
Nicht gut. Die Nicht-Existenz eines HOME ist kein Gewähr dafür, dass der Benutzer nicht existiert: k500:~ # useradd fred k500:~ # grep ^fred: /etc/passwd fred:x:501:100::/home/fred:/bin/bash k500:~ # ll /home/fred ls: /home/fred: No such file or directory Besser ist es, die Einträge in der passwd zu prüfen: k500:~ # getent passwd fred fred:x:501:100::/home/fred:/bin/bash k500:~ # echo $? 0 k500:~ # getent passwd felix k500:~ # echo $? 2 Oder alternativ: k500:~ # grep ^fred: /etc/passwd fred:x:501:100::/home/fred:/bin/bash k500:~ # echo $? 0 k500:~ # grep ^felix: /etc/passwd k500:~ # echo $? 1
Es kann aber auch sein, daß useradd einen Fehler-code ausgibt, wenn es den Benutzer bereits gibt.
Ausprobieren: k500:~ # useradd jan useradd: user jan exists k500:~ # echo $? 9 Jan
Hallo, On Sun, 17 Nov 2002, Joachim Kieferle wrote:
Jürgen Vollmer wrote: [..] nachdem ich heute mittag "meine" Version des User-Anlegen Skriptes an die Liste gepostet habe möchte ich Euch eine bessere Variante von Jürgen Vollmer nicht vorenthalten - Jürgen, vielen Dank. [..]
------------------------------------------------------------------------ #!/bin/bash # Liest die Datei $USERS # und erzeugt die entsprechenden User- und Mail Accounts. # Author: Dr. Jürgen Vollmer
# set -x CMD=`basename $0` USERS=/root/verwaltung/user/userdaten.csv
# Basisverzeichnis aller User USER_HOME_BASE=/export/home/1
# Benutzer-Shell USER_LOGIN_SHELL=/bin/false
# setquota: QUOTA_BLOCK_SOFT=100000 QUOTA_BLOCK_HARD=100000 QUOTA_INODE_SOFT=0 QUOTA_INODE_HARD=0
############################################################################# usage() { cat <
Format der Datei: Vorname:Name:Login:Passwort:MatrNr:Expires:Angelegt:Gruppe1:Gruppe2:Gruppe3 Alle Felder mit Ausname von Name, Login, Passwort können leer sein.
Optionen: -h : help
Author: Dr. Jürgen Vollmer
EOF } ############################################################################# # Einige Funktionen: #############################################################################
# Gib Fehlermeldung aus, und Abbruch error() { echo "$CMD: Fehler: $*" echo "$CMD: Fehler: $*" | mail -s "$CMD: Fehler" root exit 1; }
############################################################################# # Führe Kommando $1 mit den angegeben Argumenten aus und prüfe den # Exit-code der Kommands. # Im Fehlerfalle: gib Fehlermeldung aus (error) do_cmd() { cmd=$1 shift
$CMD "@*" || error "$cmd " "@*" }
Hier soll doch sicher: $cmd "$@" || error "$cmd $@" stehen. Aber das geht auch einfacher ;) function do_cmd() { "$@" || error "$@" } (ich schreib seit ner Weile das function gerne explizit hin, dank Syntax Highlighting macht das die Sache uebersichtlicher ;)
############################################################################# # Analysiere Argumente while getopts hv opt $*
Sollte man nicht "$@" statt $* verwenden?
do case $opt in h) usage; exit;; *) exit 1;; esac done
shift `expr $OPTIND - 1`
# Keine weiteren Argumente angegeben? [ $OPTIND -eq 1 ] || { usage; exit 1;}
Hm. Kommt mir fuer den Anwendungsfall ein wenig "aufgeblasen" for... while test $# -gt 0; do case "$1" in -h | --help) usage; exit 0;; -*) usage; exit 1;; *) ARGS="$ARGS \"$1\""; shift;; esac; done; eval set -- $ARGS; test $# -gt 0 && { usage; exit 1; } Uebrigens halte ich nix davon so wie oben [ .. ] statt 'test' zu verwenden, genausowenig wie '.' statt 'source'... ;)
#############################################################################
# IFS The Internal Field Separator that is used for word # splitting after expansion and to split lines into # words with the read builtin command. The default # value is ``<space><tab><newline>''. OLD_IFS=$IFS
Bitte OLD_IFS="$IFS" verwenden...
sort -t: -k2 $USERS | # sortiere nach Nachnamen (2. Feld) while read line do # entferne alle Leerzeichen aus der Zeile line=`echo $line | sed -e's/ //g'`
Und was ist mit z.B. nem zweiten Vornamen z.B.? IIRC ersetzt du (Joachim) bisher ja mit sed die ',' in der csv-Datei durch ':', oder? Ausserdem kann man sich das ersetzen der , durch : sparen, denn das PW koennte ja z.B. ein , enthalten, das wuerde auch durch ein : ersetzt, da aber beim CSV Export (mindestens) Strings, die den Feld-Trenner enthalten gequotet werden muessen...
# spalte Zeile auf am ":" IFS=":" echo "$line"
Wozu hier das echo? Vom debuggen uebriggelassen? ;)
set -- $line U_VORNAME=$1 U_NAME=$2 U_LOGIN=$3 U_PASSWORT=$4 U_MATRNR=$5 U_EXPIRES=$6 U_ANGELEGT=$7 U_GRUPPE1=$8 U_GRUPPE2=$9 U_GRUPPE3=${10} # bash specific!
IFS=$OLDIFS
Das letzte muesste IFS="$OLD_IFS" sein (s.o.). Ok, um also die csv-Zeile (ob jetzt , oder : der Feldtrenner ist, ist egal, richtig behandeln muss man's sowieso ;) Also (wir sind schon im while): Erstmal die Quotezeichen so "escapen", dass wir Anfang und Ende unterscheiden koennen, dann die Feldtrenner innerhalb davon ebenfalls ersetzen und dann die Quotezeichen wiederherstellen: line="`echo \"$line\" | sed 's/\"\([^\"]*\)\"/§\1¶/g'`" line="`echo \"$line\" | sed 's/\(§[^¶]*\),\([^¶]*¶\)/\1¡\2/g'`" line="`echo \"$line\" | sed 's/[§¶]/\"/g'`" IFS="," set -- $line IFS="$OLD_IFS" Ausserdem sollte man dann die Felder die leer sein duerfen ggfs. mit defaults fuellen, z.B.: U_EXPIRES="${6:-$DEF_EXPIRES}" U_ANGELEGT="${7:-$DATE}" [davor, vor der Schleife:] DATE="`date '+%d.%m.%Y'`" DEF_EXPIRES="`date -d 'year' '+%d.%m.%Y'`" Jetzt noch ein klein wenig "Magie" um die obige Ersetzung rueckgaengig zu machen ;) for var in U_VORNAME U_NAME U_LOGIN U_PASSWORT U_MATRNR U_EXPIRES \ U_ANGELEGT U_GRUPPE1 U_GRUPPE2 U_GRUPPE3 do eval wert="\$$var" wert="`echo \"$wert\" | sed 's/¡/,/g'`" eval "$var=\"$wert\"" done
# Prüfe ob Pflichtfelder angegeben wurden: [ -z "$U_NAME" ] && error "NAME nicht angegeben: $line" [ -z "$U_LOGIN" ] && error "LOGIN nicht angegeben: $line" [ -z "$U_PASSWORT" ] && error "PASSWORT nicht angegeben: $line"
Wieder wuerde ich test verwenden, und ausserdem noch die Logik umdrehen: test -n "$U_NAME" || error "..."
# Führe Kommandos aus do_cmd useradd -c "$U_VORNAME $U_NAMEs" -d $USER_HOME_BASE/$U_LOGIN \ -e $E_EXPIRES -G$U_GRUPPE1 -m -s $USER_LOGIN_SHELL ^?? Oben war's ein U!!
$U_LOGIN do_cmd smbpasswd -a $U_LOGIN $U_PASSWORT do_cmd setquota $U_LOGIN \ $QUOTA_BLOCK_SOFT $QUOTA_BLOCK_HARD \ $QUOTA_INODE_SOFT $QUOTA_INODE_HARD \ -a
Bitte alle Variablen quoten...
Hmm... Ich glaube fast, ich sollte alle meine Aenderungsvorschlaege
nochmal zusammenfassen ;)
Achtung: ich gehe davon aus, dass die Felder in der csv-Datei durch
',' getrennt sind!!!
===================================================================
==== Achtung: nur minimal getestet ====
===================================================================
#! /bin/bash
# Liest eine CSV-Datei
# und erzeugt die entsprechenden User- und Mail Accounts.
# Author: Dr. Jürgen Vollmer
David Haller wrote:
Hallo,
On Sun, 17 Nov 2002, Joachim Kieferle wrote:
Jürgen Vollmer wrote:
[..]
nachdem ich heute mittag "meine" Version des User-Anlegen Skriptes an die Liste gepostet habe möchte ich Euch eine bessere Variante von Jürgen Vollmer nicht vorenthalten - Jürgen, vielen Dank.
[ ... ]
Aber das geht auch einfacher ;)
[ Viel viel Info + Skript ... ]
-dnh
Hallo David, gewonnen ;-)). Mal sehen, ob Du nicht auch dieses Skript mit 30 Zeichen hinbekommst ;-)). Habe am Sonntag meine weiterentwickelte Version an die Liste gepostet, aber an der sieht man sofort im Vergleich zu Jürgens Version, dass ich eben Architekt und kein Informatiker bin. Werde mein Skript natürlich entsprechend Eurer Anregungen anpassen (habe noch etwas erweitert) oder sagen wir eher neu schreiben, komme aber in den ca. nächsten 2 Wochen wegen eines dringenden Jobs nicht dazu und erfahrungsgemäss braucht das Skripten dann doch die ein- oder andere Stunde. "Mailde" mich wieder, wenn ich's fertig habe; würde mich interessieren, wieviele Zeilen Du dann einsparen kannst. Ich strenge mich an ;-)) Viele Grüsse Joachim
David Haller
Hier soll doch sicher: $cmd "$@" || error "$cmd $@" stehen.
Korrekt.
(ich schreib seit ner Weile das function gerne explizit hin, dank Syntax Highlighting macht das die Sache uebersichtlicher ;) ist aber explizit bash (ok das skript auch), aber man sollte dran denken.
Sollte man nicht "$@" statt $* verwenden? ich kenne nur das idiom mit $*, Was wäre denn ein Beispiel wo es unterschiede macht?
Hm. Kommt mir fuer den Anwendungsfall ein wenig "aufgeblasen" for... Gemacksfrag, ich finde auch kleine Skripte sollten ein vernünftiges Interface bieten.
while test $# -gt 0; do case "$1" in -h | --help) usage; exit 0;; -*) usage; exit 1;; *) ARGS="$ARGS \"$1\""; shift;; esac; done; eval set -- $ARGS; test $# -gt 0 && { usage; exit 1; }
Dieser Code ist nicht äquivalent zu dem mit getopts. Grund: Die Unix-Optionen Konvention sagt, daß foo -abc xyz entweder bedeuten kann: foo -a -b -c xyz oder falls -a ein Argument nimmt: foo -a bc xyz Benutzt man das obige Muster, muß man dieses "Parameter auseinandernehmen" selbst machen. getopts erledigt das für einen. (BTW: Die gleiche Funktionalität gibt netterweise auch in der C-Bibliothek.)
Bitte OLD_IFS="$IFS" verwenden... wo liegt da der Unterschied?
Wozu hier das echo? Vom debuggen uebriggelassen? ;) jau, das ganze war sozusagen eine "Designstudie" ohne grösseren test. Mich interessierte einfach: wie splitte ich mit der Shell einen Zeile auf. (Wusse es bis dato auch nicht).
Erstmal die Quotezeichen so "escapen", dass wir Anfang und Ende unterscheiden koennen, dann die Feldtrenner innerhalb davon ebenfalls ersetzen und dann die Quotezeichen wiederherstellen: wieso? nur wg. des syntax highlights?
line="`echo \"$line\" | sed 's/\"\([^\"]*\)\"/§\1¶/g'`" das hab ich auch noch nicht gewusst, daß in einem " .. " String die back-ticks interpretiert werden. Man lernt nie aus.
Statt dreier Zeilen: line="`echo \"$line\" | sed 's/\"\([^\"]*\)\"/§\1¶/g'`" täte es auch: line="`echo \"$line\" | sed -e'...' -e'...' -e'...'`"
Wieder wuerde ich test verwenden, und ausserdem noch die Logik umdrehen: Ohne einen (der hier so beliebten Flamewar zu starten) Warum solle man test statt [ .. ] benutzen??? Ich denke sollte Aussagen sollten irgendwie begründet werden. (Damit der Leser auch was dabi lernt).
Bitte alle Variablen quoten... warum?
Jürgen -- Dr.rer.nat. Juergen Vollmer, Viktoriastrasse 15, D-76133 Karlsruhe Tel: +49(721) 9204871 Fax: +49(721) 24874 juergen@informatik-vollmer.de,vollmer@cocolab.de,Juergen.Vollmer@acm.org www.informatik-vollmer.de
* Jürgen Vollmer schrieb am 19.Nov.2002:
David Haller
:
Sollte man nicht "$@" statt $* verwenden? ich kenne nur das idiom mit $*, Was wäre denn ein Beispiel wo es unterschiede macht?
Wenn Namen Leerzeichen oder ähnliches Ungemach enthalten. Wenn Du eins "zwei Worte" 3 eingibst, so macht daraus: $* : "eins" "zwei" "Worte" "3" $@ : "eins" "zwei" "Worte" "3" "$*": "eins zwei Worte 3" "$@": "eins" "zwei Worte" "3" Nur das letzte ist richtig, und das, was man normalerweise will. $* und $@ ohne "" sind gleich. Bernd -- ROTFL = Rolling On The Floor, Laughing = Auf dem Boden wälzen, lachend. SCNR = Sorry, Could Not Resist = Sorry, Ich konte nicht wiederstehen. AFAIK = As Far As I Know = So weit ich weis|BTW = By The Way = Nebenbei bemerkt IMHO = In My Humble Opinion = meiner bescheidenen Meinung nach |Zufallssig. 9
B.Brodesser@t-online.de (Bernd Brodesser);
Sollte man nicht "$@" statt $* verwenden? ich kenne nur das idiom mit $*, Was wäre denn ein Beispiel wo es unterschiede macht?
Wenn Namen Leerzeichen oder ähnliches Ungemach enthalten. Wenn Du
eins "zwei Worte" 3
eingibst, so macht daraus:
$* : "eins" "zwei" "Worte" "3" $@ : "eins" "zwei" "Worte" "3" "$*": "eins zwei Worte 3" "$@": "eins" "zwei Worte" "3"
Nur das letzte ist richtig, und das, was man normalerweise will. $* und $@ ohne "" sind gleich.
gebongt, aber für die Stelle an der ich es benutzt habe macht es keinen Unterschied: while getopts hv opt $* do ... done denn hier wird ja eh nur die Optionen analysiert, und die fangen mit "-" an und ein Leerzeichen kann nicht (Teil einer) Option sein. Insofern ist es hier egal. Bye Jürgen -- Dr.rer.nat. Juergen Vollmer, Viktoriastrasse 15, D-76133 Karlsruhe Tel: +49(721) 9204871 Fax: +49(721) 24874 juergen@informatik-vollmer.de,vollmer@cocolab.de,Juergen.Vollmer@acm.org www.informatik-vollmer.de
Hallo, On Tue, 19 Nov 2002, Jürgen Vollmer wrote:
David Haller
:
[es stand da: '@*' statt '$@']
Hier soll doch sicher: $cmd "$@" || error "$cmd $@" stehen.
Korrekt.
(ich schreib seit ner Weile das function gerne explizit hin, dank Syntax Highlighting macht das die Sache uebersichtlicher ;) ist aber explizit bash (ok das skript auch), aber man sollte dran denken.
Jo. Das sowieso.
Sollte man nicht "$@" statt $* verwenden? ich kenne nur das idiom mit $*, Was wäre denn ein Beispiel wo es unterschiede macht?
Vergleiche: $ foo() { echo -e '$*:'; for x in $*; do echo "'$x'"; done; \ echo -e '\n"$*":'; for x in "$*"; do echo "'$x'"; done; \ echo -e '\n$@:'; for x in $@; do echo "'$x'"; done; \ echo -e '\n"$@":'; for x in "$@"; do echo "'$x'"; done; } $ foo x "y z" $*: 'x' 'y' 'z' "$*": 'x y z' $@: 'x' 'y' 'z' "$@": 'x' 'y z' Welche Version will man also in den allermeisten Faellen wohl verwenden? ;)
Hm. Kommt mir fuer den Anwendungsfall ein wenig "aufgeblasen" for... Gemacksfrag, ich finde auch kleine Skripte sollten ein vernünftiges Interface bieten.
Ack. Ich war aber zu faul, das noch auszubauen ;)
while test $# -gt 0; do case "$1" in -h | --help) usage; exit 0;; -*) usage; exit 1;; *) ARGS="$ARGS \"$1\""; shift;; esac; done; eval set -- $ARGS; test $# -gt 0 && { usage; exit 1; }
Dieser Code ist nicht äquivalent zu dem mit getopts. Grund: Die Unix-Optionen Konvention sagt, daß foo -abc xyz entweder bedeuten kann: foo -a -b -c xyz oder falls -a ein Argument nimmt: foo -a bc xyz Benutzt man das obige Muster, muß man dieses "Parameter auseinandernehmen" selbst machen. getopts erledigt das für einen. (BTW: Die gleiche Funktionalität gibt netterweise auch in der C-Bibliothek.)
Ack. I.d.R. verwende ich aber 'getopt' (also das Programm, nicht das 'getopts' builtin)... Da "wir" es hier aber nur mit einer Option zu tun haben, die evtl. auftritt...
Bitte OLD_IFS="$IFS" verwenden... wo liegt da der Unterschied?
Ich hab mal rumprobiert, und mich gewundert, dass es nicht "bricht"... Aber generell ist es gut _IMMER_ zu quoten! z.B. "brechen": var=`echo $var | sed ...` var="`echo $var | sed ...`" var=`echo "$var" | sed ...` Letztere beiden funktionieren in einigen Faellen, aber in (zuvielen) Faellen eben nicht! Zuverlaessig ist (nur?): var="`echo \"$var\" | sed ...`" Jan und ich haben das neulich hier auf der Liste mal anhand einiger "kaputter" Dateinamen[1] auseinanderklamuesert ;)
Wozu hier das echo? Vom debuggen uebriggelassen? ;) jau, das ganze war sozusagen eine "Designstudie" ohne grösseren test. Mich interessierte einfach: wie splitte ich mit der Shell einen Zeile auf. (Wusse es bis dato auch nicht).
*g* Kruschtel mal im Archiv, zum Thema IFS hatten wir's erst ;)
Erstmal die Quotezeichen so "escapen", dass wir Anfang und Ende unterscheiden koennen, dann die Feldtrenner innerhalb davon ebenfalls ersetzen und dann die Quotezeichen wiederherstellen: wieso? nur wg. des syntax highlights?
Nein! Das hat nun wieder _garnix_ mit highlighing zu tun, das liegt an der Definition des csv-Dateiformates, um das es hier ja geht. Bei csv ist (u.a.) definiert: - es gibt ein Recordtrennerzeichen (normal: \r, \r\n, oder \n, also Zeilenumbruch) - es gibt ein Feldtrennerzeichen, per definitionem das Komma (csv steht ja fuer "comma separated values"), ueblich sind aber auch noch das Semikolon und das Tab. Theoretisch ist's aber beliebig. - es gibt ein "Quotezeichen" (meist ein '"') Nun gilt: Zeichen innerhalb zweier Quotezeichen sind "literal" auszuwerten, d.h. insbesondere ist ein Feldtrennerzeichen (oder ein Leerzeichen) innerhalb von Quotes _nicht_ als ein solches zu werten. Siehe in meinen "Beispielzeilen" die Passwoerter... Das Komma darin ist gequotet, und somit _kein_ Feldtrenner... Und eben diese Defition wird durch ein einfaches Aufsplitten via IFS Spielchen, optional wie Joachim das gemacht hatte noch mit nem sed s/,/:/ davor, vollkommen ignoriert.
line="`echo \"$line\" | sed 's/\"\([^\"]*\)\"/§\1¶/g'`" das hab ich auch noch nicht gewusst, daß in einem " .. " String die back-ticks interpretiert werden. Man lernt nie aus.
*tstst* Schaem dich. Das ist Standard *SCNR* Und (s.o.) signifikant.
Statt dreier Zeilen: line="`echo \"$line\" | sed 's/\"\([^\"]*\)\"/§\1¶/g'`" täte es auch: line="`echo \"$line\" | sed -e'...' -e'...' -e'...'`"
Ack. Allerdings wuerde z.B. ein sed 's///;s///;s///' vermutlich brechen, da bei dieser Form die Reihenfolge nicht fest definiert zu sein scheint, man muss die "nacheinander" auszufuehrenden "Operationen" also explizit nacheinander via -e 's///' spezifizieren. Folgendes sollte also klappen: ==== # Feldtrenner in quotes durch '¡' ersetzen line="`echo \"$line\" | sed -e 's/\"\([^\"]*\)\"/§\1¶/g' \ -e 's/\(§[^¶]*\),\([^¶]*¶\)/\1¡\2/g' \ -e 's/[§¶]/\"/g'`" ==== (hab's jetzt aber nicht mehr getestet). Statt den 3 Zeichen § ¶ und ¡ kann man natuerlich auch andere nehmen, nur sollten sie tunlichst _nicht_ im Input auftauchen sollten und alle 3 muessen verschieden sein, sonst "bricht's" ;) Das § ist also ggfs. weniger gut gewaehlt (ich verwende es halt gewohnheitsmaessig, da ich nix mit Juratexten zu tun habe ;)
Wieder wuerde ich test verwenden, und ausserdem noch die Logik umdrehen: Ohne einen (der hier so beliebten Flamewar zu starten) Warum solle man test statt [ .. ] benutzen??? Ich denke sollte Aussagen sollten irgendwie begründet werden. (Damit der Leser auch was dabi lernt).
Oehm, ganz einfach deswegen, weil man so ein unauffaelliges [ oder . am Zeilenanfang zu gern mal uebersieht... Es ist mir schon oefter so gegangen, dass ich irgendwelche scripte angeschaut habe, und dabei immer wieder ein '. Datei' als 'Datei' gelesen habe. Und das '[' fuer test verwende ich selbst ausserhalb von scripten praktisch nicht... Ausserdem ist es IMO gerade fuer Anfaenger sinnvoll explizit den _Befehl_ 'test' zu verwenden, es kamen hier schon mehrfach Fragen, ob wie man den mit nem if testen kann, ob ein Befehl erfolgreich ausgefuehrt wurde... (meist hat das dann Bernd mit einer Engelsgeduld ausfuehrlich beantwortet ;) Das fuehrt dann zu solchen Konstruktionen: grep foo bar if [ $? = 0 ]; then ... Und das zeigt eben, dass das 'if [ .. ]' fuer die Syntax von if gehalten wird. Ok, bei dem von dir verwendeten Konstrukt [ .. ] && ... ist _diese_ Gefahr geringer, aber dennoch halte ich eben das explizite Verwenden von 'test' fuer sinnvoller und v.a. eben leichter lesbar.
Bitte alle Variablen quoten... warum?
s.o. "es bricht". Oefter als man annimmt *eg*. Und deswegen sollte man es sich IMO antrainieren _immer_ zu quoten, moeglichst mit '', falls das (z.B. eben bei zu expandierenden Variablen) nicht geht mit "", aber auch dann immer "alles" und sorgfaeltig (s.o. das var="`echo \"$foo\"`")... Andernfalls wundert man sich, besonders wenn z.B. etwas spaeter ein 'eval' kommt, u.U. warum auf einmal ein 'rm -rf /' losrattert... Und das dann evtl. auch noch als root (via cronjob)... Sorgfaeltig quoten ist relevant (essentiell sogar) fuer die Sicherheit von Shellscripts! -dnh [1] u.a. Namen mit Leerzeichen, Zeilenumbruch, Backspaces, sowie mit " und ' *eg* -- WARNING! ~/.signature contains a signature-virus and is blocked by VaporWare(tm) Antivirus-Wall V42. Please contact your local admin.
Hallo, On Tue, 19 Nov 2002, Joachim Kieferle wrote:
David Haller wrote:
On Sun, 17 Nov 2002, Joachim Kieferle wrote: gewonnen ;-)). Mal sehen, ob Du nicht auch dieses Skript mit 30 Zeichen hinbekommst ;-)).
Bestimmt nicht. ;)
"Mailde" mich wieder, wenn ich's fertig habe; würde mich interessieren, wieviele Zeilen Du dann einsparen kannst. Ich strenge mich an ;-))
Darum geht's nicht. Und auf "Zeilen" kommts erst recht nicht an. Um's "tuning" kann man sich kuemmern wenn's korrekt(!) laeuft. Man sollte sich aber schon ueberlegen, wenn man beim 4ten grep oder 2ten sed/awk/perl in Folge unterwegs ist, ob man das nicht zusammenfassen kann oder eine falschne Ansatz verfolgt ;) Apropos... das waere sowieso noch interessant, awk statt dem IFS-Kram zu verwenden... (awk -F',' '{...}', da sollte sich einiges machen lassen ;) War aber schoen mal wieder mit sed und eval rumzutricksen ;) -dnh -- Die ultimativen Man-Pages haben ein ".c" als Dateiendung. -- am DLUG-Stammtisch
participants (5)
-
B.Brodesser@t-online.de
-
David Haller
-
Jan.Trippler@t-online.de
-
Joachim Kieferle
-
Jürgen Vollmer