使特定列中的数字在 BASH 中具有相同的长度
Make Numbers in a specific column to have the same Length in BASH
我需要第 4 列的所有数字都有 4 个字符
输入
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.5 0 0 1
期望的输出
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.50 0 0 1
这不是您想要的,但提供了一致的宽度:
awk '{=sprintf("%06.2f", )}1' input
产生:
AGAP4 2061 0.534207 917.00 0 0 1
AGAP5 2061 0.536148 101.50 0 0 8
AGBL1 3201 0.514214 917.90 0 0 2
AGBL2 2709 0.444814 012.50 0 0 1
它非常不灵活,但可以解决您的具体问题:
awk 'length() == 4 { = "0" }1' file
它所做的只是在第 4 个字段的末尾添加一个 0,如果它有 4 个字符长。
如果要求更复杂,例如长度可能相差超过一位数,那么您应该更新您的问题以显示一些不同的输入。
在 bash(或 POSIX shell)中,您的主要内置格式化工具是 printf
。您可以读取每行的前 4 列和一些虚拟变量中的其余部分,然后使用 printf
打印它们,根据需要将每列格式化为特定宽度:
#!/bin/bash
while read -r c1 c2 c3 c4 stuff; do
printf "%5s %4s %8s %5s %s\n" $c1 $c2 $c3 $c4 "$stuff"
done < ""
exit 0
输入
$ cat dat/agap.txt
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.5 0 0 1
输出
$ bash fmtagap.sh dat/agap.txt
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.5 0 0 1
bash 中的 printf
采用与 C 中相同的格式字符串和格式说明符。您可以阅读 man 3 printf
中的格式化可以做的所有事情。另外bash加了几个,比如printf -v varname "fmt string"
格式化结果保存在varname
.
格式字符串的一个限制是填充。虽然您可以 0
在左侧填充,但不能 0
在右侧填充数字。无论您使用 %s
字符串转换还是 %5.1f
浮点转换,您都受限于左填充和字段宽度规范。
当然,您可以在打印前检查每个变量的长度,然后 0
以这种方式在右侧填充,但这就是您开始询问是否可以使用外部 shell 实用程序为我做这些......但是,为了完整性:
#!/bin/bash
while read -r c1 c2 c3 c4 stuff; do
while [ ${#c4} -lt 5 ]; do
c4="${c4}0"
done
printf "%s %s %s %s %s\n" $c1 $c2 $c3 $c4 "$stuff"
done < ""
exit 0
输出
$ bash fmtagap.sh dat/agap.txt
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.50 0 0 1
类似于@William 的 awk 解决方案的 Perl 解决方案:
perl -lane '$F[3] = sprintf("%06.2f", $F[3]); print join " ",@F' input
-a
将每一行自动拆分为 @F
数组
输出:
AGAP4 2061 0.534207 917.00 0 0 1
AGAP5 2061 0.536148 101.50 0 0 8
AGBL1 3201 0.514214 917.90 0 0 2
AGBL2 2709 0.444814 012.50 0 0 1
使用 substr
生成您要求的格式:
perl -lane '$F[3] = substr(sprintf("%5.2f", $F[3]),0,5); print join " ",@F' input
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.50 0 0 1
我需要第 4 列的所有数字都有 4 个字符
输入
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.5 0 0 1
期望的输出
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.50 0 0 1
这不是您想要的,但提供了一致的宽度:
awk '{=sprintf("%06.2f", )}1' input
产生:
AGAP4 2061 0.534207 917.00 0 0 1
AGAP5 2061 0.536148 101.50 0 0 8
AGBL1 3201 0.514214 917.90 0 0 2
AGBL2 2709 0.444814 012.50 0 0 1
它非常不灵活,但可以解决您的具体问题:
awk 'length() == 4 { = "0" }1' file
它所做的只是在第 4 个字段的末尾添加一个 0,如果它有 4 个字符长。
如果要求更复杂,例如长度可能相差超过一位数,那么您应该更新您的问题以显示一些不同的输入。
在 bash(或 POSIX shell)中,您的主要内置格式化工具是 printf
。您可以读取每行的前 4 列和一些虚拟变量中的其余部分,然后使用 printf
打印它们,根据需要将每列格式化为特定宽度:
#!/bin/bash
while read -r c1 c2 c3 c4 stuff; do
printf "%5s %4s %8s %5s %s\n" $c1 $c2 $c3 $c4 "$stuff"
done < ""
exit 0
输入
$ cat dat/agap.txt
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.5 0 0 1
输出
$ bash fmtagap.sh dat/agap.txt
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.5 0 0 1
bash 中的 printf
采用与 C 中相同的格式字符串和格式说明符。您可以阅读 man 3 printf
中的格式化可以做的所有事情。另外bash加了几个,比如printf -v varname "fmt string"
格式化结果保存在varname
.
格式字符串的一个限制是填充。虽然您可以 0
在左侧填充,但不能 0
在右侧填充数字。无论您使用 %s
字符串转换还是 %5.1f
浮点转换,您都受限于左填充和字段宽度规范。
当然,您可以在打印前检查每个变量的长度,然后 0
以这种方式在右侧填充,但这就是您开始询问是否可以使用外部 shell 实用程序为我做这些......但是,为了完整性:
#!/bin/bash
while read -r c1 c2 c3 c4 stuff; do
while [ ${#c4} -lt 5 ]; do
c4="${c4}0"
done
printf "%s %s %s %s %s\n" $c1 $c2 $c3 $c4 "$stuff"
done < ""
exit 0
输出
$ bash fmtagap.sh dat/agap.txt
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.50 0 0 1
类似于@William 的 awk 解决方案的 Perl 解决方案:
perl -lane '$F[3] = sprintf("%06.2f", $F[3]); print join " ",@F' input
-a
将每一行自动拆分为 @F
数组
输出:
AGAP4 2061 0.534207 917.00 0 0 1
AGAP5 2061 0.536148 101.50 0 0 8
AGBL1 3201 0.514214 917.90 0 0 2
AGBL2 2709 0.444814 012.50 0 0 1
使用 substr
生成您要求的格式:
perl -lane '$F[3] = substr(sprintf("%5.2f", $F[3]),0,5); print join " ",@F' input
AGAP4 2061 0.534207 917.0 0 0 1
AGAP5 2061 0.536148 101.5 0 0 8
AGBL1 3201 0.514214 917.9 0 0 2
AGBL2 2709 0.444814 12.50 0 0 1