
Alguém tem um script de shell de modelo para fazer algo com ls
uma 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..done
loop:
for f in *; do
echo "File -> $f"
done
Você pode substituir o *
por *.txt
ou 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 do
loop, você apenas se refere à variável do loop com o prefixo do cifrão (como $f
no exemplo acima). Você pode echo
fazer isso ou fazer qualquer outra coisa que desejar.
Por exemplo, para renomear todos os .xml
arquivos 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 ls
para 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 null
caractere, e ls
nã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 echo
para 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 for
loop lê; mesmo que haja espaço, nova linha ou qualquer outro caractere estranho no nome do arquivo, for
cada 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 find
comando irá chamar echo
e 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 -exec
argumento parece um pouco engraçada. find
pega o primeiro argumento depois -exec
e 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 -exec
precisam ser vistos. O primeiro é {}
; este argumento é substituído por um nome de arquivo gerado pelas partes anteriores find
. O segundo é ;
, que permite find
saber que este é o fim da lista de argumentos a serem passados ao programa; find
precisa disso porque você pode continuar com mais argumentos destinados find
ou 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 find
vez 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