цикл по `ls` приводит к скрипту оболочки bash

цикл по `ls` приводит к скрипту оболочки bash

Есть ли у кого-нибудь шаблон скрипта оболочки для выполнения чего-либо со lsсписком имен каталогов, прохода по каждому из них и выполнения чего-либо?

Я планирую сделать так, ls -1d */чтобы получить список имен каталогов.

решение1

Отредактировано так, чтобы не использовать ls там, где можно было бы использовать glob, как предлагали @shawn-j-goff и другие.

Просто используйте for..do..doneцикл:

for f in *; do
  echo "File -> $f"
done

Вы можете заменить его *на *.txtили любой другой глоб, который возвращает список (файлов, каталогов или чего-либо еще), на команду, которая генерирует список, например, $(cat filelist.txt), или фактически заменить его списком.

Внутри doцикла вы просто ссылаетесь на переменную цикла с префиксом в виде знака доллара (как $fв примере выше). Вы можете echoсделать с ней что угодно или что угодно.

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

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

Или, что еще лучше, если вы используете Bash, вы можете использовать расширения параметров Bash вместо создания подоболочки:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done

решение2

Использовать вывод lsдля получения имен файлов — плохая идея.. Это может привести к неисправным и даже опасным скриптам. Это происходит потому, что имя файла может содержать любой символ, кроме /и nullсимвола, и lsне использует ни один из этих символов в качестве разделителей, поэтому если имя файла содержит пробел или новую строку, выволяполучить неожиданные результаты.

Есть два очень хороших способа итерации по файлам. Здесь я использовал просто echoдля демонстрации того, как что-то делать с именем файла; вы можете использовать что угодно.

Первый способ — использовать собственные функции подстановки оболочки.

for dir in */; do
  echo "$dir"
done

Оболочка разворачивается */в отдельные аргументы, которые forсчитывает цикл; даже если в имени файла есть пробел, новая строка или любой другой странный символ, forкаждое полное имя будет рассматриваться как атомарная единица; она никак не анализирует список.

Если вы хотите рекурсивно перейти в подкаталоги, то это не сработает, если только ваша оболочка не имеет некоторых расширенных функций подстановки (например, bash's ) globstar. Если ваша оболочка не имеет этих функций или если вы хотите быть уверены, что ваш скрипт будет работать на различных системах, то следующим вариантом будет использование find.

find . -type d -exec echo '{}' \;

Здесь findкоманда вызовет echoи передаст ему аргумент имени файла. Это делается один раз для каждого найденного файла. Как и в предыдущем примере, разбор списка имен файлов не производится; вместо этого имя файла полностью отправляется как аргумент.

Синтаксис аргумента -execвыглядит немного забавно. findберет первый аргумент после -execи обрабатывает его как программу для запуска, а каждый последующий аргумент он обрабатывает как аргумент для передачи этой программе. Есть два специальных аргумента, которые -execнужно увидеть. Первый — {}; этот аргумент заменяется именем файла, которое findгенерируют предыдущие части. Второй — ;, который дает findзнать, что это конец списка аргументов для передачи программе; findэто нужно, потому что вы можете продолжить с другими аргументами, которые предназначены findи не предназначены для исполняемой программы. Причина в том, \что оболочка также обрабатывает его ;особым образом — он представляет собой конец команды, поэтому нам нужно экранировать его, чтобы оболочка передала его как аргумент, а findне использовала его для себя; другой способ заставить оболочку не обрабатывать его особым образом — заключить его в кавычки: ';'работает так же хорошо, как и \;для этой цели.

решение3

Для файлов с пробелами вам придется обязательно заключить переменную в кавычки, например:

 for i in $(ls); do echo "$i"; done; 

или вы можете изменить переменную среды разделителя полей ввода (IFS):

 (IFS=$'\n';for i in $(ls); do echo $i; done) # subshell to restore IFS

Наконец, в зависимости от того, что вы делаете, вам может даже не понадобиться ls:

 for i in *; do echo "$i"; done;

решение4

Чтобы дополнить ответ CoverosGene, вот способ вывести список только имен каталогов:

for f in */; do
  echo "Directory -> $f"
done

Связанный контент