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
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 nullglobnon ha funzionato per me su Ubuntu 17.04, ma[ -f "$fname" ] || continueha 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 (conshopt -s nullglob) - Secondo il commento dellOP ' nel loro post originale il
shopt -s nullglobnon funziona. Ho provatofinddurante la verifica della mia risposta e ha continuato a fallire. Penso a causa della cosa dellesportazione che ha detto Dani.
shopt -s nullglobprima di eseguire il ciclo for.FILES=ls * .zip; for fname in "${FILES}"...ma funziona come previsto confor fname in *.zip ; do....for file in *.zip, non`ls ...`. Il suggerimento di @cuonglm ' è che*.zipnon si espande fino a diventare nulla quando il pattern non ' t corrisponde a qualsiasi file.lssenza argomenti elenca la directory corrente.lsdeve essere generalmente evitato: Perché non analizzarels? ; vedere anche il collegamento nella parte superiore della pagina allarticolo ' s ParsingLs di BashGuide.