我有一个像这样的粘贴命令
paste -d , file1.csv file2.csv file3.csv
file2.csv 包含这样的数字
0.2
0.3339
0.111111
我希望 file2.csv 中的值具有 3 位小数,如下所示:
0.200
0.334
0.111
对于一个值,这是有效的:
printf "%.3f" "0.3339"
->0.334
但对于 file2.csv 中的多个值,这不起作用:
paste -d , file1.csv <(printf %s "%.3f" "$(< file2.csv)") file3.csv
也许有一个好的解决方案?
答案1
有一个名为 的 GNU 实用程序numfmt
,它是 GNU coreutils 工具集合的一部分,看起来在这里很有用。它允许您格式化数值,以下命令将file2.csv
使用printf
格式字符串%.3f
(“精度为三位小数的浮点值”)来格式化所有值。格式化的值将打印在标准输出上:
$ numfmt --format=%.3f <file2.csv
0.200
0.334
0.112
正如您所看到的,它默认使用“从零开始”舍入,但这可以通过以下方式更改--round=nearest
:
$ numfmt --format=%.3f --round=nearest <file2.csv
0.200
0.334
0.111
您可以paste
使用进程替换将其插入命令中,如下所示:
paste -d , file1.csv <( numfmt --format=%.3f --round=nearest <file2.csv ) file3.csv
如果您的文件是一个不“简单”的 CSV 文件,即它可能包含带引号的字段,那么您可能需要使用支持 CSV 的工具,例如磨坊主( mlr
) 处理数据。下面使用 Miller 表达式中的函数重新创建了numfmt
上面的第二个示例(该函数采用格式字符串):fmtnum()
put
printf
$ mlr --csv -N put '$1 = fmtnum($1, "%.3f")' file2.csv
0.200
0.334
0.111
和--csv
选项-N
使 Miller 将输入(并写入输出)作为无标头 CSV 读取。
答案2
你很接近;你只需要告诉printf
小数点右边的零填充:
$ cat 736678.txt
0.2
0.3339
0.111111
$ for value in $( cat 736678.txt ); do printf "%.3f\n" "$value"; done
0.200
0.334
0.111
格式字符串的%.3f
意思是“在该点右侧精确保留三位小数的浮点数”。
答案3
您可以使用它awk
来执行所有读取、格式化和粘贴操作:
LC_ALL=C awk '
{
getline f2 < "file2.csv"
getline f3 < "file3.csv"
printf "%s,%.3f,%s\n", $0, f2, f3
}' file1.csv
您将在输出中获得与其中一样多的行file1.csv
(0.000
如果文件2 的行数较少,则文件3 的行数为空字符串)。
请注意,当环境中存在变量时,某些 的实现awk
(包括 GNU)会在输入和输出时遵循区域设置的十进制基数字符。例如,在法语或德语语言环境中,小数基数字符代替,将被解释为不会被识别并被视为垃圾,并且您将得到输出,从而破坏 CSV 格式。awk
POSIXLY_CORRECT
,
.
1.2e5
1
.2e5
1,000
因此,LC_ALL=C
上面将区域设置固定为C
小数基数字符所在的位置.
。