Jak uzyskać listę plików w katalogu w skrypcie powłoki?


159

Próbuję uzyskać zawartość katalogu za pomocą skryptu powłoki.

Mój skrypt to:

for entry in `ls $search_dir`; do
    echo $entry
done

gdzie $search_dirjest ścieżka względna. Jednak $search_dirzawiera wiele plików ze spacjami w nazwach. W takim przypadku ten skrypt nie działa zgodnie z oczekiwaniami.

Wiem, że mógłbym użyć for entry in *, ale to działałoby tylko w moim bieżącym katalogu.

Wiem, że mogę przejść do tego katalogu, użyć go, for entry in *a następnie zmienić z powrotem, ale moja szczególna sytuacja uniemożliwia mi to.

Mam dwóch ścieżek względnych $search_diri $work_dir, a ja mam do pracy na obu jednocześnie, czytając im tworzenie / usuwanie plików w nich itd.

Więc co mam teraz zrobić?

PS: używam basha.

Odpowiedzi:


274
for entry in "$search_dir"/*
do
  echo "$entry"
done

4
Czy możesz wyjaśnić, dlaczego for entry in "$search_dir/*"nie działa? Dlaczego musimy umieszczać /*poza cudzysłowami?
mrgloom

6
@mrgloom: Ponieważ musisz pozwolić powłoce na globalizację symboli wieloznacznych.
Ignacio Vazquez-Abrams

nie findbyłby szybszy?
Alexej Magura

4
Rozwiązanie daje pełną ścieżkę. A co jeśli chciałbym tylko wyświetlić listę rzeczy znajdujących się w bieżącym katalogu?
ThatsRightJack

1
@mrgloom, jeśli chcesz, możesz to osiągnąć dziękifor entry in "${search_dir}/*"
funilrys

28

Inne odpowiedzi tutaj są świetne i odpowiadają na twoje pytanie, ale to jest najlepszy wynik Google dla „bash pobierz listę plików w katalogu” (którego szukałem, aby zapisać listę plików), więc pomyślałem, że opublikuję odpowiedź na ten problem:

ls $search_path > filename.txt

Jeśli chcesz tylko określonego typu (np. Dowolne pliki .txt):

ls $search_path | grep *.txt > filename.txt

Zauważ, że $ search_path jest opcjonalne; ls> nazwa_pliku.txt zrobi bieżący katalog.


Nie ma potrzeby używania grepa, aby pobrać tylko pliki .txt: `ls $ ścieżka_wyszukiwania / *. Txt> nazwa_pliku.txt '. Ale co ważniejsze, nie należy używać danych wyjściowych polecenia ls do analizowania nazw plików.
Victor Zamanian,

1
@VictorZamanian, czy możesz wyjaśnić, dlaczego nie powinniśmy używać danych wyjściowych programu lsdo analizowania nazw plików? Nie słyszałem o tym wcześniej.
samurai_jane

1
@samurai_jane Istnieje wiele linków do podania dotyczących tego tematu, ale oto pierwszy wynik wyszukiwania: mywiki.wooledge.org/ParsingLs . Widziałem nawet tutaj pytanie o SO, twierdzące, że powody nie analizowania wyjścia ls to BS i było to bardzo szczegółowe. Ale odpowiedzi / odpowiedzi nadal twierdziły, że to zły pomysł. Zobacz: unix.stackexchange.com/questions/128985/…
Victor Zamanian

26

Jest to sposób na zrobienie tego, w którym składnia jest dla mnie łatwiejsza do zrozumienia:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./jest bieżącym katalogiem roboczym, ale można go zastąpić dowolną ścieżką
*.txtzwraca cokolwiek.txt
Możesz łatwo sprawdzić, co zostanie wyświetlone, wpisując lspolecenie bezpośrednio w terminalu.

Zasadniczo tworzysz zmienną yourfilenameszawierającą wszystko, co polecenie list zwraca jako oddzielny element, a następnie przechodzisz przez nią w pętli. Pętla tworzy zmienną tymczasową, eachfilektóra zawiera pojedynczy element zmiennej, przez którą przechodzi, w tym przypadku nazwę pliku. Niekoniecznie jest to lepsze niż inne odpowiedzi, ale uważam to za intuicyjne, ponieważ znam już lspolecenie i składnię pętli for.


Działa to dobrze w przypadku szybkiego, nieformalnego skryptu lub jednej linijki, ale zepsuje się, jeśli nazwa pliku zawiera znaki nowej linii, w przeciwieństwie do rozwiązań opartych na globach.
Soren Bjornstad

@SorenBjornstad dzięki za radę! Nie wiedziałem, że w nazwach plików dozwolone są nowe linie - jakie rodzaje plików mogą je zawierać? Na przykład, czy jest to coś, co występuje powszechnie?
rrr

Z tego powodu znaki nowej linii w nazwach plików są złe i o ile wiem, nie ma uzasadnionego powodu, aby ich używać. Sam nigdy nie widziałem takiego na wolności. To powiedziawszy, jest całkowicie możliwe złośliwe tworzenie nazw plików z nowymi wierszami w taki sposób, aby to wykorzystać. (Na przykład, wyobraź sobie katalog zawierający pliki A, Bi C. Tworzysz pliki o nazwie B\nCi D, a następnie wybierasz ich usunięcie. Oprogramowanie, które nie obsługuje tego prawa, może zamiast tego usunąć istniejące wcześniej pliki B i C, nawet jeśli nie masz pozwolenie.)
Soren Bjornstad

19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done

11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

1
Wiem, że to jest dość stare, ale wydaje mi się, że nie otrzymuję ostatniego xargs -0 -i echo "{}"polecenia, czy możesz mi trochę wyjaśnić? W szczególności, co robi ta -i echo "{}"część? Czytałem również ze manstrony, która -ijest teraz przestarzała i powinniśmy użyć -Iinsted.
drkg4b

1
-izastępuje {}arg.
Noel Yap

Dzięki! Jest to przydatne, również dla wolno myślących, takich jak ja, myślę, że {}jest to ciąg, który jest zastępowany dopasowaniami przez findpolecenie.
drkg4b

3
dlaczego używasz xargs? domyślnie finddrukuje to, co znajdzie ... możesz usunąć wszystko z -print0.
gniourf_gniourf

1
Takie postępowanie nie obsługiwałoby dobrze wpisów plików ze spacjami.
Noel Yap,

4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

Wariacje

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Uwagi:

  • . : bieżący folder
  • usuń, -maxdepth 1aby wyszukiwać rekurencyjnie
  • -type f: znajdź pliki, a nie katalogi ( d)
  • -not -path '*/\.*' : nie wracaj .hidden_files
  • sed 's/^\.\///g': usuń przedrostek ./z listy wyników

