从 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_linesFNR 值。

我尝试了一些使用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 &lt;&lt;&lt; $(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 &lt; filename 获取您的计数。

【讨论】:

第二个和第三个选项是我试图避免的,但我并没有真正从第一个中得到这个想法。我在第三个中使用的第一个技术不是完全一样吗? 是的,FNR=$(awk 'END print FNR' filename)FNR=$(wc -l filename | awk 'print $1') 完全一样,除了一个计算行数的程序 -- awk/wc 你不会真的使用 wc+awk 的组合,但是你只需使用wc -l &lt;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 中设置变量的主要内容,如果未能解决你的问题,请参考以下文章

如何在fish shell中设置环境变量

如何在弹性豆茎中设置/更新 PATH 变量?

检查环境变量是不是在 Unix shell 脚本中设置的简洁方法是啥?

Day10.awk命令以及shell的脚本编写

LeetCode(Shell)- 194. 转置文件

LeetCode(Shell)- 194. 转置文件