awkはこれをサポートしています:
awk '{print $(NF-1);}'
ただし、ユーザー定義変数の場合は除きます。
awk '{a=123; b="a"; print $($b);}'
ちなみに、シェルはこれをサポートしています:
a=123;
b="a";
eval echo \${$b};
どうすればawkで目的を達成できますか?
OK、私たちの中には鼻からスパゲッティを食べるのが好きな人もいるので、これは私が過去に書いた実際の
コードです:-)
まず、それをサポートしない言語で自己修正コードを取得することは非常に非些細なことです。
動的変数、関数名をサポートしていない言語で許可するという考え方は非常に単純です。プログラムのある状態で、動的なものでコードを自己修正し、中断したところから実行を再開する必要があります。、eval()
つまり。
言語がサポートしている場合、これはすべて非常に簡単eval()
です。ただし、awkにはそのような機能はありません。したがって、プログラマーはそのようなものへのインターフェースを提供する必要があります。
これをすべて可能にするには、3つの主な問題があります
直接実行に適したサンプルコードを次に示します。これは、PROCINFOが必要なため、gawkを実行している環境に注入する不安定なものです。
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
ご覧のとおり、OSが引数で再生されず、/proc/
使用可能である限り、自己を読み取ることができます。これは最初は役に立たないように見えるかもしれませんが、スタックのプッシュ/ポップに必要です。これにより、実行状態をコードに埋め込むことができ、OSのシャットダウン/再起動を保存/再開して存続させることができます。
OS検出機能とブートローダー(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"}
コードの最後に
print "foo";
コードをpidに置き換えるだけなので、これは役に立たないように見えるかもしれません。しかし、ブロックを識別するために別々のMAGIC文字列を持つ複数の_()があり、カスタムが代わりに複数行の文字列置換ルーチンを作成した場合に便利になりますsub()
最低限、スタック、関数リスト、実行ポイントのブロックを提供します。
そして、最後の行にbar
これが含まれていることに注意してください。これ自体は単なる刺し傷ですが、このコードが繰り返し実行される場合は、次のことに注意してください。
function bar_56228_(s){print "doe"}
function bar_88128_(s){print "doe"}
...
そしてそれは成長し続けます。この例は、何も役に立たないように意図的に作成されていますが、そのコードbar_pid_(s)
の代わりに呼び出すルーチンを提供すると、突然、手元にあることを意味します:-)さて、eval()は役に立ちません:-)print "foo"
eval()
実行するたびにコードが大きくなるのではなく、コードが適切なサイズを維持できるように、カスタムメイドのremove_block()関数を提供することを忘れないでください。
通常、バイナリを呼び出すのは簡単です。ただし、awkでwithからそうする場合は、難しくなります。system()がその方法だと言うかもしれません。
それには2つの問題があります。
を使用する必要が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";}'
これで、ブロックしない非同期のsystem()呼び出しができました:-)
現時点ではない。ただし、ラッパーを提供すると、(ややハッキーで汚い)可能性があります。アイデアは、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"
}
さて、これはそれ自体で便利です。 事前に変数名を知っていると仮定すると、間接的に変数を変更して取得するラッパーを作成できます
function get(varname, this, call){call="get_"varname;return @call();}
function set(varname, arg, this, call){call="set_"varname; @call(arg);}
したがって、名前でアクセスを許可する変数名ごとに、これら2つの関数を宣言します。
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");
これは最初は役に立たないように見えるかもしれませんが、変数のリンクリストを保持したり、別の変数の配列に変数の名前を保持したりするなど、完全に良いユースケースがあります。これは配列でも機能します。正直なところ、これは配列内の配列のネストとリンクに使用するため、ポインターを使用するように複数の配列をウォークスルーできます。
この方法でawk内の変数名を参照するconfigureスクリプトを作成することもできます。これにより、実際には、インタープリター内部のインタープリタータイプのものも使用できます...
物事を行うための最良の方法ではありませんが、それは仕事を成し遂げます、そして私はnullポインター例外、またはGCなどについて心配する必要はありません:-)
$
表記は、シェル、PHP、Perlなどのように、変数のマークではありません。整数値nを受け取り、入力からn番目の列を返す演算子です。したがって、最初の例で行ったことは、変数の動的な設定/取得ではなく、演算子/関数の呼び出しです。
コメント投稿者が述べているように、配列を使用して探している動作をアーカイブできます。
awk '{a=123; b="a"; v[b] = a; print v[b];}'
「.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
}