2017-04-25 14 views
0

我想实现一个bash脚本,它从管道命令中读取输入。我想处理输入中的每一行,并在每行上执行一些处理。另外,我想重用传递给脚本的输入。但是,我注意到我无法重用输入。下面是我的脚本:Ubuntu的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 

Thi脚本只输出总数,而while循环没有执行。

此任务需要多个管道命令,因为此脚本将被许多事物使用,以便保持通用和可重用。如何在不将内容存储到文件的情况下执行此操作?

样品输入:

1 STATUS 
2 AUTHORISED 
11 SENT 

总计将是14.因此,每个项目即,1/14 * 100,2/14 * 100和11/14 * 100。

+1

您已经使用'削减-f1 $ 1' ,但是你没有通过命令行传递任何东西。所以,这个命令变成了'cut -f1',从而把'stdin'作为文件名。由于所有'stdin'耗尽,'read'在第一个'read'时失败,因此循环从不执行。 – anishsane

+0

此外,你在用'while input $ input'尝试着什么? – anishsane

+0

你是什么意思? –

回答

0

你的脚本将无法工作,因为你1 $ arg是空的。

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

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

然后,你需要去实际计算总,我这样做,在这里用awk,因为我是懒惰,但也有可能是一个更简单的方法。

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

我编辑的脚本,我在测试环境中工作,见下图:

#!/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 
0

这很简单:

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 
+0

这不适合我的需求。我有多个传送到脚本中的管道命令。所以在进入脚本之前,grep已经完成了。其他解决方案?我想将输入重用到变量中。这是我最初在这篇文章中所要求的。 –

+0

已更新答案以处理此要求... – anishsane

0

如果您想根据总数执行算术,您需要在执行算术之前知道总数。因此,您需要两个循环 - 一个读取数据并合计总数,另一个循环根据总数处理数据。

#!/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") 
0

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

(此脚本假定你是一个总计列,并在每行该列一个栏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