Ich habe Bash-Skripthandbücher gesehen, die die Verwendung von Arrays für die Arbeit mit Dateinamen empfehlen, die Leerzeichen enthalten. DashAsBinSh schlägt jedoch vor, dass Arrays nicht portierbar sind. Daher suche ich nach einer POSIX-kompatiblen Möglichkeit, mit Listen von Dateinamen zu arbeiten, die möglicherweise Leerzeichen enthalten.
Ich bin auf der Suche, das folgende Beispielskript zu ändern, damit es würde echo
foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar
Hier ist das Drehbuch
#!/usr/bin/env sh
INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps
dostuffwith() { echo $1; };
F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)
for f in $ALL_FILES
do
fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
dostuffwith $fpath
done
shell-script
filenames
quoting
posix
whitespace
Eero Aaltonen
quelle
quelle
Antworten:
POSIX Schalen weisen ein Array: die Positionsparameter (
$1
,$2
usw., kollektiv bezeichnet als"$@"
).Dies ist unpraktisch, da es nur eine gibt, und es zerstört jede andere Verwendung der Positionsparameter. Positionsparameter sind lokal für eine Funktion, die manchmal ein Segen und manchmal ein Fluch ist.
Wenn Ihre Dateinamen garantiert keine Zeilenumbrüche enthalten, können Sie Zeilenumbrüche als Trennzeichen verwenden. Wenn Sie die Variable erweitern, deaktivieren Sie zuerst das Globen mit
set -f
und stellen Sie die Liste derIFS
feldaufteilenden Zeichen so ein , dass sie nur eine neue Zeile enthält.Wenn die Elemente in Ihrer Liste durch Zeilenumbrüche getrennt sind, können Sie insbesondere viele Textverarbeitungsbefehle sinnvoll verwenden
sort
.Denken Sie daran, Variablensubstitutionen immer in doppelte Anführungszeichen zu setzen, es sei denn, Sie möchten explizit eine Feldaufteilung durchführen (und auch ein Globbing, sofern Sie dies nicht deaktiviert haben).
quelle
sort | uniq
Schritt wie beabsichtigt funktioniert.Da Ihre
$INPUT
Variable Zeilenumbrüche als Trennzeichen verwendet, gehe ich davon aus, dass Ihre Dateien keine Zeilenumbrüche in den Namen enthalten. Daher gibt es eine einfache Möglichkeit, die Dateien zu durchlaufen und Leerzeichen beizubehalten.Die Idee ist, die
read
eingebaute Shell zu verwenden. Normalerweiseread
teilen sich Leerzeichen in Leerzeichen auf und Leerzeichen brechen sie auf. Aber Sie können festlegen,IFS=$'\n'
und es wird stattdessen nur in Zeilenumbrüchen aufgeteilt. So können Sie jede Zeile in Ihrer Liste durchlaufen.Hier ist die kleinste Lösung, die ich finden konnte:
Grundsätzlich sendet es "$ INPUT" an
awk
die Deduplikate basierend auf dem Dateinamen (es teilt sich auf/
und druckt dann die Zeile, wenn das letzte Element noch nicht gesehen wurde). Sobald awk die Liste der Dateipfade erstellt hat,while read
durchlaufen wir die Liste.quelle
while
Schleife und damitdostuffwith
in einer Subshell ausgeführt wird. So gehen alle Variablen oder Änderungen an der laufenden Shell verloren, wenn die Schleife abgeschlossen ist. Die einzige Alternative ist die Verwendung eines vollständigen Heredocs, was nicht so unangenehm ist, aber ich dachte, dies wäre vorzuziehen.IFS="\n"
Aufteilung auf Backslash und n Zeichen. Aber inread file
gibt es keine Spaltung.IFS="\n"
ist immer noch nützlich, da es die leeren Zeichen aus $ IFS entfernt, die andernfalls am Anfang und Ende der Eingabe entfernt worden wären. Um eine Zeile zu lesen, die kanonische Syntax istIFS= read -r line
, obwohlIFS=anything read -r line
(vorausgesetzt , alles enthält keine Leerzeichen) wird auch funktionieren.