Ich muss die Ausgabe eines Befehls in meinem Skript in ein Array lesen. Der Befehl lautet zum Beispiel:
ps aux | grep | grep | x
und es gibt die Ausgabe Zeile für Zeile wie folgt:
10
20
30
Ich muss die Werte aus der Befehlsausgabe in ein Array lesen, und dann werde ich einige Arbeiten ausführen, wenn die Größe des Arrays weniger als drei beträgt.
Antworten:
Die anderen Antworten werden brechen , wenn Ausgabe von Kommando Leerzeichen enthält (was ziemlich häufig ist) oder glob Zeichen wie
*
,?
,[...]
.Um die Ausgabe eines Befehls in einem Array mit einer Zeile pro Element zu erhalten, gibt es im Wesentlichen drei Möglichkeiten:
Mit Bash≥4 ist
mapfile
es am effizientesten:Ansonsten eine Schleife, die die Ausgabe liest (langsamer, aber sicher):
Wie von Charles Duffy in den Kommentaren vorgeschlagen (danke!), Könnte Folgendes besser sein als die Schleifenmethode in Nummer 2:
Bitte stellen Sie sicher, dass Sie genau dieses Formular verwenden, dh stellen Sie sicher, dass Sie Folgendes haben:
IFS=$'\n'
in derselben Zeile wie dieread
Anweisung: Dadurch wird nur die UmgebungsvariableIFS
für dieread
Anweisung festgelegt. Es wirkt sich also überhaupt nicht auf den Rest Ihres Skripts aus. Der Zweck dieser Variablen besteht darin, anzugebenread
, dass der Stream beim EOL-Zeichen unterbrochen werden soll\n
.-r
: das ist wichtig. Es wird empfohlenread
, die Backslashes nicht als Escape-Sequenzen zu interpretieren.-d ''
: Bitte beachten Sie den Abstand zwischen der-d
Option und ihrem Argument''
. Wenn Sie hier kein Leerzeichen lassen,''
wird das nie angezeigt, da es im Schritt zum Entfernen von Anführungszeichen verschwindet, wenn Bash die Anweisung analysiert. Dies weistread
an, das Lesen beim Null-Byte zu beenden. Einige Leute schreiben es als-d $'\0'
, aber es ist nicht wirklich notwendig.-d ''
ist besser.-a my_array
weistread
an, das Arraymy_array
beim Lesen des Streams zu füllen.printf '\0'
Anweisung nach verwendenmy_command
, damitread
zurückgegeben wird0
. Es ist eigentlich keine große Sache, wenn Sie dies nicht tun (Sie erhalten nur einen Rückkehrcode1
, der in Ordnung ist, wenn Sie ihn nicht verwendenset -e
- was Sie sowieso nicht sollten), aber denken Sie daran. Es ist sauberer und semantisch korrekter. Beachten Sie, dass dies anders ist als dasprintf ''
, was nichts ausgibt.printf '\0'
Gibt ein Null-Byte aus, das benötigt wirdread
, um dort glücklich mit dem Lesen aufzuhören (erinnern Sie sich an die-d ''
Option?).Wenn Sie können, dh wenn Sie sicher sind, dass Ihr Code auf Bash≥4 ausgeführt wird, verwenden Sie die erste Methode. Und Sie können sehen, dass es auch kürzer ist.
Wenn Sie verwenden möchten,
read
hat die Schleife (Methode 2) möglicherweise einen Vorteil gegenüber Methode 3, wenn Sie beim Lesen der Zeilen eine Verarbeitung durchführen möchten: Sie haben direkten Zugriff darauf (über die$line
Variable in dem von mir angegebenen Beispiel) und Sie haben auch Zugriff auf die bereits gelesenen Zeilen (über das Array${my_array[@]}
in dem Beispiel, das ich gegeben habe).Beachten Sie, dass dies
mapfile
eine Möglichkeit bietet, einen Rückruf für jede gelesene Zeile auswerten zu lassen. Sie können sogar festlegen, dass dieser Rückruf nur alle N gelesenen Zeilen aufgerufen wird . Schauen Sie sichhelp mapfile
die Optionen-C
und die-c
darin enthaltenen an. (Meine Meinung dazu ist, dass es ein bisschen klobig ist, aber manchmal verwendet werden kann, wenn Sie nur einfache Dinge zu tun haben - ich verstehe nicht wirklich, warum dies überhaupt implementiert wurde!).Jetzt werde ich Ihnen erklären, warum die folgende Methode:
ist kaputt, wenn Leerzeichen vorhanden sind:
Dann empfehlen einige Leute, das Problem
IFS=$'\n'
zu beheben:Aber jetzt verwenden wir einen anderen Befehl mit Globs :
Das liegt daran, dass ich eine Datei
t
im aktuellen Verzeichnis habe… und dieser Dateiname mit dem Glob übereinstimmt[three four]
… an dieser Stelle würden einige Leute empfehlenset -f
, Globbing zu deaktivieren. Aber sehen Sie es sich an: Sie müssen es ändernIFS
und verwendenset -f
, um a zu reparieren kaputte Technik (und Sie reparieren sie nicht einmal wirklich)! Dabei kämpfen wir wirklich gegen die Shell und arbeiten nicht mit der Shell .hier arbeiten wir mit der Shell!
quelle
mapfile
, es ist genau das, was ich seit Jahren vermisst habe. Ich denke, die neuesten Versionen vonbash
haben so viele nette neue Funktionen, dass ich nur ein paar Tage damit verbringen sollte, die Dokumente zu lesen und ein schönes Cheatsheet aufzuschreiben.< <(command)
in Shell-Skripten zu verwenden, sollte die Shebang-Zeile lauten#!/bin/bash
- wenn sie als ausgeführt wird#!/bin/sh
, wird bash mit einem Syntaxfehler beendet.bash my_script.sh
und nicht mit dem Befehl sh ausgeführt werdensh my_script.sh
sh
unddash
nicht wissen , über Arrays überhaupt, außer natürlich, für das Positionsparameter-$@
Array).IFS=$'\n' read -r -d '' -a my_array < <(my_command && printf '\0')
dass beide in Bash 3.x ordnungsgemäß funktionieren und auch einen fehlgeschlagenen Exit-Status vonmy_command
bis zum durchlaufenread
.Sie können verwenden
um die Ausgabe des Befehls
<command>
im Array zu speichernmy_array
.Sie können mit auf die Länge dieses Arrays zugreifen
Jetzt wird die Länge in gespeichert
my_array_length
.quelle
VAR="$(<command>)"
und dannmy_array=("$VAR")
odermy_array+=("$VAR")
Stellen Sie sich vor, Sie werden die Dateien und Verzeichnisnamen (unter dem aktuellen Ordner) in ein Array einfügen und dessen Elemente zählen. Das Skript wäre wie;
Sie können dieses Array auch durchlaufen, indem Sie das folgende Skript hinzufügen:
Bitte beachten Sie, dass dies das Kernkonzept ist und die Eingabe zuvor als bereinigt betrachtet wird, dh zusätzliche Zeichen entfernen, leere Zeichenfolgen behandeln usw. (was nicht zum Thema dieses Threads gehört).
quelle