Non esiste un modo per proteggere gli spazi nell'espansione backtick (o $ (...))?
No, non c'è. Perché?
Bash non ha modo di sapere cosa dovrebbe essere protetto e cosa no.
Non ci sono matrici nel file / pipe unix. È solo un flusso di byte. Il comando all'interno di ``
o $()
genera un flusso, che bash ingoia e tratta come una singola stringa. A quel punto, hai solo due scelte: mettilo tra virgolette, per tenerlo come una stringa o metterlo nudo, in modo che bash lo divida in base al suo comportamento configurato.
Quindi, ciò che devi fare se vuoi un array è definire un formato byte che abbia un array, ed è quello che gli strumenti piacciono xargs
e find
fanno: se li esegui con l' -0
argomento, funzionano secondo un formato binario di array che termina gli elementi con il byte null, aggiungendo semantica al flusso di byte altrimenti opaco.
Sfortunatamente, bash
non può essere configurato per dividere le stringhe sul byte null. Grazie a /unix//a/110108/17980 per averci mostrato che zsh
possiamo.
xargs
Vuoi che il tuo comando venga eseguito una volta e hai detto che xargs -0 -n 10000
risolve il tuo problema. In caso contrario, garantisce che se si dispone di più di 10000 parametri, il comando verrà eseguito più di una volta.
Se si desidera eseguirlo rigorosamente o una volta o fallire, è necessario fornire l' -x
argomento e un -n
argomento più grandi -s
dell'argomento (in realtà: abbastanza grandi da non far rientrare un intero gruppo di argomenti di lunghezza zero più il nome del comando la -s
dimensione). ( man xargs , vedi estratto molto più in basso)
Il sistema su cui mi trovo attualmente ha uno stack limitato a circa 8 M, quindi ecco il mio limite:
$ printf '%s\0' -- {1..1302582} | xargs -x0n 2076858 -s 2076858 /bin/true
xargs: argument list too long
$ printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true
(no output)
bash
Se non si desidera coinvolgere un comando esterno, il ciclo while-read che alimenta un array, come mostrato in /unix//a/110108/17980 , è l'unico modo per bash di dividere le cose in il byte nullo.
L'idea di procurarsi lo script ( . ... "$@" )
per evitare il limite delle dimensioni dello stack è interessante (l'ho provato, funziona!), Ma probabilmente non è importante per le situazioni normali.
L'uso di una fd speciale per la pipe di processo è importante se vuoi leggere qualcos'altro da stdin, ma per il resto non ne avrai bisogno.
Quindi, il modo "nativo" più semplice, per le necessità domestiche quotidiane:
files=()
while IFS= read -rd '' file; do
files+=("$file")
done <(find ... -print0)
myscriptornonscript "${files[@]}"
Se ti piace che il tuo albero dei processi sia pulito e piacevole da guardare, questo metodo ti consente di fare exec mynonscript "${files[@]}"
, che rimuove il processo bash dalla memoria, sostituendolo con il comando chiamato. xargs
rimarrà sempre in memoria durante l'esecuzione del comando chiamato, anche se il comando verrà eseguito solo una volta.
Ciò che parla contro il metodo bash nativo è questo:
$ time { printf '%s\0' -- {1..1302581} | xargs -x0n 2076858 -s 2076858 /bin/true; }
real 0m2.014s
user 0m2.008s
sys 0m0.172s
$ time {
args=()
while IFS= read -rd '' arg; do
args+=( "$arg" )
done < <(printf '%s\0' -- $(echo {1..1302581}))
/bin/true "${args[@]}"
}
bash: /bin/true: Argument list too long
real 107m51.876s
user 107m38.532s
sys 0m7.940s
bash non è ottimizzato per la gestione dell'array.
man xargs :
-n max-args
Utilizzare al massimo argomenti max-args per riga di comando. Verranno utilizzati meno argomenti di max-args se la dimensione (vedere l'opzione -s) viene superata, a meno che non venga fornita l'opzione -x, nel qual caso uscirà xargs.
-s max-chars
Utilizzare al massimo i caratteri max-chars per riga di comando, inclusi il comando e gli argomenti iniziali e i null che terminano alle estremità delle stringhe di argomenti. Il valore massimo consentito dipende dal sistema e viene calcolato come limite di lunghezza dell'argomento per exec, meno la dimensione dell'ambiente, meno 2048 byte di headroom. Se questo valore è superiore a 128 KiB, come valore predefinito viene utilizzato 128 KiB; in caso contrario, il valore predefinito è il massimo. 1 KiB è 1024 byte.
-X
Esci se la dimensione (vedi l'opzione -s) viene superata.
IFS="
, newline,"
). Ma è necessario eseguire lo script su tutti i nomi di file? In caso contrario, considerare l'utilizzo di find stesso per eseguire lo script per ciascun file.