bash iterate filliste, bortsett fra når den er tom

Jeg trodde dette ville være enkelt – men det viser seg å være mer komplisert enn jeg forventet.

Jeg vil gjenta alle filene av en bestemt type i en katalog, så jeg skriver dette:

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

Dette fungerer så lenge det er minst én samsvarende fil i katalogen. Men hvis det ikke er noen samsvarende filer, får jeg dette:

current file is *.zip 

Jeg prøvde:

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

Selv om løkken ikke utføres når det ikke er filer, får jeg en feil fra ls:

ls: *.zip: No such file or directory 

Hvordan skriver jeg en loop som rent håndterer ingen samsvarende filer?

Kommentarer

  • Legg til shopt -s nullglob før du kjører for loop.
  • @cuolnglm: uhyggelig resulterer dette i at jeg returnerer navnet på det utførende skriptet i stedet for en tom liste i denne RHEL5-boksen (bash 3.2.25) hvis jeg gjør FILES= ls * .zip ; for fname in "${FILES}"... men det fungerer som forventet med for fname in *.zip ; do....
  • Bruk for file in *.zip, ikke `ls ...`. @cuonglm ' sitt forslag er slik at *.zip utvides til ingenting når mønsteret ikke ' t samsvarer med en hvilken som helst fil. ls uten argumenter viser den nåværende katalogen.
  • Dette spørsmålet diskuterer hvorfor parsing av utdata fra ls generelt skal være unngått: Hvorfor ikke analysere ls? ; se også lenken nær toppen av den siden til BashGuide ' s ParsingLs artikkel.
  • Mulig duplikat av Hvorfor kveles skallskriptet mitt i mellomrom eller andre spesialtegn?

Svar

I bash kan du stille inn nullglob slik at et mønster som samsvarer med ingenting som «forsvinner», i stedet for å bli behandlet som en bokstavelig streng:

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

I POSIX-skallskript bekrefter du bare at fname eksisterer (og samtidig med [ -f ], sjekk at det er en vanlig fil (eller symlink til vanlig fil) og ikke andre typer som katalog / fifo / enhet .. .):

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

Erstatt [ -f "$fname" ] med [ -e "$fname" ] || [ -L "$fname ] ønsker å løpe over alle (ikke-skjulte) filer hvis navn ender på .zip uansett deres type.

Erstatt *.zip med .*.zip .zip *.zip hvis du også vil vurdere skjulte filer hvis navn ender på .zip.

Kommentarer

  • shopt -s nullglob fungerte ikke for meg på Ubuntu 17.04, men [ -f "$fname" ] || continue fungerte bra.
  • @koppor Det høres ut som om du ikke ' ikke bruker bash.
  • +1 for 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 her nevner du å påkalle en funksjon …

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

Bruk finn

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

Du MÅ eksportere skallfunksjonen din med export -f for at dette skal fungere. Nå find utfører bash som utfører skallfunksjonen din, og forblir bare på gjeldende dir-nivå.

Kommentarer

  • Som gjentas gjennom underkataloger, og jeg vil påkalle en bash-funksjon (ikke script) for kampene.
  • @symcbean I ' har redigert for å begrense til enkelt dir og håndtere bash-funksjoner

Svar

I stedet av:

FILES=`ls *.zip` 

Prøv:

FILES=`ls * | grep *.zip` 

Denne måten hvis ls mislykkes (som det gjør i ditt tilfelle) vil det grep den mislykkede utgangen og returnere som en tom variabel.

current file is <---Blank Here 

Du kan legge til noen logikk for å få den til å returnere «Nei Fil funnet «

#!/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å denne måten hvis den forrige kommandoen lyktes (avsluttet med en 0-verdi), vil den skrive ut den nåværende filen, ellers vil den skrive ut» Nei Filer funnet «

Kommentarer

  • I thi nk er det en dårlig ide å legge til en prosess til (grep) i stedet for å prøve å løse problemet ved å bruke et bedre verktøy (find ) eller endre relevant innstilling for den gjeldende løsningen (med shopt -s nullglob)
  • I følge OP ' s kommentar på deres opprinnelige innlegg fungerer shopt -s nullglob ikke. Jeg prøvde find mens jeg bekreftet svaret mitt, og det sviktet stadig. Jeg tror på grunn av eksporttingen Dani sa.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *