Apenas fazer isso não resolve

Apenas fazer isso não resolve

Primeiro, para eliminar respostas triviais, mas inaplicáveis: não posso usar o truque find+ xargsnem suas variantes (como findwith -exec) porque preciso usar poucas dessas expressões 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 isso como argumentos para program?

Apenas fazer isso não resolve

$ ./program $(find -L some/dir -name \*.abc | sort)

falha porque programobté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 programconsidera que são dois argumentos diferentes.

Cite até que funcione

Parece que usuários novatos como eu, quando confrontados com tais problemas, tendem a adicionar aspas aleatoriamente até que finalmente funcione - só que aqui isso 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 evitam 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í, claro. Mas eles não são mais interpretados. Eles são apenas parte das cordas. Portanto, eles não apenas não impediram a divisão das palavras, mas também entraram em discussões!

Alterar IFS

Então tentei brincar com IFS. Eu preferiria findwith -print0e sortwith -zde qualquer maneira - para que eles próprios não tenham problemas em "caminhos conectados". Então, por que não forçar a divisão de palavras no nullpersonagem e ficar com 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 IFStarefa em $(…)(como mostrado acima) e antes ./program. Também tentei outra sintaxe como \0, \x0, \x00ambas citadas com 'e "bem como com e sem o $. Nada disso parecia fazer qualquer diferença...


E aqui estou sem ideias. Tentei mais algumas coisas, mas todas pareciam ter os mesmos problemas listados.

O que mais eu poderia fazer? Isso é factível?

Claro, eu poderia aceitar programos padrões e fazer as próprias pesquisas. Mas é muito trabalho duplo fixá-lo em uma sintaxe específica. (Que tal fornecer arquivos por grepexemplo?).

Também eu poderia aceitar programum arquivo com uma lista de caminhos. Então, posso facilmente despejar finda expressão em algum arquivo temporário e fornecer o caminho apenas para esse arquivo. Isso pode ser suportado ao longo de caminhos diretos, de modo que, se o usuário tiver apenas um caminho simples, ele possa ser fornecido sem arquivo intermediário. Mas isso não parece legal - é preciso criar arquivos extras e cuidar deles, sem mencionar a necessidade de implementação extra. (No entanto, o lado positivo pode ser um resgate para casos em que o número de arquivos como argumentos começa a causar problemas com o comprimento da linha de comando…)


No final, deixe-me lembrá-lo novamente que truques find+ xargs(e similares) não funcionarão no meu caso. Para simplificar a descrição, estou mostrando apenas um argumento. Mas meu verdadeiro caso é mais parecido com este:

$ 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

Então, fazer uma xargspesquisa ainda me deixa sabendo como lidar com a outra…

Responder1

Use matrizes.

Se você não precisa lidar com a possibilidade de novas linhas em seus nomes de arquivos, então você pode usar

mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)

então

./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"

Se vocêfazerprecisa lidar com novas linhas em nomes de arquivos e tem bash >= 4.4, você pode usar -print0e -d ''para encerrar os nomes com nulo durante a construção do array:

mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)

(e da mesma forma para o XYZ_FILES). Se vocênãotiver o bash mais recente, então você pode usar um loop de leitura terminado em nulo para anexar nomes de arquivos aos arrays, por exemplo

ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)

Responder2

Você pode usar IFS=newline (assumindo que nenhum nome de arquivo contenha nova linha), mas deve defini-lo no shell externo ANTES da substituição:

$ ls -1
a file with spaces
able
alpha
baker
boo hoo hoo
bravo
$ # note semicolon here; it's not enough to be in the environment passed
$ # to printf, it must be in the environment OF THE SHELL WHILE PARSING
$ IFS=$'\n'; printf '%s\n' --afiles $(find . -name 'a*') --bfiles $(find . -name 'b*')
--afiles
./able
./a file with spaces
./alpha
--bfiles
./bravo
./boo hoo hoo
./baker

Com zshmas não você também bashpode usar null . $'\0'Mesmo bashvocê poderia lidar com a nova linha se houver um caractere suficientemente estranho que nunca seja usado como

 IFS=$'\1'; ... $(find ... -print0 | tr '\0' '\1') ...

No entanto, esta abordagem não atende à solicitação adicional feita nos comentários na resposta do @steeldriver para omitir --afiles se find a estiver vazio.

Responder3

Não tenho certeza se entendi por que você desistiu xargs.

Então, fazer uma xargspesquisa ainda me deixa sabendo como lidar com a outra…

A string --xyz-filesé apenas um dos muitos argumentos e não há razão para considerá-la especial antes de ser interpretada pelo seu programa. Acho que você pode passar xargsentre os dois findresultados:

{ find -L some/dir -name \*.abc -print0 | sort -z; echo -ne "--xyz-files\0"; find -L other/dir -name \*.xyz -print0 | sort -z; } | xargs -0 ./program --abc-files

informação relacionada