PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : csv parsen und gleich die user anlegen?



403
21-05-2008, 17:23
Hallo :D,

Ich moechte die User aus einem .csv file auslesen. Irgendein $USER hat die per Tabs
separiert, allerdings habe ich hier grad eine Blockade:




#!/bin/sh -

if [ -z "$1" ]; then
echo "usage: `basename $0` \$1"
exit
fi

# set a friendly delimiter

D=':'

# use a friendly delimiter
# remove tab!

sed "s/ /$D/g" $1 | \
while read line; do
awk -F "$D" '{
echo "useradd -c $1 -d -g $2 -u uid -g gid -s shell" # }' | sh
done <"$1"


Leider bringt das nicht den gewuenschten Effekt. Ich weiss dass es ein Perl
Modul gibt, aber das moechte ich vermeiden, weil ich nicht auf jeder Kiste
erst Perl Module installieren moechte... :o

Gruss
403

jan61
21-05-2008, 19:11
Moin,

zuerst mal vermisse ich in Deinem useradd den Benutzernamen. Wo steht denn der? Du übergibst ja nur einen Kommentar (-c) und eine GID (-g; das gleich doppelt). Ich nehme an, in "uid" und "gid" stehen dann numerische Werte? Wo kriegst Du die her? Oder sollen alle Benutzer die gleiche UID erhalten? Wäre keine so tolle Idee. Und "shell" wird dann wohl auch durch einen gültigen PFad zu einer real existierenden Shell ersetzt, oder?

Wieso setzt Du erst den Delimiter von \t auf : um, wenn Du anschließend awk benutzt? Der kann mit Tabs als Feldtrenner von Haus aus prima umgehen:
jan@jack:~/tmp> echo -e "1\t2\t3" | awk ' { print "1 =", $1, "2 =", $2, "3 =", $3 } '
1 = 1 2 = 2 3 = 3Damit würde sich Deine Schleife auch gleich deutlich vereinfachen (nämlich verschwinden):
awk '{ printf "useradd -c %s -d -g %s -u uid -g gid -s shell\n", $1, $2; }' $1 | shUnd es geht sogar ganz ohne awk, weil z. B. read auch direkt Daten aus einer Tab-getrennten Liste aufnehmen kann (die fehlenden Felder solltest Du noch ergänzen):
while read comm gid; do
useradd -c $comm -d -g $gid -u uid -g gid -s shell
done <$1Der einzige Grund zum Umsetzen des Feldtrenners wäre IMHO, wenn der Kommentar Leerzeichen enthält (das dürfen dann natürlich nie Tabs sein). Dann ginge das so:
jan@jack:~/tmp> echo -e "1\t2\t3" | sed 's/\t/:/g'
1:2:3
jan@jack:~/tmp> echo -e "1\t2\t3" | sed 's/ /:/g'
1:2:3
Achtung: der Tab im 2. sed wird so erzeugt, dass nacheinander CTRL-v und TAB gedrückt werden. Wenn Du den Delimiter als Variable einsetzen willst, dann musst Du statt '' doppelte Anführungszeichen nehmen. Auch dann kannst Du übrigens mit einer einfachen read-Schleife arbeiten, Du setzt einfach vor der Schleife die Variable IFS auf den Doppelpunkt.

Jan

EDIT: Wenn der Kommentar Leerzeichen enthalten kann, dann muss das Argument für die -c-Option im useradd natürlich in "" oder '' eingeschlossen werden (oder jedes Leerzeichen durch einen \ entwertet werden ;-). Nur so als Ergänzung.

403
21-05-2008, 20:52
Danke Jan, ich gehe es mal in Ruhe durch und melde mich spaeter.

jan61
22-05-2008, 18:00
Moin,

was ich erst jetzt gesehen habe: Du hast in Deiner Schleife noch einen Fehler. Du fütterst den sed mit der einzulesenden Datei - ok, übergibst die Ausgabe des sed dann per Pipe an die read-Schleife - auch ok. Aber dann schiebst Du noch mal den Dateiinhalt per Eingabeumleitung in die Schleife ('done <"$1"'), das ist überflüssig und dürfte zu Fehlern führen. Der arme read - was soll er denn nun lesen?

Jan

