嵌套条件搜索和就地替换

时间: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/

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

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

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

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

gawk和gsub似乎仅替换范围或字符串中与给定regexp相匹配的任何部分。因此,如果我想完全更改整行,则我的regexp应该会覆盖整行。

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"/>

目标文件必须更改

2 个答案:

答案 0 :(得分:2)

您的尝试是正确的,但是假设您的意图只是在以src开头的单词(即$i)上加上,仅对该字段应用操作,而其他字段保持原样< / p>

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()适当的正则表达式匹配项,并在由&表示的匹配文本之后附加字符串

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 match()上使用awk的另一个好处是添加了第三个参数来将捕获的组存储为

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

请注意,直到4.1.2为止,GNU awk都没有就地修改,您可以对其进行修改

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

对于早于该版本的版本,请使用一个临时文件

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

或者,如果您moreutils已安装,请使用sponge从第一个命令中提取输出,然后使用最新版本重新创建文件。

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

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

答案 1 :(得分:1)

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

在此代码中,检查从<fo:字符串开始的行,然后尝试用src=捕获从/match的字符串,并将捕获的文本替换为新的字符串在这里。

如果您要将输出保存到Input_file本身,则也可以在上面的代码中附加> temp_file && mv temp_file Input_file