cd em todos os diretórios, execute o comando nos arquivos desse diretório e retorne ao diretório atual anterior

41

Estou tentando escrever um script que será executado em um determinado diretório com muitos subdiretórios de nível único. O script cdará em cada um dos subdiretórios, executará um comando nos arquivos no diretório e sairá para continuar no próximo diretório. Qual é a melhor maneira de fazer isso?

Algo Jones
fonte
11
Dado o nível atual de informações fornecidas, não vejo relevância para youtube-dl.
HalosGhost

Respostas:

83
for d in ./*/ ; do (cd "$d" && somecommand); done
psusi
fonte
12
Portanto, como o respondente omitiu qualquer tipo de explicação, tentarei uma. for d in ./*/inicia um loop que armazena todos os itens ./*/(uma lista de arquivos / pastas, neste caso) em uma variável $d. do (cd "$d" && somecommand);inicia o corpo do loop. Dentro do corpo, ele inicia um subshell e executa os comandos cde somecommand. Como é um shell filho, o shell pai (o shell do qual você está executando este comando) mantém seu CWD e outras variáveis ​​de ambiente. donesimplesmente fecha o corpo do loop.
Qix
este método funciona para subdiretórios de diretórios:, for d in ./*/ ; do (cd "$d" && ls); donenão funcionará. mas for d in ./*/ ; do (cd "$d" && for d in ./*/ ; do (cd "$d" && ls); done ); donevai funcionar. -Usando ls como o comando neste exemplo.
Michael Dimmitt
-bash: cd: ./*/: No such file or directory
S. Tarık Çetin 26/10
15

A melhor maneira é não usar cd:

find some/dir -type f -execdir somecommand {} \;

execdiré como exec, mas o diretório de trabalho é diferente:

   -execdir command {} [;|+]
          Like   -exec,   but  the  specified  command  is  run  from  the
          subdirectory 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.

Não é POSIX.

muru
fonte
Isso funciona com aliases? Eu tenho um para baixar certos arquivos, mas ele não é reconhecido quando digito find * /. Link -type f -execdir md $ (cat .link) {} \;
Something Jones
@AlgoJones não, findexecuta esses comandos, para que não tenha conhecimento de aliases. O que é mde é o .linkdiretório a?
Muru
.link é um arquivo de texto com o URL que ele precisa baixar. md é um alias para o wget com um monte de sinalizadores definidos. Existe uma maneira de torná-lo consciente de aliases?
Something Jones
@SomethingJones para seu uso caso particular, em bash: find . -type f -iname '*.link' -execdir ${BASH_ALIASES[md]} -i {} \;Você não precisa fazer catcom wgetque tem uma -ibandeira para a leitura em uma URL de um arquivo. Além disso, isso é um pouco diferente da sua pergunta original (já que você parece estar interessado apenas nos arquivos nomeados .linke não em outros arquivos que possam estar presentes).
Muru
Você sabe como fazer isso com o zsh? Eu tentei o que você me deu e estou recebendo um erro de "Substituição incorreta". Além disso, como eu poderia extrair o conteúdo do arquivo .link? Sei que não preciso disso neste caso, mas imagino que precisarei em breve.
Something Jones
2
cd -P .
for dir in ./*/
do cd -P "$dir" ||continue
   printf %s\\n "$PWD" >&2
   command && cd "$OLDPWD" || 
! break; done || ! cd - >&2

O comando acima não precisa fazer nenhum subshells - apenas rastreia seu progresso no shell atual alternando $OLDPWDe $PWD. Quando cd -o shell troca o valor dessas duas variáveis, basicamente, à medida que altera os diretórios. Ele também imprime o nome de cada diretório, pois trabalha lá para stderr.

Acabei de dar uma segunda olhada e decidi que poderia fazer um trabalho melhor com o tratamento de erros. Irá pular um diretório no qual não pode cd- e cdimprimirá uma mensagem sobre o motivo de stderr - e exibirá breakum código de saída diferente de zero se o seu commandnão for executado com êxito ou se a execução commandafetar de alguma forma a capacidade de retornar ao diretório original - $OLDPWD. Nesse caso, ele também faz uma cd -última - e grava o nome do diretório de trabalho atual resultante no stderr.

mikeserv
fonte
1
for D in ./*; do
    if [ -d "$D" ]; then
        cd "$D"
        run_something
        cd ..
    fi
done
Kirill
fonte