Primeiro, para eliminar respostas triviais, mas inaplicáveis: não posso usar o truque find
+ xargs
nem suas variantes (como find
em -exec
) porque preciso usar poucas expressões desse tipo por chamada. Voltarei a isso no final.
Agora, para um exemplo melhor, vamos considerar:
$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Como passo esses argumentos para program
?
Apenas fazê-lo não faz o truque
$ ./program $(find -L some/dir -name \*.abc | sort)
falha desde que program
obtém os seguintes argumentos:
[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc
Como pode ser visto, o caminho com espaço foi dividido e program
considera dois argumentos diferentes.
Cite até que funcione
Parece que usuários iniciantes como eu, quando enfrentamos esses problemas, tendem a adicionar aspas aleatoriamente até que finalmente funcione - só que aqui não parece ajudar…
"$(…)"
$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Como as aspas impedem a divisão de palavras, todos os arquivos são passados como um único argumento.
Citando caminhos individuais
Uma abordagem promissora:
$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"
As citações estão aí, com certeza. Mas eles não são mais interpretados. Eles são apenas parte das cordas. Portanto, não apenas eles não impediram a divisão de palavras, mas também entraram em discussões!
Alterar IFS
Então eu tentei brincar com IFS
. Eu preferiria find
com -print0
e sort
com -z
qualquer maneira - para que eles não tenham problemas nos "caminhos com fio". Então, por que não forçar a divisão de palavras no null
personagem e ter tudo?
$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc
Portanto, ele ainda se divide no espaço e não se divide no null
.
Tentei colocar a IFS
tarefa tanto em $(…)
(como mostrado acima) quanto antes ./program
. Também eu tentei outra sintaxe como \0
, \x0
, \x00
ambos citados com '
e "
, assim como com e sem o $
. Nenhum deles parecia fazer diferença ...
E aqui estou sem idéias. Tentei mais algumas coisas, mas tudo parecia ter os mesmos problemas listados.
O que mais eu poderia fazer? É factível?
Claro, eu poderia program
aceitar os padrões e fazer pesquisas em si. Mas é muito trabalho duplo, enquanto corrige-o para uma sintaxe específica. (Que tal fornecer arquivos com um grep
por exemplo?).
Também eu poderia fazer o program
aceitar um arquivo com uma lista de caminhos. Em seguida, posso despejar facilmente a find
expressão em algum arquivo temporário e fornecer o caminho apenas para esse arquivo. Isso pode ser suportado em caminhos diretos, para que, se o usuário tiver apenas um caminho simples, ele possa ser fornecido sem arquivo intermediário. Mas isso não parece bom - é preciso criar arquivos extras e cuidar deles, sem mencionar a implementação extra necessária. (No lado positivo, no entanto, pode ser um resgate para os casos em que o número de arquivos como argumentos começa a causar problemas com o comprimento da linha de comando ...)
No final, gostaria de lembrá-lo novamente que find
+ xargs
(e similares) truques não funcionarão no meu caso. Para simplificar a descrição, estou mostrando apenas um argumento. Mas meu caso verdadeiro se parece mais com isso:
$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES
Fazer xargs
uma pesquisa a partir de uma ainda me deixa com como lidar com a outra…
fonte
mapfile
(ou seu sinônimoreadarray
). Mas funciona!while
loop não limpa a matriz. O que significa que, se nenhum arquivo for encontrado, a matriz será indefinida. Enquanto se já estiver definido, novos arquivos serão anexados (em vez de substituir os antigos). Parece que adicionardeclare -a ABC_FILES='()';
anteswhile
faz o truque. (Embora apenas adicionandoABC_FILES='()';
não.)< <
significa aqui? É o mesmo que<<
? Eu não acho que alterá-lo para<<
gerar erro de sintaxe ("token inesperado` ('")). Então, o que é e como ele funciona?ABC_FILES
. Está bem. Mas é útil também criarABS_ARGS
qual é uma matriz vazia seABC_FILES
estiver vazia ou então é uma matriz('--abc-files' "${ABC_FILES[@]}")
. Dessa forma, posteriormente, eu posso usá-lo assim:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"
e certifique-se de que funcione corretamente, independentemente de qual (se houver) dos grupos estiver vazio. Ou, para dizer de maneira diferente: dessa maneira--abc-files
(e--xyz-files
) será fornecida apenas se for seguida por algum caminho real.while read ... done < <(find blah)
é o redirecionamento normal do shell<
de um arquivo especial criado pelo PROCESS SUBSTITUTION . Isso difere da tubulaçãofind blah | while read ... done
porque o pipeline executa owhile
loop em um subshell para que os vars definidos nele não sejam retidos para comandos subseqüentes.Você pode usar IFS = newline (assumindo que nenhum nome de arquivo contenha nova linha), mas você deve configurá-lo no shell externo ANTES da substituição:
Com
zsh
mas nãobash
você pode usar nulo$'\0'
também. Mesmo embash
você , você pode lidar com nova linha se houver um personagem suficientemente estranho que nunca seja usado comoNo entanto, essa abordagem não trata da solicitação adicional que você fez nos comentários na resposta do @ steeldriver para omitir os --afiles se encontrar um estiver vazio.
fonte
IFS
a divisãonull
?read -d ''
método usado no steeldriver é uma string vazia e não uma contendo byte nulo. (E uma opção de comando não é uma var, como tal, de qualquer maneira.)set -o noglob
) antes de usar esse operador split + glob (exceto emzsh
).$'\0'
e também como''
.Não sei se entendi por que você desistiu
xargs
.A string
--xyz-files
é apenas um dos muitos argumentos e não há motivo para considerá-la especial antes de ser interpretada pelo seu programa. Eu acho que você pode passarxargs
entre os doisfind
resultados:fonte
-print0
em segundofind
. Além disso, se vai desta forma eu iria colocar o--abc-files
como umecho
bem - apenas para a consistência..abc
arquivos, também não deveria haver--abc-files
(o mesmo com.xyz
). A solução baseada em array da steeldriver também exige lógica extra para isso, mas essa lógica é trivial lá, embora possa não ser tão trivial aqui, destruindo a principal vantagem dessa solução - a simplicidade.xargs
nunca vai tentar dividir argumentos e fazer alguns comandos em vez de um, a menos que ele é explicitamente instruído a fazê-lo com-L
,--max-lines
(-l
),--max-args
(-n
) ou--max-chars
(-s
argumentos). Estou certo? Ou existem alguns padrões? Como meu programa não iria lidar com esta divisão corretamente e eu preferiria ter uma falha de chamá-lo ...-print0
- reparado, obrigado. Não sei todas as respostas, mas concordo que minha solução dificulta a inclusão de lógica extra. Eu provavelmente iria com matrizes, agora quando conheço essa abordagem. Minha resposta não foi realmente para você. Você já aceitou a outra resposta e presumi que seu problema estivesse resolvido. Eu só queria ressaltar que você pode passar argumentos de várias fontesxargs
, o que não era óbvio à primeira vista. Você pode tratá-lo como uma prova de conceito. Agora todos conhecemos poucas abordagens diferentes e podemos escolher conscientemente o que se encaixa em todos os casos em particular.--abc-files
). Mas você está certo - é bom conhecer suas alternativas! Especialmente que pensei erroneamente que não é possível.