Come faccio a fallire la ricerca se fallisce -exec?

23

Quando eseguo questo comando nella shell (in una directory non vuota):

find . -exec invalid_command_here {} \;

Ho capito:

find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory

(e così via per ogni file)

Ho bisogno di find per fallire dopo il primo errore. C'è un modo per farlo funzionare? Non riesco a utilizzare xargs , perché ho spazi nel mio percorso, ma ho bisogno che lo script chiami questo per restituire un codice di errore.

    
posta Steven Fisher 18.04.2012 - 22:10
fonte

3 risposte

29

Questa è una limitazione di find . Lo standard POSIX specifica che lo stato di ritorno di find è 0 a meno che non si sia verificato un errore durante l'attraversamento le directory; lo stato di ritorno dei comandi eseguiti non vi entra.

Puoi fare in modo che i comandi scrivano il loro stato su un file o su un descrittore:

find_status_file=$(mktemp findstatus)
: >"$find_status_file"
find … -exec sh -c 'trap "echo \$?" EXIT; invalid_command "$0"' {} \;
if [ -s "$find_status_file" ]; then
  echo 1>&2 "An error occurred"
fi
rm -f "$find_status_file"

Un altro metodo, come te scoperto , usa xargs. I comandi xargs elaborano sempre tutti i file, ma restituiscono lo stato 1 se uno qualsiasi dei comandi restituisce uno stato diverso da zero.

find … -print0 | xargs -0 -n1 invalid_command

Ancora un altro metodo consiste nel rifuggire find e utilizzare invece il globbing ricorsivo nella shell: **/ indica qualsiasi profondità di sottodirectory. Ciò richiede la versione 4 o successiva di bash; macOS è bloccato alla versione 3.x quindi dovresti installarlo da una collezione di porte. Usa set -e per fermare lo script sul primo comando che restituisce uno stato diverso da zero.

shopt -s globstar
set -e
for x in **/*.xml; do invalid_command "$x"; done

Attenzione, da bash 4.0 a 4.2, funziona ma attraversa collegamenti simbolici a directory, che di solito non sono desiderabili.

Se usi zsh invece di bash, il globbing ricorsivo funziona fuori dalla scatola senza trucchi. Zsh è disponibile di default su OSX / macOS. In zsh, puoi solo scrivere

set -e
for x in **/*.xml; do invalid_command "$x"; done
    
risposta data 20.04.2012 - 02:34
fonte
18

Posso usare questo invece:

find . -name *.xml -print0 | xargs -n 1 -0 invalid_command
    
risposta data 18.04.2012 - 22:21
fonte
1

xargs è un'opzione. Tuttavia, in realtà è banalmente facile farlo anche con find utilizzando + anziché \; :

-exec  utility_name  [argument ...]   {} +

Dalla documentazione POSIX :

If the primary expression is punctuated by a plus sign, the primary shall always evaluate as true, and the pathnames for which the primary is evaluated shall be aggregated into sets. The utility utility_name shall be invoked once for each set of aggregated pathnames. Each invocation shall begin after the last pathname in the set is aggregated, and shall be completed before the find utility exits and before the first pathname in the next set (if any) is aggregated for this primary, but it is otherwise unspecified whether the invocation occurs before, during, or after the evaluations of other primaries. If any invocation returns a non-zero value as exit status, the find utility shall return a non-zero exit status. An argument containing only the two characters “{}” shall be replaced by the set of aggregated pathnames, with each pathname passed as a separate argument to the invoked utility in the same order that it was aggregated. The size of any set of two or more pathnames shall be limited such that execution of the utility does not cause the system’s {ARG_MAX} limit to be exceeded. If more than one argument containing only the two characters “{}” is present, the behavior is unspecified.

    
risposta data 28.09.2018 - 21:18
fonte

Leggi altre domande sui tag