Есть ли у кого-нибудь шаблонный скрипт для работы с ls
списком имен каталогов, циклический просмотр каждого из них и выполнение чего-либо?
Я планирую сделать, ls -1d */
чтобы получить список имен каталогов.
Есть ли у кого-нибудь шаблонный скрипт для работы с ls
списком имен каталогов, циклический просмотр каждого из них и выполнение чего-либо?
Я планирую сделать, ls -1d */
чтобы получить список имен каталогов.
Ответы:
Отредактировано, чтобы не использовать ls там, где будет делать глоб, как предложили @ shawn-j-goff и другие.
Просто используйте for..do..done
цикл:
for f in *; do
echo "File -> $f"
done
Вы можете заменить *
с *.txt
или любой другой Glob , который возвращает список (файлов, каталогов или что - нибудь в этом отношении), команда , которая генерирует список, например, $(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
for
циклов и типичную ловушку при попытке анализа выходных данных ls
. @ DanielA.Белый, вы можете подумать о том, чтобы не принять этот ответ, если он не помог (или потенциально вводит в заблуждение), поскольку, как вы сказали, вы действуете по каталогам. Ответ Шона Дж. Гоффа должен обеспечить более надежное и эффективное решение вашей проблемы.
ls
вывод , вы не должны читать вывод в for
цикле , вы должны использовать $()
вместо `` и Use More Quotes ™ . Я уверен, что @CoverosGene имел в виду хорошо, но это просто ужасно.
ls
это ls -1 | while read line; do stuff; done
. По крайней мере, это не сломается для пробелов.
mv -v
тобой не надоecho "moved $x -> $t"
Использование вывода 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
и не предназначены для программы exec'd. Причина в том \
, что оболочка также лечит;
специально - она представляет конец команды, поэтому нам нужно избегать ее, чтобы оболочка выдавала ее в качестве аргумента, find
а не использовала ее для себя; Другой способ заставить оболочку не обрабатывать ее специально - заключить ее в кавычки: ';'
работает так же, как и \;
для этой цели.
find
. Есть волшебство, которое можно сделать, и даже воспринимаемые ограничения -exec
можно обойти. -print0
Вариант также ценен для использования с xargs
.
Для файлов с пробелами вы должны обязательно заключить в кавычки переменную, например:
for i in $(ls); do echo "$i"; done;
или вы можете изменить переменную среды разделителя входных полей (IFS):
IFS=$'\n';for file in $(ls); do echo $i; done
Наконец, в зависимости от того, что вы делаете, вам может даже не понадобиться ls:
for i in *; do echo "$i"; done;
\n
недостаточен. Никогда не рекомендуется рекомендовать анализ выходных данных ls
.
Если у вас установлен GNU Parallel http://www.gnu.org/software/parallel/, вы можете сделать это:
ls | parallel echo {} is in this dir
Чтобы переименовать все .txt в .xml:
ls *.txt | parallel mv {} {.}.xml
Посмотрите вступительное видео для GNU Parallel, чтобы узнать больше: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Почему бы не установить IFS на новую строку, а затем записать вывод ls
в массив? Установка IFS на новую строку должна решить проблемы с забавными символами в именах файлов; Использование ls
может быть хорошим, потому что оно имеет встроенную функцию сортировки.
(При тестировании у меня возникли проблемы с настройкой IFS, \n
но настройка перевода строки на новую строку работает, как это предлагается в другом месте здесь):
Например (при условии, что желаемый ls
шаблон поиска передан $1
):
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
FILES=($(/bin/ls "$1"))
for AFILE in ${FILES[@]}
do
... do something with a file ...
done
IFS=$SAVEIFS
Это особенно удобно в OS X, например, для захвата списка файлов, отсортированных по дате создания (от самого старого до самого нового), ls
команда имеет вид ls -t -U -r
.
\n
недостаточен. Единственные допустимые решения используют цикл for с расширением пути или find
.
Вот как я это делаю, но, возможно, есть более эффективные способы.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done