0

Oto inny sposób wyświetlania plików w katalogu (przy użyciu innego narzędzia, nie tak wydajnego, jak niektóre inne odpowiedzi).

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *Wyprowadza wszystkie pliki z bieżącego katalogu. forPętla iteracje nad każdą nazwę pliku i wydruki na standardowe wyjście.

Dodatkowo, jeśli szukasz katalogów w katalogu, umieść to w forpętli:

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d sprawdza, czy plik jest katalogiem.


0

Zaakceptowana odpowiedź nie zwróci przedrostka plików z rozszerzeniem. Aby to zrobić, użyj

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done

-1

W wersji Linux, z którą pracuję (x86_64 GNU / Linux), działa:

for entry in "$search_dir"/*
do
  echo "$entry"
done

-1: jest identyczna z akceptowaną odpowiedzią, z tym wyjątkiem, że nie cytuje zmiennych. Brak cudzysłowu $search_diroznacza, że ​​nie działa, jeśli w nazwie katalogu jest spacja. (W tym przypadku możesz uniknąć cytowania $entry, ale lepiej byłoby to zacytować.)
Soren Bjornstad

Mylisz się. Moje rozwiązanie nie jest takie samo, nawet jeśli jest podobne do zaakceptowanej odpowiedzi. I nie jest źle, to działa. Oto, co wypróbowałem na różnych systemach uniksowych, w tym Mac OS, i działało na każdym z nich. Plik ze spacjami, nawet jeśli ma początkowe spacje w nazwie, jest poprawnie wyświetlany. search_dir =. dla wpisu w $ search_dir / *; zrobić echo $ entry; done ./ aa aaa.txt ./add ./apm_tables.txt Możesz głosować na rozwiązania tylko wtedy, gdy możesz udowodnić, że rozwiązanie NIE DZIAŁA lub daje błędne wyniki.
Andrushenko Alexander

Jeśli bliżej przeczytasz mój komentarz, zobaczysz, że problem polega na spacji w nazwie katalogu , a nie w nazwie pliku . To coś innego niż problem, na który natrafił pytający, ale nadal oznacza, że ​​kod może się nieumyślnie złamać! Oto przykład tego, co nie działa: mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. Pętla działa dwukrotnie, raz drukuje xi drugi raz y/*, co prawdopodobnie nie jest oczekiwanym wynikiem.
Soren Bjornstad

I uważam, że zmienne niecytowane niewłaściwie są niebezpiecznie błędne, co jest podstawą do negatywnego głosu. Rozważmy ten kod, aby usunąć wszelkie podkatalogi katalogu wyszukiwania, pozostawiając pliki: search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Jeśli xw bieżącym katalogu znajduje się również katalog, jest on usuwany tylko dlatego, że nie zacytowałeś swojej zmiennej!
Soren Bjornstad

Cóż, zawsze bierzesz kod utworzony dla jednego zadania, nieznacznie zmieniasz zadanie i kod staje się nieprawidłowy. Na zadane pytanie moje proponowane rozwiązanie działa. Ale ... będąc programistą, muszę zgodzić się z twoimi argumentami: jeśli możemy uczynić kod bardziej solidnym i ogólnym, powinniśmy to zrobić. Więc dodam qoutes. To sprawi, że moja odpowiedź będzie identyczna z odpowiedzią podaną powyżej, ale ze względu na dyskusję (która może być przydatna dla kogoś) zostawiam również odpowiedź.
Andrushenko Alexander

-1

Wystarczy wpisać to proste polecenie:

ls -d */

Czy możesz wyjaśnić @Michael Sisko
Vipul

ls (lista) -d (wyświetla tylko katalogi) * / (dowolnego wzorca, ukośnik odwrotny ogranicza listę tylko do katalogów).
Michael Sisko
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.