Bash awk Befehl mit Anführungszeichen

8

Ich habe eine Weile versucht, die Antwort auf diese Frage zu finden. Ich schreibe ein schnelles Skript, um einen Befehl auszuführen, der auf der Ausgabe von awk basiert.

ID_minimum=1000
for f in /etc/passwd;
do 
    awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c 'limit bsoft=5g bhard=6g $1' /home "}' $f;       
done

Die Probleme sind, dass das -cArgument einen Befehl in einfachen Anführungszeichen akzeptiert und ich nicht herausfinden kann, wie ich dem richtig entkommen kann und dass $1dies auch nicht in den Benutzernamen erweitert wird.

Im Wesentlichen versuche ich nur, es zur Ausgabe zu bringen:

xfs_quota -x -c 'limit bsoft=5g bhard=6g userone' /home
xfs_quota -x -c 'limit bsoft=5g bhard=6g usertwo' /home

usw...

ZCT
quelle
2
Mögliches Duplikat von Wie man Anführungszeichen in der Shell entgeht?
Jordan
2
Ihre Schleife wird immer nur einmal wiederholt.
Jordan
@ Jordanm Ich glaube nicht, dass das gilt fürawk
Jesse_b
2
@Jesse_b sein Zitatproblem ist Shell-Zitat, hat eigentlich nichts mit awk zu tun. Er muss verschachtelten einfachen Anführungszeichen entkommen.
Jordan

Antworten:

13

Um den Befehl xfs_quota -x -c 'limit bsoft=5g bhard=6g USER' /homefür jeden Benutzer auszuführen, USERdessen UID mindestens lautet $ID_minimum, sollten Sie zuerst diese Benutzer analysieren und dann den Befehl tatsächlich ausführen , anstatt zu versuchen, eine Zeichenfolge zu erstellen, die den Befehl darstellt, den Sie ausführen möchten.

Wenn Sie die Befehlszeichenfolge erstellen, müssten Sie sie erstellen eval. Das ist fummelig und leicht falsch zu verstehen. Es ist besser, nur eine Liste der Benutzernamen abzurufen und dann den Befehl auszuführen.

getent passwd |
awk -F: -v min="${ID_minimum:-1000}" '$3 >= min && $1 != "nfsnobody" { print $1 }' |
while IFS= read -r user; do
    xfs_quota -x -c "limit bsoft=5g bhard=6g $user" /home
done

Beachten Sie, dass für das nachfolgende Argument keine einfachen Anführungszeichen erforderlich sind -c. Hier verwende ich doppelte Anführungszeichen, weil ich möchte, dass die Shell die $userVariable erweitert , die Werte enthält, die von extrahiert wurden awk.

Ich benutze, ${ID_minimum:-1000}wenn ich der minVariablen im awkBefehl den Wert gebe . Dies wird auf den Wert von erweitert $ID_minimumoder darauf, 1000ob diese Variable leer oder nicht festgelegt ist.


Wenn Sie wirklich wollten, können Sie die Befehle in der obigen Schleife ausdrucken lassen, anstatt sie auszuführen:

getent passwd |
awk -F: -v min="${ID_minimum:-1000}" '$3 >= min && $1 != "nfsnobody" { print $1 }' |
while IFS= read -r user; do
    printf 'xfs_quota -x -c "limit bsoft=5g bhard=6g %s" /home\n' "$user"
done

Beachten Sie erneut, dass die Verwendung von doppelten Anführungszeichen in der ausgegebenen Befehlszeichenfolge (anstelle von einfachen Anführungszeichen) eine Shell in keiner Weise verwirren würde, wenn Sie die generierten Befehle mit evaloder auf andere Weise ausführen würden . Wenn es Sie stört, tauschen Sie einfach die einfachen und doppelten Anführungszeichen im ersten Argument nach printfoben aus.

