我正在尝试编写一个脚本,将行中的每个数字减少“1”,但我得到的都是“0”:
awk '{a=gensub(/([0-9]+)/,"\\1","g",$0);
if(a~/[0-9]+/) {gsub(/[0-9]+/,a-1,$0);}
print $0}'
例如字符串:
1,2,3,4-7
应该导致:
0,1,2,3-6
相反我得到:
0,0,0,0-0
答案1
awk
替代能力相当有限。至少gawk
可以gensub()
在替换中包含匹配部分的一部分,但不能对这些部分进行任何操作。
可以使用awk
,但您需要采取不同的方法:
awk '{
text = $0
$0 = ""
while (match(text, /[0-9]+/)) {
$0 = $0 substr(text, 1, RSTART-1) \
(substr(text, RSTART, RLENGTH) - 1)
text = substr(text, RSTART+RLENGTH)
}
$0 = $0 text
print}'
或者使用 GNUawk
作为 @jofel 方法的变体:
gawk -v 'RS=[0-9]+' '{printf "%s", $0 (RT==""?"":RT-1)}'
或者
gawk -v 'RS=[^0-9]+' '{printf "%s",($0==""?"":$0 - 1)RT}'
然而,这里更容易使用perl
:
perl -pe 's/\d+/$&-1/ge'
perl
可以使用捕获组(如$1
、$2
... 以及$&
整个匹配部分),并且使用该e
标志可以使用这些捕获组运行任意perl
表达式。
答案2
您的 awk 解决方案仅匹配第一个数字,然后将所有其他数字替换为第一个数字减一。
就您的程序而言,您可以与 GNU 的 awk ( gawk
) 一起使用:
awk 'BEGIN { RS="[^0-9]"; OFS=""; ORS=""; } {a=gensub(/([0-9]+)/,"\\1","g",$0);if(a~/[0-9]+/) {gsub(/[0-9]+/,(a-1),$0);} print $0,RT}'
但这可以简化为
awk 'BEGIN { RS="[^0-9]"; OFS=""; ORS=""; } {if(length($0)) {print ($0-1);}print RT}'
或者加上评论:
awk '
BEGIN {
RS="[^0-9]"; # set the record separator to a regexp matching all
OFS=""; # no output field separator
ORS=""; # no output record separator (we use RT)
}
{
if(length($0)) { # if number found
print ($0-1); # print it decreased by one
}
print RT # output current field separator (=non-digit).
}'
每个非数字用作记录分隔符并与打印语句一起重新插入。
这里有一个Python的解决方案:
python -c 'import re,sys; print re.compile("\d+").sub(lambda i: str(int(i.group())-1),sys.stdin.read()),'
答案3
使用通用(非 GNU)“awk”:
我建议将输入行分成值和分隔符的数组。然后,修改值并用分隔符重新组合它们:
awk '{
split("0," $0 ",0", numbers, "[^0-9]+"); # make sure each line starts and ends with a number
split($0, sep, "[0-9]+");
res = ""; j = 1;
for (i = 2; i < length(numbers); i ++) { # ignore the dummy numbers added above
res = res sep[j++] (numbers[i] - 1);
}
print res;
}' file