Tim*_*Tim 17 awk text-processing regular-expression
假设文件中有一些文本:
(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 23" "#2")
("Exercises 31" "#30")
("Notes and References 42" "#34"))
)
Run Code Online (Sandbox Code Playgroud)
"如果有一个,我想给每个数字加上 11,然后在每一行中加上一个,即
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 34" "#13")
("Exercises 42" "#41")
("Notes and References 53" "#45"))
)
Run Code Online (Sandbox Code Playgroud)
这是我使用 GNU AWK 和正则表达式的解决方案:
awk -F'#' 'NF>1{gsub(/"(\d+)\""/, "\1+11\"")}'
Run Code Online (Sandbox Code Playgroud)
即,我想替换(\d+)\"为 \1+10\",\1代表 的组在哪里(\d+)。但它不起作用。我怎样才能让它工作?
如果 gawk 不是最好的解决方案,还有什么可以使用的?
Ken*_*ent 13
试试这个(需要目瞪口呆)。
awk '{a=gensub(/.*#([0-9]+)(\").*/,"\\1","g",$0);if(a~/[0-9]+/) {gsub(/[0-9]+\"/,a+11"\"",$0);}print $0}' YourFile
Run Code Online (Sandbox Code Playgroud)
用你的例子测试:
kent$ echo '(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 2" "#2")
("Exercises 30" "#30")
("Notes and References 34" "#34"))
)
'|awk '{a=gensub(/.*#([0-9]+)(\").*/,"\\1","g",$0);if(a~/[0-9]+/) {gsub(/[0-9]+\"/,a+11"\"",$0);}print $0}'
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 13" "#13")
("Exercises 41" "#41")
("Notes and References 45" "#45"))
)
Run Code Online (Sandbox Code Playgroud)
请注意,如果两个数字(例如 1" 和 "#1")不同,则此命令将不起作用。或者与此模式同一行中有更多数字(例如 23" ...32"..."# 123") 在一行中。
更新
由于@Tim (OP) 表示"同一行中后跟的数字可能不同,因此我对之前的解决方案进行了一些更改,并使其适用于您的新示例。
顺便说一句,从这个例子中我觉得它可能是一个内容结构表,所以我看不出这两个数字有什么不同。第一个是打印的页码,第二个带# 是页面索引。我对吗?
无论如何,您最了解自己的要求。现在新的解决方案,仍然使用 gawk(我将命令分成几行以使其更易于阅读):
awk 'BEGIN{FS=OFS="\" \"#"}{if(NF<2){print;next;}
a=gensub(/.* ([0-9]+)$/,"\\1","g",$1);
b=gensub(/([0-9]+)\"/,"\\1","g",$2);
gsub(/[0-9]+$/,a+11,$1);
gsub(/^[0-9]+/,b+11,$2);
print $1,$2
}' yourFile
Run Code Online (Sandbox Code Playgroud)
用你的新例子测试:
kent$ echo '(bookmarks
("Chapter 1 Introduction 1" "#1"
("1.1 Problem Statement and Basic Definitions 23" "#2")
("Exercises 31" "#30")
("Notes and References 42" "#34"))
)
'|awk 'BEGIN{FS=OFS="\" \"#"}{if(NF<2){print;next;}
a=gensub(/.* ([0-9]+)$/,"\\1","g",$1);
b=gensub(/([0-9]+)\"/,"\\1","g",$2);
gsub(/[0-9]+$/,a+11,$1);
gsub(/^[0-9]+/,b+11,$2);
print $1,$2
}'
(bookmarks
("Chapter 1 Introduction 12" "#12"
("1.1 Problem Statement and Basic Definitions 34" "#13")
("Exercises 42" "#41")
("Notes and References 53" "#45"))
)
Run Code Online (Sandbox Code Playgroud)
EDIT2基于@Tim 的评论
(1) FS=OFS="\" \"#" 是否表示输入输出中的字段分隔符都是双引号、空格、双引号和#?为什么要指定双引号两次?
您适合输入和输出部分的分隔符。它将分隔符定义为:
" "#
Run Code Online (Sandbox Code Playgroud)
有两个双引号,因为更容易捕获您想要的两个数字(基于您的示例输入)。
(2)在/.*([0-9]+)$/中,$是否表示字符串结束?
确切地!
(3) 在gensub()的第三个参数中,“g”和“G”有什么区别?G 和 g 之间没有区别。看一下这个:
gensub(regexp, replacement, how [, target]) #
Search the target string target for matches of the regular expression regexp.
If "how" is a string beginning with ‘g’ or ‘G’ (short for “global”), then
replace all matches of regexp with replacement.
Run Code Online (Sandbox Code Playgroud)
这是来自http://www.gnu.org/s/gawk/manual/html_node/String-Functions.html。您可以阅读以获取 gensub 的详细用法。
与几乎所有提供正则表达式替换的工具不同,awk 不允许反向引用,例如\1在替换文本中。如果您使用该match函数,GNU Awk 可以访问匹配的组,但不能使用~或sub或gsub。
另请注意,即使\1支持,您的代码段也会附加 string +11,而不是执行数值计算。此外,您的正则表达式不太正确,您正在匹配诸如"42""和 not 之类的东西"#42"。
这是一个 awk 解决方案(警告,未经测试)。它每行只执行一次替换。
awk '
match($0, /"#[0-9]+"/) {
n = substr($0, RSTART+2, RLENGTH-3) + 11;
$0 = substr($0, 1, RSTART+1) n substr($0, RSTART+RLENGTH-1)
}
1 {print}'
Run Code Online (Sandbox Code Playgroud)
在 Perl 中会更简单。
perl -pe 's/(?<="#)[0-9]+(?=")/$1+11/e'
Run Code Online (Sandbox Code Playgroud)
awk可以做到,但它不是直接的,即使使用反向引用也是如此。
GNU awk具有(部分)反向引用,形式为gensub。
的实例123"被临时包裹 \x01并\x02标记为未修改(对于sub().co
或者,您可以在循环中逐步更改候选对象,在这种情况下,不需要反向引用和“括号”;但需要跟踪字符索引。
awk '{$0=gensub(/([0-9]+)\"/, "\x01\\1\"\x02", "g", $0 )
while ( match($0, /\x01[0-9]+\"\x02/) ) {
temp=substr( $0, RSTART, RLENGTH )
numb=substr( temp, 2, RLENGTH-3 ) + 11
sub( /\x01[0-9]+\"\x02/, numb "\"" )
} print }'
Run Code Online (Sandbox Code Playgroud)
这是另一种方法,使用 gensuband 数组split和\x01作为字段分隔符(用于split)。 \x02 将数组元素标记为算术加法的候选。
awk 'BEGIN{ ORS="" } {
$0=gensub(/([0-9]+)\"/, "\x01\x02\\1\x01\"", "g", $0 )
split( $0, a, "\x01" )
for (i=0; i<length(a); i++) {
if( substr(a[i],1,1)=="\x02" ) { a[i]=substr(a[i],2) + 11 }
print a[i]
} print "\n" }'
Run Code Online (Sandbox Code Playgroud)