find: -exec vs xargs (também conhecido por Por que “find | xargs basename” é interrompido?)

10

Eu estava tentando encontrar todos os arquivos de um determinado tipo espalhados em subdiretórios e, para meus propósitos, só precisava do nome do arquivo. Tentei remover o componente do caminho via basename, mas não funcionou com xargs:

$ find . -name '*.deb' -print | xargs basename 
basename: extra operand `./pool/main/a/aalib/libaa1_1.4p5-37+b1_i386.deb'
Try `basename --help' for more information.

Eu recebo a mesma coisa (exatamente o mesmo erro) com uma dessas variações:

$ find . -name '*.deb' -print0 | xargs -0 basename 
$ find . -name '*.deb' -print | xargs basename {}

Por outro lado, isso funciona como esperado:

$ find . -name '*.deb' -exec basename {} \;
foo
bar
baz

Isso acontece no Cygwin e no Debian 5.0.3 atualizados. Meu diagnóstico é que, por algum motivo, o xargs está passando duas linhas de entrada para o nome da base, mas por quê? O que está acontecendo aqui?

charlatão quixote
fonte

Respostas:

23

Porque basenamequer apenas um parâmetro ... não muito. E xargscria muitos parâmetros.

Para resolver seu problema real (liste apenas os nomes de arquivo):

 find . -name '*.deb' -printf "%f\n"

Que imprime apenas o 'nome da base' (localização do homem):

 %f     File's name with any leading directories
        removed (only the last element).
akira
fonte
1
oooh .... / dá um tapa na testa novamente / eu acho que eu preciso de um "encontrar for dummies" livro ...
quack quixote
Eu pensei que o ponto de xargsé que ele cria uma lista de argumentos e alimenta cada um com o comando que vem depois? caso contrário, qual é a diferença entre isso e find . -name '*.deb' | basename?
WindowsMaker
O nome de base GNU agora tem uma -aopção: "suporta múltiplos argumentos e trata cada um como um nome".
bispo
1
O @WindowsMaker xargsconverte stdinem argumentos de comando. De certa forma, é o oposto de echo, que converte seus argumentos em stdout. A diferença entre find ... | xargs -n1 basenameor find ... | xargs basename -ae find ... | basenameé que os dois primeiros trabalharão com implementações basenamedesse ignorar stdin.
precisa saber é o seguinte
19

Tente o seguinte:

find . -name '*.deb' | xargs -n1 basename
perlguy9
fonte
essa não é a explicação, é uma solução alternativa. e a solução alternativa é apenas chamar 'basename' via -exec para qualquer arquivo encontrado.
akira
4
+1 ... embora não seja uma explicação, isso me levaria a investigar o interruptor xargs que você mostra, o que acabaria me levando ao movimento de dar um tapa na testa que eu apenas usava lendo as respostas de akira e john t ...
quack quixote
1
É assim que eu faço. Eu não sinto vontade de aprender todos os detalhes do findcomando, então eu o uso apenas para encontrar e listar arquivos, e xargs para todo o resto.
22139 Ryan C. Thompson
4

basename apenas aceita um único argumento. O uso -execfunciona corretamente porque cada um {}é substituído pelo nome do arquivo atual que está sendo processado e o comando é executado uma vez por arquivo correspondente , em vez de tentar enviar todos os argumentos para o nome da base de uma só vez.

John T
fonte
3

xargs pode ser forçado a apenas passar um argumento também ...

find . -name '*.deb' -print | xargs -n1 basename

Isso funciona, no entanto, a resposta aceita está sendo usada de findmaneira mais apropriada. Encontrei esta pergunta procurando xargs basenameproblemas, pois estou usando outro comando para obter uma lista dos locais dos arquivos. A -n1bandeira para xargsfoi a resposta final para mim.

Flet
fonte