Як отримати список файлів у каталозі в сценарії оболонки?


159

Я намагаюся отримати вміст каталогу за допомогою скрипту оболонки.

Мій сценарій:

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

де $search_dirвідносний шлях. Однак $search_dirмістить багато файлів з пробілами в їх назвах. У цьому випадку цей сценарій не працює так, як очікувалося.

Я знаю, що міг би скористатися for entry in *, але це працювало б лише для мого поточного каталогу.

Я знаю, що я можу перейти до цього каталогу, використовувати for entry in *потім змінити назад, але моя конкретна ситуація заважає мені це робити.

У мене є дві відносні шляхи $search_dirі $work_dir, і я повинен працювати на обох одночасно, їх читання , створення / видалення файлів в них і т.д.

То що мені робити зараз?

PS: Я використовую bash.

Відповіді:


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

4
Чи можете ви пояснити, чому for entry in "$search_dir/*"не працюють? Чому нам потрібно розміщувати /*поза цитатами?
mrgloom

6
@mrgloom: Тому що вам потрібно дозволити оболонці проголосувати шаблоном.
Ігнасіо Васкес-Абрамс

не findбуло б швидше?
Олексій Магура

4
Рішення дає повний шлях. Що робити, якщо я просто хотів перерахувати те, що було у поточному каталозі?
ThatsRightJack

1
@mrgloom, якщо ви хочете зробити це, ви можете досягти цьогоfor entry in "${search_dir}/*"
funilrys

28

Інші відповіді тут чудові і відповідають на ваше запитання, але це найкращий результат google для "bash get list of files in directory", (який я шукав, щоб зберегти список файлів), тому я подумав, що опублікую повідомлення відповідь на цю проблему:

ls $search_path > filename.txt

Якщо ви хочете лише певного типу (наприклад, будь-які .txt файли):

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

Зауважте, що $ search_path необов’язковий; ls> filename.txt виконає поточний каталог.


Не потрібно використовувати grep, щоб отримати лише .txt файли: `ls $ search_path / *. Txt> filename.txt '. Але що ще важливіше, не слід використовувати вихід команди ls для розбору імен файлів.
Віктор Заманян

1
@VictorZamanian, чи можете ви пояснити, чому ми не повинні використовувати вихід lsдля розбору імен файлів? Я про це раніше не чув.
samurai_jane

1
@samurai_jane Існує багато посилань на цю тему, але ось один перший результат пошуку: mywiki.wooledge.org/ParsingLs . Я навіть бачив тут питання на ЗУ, де стверджувалося, що причини непідконтрольного виводу ls були BS, і це було дуже детально. Але відповіді / відповіді все ж стверджували, що це погана ідея. Подивіться: unix.stackexchange.com/questions/128985/…
Віктор Заманян

26

Це спосіб зробити це там, де синтаксис простіший для мене, щоб зрозуміти:

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

./- це поточний робочий каталог, але його можна замінити будь-яким шляхом, що
*.txtповертає будь-що.txt
Ви можете перевірити, що буде легко перераховано, ввівшиls команду прямо в термінал.

По суті, ви створюєте змінну, yourfilenamesщо містить усе, що повертається командою list, як окремий елемент, і потім ви перебираєте її. Цикл створює тимчасову змінну, eachfileяка містить один елемент змінної, через яку вона проходить цикл, в даному випадку - ім'я файлу. Це не обов'язково краще, ніж інші відповіді, але я вважаю це інтуїтивно зрозумілим, оскільки я вже знайомий із lsкомандою та синтаксисом for для циклу.


Це працює добре для швидкого, неформального сценарію або однолінійного файлу, але воно порушиться, якщо ім'я файлу містить нові рядки, на відміну від глобальних рішень.
Сорен Бьорнстад

@SorenBjornstad дякую за пораду! Я не знав, що нові файли дозволяються в іменах - які файли можуть мати їх? Мовляв, це звичайне, що трапляється?
rrr

Нові рядки у назви файлів є злі з цієї причини, і наскільки я знаю, немає законних причин використовувати їх. Сам я ніколи не бачив у дикій природі. При цьому, цілком можливо зловмисно побудувати імена файлів за допомогою нових рядків таким чином, щоб цим скористатися. (Наприклад, уявіть собі каталог, що містить файли A, Bі C. Ви створюєте файли, що називаються, B\nCі Dпотім вибираєте їх видалити. Програмне забезпечення, яке не обробляє це право, може в кінцевому рахунку видалити попередні файли B і C, навіть якщо у вас не було дозвіл на це.)
Soren Bjornstad


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

1
Я знаю, що це досить старе, але я не можу отримати останню xargs -0 -i echo "{}"команду, хочете трохи пояснити мене? Зокрема, що таке -i echo "{}"частина? Також я читаю зі manсторінки, яка -iзараз застаріла, і ми повинні використовувати -Iinsted.
drkg4b

1
-iзамінює {}арг.
Ноель Яп

Дякую! Це корисно, також для повільно налаштованих, як я, я думаю, що {}це рядок, який замінюється на відповідність findкомандою.
drkg4b

3
для чого ти користуєшся xargs? за замовчуванням findдрукує те, що знайде ... ви можете видалити все з -print0.
gniourf_gniourf

1
Це не дозволить добре обробляти записи файлів з пробілами.
Ноель Яп

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

Варіації

$ 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

Примітки:

  • . : поточна папка
  • видалити -maxdepth 1 для пошуку рекурсивно
  • -type f : знайти файли, а не каталоги (d )
  • -not -path '*/\.*' : не повертаються .hidden_files
  • sed 's/^\.\///g': видаліть попередньо ./зі списку результатів

0

Ось ще один спосіб перерахування файлів всередині каталогу (використовуючи інший інструмент, не такий ефективний, як деякі інші відповіді).

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

echo *Виводить усі файли поточного каталогу. forЦикл перебирає кожне ім'я файлу і виводить на стандартний висновок.

Крім того, якщо ви шукаєте каталоги всередині каталогу, розмістіть його всередині forциклу:

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

test -d перевіряє, чи файл є каталогом.


0

Прийнята відповідь не поверне файли з префіксом a. Для цього використовуйте

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

-1

У версії Linux я працюю з (x86_64 GNU / Linux) наступними роботами:

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

-1: Це ідентично прийнятої відповіді, за винятком того, що він не цитує змінних. Якщо не цитувати, $search_dirце означає, що воно порушується, якщо в імені каталогу є пробіл. (У цьому випадку ви можете піти, не цитуючи $entry, але краще було б цитувати це.)
Soren Bjornstad

Ви неправі. Моє рішення не те саме, навіть коли воно схоже на прийняту відповідь. І це НЕ неправильно, це працює. Ось що я спробував у різних системах Unix, включаючи Mac OS, і він працював на кожній з них. Файл із пробілами, навіть якщо в імені є провідні пробіли, відображається належним чином. search_dir =. для запису в $ search_dir / *; зробити ехо $ запис; зроблено ./aa aaa.txt ./add ./apm_tables.txt Ви можете проголосувати рішення, коли ви зможете довести, що рішення НЕ ПРАЦЮЄ або не дає помилкових результатів.
Олександр

Якщо ви прочитаєте мій коментар трохи ближче, ви побачите, що проблема полягає в пробілах у назві каталогу , а не в назві файлу . Це інакше, ніж питання, з яким стикався запитуючий, але це все ще означає, що код може ненавмисно зламатися! Ось приклад того , що не працює: mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. Цикл працює два рази, один раз друкується xі вдруге y/*, що, мабуть, не є очікуваним результатом.
Сорен Бьорнстад

І я вважаю неправильно цитовані змінні небезпечно неправильними, що є підставою для зворотного поступу. Розглянемо цей код , щоб видалити будь-які підкаталоги каталогу пошуку, залишаючи файли: search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Якщо xв поточному каталозі також є каталог, він видаляється, лише тому, що ви не цитували змінну!
Сорен Бьорнстад

Ну, ви завжди приймаєте код, створений для однієї задачі, трохи змінюйте завдання, і код стає неправильним. Для поставленого запитання працює запропоноване нами рішення. Але ... будучи розробником, я повинен погодитися з вашими аргументами: якщо ми можемо зробити код більш надійним і загальним, ми повинні це зробити. Тож я додам цити. Це зробить мою відповідь ідентичною відповіді, наведеною вище, але заради дискусії (яка може бути корисною для когось) я залишаю відповідь також.
Олександр

-1

Просто введіть цю просту команду:

ls -d */

Чи можете ви пояснити @Michael Sisko
Vipul

ls (list) -d (перелік лише каталогів) * / (будь-якого шаблону, зворотна косої риски обмежує список лише до каталогів).
Майкл Сісько
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.