我找到了一些方法来传递外部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 

我对此感到困惑。


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

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

或者更清楚地说(用更少的vs):

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

#获取shell变量到awk 可以通过几种方式来实现。有些比其他的好。这应该涵盖了大部分。如果你有一个评论,请留下 .                                                                                    v1.5


使用-v(最好的方式,最可移植)

使用-v选项:(附注:在-v之后使用空格,否则它将不那么便携。例如,awk -vvar= not awk -vvar=)

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

这应该与大多数awk兼容,并且该变量也可以在BEGIN块中使用:

如果你有多个变量:

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

警告。正如Ed Morton所写,转义序列将被解释,因此\t将成为一个真正的制表符,而不是你搜索的\t。可以使用ENVIRON[]解决,也可以通过ARGV[]访问

PS如果你有竖条或其他regexp元字符作为分隔符,如|?(等等,他们必须双重逃脱。示例3竖线| | | - f '\\|\\|\\|'.你也可以使用-F"[|][|][|]"。

从程序/函数inn获取数据到awk的示例(此处使用date)

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

用regexp来测试一个shell变量的内容:

awk -v var="$variable" '$0 ~ var{print "found it"}'

代码块后的变量

在这里,我们在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"文件

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

awk '一些代码' FS=',' file1.txt FS='

代码块后面的变量对BEGIN块不起作用:

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


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

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

这相当于:

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

附注:这将变量视为文件输入。


环境输入

正如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添加大量命令。甚至让它崩溃无效的命令。

不过,这种方法的一个有效用途是,当你想传递一个符号给awk以应用于某些输入时,例如一个简单的计算器:

$ calc() { awk -v x="$1" -v z="$3" 'BEGIN{ print x '"$2"' z }'; }

$ calc 2.7 '+' 3.4
6.1

$ calc 2.7 '*' 3.4
9.18

用shell变量的值填充awk变量是无法做到这一点的,你需要在awk解释它之前将shell变量扩展为awk脚本文本的一部分。(见下面Ed M.的评论)


额外信息:

双引号的使用

对变量$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和变量的信息

阅读这个常见问题。


似乎完全没有提到老式的ENVIRON awk内置散列。用法示例:

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

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

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

它可以重定向到另一个文件来保存


你可以使用ARGV:

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

注意,如果你要继续进入身体,你将需要调整 命令行参数个数:

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

我只是改变了@Jotne的答案“for循环”。

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

专业提示

它可以方便地创建一个函数来处理这个问题,这样你就不必每次都输入所有内容。使用选定的解决方案,我们得到…

awk_switch_columns() {
     cat < /dev/stdin | awk -v a="$1" -v b="$2" " { t = \$a; \$a = \$b; \$b = t; print; } "
}

然后把它用作…

echo 'a b c d' | awk_switch_columns 2 4

Output:
a d c b