awk是否支持动态的用户定义变量?

时间:2012-08-09 09:46:14

标签: bash dynamic awk

awk支持这个:

awk '{print $(NF-1);}'

但不适用于用户定义的变量:

awk '{a=123; b="a"; print $($b);}'
顺便说一句,shell支持这个:

a=123;
b="a";
eval echo \${$b};

如何在awk中实现我的目的?

4 个答案:

答案 0 :(得分:6)

好的,因为我们中的一些人喜欢通过他们的鼻子吃意大利面,这里是我过去写的一些实际代码:-)
首先,用不支持它的语言获取自修改代码将非常重要。

允许使用不支持动态变量,函数名称的语言的想法非常简单。在程序中的某个状态下,您需要动态任何内容来自行修改代码并恢复执行 从你离开的地方。一个eval(),即。

如果语言支持eval()和这样的等级,这一切都非常简单。但是,awk没有这样的功能。因此,你,程序员必须提供这样的接口。

为了实现这一切,您有三个主要问题

  1. 如何让我们自己,以便我们可以修改它
  2. 如何加载修改后的代码,并从我们中断的地方继续
  3. 找到解释器接受我们修改过的代码的方法
  4. 如何获得我们的自我,以便我们可以修改它

    这是一个适合直接执行的示例代码。 这是我为运行gawk的环境注入的基础设施,因为它需要PROCINFO

    echo ""| awk '
    function push(d){stack[stack[0]+=1]=d;}
    function pop(){if(stack[0])return stack[stack[0]--];return "";}
    function dbg_printarray(ary , x , s,e, this , i ){
     x=(x=="")?"A":x;for(i=((s)?s:1);i<=((e)?e:ary[0]);i++){print x"["i"]=["ary[i]"]"}}
    function dbg_argv(A ,this,p){
     A[0]=0;p="/proc/"PROCINFO["pid"]"/cmdline";push(RS);RS=sprintf("%c",0);
     while((getline v <p)>0)A[A[0]+=1]=v;RS=pop();close(p);}
    {
        print "foo";
        dbg_argv(A);
        dbg_printarray(A);
        print "bar";
    }'
    

    结果:

    foo
    A[1]=[awk]
    A[2]=[
    function push(d){stack[stack[0]+=1]=d;}
    function pop(){if(stack[0])return stack[stack[0]--];return "";}
    function dbg_printarray(ary , x , s,e, this , i ){
     x=(x=="")?"A":x;for(i=((s)?s:1);i<=((e)?e:ary[0]);i++){print x"["i"]=["ary[i]"]"}}
    function dbg_argv(A ,this,p){
     A[0]=0;p="/proc/"PROCINFO["pid"]"/cmdline";push(RS);RS=sprintf("%c",0);
     while((getline v <p)>0)A[A[0]+=1]=v;RS=pop();close(p);}
    {
    print "foo";
    dbg_argv(A);
    dbg_printarray(A);
    print "bar";
    }]
    bar
    

    正如您所看到的,只要操作系统不能使用我们的args,并且/proc/可用,就有可能 读我们自己。这可能一开始看起来没用,但我们需要它用于我们堆栈的推送/弹出, 这样我们的执行状态就可以嵌入代码中了,所以我们可以保存/恢复并在操作系统关闭/重启后继续存在

    我遗漏了操作系统检测功能和引导程序(用awk编写),因为如果我发布它, 孩子们可以构建平台独立的多线程代码,很容易造成破坏。

    如何加载修改后的代码,并从我们中断的地方继续

    现在,通常你有push()pop()注册,所以你可以保存自己的状态并玩 你的自我,从你离开的地方恢复。调用和读取堆栈是一种典型的方法 记忆地址。

    不幸的是,在awk中,在正常情况下我们不能使用指针(没有大量的脏工作), 或注册(除非你可以注入其他东西)。 但是,您需要一种方法来暂停和恢复代码。

    这个想法很简单。而不是让awk控制你的循环,而不管其他条件, 您应该使用放卷深度和功能。 保留堆栈,变量名称列表,函数名称列表,并自行管理。 只需确保您的代码始终始终调用self_modify( bool ),即使突然失败, 重新运行脚本后,我们可以输入self_modify( bool )并恢复我们的状态。 如果要自行修改代码,则必须提供自定义代码 write_stack()read_stack()代码,它将堆栈状态写为字符串,并从中读取字符串 从代码嵌入字符串本身输出的值,并恢复执行状态。

    这是一小段代码,演示了整个流程

    echo ""| awk '
    function push(d){stack[stack[0]+=1]=d;}
    function pop(){if(stack[0])return stack[stack[0]--];return "";}
    function dbg_printarray(ary , x , s,e, this , i ){
     x=(x=="")?"A":x;for(i=((s)?s:1);i<=((e)?e:ary[0]);i++){print x"["i"]=["ary[i]"]"}}
    function _(s){return s}
    function dbg_argv(A ,this,p){
     A[0]=0;p="/proc/"PROCINFO["pid"]"/cmdline";push(RS);RS=sprintf("%c",0);
     while((getline v <p)>0)A[A[0]+=1]=v;RS=pop();close(p);}
    {
        _(BEGIN_MODIFY"|");print "#foo";_("|"END_MODIFY)
        dbg_argv(A);
        sub( \
        "BEGIN_MODIFY\x22\x5c\x7c[^\x5c\x7c]*\x5c\x7c\x22""END_MODIFY", \
        "BEGIN_MODIFY\x22\x7c\x22);print \"#"PROCINFO["pid"]"\";_(\x22\x7c\x22""END_MODIFY" \
         ,A[2]) 
        print "echo \x22\x22\x7c awk \x27"A[2]"";
        print "function bar_"PROCINFO["pid"]"_(s){print \x22""doe\x22}";
        print "\x27"
    }'
    

    结果:

    与原始代码完全相同,但

    除外
    _(BEGIN_MODIFY"|");print "65964";_("|"ND_MODIFY)
    

    function bar_56228_(s){print "doe"}
    
    代码末尾的

    现在,这似乎毫无用处,因为我们只用我们的pid替换代码print "foo";。 但是当有多个_()带有单独的MAGIC字符串来识别BLOCKS时,它变得有用了, 并且客户制作了多行字符串替换例程而不是sub()

    你应该为堆栈,函数列表,执行点提供BLOCKS,作为最低限度。

    注意最后一行包含bar 这只是一个刺痛,但是当这个代码反复执行时,请注意

    function bar_56228_(s){print "doe"}
    function bar_88128_(s){print "doe"}
    ...
    

    它不断增长。虽然这个例子是故意制作的,所以没有任何用处, 如果我们提供例程来调用bar_pid_(s)而不是print "foo"代码, Sudenly 这意味着我们手上有eval() :-) 现在,不是eval()有用的: - )

    不要忘记提供一个客户自己的remove_block()函数,以便代码维护 合理的大小,而不是每次执行时增长。

    找到解释器接受我们修改过的代码的方法

    通常调用二进制文件很简单。然而,当在awk中这样做时,变得困难。 你可能会说system()就是这样。

    有两个问题。

    1. system()可能无法在某些环境中使用
    2. 在您执行代码时阻塞,trus您无法执行重复呼叫并让用户同时感到高兴。
    3. 如果您必须使用system(),请确保它不会阻止。 正常调用system("sleep 20 && echo from-sh & ")将无法正常工作。 解决方案很简单,

      echo ""|awk '{print "foo";E="echo ep ; sleep 20 && echo foo & disown ; ";  E | getline v;close(E);print "bar";}'
      

      现在您有一个不阻止的异步系统()调用: - )

答案 1 :(得分:4)

目前不是。但是,如果你提供一个包装器,它可能(有点hacky和脏)。 我们的想法是使用最近版本的gawk中引入的@运算符。

此@运算符通常用于按名称调用函数。 所以,如果你有

function foo(s){print "Called foo "s}
function bar(s){print "Called bar "s}
{
    var = "";
    if(today_i_feel_like_calling_foo){
        var = "foo";
    }else{
        var = "bar";
    }
    @var( "arg" ); # This calls function foo(), or function bar() with "arg"
}

现在,这对它自己有用。 假设我们事先知道var名称,我们可以编写一个包装来间接修改和获取vars

function get(varname, this, call){call="get_"varname;return @call();}
function set(varname, arg, this, call){call="set_"varname; @call(arg);}

现在,对于每个要通过名称进行访问的var名称,您可以声明这两个函数

function get_my_var(){return my_var;}
function set_my_var(arg){my_var = arg;}

并且,在你的BEGIN {}街区的某个地方,或许,或

BEGIN{ my_var = ""; }

将其声明为全局访问权限。 然后你可以使用

get("my_var");
set("my_var", "whatever");

这一开始可能看起来毫无用处,但是有很好的用例,例如 通过将var的名称保存在另一个var的数组中来保持变量的链表,等等。 它也适用于数组,说实话,我用它来嵌套和链接数组 数组,所以我可以像使用指针一样遍历多个数组。

你也可以用这种方式编写在awk中引用var名称的配置脚本, 实际上有一个解释器里面的翻译类型的东西......

不是最好的做事方式,但它完成了工作,我不必担心 空指针异常,或GC等: - )

答案 2 :(得分:1)

$符号不是变量的标记,如shell,PHP,Perl等。它是一个运算符,它接收整数 n 并从输入返回第n列。因此,您在第一个示例中所做的不是动态设置/获取变量,而是调用操作符/函数。

如评论者所述,您可以使用数组存档您要查找的行为:

awk '{a=123; b="a"; v[b] = a; print v[b];}'

答案 3 :(得分:0)

我有一个类似的问题需要解决,从“.ini”文件加载设置,我使用数组动态设置变量。

适用于 Awk 或 Gawk、Linux 或 Windows (GnuWin32)

gawk -v Settings_File="my_settings_file.ini" -f awk_script.awk <processing_file>

[my_settings_file.ini]

#comment
first_var=foo
second_var=bar

[awk_script.awk]

BEGIN{
    FS="=";
    while((getline < Settings_File)>0) {
        if($0 !~ /^[#;]|^(\s*)$/) {
            var_array[$1] = $2;
        }
    }

    print var_array["first_var"];
    print var_array["second_var"];

    if (var_array["second_var"] == "bar") {
        print "works!";
    }
}
{
#more processing
}
END {
#finish processing
}