403
22-05-2008, 21:22
Oh mann, so eine schlampige useradd Zeile :o Im Wesentlichen war wohl einer meiner Fehler auch $1 (File) mit $1 als Argumente der csv Datei (Input) verwechselt zu haben. Dass der sed ueberfluessig ist,
ist mir natuerlich erst nach dem Posting aufgefallen. :kopftisch

Vielen Dank nochmal.

403
28-05-2008, 21:45
Hi Jan,


mit printf hatte ich so meine Probleme um z.B. uid/gid mit %d auszulesen.


aktuelle Situation sieht jetzt so aus (ohne printf):



,uname -srm && awk '{split ($0, a, ":"); print "useradd -c " a[1], "-d " a[2],
"-g " a[3], "-u " a[4], "-s " a[5]}' bla && cat bla
SunOS 5.10 sun4u
useradd -c Looser -d /home/looser -g 100 -u 24 -s /bin/loosersh
useradd -c Winner -d /home/winner -g 101 -u 24 -s /bin/sh
Looser:/home/looser:100:24:/bin/loosersh
Winner:/home/winner:101:24:/bin/sh


Leider setzt das voraus, dass die Leute das Format einhalten. Und dass
awk tab als Standard Delimiter nimmt hab ich inzwischen aus der manpage
erfahren. Vielen Dank nochmal fuer die Hilfe.

Gruss
403

jan61
29-05-2008, 00:02
Moin,

hm ... hm!

Syntaktisch verstehe ich eins nicht: Offenbar fütterst Du ja awk nun doch mit ":"-separierten Zeilen. Warum nutzt Du dann nicht die (von Dir ja ursprünglich vorgesehene) -F-Option, um Dir den split im awk zu sparen? Ich hatte ja nur beschrieben, dass es ohne -F geht, wenn man dem awk einfach freie Hand beim Interpretieren des Feld-Delimiters gibt. Mit anderen Feldtrennern ist IMHO die -F-Option immer noch billiger als das manuelle Splitten für jede Zeile.

Ach ja: Du übergibst dem useradd immer noch keinen Benutzernamen - und dabei ist dies das Einzige, was er wirklich haben will.


...Leider setzt das voraus, dass die Leute das Format einhalten.

*AAAARGH!!!* Nein, niemals, unter keinen Umständen, bei Androhung der Todesstrafe nicht, nur gegen Bestechung in 6-stelliger Höhe (und da muss mindestens eine "9" vorn stehen ;-) darfst Du einer "Leute"-Eingabe vertrauen! Es gibt 3 Regeln beim Verarbeiten von Input:
1. Vertraue nie einer nicht 150%ig geprüften Eingabe.
2. Vertraue nie einer nicht 150%ig geprüften Eingabe.
3. Vertraue nie einer nicht 150%ig geprüften Eingabe.

Gerade bei einem so heiklen Thema wie dem Anlegen von Benutzern musst Du alles 3fach checken, sonst ist Dein System innerhalb von Sekunden nicht mehr Dein System:
- es dürfen keine existierenden Benutzer angegeben werden (egal, dass useradd dann rummault - gleich am Eingang einen Riegel vorschieben)
- warum eine User-ID vorgeben lassen? Das macht das System automatisch. Und das stopft auch gleich ein Sicherheitsloch: Füttert Dich ein "Leut" mit uid 0, dann hast Du einen neuen root-User. Wenn uid's vorgegeben werden müssen (könnte im Zusammenhang mit NFS eine Rolle spielen), dann definierst Du am besten einen zulässigen Bereich, der sicher (unprivilegiert) ist. Alles außerhalb dieses Bereichs wird verschrottet.
- Gleiches gilt für die GID - der mögliche Schaden ist vielleicht nicht ganz so groß, aber auch heftig (siehe Gruppe root, bin, ...).
- das Vorgeben der zu benutzenden Shell darfst Du auch nicht dem "Leut" überlassen - was ist, wenn er sich ein SUID-Programm einträgt, mit dem er erweiterte Privilegien erhalten kann?

