我得到了这个文本文件,调用stock.txt,文本文件的内容是:
pepsi;drinks;3
fries;snacks;6
apple;fruits;9
baron;drinks;7
orange;fruits;2
chips;snacks;8
我需要使用bash-script来输出这个输出:
Total amount for drinks: 10
Total amount for snacks: 14
Total amount for fruits: 11
Total of everything: 35
我的直觉告诉我,我需要使用sed,group,grep和其他东西 我应该从哪里开始?
答案 0 :(得分:1)
Pure Bash。一个很好的关联数组应用程序:
declare -A category # associative array
IFS=';'
while read name cate price ; do
((category[$cate]+=price))
done < stock.txt
sum=0
for cate in ${!category[@]}; do # loop over the indices
printf "Total amount of %s: %d\n" $cate ${category[$cate]}
((sum+=${category[$cate]}))
done
printf "Total amount of everything: %d\n" $sum
答案 1 :(得分:0)
我会将练习分解为步骤
步骤1:一次读取一行文件
while read -r line
do
# do something with $line
done
第2步:模式匹配(饮料,零食,水果)并做一些简单的算术。这一步要求你对每一行进行标记,然后我会留下练习让你弄明白。
if [[ "$line" =~ "drinks" ]]
then
echo "matched drinks"
.
.
.
fi
答案 2 :(得分:0)
这里有关于在bash中处理逗号分隔文件的简短描述:
http://www.cyberciti.biz/faq/unix-linux-bash-read-comma-separated-cvsfile/
你可以做类似的事情。只需将IFS
从逗号更改为分号。
哦,是的,以及学习bash的一般提示:man
是你的朋友。使用此命令查看所有(或大多数)命令和实用程序的手册页。
示例:man read
显示read命令的手册页。在大多数系统上,它将在less
中打开,所以你应该按q退出手册(可能很有趣,但我花了一些时间才弄明白)
答案 3 :(得分:0)
执行此操作的简单方法是使用哈希表,该表由bash 4.x直接支持,当然可以在awk和perl中找到。如果您没有哈希表,则需要循环两次:一次收集第二列的唯一值,一次为总计。
有很多方法可以做到这一点。这是一个有趣的,不使用awk,sed或perl。我在这里使用的唯一外部工具是cut,sort和uniq。你甚至可以用更多的努力来取代cut
。实际上,使用grep(grep $kind stock.txt
)可以更容易地编写第5-9行,但我避免使用它来展示bash的强大功能。
for kind in $(cut -d\; -f 2 stock.txt | sort | uniq) ; do
total=0
while read d ; do
total=$(( total+d ))
done < <(
while read line ; do
[[ $line =~ $kind ]] && echo $line
done < stock.txt | cut -d\; -f3
)
echo "Total amount for $kind: $total"
done
我们在这里失去了原始输出的严格排序。你的练习可能是找到一种不这样做的方法。
讨论:
第一行描述了一个使用cut
的简单管道的子shell。我们从stock.txt
文件中读取第三个字段,其中的字段由;
描述,在此处写\;
,因此shell不会对其进行解释。结果是来自stock.txt
的以换行符分隔的值列表。这是通过sort
传输,然后是uniq
。这将执行我们的“分组”步骤,因为管道将从第二列输出字母的项目列表,但无论输入文件中出现多少次,都只会列出一次。
第一行还有一个典型的for
循环:对于子shell产生的每个项,我们循环一次,将项的值存储在变量kind
中。这是分组步骤的另一半,确保每个“总计”输出行出现一次。
在第二行total
初始化为零,以便每当启动新组时它始终重置。
第三行开始'totaling'循环,其中对于当前kind
,我们找到其出现的总和。这里我们声明我们将在循环的每次迭代中从stdin中读取变量d
。
在第四行实际发生总计:使用shell arithmatic我们将d
中的值添加到total
中的值。
第五行结束while循环,然后描述其输入。我们通过<
使用shell输入重定向来指定循环的输入,从而指定read
命令的输入来自文件。然后我们使用process substitution来指定文件实际上是命令的结果。
在第六行,将开始为while-read循环提供的命令。它本身是另一个读取循环,这次读入变量line
。在第七行,测试通过conditional construct进行。在这里,我们对[[
运算符使用=~
,这是一个模式匹配运算符。我们正在测试$line
是否与我们当前的$kind
匹配。
在第八行,我们结束内部while-read循环,并指定它的输入来自stock.txt
文件,然后我们管道整个循环的输出,现在只是所有匹配的行{{ 1}},到$kind
并指示它只显示第三个字段,即数字字段。在第9行,我们然后结束进程替换命令,其输出是来自cut
指定的组中的行的数字的换行符。
鉴于现在知道总数并知道种类,将结果打印到屏幕上是一件简单的事情。
答案 4 :(得分:0)
以下答案是OP的。由于它是在问题本身编辑而OP还没有回来6年,我正在编辑问题的答案并在此处将其发布为维基。
我的回答是,为了得到总价,我用这个:
...
PRICE=0
IFS=";" # new field separator, the end of line
while read name cate price
do
let PRICE=PRICE+$price
done < stock.txt
echo $PRICE
当我回声时,它:35,这是正确的。现在我将继续使用awk来获取子类别结果。
整体解决方案:
谢谢你们,我设法自己做。这是我的代码:
#!/bin/bash
INPUT=stock.txt
PRICE=0
DRINKS=0
SNACKS=0
FRUITS=0
old_IFS=$IFS # save the field separator
IFS=";" # new field separator, the end of line
while read name cate price
do
if [ $cate = "drinks" ]; then
let DRINKS=DRINKS+$price
fi
if [ $cate = "snacks" ]; then
let SNACKS=SNACKS+$price
fi
if [ $cate = "fruits" ]; then
let FRUITS=FRUITS+$price
fi
# Total
let PRICE=PRICE+$price
done < $INPUT
echo -e "Drinks: " $DRINKS
echo -e "Snacks: " $SNACKS
echo -e "Fruits: " $FRUITS
echo -e "Price " $PRICE
IFS=$old_IFS