【问题标题】:Set variable in current shell from awk从 awk 在当前 shell 中设置变量
【发布时间】:2013-01-08 10:14:00
【问题描述】:

有没有办法在我当前的 shell 中从 awk 中设置一个变量?

我想对文件进行一些处理并打印出一些数据;因为我将通读整个文件,所以我想保存行数——在本例中为FNR

虽然我似乎找不到设置带有FNR 值的shell 变量的方法,但还是会发生;如果不是这样,我必须从我的输出文件中读取FNR,以设置num_linesFNR 值。

我尝试了一些使用awk 'END{system(...)}' 的组合,但无法使其正常工作。有什么办法吗?

【问题讨论】:

    标签: bash variables awk


    【解决方案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 'BEGIN{print "var=17"}' )
    $ echo "$var"
    17
    

    这就是为什么你应该使用 declare 而不是 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 打印的任何字符串,这可能会意外地成为一件非常糟糕的事情!

    【讨论】:

    • +1 表示推荐,但只要是我编写代码,我认为使用 eval 没有太大问题。
    • @Rubens,直到你不小心做了坏事的那一天。如果你现在养成编写安全代码的习惯,以后就不会被咬了。
    • @glennjackman +1 感谢您的提示;我想这就是为什么在这里如此考虑安全编码的原因(:
    • 我从经验中讲... :(
    【解决方案3】:

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

    1. 使用 AWK 对文件进行另一次传递以计算记录,并使用命令替换来捕获结果。例如:

      FNR=$(awk 'END {print FNR}' filename)
      
    2. 在子shell中打印FNR,并在你的其他进程中解析输出。
    3. 如果 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 "完成"

        【讨论】:

        • 你能正确地格式化你的代码并添加一些解释以便其他人可以学习吗?
        猜你喜欢
        • 2012-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-20
        • 1970-01-01
        • 2023-03-30
        • 2012-08-12
        • 2023-01-13
        相关资源
        最近更新 更多