Я долго пытался написать сценарий, в котором было бы 2 аргумента, 1 с просьбой выбрать год и 2 с просьбой выбрать, хочу ли я, чтобы минимальная, максимальная, средняя или все значения отображались как последняя строка из файлы, относящиеся к выбранному году.
В основном, у меня есть каталог, который содержит подкаталоги разных лет (2000, 2001, 2002 и т. Д.), В этих каталогах есть подкаталоги по месяцам и дням, которые содержат (а) файл (ы), информирующий о популяциях (не реальная информация, хотя) из разных городов, как последняя строка. Это часть дерева каталога:
.
|-- 2000
| |-- 01
| | `-- 18
| | `-- ff_1177818640
| |-- 02
| | |-- 02
| | | `-- ff_1669027271
| | |-- 03
| | | `-- ff_234075290
| | |-- 10
| | | `-- ff_1584524530
| | |-- 14
| | | `-- ff_113807345
| | `-- 17
| | `-- ff_1452228827
| |-- 03
| | |-- 06
| | | `-- ff_58914249
| | `-- 11
| | `-- ff_2828212321
| |-- 04
| | `-- 17
| | `-- ff_302131884
| |-- 06
| | `-- 13
| | `-- ff_2175615745
| |-- 07
| | |-- 07
| | | `-- ff_918426998
| | `-- 24
| | `-- ff_2808316425
| |-- 08
| | `-- 27
| | `-- ff_1449825497
| |-- 09
| | `-- 19
| | `-- ff_110255856
| `-- 12
| `-- 08
| `-- ff_1621190
|-- 2001
| |-- 03
| | `-- 21
| | `-- ff_517010375
| |-- 05
| | `-- 27
| | `-- ff_1458621098
| |-- 06
| | |-- 07
| | | `-- ff_155853916
| | |-- 25
| | | |-- ff_2382312387
| | | `-- ff_270731174
| | `-- 29
| | `-- ff_3228522859
| |-- 07
| | `-- 28
| | `-- ff_3215021752
| |-- 09
| | `-- 24
| | `-- ff_1080314364
| `-- 11
| `-- 24
| `-- ff_2313722442
Все файлы отформатированы одинаково:
2019-04-03
Wednesday
Newcastle-upon-Tyne
255362
Мне нужно написать скрипт, чтобы спросить, какой год мне нужен ( выберите этот каталог), а затем спросите, хотите ли я отобразить среднее, минимальное, максимальное или все вышеперечисленное для населения (это последняя строка файлов).
Это то, что у меня есть до сих пор:
#!/bin/bash
function min () {
echo $(sort -n populations | head -1)
}
function max () {
echo $(sort -n populations | tail -1)
}
function avg () {
count=0
sum=0
while read line ; do
num='echo ${line#* }'
sum='expr $sum + $num'
count='expr $count + 1'
done < populations
avg='expr $sum / $count'
echo $avg
}
echo "Please enter the year: "
read s1
echo "
Enter an option:
1. Minimum
2. Maximum
3. Average
4. All"
read s2
#echo $s2
for file in $(find ~/filesToSort/$s1 -type f) ; do
tail -1 $file >> populations
done
echo $(cat populations)
#min
#max
#avg
rm populations
Это позволяет мне выбирать каталоги, но не дает мне ответов, которые мне нужны, просто выплевывает последние строки моих файлов.
Если бы я реализовал это в bash, я бы сделал следующее. Я не буду комментировать это много: не стесняйтесь задавать конкретные вопросы , хотя - сначала проверьте страницу руководства bash, если вы не знаете, как работает конкретная команда.
#!/bin/bash
# read the population from all the files
# map the filename to it's population figure
declare -A population
while IFS= read -d '' -r filename; do
population["$filename"]=$(tail -1 "$filename")
done < <(find . -type f -print0)
# prompt the user for the year
read -rp "What year? " year
# find the relevant files for that year
year_files=()
for filename in "${!population[@]}"; do
[[ $filename == ./"$year"/* ]] && year_files+=("$filename")
done
if [[ "${#year_files[@]}" -eq 0 ]]; then
echo "No files for year '$year'"
exit 1
fi
PS3="Select a function to calculate: "
select func in minimum maximum average quit; do
case $func in
minimum)
min=${population[${year_files[0]}]}
for file in "${year_files[@]}"; do
if (( min > ${population[$file]} )); then
min=${population[$file]}
fi
done
echo "Minimum for $year is $min"
;;
maximum)
max=${population[${year_files[0]}]}
for file in "${year_files[@]}"; do
if (( max < ${population[$file]} )); then
max=${population[$file]}
fi
done
echo "Maximum for $year is $max"
;;
average)
count=0 sum=0
for file in "${year_files[@]}"; do
(( sum += ${population[$file]} ))
(( count++ ))
done
echo "Average for $year is $(( sum / count ))"
;;
quit) exit ;;
esac
done
Как написано, скрипт ничего не делает, кроме как печатает популяции, потому что avg и т. Д. Закомментированы.
Для вычисления avg эти группы должны быть отправлены в функцию avg () с чем-то вроде ...
echo "$(cat populations | avg)"
Подобные строки будут добавлены для min () и max ().
Вы можете использовать оператор case
для вызова соответствующей функции (ей) ...
:
done
#
case s2
1|4) echo "$(cat populations | min)" ;;&
2|4) echo "$(cat populations | max)" ;;&
3|4) echo "$(cat populations | avg)";;
esac
#
rm populations
1|4) echo ...
заставляет эхо работать, если введены 1 или 4. Таким образом, все 3 будут выполнены, если 4 было введено.
Я пишу простое awk
сценарий, который делает то же как, что Вы делаете:
# read 'year' & 'option' from user
# or you can pass as argument to the command $1<-->$year & $2<-->$option
find /path/to/$year -type f -exec \
awk -v select=$option '
FNR==4 { sum+=$0; avg=sum/++count;
max=(max>=$0?max:$0);
if (count==1) min=$0;
}
count>1 { min=(min<=$0?min:$0);
}
END{ stats=min","max","avg","min"\n"max"\n"avg;
split(stats, to_print,",");
print to_print[select];
}' {} +
# read 'year' & 'option' from user
# or you can pass as argument to the command $1<-->$year & $2<-->$option
find /path/to/$year -type f -exec \
# find all files under "/path/to/$year". $year will be substitute with the value
# of 'year' variable read from user-input or replace it with '$1' as first argument to the command
awk -v select=$option '
# read the value of shell 'option' variable into an awk 'select' variable
# replace with '$2' as argument to the command
FNR==4 { sum+=$0; avg=sum/++count;
# if it's 4th line of each input file, sum-up the value into 'sum' variable
# and calculate the 'avg' too when 'count' will increment once each 4th record in a file is read
max=(max>=$0?max:$0);
# its a Ternary operator (condition?if-true:if-false) and finding maximum value
if (count==1) min=$0;
# keep the first file's 4th line's value as minimum. you could use `NR==4` instead
}
count>1 { min=(min<=$0?min:$0);
# same as max, update the 'min' if value in current file is smaller than 'min' in previous file
}
END{ stats=min","max","avg","min"\n"max"\n"avg;
# saving all variables' value into single variable with comma separated. I used <min"\n"max"\n"avg> as
# fourth element which we will use it as "All" option that each separated with newlines.
split(stats, to_print, ",");
# building an array called 'to_print' from 'stats' variable above with comma separator to distinguish
# the elements from each other.
print to_print[select];
# this will print the element which user-input as an option.
# if input 1: will print 'min'
# if input 2: will print 'max'
# if input 3: will print 'avg'
# if input 4: will print 'min' \n 'max' '\n' avg
}' {} +
Спасибо за все ответы вот то, с чем я закончил:
#!/bin/bash
### Returns the minimum value by sorting the population file's data and displaying the top line.
function min () {
echo "Minimum Population: "$(sort -n populations | head -1)
}
### Returns the maximum value by sorting the population file's data and displaying the bottom line.
function max () {
echo "Maximum Population: "$(sort -n populations | tail -1)
}
### A function to return the average number of population.
function avg () {
count=0
sum=0
while read line ; do
num=`echo ${line#* }`
sum=`expr $sum + $num`
count=`expr $count + 1`
done < populations
avg=`expr $sum / $count`
echo "Average Population: "$avg
}
### Advises what the script does and asks for an imput of a year.
echo "
######################
# Population adviser #
######################
Please enter the year: "
read s1
### If statement checking the year entered is available, if not then the user is informed of invalid selection and program terminates.
if [[ $s1 -ge 2000 && $s1 -le 2019 && $s1 -ne 2009 ]] ; then
continue 2>/dev/null
else
echo "The year you entered is not valid, program terminating"
exit
fi
### Prompts user for input
echo "
Enter an option:
1. Minimum
2. Maximum
3. Average
4. All
-----(minimum) (maximum) (average) (all)-----
"
read s2
### Loops through all files within the given directory path and appends the population of each file to the population list
for file in $(find ~/filesToSort/$s1 -type f) ; do
tail -1 $file >> populations
done
### If statement to validate user input and then use the function(s) required
if [ "$s2" == "minimum" ] ; then
min
elif [ "$s2" == "maximum" ] ; then
max
elif [ "$s2" == "average" ] ; then
avg
elif [ "$s2" == "all" ] ; then
min
max
avg
else
echo "The option you chose is invalid, program terminating"
rm populations
exit
fi
### Removes "populations" file upon completion
rm populations
Когда выбор опции (1-4) вместо того, чтобы поместить нумерует слово, должен быть вставлен вместо этого, который я ненавижу, но попросился сделать это этот путь.