【问题标题】:how to count the number of lines in a variable in a shell script如何计算shell脚本中变量的行数
【发布时间】:2015-09-22 21:21:54
【问题描述】:

这里有点麻烦。我想将 ls 命令的输出捕获到变量中。然后稍后使用该变量并计算其中的行数。我尝试了一些变化

这可行,但如果没有 .txt 文件,则表示计数为 1:

testVar=`ls -1 *.txt`
count=`wc -l <<< $testVar`
echo '$count'

这适用于没有 .txt 文件的情况,但当有 .txt 文件时计数会减少 1:

testVar=`ls -1 *.txt`
count=`printf '$testVar' | wc -l`
echo '$count'

此变体还表示不存在 .txt 文件时计数为 1:

testVar=`ls -1 *.txt`
count=`echo '$testVar' | wc -l`
echo '$count'

编辑:我应该提到这是 korn shell。

【问题讨论】:

    标签: shell unix ksh


    【解决方案1】:

    我打算建议这个,这是我在 bash 中使用的构造:

    f=($(</path/to/file))
    echo ${#f[@]}
    

    要处理多个文件,您只需 .. 添加文件。

    f=($(</path/to/file))
    f=+($(</path/to/otherfile))
    

    f=($(</path/to/file) $(</path/to/otherfile))
    

    要处理大量文件,您可以循环:

    f=()
    for file in *.txt; do
        f+=($(<$file))
    done
    

    然后我看到了 chepner 的回应,我认为这比我的更korn-y。

    注意:循环比 parsing ls 更好。

    【讨论】:

      【解决方案2】:

      正确的做法是使用数组。

      # Use ~(N) so that if the match fails, the array is empty instead
      # of containing the pattern itself as the single entry.
      testVar=( ~(N)*.txt )
      count=${#testVar[@]}
      

      【讨论】:

      • 你能解释一下 ~(N)* 片段吗?这个符号叫什么?我有比 *.txt 更复杂的文件匹配逻辑来实现。
      • 我认为它只是称为子模式;它记录在手册页的“文件名生成”下,位于“引用”部分之前的部分末尾。 (文档将其称为使不匹配的模式扩展为空字符串,我认为这不太准确。它似乎与bash shell 选项nullglob 相同,这会导致不匹配的模式被简单地忽略。)
      • 没有~(N),如果没有匹配的文件,数组将包含单个元素*.txt${#testVar[@]} 将扩展为1,而不是0。
      • @GillesQuenot shopt 是一个bash 命令,不是ksh 的一部分。 (我在写这个答案时发现了这一点,这让我了解了特殊的~-patterns。)
      【解决方案3】:

      这个小问题实际上包含了三个标准 shell 陷阱(bash 和 korn shell)的结果:

      1. Here-strings (&lt;&lt;&lt;...) 如果不以换行符结尾,则会添加一个换行符。这使得无法向带有此处字符串的命令发送完全空的输入。

      2. 从用于命令替换的命令输出中删除所有尾随换行符(cmd 或最好是$(cmd))。所以你无法知道输出末尾有多少空​​行。

      3. (不是真正的 shell 陷阱,但它经常出现)。 wc -l 计算换行符的数量,而不是行数。因此,如果最后一个“行”没有以换行符终止,则不计算在内。 (不以换行符结尾的非空文件不是符合 Posix 的文本文件。所以像这样奇怪的结果并不意外。)

      所以,当你这样做时:

      var=$(cmd)
      utility <<<"$var"
      

      第一行中的命令替换删除了所有尾随换行符,然后第二行中的此处字符串扩展正好放回一个尾随换行符。这会将空输出转换为单个空行,否则会从输出末尾删除空行。

      所以utilitywc -l,那么除非输出为空,否则您将得到正确的计数,在这种情况下它将是 1 而不是 0。

      另一方面,与

      var=$(cmd)
      printf %s "$cmd" | utility
      

      和以前一样,通过命令替换删除了尾随换行符,因此printf 保留最后一行(如果有)未终止。现在,如果 utilitywc -l,如果输出为空,您将得到 0,但对于非空文件,计数将不包括输出的最后一行。

      一种可能的独立于 shell 的解决方法是使用第二个选项,但使用 grep '' 作为过滤器:

      var=$(cmd)
      printf %s "${var}" | grep '' | utility
      

      空模式 '' 将匹配每一行,grep 总是终止每一行输出。 (当然,这仍然不会计算输出末尾的空行。)


      说了这么多,尝试解析ls的输出总是一个坏主意,即使只是计算文件的数量。 (例如,文件名可能包含换行符。)因此,最好使用 glob 扩展结合一些特定于 shell 的方法来计算 glob 扩展中的对象数量(以及其他一些特定于 shell 的检测方法当没有文件与 glob 匹配时)。

      【讨论】:

        【解决方案4】:

        你也可以这样使用:

        #!/bin/bash
        
        testVar=`ls -1 *.txt`
        
        if [ -z "$testVar" ]; then
                # Empty
                count=0
        else
                # Not Empty
                count=`wc -l <<< "$testVar"`
        fi
        
        echo "Count : $count"
        

        【讨论】:

        • 这很有帮助,但我想了解为什么需要这样做。如果输出为空,wc 会处理什么?
        • @user3349673,如果输入为空而传递为HERESTRING,则wc 计为一行。您可以使用命令检查它:wc -l &lt;&lt;&lt; "".
        • @user3349673,除非是EOF ( Ctrl + D),否则wc 会增加它的行数。
        • @user3349673:sat 的解释不太准确,但完整的解释太长,无法评论,所以我做了一个答案。
        猜你喜欢
        • 1970-01-01
        • 2022-12-18
        • 2020-10-09
        • 1970-01-01
        • 2015-03-16
        • 2022-08-14
        • 2021-09-01
        • 2017-12-09
        • 1970-01-01
        相关资源
        最近更新 更多