encontre -exec com vários comandos


430

Eu estou tentando usar find -exec com vários comandos sem nenhum sucesso. Alguém sabe se comandos como os seguintes são possíveis?

find *.txt -exec echo "$(tail -1 '{}'),$(ls '{}')" \;

Basicamente, estou tentando imprimir a última linha de cada arquivo txt no diretório atual e imprimir no final da linha, uma vírgula seguida pelo nome do arquivo.



1
Quanto a verificar a possibilidade do comando, você não o testou em seu sistema?
Sriram

1
A partir da findpágina do manual: There are unavoidable security problems surrounding use of the -exec option; you should use the -execdir option instead.unixhelp.ed.ac.uk/CGI/man-cgi?find
JVE999


O link do @ JVE999 está quebrado, alternativa em ss64.com/bash/find.html #
Keith M

Respostas:


659

findaceita várias -execpartes para o comando. Por exemplo:

find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;

Observe que, neste caso, o segundo comando será executado apenas se o primeiro retornar com êxito, conforme mencionado por @Caleb. Se você deseja que ambos os comandos sejam executados independentemente do sucesso ou falha, você pode usar esta construção:

find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;

como grep duas vezes? isto está falhando: encontre ./* -exec grep -v 'COLD,' {} \; -exec egrep -i "minha_string" {} \;
rajeev 22/01

51
@rajeev O segundo exec só será executado se o código de retorno do primeiro retornar com êxito, caso contrário ele será ignorado. Provavelmente isso deve ser observado nesta resposta.
Caleb em

1
Esta seria a resposta #
pylover

1
Observe o uso de -nalgumas das outras respostas para suprimir a nova linha gerada pelo eco, o que é útil se o seu segundo comando produzir apenas uma linha de saída e você desejar que sejam mais fáceis de ler.
precisa

Ótima solução. Funciona como um encanto.
Philippe Delteil 6/03/19


52

Um dos seguintes:

find *.txt -exec awk 'END {print $0 "," FILENAME}' {} \;

find *.txt -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;

find *.txt -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;

12
Para que serve o sublinhado antes de {}?
qed

2
@ qed: é um valor de descarte que ocupa o lugar de $0. Tente isso com "foobar" em vez de "_": find /usr/bin -name find -exec sh -c 'echo "[$0] [$1]"' foobar {} \;- a saída: "[foobar] [/ usr / bin / find]".
Pausado até novo aviso.

1
@XuWang: Sim, eu diria que é esse o caso. Como você sabe, $0geralmente é o nome do programa ( ARGV[0]).
Pausado até novo aviso.

3
É fundamental, para esse método, que o script passado sh -cseja entre aspas simples, não duplas. Caso contrário, $1está no escopo errado.
Nick

1
As aspas @ Nick não têm nada a ver com isso - você pode escrever '$1'com aspas duplas desde que escape da variável ( "\$1"). Você também pode escapar de outros caracteres ( "\"").
Camilo Martin

22

Outra maneira é assim:

multiple_cmd() { 
    tail -n1 $1; 
    ls $1 
}; 
export -f multiple_cmd; 
find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;

em uma linha

multiple_cmd() { tail -1 $1; ls $1 }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
  • " multiple_cmd()" - é uma função
  • " export -f multiple_cmd" - exportará para que qualquer outro subshell possa vê-lo
  • " find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;" - encontre que executará a função no seu exemplo

Dessa maneira, multiple_cmd pode ser tão longo e complexo quanto você precisar.

Espero que isto ajude.


perfeito, exatamente o que eu precisava!
Anentropic

não funciona para mim no OSX
Thomas

@ Thomas faz isso, mas tente este forro, testado em osx. Eu criei um diretório chamado 'aaa' com alguns arquivos / diretórios e CDd nele. Então, ~/aaa$ acmd() { echo x \"$1\" x; }; export -f acmd; find . -exec bash -c 'acmd {}' \;
barlop

@ Barlop, vou tentar; obrigado!
Thomas

14

Existe uma maneira mais fácil:

find ... | while read -r file; do
    echo "look at my $file, my $file is amazing";
done

Alternativamente:

while read -r file; do
    echo "look at my $file, my $file is amazing";
done <<< "$(find ...)"

1
nomes de arquivos pode ter quebras de linha neles, é por isso achado tem o argumento -print0 e xargs tem a -0 argumento
abasterfield

@abasterfield Eu sempre espero nunca encontrar aqueles no lol selvagem
Camilo Martin

1
o que eu queria fazer era "encontrar ... -exec zcat {} | wc -l \;" o que não funcionou. No entanto, encontre ... | enquanto lê -r arquivo; echo "$ file: zcat $file | wc -l"; done funciona, então obrigado!
Greg Dougherty

No comentário acima, eu tenho "back ticks" em torno de "zcat $ file | wc -l". Infelizmente, o SO os transforma em formatação, então eu o adicionei como uma resposta real, com o código correto visível
Greg Dougherty

1
@GregDougherty Você pode escapar dos backticks `para fazer isso usando barras invertidas da seguinte forma: \​`(ainda, esse é outro bom motivo para usar $()).
Camilo Martin

8

Não sei se você pode fazer isso com o find, mas uma solução alternativa seria criar um script de shell e executá-lo com o find.

lastline.sh:

echo $(tail -1 $1),$1

Tornar o script executável

chmod +x lastline.sh

Use find:

find . -name "*.txt" -exec ./lastline.sh {} \;

7
Como os backticks foram descontinuados, incentive o uso de $ (...) que seja melhor legível, independentemente da fonte e fácil de aninhar. Obrigado.
desconhecido utilizador

4

A primeira resposta de Denis é a resposta para resolver o problema. Mas, na verdade, não é mais uma descoberta com vários comandos em apenas um executivo como o título sugere. Para responder a um executivo com vários comandos, teremos que procurar outra coisa para resolver. Aqui está um exemplo:

Mantenha as últimas 10000 linhas de arquivos .log que foram modificados nos últimos 7 dias usando 1 comando exec usando várias referências {}

1) veja o que o comando fará em quais arquivos:

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "echo tail -10000 {} \> fictmp; echo cat fictmp \> {} " \;

2) Faça: (não observe "\>", mas apenas ">" isso é desejado)

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "tail -10000 {} > fictmp; cat fictmp > {} ; rm fictmp" \;


Mas isso será interrompido se um dos nomes de arquivos tiver um espaço, eu acredito.
Camilo Martin

4

Graças a Camilo Martin, pude responder a uma pergunta relacionada:

O que eu queria fazer era

find ... -exec zcat {} | wc -l \;

o que não funcionou. Contudo,

find ... | while read -r file; do echo "$file: `zcat $file | wc -l`"; done

funciona, então obrigado!


2

Estendendo a resposta do @ Tinker,

No meu caso, eu precisava fazer um command | command | commandinterior no-exec para imprimir o nome do arquivo e o texto encontrado nos arquivos que contêm um determinado texto.

Consegui fazer isso com:

find . -name config -type f \( -exec  grep "bitbucket" {} \; -a -exec echo {} \;  \) 

o resultado é:

    url = git@bitbucket.org:a/a.git
./a/.git/config
    url = git@bitbucket.org:b/b.git
./b/.git/config
    url = git@bitbucket.org:c/c.git
./c/.git/config

1
Você também pode imprimir o nome do arquivo e o conteúdo grep'd em uma única linha, passando /dev/nullcomo um segundo argumento para o grepcomando com um -execparâmetro:find . -name config -type f -exec grep "bitbucket" {} /dev/null \;
Bill Feth

1

deve usar xargs :)

find *.txt -type f -exec tail -1 {} \; | xargs -ICONSTANT echo $(pwd),CONSTANT

outro (trabalhando no osx)

find *.txt -type f -exec echo ,$(PWD) {} + -exec tail -1 {} + | tr ' ' '/'

3
Isso ignora um caso de uso importante para findsituações em que o número de arquivos correspondentes é muito grande para uma linha de comando. -execé uma maneira de contornar esse limite. A tubulação para um utilitário perde esse benefício.
Chris Johnson

xargs -nexiste para escolher o número de correspondências por chamada. xargs -n 1 foocmdserá executado foocmd {}para cada partida.
AndrewF

0

Uma find+xargsresposta

O exemplo abaixo encontra todos os .htmlarquivos e cria uma cópia com a .BAKextensão anexada (por exemplo, 1.html> 1.html.BAK).

Comando único com vários espaços reservados

find . -iname "*.html" -print0 | xargs -0 -I {} cp -- "{}" "{}.BAK"

Vários comandos com vários espaços reservados

find . -iname "*.html" -print0 | xargs -0 -I {} echo "cp -- {} {}.BAK ; echo {} >> /tmp/log.txt" | sh

# if you need to do anything bash-specific then pipe to bash instead of sh

Este comando também funcionará com arquivos que começam com um hífen ou contêm espaços, como -my file.htmlgraças à citação de parâmetros e --depois o cpsinal para cpo final dos parâmetros e o início dos nomes de arquivos reais.

-print0 canaliza os resultados com terminadores de bytes nulos.


para xargs, o -I {}parâmetro define {}como o espaço reservado; você pode usar o espaço reservado que desejar; -0indica que os itens de entrada são separados por nulo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.