Bash:替换整条线

时间:2015-09-30 08:50:46

标签: linux bash shell awk

我使用以下行来获取特定字符串出现的行号:

nLine=$(awk '/text/{ print NR; exit }' $1)
echo "line = $nline"

返回:

line = 78

现在,我想使用:

将此特定行替换为其他字符串
awk 'NR==$nLine {$0="new text $2"} 1' test.xml

其中$ 2是给bash脚本的参数。

当命令行直接输入终端或将参数设置为:

时,此命令行正常工作
awk 'NR==78 {$0="new text data"} 1' test.xml

但是当为命令提供参数时,它永远不会按预期工作..

另外,是否可以避免终端打印?因为当我把> / dev / null在行的末尾没有任何附加内容。

2 个答案:

答案 0 :(得分:0)

这没有任何意义。只需在找到它时替换它:

awk -v nText="$2" '/text/{$0="new text " nText} 1' test.xml

ASIDE:如下所述,使用ENVIRON,请考虑以下事项:

$ foo='a\tb'
$ printf '%s\n' "$foo"
a\tb
$ awk -v foo="$foo" 'BEGIN{ print foo }'
a       b
$ foo="$foo" awk 'BEGIN{ print ENVIRON["foo"] }'
a\tb

因此,如果您不希望扩展转义序列但是它需要更多代码,并且如果您需要多次foo的值,效率较低,则ENVIRON更好。在一个循环中(在这种情况下,您将使用更多代码和更多重复单词foofoo="$foo" awk 'BEGIN{ foo=ENVIRON["foo"]; print foo }'。)

现在,让我们尝试将您感兴趣的值存储在位置参数而不是shell变量中。按照我们似乎在上面显示的模式,这将是:

$ set -- 'a\tb'
$ printf '%s\n' "$1"
a\tb
$ awk -v foo="$1" 'BEGIN{ print foo }'
a       b
$ 1="$1" awk 'BEGIN{ print ENVIRON["1"] }'
-bash: 1=a\tb: command not found

当然这不起作用,你需要注意的一个微妙的事情是,在调用awk之前发生的shell赋值的shell变量的名称不能总是shell变量的名称你想得到的值甚至可能不是你可以指定的变量:

$ foo="$1" awk 'BEGIN{ print ENVIRON["foo"] }'
a\tb

在所有情况下都适用的替代方法是在参数列表中指定变量值:

$ awk 'BEGIN{ foo=ARGV[1]; ARGV[1]=""; print foo }' "$1"
a\tb

但是这也有一些警告,因为你不能再通过ARGV循环获取输入文件的名称。

现在,让我们比较两个脚本的非常逼真的演变可能性,一个使用-v和一个ENVIRON

$ awk -v var=100000000 'BEGIN{ print var }'
100000000

$ var=100000000 awk 'BEGIN{ print ENVIRON["var"] }'
100000000

现在,假设我们想使用var作为循环的结束值。这是第3次尝试时间:

$ time awk -v var=100000000 'BEGIN{ for (i=1;i<=var;i++) i }'
real    0m7.813s
user    0m7.706s
sys     0m0.031s

$ time var=100000000 awk 'BEGIN{ for (i=1;i<=ENVIRON["var"];i++) i }'
real    0m11.673s
user    0m11.637s
sys     0m0.031s

注意ENVIRON版本的效率低得多。

或者,如果您只需要在脚本中使用它几次,那该怎么办?

$ awk -v var=100000000 'BEGIN{ print var; if (var > 5) var = 5; print var }'
100000000
5

$ var=100000000 awk 'BEGIN{ print ENVIRON["var"]; if (ENVIRON["var"] > 5) ENVIRON["var"] = 5; print ENVIRON["var"] }'
100000000
5

请注意ENVIRON版本的代码简洁得多。

在上述两种情况下,除了初始化awk变量之外,你真的不想使用ENVIRON [“var”],然后在其余的代码中使用它:

$ time var=100000000 awk 'BEGIN{ var=ENVIRON["var"]; for (i=1;i<=var;i++) i }' 
real    0m7.692s
user    0m7.612s
sys     0m0.031s

$ var=100000000 awk 'BEGIN{ var=ENVIRON["var"]; print var; if (var > 5) var = 5; print var }'
100000000
5

所以,除非你想要随着它的发展重写你的代码,如果你打算使用ENVIRON,那么相当于:

awk -v var=val 'BEGIN{ print var }'

不是

var=val awk 'BEGIN{ print ENVIRON["var"] }'

而是:

var=val awk 'BEGIN{ var=ENVIRON["var"]; print var }'
与使用-v相比,

最长且重复。

要考虑的其他事情是,当您解析文件并且每个记录由换行符分隔(假设不是默认值)时,您是否要编写:

awk -v RS='\n' '1'

awk -v RS="$'\n'" '1'

将RS设置为换行符?当然,前者更方便,直观明显,你当然不想写:

RS="$'\n'" awk 'BEGIN{ RS=ENVIRON["RS"] } 1'

FS如何成为标签:

awk -v FS='\t' '{print NF}'

VS

FS="$'\t'" awk 'BEGIN{ FS=ENVIRON["FS"] } {print NF}'

重点是 - 扩展转义几乎总是所需的效果,因此编写更长,更慢,更麻烦的代码来禁用该效果将不是编写代码的好方法。

恕我直言 - 只需使用-v,除非您不希望扩展转义序列并且不想在赋值中转义它们(最常见的是当您分配的值存储在shell变量中时,如上面的例子):

$ awk -v foo='a\tb' 'BEGIN{ print foo }'
a       b
$ awk -v foo='a\\tb' 'BEGIN{ print foo }'
a\tb

最后的想法 - 我一直告诉人们在编写shell循环时默认使用:

while IFS= read -r var
do
      whatever
done

特别是我说默认使用read -r var来阻止在shell变量中展开转义而在awk中我说使用awk -v var=导致转义在awk变量中展开

明显不一致的原因是shell是一个操作文件和进程以及调用其他工具的工具,而awk是一个操作文本的工具。

如果你在一个循环中分配一个shell变量,它应该是一个文件名循环,例如,所以不扩展转义或结果变量不会按预期包含文件的名称是至关重要的。

如果你分配一个awk变量,那么它与操作文本有关,最常见的是文本包含文字标签,换行符等。不是文本包含文字\t和{{ 1}}通过awk将\n扩展为FS='\t',这就是解析制表符分隔值文件所需的行为。

因此,除非你有特殊的原因,否则不要编写shell变量初始化代码来扩展转义,因为这可能不是你想要的shell所用的东西,并且写awk变量初始化代码来扩展转义因为那可能是给出awk用于什么的你想要的东西。

答案 1 :(得分:0)

要更换线路,您可以使用

  

Sed或Awk

以awd中的sed或NR(记录数)指定行号,如下例所示

  

awk&#39; NR == 34 {sub(&#34; AAA&#34;,&#34; BBB&#34;)}&#39;

如果要在命令行中指定多个文件,请使用

或使用FNR(文件编号记录)。

  

awk&#39; FNR == 34 {sub(&#34; AAA&#34;,&#34; BBB&#34;)}

&#39; 或

  

sed&#39; 34s / AAA / BBB /&#39;

您也可以使用$ sign1

使用变量进行替换
相关问题