正規表現で一致したファイルの文字列を、一致した文字列から生成/評価される別の文字列に置き換える方法を探しています。
たとえば、このファイルのタイムスタンプ (タイムスタンプ + 期間) を置き換えたい
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 で文字列操作と計算を直接実行できる状況に適した特定のソリューションを示してくれました。
そのため、どの方法を優先するかは、時間 (またはスクリプトを実行する時間)、データの量、およびユースケースによって異なります。