Bir dosyadaki dönem zaman damgalarını diğer biçimlerle nasıl değiştirebilirim?


10

Ben insan tarafından okunabilir dönüştürmek gerekir dönem tarihleri ​​içeren bir dosya var. Zaten tarih dönüşümünü nasıl yapacağımı biliyorum, örneğin:

[server01 ~]$ date -d@1472200700
Fri 26 Aug 09:38:20 BST 2016

.. ama ben seddosya içinde yürümek ve tüm girişleri dönüştürmek için nasıl anlamaya mücadele ediyorum . Dosya biçimi şuna benzer:

#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web

1
İleride başvurmak için (bunun bir Bash geçmiş dosyası olduğu varsayılarak, bir dosyaya benziyor), HISTTIMEFORMATyazma sırasında formatı kontrol etmek için kabuk değişkenine bakın .
Toby Speight

@ HISTTIMEFORMAT değeri görüntülenirken (stdout'a) kullanılır, ancak HISTFILE yazılırken yalnızca durumu (boş ve unset değerine ayarlanmış herhangi bir değere ayarlanır) önemlidir.
dave_thompson_085

Teşekkürler @dave, bunu bilmiyordum (tarihin kendisinin bir kullanıcısı değilim).
Toby Speight

date -dSolaris demek taşınabilir değil ... Bunun çoğunlukla GNU araçları olan bir sistemde olduğunu varsayıyorum? (GNU AWK / Perl, tarih dönüşümleriyle başa çıkmak için daha taşınabilir yöntemler olma eğilimindedir). gawk '{ if ($0 ~ /^#[0-9]*$/) {print strftime("%c",substr($0,2)); } else {print} }' < file( strftimetaşınabilir değil gibi görünüyor ...)
Gert van den Berg

Yanıtlar:


6

Tutarlı dosya biçimini varsayarsak bash, dosyayı satır satır okuyabilir, verilen biçimde olup olmadığını test edebilir ve ardından dönüşümü yapabilirsiniz:

while IFS= read -r i; do [[ $i =~ ^#([0-9]{10})$ ]] && \
      date -d@"${BASH_REMATCH[1]}"; done <file.txt

BASH_REMATCHilk öğesi Regex eşleşmesinde ilk yakalanan grup olan bir dizidir, =~bu durumda çağ.


Dosya yapısını korumak istiyorsanız:

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

bu, değiştirilmiş içerikleri STDOUT'a gönderir, örneğin bir dosyaya kaydetmek için out.txt:

while ...; do ...; done >out.txt

Şimdi isterseniz orijinal dosyayı değiştirebilirsiniz:

mv out.txt file.txt

Misal:

$ 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

Güzel .... dönüştürülen tarihi ekrana yazdırır, şimdi bu komutu dosyadaki girdileri değiştirmek için nasıl alabilirim?
makinist

@machinist Düzenlemelerimi kontrol et ..
heemayl

1
Eğer güncel bir sürümünü kullanıyorsanız bash, printfdönüşüm kendisi yapabilirsiniz: printf '#%(%F %H)T\n' "${BASH_REMATCH[1]}".
chepner

14

GNU sedile mümkün olan şeylerle mümkün olsa da :

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

Bu , neredeyse bir kabuk döngüsü kadar kötü olan her bir satır için bir kabuk ve bir komut çalıştırmak anlamına geleceğinden, bu çok korkunç bir verimsiz (ve keyfi komut enjeksiyon güvenlik açıkları 1'i tanıtmak kolaydır ) . Burada, veya tarih dönüşüm yetenekleri yerleşik olan metin işleme yardımcı programları gibi şeyleri kullanmak daha iyi olur :date#xxxxwhile readperlgawk

perl  -MPOSIX -pe 's/^#(\d+).*/ctime $1/se'

Veya:

gawk '/^#/{$0 = strftime("%c", substr($0, 2))};1'

1 (Bu yanıtın önceki bir sürümünde yaptığım gibi) ^#([0-9]).*yerine yazmış olsaydık, ^#([0-9]).*$UTF-8 olanlar gibi çok baytlı yerel ayarlarda (günümüzde norm), böyle bir girdiyle #1472047795<0x80>;reboot, <0x80>0x80 bayt değeri olan geçerli bir karakter oluşturmazsa, söz konusu skomut date -d@1472047795<0x80>; rebootörneğin çalıştırılır . Ekstra ile birlikte $, bu çizgiler ikame edilmeyecektir. Alternatif bir yaklaşım s/^#([0-9])/date -d @\1 #/eşöyledir : yani #xxxtarihten sonraki bölümü bir kabuk yorumu olarak bırakmak


1
Tüm dönüşümleri akış açısından yapmak için yalnızca tek bir örneğinidate -f kullanmaya ne dersiniz ?
Dijital Travma

Perl komutu ctime $ 1 sonra yeni bir satır eklemek gibi görünüyor ve kaldırmak için herhangi bir yol bulamıyorum.
Alex Harvey

1
@Alex. Sağ. Bkz. Düzenleme. sBayrağı eklemek, .*girişteki yeni satırı da içerecek şekilde yapar . Ayrıca kullanabilirsiniz strftime "%c", localtime $1.
Stéphane Chazelas

@ StéphaneChazelas çok teşekkürler. Harika bir cevap.
Alex Harvey

3

Diğer tüm yanıtlar date, dönüştürülmesi gereken her çağ tarihi için yeni bir süreç ortaya çıkarır . Bu, girdiniz büyükse potansiyel olarak performans ek yükü ekleyebilir.

Bununla birlikte, GNU tarihi, -ftek bir işlem vakasının dateyeni bir çatal gerekmeksizin giriş tarihlerini sürekli olarak okumasına izin veren kullanışlı bir seçeneğe sahiptir . Kullanabileceğimiz Yani sed, pasteve datebu şekilde her biri sadece (2x kez olurken alır öyle ki sedbakılmaksızın giriş ne kadar büyük olduğundan):

$ 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
$ 
  • İki sedkomut sırasıyla girişin çift ve tek satırlarını siler; birincisi , doğru dönem zaman damgası biçimini vermek için #ile değiştirilir @.
  • Daha sedsonra ilk çıkış, date -faldığı her girdi satırı için gerekli tarih dönüşümünü gerçekleştirecek şekilde borulanır.
  • Bu iki akış daha sonra, gerekli olan tek çıkışa geçilir paste. <( )Yapılarıdır bash süreç değiştirmelerin etkili bir çıkış komutu içeriden borulu okuma aslında olduğunda verilen dosya adları okuma düşünce içine yapıştırın numara. tek ve çift çıktı satırlarını bir satırsonu ile ayırmayı -d '\n'söyler paste. Örneğin, zaman damgasını diğer metinle aynı satırda istiyorsanız bunu değiştirebilirsiniz (veya kaldırabilirsiniz).

Bu komutta birkaç GNUizm ve Bashizm olduğunu unutmayın. Bu Posix uyumlu değildir ve GNU / Linux dünyası dışında taşınabilir olması beklenmemelidir. Örneğin date -fOSXes BSD datevaryantında başka bir şey yapar .


date -d(sorudan) da taşınabilir değil ... (FreeBSD üzerinde DST ayarları ile uğraşmaya çalışacak, Solaris üzerinde bir hata verecek ...) Soru olsa bir işletim sistemi belirtmiyor ...
Gert van den Berg

@GertvandenBerg evet, bu cevabın son paragrafında ele alınmaktadır.
Dijital Travma

Ben asker Nickli Üyenin örnek de (Muhtemelen bir işletim sistemi ... etiketlenmiş olması gerekirdi) taşınabilirlik sorunları ... olduğu anlamına
Gert van den Berg

1

Yayınınızdaki tarih biçiminin istediğiniz gibi olduğunu varsayarsak, aşağıdaki normal ifade ihtiyaçlarınızı karşılamalıdır.

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

Bunun satır başına yalnızca bir dönemin yerini alacağına dikkat edin.


Bu komutla aşağıdaki hatayı alıyorum: sed: -e expression #1, char 48: invalid reference \3 on 's' command's RHS
machinist

1
Benim hatam gönderiyi düzenledi.
Hatclock

0

sed kullanarak:

sed -r 's/\#([0-9]*)/echo $(date -d @\1)/eg' test.txt

çıktı :

ر أغس 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

yerel dilim Arapça olduğu için :)


0

Benim çözümüm bir boru hattında nasıl yapılır

cat test.txt | sed 's/^/echo "/; s/\([0-9]\{10\}\)/`date -d @\1`/; s/$/"/' | bash
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.