Encontre recursivamente todos os arquivos chamados “file.txt” e execute um comando sed

5

O desafio da minha tarefa é que o file.txt pode estar em subpasta ou em sub-subpastas. A estrutura geral é assim:

   folder
     |___subfolder
             |_____file.txt
     |
     |___subfolder
             |____subfolder
                      |_______file.txt
     etc

Meu código anterior não lida com a sub-pasta sub e não sei como adicionar nesta parte (uma vez que algumas subpastas não possuem pastas subseqüentes)

 for dir in ./*/ ; do
        if [ -d "$dir" ]; then 
           cd $d;
           for subdir in ./*/; do
               cd $subdir && $(sed command file.txt) && cd ..;
           done
           cd ..
        fi
done

Eu também aprendi que há um método de uma linha de usar find , algo como:

find . -name 'file.txt' | sed command file.txt

Aparentemente, este não funciona, pois find somente retorna o diretório para meu file.txt

    
por dwuuuu 25.07.2016 / 04:25

3 respostas

15

Tente:

find . -name 'file.txt' -exec sed command {} +

Isso localiza todos os arquivos chamados file.txt nos subdiretórios de . e executa sed command nesses arquivos.

Se você quiser que sed modifique esses arquivos, adicione a opção -i .

Althought -exec ... + agora é exigido por POSIX (dica do chapéu: jordanm), alguns as pessoas podem estar usando versões antigas do BSD find que não suportam + . Se você tiver um desses, use (dica do chapéu: unxnut):

find . -name 'file.txt' -exec sed command {} \;

Alternativa mais segura

Se mudanças rápidas forem feitas na estrutura de diretórios, -exec pode estar sujeito a uma condição de corrida. Para maior segurança, use -execdir (dica do chapéu: unxnut):

find . -name 'file.txt' -execdir sed command {} +

Observe que, se você tiver o diretório atual em PATH , o -execdir se recusará a ser executado.

    
por 25.07.2016 / 04:30
3

A menos que você saiba o que o comando sed está fazendo, você deseja executar um sed por arquivo de entrada.
Você pode fazer isso com um loop for (se seu shell for compatível com globalização recursiva), por exemplo, zsh , ksh93 , yash , bash ( tcsh e fish também, mas a sintaxe do loop é diferente lá).

shopt -s globstar        # bash
#set -o globstar         # ksh93
#set -o extended-glob    # yash

for f in **/file.txt; do [ -f "$f" ] && sed 'cmd' "$f"; done

ou (como apontado por outras pessoas) com find :

find . -type f -name 'file.txt' -exec sed 'cmd' {} \;

Agora, alguém pode se perguntar: por que não "otimizar" isso e usar find s -exec com + ou zsh -ism como:

sed 'cmd' **/file.txt(.)

Claro, isso seria mais eficiente, pois invocam sed com vários operandos arquivo , mas isso pode muito bem o problema:
Para simplificar, imagine que você está tentando imprimir apenas a primeira linha de cada arquivo usando sed command 1q 1 (sem editar o arquivo in-place 2 , talvez para redirecionar a saída combinada para outro arquivo ou apenas para inspecioná-lo) para que você possa executar

find . -name 'file.txt' -exec sed '1q' {} +

ou

sed '1q' **/file.txt(.)

no entanto ambos falharão na entrega porque se múltiplos operandos de arquivo forem especificados, os arquivos nomeados serão lidos na ordem especificada e a concatenação será editada
e como resultado sed somente imprimirá a 1ª linha de entrada não a 1ª linha de cada arquivo 3 .
Agora, gnu sed tem a opção -s :

-s, --separate
        consider files as separate rather than as a single continuous long stream

que pode ser útil às vezes, mas neste caso específico ( 1q ), não ajudará.

1: se você não gosta de 1q try $d , que deve apagar a última linha de cada arquivo
2: você pode se safar com gnu sed às vezes como -i implica -s , mas definitivamente não quando um dos comandos sed é q
3: você pode argumentar que isso ocorre porque q desiste - mas o mesmo aconteceria se você usasse sed -n '1p' - eu usei apenas q para enfatizar 2:

    
por 25.07.2016 / 14:45
-1

Supondo que seus caminhos de arquivo não contêm espaços em branco, novas linhas, aspas simples, aspas duplas ou caracteres de barra invertida:

find . -name 'file.txt' -type f | xargs sed command
    
por 25.07.2016 / 12:02