2013-01-24 63 views
13

有没有办法在awk以内的当前shell中设置变量?在awk的当前shell中设置变量

我想对一个文件进行一些处理,并打印出一些数据;因为我会读整个文件,所以我想节省行数 - 在这种情况下,FNR

偏偏虽然我似乎无法找到一种方法来设置与FNR值shell变量;如果不是这样的话,我必须从我的输出文件中读取FNR,以设置num_lines,其值为FNR

我一直在使用awk 'END{system(...)}'尝试了一些组合,但不能管理它的工作。任何方式在这个?

回答

16
$ echo "$var" 

$ declare $(awk 'BEGIN{print "var=17"}') 
$ echo "$var" 
17 

这也是为什么你应该使用声明的,而不是EVAL:

$ eval $(awk 'BEGIN{print "echo \"removing all of your files, ha ha ha....\""}') 
removing all of your files, ha ha ha.... 

$ declare $(awk 'BEGIN{print "echo \"removing all of your files\""}') 
bash: declare: `"removing': not a valid identifier 
bash: declare: `files"': not a valid identifier 

注意在EVAL执行的任何字符串AWK打印第一种情况下,这可能会意外地是一个非常糟糕的事情!

+0

+1,但只要是我编写代码,使用'eval'我就没有多大的问题了。 – Rubens

+10

@Rubens,直到你意外地做坏事的那一天。如果您现在习惯于编写安全代码,您将来不会被咬。 –

+0

@glennjackman +1感谢您的提示;我想这就是为什么在这里考虑安全编码的原因(: – Rubens

1

awk打印出来的赋值语句:

MYVAR=NewValue 

然后在你的shell脚本,evalawk脚本的输出:

eval $(awk ....) 
# then use $MYVAR 

编辑:人建议使用declare代替eval,如果内部脚本打印的是分配以外的内容,则稍微不太容易出错。它只是bash而已,但是当shell bash且脚本具有#!/bin/bash时,可以正确指出此依赖关系。

eval $(...)变体被广泛使用,现有的程序产生适合eval的输出,但不适用于declarelesspipe是一个例子);这就是为什么理解它很重要的原因,而且仅限bash的变体是“太本地化”的。

+0

这是唯一在技术上可能的答案 – anishsane

+0

@anishsane似乎是合理的,我(:!也想不到这一点,虽然谢谢,安东·科瓦连科 – Rubens

+0

我会用'declare',而不是'eval' – chepner

4

您无法将变量从子shell导出到其父shell。你有一些其他的选择,不过,包括:

  1. 请使用AWK来计算记录文件的另一个通,并用命令替换来捕获结果。例如:

    FNR=$(awk 'END {print FNR}' filename) 
    
  2. 打印FNR在子shell,并分析你的其他过程的输出。
  3. 如果FNR相同的行数,你可以调用wc -l < filename,让您的计数。
+0

第二和第三选项是我想要避免的,但是我没有真正从第一个选项中获得这个想法。是不是我在第三个中使用的第一个完全相同的技术? – Rubens

+0

Yep,'FNR = $(awk'END {print FNR}'filename)'和'FNR = $(wc -l filename | awk'{print $ 1}')'是完全相同的,除了一个程序计数行 - 'awk' /'wc'。 – Rubens

+3

你不会真的那样做wc + awk组合,不过,你只需要用'wc -l <​​filename'来代替。 –

14

这是另一种方法。

时,当你有值的变量的变量,你想拆起来这是非常有用的。例如,您可以从数据库中的单行中获取要创建变量的值列表。

val="hello|beautiful|world" # assume this string comes from a database query 
read a b c <<< $(echo ${val} | awk -F"|" '{print $1" "$2" "$3}') 

echo $a #hello 
echo $b #beautiful 
echo $C#world 

我们需要“这里串”在这种情况下,即< < <,因为读命令不会从管道中读取并从标准

+2

这正是我所需要的!我已经有了一个由空格分隔的字符串,所以我只是在输入就像在“read abc <<< $ var”中一样。谢谢 –

+0

<3 ..感谢。 – calbertts

+0

有一点需要提一下,如果字段之间用空格隔开,可以简化为: ''' read abc < << $(echo $ {val}) ''' –

3

警告任何人试图使用声明为而不是读由几个答案建议。

eval没有这个问题。

如果提供的awk(或其他表达式)声明结果为空字符串,则declare将转储当前环境。 这几乎肯定不是你想要的。

例如:如果您的awk模式不存在于输入中,您将永远不会打印输出,因此最终会出现意外的行为。

这样的一个例子....

unset var 
var=99 
declare $(echo "foobar" | awk '/fail/ {print "var=17"}') 
echo "var=$var" 
var=99 
The current environment as seen by declare is printed 
and $var is not changed 

一个小的改动,以存储该值在AWK变量来设置并在结束打印解决了这个....

unset var 
var=99 
declare $(echo "foobar" | awk '/fail/ {tmp="17"} END {print "var="tmp}') 
echo "var=$var" 
var= 
This time $var is unset ie: set to the null string var='' 
and there is no unwanted output. 

要显示有匹配模式

unset var 
var=99 
declare $(echo "foobar" | awk '/foo/ {tmp="17"} END {print "var="tmp}') 
echo "var=$var" 
var= 
This time $var is unset ie: set to the null string var='' 
and there is no unwanted output. 
1

要合成这里的一切这方面的工作,到目前为止,我会分享我觉得是非常有用的从使用awk读取单行文件的脚本设置shell环境变量。显然/pattern/可以用来代替NR==1找到需要的变量。

# export a variable from a script (such as in a .dotfile) 
declare $(awk 'NR==1 {tmp=$1} END {print "SHELL_VAR=" tmp}' /path/to/file) 
export SHELL_VAR 

这将避免变量的大量输出,如果declare命令不带参数,还有的盲eval的安全风险发出。