如何使用bash替换文件中最后匹配的所有内容?

时间:2011-11-06 16:08:41

标签: bash replace

假设使用 bash ,配置文件如:

  
      
  1. PARAM-A = AAAAAA
  2.   
  3. PARAM-B = BBBBBB
  4.   
  5. param-foo =首次出现< - 替换
  6.   
  7. PARAM-C = CCCCCC
  8.   
  9. #param-foo =首先发表评论foo< - 评论:不要替换
  10.   
  11. PARAM-d = DDDDDD
  12.   
  13. PARAM-E = EEEEEE
  14.   
  15. param-foo =第二次出现< - Rreplace
  16.   
  17. param-foo =第三次出现< - 最后有效:不要替换
  18.   
  19. PARAM-X = xxxxxx1
  20.   
  21. PARAM-F = FFFFFF
  22.   
  23. #param-foo = second评论foo< - 评论:不要替换
  24.   
  25. PARAM-X = xxxxxx2
  26.   

您可以在其中找到param-foo的多个注释或未注释的行, 你怎么能评论所有未注释的param-foos,除了最后一个活跃的param-foos, 导致:

  
      
  1. PARAM-A = AAAAAA
  2.   
  3. PARAM-B = BBBBBB
  4.   
  5. #param-foo =首次出现< - 已替换
  6.   
  7. PARAM-C = CCCCCC
  8.   
  9. #param-foo =评论foo< - 左
  10.   
  11. PARAM-d = DDDDDD
  12.   
  13. PARAM-E = EEEEEE
  14.   
  15. #param-foo =第二次出现< - 已替换
  16.   
  17. param-foo =第三次出现< - 左
  18.   
  19. PARAM-X = xxxxxx1
  20.   
  21. PARAM-F = FFFFFF
  22.   
  23. #param-foo = second评论foo< - 左
  24.   
  25. PARAM-X = xxxxxx2
  26.   

问题的两个部分:
1.如何只用一个已知的重复参数来做到这一点? (上例中只有param-foo)

2.如何同时使用所有多个活动参数?
(上例中的param-foo + param-x)
注意:在这种情况下,我以前不知道重复参数的名称!

由于

4 个答案:

答案 0 :(得分:1)

如果awk可以接受,则会为 param-foo param-x <​​/ em>执行此操作:

awk -F= -v p='param-foo param-x'  'BEGIN { 
  ARGV[ARGC++] = ARGV[ARGC - 1]
  n = split(p, t, OFS)
  for (i = 0; ++i <= n;) _p[t[i]]
  }
NR == FNR {
    $1 in _p && nr[$1] = NR
  next
  }  
$1 in nr && FNR != nr[$1] {
  $0 = "# " $0 
  }1' infile

您可以使用单个参数: p = param-x <​​/ em>或添加更多以空格分隔的参数: p ='param-1 param-2 ... param-n'< / em>的。

编辑:我假设真正的输入文件如下所示:

param-a=aaaaaa
param-b=bbbbbb
param-foo=first occurence
param-c=cccccc
# param-foo=commented foo
param-d=dddddd
param-e=eeeeee
param-foo=second occurence
param-foo=third occurence
param-x=xxxxxx1
param-f=ffffff
param-x=xxxxxx2

让我知道它是否不同。

第二次编辑:为 mawk 用户提供解决方案:

awk -F= -v p='param-foo param-x'  'BEGIN { 
  n = split(p, t, OFS)
  for (i = 0; ++i <= n;) _p[t[i]]
  }
NR == FNR {
    $1 in _p && nr[$1] = NR
  next
  }  
$1 in nr && FNR != nr[$1] {
  $0 = "# " $0 
  }1' infile infile

为最新要求添加解决方案:

awk -F= 'NR == FNR {
  if (NF && !/^#/)
    _p[$1]++ && nr[$1] = NR
  next
  }    
$1 in nr && FNR != nr[$1] {
  FNR != nr[$1] && $0 = "# " $0 
  }1' infile infile

答案 1 :(得分:0)

长文件有点慢,但应该适用于所有参数:

grep -v ^# $file |
cut -f1 -d= |
sort -u |
sed 's/^/grep -n . '$file' |
tac |
grep -m1 :/;s/$/= /' |
bash |
sed -r 's%([0-9]+):(.*)=(.*)%\1!s/^\2=/# \2=/%' |
sed -f- $file

答案 2 :(得分:0)

我没有完全测试过该脚本,但它在第一个例子中起作用:

#!/bin/bash
input_file=/path/to/your/input/file    
last_occurence=`nl $input_file | grep 'param-foo' | grep -v '#' | tail -1 | awk -F" " '{print $1}'`
sed -i '/#/!s/param-foo/# param-foo/g' $input_file
sed -i "${last_occurence}s/# param-foo/param-foo/" $input_file

这是非常直接的逻辑。首先我们得到最后一次出现的param-foo,它没有被评论。 第一个sed去评论所有param-foo,这些都没有评论。 第二个sed使用param-foo最后一次出现的line_number并删除#字符。你可以轻松地将它包装在一个函数中并在循环中使用它,提供一个参数列表,而不是只有一个。

答案 3 :(得分:0)

这可能有效:

param="param-foo"
tac input_file |sed '/#/!{/'"$param"'/{x;/./{x;s/'"$param"'/# &/;t};x;h;}}'|tac >output_file

对于多个参数:

cp input_file{,.backup}
params=(param-{foo,bar,baz})
tac input_file >backwards_file
for param in "${params[@]}"; do
    sed -i '/#/!{/'"$param"'/{x;/./{x;s/'"$param"'/# &/;t};x;h;}}' backwards_file
done
tac backwards_file >output_file

向后转动input_file,预先发送除$param第一次出现的所有评论#,然后还原该文件。

编辑: 要从文件中提取参数,请使用以下代码:

params=($(sed -rn '/^#/d;/^$/!s/^\s*([^=]*).*/\1/gp' input_file | sort | uniq))