【问题标题】:bash & sed: replace multiple strings in variable with for loopbash&sed:用for循环替换变量中的多个字符串
【发布时间】:2014-02-16 12:13:55
【问题描述】:

使用 bash,我试图用 sed 替换存储变量中的多行,然后使用行更改打印变量。

列表变量如下所示 - virtmx 元素都是唯一的 - 不重复

$list 变量:

domain1.com     =====>  dest.domain.com:25
domain2.com     =====>  virtmx-0050:25
domain3.com     =====>  dest.domain2.com:25
domain4.com     =====>  domain.example.com:25
domain5.com     =====>  dest.domain3.com:25
domain6.com     =====>  virtmx-0051:25
domain7.com     =====>  dest.domain4.com:25
domain8.com     =====>  dest.domain5.com:25
domain9.com     =====>  dest.domain6.com:25

我想用系统文件中的另一行替换所有 virtmx-[num]。

somefile.log 同时包含 line[num] 和替换行:

file.log 中的行示例:

cat /somedir/somefile.log | grep virtmx-*
<mta id="50" host="virtmx-0050:25" dns_type="MX" desc="mxgroupname">
<mta id="51" host="virtmx-0051:25" dns_type="MX" desc="mxgroupname2">

使用 awk 提取行的示例:

cat /somedir/file.log | grep virtmx-0050 | awk -F "\"" '{print $4,$8}'
virtmx-0050:25 mxgroupname
virtmx-0051:25 mxgroupname2

想要的输出:

domain1.com     =====>  dest.domain.com:25
domain2.com     =====>  mxgroupname
domain3.com     =====>  dest.domain2.com:25
domain4.com     =====>  domain.example.com:25
domain5.com     =====>  dest.domain3.com:25
domain6.com     =====>  mxgroupname2
domain7.com     =====>  dest.domain4.com:25
domain8.com     =====>  dest.domain5.com:25
domain9.com     =====>  dest.domain6.com:25

$listoflines 只包含行本身:

line1
line2

我正在使用 awk 提取行,然后创建一个 sed 替换字符串 - 我用它来替换 $output 变量中的条目:

line1/replacement1

我想我已经接近了,但下面的代码只是替换循环中的第一个实例,然后重新开始并用新的替换再次打印整个变量,但缺少第一个。相反,我想用所有替换打印整个变量一次。替换的数量可能会有所不同,这就是我尝试使用 for 循环的原因。

for i in $listoflines
do
    replace=`cat /dir/somemfile.log | grep $i | awk -F "\"" '{print $4,$8}' | sed 's/ /\//'`
    echo "$list" | sed 's/'$replace'/'
done

【问题讨论】:

  • somefile.log 的第二行是否有line2?另外,您为什么要在 " 而不是空格上拆分文件。此外,您几乎肯定需要在这里逐行读取变量,您总是会错误地打印您的 $output 行两次。
  • 你能放一些来自somefile.log的示例行吗?
  • @anubhava 我从 somefile.log 添加了一些示例。

标签: string bash replace sed awk


【解决方案1】:

我远不是awk 大师,而是基于glenn's 答案的谦虚尝试

awk '
    NR==FNR {split($0,x,"\""); r[x[4]]=x[8]; next}
    /virtmx/ {$3=r[$3]}
    {print $1,$2,$3}
' file.log <(echo "$list")

输出:

domain1.com =====> dest.domain.com:25
domain2.com =====> mxgroupname
domain3.com =====> dest.domain2.com:25
domain4.com =====> domain.example.com:25
domain5.com =====> dest.domain3.com:25
domain6.com =====> mxgroupname2
domain7.com =====> dest.domain4.com:25
domain8.com =====> dest.domain5.com:25
domain9.com =====> dest.domain6.com:25

讨论:

  • 将两个“文件”发送到 awk,file.log&lt;(echo "$list")
  • NR==FNR 仅用于处理第一个文件

    1. " 上拆分file.log 并分配一个关联数组r 与模式-> 替换。
    2. $list 处理为文件,如果行匹配virtmx,则使用 assoc 数组替换,最后打印三列。

编辑:在第一行使用next 删除了NR!=FNR

【讨论】:

  • 我只想{print},如果您需要很好地格式化输出,请输入| column -t},否则或多或少我会写什么。
  • @glennjackman - 谢谢。最初我尝试&lt;&lt;&lt;"$list" 而不是&lt;(echo "$list") 没有成功。不知道我明白为什么 - 你介意解释一下吗?
  • @glennjackman - nvm,我想我明白了:&lt;&lt;&lt;"$list" 将内容发送到awkstdin,但&lt;(echo "$list") 使用文件系统中的实际管道充当真实文件@987654344 @。由于我们已经将真实文件file.log 提供给awk,它将丢弃stdin 但接受/dev/fd/&lt;n&gt;
【解决方案2】:

你应该做什么:

  • 在 awk 中,不是创建一个 sed 字符串“line1/replacement”,而是创建一个关联数组repl[line1] = replacement
  • 然后,仍然在 awk 中,处理 $output(使用进程替换来使用类似文件的东西),并使用 if ($NF in repl) $NF = repl[$NF]; print

我需要查看您现有的代码才能给您更具体的建议。

【讨论】:

  • 为了清楚起见,我编辑了整篇文章。你能用我的代码给我一个数组的例子吗?我以前从未使用过数组和 awk。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-22
  • 1970-01-01
  • 1970-01-01
  • 2011-12-02
  • 2021-11-20
相关资源
最近更新 更多