Jag trodde att det här skulle vara enkelt – men det visar sig vara mer komplicerat än jag förväntade mig.
Jag vill itera igenom alla filer av en viss typ i en katalog, så jag skriver detta:
#!/bin/bash for fname in *.zip ; do echo current file is ${fname} done
Detta fungerar så länge det finns minst en matchande fil i katalogen. Men om det inte finns några matchande filer får jag det här:
current file is *.zip
Jag försökte sedan:
#!/bin/bash FILES=`ls *.zip` for fname in "${FILES}" ; do echo current file is ${fname} done
Även om slingans kropp inte körs när det inte finns några filer får jag ett fel från ls:
ls: *.zip: No such file or directory
Hur skriver jag en loop som helt enkelt hanterar inga matchande filer?
Kommentarer
Svar
I bash
kan du ställa in nullglob
så att ett mönster som matchar ingenting ”försvinner”, snarare än behandlas som en bokstavlig sträng:
shopt -s nullglob for fname in *.zip ; do echo "current file is ${fname}" done
I POSIX-skalskript verifierar du bara att fname
finns (och samtidigt med [ -f ]
, kontrollera att det är en vanlig fil (eller symlink till vanlig fil) och inte andra typer som katalog / fifo / enhet .. .):
for fname in *.zip; do [ -f "$fname" ] || continue printf "%s\n" "current file is $fname" done
Ersätt [ -f "$fname" ]
med [ -e "$fname" ] || [ -L "$fname ]
vill slinga över alla (icke dolda) filer vars namn slutar på .zip
oavsett deras typ.
Ersätt *.zip
med .*.zip .zip *.zip
om du också vill överväga dolda filer vars namn slutar på .zip
.
Kommentarer
-
shopt -s nullglob
fungerade inte för mig på Ubuntu 17.04, men[ -f "$fname" ] || continue
fungerade bra. - @koppor Det låter som om du inte ' inte använder
bash
. - +1 för en POSIX-lösning.
Svar
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
I en kommentar här nämner du att du anropar en funktion …
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 *
Svar
Använd sök
export -f myshellfunc find . -mindepth 1 -maxdepth 1 -type f -name "*.zip" -exec bash -c "myshellfunc "$0"" {} \;
Du MÅSTE exportera din skalfunktion med export -f
för att detta ska fungera. Nu kör find
bash
som kör din skalfunktion och förblir bara på den aktuella dir-nivån.
Kommentarer
- Vilket återkommer genom underkataloger, och jag vill åberopa en bash-funktion (inte skript) för matchningarna.
- @symcbean I ' har redigerats för att begränsa till enkel dir och hantera bash-funktioner
Svara
Istället av:
FILES=`ls *.zip`
Försök:
FILES=`ls * | grep *.zip`
Detta sätt om ls misslyckas (vilket det gör det i ditt fall) kommer det att misslyckas med utgången och returneras som en tom variabel.
current file is <---Blank Here
Du kan lägga till lite logik i detta för att få det att returnera ”Nej Fil hittades ”
#!/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
På det här sättet om det tidigare kommandot lyckades (avslutades med ett 0-värde) kommer den att skriva ut den aktuella filen, annars skulle den skriva ut” Nej Filer hittades ”
Kommentarer
- I thi nk det är en dålig idé att lägga till ytterligare en process (
grep
) snarare än att försöka åtgärda problemet med ett bättre verktyg (find
) eller ändra relevant inställning för den aktuella lösningen (medshopt -s nullglob
) - Enligt OP ' s kommentar på deras ursprungliga inlägg fungerar
shopt -s nullglob
inte. Jag försöktefind
medan jag verifierade mitt svar och det fortsatte att misslyckas. Jag tror på grund av exporten som Dani sa.
shopt -s nullglob
innan du kör for-loop.FILES=
ls * .zip; for fname in "${FILES}"...
men det fungerar som förväntat medfor fname in *.zip ; do....
for file in *.zip
, inte`ls ...`
. @cuonglm ' s förslag är så att*.zip
expanderar till ingenting när mönstret inte ' matchar inte någon fil.ls
utan argument listar den aktuella katalogen.ls
i allmänhet ska vara undviks: Varför inte analyserarls
? ; se även länken högst upp på sidan till BashGuide ' s ParsingLs artikel.