Kusalananda
quelle
1
Die einzige Antwort, um getent richtig zu verwenden, anstatt / etc / passwd zu analysieren.
Cas
1
@cas macOS ist ein Unix ohne getent(auf dem Desktop jedenfalls), und die Frage ist nicht mit "Linux" gekennzeichnet.
TheDudeAbides
2
@TheDudeAbides Angenommen, der Benutzer hat UID 1000 ausgewählt, da dies der Grenzwert zwischen Systemdienstkonten und Benutzerkonten auf seinem System ist, können wir daraus schließen, dass dieser Benutzer nicht unter macOS ausgeführt wird (erstes Benutzerkonto ist 501). Außerdem ist die Verwendung von XFS-Dienstprogrammen unter macOS eher ungewöhnlich , da XFS von Apple nicht wirklich unterstützt wird. Wird unter /homemacOS auch nicht wirklich verwendet (es ist da, aber normalerweise leer).
Kusalananda
@Kusalananda Punkt genommen. Ich war blind für den XFS-Teil.
TheDudeAbides
1
@TheDudeAbides getent ist nicht nur Linux. es ist auch auf netbsd, freebsd und solaris zumindest.
Cas
6
for f in /etc/passwd;

Das ist ein bisschen albern, da es wirklich keine Schleife mit nur einem Wert gibt.

Aber das Problem scheint darin zu bestehen, einfache Anführungszeichen von awk zu drucken. Sie können sie in der Shell maskieren, aber Sie können auch Backslash-Escapezeichen innerhalb von awk verwenden, um sie zu drucken. das Zeichen mit Zahlenwert ist OOO (in oktal ), so ist . Das wäre also eine Möglichkeit:\OOO\047'

awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" {
    printf "xfs_quota -x -c \047limit bsoft=5g bhard=6g %s\047 /home\n", $1}' /etc/passwd

Sie können das ähnliche Escape- \x27Zeichen in hexadezimaler Form verwenden, es kann jedoch in einigen Implementierungen falsch interpretiert werden, wenn das folgende Zeichen eine gültige hexadezimale Ziffer ist. (Und natürlich habe ich ASCII oder einen ASCII-kompatiblen Zeichensatz angenommen, z. B. UTF-8.)

ilkkachu
quelle
Beachten Sie, dass es hier in Ordnung ist, da les sich nicht um eine hexadezimale Ziffer handelt, sondern "\x27df\n"wie "\x27" "df"in der Busybox awk oder mawk behandelt wird, sondern als "\xdf"( 0x27dfin 8-Bit- Zeichen umgewandelt ) in der ursprünglichen awk und gawk (dies ist der Grund, warum POSIX dies nicht angibt \xHH). Octals ( \047) haben nicht das gleiche Problem und sind POSIX. Dies setzt in jedem Fall ein ASCII-System voraus (heutzutage eine vernünftige Annahme).
Stéphane Chazelas
6

Verwenden Sie die -f -Option to awk, um das Skript von stdin und einem Here-Dokument zu übernehmen:

awk -F: -v "ID=$ID_minimum" -f - <<'EOT' /etc/passwd
$3>=1000 && $1!="nfsnobody" {
    print "xfs_quota -x -c 'limit bsoft=5g bhard=6g "$1"' /home "
}
EOT
Mosvy
quelle
2

Das hat es geschafft.

awk -F: -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c '"'"'limit bsoft=5g bhard=6g ''"$1"'''"'"' /home "}' /etc/passwd
ZCT
quelle
1
@Jesse_b, ja, Sie können keine einfachen Anführungszeichen in eine Zeichenfolge in einfachen Anführungszeichen setzen. Sie müssen sie also zuerst beenden und dann das einfache Anführungszeichen zitieren, das Sie einfügen möchten. Es spielt keine Rolle, ob Sie '\''oder '"'"'. Beide arbeiten, beide sehen nervig aus.
Ilkkachu
@OP, hoffentlich hast du gesehen, wie Jordanm kommentiert hat, dass deine Schleife unnötig ist. Ihre Schleife wird immer nur einmal ausgeführt, es ist also nichts anderes, als denselben awk-Befehl außerhalb einer Schleife auszuführen.
Jesse_b
1

Es ist ein schrecklicher Trottel, aber es ist schnell und einfach ...

awk -F: -vQ="'" -vID=$ID_minimum '$3>=1000 && $1!="nfsnobody" { print "xfs_quota -x -c " Q "limit bsoft=5g bhard=6g $1" Q " /home "}' $f;       
Grummel
quelle
1

Dies scheint mir eine ideale Gelegenheit zu sein, Sie zu beschäftigen xargs(oder GNU Parallel ):

getent passwd \
  | awk -F: '$3>=1000 && $1!="nfsnobody" {print $1}' \
  | xargs -I{} \
      echo xfs_quota -x -c \"limit bsoft=5g bhard=6g {}\" /home

# output:
# xfs_quota -x -c "limit bsoft=5g bhard=6g userone" /home
# xfs_quota -x -c "limit bsoft=5g bhard=6g usertwo" /home

Der Vorteil der Verwendung von xargsoder parallelbesteht darin, dass Sie den Befehl einfach entfernenecho können , wenn Sie bereit sind, den Befehl tatsächlich auszuführen (ggf. durch ihn zu ersetzen sudo, falls erforderlich).

Sie können auch diese Dienstprogramme verwenden -p/ --interactive(letzteres ist GNU-only) oder --dry-run( parallelnur) Optionen, die Bestätigung zu erhalten , bevor jedes laufen, oder einfach nur , um zu sehen , was würde laufen, bevor Sie es ausführen.

Die oben verwendete allgemeine Methode sollte unter den meisten Unixen funktionieren und erfordert keine GNU-spezifischen xargsOptionen. Die doppelten Anführungszeichen tun Notwendigkeit, „entkommen“ , so dass sie buchstäblich in der Ausgabe erscheinen. Beachten Sie, dass die "Ersatzzeichenfolge" {}in xargs -I{}beliebig sein kann und -Iimpliziert -L1(führen Sie einen Befehl pro Eingabezeile aus, anstatt sie zu stapeln).

GNU Parallel nicht die benötigen -IOption ( {}ist der Standardersatzzeichenfolge), und gibt Ihnen den sofortigen Bonus viele Jobs parallel laufen, auch wenn Sie wollen über nicht die Mühe zu lernen jeder von seinen anderen Funktionen .

Nebenbei bemerkt, ich bin mir nicht einmal sicher, ob xfs_quotadie -cOption so verwendet werden soll, obwohl ich keine XFS-Dateisysteme zum Testen zur Hand habe. Sie haben nicht einmal nötig mit einer Zeichenfolge in Anführungszeichen in erster Linie zu tun (es sei denn , Sie Benutzernamen mit Leerzeichen in ihnen erwarten, was ich kann erraten), da es so aussieht , mehrere geben kann -cOptionen auf der gleichen Befehlszeile nach auf die Manpage mit xfsprogs4.5.something.

TheDudeAbides
quelle
0

Mit GNU Parallel können Sie Folgendes tun:

getent passwd |
  parallel --colsep : -q xfs_quota -x -c \
    'limit bsoft=5g bhard=6g {=1 $_ eq "nfsnobody" and skip(); $arg[3] <= 1000 and skip();  =}' /home

Erläuterung:

--colsep :Split auf:
-qSie spalten den Befehl nicht auf Räume (halten die ‚...‘ als einzelne Zeichenfolge)
{=1 ... =}werten diese Perl - Ausdruck auf dem ersten Argument der Linie
$_ eq "nfsnobody" and skip();Wenn der Wert == nfsnobody: überspringen
$arg[3] <= 1000 and skip();Wenn Argument3 <= 1000: überspringen

Ole Tange
quelle