如何仅循环浏览bash中的目录?


249

我有一个文件夹,其中包含一些目录和一些文件(有些是隐藏的,以点开头)。

for d in *; do
 echo $d
done

将遍历所有文件,但我只想遍历目录。我怎么做?

Answers:


331

您可以在末尾指定一个斜杠以仅匹配目录:

for d in */ ; do
    echo "$d"
done

7
请注意,它还包括指向目录的符号链接。
斯特凡Chazelas

1
那么您如何排除符号链接?
rubo77

3
@ rubo77:您可以测试[[ -L $d ]]$ d是否为符号链接。
choroba

1
@AsymLabs这是错误的,set -P仅影响更改目录的命令。sprunge.us/TNac
克里斯·

4
@choroba:[[ -L "$f" ]]在这种情况下,不排除与的符号链接*/,您必须删除斜杠[[ -L "${f%/}" ]](请参见测试斜杠链接
rubo77


30

请注意,如果当前目录中没有目录可用,那么choroba的解决方案虽然很优雅,但会引起意外的行为。在这种状态下,forbash不会跳过循环,而是只要d在等于的位置运行一次循环*/

#!/usr/bin/env bash

for d in */; do
    # Will print */ if no directories are available
    echo $d
done

我建议使用以下方法来防止这种情况:

#!/usr/bin/env bash

for f in *; do
    if [ -d ${f} ]; then
        # Will not run if no directories are available
        echo $f
    fi
done

此代码将循环遍历当前目录中的所有文件,检查是否f为目录,然后f在条件返回true时回显。如果f等于*/echo $f则不会执行。


3
容易得多shopt -s nullglob
choroba

21

如果您需要选择的特定文件比仅目录要多find,请将其传递给while read

shopt -s dotglob
find * -prune -type d | while IFS= read -r d; do 
    echo "$d"
done

使用shopt -u dotglob排除隐藏的目录(或setopt dotglob/ unsetopt dotglob中的zsh)。

IFS=为了避免分割包含其中一个的文件名$IFS,例如:'a b'

有关更多选项,请参见下面的AsymLabs答案find


编辑:
如果需要从while循环中创建退出值,则可以通过以下技巧来规避多余的子shell:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -prune -type d)

2
我在以下位置找到了此解决方案:stackoverflow.com/a/8489394/1069083
rubo77

11

您可以为此使用纯bash,但最好使用find:

find . -maxdepth 1 -type d -exec echo {} \;

(另外会找到隐藏目录)


8
请注意,它不包含指向目录的符号链接。您可以shopt -s dotglob用于bash包含隐藏目录。您的还将包括.。另请注意,这-maxdepth不是标准选项(-pruneis)。
斯特凡Chazelas

2
dotglob选项很有趣,但dotglob仅适用于*find .将始终包含隐藏目录(以及当前目录)
rubo77

6

这样做是为了在当前工作目录中查找可见目录和隐藏目录,但不包括根目录:

只是遍历目录:

 find -path './*' -prune -type d

在结果中包含符号链接:

find -L -path './*' -prune -type d

对每个目录执行某些操作(符号链接除外):

find -path './*' -prune -type d -print0 | xargs -0 <cmds>

排除隐藏目录:

find -path './[^.]*' -prune -type d

在返回的值上执行多个命令(一个非常人为的示例):

find -path './[^.]*' -prune -type d -print0 | xargs -0 -I '{}' sh -c \
"printf 'first: %-40s' '{}'; printf 'second: %s\n' '{}'"

代替'sh -c'也可以使用'bash -c'等。


如果回显* /中的for d中的回显'* /'包含60,000个目录,会发生什么情况?
AsymLabs

您将收到“ Too many files”错误,但可以解决:如何在debian中规避“ Too many open files”
rubo77 2013年

1
xargs示例仅适用于目录名称9e.g的子集。那些带有空格或换行符的行将无效)。... -print0 | xargs -0 ...如果您不知道确切的名称是更好的用法。
Anthon

1
@ rubo77添加了排除隐藏文件/目录和执行多个命令的方法-当然,这也可以通过脚本来完成。
AsymLabs

1
我在底部的答案中添加了一个示例,该示例如何使用具有以下功能的函数对每个目录进行操作find * | while read file; do ...
rubo77

3

您可以使用以下命令循环浏览所有目录,包括一行隐藏目录(以点开头)和多个命令:

for file in */ .*/ ; do echo "$file is a directory"; done

如果要排除符号链接:

for file in *; do 
  if [[ -d "$file" && ! -L "$file" ]]; then
    echo "$file is a directory"; 
  fi; 
done

注意:使用列表*/ .*/可以在bash中使用,但也可以显示文件夹...而在zsh中则不会显示这些文件夹,但是如果文件夹中没有隐藏文件,则会抛出错误

一个更干净的版本将包含隐藏目录并排除../,它将带有dotglob选项:

shopt -s dotglob
for file in */ ; do echo "$file is a directory"; done

(或setopt dotglob在zsh中)

您可以使用取消设置dotglob

shopt -u dotglob

2

这将包括列表中每个目录中的完整路径:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done

1
使用带空格的文件夹时会中断
Alejandro Sazo

0

使用findwith -exec遍历目录并在exec选项中调用一个函数

dosomething () {
  echo "doing something with $1"
}
export -f dosomething
find -path './*' -prune -type d -exec bash -c 'dosomething "$0"' {} \;

使用shopt -s dotglobshopt -u dotglob包含/排除隐藏目录


0

这列出了所有目录以及给定路径中的子目录数:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done

-1
ls -d */ | while read d
do
        echo $d
done

This is one directory with spaces in the name-但解析为多个。
Piskvor

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.