web-dev-qa-db-ja.com

ファイル内のエポックタイムスタンプを他の形式に置き換える方法は?

人が読める形式に変換する必要があるエポック日付を含むファイルがあります。私はすでに日付変換を行う方法を知っています、例えば:

[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
10
machinist

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
6
heemayl

GNU sedでは次のようなことが可能ですが、

_sed -E 's/^#([0-9]+).*$/date -d @\1/e'
_

それはひどく非効率的です(そして任意のコマンドインジェクションの脆弱性を導入するのは簡単です)1)つまり、_#xxxx_行ごとに1つのシェルと1つのdateコマンドを実行し、実質的に シェルとして悪い_while read_ループ を実行します。ここでは、Perlgawkのようなものを使用する方が良いでしょう。これは、日付変換機能が組み込まれたテキスト処理ユーティリティです。

_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_の日付の後の部分をシェルコメントとして残す

14

他のすべての回答は、変換が必要なエポックの日付ごとに新しいdateプロセスを生成します。入力が大きい場合、これによりパフォーマンスのオーバーヘッドが増える可能性があります。

ただしGNU dateには便利な_-f_オプションがあり、dateの単一のプロセスインスタンスが新しいフォークを必要とせずに継続的に入力日付を読み取ることができます。したがって、このようにsedpastedateを使用すると、入力の大きさに関係なく、それぞれが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
$ 
_
  • 2つのsedコマンドは、基本的にそれぞれ入力の偶数行と奇数行を削除します。最初のものは、_#_を_@_に置き換えて、正しいエポックタイムスタンプ形式を提供します。
  • 次に、最初のsed出力は、_date -f_にパイプ処理されます。これにより、受け取った入力のすべての行について、必要な日付変換が行われます。
  • これらの2つのストリームは、pasteを使用して必要な単一の出力にインターレースされます。 <( )構文は bashプロセスの置換 であり、実際に内部のコマンドからパイプされた出力を読み取っているときに、与えられたファイル名から読み取っていると考えるようにペーストを効果的にトリックします。 _-d '\n'_は、奇数行と偶数行を改行で区切るようにpasteに指示します。たとえば、他のテキストと同じ行のタイムスタンプが必要な場合は、これを変更(または削除)できます。

このコマンドにはいくつかのGNUismとBashismがあることに注意してください。これはPosixに準拠していないため、GNU/Linuxの世界以外での移植性は期待できません。たとえば、_date -f_は、OSXes BSD dateバリアントで別のことを行います。

3
Digital Trauma

あなたがあなたの投稿に持っている日付形式があなたが望むものであると仮定すると、次の正規表現はあなたのニーズに合うはずです。

sed -E 's/\#(1[0-9]{9})(.*)/echo \1 $(date -d @\1)/e' log.file

これは行ごとに1つのエポックのみを置き換えることに注意してください。

1
Hatclock

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

私のロケール言語はアラビア語なので:)

0
hassan

私のソリューションは、パイプラインでそれを行う方法

cat test.txt | sed 's/^/echo "/; s/\([0-9]\{10\}\)/`date -d @\1`/; s/$/"/' | bash
0
kayn