Du musst also Deinen awk noch kräftig aufbohren, um die ankommenden Daten halbwegs sicher zu verifizieren. Wenn Du es sauber machst, wird sich mindestens 80% Deines Codes nur um die Prüfung des Inputs drehen. Willkommen im Klub - so sieht das immer aus. Die andere Variante ist: Lass Dir einen Benutzernamen geben, prüfe ihn auf Existenz und mach den Rest selber. Wie gesagt - die ganzen Optionen sind alle optional (wie der Name schon sagt ;-) - das einzige Argument, was useradd immer haben will, ist der Benutzername.

Jan

403
29-05-2008, 17:44
moin,

Vielen Dank fuer den wichtigen Hint! Hatte mich bisher nur auf den awk konzentriert ;)
Ein Teilproblem ist, das der Sun awk wohl kein space nach -F akzeptiert:



$ awk -F ':' '{print $1}' bla
awk: syntax error near line 1
awk: bailing out near line 1
$ awk -F: '{print $1}' bla
Looser
Winner



Und ob ich nun mit -F den Delimiter ersetze oder mit split ist mir momentan egal.


Zum Input: Ich weiss, dass $ADMIN vorher sich die csv Datei ansehen muss, damit meinte ich vor
allem dass man ja nicht weiss welche legalen n Felder der Kunde dort reinschreibt. illegaler Input
muss natuerlich weg :)



bash-3.00# cat bla
Looser:/home/looser:100:60001:/bin/sh:looser
Winner:/home/winner:101:60001:/bin/sh:winner
bash-3.00# awk '{split ($0, a, ":"); print "useradd -c " a[1], "-d " a[2], "-u " a[3], "-g " a[4], "-s " a[5]," " a[6]}' bla | sh
bash-3.00# echo $?
0
bash-3.00# id looser winner
Usage: id [-ap] [user]
bash-3.00# id looser
uid=100(looser) gid=60001(nobody)
bash-3.00# id winner
uid=101(winner) gid=60001(nobody)


Bitte jetzt kein Gemecker wegen der Gruppe nobody ;) Hab inzwischen eine Testmaschine erkaempft und da darf ich
jetzt basteln. Was noch optimiert werden muss: Die Pipe an sh sollte nur einmal stattfinden. Und das Einbauen der o.g.
Checks.

Gruss 403

jan61
29-05-2008, 22:31
Moin,


Ein Teilproblem ist, das der Sun awk wohl kein space nach -F akzeptiert

Das ist richtig beobachtet (dürfte auf alle Unix-Systeme zutreffen, die keinen GNU-awk haben) - aber das ist ja nicht wirklich ein Problem ;-) Der GNU-awk akzeptiert die Option auch ohne Leerzeichen. Lass es weg, damit bist Du portabel.


Und ob ich nun mit -F den Delimiter ersetze oder mit split ist mir momentan egal.

Es war ja nur ein Hinweis, das macht den Code IMHO leichter lesbar und dürfte auch schneller sein, immerhin sparst Du je Zeile eine Funktion ein.


Bitte jetzt kein Gemecker wegen der Gruppe nobody ;) Hab inzwischen eine Testmaschine erkaempft und da darf ich jetzt basteln.

Ich mecker ja gar nicht - nobody ist doch ne gute Gruppe - im Gegensatz zu Italowestern zieht die selten schneller ;-)


Was noch optimiert werden muss: Die Pipe an sh sollte nur einmal stattfinden.

Die Pipe findet doch nur einmal statt. Alle Ausgaben des awk werden über die eine Pipe an die Shell weitergereicht.

Ich komme wieder auf meinen Vorschlag zu sprechen, das alles ganz und gar ohne awk und Pipes zu machen (ungetestet):
OLD_IFS="$IFS"
IFS=":"
while read comment home uid gid shell user; do
# Tests
useradd -c "$comment" -d "$home" -u $uid -g $gid -s $shell $user
done <bla
IFS="$OLD_IFS"Eine andere Variante wäre, den print, die Pipe und die Shell einfach wegzulassen und im awk den system()-Call zu verwenden. Du musst Dir vor Augen halten, dass Du durch eine Pipe an eine Shell keinen einzigen Prozess einsparst (im Gegenteil, die Pipe und der Shell-Aufruf sind je einer mehr) - der useradd ist nämlich kein Shell-Builtin und wird deshalb sowieso immer per fork() als eigener Prozess aufgerufen.

Jan