从 awk 在当前 shell 中设置变量
Posted
技术标签:
【中文标题】从 awk 在当前 shell 中设置变量【英文标题】:Set variable in current shell from awk 【发布时间】:2013-01-08 10:14:00 【问题描述】:有没有办法在我当前的 shell 中从 awk
中设置一个变量?
我想对文件进行一些处理并打印出一些数据;因为我将通读整个文件,所以我想保存行数——在本例中为FNR
。
虽然我似乎找不到设置带有FNR
值的shell 变量的方法,但还是会发生;如果不是这样,我必须从我的输出文件中读取FNR
,以设置num_lines
和FNR
值。
我尝试了一些使用awk 'ENDsystem(...)'
的组合,但无法使其正常工作。有什么办法吗?
【问题讨论】:
【参考方案1】:这是另一种方式。
当您将变量的值放在一个单个变量中并希望将它们拆分时,这尤其有用。例如,您有一个来自数据库中单行的值列表,您想从中创建变量。
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
我们需要'这里的字符串',即
【讨论】:
这正是我所需要的!我已经有一个字符串用空格分隔的 var,所以我只是在输入中给出了它(比如“read a b c 有一点要提一下,如果字段用空格分隔,这可以简化为: `` read a b c 相反,如果您的一个字段中有空格,这将不起作用。您最终会将输出错误地划分为变量,并将“runoff”读入最后一个变量,包括空格。 其实里面有空格的情况下,将字段读入变量是没有问题的。如果包含空格的字段被另一个分隔符分隔,您可以像这样设置 IFS(以及 awk 分隔符)(这里逗号是分隔符):IFS=',' read aa bb <<< $(echo $line | awk -F',' 'print $3","$4')
如果只有空格,则只取所需的空格数字。【参考方案2】:
$ echo "$var"
$ declare $( awk 'BEGINprint "var=17"' )
$ echo "$var"
17
这就是为什么你应该使用 declare 而不是 eval:
$ eval $( awk 'BEGINprint "echo \"removing all of your files, ha ha ha....\""' )
removing all of your files, ha ha ha....
$ declare $( awk 'BEGINprint "echo \"removing all of your files\""' )
bash: declare: `"removing': not a valid identifier
bash: declare: `files"': not a valid identifier
请注意,在第一种情况下,eval 会执行 awk 打印的任何字符串,这可能会意外地成为一件非常糟糕的事情!
【讨论】:
+1 表示推荐,但只要是我编写代码,我认为使用eval
没有太大问题。
@Rubens,直到你不小心做了坏事的那一天。如果你现在养成编写安全代码的习惯,以后就不会被咬了。
@glennjackman +1 感谢您的提示;我想这就是为什么在这里如此考虑安全编码的原因(:
我从经验中讲... :(【参考方案3】:
您不能将变量从子 shell 导出到其父 shell。不过,您还有其他一些选择,包括:
使用 AWK 对文件进行另一次传递以计算记录,并使用命令替换来捕获结果。例如:
FNR=$(awk 'END print FNR' filename)
在子shell中打印FNR,并在你的其他进程中解析输出。
如果 FNR 与行数相同,您可以致电wc -l < filename
获取您的计数。
【讨论】:
第二个和第三个选项是我试图避免的,但我并没有真正从第一个中得到这个想法。我在第三个中使用的第一个技术不是完全一样吗? 是的,FNR=$(awk 'END print FNR' filename)
和 FNR=$(wc -l filename | awk 'print $1')
完全一样,除了一个计算行数的程序 -- awk
/wc
。
你不会真的使用 wc+awk 的组合,但是你只需使用wc -l <filename
。【参考方案4】:
对尝试使用 declare 的人的警告,如几个答案所建议的那样。
eval没有这个问题。
如果提供给声明的 awk(或其他表达式)导致空字符串,则声明将转储当前环境。 这几乎肯定不是您想要的。
例如:如果您的 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.
【讨论】:
【参考方案5】:让awk
打印出赋值语句:
MYVAR=NewValue
然后在你的 shell 脚本中,eval
你的awk
脚本的输出:
eval $(awk ....)
# then use $MYVAR
编辑:人们建议使用declare
而不是eval
,如果内部脚本打印了除分配之外的其他内容,则更不容易出错。它是 bash-only,但是当 shell 是 bash 并且脚本有 #!/bin/bash
,正确地说明这种依赖关系时,没关系。
eval $(...)
变体被广泛使用,现有程序生成适合eval
但不适用于declare
的输出(lesspipe
是一个示例);这就是理解它很重要的原因,而仅 bash 的变体“过于本地化”。
【讨论】:
这是技术上唯一可行的答案 @anishsane 对我来说似乎是合理的(:但我想不出来。谢谢,Anton Kovalenko! 我会使用declare
而不是eval
。
@anishsane 等人 - 不,这是错误的答案。使用 declare
而不是 eval。
@chepner declare
仅限 bash,这不一定是坏事,但这就是我默认不使用它的原因。【参考方案6】:
为了综合到目前为止的所有内容,我将分享我发现从使用 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
的安全风险。
【讨论】:
【参考方案7】:echo "第一个参数:$1" 对于 ((i=0 ; i $i.xml 完毕 echo "完成"
【讨论】:
你能正确地格式化你的代码并添加一些解释以便其他人可以学习吗?以上是关于从 awk 在当前 shell 中设置变量的主要内容,如果未能解决你的问题,请参考以下文章