bash iterate fillista, utom när den är tom

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

  • Lägg till shopt -s nullglob innan du kör for-loop.
  • @cuolnglm: det resulterar i att det returnerar namnet på det exekverande skriptet snarare än en tom lista i denna RHEL5-ruta (bash 3.2.25) om jag gör FILES= ls * .zip ; for fname in "${FILES}"... men det fungerar som förväntat med for fname in *.zip ; do....
  • Använd 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.
  • Denna fråga diskuterar varför analysering av utdata från ls i allmänhet ska vara undviks: Varför inte analyserar ls? ; se även länken högst upp på sidan till BashGuide ' s ParsingLs artikel.
  • Möjlig duplikat av Varför kväver mitt skalskript i mellanslag eller andra specialtecken?

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 (med shopt -s nullglob)
  • Enligt OP ' s kommentar på deras ursprungliga inlägg fungerar shopt -s nullglob inte. Jag försökte find medan jag verifierade mitt svar och det fortsatte att misslyckas. Jag tror på grund av exporten som Dani sa.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *