Syn*_*r0r 4 unix shell awk sed
编辑:我事先不知道我的数字将在哪个"列",我想要一个单行.显然sed不做算术,所以也许基于awk的单线程解决方案?
我有一个字符串:(注意间距)
eh oh 37
Run Code Online (Sandbox Code Playgroud)
我希望它成为:
eh oh 36
Run Code Online (Sandbox Code Playgroud)
(所以我想保持间距)
使用awk我找不到怎么做,到目前为止我有:
echo "eh oh 37" | awk '$3>=0&&$3<=99 {$3--} {print}'
Run Code Online (Sandbox Code Playgroud)
但这给了:
eh oh 36
Run Code Online (Sandbox Code Playgroud)
(丢失的间距字符,因为字段分隔符是'')
有没有办法问awk类似"使用与输入完全相同的字段分隔符打印输出"?
然后我尝试了其他东西,使用awk的sub(..,..)方法:
' sub(/[0-9][0-9]/, ...) {print}'
Run Code Online (Sandbox Code Playgroud)
但是还没有雪茄:我不知道如何引用正则表达式并在第二个参数中对其进行算术运算(我现在用'...'留下).
然后我尝试用sed,但在此之后卡住了:
echo "eh oh 37" | sed -e 's/\([0-9][0-9]\)/.../'
Run Code Online (Sandbox Code Playgroud)
我可以使用对匹配数字的引用从sed进行算术运算,并且输出不会修改间距字符数吗?
请注意,它与我关于Emacs的问题以及如何将其应用于某个(大)Emacs区域(使用Emacs的shell-command-on-region的替换区域)有关,但这不是一个相同的问题:这个问题具体是关于如何使用awk/sed/etc时"保留空格".
这是ghostdog74的答案的变体,不需要将数字锚定在字符串的末尾.这是通过使用match而不是依赖于处于特定位置的数字来实现的.
这将替换第一个数字,其值减去1:
$ echo "eh oh 37 aaa 22 bb" | awk '{n = substr($0, match($0, /[0-9]+/), RLENGTH) - 1; sub(/[0-9]+/, n); print }'
eh oh 36 aaa 22 bb
Run Code Online (Sandbox Code Playgroud)
使用gsub那里而不是sub用"36"替换"37"和"22".如果线路上只有一个号码,那么您使用哪个号码无关紧要.但是,通过这种方式,它将处理带有尾随空格的数字以及可能存在的其他非数字字符(在某些空格之后).
如果你有gawk,你可以gensub像这样用来挑选字符串中的任意数字(只需设置值which):
$ echo "eh oh 37 aaa 22 bb 19" |
awk -v which=2 'BEGIN { regex = "([0-9]+)\\>[^0-9]*";
for (i = 1; i < which; i++) {regex = regex"([0-9]+)\\>[^0-9]*"}}
{ match($0, regex, a);
n = a[which] - 1; # do the math
print gensub(/[0-9]+/, n, which) }'
eh oh 37 aaa 21 bb 19
Run Code Online (Sandbox Code Playgroud)
第二个(which=2)数字从22变为21.并且嵌入的空间被保留.
它在多行上分解,以便于阅读,但它是复制/可管理的.