quante chiamate di comandi con find -exec command {} +
trova gli stati della manpage:
-exec command {} +
This variant of the -exec action runs the specified command on the selected files,
but the command line is built by appending each selected file name at the end;
the total number of invocations of the command will be
much less than the number of matched files.
Ho sempre pensato che questo avrebbe causato findl'esecuzione commandesattamente una volta. C'è un modo per sapere quante volte viene chiamato il comando?
Nota che questo è importante come se fosse solo una volta come pensavo, quindi c'è il pericolo di costruire un elenco di argomenti troppo grande per commandessere gestito; ma se find finirà per dividere le invocazioni (in qualche modo simile a parallel), allora questo sarebbe mitigato.
Risposte
Il buffer utilizzato dipende dalla findversione e sembra avere una dimensione di circa 256Kb nella casella SuSE che ho disponibile qui.
Quindi, per calcolare quante volte "comando" viene richiamato, dovresti conoscere la lunghezza di ogni percorso di file trovato, quindi sarebbe (approssimativamente) la somma di tutte le lunghezze del percorso aumentata di uno per lo spazio di divisione, meno il comando stesso, diviso per la dimensione del buffer.
Ad esempio, trovi 20.000 file con una lunghezza media del percorso di 200 byte, ovvero 4.020.000 byte, diviso per 256 Kb è 15,33, quindi avresti bisogno di circa 16 chiamate.
Il calcolo esatto sarebbe leggermente più complesso per tenere conto della necessità di non interrompere un percorso di file tra due chiamate consecutive, ma si ottiene una cifra approssimativa.
Vedi qui per un thread (con codice sorgente) in cui la dimensione è segnalata essere 32Kb, e considerata inutilmente bassa (ora che ci penso, forse il mio find sta usando i syslimits. Non ho sperimentato); coreutilsLa versione di, per inferenza, sembra essere quattro volte quella, cioè 128 Kb .
Il limite dipenderà find(1)dai buffer di e da cosa gestisce il comando (dipendente dal kernel). A meno che l'ultima percentuale di prestazioni non sia critica, le impostazioni predefinite del sistema dovrebbero andare bene.
Se ti preoccupi delle prestazioni, considera l' intero sistema che lo fa e misura dove si trovano i colli di bottiglia. È probabile che rimarrai molto sorpreso dalle tue scoperte. Bentley, nella sua deliziosa "Scrittura di programmi efficienti" (Prentice-Hall, 1982), purtroppo fuori stampa da tempo, condivide diverse storie di attente "ottimizzazioni" che hanno reso "più veloce" il codice essenzialmente inutilizzato, fatalmente difettoso o ottimizzato il ciclo inattivo di un sistema operativo dopo aver misurato che ha occupato una parte sostanziale del tempo del computer. Le persone sono notoriamente cattive nell'indovinare dove risiedono le inefficienze. Inoltre, lavorare ai livelli più alti (architettura del sistema, organizzazione generale, algoritmi e strutture dati) paga molto di più che sui dettagli.
Nota preliminare: il manuale e la tua domanda usano commandper denotare il comando, ma poiché POSIX definisce un'utilità letteralmente chiamata command, la mia risposta userà cmmnd.
Se si desidera eseguire in realtà cmmnd(s) e solo contare il numero di invocazioni (per sapere che dopo find finiture) quindi creare un wrapper che fa qualcosa che si può contare (ad esempio stampe a stderr, stampe ad un file di log, emette un segnale acustico) e, infine, gestisce il cmmnd. Esempio:
#!/bin/sh
echo "invoking cmmnd" >&2
cmmnd "$@"
Quindi utilizzare il wrapperposto della cmmndparte interna find.
Nota finduserà la /absolute/path/to/wrappercreazione di comandi che non sono troppo lunghi; quindi il wrapper utilizzerà /absolute/path/to/cmmnd. Se quest'ultimo è più lungo, alcune righe di comando che lo contengono potrebbero risultare comunque troppo lunghe. Quindi questo approccio non è così semplice come desideriamo. Puoi estendere il percorso precedente fornendolo alla findlettera con barre aggiuntive (ad esempio /absolute/path/to/////wrapper).
Ora presumo che tu voglia conoscere il numero prima di decidere di correre cmmnd. Come nel caso in cui chiamare cmmnddue volte è una cosa negativa (per qualsiasi motivo) e vuoi assicurarti findche venga eseguito esattamente una volta.
È cmmnd "$@"possibile utilizzare il wrapper sopra con commentato. Di seguito sono riportate alcune altre idee (alla fine non così diverse).
Supponiamo che tu voglia fare questo:
find . -exec cmmnd … {} +
(dove …denota argomenti costanti). Scopri qual cmmndè veramente il percorso assoluto verso . Ad esempio, può essere /bin/cmmnd. Quindi esegui qualcosa del genere:
find . -exec /aaa/zzzzz … {} +
dove /aaa/zzzzzè un comando inesistente il cui nome è della stessa lunghezza di /bin/cmmnd. Ora findcreerà le righe di comando con /aaa/zzzzzche saranno della stessa lunghezza delle righe di comando con /bin/cmmnd. Otterrete
find: '/aaa/zzzzz': No such file or directory
una o più volte. Contali per ottenere il numero che desideri. Questo semplice approccio:
find . -exec /aaa/zzzzz … {} + 2>&1 | wc -l
non è il massimo perché findpuò anche stampare, ad esempio, permission deniedper alcuni file che incontra. Ma se crei /aaa/zzzzzcome eseguibile valido che stampa esattamente una riga (può essere una riga vuota), allora dovrebbe funzionare:
find . -exec /aaa/zzzzz … {} + | wc -l
Un altro miglioramento consiste nel nominare lo strumento /a(invece di /aaa/zzzzz) e chiamarlo come /////ao /////////////////aecc., A seconda della lunghezza necessaria. Esempio:
find . -exec /////////a … {} + | wc -l
Per completezza, ecco come apotrebbe apparire:
#!/bin/sh
echo
È quasi come il nostro wrapper senza cmmnd "$@", però usa lo stdout.
Appunti:
Il numero esatto di
/caratteri non è critico. Un errore di pochi non cambierà drasticamente il risultato . Se hai bisogno di un risultato di stima , puoi usarlo alla cieca///////////ao giù di lì, a meno che il percorso per il percorso noncmmndsia insolitamente lungo. Nota che usare esattamente/ati darà il limite inferiore.In pratica hai spesso altri test prima
-exec cmmnd … {} +. Se si sostituiscecmmndcon/////////ao così, gli altri test verranno comunque eseguiti. Non dovresti ometterli perché-execin primo luogo decidono a quali percorsi arrivare . Ma se i test fanno o cambiano qualcosa, potrebbe essere che eseguirli senza checmmndsia sbagliato.Ad esempio, potresti voler eliminare i file con
-delete -exec cmmnd … {} +, dovecmmndgenera un rapporto sui file che sono stati eliminati. In questo caso, l'utilizzo/////////acancellerà i file senza generare alcun report. Quindi pensa prima di agire.Assicurati di test / azioni / qualsiasi cosa diversa da
-exec /////////a … {} +stampare nulla su stdout. O lascia/ausare qualche altro canale.L'elaborazione degli alberi di directory dati e l'esecuzione di (altri) test possono richiedere del tempo anche senza
cmmnd.
Ebbene, il testo standard dice:
La dimensione di qualsiasi insieme di due o più nomi di percorso deve essere limitata in modo tale che l'esecuzione dell'utilità non causi il superamento del limite {ARG_MAX} del sistema.
Quindi non dovrebbe creare un elenco di argomenti troppo grande per essere eseguito. Ciò vanificherebbe lo scopo di una funzionalità come questa.
Il numero di invocazioni che esegue esattamente dipende dall'implementazione, ed è probabilmente qualcosa di cui non dovresti preoccuparti troppo. Lo standard promette che le invocazioni della stessa -execclausola non si sovrappongono, il che può essere rilevante per la correttezza se si esegue qualcosa che ha uno stato esterno.
Tuttavia, su Linux, la dimensione massima effettiva degli argomenti della riga di comando è basata sulla dimensione dello stack e può essere modificata indirettamente con ulimit -s. E sembra che, a differenza xargs, ad esempio , findsul mio Debian e Ubuntu non controlla effettivamente il limite in fase di esecuzione, quindi è teoricamente possibile incontrare problemi.
$ mkdir bar $ touch bar/{00000..99999}
$ ulimit -Ss 512 $ getconf ARG_MAX
131072
$ find bar -type f -exec sh ./args.sh {} +
find: ‘sh’: Argument list too long
find: ‘sh’: Argument list too long
...
Tuttavia, il valore predefinito per ulimit -sè 8192, quindi non è probabile che si verifichi questo problema, tranne su un sistema molto vincolato.