regex 如何使用sed、awk、grep等在跨多个文件的行的子字符串中用%20替换空格

btqmn9zl  于 2022-11-18  发布在  其他
关注(0)|答案(3)|浏览(165)

在最近的一次更新中,neomutt改变了它处理regexp匹配的方式,这打破了我配置中不多的URI。解决方案似乎是用%20替换URI中的空格。这不是什么大问题,除非我在多个配置文件中定义了很多虚拟邮箱。下面是一个配置的示例:

"Inbox"                 "notmuch://?query=folder:gmail/INBOX and tag:inbox" \
"Drafts"                "notmuch://?query=folder:gmail/Drafts" \
"Sent Mail"             "notmuch://?query=folder:gmail/Sent%20Mail" \
"Trash"                 "notmuch://?query=folder:gmail/Trash" \
"Today"                 "notmuch://?query=to:rsstinnett@gmail.com and date:today" \
"Yesterday"             "notmuch://?query=to:rsstinnett@gmail.com and date:yesterday" \
"This Week"             "notmuch://?query=to:rsstinnett@gmail.com and date:this_week" \
"Todo"                  "notmuch://?query=to:rsstinnett@gmail.com and tag:todo" \
"Starred"               "notmuch://?query=to:rsstinnett@gmail.com and tag:star" \
"Burning Man"           'notmuch://?query=folder:"gmail/Burning Man"' \
"  Work List"           'notmuch://?query=folder:"gmail/Burning Man/Work List"' \
"ATXHS"                 'notmuch://?query=folder:"gmail/ATX Hackerspace" and not tag:archive' \
"  ATXHS Members"       'notmuch://?query=folder:"gmail/ATX Hackerspace/Members" and not tag:archive' \
"  ATXHS Discuss"       'notmuch://?query=folder:"gmail/ATX Hackerspace/Discuss" and not tag:archive' \
"  ATXHS Announce"      'notmuch://?query=folder:"gmail/ATX Hackerspace/Announce" and not tag:archive'

使用sedawkgrep等,如何在不影响" and not tag:archive"的情况下将"gmail/ATX Hackerspace"更改为"gmail/ATX%20Hackerspace"
我知道还需要做其他的修改,但这是我唯一的一个被卡住的地方。基本上,我需要修改folder:"和下一个双引号之间的空格。我不知道这是否合理。

y0u0uwnf

y0u0uwnf1#

基于 I need to change the spacesbetween folder:" and the next instance of a double quote,下面的代码看起来是一个非常简单且可读性很强的解决方案:

sed -E ':a;s/(folder:"[^ "]*) /\1%20/;ta' yourinput

它基本上是一个while循环

  • 主体s/(folder:"[^ "]*) /\1%20/试图挑选在结束"之前跟随folder:"的第一空格(如果有的话),
    • 重复 * 循环的条件是尝试成功(即,替换确实完成); ta实际上t测试任何s命令是否在当前行上成功,并且如果是这种情况,则它将控制转移到标记为:a的行。
      更新

关于-E选项,我在GNU sed上测试了上面的答案。Ed Morton在OSX/BSD上测试了它,我提供的命令给出了不变的输出。
我认为原因可能是-E,或者可能是ta之后缺少了;,但根据Ed Morton的尝试,情况似乎并非如此。
根据以下摘自GNU sed手册页的内容,我最初认为该命令符合POSIX:

-E, -r, --regexp-extended

              use extended regular expressions in the script (for portability
              use POSIX -E).

此外,在this GNU page上,我看到
过去这是一个GNU扩展,但-E扩展后来被添加到POSIX标准(http://austingroupbugs.net/view.php?id=528)中,因此使用-E是为了可移植性。
然而,到目前为止,这就是GNUPOSIX的评价。
如果你去那个链接,问题历史部分的最后一行是日期 2020-03-18 15:37,并写着 Resolved =〉Applied,但我不知道这些网站与POSIX有什么关系。
底线是:我不知道-E是否与POSIX兼容。

u59ebvdq

u59ebvdq2#

在每个UNIX系统上的任何shell中使用任何awk:

$ awk 'match($0,/folder:"[^"]+"/) {
    tgt = substr($0,RSTART,RLENGTH)
    gsub(/ /,"%20",tgt)
    $0 = substr($0,1,RSTART-1) tgt substr($0,RSTART+RLENGTH)
 } 1' file
