【问题标题】:Nested conditional search and in-place substituition嵌套条件搜索和就地替换
【发布时间】:2018-11-22 08:51:20
【问题描述】:

我是个 awk 新手。我有一个看起来像这样的文件:

beans and celery  
beans and oatmeal  
beans and beans  
quinoa
<fo:external-graphic width="auto" height="auto" content-width="36pt" src="url(file:/C:/Users/xxx/images/tip.svg)"/>
<fo:external-graphic src="url(images/image1.png)" width="6.3in" height="auto" content-width="246px" content-height="322px"/>

我正在尝试执行搜索并就地替换“fo”标签。我想捕获标签的开头,以及“src”参数。请注意,src 标签的位置因行而异!

我已经能够使用以下方法获得我想要的字段:

awk '/<fo:external-graphic.*/ {for (i=1; i<=NF; ++i) {if ($i ~ "src") print $1 " " $i}}' inventory.txt

如何进行就地替换? 我还想将一个字符串附加到该行的新内容。我试过了:

awk '/<fo:external-graphic.*/ {for (i=1; i<=NF; ++i) {if ($i ~ "src") print $1 " " $i "misc stuff here"}}' inventory.txt

但它完全打乱了结果字符串的顺序,我希望它的形式是:

<fo:external-graphic src="url(images/image1.png)" misc stuff here

PS1: 进一步澄清我想要什么结果: 该文件包含如下字符串:

<fo:external-graphic width="auto" height="auto" content-width="36pt" src="url(file:/C:/Users/xxx/images/tip.svg)"/>
<fo:external-graphic src="url(images/image1.png)" width="6.3in" height="auto" content-width="246px" content-height="322px"/>

我想处理这些并获得如下输出:

<fo:external-graphic src="url(images/image1.png)" _completely new stuff here, till end of string_ />

例如:

<fo:external-graphic src="url(images/image1.png)" age="25" sex="M" />

我希望结果始终以:

<fo:external-graphic src="url(images/image1.png)"

然后是额外的东西,例如:

age="25" sex="M" />

在最终输出中不需要原始字符串的其他部分。

PS2:我可以将所有这些打包到 gsub 中吗?据我所知,gsub 只接受两个参数。我试图为 replace 参数制作一个复杂的表达式,但它一直失败,例如:

gawk '/<fo:external-graphic.*/ {for (i=1; i<=NF; ++i) {if ($i ~ "src") gsub($0, "boy band"); {print}}}' inventory.txt > testres

PS3:这只是一个新手观察,也许我错了。考虑一个包含以下内容的文件:

Donald Trump
Donald Duck
George Bush
Steve Austin

搜索所有以 Donald 开头的行的正则表达式是:

/^Donald/

如果我想用“Barrack”替换所有出现的“Donald”,我可以执行以下操作:

gawk -i inplace '{ gsub(/^Donald/, "Barrack"); { print } }' FILENAME

如果我想完全更改所有包含“Donald”的行,我会这样做:

gawk -i inplace '{ gsub(/^Donald.*/, "Barrack"); { print } }' FILENAME

gawk 和 gsub 似乎只替换 span 或字符串的任何部分与给定的正则表达式匹配。因此,如果我想完全改变一整行,我的正则表达式应该跨越整行。

PS4:只是为了澄清我所期望的解决方案的任何歧义。给定以下文件:

<fo:external-graphic width="auto" height="auto" content-width="36pt" src="url(file:/C:/Users/xxx/images/tip.svg)"/>
<fo:external-graphic width="6.3in" height="auto" src="url(images/image1.png)" content-width="246px" content-height="322px"/>
<fo:external-graphic src="url(images/image1.png)" width="6.3in" content-width="246px" content-height="322px"/>

我正在寻找一种 awk/gawk 解决方案,该解决方案将把这个文件替换为:

<fo:external-graphic src="url(file:/C:/Users/xxx/images/tip.svg)" age="25" sex="M" />
<fo:external-graphic src="url(images/image1.png)" age="25" sex="M"/>
<fo:external-graphic src="url(images/image1.png)" age="25" sex="M"/>

目标文件必须更改

【问题讨论】:

  • @Inian 似乎没有更新文件。请查看我更新的问题以查看我期望的最终答案的形式!
  • @RavinderSingh13 请查看我的问题的最新更新!如果您有任何疑问,请告诉我。谢谢
  • @Inian 请查看我的问题的最新更新!如果您有任何疑问,请告诉我。谢谢
  • 我的最后一次更新应该可以解决您的问题

标签: awk


【解决方案1】:

