[英]bash: How to replace an entire line in a text file by a part of its content
[英]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.