Não existe uma maneira de proteger espaços na expansão de backtick (ou $ (...))?
Não, não existe. Por que é que?
Bash não tem como saber o que deve ser protegido e o que não deve.
Não há matrizes no arquivo / canal unix. É apenas um fluxo de bytes. O comando dentro do ``
ou $()
gera um fluxo, que bash engole e trata como uma única sequência. Nesse ponto, você só tem duas opções: colocá-lo entre aspas, mantê-lo como uma sequência ou nu, para que o bash o divida de acordo com o comportamento configurado.
Então, o que você deve fazer se quiser uma matriz é definir um formato de bytes que tenha uma matriz, e é isso que as ferramentas gostam xargs
e find
fazem: se você as executa com o -0
argumento, elas funcionam de acordo com um formato de matriz binária que termina os elementos com o byte nulo, adicionando semântica ao fluxo de bytes opaco.
Infelizmente, bash
não pode ser configurado para dividir seqüências de caracteres no byte nulo. Agradecemos a /unix//a/110108/17980 por nos mostrar o que zsh
pode.
xargs
Você deseja que seu comando seja executado uma vez e disse que xargs -0 -n 10000
resolve o seu problema. Não, garante que, se você tiver mais de 10000 parâmetros, seu comando será executado mais de uma vez.
Se você deseja executá-lo estritamente uma vez ou falhar, é necessário fornecer o -x
argumento e um -n
argumento maior que o -s
argumento (realmente: grande o suficiente para que um monte de argumentos de comprimento zero mais o nome do comando não se encaixem o -s
tamanho). ( homem xargs , veja trecho bem abaixo)
O sistema em que estou atualmente tem uma pilha limitada a cerca de 8 milhões, então aqui está o meu 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)
bater
Se você não deseja envolver um comando externo, o loop while-read que alimenta uma matriz, conforme mostrado em /unix//a/110108/17980 , é a única maneira de o bash dividir as coisas em o byte nulo.
A ideia de criar o script ( . ... "$@" )
para evitar o limite de tamanho da pilha é legal (tentei, funciona!), Mas provavelmente não é importante para situações normais.
Usar um fd especial para o pipe de processo é importante se você quiser ler algo mais do stdin, mas, caso contrário, não precisará dele.
Portanto, a maneira "nativa" mais simples, para as necessidades domésticas diárias:
files=()
while IFS= read -rd '' file; do
files+=("$file")
done <(find ... -print0)
myscriptornonscript "${files[@]}"
Se você deseja que sua árvore de processos seja limpa e agradável de ver, esse método permite exec mynonscript "${files[@]}"
, o que remove o processo bash da memória, substituindo-o pelo comando chamado. xargs
sempre permanecerá na memória enquanto o comando chamado é executado, mesmo se o comando for executado apenas uma vez.
O que fala contra o método bash nativo é o seguinte:
$ 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
O bash não é otimizado para manipulação de array.
homem xargs :
-n max-args
Use no máximo argumentos max-args por linha de comando. Argumentos menores que max-args serão usados se o tamanho (consulte a opção -s) for excedido, a menos que a opção -x seja fornecida, nesse caso o xargs será encerrado.
-s max-chars
Use no máximo caracteres max-chars por linha de comando, incluindo o comando e os argumentos iniciais e os nulos finais no final das sequências de argumentos. O maior valor permitido depende do sistema e é calculado como o limite de comprimento do argumento para exec, menos o tamanho do seu ambiente, menos 2048 bytes de altura livre. Se esse valor for maior que 128KiB, 128Kib será usado como o valor padrão; caso contrário, o valor padrão é o máximo. 1KiB é 1024 bytes.
-x
Saia se o tamanho (consulte a opção -s) for excedido.
IFS="
newline"
). Mas é necessário executar o script em todos os nomes de arquivos? Caso contrário, considere usar-se para executar o script para cada arquivo.