bash - czy mogę: znaleźć… -wykonać to i & tamto?

23

Czy istnieje sposób na logiczne połączenie dwóch poleceń powłoki, które są wywoływane przez find - exec ?

Na przykład, aby wydrukować wszystkie pliki .csv , które zawierają ciąg foo wraz z jego wystąpieniem, chciałbym zrobić:

find . -iname \*.csv -exec grep foo {} && echo {} \;

ale bash narzeka na „brakujący argument na„ -exec ””

Marcus Junius Brutus
źródło
2
Możesz użyć 2 -execw sekwencji lub użyć jednego -exec sh -c 'grep foo "$0" && printf %s\\n "$0"' {} \;.
jw013,
Powtarzało mi się to wielokrotnie: zawsze oczekuję, że pierwszy przekazany argument sh(w tym przypadku {}) będzie $1i $0będzie podobny sh. Ale w rzeczywistości masz rację, pierwszy argument pojawia się jako $0. Posiadanie pierwszego argumentu jako nazwy polecenia wywołującego jest tylko konwencją, która w takich przypadkach nie jest automatycznie egzekwowana.
dubiousjim
Może powinien zostać połączony z tym pytaniem: unix.stackexchange.com/q/18077/4801
dubiousjim

Odpowiedzi:

24

-execjest predykatem, który uruchamia polecenie (nie powłokę) i zwraca wartość true lub false na podstawie wyniku polecenia (zero lub niezerowy status wyjścia).

Więc:

find . -iname '*.csv' -exec grep foo {} \; -print

by wydrukować ścieżkę do pliku, czy grepznaleziska foo w pliku. Zamiast tego -printmożesz użyć innego -execpredykatu lub dowolnego innego predykatu

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

Zobacz także !i -oznajdź operatory negacji i lub .

Alternatywnie możesz uruchomić powłokę jako:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

Lub, aby uniknąć konieczności uruchamiania powłoki dla każdego pliku:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +
Stéphane Chazelas
źródło
10

Problem, przed którym stoisz, polega na tym, że powłoka najpierw analizuje wiersz poleceń i widzi dwa proste polecenia oddzielone przez &&operatora: find . -iname \*.csv -exec grep foo {}i echo {} \;. Cytując &&( find . -iname \*.csv -exec grep foo {} '&&' echo {} \;) omija, ale teraz komenda wykonywana przez findto coś grepz argumentami foo, wibble.csv, &&, echoi wibble.csv. Musisz poinstruować, findaby uruchomić powłokę, która zinterpretuje &&operatora:

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

Zauważ, że pierwszy argument po sh -c SOMECOMMANDto $0, nie $1.

Możesz zaoszczędzić czas uruchamiania procesu powłoki dla każdego pliku, grupując wywołania poleceń -exec … +. Aby ułatwić przetwarzanie, podaj pewną wartość zastępczą, $0aby "$@"wyliczała nazwy plików.

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

Jeśli polecenie powłoki dzieli tylko dwa programy &&, findmoże wykonać zadanie samodzielnie: napisz dwie kolejne -execakcje, a druga zostanie wykonana tylko wtedy, gdy pierwsza zakończy pracę ze statusem 0.

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(Zakładam, że grepi echosą tylko w celach ilustracyjnych, ponieważ -exec echomożna je zastąpić, -printa wynikowy wynik i tak nie jest szczególnie użyteczny.)

Gilles „SO- przestań być zły”
źródło
2
Zazwyczaj unikam używania „0 $”, ponieważ jest on również używany przez powłokę do wyświetlania komunikatów o błędach. Na przykład może pojawić się mylący ./some-file: grep: command not foundkomunikat o błędzie. -exec find sh -c '... "$1"' sh {} \;nie miałbym problemu. W drugim poleceniu find znajduje się literówka (związana).
Stéphane Chazelas
3

W tym konkretnym przypadku zrobiłbym:

find . -iname \*.csv -exec grep -l foo \{\} \;

Lub jeśli masz ACK :

ack -al -G '.*\.csv' foo

Aby odpowiedzieć na twoje aktualne pytanie, coś takiego może działać:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;

Dennis Kaarsemaker
źródło
3
Ta ostatnia komenda find jest nie tylko nieprzenośna, ale także bardzo niebezpieczna, ponieważ ścieżki plików są ostatecznie oceniane jako kod powłoki.
Stéphane Chazelas
Tym bardziej należy tego unikać, używając poprawnie ack / grep :)
Dennis Kaarsemaker 13.11. O
1
Alternatywa jw013 (podana w komentarzu do pytania) jest pod tym względem bezpieczniejsza. (Zauważyłem, że odpowiedzi Gillesa i Stephane'a wykorzystują tę samą technikę.)
dubiousjim