2009-08-28 17:03:49 +0000 2009-08-28 17:03:49 +0000
101
101
Advertisement

looping through &007 resulta em bash shell script

Advertisement

Alguém tem um template shell script para fazer algo com ls para uma lista de nomes de directórios e looping através de cada um e fazer algo?

Eu estou a planear fazer ls para obter a lista de nomes de directórios.

Advertisement
Advertisement

Respostas (7)

120
120
120
2009-08-28 17:09:37 +0000

Edited to not to use ls where a glob would do, as @shawn-j-goff and others suggested.

Basta usar um laço for..do..done:

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

Pode substituir o * por *.txt ou qualquer outro glob que devolva uma lista (de ficheiros, directórios, ou qualquer outra coisa para esse efeito), um comando que gere uma lista, por exemplo $(cat filelist.txt), ou substitui-lo por uma lista.

Dentro do laço do, basta referir-se à variável do laço com o prefixo do sinal do dólar (assim $f no exemplo acima). Pode echo ou fazer-lhe o que quiser.

Por exemplo, para renomear todos os ficheiros .xml no directório actual para .txt:

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

Ou ainda melhor, se estiver a usar o Bash pode usar expansões de parâmetros Bash em vez de desovar uma sub-camada:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done
69
69
69
2012-04-29 22:16:24 +0000

Utilizar a saída de ls para obter nomes de ficheiros é uma má ideia . Pode levar a um mau funcionamento e até mesmo a scripts perigosos. Isto porque um nome de ficheiro pode conter qualquer caractere excepto / e o caractere null, e ls não usa nenhum desses caracteres como delimitadores, por isso se um nome de ficheiro tem um espaço ou uma nova linha, você _obterá resultados inesperados.

Há duas boas maneiras de iterar sobre ficheiros. Aqui, usei simplesmente echo para demonstrar que se faz algo com o nome do ficheiro; pode usar qualquer coisa, no entanto.

A primeira é usar as funcionalidades nativas do globbing da shell.

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

A shell expande */ em argumentos separados que o laço for lê; mesmo que haja um espaço, uma nova linha, ou qualquer outro carácter estranho no nome do ficheiro, for verá cada nome completo como uma unidade atómica; não está a analisar a lista de forma alguma.

