Есть ли у кого-нибудь сценарий оболочки шаблона для того, чтобы сделать что-то с ls
для списка имен каталогов и пройтись по каждому из них и что-то сделать?
Я планирую сделать ls -1d */
чтобы получить список имен каталогов.
Есть ли у кого-нибудь сценарий оболочки шаблона для того, чтобы сделать что-то с ls
для списка имен каталогов и пройтись по каждому из них и что-то сделать?
Я планирую сделать ls -1d */
чтобы получить список имен каталогов.
Отредактировано, чтобы не использовать ls там, где будет делать глоб, как предложили @ 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
Использование вывода ls
для получения имен файлов - плохая идея. Это может привести к неправильной работе и даже опасным сценариям. Это происходит потому , что имя файла может содержать любые символы , кроме /
и null
символ, и ls
не использует любой из этих символов в качестве разделителей, так что если имя файла содержит пробел или символ новой строки, вы получите неожиданные результаты.
Есть два очень хороших способа перебора файлов. Здесь я использовал просто echo
чтобы продемонстрировать что-то с именем файла; Вы можете использовать все, что угодно.
Во-первых, использовать встроенные функции оболочки.
for dir in */; do
echo "$dir"
done
Оболочка раскрывает */
в отдельные аргументы, которые читает цикл for
; даже если в имени файла есть пробел, символ новой строки или любой другой странный символ, for
каждое полное имя будет рассматриваться как атомарная единица; это не разбирает список в любом случае.
Если вы хотите идти рекурсивно в подкаталогах, то это не будет делать , если ваша оболочка не имеет некоторые расширенные подстановка функции (например, bash
globstar
«ы. Если ваша оболочка не имеет этих функций, или если вы хотите убедиться, что ваш скрипт будет работать на различных системах, то следующий вариант - использовать find
.
find . -type d -exec echo '{}' \;
Здесь команда find
вызовет echo
и передаст ей аргумент имени файла. Он делает это один раз для каждого найденного файла. Как и в предыдущем примере, нет разбора списка имен файлов; вместо этого имя файла отправляется полностью в качестве аргумента.
Синтаксис аргумента -exec
выглядит немного забавно. find
принимает первый аргумент после -exec
и обрабатывает его как программу для запуска, а каждый последующий аргумент - как аргумент для передачи этой программе. Есть два специальных аргумента, которые нужно увидеть -exec
. Первый - {}
; этот аргумент заменяется именем файла, которое генерирует предыдущие части find
. Второй - это ;
, что позволяет find
что это конец списка аргументов для передачи в программу; find
нуждается в этом, потому что вы можете продолжить с большим количеством аргументов, предназначенных для find
а не предназначенных для программы exec'd. Причиной \
является то, что оболочка также лечит ;
специально - она представляет конец команды, поэтому нам нужно избегать ее, чтобы оболочка выдавала ее в качестве аргумента для find
а не потребляла ее для себя; Другой способ заставить оболочку не обрабатывать ее специально, заключить в кавычки: ';'
работает так же, как \;
для этого.
Для файлов с пробелами вы должны обязательно указать переменную в кавычках:
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;
Если у вас установлен 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
Просто чтобы добавить ответ CoverosGene, вот способ перечислить только имена каталогов:
for f in */; do
echo "Directory -> $f"
done
Почему бы не установить 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
.
Вот как я это делаю, но, возможно, есть более эффективные способы.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt