【问题标题】:In bash, how do I count the number of lines in a variable?在 bash 中,如何计算变量中的行数?
【发布时间】:2011-09-12 23:14:31
【问题描述】:

我有一个变量,其中存储了一个字符串,需要检查它是否有行:

var=`ls "$sdir" | grep "$input"`

伪代码:

while [ ! $var's number of lines -eq 1 ]
  do something

这是我关于如何检查它的想法。 echo $var | wc -l 不起作用 - 它总是显示 1,即使它有 3

echo -e 也不行。

【问题讨论】:

    标签: bash string


    【解决方案1】:

    为了避免“wc -l”命令中的文件名:

    lines=$(< "$filename" wc -l)
    echo "$lines"
    

    【讨论】:

      【解决方案2】:

      没有人提到参数扩展,所以这里有两种使用纯 bash 的方法。

      方法一

      去掉非换行符,然后得到字符串长度+1。引号很重要

       var="${var//[!$'\n']/}"
       echo $((${#var} + 1))
      

      方法二

      转换为数组,然后得到数组长度。 为此,请不要使用引号

       set -f # disable glob (wildcard) expansion
       IFS=$'\n' # let's make sure we split on newline chars
       var=(${var})
       echo ${#var[@]}
      

      【讨论】:

      • 方法 2 更干净,可能更快。但是它依赖于IFS。所以设置IFS=$'\n' 以确保在将变量扩展为数组时将其拆分为新行:IFS=$'\n'; var=(${var})
      • 我喜欢方法 2,因为它的开销最小:没有外部命令,甚至没有内置命令。它的可读性也很强。
      • ShellCheck 抱怨:github.com/koalaman/shellcheck/wiki/SC2206。它在某事上。需要set -f 以避免不必要的全局扩展。尝试在var=$'*\n.*'上使用方法2。
      • 纯内部 bash 无外部 wc -l 的自动奖励
      【解决方案3】:

      另一种计算变量行数的方法 - 假设您确实检查了它是否已成功填充或它不为空,为此只需检查 $?在 var 子外壳结果做作之后 - :

      readarray -t tab <<<"${var}"
      echo ${#tab[@]}
      

      readarray|mapfile 是 bash 内部命令,它将输入文件或 这里的字符串 在这种情况下转换为基于换行符的数组。

      -t 标志防止在数组的单元格末尾存储换行符,对以后使用存储的值很有用

      这种方法的优点是:

      • 没有外部命令(wc、grep、...)
      • 没有子外壳(管道)
      • 没有 IFS 问题(修改后恢复,难以在内部命令的命令限制范围内使用,...)

      【讨论】:

        【解决方案4】:

        @Julian 答案的更简单版本,适用于所有字符串,无论是否有尾随 \n(它确实将仅包含单个尾随 \n 的文件视为空):

        printf "%s" "$a" | grep -c "^"

        • 返回零:未设置的变量、空字符串、包含裸换行符的字符串
        • 返回 1:任何非空行,带或不带尾随换行符

        输出:

        # a=
        # printf "%s" "$a" | grep -c "^"
        0
        
        # a=""
        # printf "%s" "$a" | grep -c "^"
        0
        
        # a="$(printf "")"
        # printf "%s" "$a" | grep -c "^"
        0
        
        # a="$(printf "\n")"
        # printf "%s" "$a" | grep -c "^"
        0
        
        # a="$(printf " \n")"
        # printf "%s" "$a" | grep -c "^"
        1
        
        # a="$(printf " ")"
        # printf "%s" "$a" | grep -c "^"
        1
        
        # a="aaa"
        # printf "%s" "$a" | grep -c "^"
        1
        
        # a="$(printf "%s" "aaa")"
        # printf "%s" "$a" | grep -c "^"
        1
        
        # a="$(printf "%s\n" "aaa")"
        # printf "%s" "$a" | grep -c "^"
        1
        
        # a="$(printf "%s\n%s" "aaa" "bbb")"
        # printf "%s" "$a" | grep -c "^"
        2
        
        # a="$(printf "%s\n%s\n" "aaa" "bbb")"
        # printf "%s" "$a" | grep -c "^"
        2
        

        【讨论】:

        • +1。将标志传递给echo(例如echo -n)不是标准的,并且可能在不同的实现上给出不同的结果。而printf 默认做我们想要的。作为奖励,使用printf 可以为您节省一个进程,因为它是一个内置的shell。 (建议:printf "$a" | wc -l更简洁,避免不必要的使用grep
        • @joshtch 嗯.. 不。正如本次讨论中的其他人所说,printf .... | wc -l 只会删除一个额外的行(换行符),以便在 空行 的情况下,结果将是 0。正确的。但是如果我们传递 2 行,结果将是 1,其中传递给 printf ... | grep "^" 的相同变量将正确返回 2。此外,直接使用 printf "$a" 是非常危险的,因为如果字符串不小心会导致静默错误包含%s%d 等字符...如果字符串以破折号开头,则相同。相反,printf 中的 2 参数会自动转义
        【解决方案5】:

        在 bash 中使用 here strings 的另一种方式:

        wc -l <<< "$var"
        

        this comment 中所述,空的$var 将产生1 行而不是0 行,因为这里的字符串 在这种情况下添加了换行符(explanation)。

        【讨论】:

        • 我必须这样做才能得到正确答案:wc -l &lt;&lt;&lt;"$(echo "$var")"(是的,每个符号都是必要的)
        • @NicolaiS 那你做错了什么。 $var的内容是什么?
        • @NicolaiS 这是正确的,因为您的var 包含一行:您没有将\n 解释为任何内容。将多行放入您的 var 中,它将起作用,例如var="foo&lt;ENTER&gt;bar&lt;ENTER&gt;baz"&lt;ENTER&gt;.
        • xxd &lt;&lt;&lt; '' 创建这个 hexdump 00000000: 0a。因此,&lt;&lt;&lt; (Here Strings) 为任何内容添加换行符。
        • @MiniMax 谢谢,我将您的输入添加到我的答案中。您可以找到对此行为的解释 here
        【解决方案6】:

        如果 grep 未返回任何结果,则投票最多的答案将失败。

        Homer Simpson
        Marge Simpson
        Bart Simpson
        Lisa Simpson
        Ned Flanders
        Rod Flanders
        Todd Flanders
        Moe Szyslak
        

        这是the wrong way to do it

        wiggums=$(grep -iF "Wiggum" characters.txt);
        num_wiggums=$(echo "$wiggums" | wc -l);
        echo "There are ${num_wiggums} here!";
        

        会告诉我们,列表中有1Wiggum,即使没有。

        相反,您需要额外检查变量是否为空(-z,如“为零”)。如果 grep 没有返回任何内容,则该变量将为空。

        matches=$(grep -iF "VanHouten" characters.txt);
        
        if [ -z "$matches" ]; then
            num_matches=0;
        else
            num_matches=$(echo "$matches" | wc -l);
        fi
        
        echo "There are ${num_matches} VanHoutens on the list";
        

        【讨论】:

          【解决方案7】:

          此处发布的已接受答案和其他答案在空变量(未定义或空字符串)的情况下不起作用。

          这行得通:

          echo -n "$VARIABLE" | grep -c '^'
          

          例如:

          ZERO=
          ONE="just one line"
          TWO="first
          > second"
          
          echo -n "$ZERO" | grep -c '^'
          0
          echo -n "$ONE" | grep -c '^'
          1
          echo -n "$TWO" | grep -c '^'
          2
          

          【讨论】:

          • 我无法重现来自 @PolyTekPatrick 的错误,即我的 shell (bash) 中的 WHITESPACE_ONE=' ' 仍然有效,正确报告了一行。
          • 你是对的。我发誓我测试过它,但我一定错过了双引号或输入错误的东西。正如预期的那样,一个或多个空白字符确实被计为 1 行。我删除了我之前的评论以避免混淆。
          • 完美!这应该是公认的答案。这是迄今为止唯一能够正确回答所有情况的问题的解决方案。感谢您展示测试用例来证明这一点。
          • 是的!我也认为这应该是公认的答案。
          • 这是正确的,但可以使用printf 而不是echo -n 来简化。我已将此添加为替代答案,以包含测试结果。见下文。
          【解决方案8】:

          您可以用“wc -w”代替“wc -l”来计算字数而不是行数。这不会计算任何新行,可用于在您继续之前测试您的原始结果是否为空。

          【讨论】:

          • wc -lsolutions即使输入变量为空也会输出1,所以wc -w会更好用。
          【解决方案9】:

          行情很重要。

          echo "$var" | wc -l
          

          【讨论】:

          • 这不是围绕命令的引号,而是围绕命令替换的引号。引号对不会干扰。
          • 这有一个微妙之处。空字符串将返回 1,因为空字符串上的 echo 会打印一个换行符。
          • 换句话说:if [ -z "$var" ]; then printf '%s\n' '0'; else printf '%s\n' "${var%$'\n'}" | wc -l; fi。尝试使用 var=(无行)、var='foo'var=$'foo\n'(在 *nix 意义上都是一行)。
          • 还有另一种将其放入变量的方法:LINE_COUNT=$(wc -l &lt;&lt;&lt; "${var}")
          • @Tim echo -n 只会将计数减一。 @lucifurious echo $(wc -l &lt;&lt;&lt; "${NONEXISTENTVAR}") 仍然给出 1,而不是 0
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-07
          • 2013-03-13
          • 2020-07-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多