Bash脚本:最大,最小,总和-许多来源作为参数

时间:2019-04-26 17:50:24

标签: linux bash algorithm shell

是否可以编写一个脚本来读取包含数字的文件(每行一个)并写入其最大值,最小值和总和。如果文件为空,它将打印一条适当的消息。文件名将作为脚本的参数给出。我要创建以下脚本,但是有2个错误:

./4.3: line 20: syntax error near unexpected token `done'
./4.3: line 20: `done echo "Max: $max" '

是否可以添加多个文件作为参数?

lines=`cat "$1" | wc -l` 
if [ $lines -eq 0 ]; 
then echo "File $1 is empty!" 
exit fi min=`cat "$1" | head -n 1` 
max=$min sum=0 
while [ $lines -gt 0 ]; 
do num=`cat "$1" | 
tail -n $lines` 
if [ $num -gt $max ]; 
then max=$num 
elif [ $num -lt $min ]; 
then min=$num fiS 
sum=$[ $sum + $num] lines=$[ $lines - 1 ] 
done echo "Max: $max" 
echo "Min: number $min"
echo "Sum: $sum"

5 个答案:

答案 0 :(得分:2)

在这里相当吸引人地使用GNU datamash:

read sum min max < <( datamash  sum 1 min 1 max 1 < "$1" )
[[ -z $sum ]] && echo "file is empty"
echo "sum=$sum; min=$min; max=$max"

或者,排序并awk:

sort -n "$1" | awk '
    NR == 1 { min = $1 }
    { sum += $1 }
    END {
        if (NR == 0) {
            print "file is empty"
        } else {
            print "min=" min
            print "max=" $1
            print "sum=" sum
        }
    }
'

答案 1 :(得分:2)

在这里,我会尽可能地保留您的初衷,以解决您的原始尝试:

#!/usr/bin/env bash

lines=$(wc -l "$1")

if [ "$lines" -eq 0 ]; then
    echo "File $1 is empty!"
    exit
fi

min=$(head -n 1 "$1")
max=$min
sum=0

while [ "$lines" -gt 0 ]; do
    num=$(tail -n "$lines" "$1")
    if [ "$num" -gt "$max" ]; then
        max=$num
    elif [ "$num" -lt "$min" ]; then
        min=$num
    fi
    sum=$(( sum + num ))
    lines=$(( lines - 1 ))
done

echo "Max: $max"
echo "Min: number $min"
echo "Sum: $sum"

交易突破者缺少换行符(不能在没有exit fi的一行上使用;);其他更改也是好的做法(引用扩展名,cat的无用使用),但不会阻止脚本工作;其他则是装饰性的(缩进,没有反引号)。

不过,整体方法是一个庞大的反模式:您要读取要处理的每一行的整个文件。


这是我要怎么做:

#!/usr/bin/env bash

for fname in "$@"; do
    [[ -s $fname ]] || { echo "file $fname is empty" >&2; continue; }

    IFS= read -r min < "$fname"
    max=$min
    sum=0

    while IFS= read -r num; do
        (( sum += num ))
        (( max = num > max ? num : max ))
        (( min = num < min ? num : min ))
    done < "$fname"

    printf '%s\n' "$fname:" "  min: $min" "  max: $max" "  sum: $sum"
done

这使用正确的方法来循环输入文件,并在算术上下文中利用三元运算符。

最外面的for循环遍历所有参数。

答案 2 :(得分:0)

您可以在shell脚本中的while循环中完成全部操作。这是bash版本:

s=0
while read x; do
  if [ ! $mi ]; then
    mi=$x
  elif [ $mi -gt $x ]; then
    mi=$x
  fi
  if [ ! $ma ]; then
    ma=$x
  elif [ $ma -lt $x ]; then
    ma=$x
  fi
  s=$((s+x))
done
if [ ! $ma ]; then
  echo "File is empty."
else
  echo "s=$s, mi=$mi, ma=$ma"
fi

将该脚本保存到文件中,然后可以使用管道将任意数量的输入文件发送到其中,就像这样(假设脚本称为“ mysum”):

cat file1 file2 file3 | mysum

或单个文件

mysum < file1

(请确保脚本是可执行文件,并且在$ PATH上,否则,对当前目录中的脚本使用“ ./mysum”;如果不可执行,则使用“ bash mysum”。)

该脚本假定每行数字为1,并且该行没有其他内容。如果输入为空,则会显示一条消息。

它如何工作? “ read x”将逐行从stdin输入。如果文件为空,则while循环将永远不会运行,因此不会设置变量mi和ma。因此,我们最后使用它来触发适当的消息。否则,循环将首先检查mi和ma变量是否存在。如果不是,则使用第一个x进行初始化。否则,检查下一个x是否需要更新到目前为止找到的mi和ma。

请注意,此技巧可确保您输入任意数字序列。否则,您必须使用绝对太大的东西来初始化mi,而必须使用绝对太小的东西来初始化-直到您遇到一个奇怪的数字列表时,它才起作用。

请进一步注意,这仅适用于整数。如果您需要使用浮子,则需要使用除Shell之外的其他工具,例如啊!

只是为了好玩,这是awk版本,单行,按原样或在脚本中使用,它也适用于浮点数:

cat file1 file2 file3 | awk 'BEGIN{s=0}; {s+=$1; if(length(mi)==0)mi=$1; if(length(ma)==0)ma=$1; if(mi>$1)mi=$1; if(ma<$1)ma=$1} END{print s, mi, ma}'

或一个文件:

awk 'BEGIN{s=0}; {s+=$1; if(length(mi)==0)mi=$1; if(length(ma)==0)ma=$1; if(mi>$1)mi=$1; if(ma<$1)ma=$1} END{print s, mi, ma}' < file1

缺点:如果没有给出有关空文件的错误消息。

答案 3 :(得分:0)

  

一个脚本,该文件读取包含数字的文件(每行一个)并写入其最大值,最小值和总和

使用sort的Bash解决方案:

<file sort -n | {
    read -r sum
    echo "Min is $sum"
    while read -r num; do
       sum=$((sum+num));
    done
    echo "Max is $num"
    echo "Sum is $sum"
}

让我们通过使用teetr进行一些智能解析并使用bc进行计算来加快速度,如果我们不介意使用stderr进行输出。但是我们可以做些fifo并同步tee输出。无论如何:

{ 
    <file sort -n | 
    tee >(echo "Min is $(head -n1)" >&2) >(echo "Max is $(tail -n1)" >&2) |
    tr '\n' '+'; 
    echo 0; 
} | bc | sed 's/^/Sum is /'

总是有datamash。以下Willl输出3个数字,即和,最小和最大:

<file datamash sum 1 min 1 max 1

答案 4 :(得分:0)

您可以尝试使用shell循环和dc

while [ $# -gt 0 ] ; do
  dc -f - -e '
    ['"$1"' is empty]sa
    [la p q ]sZ
    z 0 =Z
# if file is empty
    dd sb sc
# populate max and min with the first value
    [d sb]sY
    [d lb <Y ]sM
# if max keep it
    [d sc]sX
    [d lc >X ]sN
# if min keep it
    [lM x lN x ld + sd z 0 <B]sB
    lB x
# on each line look for max, min and keep the sum
    [max for '"$1"' = ] n lb p
    [min for '"$1"' = ] n lc p
    [sum for '"$1"' = ] n ld p
# print summary at end of each file
  ' <"$1"
  shift
done