Alguém tem um script de shell de modelo para fazer algo com lsuma lista de nomes de diretório e percorrer cada um deles e fazer alguma coisa?
Estou planejando fazer ls -1d */para obter a lista de nomes de diretório.
Alguém tem um script de shell de modelo para fazer algo com lsuma lista de nomes de diretório e percorrer cada um deles e fazer alguma coisa?
Estou planejando fazer ls -1d */para obter a lista de nomes de diretório.
Respostas:
Editado para não usar ls onde uma glob faria, 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 *com *.txtou qualquer outro globo que retorne uma lista (de arquivos, diretórios ou qualquer outra coisa), um comando que gera uma lista, por exemplo $(cat filelist.txt), ou substituí-lo por uma lista.
Dentro do doloop, você apenas se refere à variável do loop com o prefixo do cifrão ( $fno exemplo acima). Você pode echofazer 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 as expansões de parâmetro 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
forloops e uma armadilha típica de tentar analisar a saída de ls. @ DanielA.White, você pode considerar não aceitar esta resposta se ela não foi útil (ou é potencialmente enganosa), pois como você disse, você está atuando em diretórios. A resposta de Shawn J. Goff deve fornecer uma solução mais robusta e funcional para o seu problema.
lssaída , não deve ler a saída em forloop , deve usar em $()vez de `` e Usar mais cotações ™ . Tenho certeza que o @CoverosGene teve um bom resultado, mas isso é simplesmente terrível.
lsé ls -1 | while read line; do stuff; done. Pelo menos esse não será quebrado por espaços em branco.
mv -vvocê não precisaecho "moved $x -> $t"
Usar a saída de lspara obter nomes de arquivos é uma má idéia . Isso pode levar a scripts com mau funcionamento e até perigosos. Isso ocorre porque um nome de arquivo pode conter qualquer caractere, exceto /e o nullcaráter, e lsnão usa qualquer um desses caracteres como delimitadores, por isso, se um nome de arquivo tem um espaço ou uma nova linha, você vai obter resultados inesperados.
Existem duas maneiras muito boas de iterar sobre arquivos. Aqui, usei simplesmente echopara demonstrar fazer algo com o nome do arquivo; você pode usar qualquer coisa.
A primeira é usar os recursos de globbing nativos do shell.
for dir in */; do
echo "$dir"
done
O shell se expande */em argumentos separados que o forloop lê; mesmo se houver 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 o seu shell tenha alguns recursos de globbing estendidos (como basho s globstar. Se o seu shell não tiver esses recursos, ou se você quiser garantir que seu script funcione em uma variedade de sistemas, a próxima opção é usar find.
find . -type d -exec echo '{}' \;
Aqui, o findcomando chamará echoe passará um argumento para o nome do arquivo. Faz isso uma vez para cada arquivo encontrado. Como no exemplo anterior, não há análise de uma lista de nomes de arquivos; em vez disso, um fileneame é enviado completamente como argumento.
A sintaxe do -execargumento parece um pouco engraçada. findpega o primeiro argumento depois -exece trata isso como o programa a ser executado, e todo argumento subsequente, leva como argumento a ser transmitido para esse programa. Existem dois argumentos especiais que -execprecisam ser vistos. O primeiro é {}; esse argumento é substituído por um nome de arquivo findgerado pelas partes anteriores . O segundo é ;, que informa que findeste é o fim da lista de argumentos a serem passados para o programa; findprecisa disso, porque você pode continuar com mais argumentos destinados finde não destinados ao programa exec'd. A razão para \isso é que o shell também trata;especialmente - ele representa o fim de um comando; portanto, precisamos escapar dele para que o shell o dê como argumento, em findvez de consumi-lo por si mesmo; Outra maneira de fazer com que o shell não o trate especialmente é colocá-lo entre aspas: ';'funciona tão bem quanto \;para esse propósito.
find. Há mágica que pode ser feita, e mesmo as limitações percebidas -execpodem ser contornadas. A -print0opção também é valiosa para uso com xargs.
Para arquivos com espaços, certifique-se de citar a variável como:
for i in $(ls); do echo "$i"; done;
ou, você pode alterar a variável de ambiente separador de campo de entrada (IFS):
IFS=$'\n';for file in $(ls); do echo $i; done
Finalmente, dependendo do que você está fazendo, você pode nem precisar dos ls:
for i in *; do echo "$i"; done;
\né insuficiente. Nunca é uma boa idéia recomendar a análise da saída de ls.
Se você possui o GNU Parallel http://www.gnu.org/software/parallel/ instalado, você pode fazer isso:
ls | parallel echo {} is in this dir
Para renomear todos os .txt para .xml:
ls *.txt | parallel mv {} {.}.xml
Assista ao vídeo de introdução do GNU Parallel para saber mais: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Por que não definir o IFS para uma nova linha e capturar a saída de lsem uma matriz? Definir o IFS como nova linha deve resolver problemas com caracteres engraçados nos nomes de arquivo; O uso lspode ser bom porque possui um recurso de classificação interno.
(Nos testes, eu estava tendo problemas para configurar o IFS, \nmas configurá-lo para o backspace da nova linha funciona, conforme sugerido em outro lugar aqui):
Por exemplo (supondo que o lspadrão de pesquisa desejado seja passado $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
Isso é especialmente útil no OS X, por exemplo, para capturar uma lista de arquivos classificados por data de criação (da mais antiga para a mais nova), o lscomando é ls -t -U -r.
\né insuficiente. As únicas soluções válidas usam um loop for com expansão do nome do caminho ou find.
É assim que eu faço, mas provavelmente existem maneiras mais eficientes.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done