Przeglądaj wszystkie pliki z określonym rozszerzeniem

109
for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Chcę przejrzeć każdy plik w bieżącym folderze i sprawdzić, czy pasuje do określonego rozszerzenia. Powyższy kod nie działa, czy wiesz dlaczego?

AR89
źródło
4
O co chodzi for i in $(ls *.java); do echo "do something with file $i"; done?
mówca
nie ma sposobu, aby to naprawić, jeśli oświadczenie?
AR89
1
Porównujesz $iz literałem „* .java”; ekspansja wzorca nie jest tutaj wykonywana.
chepner
Aby naprawić instrukcję if tak, jak ją masz, użyj if [[ $i == *.java ]]; then.. (zwróć uwagę na podwójne [[]] si niecytowane * .java).
ten drugi facet
2
Nie analizujls - zaakceptuj odpowiedź @ chepner
glenn jackman

Odpowiedzi:

201

Żadne wymyślne sztuczki nie są potrzebne:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

Strażnik zapewnia, że ​​jeśli nie ma pasujących plików, pętla zakończy działanie bez próby przetworzenia nieistniejącej nazwy pliku *.java. W bash(lub powłokach obsługujących coś podobnego) możesz użyć nullglobopcji, aby po prostu zignorować nieudane dopasowanie i nie wchodzić w treść pętli.

shopt -s nullglob
for i in *.java; do
    ...
done
Chepner
źródło
5
Najprostszym sposobem jest dodanie innego wzoru: for i in *.java *.cpp; do. Jeśli rozszerzony wzory odblokowane bashz shopt -s extglob, można napisać for i in *.@(java|cpp); do.
chepner
8
Będzie tak, jeśli faktycznie pasuje do jakichkolwiek plików. Musisz użyć, shopt -s nullglobaby niepasujący wzorzec rozszerzał się do pustej sekwencji, a nie był traktowany dosłownie.
chepner
1
@zygimantus tak, powinno, o ile bieżący katalog jest miejscem, z którego uruchamiany jest skrypt. Jeśli nie jesteś w katalogu, w którym chcesz się znaleźć, powinieneś cdprzejść do tego katalogu przed rozpoczęciem forpętli
danielsdesk
1
@puk To zależy od opcji powłoki. Domyślnie niepasujący wzorzec jest traktowany jako ciąg literału. Możesz ustawić nullglobtraktowanie go jako pustej sekwencji lub ustawić failglobjako błąd. (Jeśli ustawisz oba, failglobpierwszeństwo ma.)
chepner
3
@chepner Może to być przydatne dla osób nie będących ekspertami, jeśli w odpowiedzi zostanie to zaznaczone, ponieważ ktoś może go skopiować, wkleić i stwierdzić, że nie działa. Doskonałym przykładem może być ktoś, kto konwertuje wszystkie pliki.jpg” na „ .png” przed wykonaniem kluczowej funkcji
puk,
13

prawidłowa odpowiedź to @ chepner's

EXT=java
for i in *.${EXT}; do
    ...
done

jednak oto mała sztuczka, aby sprawdzić, czy nazwa pliku ma dane rozszerzenia:

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done
umläute
źródło
jak by to było ze zmienną extzamiast .java?
AR89
13

Rekurencyjnie dodawaj podfoldery,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done
luismartingil
źródło
zamiast find . -name "*.java" -type f -exec echo \{\} \; uniknąć błędnej interpretacji wynikówfind
umläute
1
A jeśli nie, przynajmniej powinieneś cytować "$i"wewnątrz pętli.
tripleee
2
To nie zadziała, jeśli którykolwiek z plików ma w nazwie spację.
codeforester
1
@codeforester Miałem dokładnie ten problem i naprawiłem go metodą z tej odpowiedzi: askubuntu.com/a/343753/551184
fsinisi90
7

Pętli wszystkich plików kończących się: .img, .bin, .txtprzyrostek i wydrukować nazwę pliku:

for i in *.img *.bin *.txt;
do
  echo "$i"
done

Lub rekurencyjnie (znajdź także we wszystkich podkatalogach):

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done
Benny
źródło
3

Zgadzam się z innymi odpowiedziami dotyczącymi prawidłowego sposobu przeglądania plików. Jednak PO zapytał:

Powyższy kod nie działa, czy wiesz dlaczego?

Tak!

Doskonały artykuł Jaka jest różnica między testem [i [?] Wyjaśnia szczegółowo, że pośród innych różnic nie można używać polecenia expression matchinglub pattern matchingw ramach testpolecenia (co jest skrótem [)

Przedstaw nowy test [[stary test [Przykład

Dopasowanie do wzorca = (lub ==) (niedostępne) [[$ name = a *]] || echo „nazwa nie zaczyna się od„ a ”: $ name”

Wyrażenie regularne = ~ (niedostępne) [[$ (data) = ~ ^ Pt \ ... \ 13]] && echo "Jest piątek trzynastego!"
pasujący

To jest powód niepowodzenia twojego skryptu. Jeśli OP jest zainteresowany odpowiedzią ze [[składnią (która ma tę wadę, że nie jest obsługiwana na tylu platformach, co [polecenie), byłbym szczęśliwy, aby edytować moją odpowiedź, aby ją uwzględnić.

EDYCJA: przydatne byłyby wszelkie wskazówki dotyczące formatowania danych w odpowiedzi jako tabeli!

user000001
źródło
2

jak mówi @chepner w swoim komentarzu, porównujesz $ i do ustalonego ciągu.

Aby rozwinąć i naprawić sytuację, powinieneś użyć [[]] z operatorem regex = ~

na przykład:

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

wyrażenie regularne po prawej stronie = ~ jest sprawdzane pod kątem wartości operatora po lewej stronie i nie powinno być cytowane (cudzysłów nie spowoduje błędu, ale zostanie porównany z ustalonym ciągiem, więc najprawdopodobniej zakończy się niepowodzeniem "

ale odpowiedź @chepnera powyżej przy użyciu globu jest znacznie wydajniejszym mechanizmem.

peteches
źródło
jak by to było ze zmienną extzamiast .java?
AR89
1
ACK, nie ma potrzeby używania wyrażenia regularnego: if [[ $i == *.java ]]lub if [[ $i == *.$ext ]]. Ale nie analizuj ls
glenn jackman
1

Uważam, że to rozwiązanie jest bardzo przydatne. Wykorzystuje -oropcję w find:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

Będzie znaleźć pliki z rozszerzeniem tex, pngi pdf.

f0nzie
źródło