【问题标题】:Reading filenames into an array将文件名读入数组
【发布时间】:2012-06-14 10:29:07
【问题描述】:

我想获取一个文件列表,然后将结果读入一个数组,其中每个数组元素对应一个文件名。这可能吗?

【问题讨论】:

  • 是的,有可能。如果名称可能包含任意字符(名称中的空格和换行符会导致悲伤),则可能不建议这样做,但这是可行的。您对手册的哪一部分有理解困难?
  • list 是如何定义的? bash 有数组,但是根据列表的生成方式,不同的技术比其他的要好。无论如何,也请发布您自己解决问题的尝试。

标签: bash unix cygwin


【解决方案1】:

以下将在当前目录中创建一个带有 ls 输出的数组 arr:

arr=( $(ls) )

虽然使用ls 的输出根本不安全。

ls 更好更安全,你可以使用echo *

arr=( * )

echo ${#arr[@]} # will echo number of elements in array

echo "${arr[@]}" # will dump all elements of the array

【讨论】:

  • ls 不是必需的,不应用于此目的。
  • 同意,我只是想告诉你如何从一些命令输出创建一个数组。但是我编辑了我的答案,强调应该避免ls 输出。
  • 当数组扩展没有被引用时,数组的所有元素都表示为一个字符串,而不是数组的单个元素。未引用的 ${arr[*]}${arr[@]} 是相同的。
  • 有谁知道数组可以容纳的文件名/元素的最大数量是多少?
  • anubhava,请删除您的 arr=( $(ls) ) 行。或者,如果您想保留它,请明确添加诸如 不要这样做,它已损坏。 之类的提及
【解决方案2】:

不要使用ls,它是not intended 用于此目的。使用通配符。

shopt -s nullglob
array=(*)
array2=(file*)
array3=(dir/*)

如果没有匹配项,nullglob 选项会导致数组为空。

【讨论】:

  • 谢谢,无论如何我可以通过管道传输这些结果吗?我尝试了类似 arr(* | grep ".txt") 但它不喜欢它。
  • @dublintech:你不需要grep,只需在你的glob中包含字符串:array=(*.txt)array=(*foo*)
  • 你也可以append filenames到一个数组,output filenamesloop through他们。
【解决方案3】:

实际上,ls 不是要走的路。试试这个:

declare -a FILELIST
for f in *; do 
    #FILELIST[length_of_FILELIST + 1]=filename
    FILELIST[${#FILELIST[@]}+1]=$(echo "$f");
done

要从数组中获取文件名,请使用:

echo ${FILELIST[x]}

要从从 x 开始的数组中获取 n 个文件名,请使用:

echo ${FILELIST[@]:x:n}

有关 bash 数组的精彩教程,请参阅: http://www.thegeekstuff.com/2010/06/bash-array-tutorial/

【讨论】:

  • 您应该遍历文件 glob OR 使用文件 glob 创建一个数组,如我的回答 - 而不是 both!。向数组添加元素时,没有理由在复杂的索引表达式中使用数组的长度(一方面,如果数组稀疏,它将无法按预期工作)。 array+=(element)。没有理由使用$(echo "$f") 直接做分配。您的 echo 语句之一缺少右大括号。
  • 您是否知道您正在做的只是做FILELIST = ( * ) 的错误方式? (或者,更确切地说是FILELIST += ( * ))。为什么你使用$(echo "$f") 而不仅仅是"$f"
【解决方案4】:

试试这个,

path="" # could set to any absolute path
declare -a array=( "${path}"/* )

我假设你稍后会从列表中取出不需要的东西。

【讨论】:

    【解决方案5】:

    在 bash 中,您可以使用 路径名扩展(通配符) 创建一个文件名数组,如下所示:

    #!/bin/bash
    SOURCE_DIR=path/to/source
    files=(
       "$SOURCE_DIR"/*.tar.gz
       "$SOURCE_DIR"/*.tgz
       "$SOURCE_DIR"/**/*
    )
    

    上面将创建一个名为files 的数组并向其添加N 个数组元素,其中数组中的每个元素对应于SOURCE_DIR 中以.tar.gz.tgz 结尾的项,或a 中的任何项其子目录,子目录递归可能为 cmets 中的 Dennis points out

    然后您可以使用printf 查看数组的内容,包括路径:

    printf '%s\n' "${files[@]}" # i.e. path/to/source/filename.tar.gz
    

    或者使用参数替换来排除路径名:

    printf '%s\n' "${files[@]##*/}" # i.e. filename.tgz
    

    【讨论】:

    • 您需要拥有shopt -s globstar 才能使用递归通配符 (**)。
    • 不适合我。 MacOS 上的默认 Bash 是 3.2,它没有 globstar。为您计算带有echo /usr/**/ | wc -c 输出的某些目录名称中的字符是什么?在我的 Mac 上,在带有 Bash 3.2 的 Sierra 或在关闭 globstar 的 Bash 4.4 中,它输出 122。如果我在打开 globstar 的情况下运行 Bash 4.4,我得到 277904。后者显然是递归的,而前者不是。顺便说一句,shopt 已定义,但我认为您的意思是 globstar(在 Bash 3.2 中,shopt -p globstar 给出错误,在 Bash 4.4 中显示它是设置还是取消设置)。
    • hash 仅适用于外部可执行文件(尝试hash -t lshelp hash)并且不会为shopt 显示任何内容,因为它是内置的或globstar 因为它是选项而不是可执行文件。尝试type -a shopt 显示shopt 的来源,并尝试shopt 本身显示所有选项的设置。使用echo "$BASH_VERSION" 显示您当前运行的shell 的版本(如果它是Bash)并查看ps -o tty,command 的输出以查看它是否真的是/bin/bash。比较find /usr -type d | wc -lecho /usr/**/ | tr -cd " " | wc -c ...
    • ... 如果递归 globbing 工作,计数应该相当接近。我的计数在 3800 左右。
    • 我可以把它留在这里。它不是特定于 OP 的问题,但在输出文件名/路径时它是一种有用的技术。
    【解决方案6】:

    如果您需要无法通过通配符返回的更具体的文件列表,则可以使用进程替换来替换 find 命令,并使用以空字符分隔的 while 循环。

    例子:

    files=()
    while IFS= read -r -d $'\0' f; do
        files+=("$f")
    done < <(find . -type f -name '*.dat' -size +1G -print0)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-16
      • 2021-01-03
      • 2010-09-22
      相关资源
      最近更新 更多