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

het doorlussen van &007 resulteert in bash shell script

Heeft iemand een template shell script voor het doen van iets met ls voor een lijst van mapnamen en het doorlussen van elke map en het doen van iets?

Ik ben van plan om ls te doen om de lijst van mapnamen te krijgen.

Antwoorden (7)

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

Gebruik geen ls waar een glob zou doen, zoals @shawn-j-goff en anderen suggereerden.

Gebruik gewoon een for..do..done-lus:

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

U kunt de * vervangen door *.txt of een andere glob die een lijst (van bestanden, mappen, of wat dan ook) terugstuurt, een commando dat een lijst genereert, bijv, $(cat filelist.txt), of eigenlijk vervangen door een lijst.

Binnen de do-lus verwijs je gewoon naar de lusvariabele met het voorvoegsel van het dollarteken (dus $f in het bovenstaande voorbeeld). Je kunt echo het of iets anders doen wat je wilt.

Bijvoorbeeld, om alle .xml bestanden in de huidige directory te hernoemen naar .txt:

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

Of nog beter, als je Bash gebruikt kun je Bash parameter uitbreidingen gebruiken in plaats van een subshell te paaien:

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

Voor bestanden met spaties in je zal je ervoor moeten zorgen om de variabele te citeren zoals:

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

of, je kan de invoerveld scheidingsteken (IFS) omgevingsvariabele:

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

wijzigen Tenslotte, afhankelijk van wat je doet, heb je misschien niet eens de ls nodig:

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

Als u GNU Parallel http://www.gnu.org/software/parallel/ hebt geïnstalleerd, kunt u dit doen:

ls | parallel echo {} is in this dir

Om alle .txt te hernoemen naar .xml:

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

Bekijk de introvideo voor GNU Parallel voor meer informatie http://www.youtube.com/watch?v=OpaiGYxkSuQ

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

Om aan het antwoord van CoverosGene toe te voegen, is hier een manier om alleen de mapnamen op te sommen:

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

Waarom zet u IFS niet op een nieuwe lijn en legt u vervolgens de output van ls vast in een array? Het instellen van IFS op newline zou problemen met grappige karakters in bestandsnamen moeten oplossen; het gebruik van ls kan leuk zijn omdat het een ingebouwde sorteermogelijkheid heeft.

(Bij het testen had ik moeite met het instellen van IFS op \n maar het instellen op newline backspace werkt, zoals elders hier gesuggereerd):

Bijv. (aangenomen dat het gewenste ls zoekpatroon in $1 is gepasseerd):

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

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

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

IFS=$SAVEIFS

Dit is vooral handig op OS X, bijv, om een lijst met bestanden vast te leggen gesorteerd op aanmaakdatum (oudste naar nieuwste), is het ls commando ls -t -U -r.

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

Zo doe ik het, maar er zijn waarschijnlijk efficiëntere manieren.

ls > filelist.txt

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