繁体   English   中英

重击:替换整行

[英]Bash : Replace entire line

我正在使用以下行来获取发生特定字符串的行号:

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时,没有任何追加。

这是没有意义的。 只要找到它就将其替换:

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

旁:按以下所述使用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

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

现在,当您感兴趣的值存储在位置参数而不是外壳变量中时,让我们尝试一下。 按照我们上面显示的模式,可能是:

$ 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作为循环的最终值。 这是第三次尝试时间:

$ 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}'

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是一种用于处理文本的工具。

例如,如果在循环中分配外壳变量,则它应该是文件名上的循环,因此,至关重要的是不要扩展转义符,否则结果变量将不包含预期的文件名。

如果您分配awk变量,则与操作文本有关,最常见的事情是该文本包含文字标签,换行符等。不是该文本包含文字\\t s和\\n s,因此通过awk扩展FS='\\t'FS=<a literal tab>就是您要解析由制表符分隔的值的文件的行为。

因此,除非您有其他特殊原因-不要编写shell变量初始化代码来扩展转义,因为给定了shell的用途,这可能不是您想要的,而是编写awk变量初始化代码来扩展转义,因为这可能就是您想要的给定awk的用途。

要替换线路,您可以使用

Sed或Awk

如下例所示,以sed或awk表示NR(记录数)指定行号

awk'NR == 34 {sub(“ AAA”,“ BBB”)}'

如果要在命令行上指定多个文件,请使用FNR(文件编号记录)。

awk'FNR == 34 {sub(“ AAA”,“ BBB”)}

' 要么

sed'34s / AAA / BBB /'

您也可以使用$ sign1来使用变量进行替换

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM