我有很长的清单如下:
D6N T69TN K70R M184V T215FEG
结果必须是这样的:
D006N T069TN K070R M184V T215FEG
我是 bash 的新手,我尝试了将其拆分为列并重新格式化的方法。然而,第 2 列和第 3 列假定列的位置和长度不固定。感谢您的任何帮助!
您可以使用 awk 执行此操作,使用内置match
函数:
awk 'match($0, /[0-9]+/) { printf "%s%03d%s\n",
substr($0, 0, RSTART - 1), substr($0, RSTART, RLENGTH), substr($0, RSTART + RLENGTH) }' file
成功时match
,设置两个变量RSTART
和RLENGTH
,可用于提取子串。中间的子字符串使用%03d
, 格式化,用前导零填充。
不会打印任何与模式不匹配的行。
使用 perl 的另一个选项:
perl -pe 's/\d{1,3}/sprintf("%03d", $&)/eg' file
这将用零填充的三位数替换任何一到三位数的序列。在这个版本中,所有行都被打印出来。
的正则表达式稍微长一点sed
,但这里是Perl
:
echo "D6N" | perl -pe 's/(\D)(\d)(\D)/${1}0$2$3/g; s/(\D)(\d\d)(\D)/${1}0$2$3/g;'
它将用被非数字包围的零填充 1 位和 2 位数字。它通过一个简单的技巧做到这一点:用一个零填充 1 位数字(因此 1 位数字变成 2 位数字),然后用另一个 0 填充 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", $1, $2, $3 or print'
另一个sed
基于实现:
$ cat testfile
D6N
T69TN
K70R
M184V
T215FEG
$ sed -r 's/[0-9]+/00&/g; s/0?0?([0-9]{3})/\1/g' testfile
D006N
T069TN
K070R
M184V
T215FEG
逻辑:无条件为数字添加 2 个零并删除前导零,直到数字长为 3 位。
这个 gnu awk 也可以完成工作:
awk -v RS='[0-9]+' 'RT{print $0 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
数字组短于三位数,则用零填充。