我认为没有人问过这个问题:我一直在研究一个小的bash脚本来提取一些数据,而我的想法是将今天的数据与昨天的数据进行比较以找到用法。
Today
的文件如下:
150
100
50
Yesterday
的文件如下:
145
99
20
当两个文件的内容存储在同名变量中并打印出结果时,我正在寻找能够从Today
中减去{1}}的第1行的内容。
Yesterday
从包含文件的命令行开始,它可以工作,但是从脚本中读取包含数据的变量的内容,它打印出第一个Total
5
1
30
然后找不到文件:
cat
有什么想法吗?
答案 0 :(得分:2)
更新:此答案基于$Today
和$Yesterday
引用文件的假设,如OP的命令行示例;事实证明,这些变量实际上包含数据。
但是,这个答案仍然正确地解释了附加问题:单引号 {{1}中对 shell 变量的错误引用程序。
它还使用awk
和paste
提供了一个更简单的(事实证明:更快 - 感谢,JS웃)替代命令。
awk
不会在您的$Yesterday
计划中展开,因为awk
程序整体都包含在单引号中,这意味着任何其中的shell变量引用未被扩展。
要将shell变量值传递到awk
程序,请使用awk
选项:
-v varName=varValue
cat "$Today" | awk -v Yesterday="$Yesterday" '{n=$0; getline < Yesterday; print" " n-$0}'
定义了 -v Yesterday="$Yesterday"
变量awk
,然后可以在Yesterday
内使用{没有引号,没有$
前缀) {1}}程序。或者,您可以将awk
与paste
合并:
awk
paste file1 file2 | awk '{ print $1 - $2 }'
合并paste
和file1
中的相应行,file2
可以轻松解析并执行算术运算。答案 1 :(得分:2)
我看到此标记为awk,但您也可以使用bc
。此处Today
和Yesterday
是包含您的数据的文件名:
$ echo Total; paste -d- Today Yesterday | bc
Total
5
1
30
$
<强>更新强>
因为我们现在知道$Today
和$Yesterday
是包含换行符分隔列表的bash变量,所以这是实现所需内容的另一种方法,它只使用bash内置功能。如果您拥有大量数据集,则无需生成外部流程(awk
,paste
,bc
等)将提高性能。
# Create array versions of the variables
TodayArr=($Today)
YesterdayArr=($Yesterday)
# Loop over the list of indices in the Today array and do bash-based arithmetic
for i in ${!TodayArr[@]}; do
echo $(( ${TodayArr[$i]} - ${YesterdayArr[$i]} ))
done
上面的警告是bash内置算术$(( ))
只处理整数(通常是64位签名)。如果您的值包含小数,那么您必须再次使用bc
或其中一个awk解决方案。 for
循环看起来像这样:
# Loop over the list of indices in the Today array and do bc-based arithmetic
for i in ${!TodayArr[@]}; do
echo "${TodayArr[$i]} - ${YesterdayArr[$i]}"
done | bc
答案 2 :(得分:1)
这是awk
的一种方式:
$ awk 'NR==FNR{a[NR]=$1;next}{print $1-a[FNR]}' yesterdaysfile todaysfile
5
1
30
NR
索引的数组。 FNR
减去带有数组的todays文件。 NR
和FNR
是存储行号的变量,当读取新文件时,差异为FNR
会重置为1
。 NR
没有。因此明智地使用它们可以获得您寻求的结果。
$ seq 10000000 > f1
$ seq 10000000 > f2
$ time paste f1 f2 | awk '{ print $1 - $2 }' >/dev/null
real 0m12.894s
user 0m13.519s
sys 0m0.229s
$ time cat f1 | awk '{n=$0; getline < "f2"; print" " n-$0}' >/dev/null
real 0m14.615s
user 0m14.428s
sys 0m0.154s
$ time awk 'NR==FNR{a[NR]=$1;next}{print $1-a[FNR]}' f1 f2 >/dev/null
real 0m18.631s
user 0m17.459s
sys 0m1.094s
$ time paste -d- f1 f2 | bc >/dev/null
real 0m37.221s
user 0m32.027s
sys 0m6.535s
答案 3 :(得分:1)
鉴于$Today
和$Yesterday
包含数据或者文件内容,而不是文件名,如果您想使用您的代码,能做到:
$ awk '{n=$1; getline < ARGV[2]; print" " n-$1}' <(echo "$Today") <(echo "$Yesterday")
5
1
30
awk: warning: close of fd 62 (`/dev/fd/62') failed (Bad file descriptor)
但正如您所看到的,您将收到 gawk 的警告。正如mklement0的评论中所述,您可以通过在结尾处添加2>/dev/null
来安全地忽略此警告,但只有在您确定它可以按预期工作之后。否则,您可能会遗漏一些重要的调试信息。
更惯用的方式是:
$ awk 'BEGIN { print "Total"}
NR == FNR { n[FNR] = $1;next}
NF { print n[FNR] - $1 }' <(echo "$Today") <(echo "$Yesterday")
Total
5
1
30