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?


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

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

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

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

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!


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.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.