如何在Linux Bash中将ls指令赋值给数组?

116
array=${ls -d */}
echo ${array[@]}  

我有三个目录:wweeqq。我想把它们放到一个数组中,然后打印出这个数组。

1
这个回答解决了你的问题吗?将文件名读入数组 - akostadinov
4个回答

157

就是这样

array=($(ls -d */))

编辑:请查看 Gordon Davisson 的解决方案,该解决方案更通用(即适用于文件名包含特殊字符的情况)。本答案仅纠正语法。


21
如果目录名包含空格,这将无法正常工作。 - chepner
1
为了解决空格或特殊字符的问题,首先在没有任何值的情况下声明数组,然后使用内置的ARR+=('$(ls -d */)')编辑数组中的元素,同时使用单引号来处理特殊字符,以添加或删除元素到该数组中。单引号保留字面字符并消除了在目录名称中处理它们所需的转义需要。 - Yokai
3
我的错误。应该使用双引号,这样命令替换才能被展开。ARR+=("$(ls -d */)") 应该可以工作。如果不行,请告诉我。 - Yokai
3
@Yokai 只添加了一个元素(其中包含一堆换行符),而不是每个目录都添加一个元素。 - Charles Duffy
2
根据Shellcheck的建议,以下将是更好的选项: mapfile -t contents < <(ls -d -- *) 原始解决方案存在两个问题:(i)不支持路径中的空格,(ii)以破折号开头的文件名被解释为ls的参数而不是文件名。 - Christoph Thiede
显示剩余4条评论

103
尽可能避免解析ls的输出(请参阅Greg关于此主题的维基百科)。基本上,如果文件名中有奇怪的字符,则ls的输出将是模糊的。这通常也是浪费时间的。在这种情况下,当您执行ls -d */时,发生的情况是shell将*/扩展为子目录列表(这已经是您想要的),将该列表作为参数传递给ls -d,后者查看每个子目录,说“没错,那确实是一个目录”,并打印它(以不一致和有时模糊的格式)。ls命令没有做任何有用的事情!
好吧,它确实做了一件有用的事情:如果没有子目录,则*/将保持不变,ls将搜索名为“*” 的子目录,找不到它,将其打印为不存在的错误消息(到stderr),并且不会将“*/”打印出来(到stdout)。
制作子目录名称数组的更清晰的方法是使用glob(*/)而不是将其传递给ls。但是,为了避免在没有实际子目录的情况下将“*/”放入数组中,您应该首先设置nullglob(再次请参见Greg的维基百科):
shopt -s nullglob
array=(*/)
shopt -u nullglob # Turn off nullglob to make sure it doesn't interfere with anything later
echo "${array[@]}"  # Note double-quotes to avoid extra parsing of funny characters in filenames

如果你想在没有子目录时打印错误消息,最好自己处理:
if (( ${#array[@]} == 0 )); then
    echo "No subdirectories found" >&2
fi

3
感谢您的评论,虽然它没有被标记为答案,但我觉得它很有启发性,非常感激。 - Jake H
如果您不想在目录名称中包含“/”怎么办? - shinzou
Greg的维基中概述的问题并不是仅凭条件结构就无法轻松处理的事情。ls是一个简单的命令,它的输出是字符串。Bash在解析字符串方面非常出色。正确的正则表达式就足以安全地解析ls的输出。 - Yokai
1
@Yokai 不同版本的 ls 在文件名中使用有趣/非打印字符时会执行不同的操作,因此没有一种一致的方法可以将其输出解析为文件名列表。正如我所说,它实际上并没有做任何有用的事情:如果您正确解析 ls -d */ 的输出,您最终得到的东西与直接从 */ 得到的完全相同(除非没有匹配项,这很容易检查)。 - Gordon Davisson
在我的 Facebook Bash 组讨论了这个话题后,我们发现 shopt 在 Debian 或其衍生发行版中默认情况下不包含,因此可能不是一个足够便携的选项。不过我确实喜欢 glob 的想法。 - Yokai
显示剩余7条评论

16

这将逐行打印这些目录中的文件。

array=(ww/* ee/* qq/*)
printf "%s\n" "${array[@]}"

好吧,这并不是“ls”,但对于OP的目的,“ls”在这里似乎并不必要。而且它可以处理带有空格的情况。漂亮而简单! - spawn

0
试试这个...我只是用它来重命名文件夹中的所有MP4文件,将空格替换为下划线。
filesArray=(*.mp4)
filesCount=${#filesArray[@]}
for (( i=0;i<$filesCount;i++)); do
    singleFile="${filesArray[$i]}"
    #here I replace ALL the spaces in the file's name, by underscores.
    trimmed=${singleFile// /\_}
    mv "${singleFile}" "${trimmed}"
    echo "renamed ${singleFile} as ${trimmed} !"
done

请帮我改进我的回答,如果有任何错误,请指出!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接