Como executo uma ação em todos os arquivos com uma extensão específica nas subpastas de uma maneira elegante?

16

Minha melhor aposta atual é:

for i in $(find . -name *.jpg); do echo $i; done

Problema: não manipula espaços em nomes de arquivos.

Nota: Eu também adoraria uma maneira gráfica de fazer isso, como o comando "tree".

    
por Vorac 09.10.2012 / 10:24

5 respostas

24

A maneira canônica é fazer

find . -name '*.jpg' -exec echo {} \;

(substitua \; por + para passar mais de um arquivo para echo de cada vez)

ou (específico do GNU, embora alguns BSDs também o tenham):

find . -name '*.jpg' -print0 | xargs -r0 echo

zsh:

for i (**/*.jpg(D)) echo $i
    
por 09.10.2012 / 10:27
1

Respostas melhores já foram dadas.

Mas observe que os espaços não são o único problema no código que você forneceu. tab, newline e curinga também são um problema.

com

IFS='
'
set -f
for i in $(find . -name '*.jpg'); do echo "$i"; done

Então, apenas os caracteres de nova linha são um problema.

    
por 09.10.2012 / 10:48
0

O que você mencionou é um dos problemas básicos que as pessoas enfrentam quando tentam ler nomes de arquivos. Às vezes, pessoas com conhecimento limitado e com um equívoco na estrutura de arquivos e pastas tendem a esquecer que " No UNIX Tudo é um arquivo ". Então, eles não entendem que precisam manipular espaços também, já que o nome do arquivo pode consistir em 2 ou mais palavras com espaços.

Solução: Então, uma das maneiras mais conhecidas de fazer isso é fazer uma leitura limpa .

Nós aqui vamos ler todos os arquivos presentes e mantê-los em uma variável, da próxima vez que fizermos o processamento desejado, teremos que manter a variável dentro de aspas, o que preservará os nomes dos arquivos com espaços. Esta é uma das maneiras básicas de fazer isso, no entanto, as outras respostas fornecidas pelas outras pessoas aqui funcionam bem.

SCRIPT:

 find /path/to/files/ -iname "*jpg" | \
  while read I; do
   cp -v --parent "$I" /backup/dir/
  done

Aqui estou lendo os arquivos, dando o caminho para eles e o que eu estou lendo eu estou mantendo-os dentro de uma variável I, que vou citar em um momento posterior, enquanto o processamento para preservar os espaços para processá-lo corretamente .

Espero que isso ajude você de alguma forma.

    
por 09.10.2012 / 10:59
0

Simplesmente com find e bash :

find . -name '*.jpg' -exec bash -c '
    echo "treating file $1 in bash, from path : ${1%/*}" 
' -- {} \;

Dessa forma, usamos $1 no bash, como em um script básico, que abre boas perspectivas para executar qualquer tarefa avançada (ou não).

Uma maneira mais eficiente (para evitar ter que iniciar um novo bash para cada arquivo):

find . -name '*.jpg' -exec bash -c 'for i do
    echo "treating file $i in bash, from path : ${i%/*}" 
  done' -- {} +
    
por 09.10.2012 / 12:57
0

Na versão recente do bash, você pode usar a opção globstar :

shopt -s globstar
for file in **/*.jpg ; do
    echo "$file"
done

Para ações simples, você pode até ignorar completamente o loop:

echo **/*.jpg
    
por 09.10.2012 / 10:45

Tags