如何在awk脚本中使用shell变量?


289

我发现了一些将外部shell变量传递给awk脚本的方法,但是我对'和感到困惑"

首先,我尝试使用shell脚本:

$ v=123test
$ echo $v
123test
$ echo "$v"
123test

然后尝试awk:

$ awk 'BEGIN{print "'$v'"}'
$ 123test
$ awk 'BEGIN{print '"$v"'}'
$ 123

为什么有区别?

最后,我尝试了这个:

$ awk 'BEGIN{print " '$v' "}'
$  123test
$ awk 'BEGIN{print ' "$v" '}'
awk: cmd. line:1: BEGIN{print
awk: cmd. line:1:             ^ unexpected newline or end of string 

我对此感到困惑。


2
我喜欢如下所示的-v,但这确实是考虑如何保护外壳程序的绝妙练习。通过这一工作,我的第一个削减在空格和美元符号上使用了反斜杠。不用说这里的例子很值得我花时间。
克里斯,


如果您的awk搜索需要正则表达式,则不能放/var/。相反,请使用波浪号:awk -v var="$var" '$0 ~ var'
Noam Manos

Answers:


496

将shell变量放入 awk

可以通过几种方式完成。有些比其他更好。这应该涵盖其中的大多数。如果您有任何评论,请在下面离开。v1.5


使用-v (最佳方式,最便携)

使用以下-v选项:(PS使用空格,-v否则它将不那么方便。例如,awk -v var=不是awk -vvar=

variable="line one\nline two"
awk -v var="$variable" 'BEGIN {print var}'
line one
line two

这应该与most兼容awk,并且该变量在BEGIN块中也可用:

如果您有多个变量:

awk -v a="$var1" -v b="$var2" 'BEGIN {print a,b}'

警告。正如埃德·莫顿(Ed Morton)所写,转义序列将被解释,因此\t将成为真实序列,tab而不是\t您要搜索的序列。可以通过使用ENVIRON[]或访问来解决ARGV[]

PS:如果您喜欢三个竖线作为分隔符|||,则无法逃脱,请使用-F"[|][|][|]"

从程序/函数客栈获取数据的示例awk(此处使用日期)

awk -v time="$(date +"%F %H:%M" -d '-1 minute')" 'BEGIN {print time}'

代码块后的变量

在这里,我们在awk代码后得到变量。只要您不需要该BEGIN块中的变量,这就可以正常工作:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • 添加多个变量:

awk '{print a,b,$0}' a="$var1" b="$var2" file

  • 这样,我们还可FS以为每个文件设置不同的字段分隔符。

awk 'some code' FS=',' file1.txt FS=';' file2.ext

  • 代码块后的变量不适用于该BEGIN块:

echo "input data" | awk 'BEGIN {print var}' var="${variable}"


这里串

也可以从支持它们的shell(包括Bash)中awk使用here-string将变量添加到其中:

awk '{print $0}' <<< "$variable"
test

这与以下内容相同:

printf '%s' "$variable" | awk '{print $0}'

PS将此变量视为文件输入。


ENVIRON 输入

正如TrueY所写,您可以使用ENVIRON打印环境变量。在运行AWK之前设置变量,您可以像这样将其打印出来:

X=MyVar
awk 'BEGIN{print ENVIRON["X"],ENVIRON["SHELL"]}'
MyVar /bin/bash

ARGV 输入

正如史蒂文·潘尼(Steven Penny)所写,您可以ARGV用来将数据放入awk:

v="my data"
awk 'BEGIN {print ARGV[1]}' "$v"
my data

要将数据放入代码本身,而不仅仅是BEGIN:

v="my data"
echo "test" | awk 'BEGIN{var=ARGV[1];ARGV[1]=""} {print var, $0}' "$v"
my data test

代码中的变量:小心使用

您可以在awk代码中使用变量,但是它很杂乱且难以阅读,并且Charles Duffy指出,该版本也可能是代码注入的受害者。如果有人在变量中添加了不良内容,它将作为awk代码的一部分执行。

这是通过在代码中提取变量来实现的,因此它成为了其中的一部分。

如果您想通过awk变量的使用来使它动态变化,可以这样做,但是不要将其用于普通变量。

variable="line one\nline two"
awk 'BEGIN {print "'"$variable"'"}'
line one
line two

这是代码注入的示例:

variable='line one\nline two" ; for (i=1;i<=1000;++i) print i"'
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
1
2
3
.
.
1000

您可以通过awk这种方式添加许多命令。甚至使用无效命令使其崩溃。


额外信息:

使用双引号

用双引号引起来的变量总是很好,"$variable"
否则,将多行添加为长单行。

例:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

没有双引号的其他错误:

variable="line one\nline two"
awk -v var=$variable 'BEGIN {print var}'
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ backslash not last character on line
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ syntax error

用单引号括起来,它不会扩展变量的值:

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

有关AWK和变量的更多信息

阅读此常见问题解答


2
当直接将字符串替换为awk代码时,“混乱且难以阅读”忽略了代码注入的更重要的安全问题。
查尔斯·达菲

阅读上面的答案,我可以没有错误地运行我的脚本,但是它不能完成任务:awk -v repo =“ $ 1” -v tag =“ $ 2”'{sub(/ image:Registryabx.azurecr.io \ / {打印回购}:([a-z0-9] +)$ /,“图像:registryabc.azurecr。io / {打印回购}:{打印标签}”);} 1'./services/appscompose.yaml >> newcompose.yaml。是因为括号{
达里恩·巴德利多(Darion Badlydone),

@DarionBadlydone试试这个awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'。它将查看是否打印变量。如果您无法解决,请发表自己的问题。
乔特内,

@Jotne是的,它打印值,所以我以这种方式尝试过:awk -v repo =“ $ 1” -v tag =“ $ 2”'{print“ {sub(/ image:Registryabc.azurecr.io/"repo” :( [a-z0-9] +)$ /,\“图像:registryabc.azurecr.io/” repo“:” tag“ \”);} 1“}'./services/appscompose.yaml >> newcompose.yaml但无法正常工作。它用打印的命令替换源文件的每一行
Darion Badlydone

@Jotne我用sed做到了,无论如何谢谢
Darion Badlydone

28

看来好老 ENVIRON 完全没有提到内置哈希。其用法示例:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt

4
这是一个很好的建议,因为它逐字传递了数据。-v该值包含反斜杠时不起作用。
那个人

2
@thatotherguy我不知道!我认为如果我使用awk -v x='\c\d' ...它,那么它将被正确使用。但是当awkx打印时会掉落著名的:错误消息...谢谢!awk: warning: escape sequence '\c' treated as plain 'c'
TrueY

它确实可以正常工作-在这种情况下,适当地意味着扩展转义序列,因为这-v是设计的工作方式,因此您可以\t在变量中使用并使其与数据中的文字选项卡匹配。如果这不是您想要的行为,则不要-v使用ARGV[]ENVIRON[]
Ed Morton

9

根据要在shell变量中处理反斜杠的方式(avar是awk变量,svar是shell变量),使用以下任一方法:

awk -v avar="$svar" '... avar ...' file
awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}... avar ...' "$svar" file

有关详细信息和其他选项,请参见http://cfajohnson.com/shell/cus-faq-2.html#Q24。上面的第一种方法几乎总是您的最佳选择,并且具有最明显的语义。


6

您可以在命令行选项中 传入-v变量名称(v)和=环境变量("${v}")的值():

% awk -vv="${v}" 'BEGIN { print v }'
123test

或使其更清楚(vs 少得多):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test

3

您可以使用ARGV:

v=123test
awk 'BEGIN {print ARGV[1]}' "$v"

请注意,如果您要继续进入身体,则需要调整ARGC:

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"

1

我只是将@Jotne的答案更改为“ for循环”。

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done

1
这似乎只是如何使用Awk -v选项的另一种说明,该选项已在许多现有答案中提到。如果您想展示如何循环运行Awk,那确实是一个不同的问题。
Tripleee '19

0

我必须在日志文件的各行开头插入日期,如下所示:

DATE=$(date +"%Y-%m-%d")
awk '{ print "'"$DATE"'", $0; }' /path_to_log_file/log_file.log

可以重定向到另一个文件进行保存


双引号-单引号-双引号正是我使我的工作所需的。
user53029 '16

2
接受的答案中已经提到了这种方法,由于代码注入漏洞,您不应该使用这种方法。因此,这里的信息是多余的(已经在接受的答案中进行了描述),并且是不完整的(没有提及此方法的问题)。
詹森·S
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.