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


10

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

[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

1
将来の参照(これはBash履歴ファイルであると想定し、1つのように見える)のために、HISTTIMEFORMATシェル変数を調べて、書き込み時にフォーマットを制御します。
Toby Speight

@Toby HISTTIMEFORMATの値は(stdoutに)表示するときに使用されますが、HISTFILEを書き込むときは、そのステータス(nullまたはunsetでさえ設定されている)だけが重要です。
dave_thompson_085 2016

@daveに感謝します。私はそれを知りませんでした(私自身の歴史の時代のユーザーではありません)。
Toby Speight 2016

date -dSolarisと言っても移植性はありません...これは主にGNUツールを備えたシステム上にあると思いますか?(GNU AWK / Perlは、日付変換を処理するためのより移植性の高い方法である傾向があります)。gawk '{ if ($0 ~ /^#[0-9]*$/) {print strftime("%c",substr($0,2)); } else {print} }' < filestrftimeポータブルではないようです...)
Gert van den Berg

回答:


6

ファイル形式が一貫していると仮定すると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

変換された日付を画面に出力しますが、ファイルのエントリを置き換えるコマンドを取得するにはどうすればよいですか?
機械工

@machinist編集内容を確認してください
。– heemayl

1
あなたが最近のバージョンを使用している場合はbashprintf変換自体を行うことができますprintf '#%(%F %H)T\n' "${BASH_REMATCH[1]}"
chepner

14

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

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

それは実質的に非効率的であり(そして任意のコマンドインジェクションの脆弱性1を導入するのは簡単です)、それは実質的にシェルループと同じくらい悪いdate各行に対して1つのシェルと1つのコマンドを実行することを意味します。ここでは、または、つまり、日付変換機能が組み込まれたテキスト処理ユーティリティを使用することをお勧めします。#xxxxwhile readperlgawk

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


1
単一のインスタンスdate -fだけを使用して、ストリームごとにすべての変換を行うのはどうですか?
デジタルトラウマ2016

perlコマンドはctime $ 1の後に新しい行を追加するようで、それを削除する方法が見つかりません。
Alex Harvey

1
@アレックス。正しい。編集を参照してください。sフラグを追加すると、.*入力時に改行も含まれます。も使用できますstrftime "%c", localtime $1
ステファンChazelas

@StéphaneChazelasありがとうございます。それは素晴らしい答えです。
Alex Harvey

3

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

ただし、GNUの日付には、-fの単一のプロセスインスタンスがdate新しいフォークを必要とせずに継続的に入力日付を読み取ることができる便利なオプションがあります。私たちが使用できるようにsedpasteそしてdate、このように各1は(2倍のために一度起動されますようにsed関係なく、入力がどのように大規模の):

$ 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バリアントで他のことを行います。


date -d(質問から)も移植不可能です...(FreeBSDではDST設定を台無しにしようとしますが、Solarisではエラーになります...)質問はOSを指定していません...
Gert vanデンバーグヤン

@GertvandenBergはい、これはこの回答の最後の段落で説明されています。
デジタルトラウマ2017年

質問者のサンプルにも移植性の問題があることを意味します...(おそらくOSにタグを付けたはずです...)
Gert van den Berg

1

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

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

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


:私はそのコマンドで次のエラーを取得しています sed: -e expression #1, char 48: invalid reference \3 on 's' command's RHS
機械工

1
私の間違い、投稿を編集しました。
ハットクロック

0

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

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

cat test.txt | sed 's/^/echo "/; s/\([0-9]\{10\}\)/`date -d @\1`/; s/$/"/' | bash
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.