2013-04-06 2 views
10

У меня есть фундаментальный вопрос о том, как работает bash, и связанный с ним практический вопрос.Bash, переплетение каталогов

Фундаментальный вопрос: предположим, что я в каталоге, который имеет три подкаталога: a, b и c.

курица код

for dir in $(ls) 
do 
    echo $dir 
done 

выплевывает:

a b c 
a b c 
a b c 

т.е., 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), поскольку она не исключает файлы. Предложения оценены.

+0

Ваш фрагмент кода не дает результата, который вы говорите. Прочтите более подробный код. –

+0

Вы правы. Когда я впервые пытался использовать этот код, я использовал '$ (ls)', но когда я написал вопрос, я изменил '$ (ls)' на '$ (ls -l)', потому что это похоже на то, как это делается везде Я посмотрел. Я предположил, что выход будет таким же, и я удивлен, что это не так. – alexvas

+0

@alexvas Почему вы удивлены тем, что вывод 'ls -l' отличается от результата' ls'? Параметр изменяет свое поведение (подробнее см. «Man ls'):' -l' дает вам длинный вывод, включая разрешения, право собственности, дату и т. Д. Это еще больший no-no для синтаксического анализа, потому что его результат может сильно отличаться о реализации и вашей «локали». 'ls -l' - это то, что вы использовали бы в командной строке, но никогда в сценарии, подобном этому. Если вы видели код, который анализирует свой результат в вашей среде, вы должны связаться с автором и попросить его исправить. –

ответ

32

Никогда не разбирайте выходные данные ls следующим образом (Why you shouldn't parse the output of 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 
+0

Спасибо. Однако я хочу знать, где мое понимание 'for dir in $ (ls)' неверно. Я предположил, что 'ls' возвращает массив, хранящий каталоги и файлы в моем cwd, а затем' for' будет перебирать их. Почему этого не происходит? – alexvas

+0

Он не возвращает массив, он выводит строку на stdout, которую вы захватываете с помощью синтаксиса '$(). Помимо этого я не могу ответить на ваш вопрос, потому что цикл for не должен печатать такую ​​матрицу, как вы опубликовали, в зависимости от сценария, который вы упомянули. –

+0

Обновлен мой ответ, чтобы показать, что вывод должен выглядеть так, как вы ожидали в первую очередь, возможно, вы захотите попробовать это снова. –

0

Ну, вы знаете, что вы видите, это не то, что вы ожидаете. Результат, который вы видите, не из команды 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 
7

Это должно работать:

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 

From Bash Manual:

globstar

Если установлено, шаблон «**» используется в контексте расширительный файла будет матч все файлы и ноль или несколько каталогов и подкаталогов. Если , за шаблоном следует '/', только каталоги и подкаталоги .

nullglob

Если установлено, Bash позволяет имя файла модели, которые не соответствуют ни одного файла для расширения в пустую строку, а не сами по себе.

Смежные вопросы