根据外部或内部单引号以不同方式替换空格

Replace spaces differently depending on outside or inside single quotes

我的输入有一些字段

这是一个示例输入:

active=1 'oldest active'=0s disabled=0 'function call'=0

我想替换:

输出将是:

active=1|'oldest_active'=0s|disabled=0|'function_call'=0

我尝试了在网上找到的 sedperl 的不同解决方案,但未能如愿以偿。

$ s="active=1 'oldest active'=0s disabled=0 'function call'=0"
$ echo "$s" | perl -pe "s/'[^']*'(*SKIP)(*F)| /|/g; s/ /_/g"
active=1|'oldest_active'=0s|disabled=0|'function_call'=0

两步替换:

  • 首先,'[^']*'(*SKIP)(*F)会跳过所有被'包围的模式,并将剩余的空格替换为|
  • 其次,'中现在剩余的空格将替换为_


替代解决方案:

$ echo "$s" | perl -pe "s/'[^']*'/$& =~ s| |_|gr/ge; s/ /|/g"
active=1|'oldest_active'=0s|disabled=0|'function_call'=0
  • 灵感来自
  • '[^']*'/$& =~ s| |_|gr/ge 使用另一个替换命令替换匹配模式 '[^']*' 中的所有空格。 e 修饰符允许在替换部分使用命令而不是字符串
  • 剩下的空间用s/ /|/g
  • 处理


延伸阅读:

我们可以在循环中使用正则表达式。

$str = "active=1 'oldest active'=0s disabled=0 'function call'=0";
print "\nBEF: $str\n";
$str =~s#active=1 'oldest active'=0s disabled=0 'function call'=0# my $tmp=$&; $tmp=~s/\'([^\']*)\'/my $tes=$&; $tes=~s{ }{\_}g; ($tes)/ge; $tmp=~s/ /\|/g; ($tmp); #ge;
print "\nAFT: $str\n";

除此之外可能还有一些捷径。

使用 gnu awk FPAT,你可以这样做:

s="active=1 'oldest active'=0s disabled=0 'function call'=0"

awk -v OFS="|" -v FPAT="'[^']*'[^[:blank:]]*|[^[:blank:]]+" '{
   for (i=1; i<=NF; i++) gsub(/[[:blank:]]/, "_", $i)} 1' <<< "$s"

active=1|'oldest_active'=0s|disabled=0|'function_call'=0
  • FPAT 正则表达式中,我们使用交替来创建所有单引号值+非 space 值的字段,即 '[^']*'[^[:blank:]]* 或非白色 space 值,即[^[:blank:]]+ 来自输入。
  • 使用 gsub 我们只需将所有 space 替换为 _ 因为我们只会在所有字段中得到单引号内的 space。
  • 最后使用 OFS='|' 我们用 |
  • 分隔输出

参考: Effective AWK Programming

这可能对你有用 (GNU sed):

sed -r ":a;s/^([^']*('[^ ']*')*[^']*'[^' ]*) /_/;ta;y/ /|/" file

这首先用 _ 替换引号字符串中的所有空格,然后将剩余的空格转换为 |

@anubhava 的解决方案让人想起老派的 perl 解决方案:

$ echo $s | perl -047 -pe "($.%2)?s/ /|/g:s/ /_/g;"
active=1|'oldest_active'=0s|disabled=0|'function_call'=0

根据even/odd.

用单引号(047)分行
$ awk -F\' '{OFS=FS; for (i=1;i<=NF;i++) gsub(/ /,(i%2?"|":"_"),$i)}1' file
active=1|'oldest_active'=0s|disabled=0|'function_call'=0