Zuerst, um triviale, aber nicht zutreffende Antworten abzuschneiden: Ich kann weder den find
+ xargs
Trick noch seine Varianten (wie find
bei -exec
) verwenden, da ich pro Aufruf nur wenige solcher Ausdrücke verwenden muss. Ich werde am Ende darauf zurückkommen.
Betrachten wir nun für ein besseres Beispiel:
$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Wie gebe ich diese als Argumente weiter program
?
Nur das zu tun, reicht nicht aus
$ ./program $(find -L some/dir -name \*.abc | sort)
schlägt fehl, da program
folgende Argumente abgerufen werden:
[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc
Wie zu sehen ist, wurde der Pfad mit dem Leerzeichen aufgeteilt und program
betrachtet ihn als zwei verschiedene Argumente.
Zitieren, bis es funktioniert
Es scheint, dass Anfänger wie ich, wenn sie mit solchen Problemen konfrontiert sind, dazu neigen, zufällig Anführungszeichen hinzuzufügen, bis es schließlich funktioniert - nur hier scheint es nicht zu helfen ...
"$(…)"
$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Da die Anführungszeichen das Aufteilen von Wörtern verhindern, werden alle Dateien als einzelnes Argument übergeben.
Einzelne Pfade zitieren
Ein vielversprechender Ansatz:
$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"
Die Zitate sind da, klar. Sie werden aber nicht mehr interpretiert. Sie sind nur ein Teil der Saiten. Sie haben also nicht nur die Wortteilung nicht verhindert, sondern auch Streitigkeiten geführt!
IFS ändern
Dann habe ich versucht, damit herumzuspielen IFS
. Ich würde es sowieso find
mit -print0
und sort
mit bevorzugen -z
- damit sie selbst keine Probleme mit "verdrahteten Pfaden" haben. Warum also nicht dem null
Charakter die Wortteilung aufzwingen und alles haben?
$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc
Es teilt sich also immer noch im Raum und nicht im null
.
Ich habe versucht, die IFS
Aufgabe sowohl in $(…)
(wie oben gezeigt) als auch zuvor zu platzieren ./program
. Auch habe ich versucht , andere Syntax wie \0
, \x0
, \x00
beide zitiert mit '
und "
sowie mit und ohne $
. Keiner von diesen schien einen Unterschied zu machen ...
Und hier habe ich keine Ideen mehr. Ich habe noch ein paar Dinge ausprobiert, aber alle schienen auf die gleichen Probleme wie die aufgelisteten zurückzuführen zu sein.
Was könnte ich noch tun? Ist es überhaupt machbar?
Klar, ich könnte program
die Muster akzeptieren lassen und selbst suchen. Aber es ist eine Menge Doppelarbeit, wenn man es auf eine bestimmte Syntax fixiert. (Was ist grep
zum Beispiel mit der Bereitstellung von Dateien durch a ?)
Außerdem könnte ich das program
Akzeptieren einer Datei mit einer Liste von Pfaden veranlassen. Dann kann ich den find
Ausdruck einfach in eine temporäre Datei kopieren und nur den Pfad zu dieser Datei angeben. Dies kann auf direkten Pfaden unterstützt werden, sodass Benutzer, die nur einen einfachen Pfad haben, diese ohne Zwischendatei bereitstellen können. Aber das scheint nicht schön zu sein - man muss zusätzliche Dateien erstellen und sich um sie kümmern, ganz zu schweigen von der zusätzlichen Implementierung. (Auf der positiven Seite könnte dies jedoch eine Rettung für Fälle sein, in denen die Anzahl der Dateien als Argumente Probleme mit der Befehlszeilenlänge verursacht…)
Lassen Sie mich am Ende noch einmal daran erinnern, dass find
+ xargs
(und ähnliche) Tricks in meinem Fall nicht funktionieren. Zur Vereinfachung der Beschreibung zeige ich nur ein Argument. Aber mein wahrer Fall sieht eher so aus:
$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES
Wenn ich also xargs
von einer Suche aus mache, habe ich immer noch die Frage, wie ich mit der anderen umgehen soll ...
quelle
mapfile
(oder sein Synonymreadarray
). Aber es funktioniert!while
Schleife löscht das Array nicht. Das heißt, wenn keine Dateien gefunden werden, ist das Array undefiniert. Wenn dies bereits definiert ist, werden neue Dateien angehängt (anstatt die alten zu ersetzen). Es scheint, dass das Hinzufügendeclare -a ABC_FILES='()';
vorherwhile
den Trick macht. (Während nur das HinzufügenABC_FILES='()';
nicht tut.)< <
hier? Ist es das gleiche wie<<
? Ich denke nicht, dass eine Änderung zu einem<<
Syntaxfehler führt ("unerwartetes Token" (")"). Also, was ist es und wie funktioniert es?ABC_FILES
. Das ist gut. Es ist jedoch nützlich, auchABS_ARGS
ein leeres Array zu erstellen, wennABC_FILES
es leer oder ein Array ist('--abc-files' "${ABC_FILES[@]}")
. Auf diese Weise kann ich es später folgendermaßen verwenden:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"
und sicherstellen, dass es ordnungsgemäß funktioniert, unabhängig davon, welche der Gruppen (falls vorhanden) leer ist. Oder anders ausgedrückt: Dieser Weg--abc-files
(und--xyz-files
) wird nur bereitgestellt, wenn ihm ein tatsächlicher Pfad folgt.while read ... done < <(find blah)
ist eine normale Shell-Umleitung<
aus einer speziellen Datei, die von PROCESS SUBSTITUTION erstellt wurde . Dies unterscheidet sich von Piping dadurch,find blah | while read ... done
dass die Pipeline diewhile
Schleife in einer Subshell ausführt, sodass die darin festgelegten Variablen für nachfolgende Befehle nicht beibehalten werden.Sie können IFS = newline verwenden (vorausgesetzt, keine Dateinamen enthalten newline), aber Sie müssen es vor der Ersetzung in der äußeren Shell festlegen:
Mit,
zsh
aber nichtbash
, können Sie auch null verwenden$'\0'
. Sogar inbash
könnte man mit Zeilenumbruch umgehen, wenn es einen ausreichend seltsamen Charakter gibt, der niemals so verwendet wirdDieser Ansatz behandelt jedoch nicht die zusätzliche Anforderung, die Sie in Kommentaren zur Antwort von @ steeldriver gestellt haben, die --afiles wegzulassen, wenn find a leer ist.
quelle
IFS
eine Spaltung zu erzwingennull
?read -d ''
in steeldriver verwendete Methode eine leere Zeichenfolge ist, die kein Null-Byte enthält. (Und eineset -o noglob
) deaktivieren, bevor Sie diesen split + glob-Operator verwenden (außer inzsh
).$'\0'
und auch als''
.Ich bin mir nicht sicher, warum du aufgegeben hast
xargs
.Die Zeichenfolge
--xyz-files
ist nur eines von vielen Argumenten, und es gibt keinen Grund, sie als besonders zu betrachten, bevor sie von Ihrem Programm interpretiert wird. Ich denke, Sie können esxargs
zwischen beidenfind
Ergebnissen durchgehen :quelle
-print0
zweiten Platz verpasst habenfind
. Auch wenn ich diesen Weg gehe, würde ich das auch--abc-files
alsecho
- nur aus Gründen der Konsistenz..abc
Dateien vorhanden sind, auch keine vorhanden sein sollten--abc-files
(dasselbe gilt für.xyz
). Die Array-basierte Lösung von Steeldriver erfordert auch zusätzliche Logik, aber diese Logik ist dort trivial, während sie hier möglicherweise nicht so trivial ist und den Hauptvorteil dieser Lösung zerstört - die Einfachheit.xargs
nie Argumente werden versuchen , zu teilen und einige Befehle statt zu machen, es sei denn , es wird ausdrücklich dazu aufgefordert werden so mit-L
,--max-lines
(-l
),--max-args
(-n
) oder--max-chars
(-s
) Argumente. Habe ich recht? Oder gibt es einige Standardeinstellungen? Da mein Programm eine solche Aufteilung nicht richtig handhaben würde und ich es lieber nicht nennen würde ...-print0
- behoben, danke. Ich kenne nicht alle Antworten, aber ich stimme zu, dass meine Lösung es schwierig macht, zusätzliche Logik aufzunehmen. Ich würde wahrscheinlich selbst mit Arrays gehen, wenn ich diesen Ansatz kenne. Meine Antwort war nicht wirklich für dich. Sie hatten die andere Antwort bereits akzeptiert und ich nahm an, dass Ihr Problem gelöst ist. Ich wollte nur darauf hinweisen, dass Sie Argumente aus mehreren Quellen weitergeben könnenxargs
, was auf den ersten Blick nicht offensichtlich war. Sie können es als Proof of Concept behandeln. Jetzt kennen wir alle nur noch wenige unterschiedliche Ansätze und können bewusst auswählen, was in jedem Einzelfall zu uns passt.--abc-files
). Aber Sie haben Recht - es ist gut, Ihre Alternativen zu kennen! Vor allem, dass ich fälschlicherweise dachte, es sei nicht möglich.