【问题标题】:How to get the number of files in a folder as a variable?如何获取文件夹中的文件数作为变量?
【发布时间】:2012-06-23 06:23:30
【问题描述】:

使用 bash,如何在没有解释器抱怨的情况下获取文件夹中的文件数量,不包括 shell 脚本中的目录?

在朋友的帮助下,我试过了

$files=$(find ../ -maxdepth 1 -type f | sort -n)
$num=$("ls -l" | "grep ^-" | "wc -l")

从命令行返回:

../1-prefix_blended_fused.jpg: No such file or directory
ls -l :  command not found
grep ^-: command not found
wc -l:   command not found

分别。这些命令在命令行上工作,但不适用于 bash 脚本。

给定一个包含1-pano.jpg 格式的图像文件的文件,我想抓取目录中的所有图像以获取最大编号的文件以附加到正在处理的下一个图像上。

为什么会出现差异?

【问题讨论】:

  • 你的命令周围的引号是什么?
  • 另外,在赋值给变量时去掉美元符号; files=$(find ...)num=$( ls -l ... ).
  • @sarnold,一些脚本教程说在执行块中有空格是一件坏事,使用双引号来降低风险。
  • @Jason - 您在“狂野”中找到的大多数脚本教程都是垃圾并且教坏习惯。 mywiki.wooledge.org/BashGuide
  • @jason:我很想看到对建议 foo=$("ls -l") 的指南的参考是一件有用的事情...... :)

标签: bash shell


【解决方案1】:

我能想到的最直接、最可靠的方法是使用find 命令来创建可靠的可数输出。

wc统计find的字符输出:

find . -maxdepth 1 -type f -printf '.' | wc --char

find输出的字符串长度:

