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.