Bash-скрипт для выполнения команды над всеми файлами в каталоге

189

Может кто-нибудь, пожалуйста, предоставьте код, чтобы сделать следующее: Предположим, что есть каталог файлов, все из которых нужно запускать через программу. Программа выводит результаты на стандарт. Мне нужен script, который войдет в каталог, выполнит команду для каждого файла и разделит вывод на один большой выходной файл.

Например, для запуска команды в 1 файл:

$ cmd [option] [filename] > results.out
  • 3
    Я хотел бы добавить к вопросу. Можно ли это сделать с помощью xargs? например, ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out
  • 0
    Может, но вы, вероятно , не хотите использовать ls для управления xargs . Если cmd написан грамотно, возможно, вы можете просто использовать cmd <wildcard> .
Теги:
scripting

6 ответов

281
Лучший ответ

Следующий bash код передаст $file для команды, где $file будет представлять каждый файл в каталоге /dir

for file in /dir/*
do
  cmd [option] "$file" >> results.out
done

Пример

el@defiant ~/foo $ touch foo.txt bar.txt baz.txt
el@defiant ~/foo $ for i in *.txt; do echo "hello $i"; done
hello bar.txt
hello baz.txt
hello foo.txt
  • 16
    Если в /dir/ нет файлов, то цикл все равно выполняется один раз со значением '*' для $file , что может быть нежелательно. Чтобы избежать этого, включите nullglob на время цикла. Добавьте эту строку перед циклом shopt -s nullglob и эту строку после цикла shopt -u nullglob #revert nullglob back to it's normal default state .
  • 38
    +1, и это просто стоило мне всей моей коллекции обоев. все после меня, используйте двойные кавычки. «$ Файл»
Показать ещё 6 комментариев
124

Как насчет этого:

find /some/directory -maxdepth 1 -type f -exec cmd option {} \; > results.out
  • Аргумент
  • -maxdepth 1 не позволяет найти из рекурсивного спуска в любые подкаталоги. (Если вы хотите, чтобы такие вложенные каталоги обрабатывались, вы можете опустить это.)
  • -type -f указывает, что будут обрабатываться только простые файлы.
  • -exec cmd option {} сообщает, что он запускает cmd с указанным option для каждого найденного файла, при этом имя файла заменяется на {}
  • \; обозначает конец команды.
  • Наконец, вывод из всех индивидуальных cmd исполнений перенаправляется на results.out

Однако, если вы заботитесь о порядке обработки файлов, вы может быть лучше писать петлю. Я думаю, что find обрабатывает файлы в порядке inode (хотя я мог ошибаться в этом), что может и не быть вы хотите.

  • 1
    Это правильный способ обработки файлов. Использование цикла for подвержено ошибкам по многим причинам. Также сортировку можно выполнить с помощью других команд, таких как stat и sort , что, конечно, зависит от критериев сортировки.
  • 0
    если бы я хотел запустить две команды, как бы я связал их после опции -exec ? Должен ли я обернуть их в одинарные кавычки или что-то?
Показать ещё 2 комментария
33

Я делаю это на моем малине pi из командной строки, запустив:

for i in *;do omxplayer "$i";done
  • 2
    Это должно быть самое простое решение
2

Один быстрый и грязный способ, который иногда выполняет эту работу:

find directory/ | xargs  Command 

Например, чтобы найти количество строк во всех файлах в текущем каталоге, вы можете:

find . | xargs wc -l
  • 1
    не будет работать для файлов с символами новой строки в их именах
  • 3
    @Hubert Почему у вас есть переводы строк в ваших именах файлов ?!
Показать ещё 3 комментария
2

Основываясь на подходе @Jim Lewis:

Вот быстрое решение, использующее find, а также сортировку файлов по дате их изменения:

$ find  directory/ -maxdepth 1 -type f -print0 | \
  xargs -r0 stat -c "%y %n" | \
  sort | cut -d' ' -f4- | \
  xargs -d "\n" -I{} cmd -op1 {} 

Для сортировки см.

http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time

  • 0
    это не будет работать, если файлы имеют переводы строк в своих именах
  • 1
    @HubertKario Возможно, вы захотите прочитать больше о -print0 для find и -0 для xargs которые используют нулевой символ вместо пробела (включая символы новой строки).
Показать ещё 1 комментарий
1

Мне нужно было скопировать все файлы .md из одного каталога в другой, вот что я сделал.

for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done

Это довольно трудно прочитать, поэтому давайте сломаем его.

первый cd в каталог с вашими файлами,

for i in **/*.md; для каждого файла в вашем шаблоне

mkdir -p ../docs/"$i" сделать этот каталог в папке docs за пределами папки, содержащей ваши файлы. Что создает дополнительную папку с тем же именем, что и этот файл.

rm -r ../docs/"$i" удалите дополнительную папку, созданную в результате mkdir -p

cp "$i" "../docs/$i" Скопировать фактический файл

echo "$i -> ../docs/$i" Эхо, что ты сделал

; done Жить счастливо после

  • 0
    Примечание: для работы ** необходимо установить globstar оболочки shopt -s globstar : shopt -s globstar

Ещё вопросы

Сообщество Overcoder
Наверх
Меню