bash iterate filliste, undtagen når den er tom

Jeg troede, det ville være simpelt – men det viser sig at være mere komplekst, end jeg havde forventet.

Jeg vil gentage alle filerne af en bestemt type i en mappe, så jeg skriver dette:

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

Dette fungerer så længe der er mindst en matchende fil i biblioteket. Men hvis der ikke er nogen matchende filer, får jeg dette:

current file is *.zip 

Jeg prøvede derefter:

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

Selvom sløjfens krop ikke udføres, når der ikke er nogen filer, får jeg en fejl fra ls:

ls: *.zip: No such file or directory 

Hvordan skriver jeg en loop, der rent håndterer ingen matchende filer?

Kommentarer

  • Tilføj shopt -s nullglob inden du kører for-loop.
  • @cuolnglm: uhyggeligt resulterer dette i, at vi returnerer navnet på det udførende script i stedet for en tom liste i dette RHEL5-felt (bash 3.2.25), hvis jeg gør FILES= ls * .zip ; for fname in "${FILES}"... men det fungerer som forventet med for fname in *.zip ; do....
  • Brug for file in *.zip, ikke `ls ...`. @cuonglm ' s forslag er således, at *.zip udvides til intet, når mønsteret ikke ' t matche enhver fil. ls uden argumenter viser den aktuelle mappe.
  • Dette spørgsmål diskuterer, hvorfor analyse af output af ls generelt skal være undgås: Hvorfor ikke analyserer ls? ; se også linket øverst på siden til BashGuide ' s ParsingLs artikel.
  • Mulig duplikat af Hvorfor kvæler mit shell-script i hvidt mellemrum eller andre specialtegn?

Svar

I bash kan du indstille nullglob, så et mønster, der matcher intet “forsvinder” snarere end behandlet som en bogstavelig streng:

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

I POSIX-shell-script kontrollerer du bare, at fname eksisterer (og på samme tid med [ -f ], skal du kontrollere, at det er en almindelig fil (eller symlink til almindelig fil) og ikke andre typer som katalog / fifo / enhed .. .):

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

Udskift [ -f "$fname" ] med [ -e "$fname" ] || [ -L "$fname ] ønsker at løkke over alle (ikke-skjulte) filer, hvis navn ender på .zip uanset deres type.

Erstat *.zip med .*.zip .zip *.zip hvis du også vil overveje skjulte filer, hvis navn ender på .zip.

Kommentarer

  • shopt -s nullglob fungerede ikke for mig på Ubuntu 17.04, men [ -f "$fname" ] || continue fungerede godt.
  • @koppor Det lyder som om du ikke ' faktisk ikke bruger 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 nævner du at påkalde 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

Brug find

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

Du SKAL eksportere din shell-funktion med export -f for at dette fungerer. Nu find udfører bash som udfører din shell-funktion og forbliver kun på det aktuelle dir-niveau.

Kommentarer

  • Hvilket gentages gennem underkataloger, og jeg vil påberåbe en bash-funktion (ikke script) til matchene.
  • @symcbean I ' er redigeret for at begrænse til enkelt dir og håndtere bash-funktioner

Svar

I stedet af:

FILES=`ls *.zip` 

Prøv:

FILES=`ls * | grep *.zip` 

Denne måde, hvis ls mislykkes (som det gør i dit tilfælde) vil det grep den mislykkede output og returnere som en tom variabel.

current file is <---Blank Here 

Du kan tilføje nogle logik til dette for at få det til at returnere “Nej Fil fundet “

#!/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åde, hvis den forrige kommando lykkedes (afsluttet med en 0-værdi), vil den udskrive den aktuelle fil, ellers vil den udskrive” Nej Filer fundet “

Kommentarer

  • I thi nk det er en dårlig idé at tilføje endnu en proces (grep) snarere end at prøve at løse problemet ved hjælp af et bedre værktøj (find ) eller ændre den relevante indstilling for den aktuelle løsning (med shopt -s nullglob)
  • Ifølge OP ' s kommentar på deres oprindelige indlæg fungerer shopt -s nullglob ikke. Jeg prøvede find, mens jeg bekræftede mit svar, og det fortsatte med at fejle. Jeg tror på grund af eksporten, som Dani sagde.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *