awk支持这个:
awk '{print $(NF-1);}'
但不适用于用户定义的变量:
awk '{a=123; b="a"; print $($b);}'
顺便说一句,shell支持这个:
a=123;
b="a";
eval echo \${$b};
如何在awk中实现我的目的?
答案 0 :(得分:6)
好的,因为我们中的一些人喜欢通过他们的鼻子吃意大利面,这里是我过去写的一些实际代码:-)
首先,用不支持它的语言获取自修改代码将非常重要。
允许使用不支持动态变量,函数名称的语言的想法非常简单。在程序中的某个状态下,您需要动态任何内容来自行修改代码并恢复执行
从你离开的地方。一个eval()
,即。
如果语言支持eval()
和这样的等级,这一切都非常简单。但是,awk没有这样的功能。因此,你,程序员必须提供这样的接口。
为了实现这一切,您有三个主要问题
这是一个适合直接执行的示例代码。 这是我为运行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()就是这样。
有两个问题。
如果您必须使用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
}