3

正規表現で一致したファイルの文字列を、一致した文字列から生成/評価される別の文字列に置き換える方法を探しています。

たとえば、このファイルのタイムスタンプ (タイムスタンプ + 期間) を置き換えたい

1357222500 3600 ...
Maybe intermediate strings...
1357226100 3600 ...
Maybe intermediate strings...
...

人間が読める日付表現 (日付範囲)。

これまで、私は常に Bash のようなシェル スクリプトを使用して各行を繰り返し処理し、行 X を照合し、一致したグループ文字列を取得し、処理後に行を出力しました。たとえば、次のように (メモリから):

IFS="
"
for L in `cat file.txt`; do
  if [[ "${L}" =~ ^([0-9]{1,10})\ ([0-9]{1,4})\ .*$ ]]; then
    # Written as three lines for better readability/recognition
    echo -n "`date --date=@${BASH_REMATCH[1]}` - "
    echo -n "`date --date=@$(( ${BASH_REMATCH[1]} + ${BASH_REMATCH[2]} ))`"
    echo ""
  else
    echo "$L"
  fi
done

架空の(?)「sed-2.0」でこんなのがあるのか​​な。

cat file.txt | sed-2.0 's+/^\([0-9]\{1,10\}\) \([0-9]\{1,4\}\) .*$+`date --date="@\1"` - `date --date="@$(( \1 + \2 ))`'

一方、sed-2.0 置換のバッククォートは、一致したグループ\1とを渡すシェル コマンドとして評価されます\2

これが思い通りにいかないことはわかっていますが、このようなことを書きたいと思います。

編集 1

上記の質問の編集: Bash スクリプトの例の欠落echo ""を追加しました。if

これは予想される出力です。

Do 3. Jan 15:15:00 CET 2013 - Do 3. Jan 16:15:00 CET 2013
Maybe intermediate strings...
Do 3. Jan 16:15:00 CET 2013 - Do 3. Jan 17:15:00 CET 2013
Maybe intermediate strings...
...

タイムスタンプはタイムゾーンに依存することに注意してください。

編集 2

上記の質問の編集: Bash スクリプトの例の構文エラーを修正し、コメントを追加しました。

編集 3

上記の質問の編集: Bash スクリプトの例の構文エラーを修正しました。「昔ながらの例」というフレーズを「Bash スクリプトの例」に変更しました。


ケントさんとグレン・ジャックマンさんの回答まとめ

両方のアプローチには大きな違いがあります: 実行時間です。4つの方法すべてを比較しました。結果は次のとおりです。

gawk を使用してstrftime()

/usr/bin/time gawk '/^[0-9]+ [0-9]+ / {t1=$1; $1=strftime("%c -",t1); $2=strftime("%c",t1+$2)} 1' /tmp/test
...
0.06user 0.12system 0:00.30elapsed 60%CPU (0avgtext+0avgdata 1148maxresident)k
0inputs+0outputs (0major+327minor)pagefaults 0swaps

gawk による実行getline( Gnu AWK マニュアル)

/usr/bin/time gawk '/^[0-9]{1,10} [0-9]{1,4}/{l=$1+$2; "date --date=@"$1|getline d1; "date --date=@"l|getline d2;print d1" - "d2;next;}1' /tmp/test
...
1.89user 7.59system 0:10.34elapsed 91%CPU (0avgtext+0avgdata 5376maxresident)k
0inputs+0outputs (0major+557419minor)pagefaults 0swaps

カスタム Bash スクリプト

./sed-2.0.sh /tmp/test
...
3.98user 10.33system 0:15.41elapsed 92%CPU (0avgtext+0avgdata 1536maxresident)k
0inputs+0outputs (0major+759829minor)pagefaults 0swaps

eオプションを使用してsed

/usr/bin/time sed -r 's#^([0-9]{1,10}) ([0-9]{1,4})(.*$)#echo $(date --date=@\1 )" - "$(date --date=@$((\1+\2)))#ge' /tmp/test
...
3.88user 16.76system 0:21.89elapsed 94%CPU (0avgtext+0avgdata 1272maxresident)k
0inputs+0outputs (0major+1253409minor)pagefaults 0swaps

