bash itereren bestandslijst, behalve wanneer leeg

Ik dacht dat dit eenvoudig zou zijn – maar het blijkt complexer te zijn dan ik had verwacht.

Ik wil alle bestanden van een bepaald type in een directory doorlopen, dus ik schrijf dit:

#!/bin/bash for fname in *.zip ; do echo current file is ${fname} done 

Dit werkt zolang er is minstens één overeenkomend bestand in de directory. Als er echter geen overeenkomende bestanden zijn, krijg ik dit:

current file is *.zip 

Ik probeerde toen:

#!/bin/bash FILES=`ls *.zip` for fname in "${FILES}" ; do echo current file is ${fname} done 

Hoewel de body van de lus niet wordt uitgevoerd als er geen bestanden zijn, krijg ik een foutmelding van ls:

ls: *.zip: No such file or directory 

Hoe schrijf ik een loop die netjes geen overeenkomende bestanden verwerkt?

Reacties

  • Voeg shopt -s nullglob toe voordat je de for-lus start.
  • @cuolnglm: spookachtig resulteert dit in ls die de naam van het uitvoerende script retourneert in plaats van een lege lijst in deze RHEL5-box (bash 3.2.25) als ik dat doe FILES= ls * .zip ; for fname in "${FILES}"... maar het werkt zoals verwacht met for fname in *.zip ; do....
  • Gebruik for file in *.zip, niet `ls ...`. @cuonglm ' s suggestie is zo dat *.zip zich uitbreidt tot niets wanneer het patroon niet ' t komt overeen met een bestand. ls geeft zonder argumenten de huidige directory weer.
  • Deze vraag bespreekt waarom het parseren van de uitvoer van ls over het algemeen moet gebeuren vermeden: Waarom niet ontleden ls? ; zie ook de link bovenaan die pagina naar het artikel BashGuide ' s ParsingLs .
  • Mogelijk duplicaat van Waarom verslikt mijn shellscript in witruimte of andere speciale tekens?

Antwoord

In bash kunt u de optie nullglob zo instellen dat een patroon komt overeen met niets “verdwijnt”, in plaats van behandeld als een letterlijke tekenreeks:

shopt -s nullglob for fname in *.zip ; do echo "current file is ${fname}" done 

In POSIX shell-script, verifieer je dat fname bestaat (en tegelijkertijd met [ -f ], controleer of het een gewoon bestand is (of een symlink naar een gewoon bestand) en geen andere typen zoals directory / fifo / device .. .):

for fname in *.zip; do [ -f "$fname" ] || continue printf "%s\n" "current file is $fname" done 

Vervang [ -f "$fname" ] door [ -e "$fname" ] || [ -L "$fname ] als je wil alle (niet-verborgen) bestanden herhalen waarvan de naam eindigt op .zip, ongeacht hun type.

Vervang *.zip door .*.zip .zip *.zip als je ook verborgen bestanden wilt overwegen waarvan de naam eindigt op .zip.

Reacties

  • shopt -s nullglob werkte niet voor me op Ubuntu 17.04, maar [ -f "$fname" ] || continue werkte goed.
  • @koppor Het klinkt alsof je ' niet daadwerkelijk gebruikt bash.
  • +1 voor een POSIX-oplossing.

Antwoord

set ./* #set the arg array to glob results ${2+":"} [ -e "$1" ] && #if more than one result skip the stat "$1" printf "current file is %s\n" "$@" #print the whole array at once ###or### ${2+":"} [ -e "$1" ] && #same kind of test for fname #iterate singly on $fname var for array do printf "file is %s\n" "$fname" #print each $fname for each iteration done 

In een opmerking hier vermeld je het aanroepen van een functie …

file_fn() if [ -e "$1" ] || #check if first argument exists [ -L "$1" ] #or else if it is at least a broken link then for f #if so iterate on "$f" do : something w/ "$f" done else command <"${1-/dev/null}" #only fail w/ error if at least one arg fi file_fn * 

Answer

Find gebruiken

export -f myshellfunc find . -mindepth 1 -maxdepth 1 -type f -name "*.zip" -exec bash -c "myshellfunc "$0"" {} \; 

Je MOET je shell-functie exporteren met export -f om dit te laten werken. Nu voert find bash uit die uw shell-functie uitvoert, en blijft alleen op het huidige dir-niveau.

Opmerkingen

  • Die terugkeert via submappen, en ik wil een bash-functie (geen script) aanroepen voor de overeenkomsten.
  • @symcbean I ' heb bewerkt om te beperken tot enkele map en bash-functies af te handelen

Antwoord

In plaats daarvan van:

FILES=`ls *.zip` 

Probeer:

FILES=`ls * | grep *.zip` 

Op deze manier als ls mislukt (wat het doet in jouw geval) het zal de mislukte uitvoer grepen en terugkeren als een lege variabele.

current file is <---Blank Here 

Je kunt hier wat logica aan toevoegen om het “Nee Bestand gevonden “

#!/bin/bash FILES=`ls * | grep *.zip` if [[ $? == "0" ]]; then for fname in "$FILES" ; do echo current file is $fname done else echo "No Files Found" fi 

Op deze manier, als het vorige commando slaagde (verlaten met een 0-waarde), zal het het huidige bestand afdrukken, anders zou het” Nee Bestanden gevonden “

Reacties

  • Ik thi nk, het is een slecht idee om nog een proces toe te voegen (grep) in plaats van te proberen het probleem op te lossen met een betere tool (find ) of het wijzigen van de relevante instelling voor de huidige oplossing (met shopt -s nullglob)
  • Volgens het OP ' s commentaar op hun oorspronkelijke bericht werkt de shopt -s nullglob niet. Ik heb find geprobeerd terwijl ik mijn antwoord verifieerde, maar het bleef maar falen. Ik denk dat Dani vanwege de export zei.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *