Najpierw odetnij banalne, ale nie dające się zastosować odpowiedzi: nie mogę użyć ani sztuczki find
+, xargs
ani jej wariantów (podobnie jak w find
przypadku -exec
), ponieważ muszę użyć kilku takich wyrażeń na rozmowę. Wrócę do tego na końcu.
Teraz dla lepszego przykładu zastanówmy się:
$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Jak przekazać je jako argumenty program
?
Samo robienie tego nie załatwia sprawy
$ ./program $(find -L some/dir -name \*.abc | sort)
kończy się niepowodzeniem, ponieważ program
otrzymuje następujące argumenty:
[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc
Jak widać, ścieżka z przestrzenią została podzielona i program
uważa, że to dwa różne argumenty.
Cytuj, aż to zadziała
Wydaje się, że początkujący użytkownicy tacy jak ja, w obliczu takich problemów, mają tendencję do losowego dodawania cudzysłowów, aż w końcu to działa - tylko tutaj nie wydaje się, aby to pomogło…
"$(…)"
$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Ponieważ cudzysłowy uniemożliwiają dzielenie słów, wszystkie pliki są przekazywane jako pojedynczy argument.
Cytując poszczególne ścieżki
Obiecujące podejście:
$ ./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"
Oczywiście są tam cytaty. Ale nie są już interpretowane. Są tylko częścią strun. Więc nie tylko nie zapobiegli podziałowi słów, ale także wdali się w kłótnie!
Zmień IFS
Potem próbowałem się bawić IFS
. Wolałbym find
się -print0
i sort
ze -z
i tak - tak, że będą mieli żadnych problemów na „wired” samych ścieżkach. Dlaczego więc nie wymusić podziału słów na null
postać i mieć to wszystko?
$ ./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
Więc nadal dzieli się na przestrzeń i nie dzieli na null
.
Próbowałem umieścić IFS
zadanie zarówno w $(…)
(jak pokazano powyżej), jak i wcześniej ./program
. Próbowałem również inne składni jak \0
, \x0
, \x00
zarówno cytowana '
i "
jak również i bez $
. Żadne z nich nie miało znaczenia…
I tutaj brakuje mi pomysłów. Próbowałem jeszcze kilku rzeczy, ale wszystkie wydawały się sprowadzać do tych samych problemów, co wymienione.
Co jeszcze mogłem zrobić? Czy to w ogóle wykonalne?
Jasne, mógłbym program
zaakceptować wzorce i sam przeszukać. Ale jest to dużo podwójnej pracy przy ustalaniu określonej składni. (A co grep
na przykład z udostępnianiem plików ?).
Mógłbym również program
zaakceptować plik z listą ścieżek. Następnie mogę łatwo zrzucić find
wyrażenie do pliku tymczasowego i podać ścieżkę tylko do tego pliku. Może to być obsługiwane wzdłuż bezpośrednich ścieżek, dzięki czemu jeśli użytkownik ma tylko prostą ścieżkę, można ją podać bez pośredniego pliku. Ale to nie wydaje się miłe - trzeba stworzyć dodatkowe pliki i się nimi zająć, nie wspominając o dodatkowej implementacji. (Na plus można jednak ratować przypadki, w których liczba plików jako argumentów zaczyna powodować problemy z długością wiersza poleceń…)
Na koniec jeszcze raz przypomnę, że sztuczki find
+ xargs
(i podobne) nie będą działać w moim przypadku. Dla uproszczenia opisu pokazuję tylko jeden argument. Ale mój prawdziwy przypadek wygląda mniej więcej tak:
$ 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
Więc wykonanie xargs
jednego wyszukiwania wciąż pozostawia mi sposób radzenia sobie z drugim…
źródło
mapfile
(ani jego synonimiereadarray
). Ale to działa!while
pętlą nie czyści tablicy. Co oznacza, że jeśli nie zostaną znalezione żadne pliki, tablica jest niezdefiniowana. Chociaż jeśli jest już zdefiniowany, nowe pliki zostaną dodane (zamiast zastępować stare). Wygląda na to, że dodawaniedeclare -a ABC_FILES='()';
wcześniejwhile
działa. (Podczas gdy tylko dodawanieABC_FILES='()';
tego nie robi.)< <
oznacza tutaj? Czy to to samo co<<
? Nie wydaje mi się, żeby zmiana na<<
zwróciła błąd składniowy („nieoczekiwany token” („”). Więc co to jest i jak działa?ABC_FILES
. W porządku. Przydatne jest jednak także określenie,ABS_ARGS
która jest pustą tablicą, jeśliABC_FILES
jest pusta lub inaczej jest tablicą('--abc-files' "${ABC_FILES[@]}")
. W ten sposób później mogę go używać w następujący sposób:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"
i upewnij się, że będzie działał poprawnie bez względu na to, która grupa (jeśli w ogóle) jest pusta. Lub inaczej: to ten sposób--abc-files
(i--xyz-files
) będzie zapewniony tylko wtedy, gdy będzie po nim jakaś rzeczywista ścieżka.while read ... done < <(find blah)
jest normalnym przekierowaniem powłoki<
ze specjalnego pliku utworzonego przez PROCES SUBSTITUTION . Różni się to od pipingu,find blah | while read ... done
ponieważ potok uruchamiawhile
pętlę w podpowłoce, więc ustawione w nim zmienne nie są zachowywane dla kolejnych poleceń.Możesz użyć IFS = nowa linia (zakładając, że żadne nazwy plików nie zawierają nowej linii), ale musisz ustawić ją w zewnętrznej powłoce PRZED podmiany:
Z,
zsh
ale niebash
możesz również użyć null$'\0'
. Nawet wbash
tobie poradzisz sobie z nową linią, jeśli istnieje wystarczająco dziwna postać, która nigdy nie jest używanaJednak to podejście nie obsługuje dodatkowego żądania złożonego w komentarzach do odpowiedzi @ steeldriver, aby pominąć --afiles, jeśli find jest pusty.
źródło
IFS
do podziałunull
?read -d ''
w metodach steeldriver użyto pustego ciągu, który nie zawiera bajtu zerowego. (A zresztą opcja polecenia nie jest var jako taka.)set -o noglob
) przed użyciem operatora split + glob (z wyjątkiem inzsh
).$'\0'
i również''
.Nie jestem pewien, czy rozumiem, dlaczego się poddałeś
xargs
.Ciąg
--xyz-files
jest tylko jednym z wielu argumentów i nie ma powodu, aby uważać go za specjalny, zanim zostanie zinterpretowany przez twój program. Myślę, że można to przełożyćxargs
między obafind
wyniki:źródło
-print0
w sekundęfind
. Także jeśli pójdę w ten sposób, postaram się--abc-files
toecho
również - dla zachowania spójności..abc
plików, to nie powinno też być--abc-files
(tak samo z.xyz
). Rozwiązanie oparte na tablicy przez steeldriver również wymaga dodatkowej logiki dla niego jednak, że logika nie jest trywialny, gdy może być nie tak trywialne tutaj niszcząc Główną zaletą tego rozwiązania - prostota.xargs
nigdy nie spróbuję podzielić argumentów i wykonać kilku poleceń zamiast jednego, chyba że zostanie to wyraźnie polecone, aby użyć argumentów-L
,--max-lines
(-l
),--max-args
(-n
) lub--max-chars
(-s
). Czy mam rację? Czy są jakieś domyślne? Ponieważ mój program nie poradziłby sobie z takim podziałem poprawnie i wolałbym nie nazwać go ...-print0
- naprawiono, dziękuję. Nie znam wszystkich odpowiedzi, ale zgadzam się, że moje rozwiązanie utrudnia włączenie dodatkowej logiki. Prawdopodobnie sam bym poszedł z tablicami, teraz, kiedy znam to podejście. Moja odpowiedź nie była dla ciebie. Przyjąłeś już drugą odpowiedź i założyłem, że twój problem został rozwiązany. Chciałem tylko zaznaczyć, że można przekazywać argumenty z wielu źródełxargs
, co na pierwszy rzut oka nie było oczywiste. Możesz potraktować to jako dowód koncepcji. Teraz wszyscy znamy kilka różnych podejść i możemy świadomie wybrać to, co pasuje do nas w każdym konkretnym przypadku.--abc-files
). Ale masz rację - dobrze jest znać swoje alternatywy! Zwłaszcza, że błędnie myślałem, że to niemożliwe.