bash iterare lelenco dei file, tranne quando è vuoto

Ho pensato che sarebbe stato semplice, ma si sta dimostrando più complesso di quanto mi aspettassi.

Voglio scorrere tutti i file di un tipo particolare in una directory, quindi scrivo questo:

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

Funziona fintanto che cè almeno un file corrispondente nella directory. Tuttavia, se non ci sono file corrispondenti, ottengo questo:

current file is *.zip 

Ho quindi provato:

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

Anche se il corpo del ciclo non viene eseguito quando non ci sono file, ricevo un errore da ls:

ls: *.zip: No such file or directory 

Come scrivo un ciclo che gestisce in modo pulito nessun file corrispondente?

Commenti

  • Aggiungi shopt -s nullglob prima di eseguire il ciclo for.
  • @cuolnglm: spaventosamente questo si traduce in ls che restituisce il nome dello script in esecuzione anziché un elenco vuoto su questa casella RHEL5 (bash 3.2.25) se lo faccio FILES= ls * .zip ; for fname in "${FILES}"... ma funziona come previsto con for fname in *.zip ; do....
  • Usa for file in *.zip, non `ls ...`. Il suggerimento di @cuonglm ' è che *.zip non si espande fino a diventare nulla quando il pattern non ' t corrisponde a qualsiasi file. ls senza argomenti elenca la directory corrente.
  • Questa domanda spiega perché lanalisi delloutput di ls deve essere generalmente evitato: Perché non analizzare ls? ; vedere anche il collegamento nella parte superiore della pagina allarticolo ' s ParsingLs di BashGuide.
  • Possibile duplicato di Perché il mio script di shell si blocca su spazi o altri caratteri speciali?

Risposta

In bash, puoi impostare lopzione nullglob in modo che un pattern che non corrisponde a nulla “scompare”, piuttosto che essere trattato come una stringa letterale:

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

Nello script della shell POSIX, devi solo verificare che fname esiste (e allo stesso tempo con [ -f ], controlla che sia un file normale (o link simbolico a un file normale) e non altri tipi come directory / fifo / device .. .):

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

Sostituisci [ -f "$fname" ] con [ -e "$fname" ] || [ -L "$fname ] se tu desidera eseguire il ciclo su tutti i file (non nascosti) il cui nome termina con .zip indipendentemente da il loro tipo.

Sostituisci *.zip con .*.zip .zip *.zip se desideri considerare anche i file nascosti il cui nome termina con .zip.

Commenti

  • shopt -s nullglob non ha funzionato per me su Ubuntu 17.04, ma [ -f "$fname" ] || continue ha funzionato bene.
  • @koppor Sembra che tu non ' stia effettivamente utilizzando bash.
  • +1 per una soluzione POSIX.

Risposta

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 

In un commento qui parli di invocare una funzione …

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 * 

Risposta

Usa find

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

DEVI esportare la tua funzione shell con export -f affinché funzioni. Ora find esegue bash che esegue la funzione della shell e rimane solo al livello di directory corrente.

Commenti

  • Che ricorre attraverso le sottodirectory e voglio invocare una funzione bash (non script) per le corrispondenze.
  • @symcbean I ' ho modificato per limitare a una singola directory e gestire le funzioni bash

Answer

Invece di:

FILES=`ls *.zip` 

Prova:

FILES=`ls * | grep *.zip` 

In questo modo se ls fallisce (cosa che fa nel tuo caso) grep loutput non riuscito e restituirà una variabile vuota.

current file is <---Blank Here 

Puoi aggiungere un po di logica a questo per farlo tornare “No File trovato “

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

In questo modo se il comando precedente è riuscito (uscito con un valore 0) stamperà il file corrente, altrimenti stamperà” No File trovati “

Commenti

  • Ho thi nk è una cattiva idea aggiungere un altro processo (grep) invece di provare a risolvere il problema utilizzando uno strumento migliore (find ) o modificando limpostazione pertinente per la soluzione corrente (con shopt -s nullglob)
  • Secondo il commento dellOP ' nel loro post originale il shopt -s nullglob non funziona. Ho provato find durante la verifica della mia risposta e ha continuato a fallire. Penso a causa della cosa dellesportazione che ha detto Dani.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *