Подтвердить что ты не робот

Как использовать цикл for для каждого цикла для путей по файлам в bash?

Следующая команда пытается перечислить все *.txt файлы в текущем каталоге и обрабатывать их по одному:

for line in "find . -iname '*.txt'"; do 
     echo $line
     ls -l $line; 
done

Почему возникает следующая ошибка?:

ls: invalid option -- 'e'
Try `ls --help' for more information.
4b9b3361

Ответ 1

Вот лучший способ перебрать файлы, поскольку он обрабатывает пробелы и символы новой строки в именах файлов:

find . -type f -iname "*.txt" -print0 | while IFS= read -r -d $'\0' line; do
    echo "$line"
    ls -l "$line"    
done

Ответ 2

for -loop будет выполнять итерацию по каждой (пробельной) записи в предоставленной строке.

Фактически вы не выполняете команду find, но предоставляете ее как строку (которая выполняется итерацией for -loop). Вместо двойных кавычек используйте либо обратные такты, либо $():

for line in $(find . -iname '*.txt'); do 
     echo "$line"
     ls -l "$line"
done

Кроме того, если ваши пути/имена файлов содержат пробелы, этот метод терпит неудачу (поскольку for -loop выполняет итерацию по элементам, разделенным пробелами). Вместо этого лучше использовать метод, описанный в dogbanes answer.


Чтобы прояснить вашу ошибку:

Как сказано, for line in "find . -iname '*.txt'"; выполняет итерацию по всем разделенным пробелом элементам:

  • найти
  • .
  • -iname
  • '*. txt' (я думаю...)

Первые два не приводят к ошибке (помимо нежелательного поведения), но третий является проблематичным, поскольку он выполняет:

ls -l -iname

Многие команды (bash) могут комбинировать параметры одного символа, поэтому -iname совпадает с -i -n -a -m -e. И voila: ваша ошибка invalid option -- 'e'!

Ответ 3

Более компактная версия, работающая с пробелами и символами новой строки в имени файла:

find . -iname '*.txt' -exec sh -c 'echo "{}" ; ls -l "{}"' \;

Ответ 4

Используйте замену команд вместо кавычек для выполнения find вместо передачи команды в виде строки:

for line in $(find . -iname '*.txt'); do 
     echo $line
     ls -l $line; 
done