bash 0 个子字符串上的填充数字
bash 0 pad numbers on substrings
我有一长串如下:
D6N
T69TN
K70R
M184V
T215FEG
结果一定是这样的:
D006N
T069TN
K070R
M184V
T215FEG
我是 bash 的新手,我尝试了基于将其拆分为多列并重新格式化的方法。然而,第二和第三假定列的位置和长度不固定。
感谢您的帮助!
sed
的正则表达式有点长,但这里是 Perl
:
echo "D6N" | perl -pe 's/(\D)(\d)(\D)/0/g; s/(\D)(\d\d)(\D)/0/g;'
它会用非数字包围的 1 位和 2 位数字填充零。它用一个简单的技巧来做到这一点:用一个零填充 1 位数字(因此 1 位数字变成 2 位数字),然后用另一个零填充 2 位数字。
AFAIK,对此没有简单的纯Bash 解决方案。所以,我更喜欢Perl,因为Perl的表达很简洁,而且Perl无处不在。
s='D6N
T69TN
K70R
M184V
T215FEG'
echo "$s" | perl -ne '/^(\D*)(\d{1,2})(\D*)$/m and printf "%s%03s%s", , , or print'
您可以使用 awk 执行此操作,使用内置的 match
函数:
awk 'match([=10=], /[0-9]+/) { printf "%s%03d%s\n",
substr([=10=], 0, RSTART - 1), substr([=10=], RSTART, RLENGTH), substr([=10=], RSTART + RLENGTH) }' file
当match
成功时,设置了两个变量RSTART
和RLENGTH
,可以用来提取子串。中间的子字符串使用 %03d
格式化,用前导零填充。
任何与模式不匹配的行都不会被打印。
另一个使用 perl 的选项:
perl -pe 's/\d{1,3}/sprintf("%03d", $&)/eg' file
这会将任何一到三位数字的序列替换为零填充的三位数字。在此版本中,打印所有行。
另一个基于 sed
的实现:
$ cat testfile
D6N
T69TN
K70R
M184V
T215FEG
$ sed -r 's/[0-9]+/00&/g; s/0?0?([0-9]{3})//g' testfile
D006N
T069TN
K070R
M184V
T215FEG
逻辑:无条件地在数字前加 2 个零并删除前导零,直到数字长为 3 位。
这个gnu awk也能搞定:
awk -v RS='[0-9]+' 'RT{print [=10=] sprintf("%03d", RT); next} 1' ORS= file
D006N
T069TN
K070R
M184V
T215FEG
使用 Bash 正则表达式:
#!/bin/bash
re='([[:alpha:]]*)([[:digit:]]*)([[:alpha:]]*)'
while IFS= read -r line; do
[[ $line =~ $re ]]
printf "%s%03d%s\n" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"
done < infile
这会将每一行与正则表达式匹配并捕获三组:字母、数字、字母。 printf
格式字符串确保数字组在短于三位数字时用零填充。
我有一长串如下:
D6N T69TN K70R M184V T215FEG
结果一定是这样的:
D006N T069TN K070R M184V T215FEG
我是 bash 的新手,我尝试了基于将其拆分为多列并重新格式化的方法。然而,第二和第三假定列的位置和长度不固定。 感谢您的帮助!
sed
的正则表达式有点长,但这里是 Perl
:
echo "D6N" | perl -pe 's/(\D)(\d)(\D)/0/g; s/(\D)(\d\d)(\D)/0/g;'
它会用非数字包围的 1 位和 2 位数字填充零。它用一个简单的技巧来做到这一点:用一个零填充 1 位数字(因此 1 位数字变成 2 位数字),然后用另一个零填充 2 位数字。
AFAIK,对此没有简单的纯Bash 解决方案。所以,我更喜欢Perl,因为Perl的表达很简洁,而且Perl无处不在。
s='D6N
T69TN
K70R
M184V
T215FEG'
echo "$s" | perl -ne '/^(\D*)(\d{1,2})(\D*)$/m and printf "%s%03s%s", , , or print'
您可以使用 awk 执行此操作,使用内置的 match
函数:
awk 'match([=10=], /[0-9]+/) { printf "%s%03d%s\n",
substr([=10=], 0, RSTART - 1), substr([=10=], RSTART, RLENGTH), substr([=10=], RSTART + RLENGTH) }' file
当match
成功时,设置了两个变量RSTART
和RLENGTH
,可以用来提取子串。中间的子字符串使用 %03d
格式化,用前导零填充。
任何与模式不匹配的行都不会被打印。
另一个使用 perl 的选项:
perl -pe 's/\d{1,3}/sprintf("%03d", $&)/eg' file
这会将任何一到三位数字的序列替换为零填充的三位数字。在此版本中,打印所有行。
另一个基于 sed
的实现:
$ cat testfile
D6N
T69TN
K70R
M184V
T215FEG
$ sed -r 's/[0-9]+/00&/g; s/0?0?([0-9]{3})//g' testfile
D006N
T069TN
K070R
M184V
T215FEG
逻辑:无条件地在数字前加 2 个零并删除前导零,直到数字长为 3 位。
这个gnu awk也能搞定:
awk -v RS='[0-9]+' 'RT{print [=10=] sprintf("%03d", RT); next} 1' ORS= file
D006N
T069TN
K070R
M184V
T215FEG
使用 Bash 正则表达式:
#!/bin/bash
re='([[:alpha:]]*)([[:digit:]]*)([[:alpha:]]*)'
while IFS= read -r line; do
[[ $line =~ $re ]]
printf "%s%03d%s\n" "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"
done < infile
这会将每一行与正则表达式匹配并捕获三组:字母、数字、字母。 printf
格式字符串确保数字组在短于三位数字时用零填充。