357

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

首先,我尝试了一个 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 

我对此感到困惑。

4

9 回答 9

592

#Getting 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}'

将 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}"


这里字符串

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

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

这与以下内容相同:

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

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


ENVIRON输入

正如 TrueY 所写,您可以使用ENVIRON打印Environment Variables。在运行 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 和变量的更多信息

阅读此常见问题解答

于 2013-09-29T07:51:52.967 回答
31

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

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt
于 2014-11-06T12:46:34.050 回答
14

根据您希望如何处理 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。上面的第一种方法几乎总是您的最佳选择,并且具有最明显的语义。

于 2013-09-29T13:22:50.273 回答
10

您可以使用变量名 ( ) 和环境变量 ( ) 的值 ( )传入命令行选项-vv="${v}"

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

或者让它更清楚(s 少得多v):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test
于 2013-09-29T07:49:33.183 回答
7

您可以使用 ARGV:

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

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

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"
于 2017-01-15T07:05:15.830 回答
1

我刚刚更改了@Jotne 对“for loop”的回答。

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done
于 2018-04-10T07:34:20.353 回答
0

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

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

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

于 2016-03-02T08:07:01.883 回答
0

专家提示

创建一个处理此问题的函数可能会派上用场,这样您就不必每次都键入所有内容。使用我们得到的选定解决方案...

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
于 2021-04-18T06:24:18.273 回答
0

例子:

在.txt:

foo
bar

多变的:

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

命令:

echo -e "$var" > out.txt

出.txt

foo
bar

其他:

在.txt

foo,aaa
bar,bbb

多变的:

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

出.txt

foo
bar

或者:

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

出.txt

aaa
bbb
于 2021-08-28T16:53:31.453 回答