lista plików iteracji bash, z wyjątkiem sytuacji, gdy jest pusta

Myślałem, że to będzie proste – ale okazuje się bardziej złożone niż się spodziewałem.

Chcę przejrzeć wszystkie pliki określonego typu w katalogu, więc piszę to:

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

To działa o ile istnieje co najmniej jeden pasujący plik w katalogu. Jeśli jednak nie ma pasujących plików, otrzymuję to:

current file is *.zip 

Następnie próbowałem:

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

Chociaż treść pętli nie jest wykonywana, gdy nie ma żadnych plików, pojawia się błąd z ls:

ls: *.zip: No such file or directory 

Jak napisać pętla, która bezproblemowo nie obsługuje żadnych pasujących plików?

Komentarze

  • Dodaj shopt -s nullglob przed uruchomieniem pętli for.
  • @cuolnglm: upiornie powoduje to, że ls zwraca nazwę wykonywanego skryptu zamiast pustej listy w tym polu RHEL5 (bash 3.2.25), jeśli zrobię FILES= ls * .zip ; for fname in "${FILES}"..., ale działa zgodnie z oczekiwaniami z for fname in *.zip ; do....
  • Użyj for file in *.zip, a nie `ls ...`. Sugestia @cuonglm ' jest taka, aby *.zip rozwijało się do zera, gdy wzorzec nie ' t pasuje do dowolnego pliku. ls bez argumentów wyświetla bieżący katalog.
  • To pytanie wyjaśnia, dlaczego analizowanie danych wyjściowych ls ma być generalnie uniknięto: Dlaczego nie przeanalizować ls? ; zobacz także link u góry tej strony do artykułu BashGuide ' s ParsingLs .
  • Możliwy duplikat Dlaczego mój skrypt powłoki blokuje się białymi znakami lub innymi znakami specjalnymi?

Odpowiedź

W bash możesz ustawić opcję nullglob tak, aby wzorzec, nie pasuje do niczego „znika”, zamiast traktować go jako literał:

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

W skrypcie powłoki POSIX wystarczy sprawdzić, czy fname istnieje (i jednocześnie z [ -f ], sprawdź, czy jest to zwykły plik (lub dowiązanie symboliczne do zwykłego pliku), a nie inne typy, takie jak katalog / fifo / urządzenie .. .):

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

Zamień [ -f "$fname" ] na [ -e "$fname" ] || [ -L "$fname ], jeśli chcesz przejrzeć wszystkie (nieukryte) pliki, których nazwa kończy się na .zip niezależnie od ich typ.

Zamień *.zip na .*.zip .zip *.zip, jeśli chcesz również uwzględnić pliki ukryte, których nazwa kończy się na .zip.

Komentarze

  • shopt -s nullglob nie działa dla ja na Ubuntu 17.04, ale [ -f "$fname" ] || continue działało dobrze.
  • @koppor Wygląda na to, że nie ' nie używasz bash.
  • +1 dla rozwiązania POSIX.

Odpowiedź

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 

W komentarzu wspominasz tutaj o wywołaniu funkcji …

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 * 

Odpowiedź

Użyj funkcji find

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

MUSISZ wyeksportować swoją funkcję powłoki z export -f, aby to zadziałało. Teraz find wykonuje bash, który wykonuje funkcję powłoki i pozostaje tylko na bieżącym poziomie katalogu.

Komentarze

  • Który powtarza się w podkatalogach, a ja chcę wywołać funkcję bash (nie skrypt) dla dopasowań.
  • @symcbean I ' został zmieniony w celu ograniczenia do pojedynczego katalogu i obsługi funkcji basha

Odpowiedź

Zamiast tego z:

FILES=`ls *.zip` 

Spróbuj:

FILES=`ls * | grep *.zip` 

W ten sposób, jeśli ls zawiedzie (co w twoim przypadku) spowoduje grepowanie nieudanych danych wyjściowych i zwrócenie jako pusta zmienna.

current file is <---Blank Here 

Możesz dodać do tego logikę, aby zwracała „Nie Plik znaleziony „

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

W ten sposób, jeśli poprzednie polecenie zakończyło się powodzeniem (zakończyło się wartością 0), wydrukuje bieżący plik, w przeciwnym razie wypisze” Nie Znalezione pliki „

Komentarze

  • I thi nk, to zły pomysł, aby dodać jeszcze jeden proces (grep) zamiast próbować rozwiązać problem za pomocą lepszego narzędzia (find ) lub zmieniając odpowiednie ustawienie dla bieżącego rozwiązania (z shopt -s nullglob)
  • Zgodnie z komentarzem OP ' w oryginalnym poście shopt -s nullglob nie działa. Próbowałem find podczas weryfikowania odpowiedzi i wciąż kończyło się to niepowodzeniem. Myślę, że z powodu rzeczy eksportowej, którą powiedział Dani.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *