【问题标题】:How do you store a list of directories into an array in Bash (and then print them out)?如何将目录列表存储到 Bash 中的数组中(然后将它们打印出来)?
【发布时间】:2011-05-28 12:25:21
【问题描述】:

我想编写一个 shell 脚本来显示用户输入的目录列表,然后让用户根据有多少目录选择具有索引号的目录之一

我认为这是某种数组操作,但我不确定如何在 shell 脚本中执行此操作

示例:

> whichdir
There are 3 dirs in the current path
1 dir1
2 dir2
3 dir3
which dir do you want? 
> 3
you selected dir3!

【问题讨论】:

  • 你真的想要 select 内置命令,这就是它的目的。丹尼斯就是一个很好的例子。

标签: arrays bash directory


【解决方案1】:
$ ls -a
./ ../ .foo/ bar/ baz qux*
$ shopt -s dotglob
$ shopt -s nullglob
$ array=(*/)
$ for dir in "${array[@]}"; do echo "$dir"; done
.foo/
bar/
$ for dir in */; do echo "$dir"; done
.foo/
bar/
$ PS3="which dir do you want? "
$ echo "There are ${#array[@]} dirs in the current path"; \
select dir in "${array[@]}"; do echo "you selected ${dir}"'!'; break; done
There are 2 dirs in the current path
1) .foo/
2) bar/
which dir do you want? 2
you selected bar/!

【讨论】:

  • +1 表示select。这是人们经常尝试重新发明的隐藏的 bash 宝藏之一,因为他们不知道它的存在
  • @KhurshidAlam:是的
  • in */; 是什么意思?
  • Here 是解释的 shell 内置函数,nullglob 应该排除文件。它可以工作。
【解决方案2】:

数组语法

假设您将目录存储在数组中:

dirs=(dir1 dir2 dir3)

这样就可以得到数组的长度了:

echo "There are ${#dirs[@]} dirs in the current path"

你可以像这样循环遍历它:

let i=1

for dir in "${dirs[@]}"; do
    echo "$((i++)) $dir"
done

假设您已经得到用户的答案,您可以按如下方式对其进行索引。请记住,数组是从 0 开始的,因此第 3 个条目是索引 2。

answer=2

echo "you selected ${dirs[$answer]}!"

查找

无论如何,如何将文件名放入数组中?这有点棘手。如果您有find,那可能是最好的方法:

readarray -t dirs < <(find . -maxdepth 1 -type d -printf '%P\n')

-maxdepth 1 停止查找子目录,-type d 告诉它查找目录并跳过文件,-printf '%P\n' 告诉它打印不带前导 ./ 的目录名称,它通常喜欢打印。

【讨论】:

  • @johnstamos,空格不是唯一的问题——如果您有一个使用mkdir '*' 创建的目录,您还需要担心它会被扩展,并且每个其他 该位置的目录被列出两次,除非一个已运行 set -f 以禁用通配符。
  • 使用-mindepth 1 -maxdepth 1打印一个空行(即.目录)
【解决方案3】:
#! /bin/bash

declare -a dirs
i=1
for d in */
do
    dirs[i++]="${d%/}"
done
echo "There are ${#dirs[@]} dirs in the current path"
for((i=1;i<=${#dirs[@]};i++))
do
    echo $i "${dirs[i]}"
done
echo "which dir do you want?"
echo -n "> "
read i
echo "you selected ${dirs[$i]}"

【讨论】:

  • 哦,哇,这很有效,除了你如何让它不包含 .和 .. 目录?
  • 我测试了这个,它不应该包括它们。
  • 看看 select 内置函数,可以说你是在重新发明轮子
【解决方案4】:

更新:我的回答是错误的

把它留在这里是为了解决一个常见的误解,行以下是错误的。


要将目录放入数组中,您可以这样做...

array=( $( ls -1p | grep / | sed 's/^\(.*\)/"\1"/') )

这将捕获目录名称,包括带有空格的名称。


从 cmets 中提取:

文字引号对字符串拆分没有任何影响,因此 array=( echo '"hello world" "goodbye world"' ) 是一个包含四个元素而不是两个元素的数组

@Charles Duffy

Charles 还提供了以下链接 Bash FAQ #50,这是对此问题的扩展讨论。

我还应该提请注意@Dennis Williamson - why I shouldn't have used ls 发布的链接

【讨论】:

  • 如果任何目录的名称中有空格,这将失败。
  • 不,这不是问题所在。这是因为您使用的是ls。见some reasons not to
  • 虽然更新有效,但for d in */ do 更好。链接文章指出,我从来没有遇到过ls 乱码名称的问题,但值得知道的是,错误的文件名将被处理得不好,谢谢。
  • @ocodo,不,更新不起作用;文字引号对字符串拆分没有任何影响,因此array=( echo '"hello world" "goodbye world"' ) 是一个包含四个元素而不是两个元素的数组——第一个元素是"hello,第二个元素是world"。请参阅BashFAQ #50 讨论由相同误解引起的其他错误。
  • @CharlesDuffy 感谢您的链接和澄清。我将把这个答案留在这里,但将其注释为错误,以供任何未来的读者阅读。
猜你喜欢
  • 2023-03-14
  • 2015-03-26
  • 1970-01-01
  • 1970-01-01
  • 2014-09-23
  • 2019-04-09
  • 2020-11-20
  • 2011-09-21
  • 2015-09-01
相关资源
最近更新 更多