Скрипт Bash: max, min, sum - много источников в качестве параметра - PullRequest
0 голосов
/ 26 апреля 2019

Можно ли написать скрипт, который читает файл, содержащий числа (по одному в строке) и записывает их максимум, минимум и сумму. Если файл пуст, он напечатает соответствующее сообщение. Имя файла указывается в качестве параметра скрипта. Мне удается создать приведенный ниже скрипт, но есть 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 ]

2 голосов
/ 26 апреля 2019

Вот как я бы исправил вашу первоначальную попытку, сохранив как можно больше намерений:

#!/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 голосов
/ 26 апреля 2019

Довольно убедительное использование 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
        }
    }
'
0 голосов
/ 27 апреля 2019

Можно попробовать с оболочкой цикла и 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
0 голосов
/ 26 апреля 2019

скрипт, который читает файл, содержащий числа (по одному в строке) и записывает их максимум, минимум и сумму

Решение Bash с использованием sort:

<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"
}

Давайте ускоримся, используя умный анализ с использованием tee, tr и вычисление с помощью bc, и если мы не возражаем, используя stderr для вывода.Но мы могли бы сделать немного fifo и синхронизировать вывод тройника.В любом случае:

{ 
    <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.Следующие числа будут выводить 3 числа: сумма, мин и макс:

<file datamash sum 1 min 1 max 1
0 голосов
/ 26 апреля 2019

Вы можете сделать все это в одном цикле 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», если он не исполняемый.)

Сценарий предполагает, что числа по одному в строке и что в строке больше ничего нет.Выдает сообщение, если ввод пуст.

Как это работает?«Чтение x» будет принимать ввод от stdin построчно.Если файл пуст, цикл while никогда не запустится, поэтому переменные mi и ma не будут установлены.Таким образом, мы используем это в конце, чтобы вызвать соответствующее сообщение.В противном случае цикл сначала проверяет, существуют ли переменные mi и ma.Если они этого не делают, они инициализируются с первым х.В противном случае проверяется, требует ли следующий x обновления найденных миль и ма.

Обратите внимание, что этот прием гарантирует, что вы можете вводить любую последовательность чисел.В противном случае вам придется инициализировать mi с чем-то, что определенно слишком велико, и ma с чем-то, что определенно слишком мало, - что работает, пока не встретится странный список чисел.

Обратите внимание, что это работает только для целых чисел.Если вам нужно работать с плавающей точкой, вам нужно использовать другой инструмент, отличный от оболочки, например, awk.

Просто для удовольствия, вот версия 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

Недостаток: если не выдает сообщение об ошибке для пустого файла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...