如何列出目录的子目录?

如何列出目录的子目录?

我需要列出某个路径的子目录。分隔应该用空格而不是换行。

我也不想要子目录的绝对路径,只想要它们的名称。

# correct
dir1 dir2 dir3

# incorrect: separation with new lines
dir1
dir2
dir3

# incorrect: absolute paths
/home/x/y/dir1 /home/x/y/dir2 /home/x/y/dir3

我看过很多其他帖子,例如这个帖子,但他们没有完成我的要求。

我已经尝试过,ls -d ~/y但它列出了绝对路径并用新行分隔。我想我可以使用 sed 删除路径中不相关的部分,然后删除所有新行。但我无法让它工作,似乎应该有更好的解决方案

答案1

假设您正在使用 GNU 工具,您可以使用 GNUbasename来获取特定目录中所有子目录的名称。然后,您可以将paste其格式化为空格分隔的列表。

basename -a /some/path/*/ | paste -d ' ' -s -

上面的命令利用了这样一个事实:GNUbasename有一个-a选项可以返回在其命令行上作为操作数给出的多个路径名的文件名部分。

我们使用以 结尾的文件匹配模式/来生成 GNU 的路径名basename。只有目录可以匹配这样的模式。

最后,paste从 GNU 生成的换行符分隔列表中创建空格分隔列表basename

请注意,如果任何原始目录名称包含空格字符,则很难解析生成的文件名列表。

请注意,如果目录包含符号链接,此方法将尝试跟踪这些符号链接。


为了限制我们使用任何外部工具,我们可以在 shell 中使用数组bash来存储和操作目录路径。

shopt -s nullglob

topdir=/some/path

dirpaths=( "$topdir"/*/ )
dirpaths=( "${dirpaths[@]#$topdir/}" )
dirpaths=( "${dirpaths[@]%/}" )

printf '%s\n' "${dirpaths[*]}"

上面的 shell 代码扩展了与我们在本答案的第一部分中使用的相同的通配模式,但将结果目录路径存储在数组中dirpaths。然后,它$topdir/从数组的每个元素和尾随中删除已知前缀,/然后将数组打印为空格分隔名称的单个字符串。最后一行的名称之间使用的分隔符将是 的第一个字符$IFS,默认情况下是空格。


使用find,您可以在您感兴趣的特定顶级目录中查找子目录,同时确保不返回顶级目录本身。您还将停止find进入子目录。

topdir=/some/path
find "${topdir%/}/." ! -name . -prune -type d -exec basename {} \; | paste -d ' ' -s -

上面的命令使用否定测试来避免搜索起点-name,并且它会修剪搜索树,-prune以便find不会递归到任何子目录。我们调用basename每个找到的目录,将目录的文件名输出到单独的行上。作为最后一步,我们find通过管道传输结果,将paste输出格式化为单行上的空格分隔列表。

使用 GNU find,您可以将其写为

find /some/path -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | paste -d ' ' -s -

像这样使用find将列出具有隐藏名称的目录,并且您将看不到任何符号链接的目录。


zshshell 中,您将能够使用更高级的 shell 通配模式来仅挑选目录的文件名并一次性打印它们。

print -r -- /some/path/*(/:t)

此命令使用 glob 限定符 ,/:t由两部分组成,影响前面的 globbing 模式/some/path/*。使/模式仅匹配目录(不是符号链接的目录;用于该用途-/),同时:t提取每个生成的路径名的“尾部”,即文件名部分。

print -r命令使用空格作为分隔符来打印其参数,同时避免扩展数据中的转义序列(如\n或 ) 。\t使用--分隔选项中的操作数(也适用于shell-中的操作ksh)可确保 glob 扩展产生的目录名称不会被视为选项,即使它们以-.

您可以在bashshell 中使用它来生成列表。

zsh -c 'print -r -- /some/path/*(/:t)'

答案2

由于您已经知道要从中列出子目录的目录,因此可以使用以下...

find /home/x/y -maxdepth 1 -type d -printf '%P '

答案3

这是我使用for循环的建议:

$ echo "$(for node in $(ls ~/); do if test -d "${node}"; then printf "${node} "; fi; done)"
Desktop Documents Downloads Music Pictures Public Templates Videos

替代使用find

$ echo "$(for node in $(find ~/ -mindepth 1 -maxdepth 1 -type d); do printf "$(basename ${node}) "; done)"
.mozilla Videos Pictures Music Documents Public Templates Downloads .gnupg .config .local .cache Desktop

编辑:关于他们的评论,您需要更改IFS带有空格的目录的变量:

$ IFS_orig=${IFS}
$ IFS=$'\n'
$ echo "$(for node in $(ls ~/); do if test -d "${node}"; then printf "'${node}' "; fi; done)"
'Desktop' 'Documents' 'Downloads' 'Music' 'My Test' 'Pictures' 'Public' 'Templates' 'Videos'

$ IFS=${IFS_orig}

编辑:最简单的选择似乎是使用ls -d *

$ ls -d *
 Desktop   Documents   Downloads   Music  'My Test'   Pictures   Public   Templates   Videos

另外,忘记在for循环中应该test使用绝对路径或相对路径:

$ echo "$(for node in $(ls "${HOME}"); do if test -d "${HOME}/${node}"; then printf "'${node}' "; fi; done)"
'Desktop' 'Documents' 'Downloads' 'Music' 'My Test' 'Pictures' 'Public' 'Templates' 'Videos'

答案4

我得到的最后命令是:ls -d ~/y/*/ | awk -F/ '{print$5}' | sed 's/ /\\ /' | tr '\n' ' '

起源来自一个现在已删除帖子的用户,但他们没有考虑到不在当前目录上使用 ls,所以我对此进行了调整。我还添加了 sed 管道,以便它为目录名称中包含空格的目录名称添加反斜杠。

它是相当硬编码的,print$5所以它只适用于~目录中的目录。

相关内容