Se quiser ir recursivamente para subdirectórios, então isto não servirá a não ser que a sua shell tenha algumas características de globbing estendidas (tais como bash‘s globstar. Se a sua shell não tiver estas funcionalidades, ou se quiser garantir que o seu script irá funcionar numa variedade de sistemas, então a próxima opção é usar find.

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

Aqui, o comando find irá chamar echo e passar-lhe um argumento do nome do ficheiro. Ele faz isso uma vez para cada arquivo que encontrar. Tal como no exemplo anterior, não há análise de uma lista de nomes de ficheiros; em vez disso, um nome de ficheiro é enviado completamente como argumento.

A sintaxe do argumento -exec parece um pouco engraçada. find pega no primeiro argumento depois de -exec e trata-o como o programa a correr, e em cada argumento subsequente, toma como argumento para passar para esse programa. Existem dois argumentos especiais que o -exec precisa de ver. O primeiro é o {}; este argumento é substituído por um nome de ficheiro que as partes anteriores do find geram. O segundo é o ;, que permite ao find saber que este é o fim da lista de argumentos a passar para o programa; find precisa disto porque pode continuar com mais argumentos que se destinam ao find e não se destinam ao programa de execução. A razão para o [ Utilizar a saída delspara obter nomes de ficheiros é uma má ideia ]&003. Pode levar a um mau funcionamento e até mesmo a scripts perigosos. Isto porque um nome de ficheiro pode conter qualquer caractere excepto/e o caracterenull, els` não usa nenhum desses caracteres como delimitadores, por isso se um nome de ficheiro tem um espaço ou uma nova linha, você _obterá resultados inesperados.

Há duas boas maneiras de iterar sobre ficheiros. Aqui, usei simplesmente echo para demonstrar que se faz algo com o nome do ficheiro; pode usar qualquer coisa, no entanto.

A primeira é usar as funcionalidades nativas do globbing da shell.

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

A shell expande */ em argumentos separados que o laço for lê; mesmo que haja um espaço, uma nova linha, ou qualquer outro carácter estranho no nome do ficheiro, for verá cada nome completo como uma unidade atómica; não está a analisar a lista de forma alguma.

Se quiser ir recursivamente para subdirectórios, então isto não servirá a não ser que a sua shell tenha algumas características de globbing estendidas (tais como bash’s globstar. Se a sua shell não tiver estas funcionalidades, ou se quiser garantir que o seu script irá funcionar numa variedade de sistemas, então a próxima opção é usar find.

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

Aqui, o comando find irá chamar echo e passar-lhe um argumento do nome do ficheiro. Ele faz isso uma vez para cada arquivo que encontrar. Tal como no exemplo anterior, não há análise de uma lista de nomes de ficheiros; em vez disso, um nome de ficheiro é enviado completamente como argumento.

A sintaxe do argumento -exec parece um pouco engraçada. find pega no primeiro argumento depois de -exec e trata-o como o programa a correr, e em cada argumento subsequente, toma como argumento para passar para esse programa. Existem dois argumentos especiais que o -exec precisa de ver. O primeiro é o {}; este argumento é substituído por um nome de ficheiro que as partes anteriores do find geram. O segundo é o ;, que permite ao find saber que este é o fim da lista de argumentos a passar para o programa; find precisa disto porque pode continuar com mais argumentos que se destinam ao find e não se destinam ao programa de execução. A razão para o é que a shell também trata o ; especialmente - representa o fim de um comando, por isso precisamos de escapar a ele para que a shell o dê como argumento ao find em vez de o consumir por si; outra forma de fazer com que a shell não o trate especialmente é colocá-lo entre aspas: o ';' funciona tão bem como o \; para este propósito.

17
Advertisement
17
17
2009-08-28 17:26:27 +0000
Advertisement

Para ficheiros com espaços em si terá de se certificar de citar a variável:

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

ou, pode alterar a variável de ambiente do separador de campos de entrada (IFS):

IFS=$'\n';for file in $(ls); do echo $i; done

Finalmente, dependendo do que está a fazer, pode nem sequer precisar dos ls:

for i in *; do echo "$i"; done;
5
5
5
2010-08-22 20:02:35 +0000

Se tem o GNU Parallel http://www.gnu.org/software/parallel/ instalado pode fazer isto:

ls | parallel echo {} is in this dir

Para renomear todos os .txt para .xml:

ls *.txt | parallel mv {} {.}.xml

Veja o vídeo de introdução ao GNU Parallel para saber mais http://www.youtube.com/watch?v=OpaiGYxkSuQ

4
Advertisement
4
4
2015-02-11 14:36:01 +0000
Advertisement

Apenas para adicionar à resposta da CoverosGene, aqui está uma maneira de listar apenas os nomes dos directórios:

for f in */; do
  echo "Directory -> $f"
done
1
1
1
2012-05-10 22:36:39 +0000

Por que não definir o IFS para uma nova linha, e depois capturar a saída de ls em uma matriz? Definir o IFS para nova linha deve resolver problemas com caracteres engraçados nos nomes dos ficheiros; usar ls pode ser bom porque tem um recurso de ordenação incorporado.

(Nos testes eu estava a ter problemas em definir o IFS para \n mas defini-lo para o novo backspace da linha funciona, como sugerido noutro lugar aqui):

E.g. (assumindo o padrão de pesquisa ls desejado passado em $1):

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

Isto é especialmente útil no OS X, por exemplo, para capturar uma lista de ficheiros ordenados por data de criação (do mais antigo para o mais recente), o comando ls é ls -t -U -r.

-3
Advertisement
-3
-3
2009-08-28 17:28:24 +0000
Advertisement

É assim que eu faço, mas provavelmente há formas mais eficientes.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt
Advertisement

Questões relacionadas

6
10
11
37
4
Advertisement
Advertisement