Bash, переключение по каталогам
У меня есть фундаментальный вопрос о том, как работает bash, и связанный с ним практический вопрос.
Фундаментальный вопрос: предположим, что я в директории с тремя подкаталогами: a, b и c.
с кодом
for dir in $(ls)
do
echo $dir
done
выплевывает:
a b c
a b c
a b c
i.e, dir
всегда хранит список всех файлов/каталогов в моем cwd
. Мой вопрос: почему в мире это было бы удобно? На мой взгляд, гораздо более полезно и интуитивно иметь dir
хранить каждый элемент за раз, т.е. Я хотел бы иметь вывод
a
b
c
Кроме того, согласно одному из ответов - неправильно использовать for dir in $(ls)
, но когда я использую for dir in $(ls -l)
, я получаю еще больше копий a b c
(больше, чем есть каталоги/файлы в cwd). Почему это?
Мой второй вопрос практичен: как мне перебирать все каталоги (а не файлы!) в моем cwd
, которые начинаются с капитала W? Я начал с
for dir in `ls -l W*`
но это не удается, потому что: a) причина в вопросе 1 и b), поскольку она не исключает файлы. Предложения оценены.
Ответы
Ответ 1
Никогда не анализируйте вывод ls
следующим образом (Почему вы не должны разбирать вывод ls (1)).
Кроме того, ваш синтаксис неверен. Вы не имеете в виду ()
, вы имеете в виду $()
.
Чтобы сказать, что для перебора каталогов, начинающихся с W, вы будете делать (или вместо этого используйте команду find
, в зависимости от вашего сценария):
for path in /my/path/W*; do
[ -d "${path}" ] || continue # if not a directory, skip
dirname="$(basename "${path}")"
do_stuff
done
Что касается вывода, который вы получаете от злого ls-цикла, он не должен выглядеть так. Это ожидаемый результат и демонстрирует, почему вы не хотите использовать ls в первую очередь:
$ find
.
./c
./a
./foo bar
./b
$ type ls
ls is hashed (/bin/ls)
$ for x in $(ls); do echo "${x}"; done
a
b
c
foo
bar
Ответ 2
Это должно работать:
shopt -s nullglob # empty directory will return empty list
for dir in ./*/;do
echo "$dir" # dir is directory only because of the / after *
done
Чтобы быть рекурсивным в подкаталогах, используйте globstar
:
shopt -s globstar nullglob
for dir in ./**/;do
echo "$dir" # dir is directory only because of the / after **
done
Вы можете сделать метод @Adrian Frühwirths рекурсивным для подкаталогов с помощью globstar
тоже:
shopt -s globstar
for dir in ./**;do
[[ ! -d $dir ]] && continue # if not directory then skip
echo "$dir"
done
Из Bash Вручную:
globstar
Если установлено, шаблон '**, используемый в контексте расширения имени файла, будет сопоставлять все файлы и ноль или более каталогов и подкаталогов. Если за шаблоном следуют "/, только каталоги и подкаталоги матч.
nullglob
Если установлено, Bash разрешает шаблоны имен файлов, которые не соответствуют файлам для расширения к пустой строке, а не к себе.
Ответ 3
Хорошо, вы знаете, что вы видите, это не то, что вы ожидаете. Результат, который вы видите, не из команды echo, а из команды dir.
Попробуйте следующее:
ls -1 | while read line; do
if [-d "$line" ] ; then
echo $line
fi
done
for files in $(ls) ; do
if [-d "$files" ] ; then
echo $files
fi
done