Как получить список файлов в каталоге в скрипте оболочки?

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.

jrharshath
источник

Ответы:

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 сделает текущий каталог.

Tegan
источник
Нет необходимости использовать grep для получения только файлов .txt: `ls $ search_path / *. Txt> filename.txt '. Но что более важно, не следует использовать вывод команды ls для разбора имен файлов.
Виктор Заманян
1
@VictorZamanian, можете ли вы уточнить, почему мы не должны использовать выходные данные lsдля разбора имен файлов? Не слышал об этом раньше.
samurai_jane
1
@samurai_jane Есть много ссылок по этой теме, но вот один из первых результатов поиска: mywiki.wooledge.org/ParsingLs . Я даже видел здесь вопрос о SO, утверждая, что причинами того, что выходные данные ls не были проанализированы, были BS, и это было очень проработано. Но ответы / ответы все еще утверждали, что это была плохая идея. Посмотрите: unix.stackexchange.com/questions/128985/…
Виктор Заманян
26

Это способ сделать это там, где синтаксис для меня проще понять:

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

./является текущим рабочим каталогом, но может быть заменен любым путем и
*.txtвозвращает что угодно.
Вы можете легко проверить, что будет в списке, введя lsкоманду прямо в терминал.

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

ррр
источник
Это работает хорошо для быстрого, неформального скрипта или однострочного текста, но оно сломается, если имя файла содержит новые строки, в отличие от глобальных решений.
Сорен Бьорнстад
@SorenBjornstad спасибо за совет! Я не знал, что в именах файлов разрешены переводы строк - какие файлы могут их иметь? Мол, это часто встречается?
ррр
Новые строки в именах файлов являются злыми по этой причине, и, насколько я знаю, нет законных оснований использовать их. Я никогда не видел никого в дикой природе. Тем не менее, вполне возможно злонамеренно создать имена файлов с символами новой строки таким образом, чтобы использовать это. (Например, представьте каталог, содержащий файлы A, Bи C. Вы создаете файлы с именем B\nCand D, а затем выбираете их удаление. Программное обеспечение, которое не обрабатывает это право, может в итоге удалить ранее существующие файлы B и C, даже если у вас их не было. разрешение на это.)
Сорен Бьорнстад
19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done
ghostdog74
источник
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 проверяет, является ли файл каталогом.

SnoopDogg
источник
0

Принятый ответ не вернет префикс файлов с. Для этого используйте

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done
TrevTheDev
источник
-1

В версии для Linux, с которой я работаю (x86_64 GNU / Linux), выполняются следующие работы:

for entry in "$search_dir"/*
do
  echo "$entry"
done
Андрушенко Александр
источник
-1: это идентично принятому ответу за исключением того, что он не заключает в кавычки переменные. Отсутствие кавычек $search_dirозначает, что он разрывается, если в имени каталога есть пробел. (В этом случае вы можете избежать цитирования $entry, но было бы лучше процитировать его.)
Сорен Бьорнстад,
Вы неправы. Мое решение не то же самое, даже когда оно похоже на принятый ответ. И это НЕ неправильно, это работает. Вот что я пробовал на разных системах Unix, включая Mac OS, и это работало на каждой из них. Файл с пробелами, даже если он содержит начальные пробелы в имени, отображается правильно. search_dir =. для записи в $ search_dir / *; сделать echo $ entry; готово ./aaaaa.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 */
Михаил Сиско
источник
Не могли бы вы дать какое-то объяснение @ Михаил Сиско
Vipul
ls (список) -d (список только каталогов) * / (для любого шаблона обратная косая черта ограничивает список только каталогами).
Михаил Сиско