"Inbox"                 "notmuch://?query=folder:gmail/INBOX and tag:inbox" \
"Drafts"                "notmuch://?query=folder:gmail/Drafts" \
"Sent Mail"             "notmuch://?query=folder:gmail/Sent%20Mail" \
"Trash"                 "notmuch://?query=folder:gmail/Trash" \
"Today"                 "notmuch://?query=to:rsstinnett@gmail.com and date:today" \
"Yesterday"             "notmuch://?query=to:rsstinnett@gmail.com and date:yesterday" \
"This Week"             "notmuch://?query=to:rsstinnett@gmail.com and date:this_week" \
"Todo"                  "notmuch://?query=to:rsstinnett@gmail.com and tag:todo" \
"Starred"               "notmuch://?query=to:rsstinnett@gmail.com and tag:star" \
"Burning Man"           'notmuch://?query=folder:"gmail/Burning%20Man"' \
"  Work List"           'notmuch://?query=folder:"gmail/Burning%20Man/Work%20List"' \
"ATXHS"                 'notmuch://?query=folder:"gmail/ATX%20Hackerspace" and not tag:archive' \
"  ATXHS Members"       'notmuch://?query=folder:"gmail/ATX%20Hackerspace/Members" and not tag:archive' \
"  ATXHS Discuss"       'notmuch://?query=folder:"gmail/ATX%20Hackerspace/Discuss" and not tag:archive' \
"  ATXHS Announce"      'notmuch://?query=folder:"gmail/ATX%20Hackerspace/Announce" and not tag:archive'
jdzmm42g

jdzmm42g3#

为了好玩,这里有另一个只使用sed的解决方案。(在生产环境中,当有更好的工具可用时,没有充分的理由单独使用sed;但这仍然是一个很好的训练练习。)
与Enrico De Angelis发布的简单而简短的解决方案相比,他的方法和我下面提出的方法有两个不同之处。
首先,如果“替换”文本中包含空格(例如,如果每个空格都必须替换为百分号后带有空格的% 20),恩里科的答案中的方法就不起作用。但在更一般的问题中,Enrico解决方案中的循环方法可能导致无限循环。
第二,循环方法要求对每个必须替换的空格运行一次regexp匹配。相反,虽然下面的解决方案也运行s命令多次,但它是每个输入行的固定运行次数,而不管要替换的空格数。同样,在OP的问题中,这不是问题,因为每一行上只有很少的空格需要替换。下面的方法在更一般的情况下可能是有帮助的,其中每一行需要大量的替换。
这个想法相对简单,但是sed只有两个缓冲区,我们可以在这两个缓冲区之间来回切换,我们可以“保存”一部分不需要修改的字符串,然后在剩下的字符串中进行修改。由于我们只有两个缓冲区和三个相关的子字符串,我们被迫在解决方案的前半部分进行“太多的修改,”然后在后半部分撤销不必要的修改。这个解决方案也有一个明显的弱点:如果字符串的最后一部分已经包含了%20(在与folder相关的结束双引号之后),则这些字符串将被更改为空格,即使它们在原始字符串中不是空格。
我想知道是否有更好的方法沿着这些路线(意思是,具体地说,不涉及循环过程)。

$ sed -E '/folder:"/{h;s/(^.*?folder:").*/\1/;x;s/^.*?folder:"//;s/ /%20/g;x;G;
> /folder:"/s/\n//;h;s/(^.*?folder:"[^"]*").*/\1/;x;s/.*?folder:"[^"]*"//;
> s/%20/ /g;x;G;/folder:"/s/\n//}' inputfile

通常,前导的$>是shell提示符(不是sed命令的一部分)。

EDIT正如EdMorton在下面的评论中指出的,懒惰量词是Perl的一个特性,在sed中不受支持。以下是POSIX ERE兼容版本:

$ sed -E '/folder:"/{h;s/(^.*folder:").*/\1/;x;s/^.*folder:"//;s/ /%20/g;x;G;
> /folder:"/s/\n//;h;s/(^.*folder:"[^"]*").*/\1/;x;s/.*folder:"[^"]*"//;
> s/%20/ /g;x;G;/folder:"/s/\n//}' inputfile

相关问题