list=`ls -a R*`
echo $list
In einem Shell-Skript listet dieser Echo-Befehl alle Dateien aus dem aktuellen Verzeichnis auf, beginnend mit R, jedoch in einer Zeile. Wie kann ich jeden Artikel in einer Zeile drucken?
Ich brauche einen allgemeinen Befehl für alle Szenarien geschieht mit ls
, du
, find -type -d
etc.
Antworten:
Wenn die Ausgabe des Befehls mehrere Zeilen enthält, zitieren Sie Ihre Variablen, um diese Zeilenumbrüche beim Echo beizubehalten:
Andernfalls wird die Shell den Inhalt der Variablen in Leerzeichen (Leerzeichen, Zeilenumbrüche, Tabulatoren) erweitern und aufteilen, und diese gehen verloren.
quelle
Anstatt die
ls
Ausgabe schlecht in eine Variable einzufügen und sie dann zu bearbeitenecho
, wodurch alle Farben entfernt werden, verwenden SieVon
man ls
Ich rate dir nicht, irgendetwas mit der Ausgabe von zu tun
ls
, außer sie anzuzeigen :)Verwenden Sie zum Beispiel Shell-Globs und eine
for
Schleife, um etwas mit Dateien zu tun ...* Dies wird das aktuelle Verzeichnis nicht enthalten
.
oder seine Eltern ,..
obwohlquelle
echo "$i"
mitprintf "%s\n" "$i"
(dass man nicht erstickt, wenn ein Dateiname mit einem "-" beginnt). Eigentlich sollte die meiste Zeitecho something
durch ersetzt werdenprintf "%s\n" "something"
.R
hier an, aber im Allgemeinen ja. Doch selbst bei Skripten verursacht der Versuch, immer die führenden-
Pfade zu berücksichtigen, Probleme: Die Leute gehen davon aus--
, dass nur einige Befehle das Ende der Optionen bedeuten . Ich habe gesehen,find . [tests] -exec [cmd] -- {} \;
wo[cmd]
nicht unterstützt--
. Jeder Weg dorthin beginnt.
sowieso mit! Aber das ist kein Argument gegen das Ersetzenecho
durchprintf '%s\n'
. Hier kann man die gesamte Schleife durch ersetzenprintf '%s\n' R/*
. Bash istprintf
eingebaut, daher gibt es praktisch keine Begrenzung für die Anzahl / Länge der Argumente.Wenn Sie es in Anführungszeichen setzen, wie von @muru vorgeschlagen, wird dies tatsächlich das tun, wonach Sie gefragt haben. Vielleicht möchten Sie auch ein Array dafür verwenden. Zum Beispiel:
Die
IFS=$'\n'
Anweisung bash, die Ausgabe nur auf Zeilenumbrüche aufzuteilen, um jedes Element des Arrays abzurufen. Ohne es wird es auf Leerzeichen aufgeteilt, alsoa file name with spaces.txt
wären 5 separate Elemente anstelle von einem. Dieser Ansatz wird jedoch unterbrochen, wenn Ihre Datei- / Verzeichnisnamen newlines (\n
) enthalten können . Jede Zeile der Befehlsausgabe wird als Element des Arrays gespeichert.Beachten Sie, dass ich auch die alten Stil geändert
`command`
zu$(command)
denen ist die bevorzugte Syntax.Sie haben jetzt ein Array mit dem Namen
$dirs
jedes Bof, dessen Elemente eine Zeile der Ausgabe des vorherigen Befehls sind. Zum Beispiel:Aufgrund einiger Bash-Seltsamkeiten (siehe hier ) müssen Sie danach
IFS
auf den ursprünglichen Wert zurücksetzen . Also entweder speichern und neu signieren:Oder machen Sie es manuell:
Alternativ schließen Sie einfach das aktuelle Terminal. Bei Ihrem neuen wird das ursprüngliche IFS erneut eingestellt.
quelle
du
OP sieht mehr danach aus, das Ausgabeformat beizubehalten.Wenn Sie die Ausgabe speichern müssen und ein Array möchten,
mapfile
ist dies einfach.Überlegen Sie zunächst, ob Sie die Ausgabe Ihres Befehls überhaupt speichern müssen . Wenn Sie dies nicht tun, führen Sie einfach den Befehl aus.
Wenn Sie die Ausgabe eines Befehls als Zeilenarray lesen möchten, können Sie das Globbing deaktivieren, das Aufteilen
IFS
in Zeilen festlegen , die Befehlssubstitution in der(
)
Syntax zur Arrayerstellung verwenden und anschließend zurücksetzenIFS
. Das ist komplexer als es scheint. Terdons Antwort deckt einen Teil dieses Ansatzes ab. Ich schlage jedoch vor, dass Sie stattdessen den inmapfile
die Bash-Shell integrierten Befehl zum Lesen von Text als Zeilenarray verwenden. Hier ist ein einfacher Fall, in dem Sie aus einer Datei lesen:Dies liest Zeilen in ein Array namens
MAPFILE
. Ohne die Umleitung der Eingabe würden Sie anstelle der von benannten Datei von der Standardeingabe der Shell (normalerweise Ihrem Terminal) lesen . Übergeben Sie den Namen, um in ein anderes als das Standardarray einzulesen . Dies liest beispielsweise in das Array ein :< filename
filename
MAPFILE
lines
Ein weiteres Standardverhalten, das Sie möglicherweise ändern möchten, besteht darin, dass die Zeilenumbrüche an den Zeilenenden beibehalten werden. Sie werden als letztes Zeichen in jedem Array-Element angezeigt (es sei denn, die Eingabe endete ohne ein Zeilenumbruchzeichen. In diesem Fall hat das letzte Element kein Zeichen). Um diese Zeilenumbrüche chomp , so dass sie in dem Array nicht angezeigt werden , übergeben Sie die
-t
Optionmapfile
. Dies liest beispielsweise in das Arrayrecords
und schreibt keine nachgestellten Zeilenumbrüche in seine Array-Elemente:Sie können auch verwenden,
-t
ohne einen Array-Namen zu übergeben. Das heißt, es funktioniert auch mit einem impliziten Array-Namen vonMAPFILE
.Die integrierte
mapfile
Shell unterstützt andere Optionen und kann alternativ als aufgerufen werdenreadarray
. Führen Siehelp mapfile
(undhelp readarray
) für Details aus.Sie möchten jedoch nicht aus einer Datei lesen, sondern aus der Ausgabe eines Befehls. Verwenden Sie dazu die Prozessersetzung . Dieser Befehl liest Zeilen aus dem Befehl
some-command
mitarguments...
seinen Befehlszeilenargumenten und platziert sie immapfile
StandardarrayMAPFILE
, wobei die abschließenden Zeilenumbruchzeichen entfernt werden:Die Prozessersetzung wird durch einen tatsächlichen Dateinamen ersetzt, aus dem die Ausgabe von running gelesen werden kann. Die Datei ist eher eine Named Pipe als eine reguläre Datei. Unter Ubuntu wird sie wie folgt benannt (manchmal mit einer anderen Nummer als ), aber Sie müssen sich nicht um die Details kümmern, da sich die Shell darum kümmert alles hinter den Kulissen.
<(some-command arguments...)
some-command arguments...
/dev/fd/63
63
Sie könnten denken, Sie könnten auf die Prozessersetzung verzichten, indem Sie stattdessen verwenden, aber das funktioniert nicht, da Bash , wenn Sie eine Pipeline mit mehreren durch getrennten Befehlen haben , alle Befehle in Subshells ausführt . Somit werden beide und in ihren eigenen Umgebungen ausgeführt , die von der Umgebung der Shell, in der Sie die Pipeline ausführen, initialisiert, aber von dieser getrennt sind. In der Subshell, in der ausgeführt wird, wird das Array zwar gefüllt , aber dieses Array wird verworfen, wenn der Befehl endet. wird niemals für den Anrufer erstellt oder geändert.
some-command arguments... | mapfile -t
|
some-command arguments...
mapfile -t
mapfile -t
MAPFILE
MAPFILE
So sieht das Beispiel in Terdons Antwort vollständig aus, wenn Sie Folgendes verwenden
mapfile
:Das ist es. Sie müssen nicht überprüfen
IFS
, ob und mit welchem Wert festgelegt wurde, ob es nicht festgelegt wurde, auf eine neue Zeile setzen und später zurücksetzen oder wieder deaktivieren. Sie müssen das Globbing nicht deaktivieren (z. B. mitset -f
) - was wirklich erforderlich ist, wenn Sie diese Methode ernsthaft verwenden möchten , da Dateinamen möglicherweise enthalten*
,?
und - und[
anschließend erneut aktivieren (set +f
).Sie können diese bestimmte Schleife auch vollständig durch einen einzelnen
printf
Befehl ersetzen - obwohl dies eigentlich kein Vorteil istmapfile
, da Sie dies unabhängig davon tun können, ob Siemapfile
das Array oder eine andere Methode zum Auffüllen des Arrays verwenden. Hier ist eine kürzere Version:Es ist wichtig zu beachten, dass, wie Terdon erwähnt , das zeilenweise Arbeiten nicht immer angemessen ist und mit Dateinamen, die Zeilenumbrüche enthalten, nicht ordnungsgemäß funktioniert. Ich empfehle, Dateien nicht so zu benennen, aber es kann passieren, auch aus Versehen.
Es gibt keine einheitliche Lösung.
Sie haben nach "einem generischen Befehl für alle Szenarien" und dem Ansatz gefragt,
mapfile
etwas näher an dieses Ziel heranzugehen, aber ich fordere Sie auf, Ihre Anforderungen zu überdenken.Die oben gezeigte Aufgabe wird besser mit nur einem einzigen
find
Befehl erreicht:Sie können auch einen externen Befehl verwenden , der am Anfang jeder Zeile
sed
hinzugefügtDIR:
werden soll. Dies ist wohl etwas hässlich, und im Gegensatz zu diesem Befehl find werden zusätzliche "Präfixe" in Dateinamen hinzugefügt, die Zeilenumbrüche enthalten. Es funktioniert jedoch unabhängig von der Eingabe, sodass es Ihren Anforderungen entspricht, und es ist immer noch vorzuziehen, die Ausgabe in a einzulesen Variable oder Array :Wenn Sie für jedes gefundene Verzeichnis eine Aktion auflisten und ausführen müssen, z. B. das Ausführen
some-command
und Übergeben des Verzeichnispfads als Argument,find
können Sie dies auch tun:Kehren wir als weiteres Beispiel zur allgemeinen Aufgabe zurück, jeder Zeile ein Präfix hinzuzufügen. Angenommen, ich möchte die Ausgabe von sehen,
help mapfile
aber die Zeilen nummerieren. Ich würdemapfile
dafür weder eine andere Methode verwenden, die es in eine Shell-Variable oder ein Shell-Array einliest. Angenommen,help mapfile | cat -n
es wird nicht die gewünschte Formatierung angegeben, kann ich Folgendes verwendenawk
:Das Einlesen der gesamten Ausgabe eines Befehls in eine einzelne Variable oder ein einzelnes Array ist manchmal nützlich und angemessen, hat jedoch große Nachteile. Sie müssen sich nicht nur mit der zusätzlichen Komplexität der Verwendung Ihrer Shell auseinandersetzen, wenn ein vorhandener Befehl oder eine Kombination vorhandener Befehle möglicherweise bereits das tut, was Sie benötigen, oder besser, sondern die gesamte Ausgabe des Befehls muss im Speicher gespeichert werden. Manchmal wissen Sie vielleicht, dass dies kein Problem ist, aber manchmal verarbeiten Sie eine große Datei.
Eine Alternative, die häufig versucht wird - und manchmal richtig gemacht wird -, besteht darin, die Eingabe Zeile für Zeile
read -r
in einer Schleife zu lesen . Wenn Sie während der Bearbeitung späterer Zeilen keine vorherigen Zeilen speichern müssen und lange Eingaben verwenden müssen, ist dies möglicherweise besser alsmapfile
. Aber es sollte auch in Fällen vermieden werden, in denen Sie es einfach an einen Befehl weiterleiten können, der die Arbeit erledigen kann, was in den meisten Fällen der Fall ist .quelle