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

looping through &007 results in bash shell script

Does any one have a template shell script for doing something with ls for a list of directory names and looping through each one and doing something?

I’m planning to do ls to get the list of directory names.

Odpowiedzi (7)

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

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

Just use a for..do..done loop:

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

You can replace the * with *.txt or any other globus that returns a list (of files, directories, or anything for that matter), a command that generates a list, e.g, $(cat filelist.txt), lub faktycznie zastąpić ją listą.

W obrębie pętli do, po prostu odnosisz się do zmiennej pętli z prefiksem znaku dolara (więc $f w powyższym przykładzie). Możesz echo to lub zrobić z tym cokolwiek innego, co chcesz.

Na przykład, aby zmienić nazwę wszystkich plików .xml w bieżącym katalogu na .txt:

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

Lub nawet lepiej, jeśli używasz Bash'a, możesz użyć rozwinięcia parametrów Bash'a zamiast tworzenia podpowłoki:

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

Het gebruik van de uitvoer van ls om bestandsnamen te krijgen is een slecht idee ](http://mywiki.wooledge.org/ParsingLs). Het kan leiden tot slecht functionerende en zelfs gevaarlijke scripts. Dit komt omdat een bestandsnaam elk karakter kan bevatten behalve / en het null-karakter, en ls gebruikt geen van beide karakters als scheidingstekens, dus als een bestandsnaam een spatie of een nieuwe regel heeft, krijg je zal onverwachte resultaten.

Er zijn twee zeer goede manieren om te itereren over bestanden. Hier heb ik eenvoudigweg echo gebruikt om te demonstreren dat je iets met de bestandsnaam doet; je kunt echter alles gebruiken.

De eerste is het gebruik van de native globbing functies van de shell.

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

De shell breidt */ uit tot afzonderlijke argumenten die de for-lus leest; zelfs als er een spatie, newline, of een ander vreemd karakter in de bestandsnaam staat, zal for elke volledige naam als een atomaire eenheid zien; het parseert de lijst op geen enkele manier.

Als je recursief in subdirectories wilt gaan, dan kan dit niet, tenzij je shell een aantal uitgebreide globbing-functies heeft (zoals bash‘s globstar. Als je shell deze functies niet heeft, of als je er zeker van wilt zijn dat je script op verschillende systemen werkt, dan is de volgende optie het gebruik van find.

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

Hier zal het find commando echo aanroepen en een argument van de bestandsnaam doorgeven. Het doet dit één keer voor elk bestand dat het vindt. Net als in het vorige voorbeeld is er geen ontleding van een lijst met bestandsnamen; in plaats daarvan wordt een bestandsnaam volledig als argument verzonden.

De syntaxis van het -exec-argument ziet er een beetje grappig uit. find neemt het eerste argument na -exec en behandelt dat als het programma om te draaien, en elk volgend argument, neemt het als argument om door te geven aan dat programma. Er zijn twee speciale argumenten die -exec moet zien. Het eerste is {}; dit argument wordt vervangen door een bestandsnaam die de vorige delen van find genereren. De tweede is ;, die find laat weten dat dit het einde is van de lijst met argumenten die aan het programma moeten worden doorgegeven; find heeft dit nodig omdat je verder kunt gaan met meer argumenten die bedoeld zijn voor find en niet voor het exec’d programma. De reden voor de Het gebruik van de uitvoer vanlsom bestandsnamen te krijgen is een slecht idee ]&003. Het kan leiden tot slecht functionerende en zelfs gevaarlijke scripts. Dit komt omdat een bestandsnaam elk karakter kan bevatten behalve/en hetnull-karakter, enls` gebruikt geen van beide karakters als scheidingstekens, dus als een bestandsnaam een spatie of een nieuwe regel heeft, krijg je zal onverwachte resultaten.

Er zijn twee zeer goede manieren om te itereren over bestanden. Hier heb ik eenvoudigweg echo gebruikt om te demonstreren dat je iets met de bestandsnaam doet; je kunt echter alles gebruiken.

De eerste is het gebruik van de native globbing functies van de shell.

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

De shell breidt */ uit tot afzonderlijke argumenten die de for-lus leest; zelfs als er een spatie, newline, of een ander vreemd karakter in de bestandsnaam staat, zal for elke volledige naam als een atomaire eenheid zien; het parseert de lijst op geen enkele manier.

Als je recursief in subdirectories wilt gaan, dan kan dit niet, tenzij je shell een aantal uitgebreide globbing-functies heeft (zoals bash’s globstar. Als je shell deze functies niet heeft, of als je er zeker van wilt zijn dat je script op verschillende systemen werkt, dan is de volgende optie het gebruik van find.

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

Hier zal het find commando echo aanroepen en een argument van de bestandsnaam doorgeven. Het doet dit één keer voor elk bestand dat het vindt. Net als in het vorige voorbeeld is er geen ontleding van een lijst met bestandsnamen; in plaats daarvan wordt een bestandsnaam volledig als argument verzonden.

De syntaxis van het -exec-argument ziet er een beetje grappig uit. find neemt het eerste argument na -exec en behandelt dat als het programma om te draaien, en elk volgend argument, neemt het als argument om door te geven aan dat programma. Er zijn twee speciale argumenten die -exec moet zien. Het eerste is {}; dit argument wordt vervangen door een bestandsnaam die de vorige delen van find genereren. De tweede is ;, die find laat weten dat dit het einde is van de lijst met argumenten die aan het programma moeten worden doorgegeven; find heeft dit nodig omdat je verder kunt gaan met meer argumenten die bedoeld zijn voor find en niet voor het exec’d programma. De reden voor de is dat de shell ; ook speciaal behandelt - het vertegenwoordigt het einde van een commando, dus we moeten er aan ontsnappen zodat de shell het als argument aan find geeft in plaats van het voor zichzelf te consumeren; een andere manier om de shell zover te krijgen dat hij het niet speciaal behandelt is om het tussen aanhalingstekens te zetten: ';' werkt net zo goed als \; voor dit doel.

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

W przypadku plików ze spacjami należy pamiętać o zacytowaniu zmiennej jak:

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

lub zmienić zmienną środowiskową separatora pól wejściowych (IFS):

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

Na koniec, w zależności od tego, co robisz, możesz nawet nie potrzebować ls:

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

Jeśli masz zainstalowaną GNU Parallel http://www.gnu.org/software/parallel/ możesz to zrobić:

ls | parallel echo {} is in this dir

Aby zmienić nazwę wszystkich .txt na .xml:

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

Obejrzyj film wstępny dla GNU Parallel aby dowiedzieć się więcej http://www.youtube.com/watch?v=OpaiGYxkSuQ

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

Aby dodać do odpowiedzi CoverosGene'a, oto sposób, aby wymienić tylko nazwy katalogów:

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

Dlaczego nie ustawić IFS na nową linię, a następnie uchwycić wyjście z ls w macierzy? Ustawienie IFS na newline powinno rozwiązać problemy z zabawnymi znakami w nazwach plików; użycie ls może być fajne, ponieważ ma wbudowaną funkcję sortowania.

(W testach miałem problem z ustawieniem IFS na \n, ale ustawienie go na backspace newline działa, jak sugerowano gdzie indziej):

Np. (zakładając pożądany wzorzec wyszukiwania ls podany w $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

Jest to szczególnie przydatne na OS X, np, aby przechwycić listę plików posortowanych według daty utworzenia (od najstarszej do najnowszej), polecenie ls to ls -t -U -r.

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

Tak to robię, ale są chyba bardziej efektywne sposoby.

ls > filelist.txt

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