人が読める形式に変換する必要があるエポック日付を含むファイルがあります。私はすでに日付変換を行う方法を知っています、例えば:
[server01 ~]$ date -d@1472200700
Fri 26 Aug 09:38:20 BST 2016
..しかし、sed
がファイルをウォークスルーしてすべてのエントリを変換する方法を理解するのに苦労しています。ファイル形式は次のようになります。
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
bash
を使用すると、一貫したファイル形式を想定して、ファイルを1行ずつ読み取り、指定された形式かどうかをテストしてから、変換を実行できます。
while IFS= read -r i; do [[ $i =~ ^#([0-9]{10})$ ]] && \
date -d@"${BASH_REMATCH[1]}"; done <file.txt
BASH_REMATCH
は、最初の要素が正規表現一致で最初にキャプチャされたグループである配列=~
、この場合はエポック。
ファイル構造を保持したい場合:
while IFS= read -r i; do if [[ $i =~ ^#([0-9]{10})$ ]]; then printf '#%s\n' \
"$(date -d@"${BASH_REMATCH[1]}")"; else printf '%s\n' "$i"; fi; done <file.txt
これにより、変更された内容がSTDOUTに出力され、ファイルに保存されます。 out.txt
:
while ...; do ...; done >out.txt
必要に応じて、元のファイルを置き換えることができます。
mv out.txt file.txt
例:
$ cat file.txt
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
$ while IFS= read -r i; do [[ $i =~ ^#([0-9]{10})$ ]] && date -d@"${BASH_REMATCH[1]}"; done <file.txt
Wed Aug 24 20:09:55 BDT 2016
Wed Aug 24 20:11:46 BDT 2016
Wed Aug 24 20:13:58 BDT 2016
$ while IFS= read -r i; do if [[ $i =~ ^#([0-9]{10})$ ]]; then printf '#%s\n' "$(date -d@"${BASH_REMATCH[1]}")"; else printf '%s\n' "$i"; fi; done <file.txt
#Wed Aug 24 20:09:55 BDT 2016
ll /data/holding/email
#Wed Aug 24 20:11:46 BDT 2016
cat /etc/rsyslog.conf
#Wed Aug 24 20:13:58 BDT 2016
ll /data/holding/web
GNU sed
では次のようなことが可能ですが、
_sed -E 's/^#([0-9]+).*$/date -d @\1/e'
_
それはひどく非効率的です(そして任意のコマンドインジェクションの脆弱性を導入するのは簡単です)1)つまり、_#xxxx
_行ごとに1つのシェルと1つのdate
コマンドを実行し、実質的に シェルとして悪い_while read
_ループ を実行します。ここでは、Perl
やgawk
のようなものを使用する方が良いでしょう。これは、日付変換機能が組み込まれたテキスト処理ユーティリティです。
_Perl -MPOSIX -pe 's/^#(\d+).*/ctime $1/se'
_
または:
_gawk '/^#/{$0 = strftime("%c", substr($0, 2))};1'
_
1^#([0-9]).*
の代わりに^#([0-9]).*$
を記述した場合(この回答の以前のバージョンで行ったように)、UTF-8ロケールのようなマルチバイトロケール(今日の標準)では、 _#1472047795<0x80>;reboot
_のような入力がある場合、その_<0x80>
_は有効な文字を形成しないバイト値0x80であり、そのs
コマンドは_date -d@1472047795<0x80>; reboot
_を実行することになります。 。追加の_$
_を使用しても、これらの行は置換されません。別のアプローチは次のようになります:s/^#([0-9])/date -d @\1 #/e
、つまり_#xxx
_の日付の後の部分をシェルコメントとして残す
他のすべての回答は、変換が必要なエポックの日付ごとに新しいdate
プロセスを生成します。入力が大きい場合、これによりパフォーマンスのオーバーヘッドが増える可能性があります。
ただしGNU dateには便利な_-f
_オプションがあり、date
の単一のプロセスインスタンスが新しいフォークを必要とせずに継続的に入力日付を読み取ることができます。したがって、このようにsed
、paste
、date
を使用すると、入力の大きさに関係なく、それぞれが1回だけ生成されます(sed
の場合は2倍)。
_$ paste -d '\n' <( sed '2~2d;y/#/@/' Epoch.txt | date -f - ) <( sed '1~2d' Epoch.txt )
Wed Aug 24 07:09:55 PDT 2016
ll /data/holding/email
Wed Aug 24 07:11:46 PDT 2016
cat /etc/rsyslog.conf
Wed Aug 24 07:13:58 PDT 2016
ll /data/holding/web
$
_
sed
コマンドは、基本的にそれぞれ入力の偶数行と奇数行を削除します。最初のものは、_#
_を_@
_に置き換えて、正しいエポックタイムスタンプ形式を提供します。sed
出力は、_date -f
_にパイプ処理されます。これにより、受け取った入力のすべての行について、必要な日付変換が行われます。paste
を使用して必要な単一の出力にインターレースされます。 <( )
構文は bashプロセスの置換 であり、実際に内部のコマンドからパイプされた出力を読み取っているときに、与えられたファイル名から読み取っていると考えるようにペーストを効果的にトリックします。 _-d '\n'
_は、奇数行と偶数行を改行で区切るようにpaste
に指示します。たとえば、他のテキストと同じ行のタイムスタンプが必要な場合は、これを変更(または削除)できます。このコマンドにはいくつかのGNUismとBashismがあることに注意してください。これはPosixに準拠していないため、GNU/Linuxの世界以外での移植性は期待できません。たとえば、_date -f
_は、OSXes BSD date
バリアントで別のことを行います。
あなたがあなたの投稿に持っている日付形式があなたが望むものであると仮定すると、次の正規表現はあなたのニーズに合うはずです。
sed -E 's/\#(1[0-9]{9})(.*)/echo \1 $(date -d @\1)/e' log.file
これは行ごとに1つのエポックのみを置き換えることに注意してください。
sedの使用:
sed -r 's/\#([0-9]*)/echo $(date -d @\1)/eg' test.txt
出力:
ر أغس 24 16:09:55 EET 2016
ll /data/holding/email
ر أغس 24 16:11:46 EET 2016
cat /etc/rsyslog.conf
ر أغس 24 16:13:58 EET 2016
ll /data/holding/web
私のロケール言語はアラビア語なので:)
私のソリューションは、パイプラインでそれを行う方法
cat test.txt | sed 's/^/echo "/; s/\([0-9]\{10\}\)/`date -d @\1`/; s/$/"/' | bash