Text-Processing
如何用其他格式替換文件中的紀元時間戳?
我有一個包含紀元日期的文件,我需要將其轉換為人類可讀的。我已經知道如何進行日期轉換,例如:
[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
您可以逐行讀取文件,測試它是否為給定格式,然後進行轉換:while IFS= read -r i; do [[ $i =~ ^#([0-9]{10})$ ]] && \ date -d@"${BASH_REMATCH[1]}"; done <file.txt
BASH_REMATCH
是一個數組,其第一個元素是正則表達式匹配中第一個擷取的組=~
,在本例中為 epoch。如果要保留文件結構:
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),因為這意味著
date
每行執行一個 shell 和一個命令#xxxx
,實際上與shell循環一樣糟糕while read
。在這裡,最好使用perl
or之類的東西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
。而使用 extra$
時,這些行將不會被替換。另一種方法是:s/^#([0-9])/date -d @\1 #/e
,即將日期之後的部分保留#xxx
為外殼註釋