bash iterează lista de fișiere, cu excepția cazului în care este gol

Am crezut că acest lucru ar fi simplu – dar se dovedește mai complex decât mă așteptam.

Vreau să iterez prin toate fișierele unui anumit tip dintr-un director, așa că scriu acest lucru:

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

Acest lucru funcționează atâta timp cât există cel puțin un fișier care se potrivește în director. Cu toate acestea, dacă nu există fișiere potrivite, obțin acest lucru:

current file is *.zip 

Am încercat apoi:

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

În timp ce corpul buclei nu se execută atunci când nu există fișiere, primesc o eroare de la ls:

ls: *.zip: No such file or directory 

Cum scriu un buclă care nu gestionează în mod curat niciun fișier care se potrivește?

Comentarii

  • Adăugați shopt -s nullglob înainte de a rula bucla for.
  • @cuolnglm: în mod ciudat, acest lucru duce la returnarea numelui scriptului de executare, mai degrabă decât a unei liste goale din această casetă RHEL5 (bash 3.2.25) dacă fac FILES= ls * .zip ; for fname in "${FILES}"... dar funcționează așa cum era de așteptat cu for fname in *.zip ; do....
  • Utilizați for file in *.zip, nu `ls ...`. @cuonglm ' sugestia este astfel încât *.zip să se extindă la nimic atunci când modelul nu ' nu se potrivește cu orice fișier. ls fără argumente listează directorul curent.
  • Această întrebare discută de ce analizarea rezultatului ls trebuie să fie în general evitat: De ce nu analizați ls? ; consultați, de asemenea, linkul din partea de sus a paginii către BashGuide ' s ParsingLs articol.
  • Posibil duplicat al De ce scriptul meu shell se sufocă în spațiul alb sau alte caractere speciale?

Răspunde

În bash, puteți seta opțiunea nullglob astfel încât un model care nimeni nu „dispare”, mai degrabă decât tratat ca un șir literal:

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

În scriptul shell POSIX, trebuie doar să verificați dacă fname există (și în același timp cu [ -f ], verificați dacă este un fișier obișnuit (sau link simbolic către un fișier obișnuit) și nu alte tipuri precum directorul / fifo / dispozitiv .. .):

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

Înlocuiți [ -f "$fname" ] cu [ -e "$fname" ] || [ -L "$fname ] dacă doriți să parcurgeți toate fișierele (care nu sunt ascunse) al căror nume se termină cu .zip, indiferent de tipul lor.

Înlocuiți *.zip cu .*.zip .zip *.zip dacă doriți să luați în considerare și fișierele ascunse al căror nume se termină cu .zip.

Comentarii

  • shopt -s nullglob nu a funcționat pentru eu pe Ubuntu 17.04, dar [ -f "$fname" ] || continue a funcționat bine.
  • @koppor Se pare că nu folosiți ' bash.
  • +1 pentru o soluție POSIX.

Răspuns

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 

Într-un comentariu aici menționați invocarea unei funcții …

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 * 

Răspuns

Utilizați găsi

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

TREBUIE să exportați funcția shell cu export -f pentru ca acest lucru să funcționeze. Acum find execută bash care execută funcția dvs. de shell și rămâne doar la nivelul actual al dir.

Comentarii

  • Care se repetă prin subdirectoare și vreau să invoc o funcție bash (nu script) pentru potriviri.
  • @symcbean I ' a fost editat pentru a se limita la direcția unică și a gestiona funcțiile bash

Răspuns

În schimb of:

FILES=`ls *.zip` 

Încercați:

FILES=`ls * | grep *.zip` 

În acest fel, dacă ls eșuează (care este face în cazul dvs.) va grep ieșirea eșuată și va reveni ca o variabilă necompletată.

current file is <---Blank Here 

Puteți adăuga o logică la aceasta pentru a face să revină „Nu Fișier găsit „

#!/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 

În acest fel, dacă comanda anterioară a reușit (a ieșit cu o valoare 0), atunci va imprima fișierul curent, altfel va imprima„ Nu Fișiere găsite „

Comentarii

  • I thi nk, este o idee proastă să mai adăugați un proces (grep) decât să încercați să remediați problema utilizând un instrument mai bun (find ) sau modificarea setării relevante pentru soluția curentă (cu shopt -s nullglob)
  • Conform comentariului OP ' în postarea lor originală shopt -s nullglob nu funcționează. Am încercat find în timp ce mi-am verificat răspunsul și nu a reușit. Cred că din cauza exportului a spus Dani.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *