在 Bash 中循环目录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15854559/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Looping over directories in Bash
提问by alexvas
I have a fundamental question about how bash works, and a related practical question.
我有一个关于 bash 如何工作的基本问题,以及一个相关的实际问题。
Fundamental question: suppose I am in a directory that has three subdirectories: a, b, and c.
基本问题:假设我在一个包含三个子目录的目录中:a、b 和 c。
hen the code
代码
for dir in $(ls)
do
echo $dir
done
spits out:
吐出:
a b c
a b c
a b c
i.e, dir
always stores a list of all of the files/directories in my cwd
. My question is: why in the world would this be convenient? In my opinion it is far more useful and intuitive to have dir
store each element at a time, i.e I would want to have output
即,dir
始终将所有文件/目录的列表存储在我的cwd
. 我的问题是:为什么这会很方便?在我看来,dir
一次存储每个元素更加有用和直观,即我想要输出
a
b
c
Also, as per one of the answers - it is wrong to use for dir in $(ls)
, but when I use for dir in $(ls -l)
I get even more copies of a b c
(more than there are directories/files in the cwd). Why is that?
另外,根据其中一个答案 - 使用是错误的for dir in $(ls)
,但是当我使用时,for dir in $(ls -l)
我得到了更多的副本a b c
(比 cwd 中的目录/文件还多)。这是为什么?
My second question is practical: how do I loop over all the directories (not files!) in my cwd
that start with capital W? I started with
我的第二个问题是实用的:如何遍历cwd
以大写 W 开头的所有目录(不是文件!)?我开始了
for dir in `ls -l W*`
but this fails because a) the reason in question 1 and b) because it doesn't exclude files. Suggestions appreciated.
但这失败了,因为 a) 问题 1 中的原因和 b) 因为它不排除文件。建议表示赞赏。
回答by Adrian Frühwirth
Never ever parse the output of ls
like this (Why you shouldn't parse the output of ls(1)).
永远不要解析这样的输出ls
(为什么你不应该解析 ls(1) 的输出)。
Also, your syntax is wrong. You don't mean ()
, you mean $()
.
另外,你的语法是错误的。你不是说()
,你是说$()
。
That being said, to loop over directories starting with W you would do (or use the find
command instead, depending on your scenario):
话虽如此,要循环以 W 开头的目录,您会这样做(或使用该find
命令,具体取决于您的情况):
for path in /my/path/W*; do
[ -d "${path}" ] || continue # if not a directory, skip
dirname="$(basename "${path}")"
do_stuff
done
As for the output you get from the evil ls-loop, it should not look like that. This is the expected output and demonstrates why you do not want to use ls in the first place:
至于你从邪恶的 ls-loop 得到的输出,它不应该是那样的。这是预期的输出,并说明了为什么您不想首先使用 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
回答by Jahid
This should work:
这应该有效:
shopt -s nullglob # empty directory will return empty list
for dir in ./*/;do
echo "$dir" # dir is directory only because of the / after *
done
To be recursive in subdirectories too, use globstar
:
要在子目录中也递归,请使用globstar
:
shopt -s globstar nullglob
for dir in ./**/;do
echo "$dir" # dir is directory only because of the / after **
done
You can make @Adrian Frühwirths' method to be recursive to sub-directories by using globstar
too:
您也可以使用 @Adrian Frühwirths 的方法递归到子目录globstar
:
shopt -s globstar
for dir in ./**;do
[[ ! -d $dir ]] && continue # if not directory then skip
echo "$dir"
done
globstar
If set, the pattern ‘**' used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a ‘/', only directories and subdirectories match.
nullglob
If set, Bash allows filename patterns which match no files to expand to a null string, rather than themselves.
环球之星
如果设置,文件名扩展上下文中使用的模式 '**' 将匹配所有文件以及零个或多个目录和子目录。如果模式后跟一个“/”,则只有目录和子目录匹配。
空球
如果设置,Bash 允许不匹配任何文件的文件名模式扩展为空字符串,而不是它们本身。
回答by user1587276
Well, you know what you are seeing is not what you are expecting. The output you are seeing is not from the echo command, but from the dir command.
好吧,您知道您所看到的不是您所期望的。您看到的输出不是来自 echo 命令,而是来自 dir 命令。
Try the following:
请尝试以下操作:
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