【问题标题】:Ubuntu bash script reuse input variableUbuntu bash 脚本重用输入变量
【发布时间】:2017-04-25 10:24:52
【问题描述】:

我正在尝试实现一个从管道命令读取输入的 bash 脚本。我想处理输入上的每一行并在每一行上执行一些处理。此外,我想重用传递给脚本的输入。但是,我注意到我无法重用输入。以下是我的脚本:

#!/bin/bash
while read line
do
    percentage=$(echo $line | awk 'printf $1')
    # perform some processing using the total i.e. percentage/total*100
done

这个脚本是这样执行的:

cat data.txt | grep "status" | ./myscript.sh

样本数据是:

1 STATUS
2 AUTHORISED
11 SENT

如果脚本被修改成这样:

#!/bin/bash
input=$1
total=$(cut -f1 $1)
echo $total
while read $input
do
    percentage=$(echo $line | awk 'printf $1')
     # perform some processing using the total i.e. percentage/total*100
done

这个脚本只输出总数,不执行while循环。

此任务需要多个管道命令,因为此脚本将被许多事物使用,以保持其通用性和可重用性。如果不将内容存储到文件中,如何实现这一点?

示例输入:

1 STATUS
2 AUTHORISED
11 SENT

总计为 14。所以每个项目即 1/14*100、2/14*100 和 11/14*100。

【问题讨论】:

  • 您使用了cut -f1 $1,但您没有通过命令行传递任何内容。因此,该命令变为 cut -f1,因此将 stdin 作为文件名。由于所有stdin 都用尽了,while read 第一个read 失败,因此循环永远不会执行。
  • 另外,你用while read $input做了什么?
  • 什么意思?
  • 我正在尝试通过打印每一行并执行一些计算(例如百分比等)来处理每一行。命令 grep "status" 就是一个示例。它可能是 grep S ,它将返回所有 3 个项目。
  • 我不明白你的例子。如果您的输入与您显示的一样,那么grep status 将不会产生任何输出。 grep -i status 会产生一行输出...但在这种情况下,您要求和,总数是多少?

标签: linux bash ubuntu grep pipe


【解决方案1】:

您的脚本无法运行,因为您的 $1 参数为空。

使用 xargs 获取参数,而不是将其用作标准输入。此外,由于输出可能是多行,您可以在 xargs 上使用 -0 来使用空分隔符。

> cat data.txt | grep -i 'status' | xargs -0 ./myscript.sh

然后你需要实际计算总数,我在这里使用 awk 进行了计算,因为我很懒,但可能有更简单的方法。

然后您可以使用 read 逐行读取输入。

我编辑了脚本以在测试环境中为我工作,见下文:

#!/bin/bash                                              
input="$1"                                               
echo "$input"                                            
total=$(echo "$input" | awk '{sum+=$1} END {print sum}') 
echo "$input" | while read line; do                      
      if [ ! -z "$line" ]; then                          
          num=$(echo $line | awk '{print $1}')           
          percentage=$((100 * num / total))              
          echo "$percentage"                             
      fi                                                 
done  

【讨论】:

    【解决方案2】:

    这很简单:

    awk 'NR==FNR{a+=$1; next;} {print $1*100/a;}' data.txt data.txt
    

    对于grep操作部分:

    awk 'NR==FNR{if ($0 ~ pattern) a+=$1; next;} {if ($0 ~ pattern) print $1*100/a;}' pattern=STATUS data.txt data.txt
    

    如果输入不在文件中,我们可以将其缓存在awk 本身中。

    awk '{a+=$1; b[NR]=$1;} END {for(i in b) print b[i]*100/a;}' data.txt
    
    awk '$0 ~ pattern {a+=$1; b[NR]=$1;} END {for(i in b) print b[i]*100/a;}'  pattern=STATUS data.txt
    
    some_command | awk '$0 ~ pattern {a+=$1; b[NR]=$1;} END {for(i in b) print b[i]*100/a;}'  pattern=STATUS
    

    【讨论】:

    • 这不能满足我的需要。我有多个输入脚本的管道命令。所以 grep 在进入脚本之前就已经完成了。还有其他解决方案吗?我想将输入重用到一个变量中。这就是我最初在这篇文章中要求的。
    【解决方案3】:

    如果您想根据总数执行算术,您需要在执行算术之前知道总数。因此,您需要两个循环——一个用于读取数据并将总数相加,另一个用于根据总数处理该数据。

    #!/usr/bin/env bash
    
    # Prepare our variables
    declare total=0
    declare -a data=()
    declare -a newdata=()
    
    # Collect the array data and build our total...
    while read value text; do
        data+=($value)
        ((total+=$value))
    done
    
    # Process the array, populating a new array with the results.
    for i in "${!data[@]}"; do
        newdata[$i]=$(( 100 * ${data[$i]} / $total ))
    done
    
    # Show our results
    declare -p data
    declare -p newdata
    

    请记住,bash 仅支持整数数学,因此使用100 * $val1 / $val2 而不是$val1 / $val2 * 100 很重要。如果您想要更高的精度,请考虑使用 bcdc 等外部工具,而不是仅在 bash 中进行数学运算。

    上面的脚本对您问题中的示例数据的结果:

    $ testme.sh < testme.txt
    declare -a data=([0]="1" [1]="2" [2]="11")
    declare -a newdata=([0]="7" [1]="14" [2]="78")
    

    【讨论】:

      【解决方案4】:

      最好使用临时文件。但是将数据存储在内存中很容易。

      (此脚本假设您汇总第一列,并且每行中的第一列是与第 2 列中的状态匹配的数据点的计数):

      #!/bin/sh
      
      data=$(cat)  # Read all of the input and store it
      total=$(echo "$data" | awk '{s+=$1}END{print s}')
      echo "$data" | while read count status; do
              printf "%s:\t%2.2f%%\n" \
              "$status" "$(echo "20k$count $total/100*p" | dc)"
      done
      

      【讨论】:

        猜你喜欢
        • 2021-08-22
        • 1970-01-01
        • 2021-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多