awkで日付から誤った応答を受け取る

awkで日付から誤った応答を受け取る

インターフェイスの最後の入力と出力を表示するCiscoスイッチの出力を便利な形式に変換できるスクリプトを作成しようとしています。これは、たとえば、過去6ヶ月間、どのインターフェイスが使用されていないかを知ることができるようにするためです。シスコでは、インターフェイスへの最後の入力が10w4d

これまでに得たのは、次のような入力ファイルです。

FastEthernet0/4,3 days ago,3 days ago
FastEthernet0/8,46 years ago ,46 years ago 
FastEthernet0/10,46 years ago ,46 years ago 
FastEthernet0/11,46 years ago ,46 years ago 
FastEthernet0/12,08:47:53,04:00:32
FastEthernet0/13,1 year ago 46 weeks ago ,1 year ago 46 weeks ago 
FastEthernet0/14,46 years ago ,46 years ago 
FastEthernet0/15,13 weeks ago 4 days ago ,13 weeks ago 4 days ago 
FastEthernet0/16,46 years ago ,46 years ago 
FastEthernet0/18,46 years ago ,46 years ago 
FastEthernet0/19,46 years ago ,46 years ago 
FastEthernet0/20,46 years ago ,46 years ago 
FastEthernet0/22,46 years ago ,1 year ago 50 weeks ago 
FastEthernet0/24,46 years ago ,46 years ago

有効な日付を取得できるように、Never Last Input and Output状態のすべてのインターフェイスを46年前に設定しました。はい、これを考慮できる稼働時間の記録があります。

私の問題は、現在有効な日付に変換しようとしていることです。これはawk正常に動作します。xargs

awk -F, '{print $2}' tst | xargs -i date -d "{}" +%Y-%m-%d

私は次のような答えを受けました。

2016-08-18
1970-08-21
1970-08-21
1970-08-21
2016-08-21
2014-10-03
1970-08-21
2016-05-18
1970-08-21
1970-08-21
1970-08-21
1970-08-21
1970-08-21
1970-08-21

しかし、これを行うと、次のような結果が出るようです。

awk -F, -v OFS="," '{
    cmd2=("date \"+'%Y-%m-%d'\" -d \""$2 "\"")
    cmd2|getline d2
    print cmd2,d2
}' tst

私は次のような答えを受けました。

date "+%Y-%m-%d" -d "3 days ago",2016-08-18
date "+%Y-%m-%d" -d "46 years ago ",1970-08-21
date "+%Y-%m-%d" -d "46 years ago ",1970-08-21
date "+%Y-%m-%d" -d "46 years ago ",1970-08-21
date "+%Y-%m-%d" -d "08:47:53",2016-08-21
date "+%Y-%m-%d" -d "1 year ago 46 weeks ago ",2014-10-03
date "+%Y-%m-%d" -d "46 years ago ",2014-10-03
date "+%Y-%m-%d" -d "13 weeks ago 4 days ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18
date "+%Y-%m-%d" -d "46 years ago ",2016-05-18

cmd2ちょうどコマンドに追加して、何をするのかを確認しました。printこのコマンドを実行すると、正しい日付が表示されます。

簡単な内容が欠けているようですが、3〜4時間繰り返して試した結果、もう一度見てみたいです。

誰かが私に正しい方向を教えてもらえますか?

書き直す! Stephenも指摘したように、入力ファイルの3行だけで問題はありませんでした。テストされていないシナリオを投稿して申し訳ありません。

答え1

これを行うとき:

cmd = "some command"
cmd | getline d2
cmd | getline d2

awk最初のものはgetline出力の最初の行(レコード)を取得し、2番目は2番目の行を取得します。出力の終わりに達するとゼロが返されます。cmdcmdgetlinegetline

あなたの場合、cmd = "date -d \"46 years ago\""コマンドは1行だけを出力するので、最初のコマンドはその行と正数をgetline返しますが、2番目のコマンドは到着するので0を返し、変更されません。d2eofd2

これには次のものが必要です。

 cmd | getline d2; close(cmd)

だからcmd毎回実行する必要があります。

または、同じパラメータで同じコマンドを複数回実行しないように、出力をハッシュに保存しますcmd

if (!($2 in cache)) {cmd=...; cmd | getline cache[$2]; close(cmd)}
print cache[$2]

cmd多くのファイル記述子を開かない場合は、その場所を閉じることをお勧めします。


close(cmd)when のあいまいさは とcmdに両方適用されます。閉じるかどうかgetline < cmdcmd | getlinecmd 注文するまたはcmd 文書それとも両方?