入力データ

for N in `seq 1 1000`; do echo -e "$(( 1357226100 + ( $N * 3600 ) )) 3600 ...\nSomething else ..." >> /tmp/test ; done

strffime()メソッドを使用した AWK が最も高速であることがわかります。しかし、Bash スクリプトでさえ、sedシェル実行よりも高速です。

Kentは、私が要求したことを達成するための、より一般的で普遍的な方法を示してくれました。私の質問は、実際にはタイムスタンプの例に限定されたものではありませんでした。この場合、正確にこれを行う必要がありました (タイムスタンプ + 期間を人間が読める日付表現に置き換えます) が、他のコードを実行しなければならない状況がありました。

glenn jackmanは、AWK で文字列操作と計算を直接実行できる状況に適した特定のソリューションを示してくれました。

そのため、どの方法を優先するかは、時間 (またはスクリプトを実行する時間)、データの量、およびユースケースによって異なります。

4

2 に答える 2

3

サンプル入力に基づいて:

gawk '/^[0-9]+ [0-9]+ / {t1=$1; $1=strftime("%c -",t1); $2=strftime("%c",t1+$2)} 1'

出力

Thu 03 Jan 2013 09:15:00 AM EST - Thu 03 Jan 2013 10:15:00 AM EST ...
Maybe intermediate strings...
Thu 03 Jan 2013 10:15:00 AM EST - Thu 03 Jan 2013 11:15:00 AM EST ...
Maybe intermediate strings...
...
于 2012-12-31T15:13:13.637 に答える
2

awk oneliner: (日時形式は出力とは異なる場合があります)

awk '/^[0-9]{1,10} [0-9]{1,4}/{l=$1+$2; "date --date=@"$1|getline d1; "date --date=@"l|getline d2;print d1" - "d2;next;}1' file

テスト:

kent$  echo "1357222500 3600 ...
Maybe intermediate strings...
1357226100 3600 ...
Maybe intermediate strings...
..."|awk '/^[0-9]{1,10} [0-9]{1,4}/{l=$1+$2; "date --date=@"$1|getline d1; "date --date=@"l|getline d2;print d1" - "d2;next;}1'    
Thu Jan  3 15:15:00 CET 2013 - Thu Jan  3 16:15:00 CET 2013
Maybe intermediate strings...
Thu Jan  3 15:15:00 CET 2013 - Thu Jan  3 17:15:00 CET 2013
Maybe intermediate strings...
...

ヌーセド

gnu sed を使用している場合、「機能していない」sed ラインからのアイデアは、gnu sed を適用することで現実の世界で機能する可能性がありs/foo/shell cmds/geます。以下を参照してください。

sed -r 's#^([0-9]{1,10}) ([0-9]{1,4})(.*$)#echo $(date --date=@\1 )" - "$(date --date=@$((\1+\2)))#ge'  file

テスト

kent$  echo "1357222500 3600 ...
Maybe intermediate strings...
1357226100 3600 ...
Maybe intermediate strings...
..."|sed -r 's#^([0-9]{1,10}) ([0-9]{1,4})(.*$)#echo $(date --date=@\1 )" - "$(date --date=@$((\1+\2)))#ge'                                                                 
Thu Jan 3 15:15:00 CET 2013 - Thu Jan 3 16:15:00 CET 2013
Maybe intermediate strings...
Thu Jan 3 16:15:00 CET 2013 - Thu Jan 3 17:15:00 CET 2013
Maybe intermediate strings...
...

私がこれに取り組むなら、個人的には awk を使います。簡潔で書きやすいからです。

最後に、sed/awk のバージョン情報を貼り付けます。

kent$  sed --version|head -1
sed (GNU sed) 4.2.2

kent$  awk -V|head -1
GNU Awk 4.0.1
于 2012-12-31T14:21:36.093 に答える