Como executar um comando bash em todos os subdiretórios possíveis?

7

Digamos que meu diretório principal seja /home/test

Sob ele, eu tenho muitos subdiretórios e, em subdiretórios, muitos deles e assim por diante.

Exemplo:

/home/test
/home/test/subdir1
/home/test/subdir1/subdir2
/home/test/subdir1/subdir2/subdir3
/home/test/subdir1/subdir2/subdir4
/home/test/subdir1/subdir2/subdir4/subdir5

e assim por diante ...

Eu quero um script simples que pegue cada diretório e apenas execute o comando pwd .

    
por youweek1 17.05.2015 / 04:32

5 respostas

7

Solução usando Paralelo

Você poderia usar o GNU Parallel para uma solução compacta e mais rápida.

find . -type d -print0 | parallel -0 cd {}'&&' <command-name>

Isso funcionará absolutamente bem, mesmo para nomes de diretório contendo espaços e novas linhas. O que o parallel faz aqui é que ele pega a saída de find , que é todo diretório e, em seguida, o alimenta para o cd usando {} . Em seguida, se a alteração do diretório for bem-sucedida, um comando separado será executado nesse diretório.

Solução Regular usando loop while

find "$PWD" -type d | while read -r line; do cd "$line" && <command-name>; done;

Observe que $PWD é usado aqui porque essa variável contém o caminho absoluto do diretório atual de onde o comando está sendo executado. Se você não usar o caminho absoluto, então cd poderá lançar erro no loop while.

Esta é uma solução mais fácil. Ele funcionará na maior parte do tempo, exceto quando os nomes de diretórios contiverem caracteres estranhos, como novas linhas (veja os comentários).

    
por 17.05.2015 / 06:55
5

O comando find é poderoso, mas isso dificulta o uso.
Tenho certeza que pode fazer o que você precisa - este comando abaixo é "quase" o que você pede:

find . -type d -execdir pwd \;

Mas - isso não executa o comando no nível de diretório mais profundo - ele é executado nos diretórios nos quais outros diretórios são encontrados.

Por isso, será executado em subdir4 , porque contém subdir5 . Mas não em subdir5 - como você provavelmente espera.

É importante que a opção -execdir seja usada, não a mais conhecida -exec (consulte man find ):

A opção -exec executa o comando no diretório inicial e também tem outras desvantagens:

    -exec command ;
           Execute command; true if 0 status  is  returned.   All  following
           arguments  to find are taken to be arguments to the command until
           an argument consisting of ';' is encountered.  The string '{}' is
           replaced  by  the current file name being processed everywhere it
           occurs in the arguments to the command,  not  just  in  arguments
           where  it  is  alone, as in some versions of find.  Both of these
           constructions might need to be escaped (with a \ ) or quoted  to
           protect  them from expansion by the shell.  See the EXAMPLES sec‐
           tion for examples of the use of the -exec option.  The  specified
           command  is  run once for each matched file.  The command is exe‐
           cuted in the starting directory.   There are unavoidable security
           problems  surrounding use of the -exec action; you should use the
          -execdir option instead.

Mas a opção -execdir é exatamente o que você pede:

   -execdir command ;
   -execdir command {} +
           Like  -exec,  but the specified command is run from the subdirec‐
           tory containing the matched  file,  which  is  not  normally  the
           directory  in  which  you  started find.  This a much more secure
           method for invoking commands, as it avoids race conditions during
           resolution  of the paths to the matched files.  As with the -exec
           action, the '+' form of -execdir will build  a  command  line  to
           process  more  than one matched file, but any given invocation of
           command will only list files that exist in the same subdirectory.
           If  you use this option, you must ensure that your $PATH environ‐
           ment variable does not reference '.'; otherwise, an attacker  can
           run any commands they like by leaving an appropriately-named file
           in a directory in which you will run -execdir.  The same  applies
           to having entries in $PATH which are empty or which are not abso‐
           lute directory names.
    
por 17.05.2015 / 13:01
1

Uma das outras respostas chegou perto disso:

find . -type d -exec sh -c 'cd "$0" && cmd' {} \;

(executando o comando somente se o cd for bem-sucedido). Algumas pessoas recomendam inserir um argumento falso, então o diretório encontrado ( {} ) desliza para $1 :

find . -type d -exec sh -c 'cd "$1" && cmd' foo {} ";"

Claro, você pode usar qualquer string aqui no lugar de foo . As opções comuns incluem - , -- e sh . Essa string será usada em mensagens de erro (se houver), por exemplo,

foo: line 0: cd: restricted_directory: Permission denied

então sh parece ser uma boa escolha. \; e ";" são absolutamente equivalentes; isso é apenas uma preferência de estilo.

Os comandos acima executam o shell uma vez para cada diretório. Isto pode vir com alguma penalidade de desempenho (especialmente se o comando a ser executado for relativamente leve). Alternativas incluem

find . -type d -exec sh -c 'for d; do (cd "$d" && cmd); done' sh {} +

que executa o shell uma vez, mas bifurca uma vez para cada diretório e

find . -type d -exec sh -c 'save_d=$PWD; for d; do cd "$d" && cmd; cd "$save_d"; done' sh {} +

que não gera nenhum subshell. Se você está procurando um caminho absoluto, como sua pergunta sugere, você pode combinar o acima e fazer

find /home/test -type d -exec sh -c 'for d; do cd "$d" && cmd; done' sh {} +

que não cria subpastos e também não precisa se preocupar em salvar o ponto de partida.

    
por 17.05.2015 / 22:45
0

Você não precisa executar o comando pwd extra, você pode executar o seguinte comando:

find test/ -type d

Você pode substituir seu diretório por test/

    
por 17.05.2015 / 04:37
0

Você pode usar algo assim

find /home/test/ -type d -exec bash -c '<Command to be executed>' \; 

Exemplo:

O comando abaixo exibirá o dirname, similar a executar um comando pwd.

find /home/test/ -type d -exec bash -c 'echo $0' \;
    
por 17.05.2015 / 06:31