a=$(find . -maxdepth 1 -type f -printf '.')
echo ${#a}

或使用find 输出来填充算术表达式:

echo $(($(find . -maxdepth 1 -type f -printf '+1')))

【讨论】:

    【解决方案2】:

    这是您可以将其作为函数执行的一种方法。注意:你可以传递这个例子,dirs 表示(目录计数),files 表示文件计数或“all”表示目录中所有内容的计数。不遍历树,因为我们不想这样做。

    function get_counts_dir() {
    
        # -- handle inputs (e.g. get_counts_dir "files" /path/to/folder)
        [[ -z "${1,,}" ]] && type="files" || type="${1,,}"
        [[ -z "${2,,}" ]] && dir="$(pwd)" || dir="${2,,}"
    
        shopt -s nullglob
        PWD=$(pwd)
        cd ${dir}
    
        numfiles=(*)
        numfiles=${#numfiles[@]}
        numdirs=(*/)
        numdirs=${#numdirs[@]}
    
        # -- handle input types files/dirs/or both
        result=0
        case "${type,,}" in
            "files")
                result=$((( numfiles -= numdirs )))
            ;;
            "dirs")
                result=${numdirs}
            ;;
            *)  # -- returns all files/dirs
                result=${numfiles}
            ;;
    
        esac
    
        cd ${PWD}
        shopt -u nullglob
    
        # -- return result --
        [[ -z ${result} ]] && echo 0 || echo ${result}
    }
    

    函数使用示例:

    folder="/home"
    get_counts_dir "files" "${folder}"
    get_counts_dir "dirs" "${folder}"
    get_counts_dir "both" "${folder}"
    

    将打印类似:

    2
    4
    6
    

    【讨论】:

      【解决方案3】:

      引号导致错误消息。

      获取目录中文件的数量:

      shopt -s nullglob
      numfiles=(*)
      numfiles=${#numfiles[@]}
      

      创建一个数组,然后用其元素的数量替换它。这将包括文件和目录,但不包括点文件或 ... 或其他点目录。

      使用nullglob,因此空目录的计数为 0 而不是 1。

      您可以改用find -type f 或者您可以计算目录并减去:

      # continuing from above
      numdirs=(*/)
      numdirs=${#numdirs[@]}
      (( numfiles -= numdirs ))
      

      另见“How can I find the latest (newest, earliest, oldest) file in a directory?

      您可以在一个执行块中拥有任意数量的空格。它们通常有助于提高可读性。唯一的缺点是它们会使文件变大一点,并且可能会(仅)稍微减慢初始解析。有一些地方必须有空格(例如在比较中[[[]]]= 周围)和一些不能(例如在分配中的= 周围)。

      【讨论】:

      • bash 数组对于这种用途来说有点太多了! (变大时变慢)
      • numfiles=(*); numfiles=${#numfiles[@]} 在空目录上返回 1
      • @Meisner:应该使用`nullglob。我已经编辑了我的答案。谢谢。
      【解决方案4】:

      扩展已接受的答案(Dennis W):当我尝试这种方法时,在 Bash 4.4.5 中没有子目录的目录计数不正确。

      问题在于,默认情况下,Bash 中未设置 nullglob,numdirs=(*/) 设置了一个具有 glob 模式 */ 的 1 元素数组。同样,我怀疑numfiles=(*) 会有一个空文件夹的元素。

      设置shopt -s nullglob 禁用nullglobbing 为我解决了这个问题。有关为什么在 Bash 上默认未设置 nullglob 的精彩讨论,请参阅此处的答案:Why is nullglob not default?

      注意:我会直接评论答案,但缺乏声誉点。

      【讨论】:

        【解决方案5】:
        ls -l | grep -v ^d | wc -l
        

        一行。

        【讨论】:

        • 这并不是普遍正确的。在大多数情况下,此调用将包含“total ”行(因此计数太多,请参阅stackoverflow.com/questions/7401704/…)。添加通配符以防止这种情况: ls -l * | grep -v ^d | wc -1
        • 很好看,也许:ls -l | grep -v ^d | grep -v ^t |然后 wc -l。
        • 我复制/粘贴了@OliverZendel 的评论,并得到 wc 的“非法选项”错误。 wc 的选项必须是 l,而不是 1 :-)
        • 我真的只是想知道一个目录中有多少个文件,所以这对我来说已经足够接近了。
        • 要排除符号链接,可以使用 ls -l | grep -v ^d | grep -v ^t | grep -v ^l | wc -l .
        【解决方案6】:

        简单高效的方法:

        #!/bin/bash
        RES=$(find ${SOURCE} -type f | wc -l)
        

        【讨论】:

        • 您能简要解释一下为什么会这样吗?代码转储(通常)是不鼓励的,因为 OP 可能对语法知之甚少,无法完全理解您正在尝试做什么。
        • 注意,如果目录中存在带换行符的文件名,这将不起作用。
        • RES=$(find ${SOURCE} -maxdepth 1 -type f -printf '.' | wc -c) 与文件名字符无关,因为它不计算名称,而是计算 find 为每个找到的文件打印的点字符的数量。
        【解决方案7】:

        file_num=$(ls -1 --file-type | grep -v '/$' | wc -l)

        这个比find命令轻一点,统计当前目录下的所有文件。

        【讨论】:

        • 对我来说,ls -1 --file-type 返回ls: illegal option -- -。如果您要提供特定于供应商的解决方案,请至少提及供应商。此外,这不排除符号链接。
        【解决方案8】:

        怎么样:

        count=$(find .. -maxdepth 1 -type f|wc -l)
        echo $count
        let count=count+1 # Increase by one, for the next file number
        echo $count
        

        请注意,此解决方案效率不高:它为 findwc 命令生成子 shell,但它应该可以工作。

        【讨论】:

        • 这与我最终做的类似。此外,以微秒为单位的效率并不是太大的问题,因为此命令将以大约 20-40 分钟的间隔运行。
        • 完全正确;如果您正确使用流水线,则没有任何生成将位于内部循环中。好吧,它们都将成为内循环装配线的一部分;但它们不会成为瓶颈。
        【解决方案9】:

        删除qoutes,你会没事的

        【讨论】:

          【解决方案10】:

          去掉引号。 shell 将它们视为一个文件,因此它正在寻找“ls -l”。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-07-25
            • 1970-01-01
            • 2011-06-10
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多