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

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

我对此感到困惑。

641391 次浏览

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

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

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

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

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


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

使用-v选项:(附注:在-v之后使用空格,否则它将不那么便携。例如,awk -v var=不是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将成为真正的tab,而不是\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" file

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

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

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

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


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

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

这相当于:

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

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


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添加很多命令。甚至让它崩溃无效的命令。

不过,这种方法的一个有效用途是,当你想传递一个符号给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脚本文本的一部分。


额外信息:

双引号的使用

双引号变量"$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和变量的信息

阅读这个常见问题

根据你想在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。上面的第一个方法几乎总是您的最佳选择,并且具有最明显的语义。

似乎旧的ENVIRON 内置哈希根本没有被提及。用法示例:

$ 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"
注意,如果你要继续进入主体,你将需要调整 命令行参数个数:< / p >
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

例子:

in.txt:

foo
bar

变量:

var=$(awk '{print $1}' in.txt)

命令:

echo -e "$var" > out.txt

out.txt

foo
bar

另一个问题:

in.txt

foo,aaa
bar,bbb

变量:

var=$(awk -F "," '{print $1}' in.txt)

out.txt

foo
bar

或者:

var=$(awk -F "," '{print $2}' in.txt)

out.txt

aaa
bbb