percorrer os resultados de `ls` no script shell bash

percorrer os resultados de `ls` no script shell bash

Alguém tem um script de shell de modelo para fazer algo com lsuma lista de nomes de diretórios e percorrer cada um deles e fazer alguma coisa?

Estou planejando fazer ls -1d */para obter a lista de nomes de diretórios.

Responder1

Editado para não usar ls onde um glob serviria, como @shawn-j-goff e outros sugeriram.

Basta usar um for..do..doneloop:

for f in *; do
  echo "File -> $f"
done

Você pode substituir o *por *.txtou qualquer outro glob que retorne uma lista (de arquivos, diretórios ou qualquer coisa nesse sentido), um comando que gere uma lista, por exemplo, $(cat filelist.txt)ou realmente substituí-lo por uma lista.

Dentro do doloop, você apenas se refere à variável do loop com o prefixo do cifrão (como $fno exemplo acima). Você pode echofazer isso ou fazer qualquer outra coisa que desejar.

Por exemplo, para renomear todos os .xmlarquivos no diretório atual para .txt:

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

Ou melhor ainda, se você estiver usando o Bash, poderá usar expansões de parâmetros do Bash em vez de gerar um subshell:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done

Responder2

Usar a saída de lspara obter nomes de arquivos é uma má ideia. Isso pode levar ao mau funcionamento e até mesmo a scripts perigosos. Isso ocorre porque um nome de arquivo pode conter qualquer caractere, exceto /e o nullcaractere, e lsnão usa nenhum desses caracteres como delimitadores, portanto, se um nome de arquivo tiver um espaço ou uma nova linha, vocêvaiobter resultados inesperados.

Existem duas maneiras muito boas de iterar arquivos. Aqui, usei simplesmente echopara demonstrar como fazer algo com o nome do arquivo; você pode usar qualquer coisa, no entanto.

A primeira é usar os recursos nativos de globbing do shell.

for dir in */; do
  echo "$dir"
done

O shell se expande */em argumentos separados que o forloop lê; mesmo que haja espaço, nova linha ou qualquer outro caractere estranho no nome do arquivo, forcada nome completo será visto como uma unidade atômica; não está analisando a lista de forma alguma.

Se você quiser entrar recursivamente em subdiretórios, isso não funcionará, a menos que seu shell tenha alguns recursos de globbing estendidos (como bash's globstar. Se seu shell não tiver esses recursos ou se você quiser garantir que seu script funcionará em uma variedade de sistemas, a próxima opção é usar find.

find . -type d -exec echo '{}' \;

Aqui, o findcomando irá chamar echoe passar um argumento do nome do arquivo. Ele faz isso uma vez para cada arquivo encontrado. Tal como acontece com o exemplo anterior, não há análise de uma lista de nomes de arquivos; em vez disso, um nome de arquivo é enviado completamente como argumento.

A sintaxe do -execargumento parece um pouco engraçada. findpega o primeiro argumento depois -exece o trata como o programa a ser executado, e cada argumento subsequente é usado como um argumento para passar para esse programa. Existem dois argumentos especiais que -execprecisam ser vistos. O primeiro é {}; este argumento é substituído por um nome de arquivo gerado pelas partes anteriores find. O segundo é ;, que permite findsaber que este é o fim da lista de argumentos a serem passados ​​ao programa; findprecisa disso porque você pode continuar com mais argumentos destinados findou não ao programa executado. A razão para isso \é que o shell também trata ;especialmente - ele representa o fim de um comando, então precisamos escapar dele para que o shell o forneça como argumento em findvez de consumi-lo por si mesmo; outra maneira de fazer com que o shell não o trate de maneira especial é colocá-lo entre aspas: ';'funciona tão bem quanto \;para esse propósito.

Responder3

Para arquivos com espaços, você deverá citar a variável como:

 for i in $(ls); do echo "$i"; done; 

ou você pode alterar a variável de ambiente do separador de campo de entrada (IFS):

 (IFS=$'\n';for i in $(ls); do echo $i; done) # subshell to restore IFS

Finalmente, dependendo do que você está fazendo, talvez você nem precise do ls:

 for i in *; do echo "$i"; done;

Responder4

Apenas para complementar a resposta do CoverosGene, aqui está uma maneira de listar apenas os nomes dos diretórios:

for f in */; do
  echo "Directory -> $f"
done

informação relacionada