我有一个文件,其列包含简单的算术方程,我想将其合并到算术结果中。
输入样本(制表符分隔的列):
+104-1+12 6 +3
我想计算每列内的算术和。如果一列不包含算术符号,我会将其视为+
在该项目之前包含 a 。尽管+
如果列以无符号开头,则通过 sed 添加符号会很容易(sed -E 's/(\t)([0-9]*)/\1\t+\2/g'
可以工作,假设行从不以数字开头,如示例中所示)
我期望的输出如下:
115 6 3
我怎样才能在unix中实现这一点? awk/sed 解决方案是首选。
答案1
你可以使用perl
:
perl -pe 's/[\d+-]+/eval$&/ge' your-file
甚至:
perl -pe 's/[\d+-]+/$&/gee' 你的文件(感谢 Rakesh)
zsh
与:相同
set -o extendedglob # for the ## operator (same as ERE +)
while IFS= read -r line; do
printf '%s\n' ${line//(#m)[0-9+-]##/$((MATCH))}
done < your-file
或者:
zmodload zsh/mapfile
set -o extendedglob
printf %s ${mapfile[your-file]//(#m)[0-9+-]##/$((MATCH))}
在这四个中,我们正在寻找数字-
和+
字符序列,并将它们传递给解释器的算术处理器(eval
in perl
(或ee
导致替换扩展被评估为perl
代码的标志),$((...))
in zsh
)。
在传递给解释器之前我们不会验证表达式,因此它可能会导致失败(例如在像-+-
or之类的序列上3++
),但至少,因为我们只考虑数字和-
/+
字符,所以它不会造成比报告错误消息并中止命令。
答案2
我不会重复添加“sed”回答;我也没有在 awk 中找到方法,但这里有一个 bash 版本:
while IFS= read -r line
do
set -f; set -- $line
for e in "$@"
do
printf "%d " "$(( e ))"
done
echo
done < input
答案3
sed -E 's/(\t)([0-9])/\1+\2/g' data.file |
while IFS= read -r l; do
set -f; IFS=$'\t'
printf '0%s\n' $l | bc -l | paste -s -
done
sed -e 's/\t\([0-9]\)/\t+\1/' data.file |
while IFS= read -r l; do
set -f; IFS=$'\t'
printf '0%s\n' $l | bc -c |
sed -ne '
$!{
y/:@irKW/ /
s/[^ 0-9]/ & /g
s/[ ][ ]*/ /g;s/^[ ]*//;s/[ ]*$/p/p
}
' | dc | paste -s -
done
在这里,我们生成postfix
数学表达式的表示,并在将其传递到后缀计算器之前dc
,我们从命令的输出中清除非数学信息bc -c
。
结果
115 6 3
答案4
这是一个全 awk 解决方案,利用 awk 将数字的字符串表示形式编组为数字表示形式的能力,而不使用外部可执行文件:
awk -F"\t" \
'BEGIN { OFS="\t" }
{ gsub(/-/,"|-")
gsub(/\+/,"|")
for(i=1; i<=NF; i++) { ## iterate over columns
num_parts=split($i,parts,"|")
for(j=1; j<=num_parts; j++) ## iterate over arithmetic expression parts
sums[i] += parts[j]+0 ## Adding zero marshals the string into a numeric
}}
END{
for(i=1; i<=NF; i++) {
if(i>1) printf OFS
printf sums[i]
}
print "" }' file