$ cat test.awk
BEGIN{
  OFS=","
  getline a1 < "uname"
  "uname" | getline b1
  close("uname")

  getline a2 < "uname"
  "uname" | getline b2
  print a1,b1,a2,b2
}
$ echo test > uname
$ mawk -f test.awk
test,Linux,test,Linux
$ bwk-awk -f test.awk
test,Linux,test,Linux
$ gawk -f test.awk
test,Linux,,Linux
$ busybox awk -f test.awk
test,,test,

(Brian Kernighan(at)が管理し、bwk-awkSolarisやFreeBSDなどに基づくawkの実装と同様に動作します。)kawknawkawk

"uname"busybox awkを使用してファイルとコマンドを同時に使用できない方法と閉じないgetline方法を学びます。close("uname")uname 文書存在するgawk

したがって、同じ名前のファイルとコマンドを同時に使用しないことをお勧めします。"\n"開始または終了に追加できます。注文するに関連する可能性を下げること文書

良い:

 awk 'BEGIN {getline foo < $ENVIRON["FILE"]; "uname\n" | getline system}'

$FILEもしそうなら、この問題は避けることができるでしょうuname$FILEそうではないかもしれませんが、uname<newline>可能性は低いです)。

答え2

興味深い点は、この構造体で以前にこのawk呼び出しを行ったことを覚えていて、やり直さなかったため、データがreadline

私たちはこれを行うことでこれを見ることができますstrace -f -o xxx awk ....

追跡により、以下を確認できます。

3401  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "3 days ago"], [/* 50 vars */]) = 0
3403  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "46 years ago "], [/* 50 vars */]) = 0
3405  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "08:47:53"], [/* 50 vars */]) = 0
3407  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "1 year ago 46 weeks ago "], [/* 50 vars */]) = 0
3409  execve("/bin/date", ["date", "+%Y-%m-%d", "-d", "13 weeks ago 4 days ago "], [/* 50 vars */]) = 0

したがって、各一意の日付は一度だけ渡されますが、再び渡​​されません。これはCentOS 7とDebianのGNU awkで発生するため、mawkこれは予想される動作のようです。

簡単な方法は、各行を一意にすることです。たとえば、行番号でコメントを追加します。

cmd2="date \"+'%Y-%m-%d'\" -d \""$2 "\" ; # " NR 

生成されたコードは次のとおりです。

awk -F, -v OFS="," '{cmd2="date \"+'%Y-%m-%d'\" -d \""$2 "\" ; # " NR ;
                     cmd2|getline d2;
                     print cmd2,d2}' x
date "+%Y-%m-%d" -d "3 days ago" ; # 1,2016-08-18
date "+%Y-%m-%d" -d "46 years ago " ; # 2,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 3,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 4,1970-08-21
date "+%Y-%m-%d" -d "08:47:53" ; # 5,2016-08-21
date "+%Y-%m-%d" -d "1 year ago 46 weeks ago " ; # 6,2014-10-03
date "+%Y-%m-%d" -d "46 years ago " ; # 7,1970-08-21
date "+%Y-%m-%d" -d "13 weeks ago 4 days ago " ; # 8,2016-05-18
date "+%Y-%m-%d" -d "46 years ago " ; # 9,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 10,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 11,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 12,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 13,1970-08-21
date "+%Y-%m-%d" -d "46 years ago " ; # 14,1970-08-21

より複雑な解決策は、結果を配列にキャッシュし、date以前にこの日付を見たことがない場合にのみ呼び出すことです。

答え3

2列と3列の相対日付を実際の日付に置き換えます。

awk -F, '{for(i=2;i<4;i++){"date -I -d \""$i"\"" | getline a; $i=a}}1' OFS=, tst

直接動作しないという事実に驚きましたgetline $i。両方のフィールドのいずれかを変更したり、何も変更しません。したがって、a
定義されたユーザー関数に追加の変数を含める必要があります。

awk -F, '
    function cmd2(date){
        "date -I -d \""date"\"" | getline a
        return a
    }
    {
        $2=cmd2($2)
        $3=cmd2($3)
        print
    }' OFS=, tst

ただし、関数がローカル変数として定義されている場合は、上記とcmd2(date, a)同じ動作を示します。

均質なライン操作を簡単に完了できます。GNU sed

tr ',' '\n' tst |
sed '1~3!{s/^/date -I -d "/;s/$/"/e;}' |
paste -sd ',,\n'

関連情報