您的尝试是正确的,但假设您的意图是仅添加以 src 开头的单词,即在 $i 上,请仅对该字段应用操作,并保持其他字段不变

awk '/<fo:external-graphic.*/ {for (i=1; i<=NF; ++i) {if ($i ~ "src") $i = $i " misc stuff here"}}1' inventory.txt

$i = $i " misc stuff here" 部分仅将字符串附加到与您的正则表达式条件匹配的字段上。注意print 的删除和{..}1 在末尾的附加。这基本上意味着根据{..} 内部所做的修改重新构建整行。由于我们只修改了某些字段,因此其他字段保持不变。

如果您想重写以src 开头的整个字段并附加一些字符串,请使用适当的正则表达式匹配gsub(),并在&amp; 表示的匹配文本之后附加字符串

awk '/<fo:external-graphic.*/ {for (i=1; i<=NF; ++i) { if ($i ~ "src") gsub(/src=\"url([^"]*)\"/, "& new string", $i ) }}1' inventory.txt

从 OP 的最新编辑来看,似乎 OP 只是希望在末尾附加用新字符串修改的 src 字段。其余字段似乎可以忽略。在 GNU awk 上使用 match() 有一个额外的优势,即添加第三个参数来将捕获的组存储为

awk -v newstr="age=\"25\" sex=\"M\"" 'match($0, /^(<fo:external-graphic).*(src=\"url([^"]*)\").*(\/>)$/, arr){ print arr[1]" "arr[2]" "newstr""arr[4]  }' file

请注意,GNU awk 在 4.1.2 之前没有就地修改,您可以这样做

gawk -i inplace '{...}' inventory.txt

对于之前的版本,使用临时文件

awk '{...}' inventory.txt > tmpfile && mv tmpfile inventory.txt

或者,如果您安装了moreutils,请使用sponge 从第一个命令中获取输出并使用最新的重新创建文件。

awk '{...}' inventory.txt | sponge inventory.txt

从您关于问题的示例文本中,无法识别它是否是某种标记语言(XML、HTML)。如果它是一种正确的语法感知语言,那么您应该使用知道该语法的解析器。

【讨论】:

    【解决方案2】:

    EDIT3:根据 OP 的新编辑在此处再添加一个代码。

    awk  '
    / width.*content-width.*src/{
      sub(/ width.*content-width.*src/," src")
      sub(/\/>$/," age=\"25\" sex=\"M\"&")
    }
    /src.*width/{
      match($0,/src[^)]*/)
      val=substr($0,RSTART,RLENGTH+2)
      sub(/src.*/,"")
      $0=$0 OFS val OFS "age=\"25\" sex=\"M\"/>"
    }
    1
    '  Input_file
    


    EDIT2:要更改关于 OP 的 PS3 的完整线路,请尝试以下操作。

    awk '/^Donald/{$0="new_line_value"} 1'  Input_file
    new_line_value
    new_line_value
    George Bush
    Steve Austin
    


    编辑:由于 OP 已更改预期输出,因此现在也根据该输出添加解决方案。

    awk '/^<fo:external-graphic src=.*/ && match($0,/src=.*\)\"/){$0=substr($0,1,RSTART+RLENGTH) " new_value_bla_bla_here.. />"} 1' Input_file
    


    您能否尝试以下操作(尚未彻底测试,因为您的预期输出不清楚)。

    awk '
    /^<fo:/ && match($0,/src=.*>/){
      $0=substr($0,1,RSTART-1) OFS "new_value_here.." OFS substr($0,RSTART+RLENGTH+1)
    }
    1
    '  Input_file
    

    在此代码中,检查从&lt;fo: 字符串开始的行,然后尝试通过match 捕获从src=/ 的字符串,并在此处用新字符串替换捕获的文本。

    如果您想将输出保存到 Input_file 本身,请在上面的代码中添加 &gt; temp_file &amp;&amp; mv temp_file Input_file

    【讨论】:

    • 第二行就ok了。第一行打印出来: 。请随时根据我在问题开头提供的文件测试您的解决方案
    • @user1801060,现在检查并让我知道吗?我没有检查字符串^&lt;fo:external-graphic src= 的条件现在检查一次?
    • 也许是我的错误。这是我的结果截图:imgur.com/aENVTyD
    • @user1801060,它是像您想要匹配的所有src 还是您想要它执行的任何特定字符串?
    • 辛苦了!第一个没用,后两个有效。我相信让一个正则表达式适用于所有变体是问题所在。我明天去看看。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-12
    • 1970-01-01
    • 2018-07-01
    • 2020-03-21
    • 2014-01-24
    • 2020-03-06
    • 2017-01-19
    相关资源
    最近更新 更多