अन्य स्वरूपों के साथ एक फ़ाइल में युग टाइमस्टैम्प को कैसे बदलें?


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
भविष्य के संदर्भ के लिए (यह एक बैश इतिहास फ़ाइल है; यह एक जैसा दिखता है), HISTTIMEFORMATलेखन के समय प्रारूप को नियंत्रित करने के लिए शेल चर को देखें।
टोबी स्पाइट

HISTTIMEFORMAT का मान @ उपयोग किया जाता है (प्रदर्शित करने के लिए (स्टडआउट करने के लिए), लेकिन केवल इसका स्टेटस (HISTFILE लिखते समय कुछ भी अशक्त बनाम अशांत) के लिए सेट होता है।
dave_thompson_085

धन्यवाद @dave, मुझे नहीं पता था कि (खुद इतिहास के समय का उपयोगकर्ता नहीं)।
टोबे स्पाइट

date -dSolaris कहने के लिए पोर्टेबल नहीं है ... मुझे लगता है कि यह ज्यादातर GNU टूल के साथ एक सिस्टम पर है? (जीएनयू एडब्ल्यूके / पर्ल डेट रूपांतरणों से निपटने के लिए अधिक पोर्टेबल तरीके हैं)। gawk '{ if ($0 ~ /^#[0-9]*$/) {print strftime("%c",substr($0,2)); } else {print} }' < file( strftimeगैर-पोर्टेबल लगता है ...)
Gert van den Berg

जवाबों:


6

सुसंगत फ़ाइल प्रारूप मानकर, bashआप फ़ाइल लाइन को लाइन से पढ़ सकते हैं, यदि यह दिए गए प्रारूप में है तो परीक्षण करें और फिर रूपांतरण करें:

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
यदि आप हाल के संस्करण का उपयोग कर रहे हैं bash, printfतो रूपांतरण स्वयं कर सकते हैं printf '#%(%F %H)T\n' "${BASH_REMATCH[1]}":।
chepner

14

जबकि GNU के sedसाथ यह संभव है :

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

यह बहुत ही अयोग्य होगा (और मनमानी कमांड इंजेक्शन कमजोरियों को पेश करना आसान है ) 1 इसका मतलब होगा कि dateप्रत्येक #xxxxपंक्ति के लिए एक शेल और एक कमांड चल रहा है , जो वास्तव में शेल 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]).*$(जैसा कि मैंने इस उत्तर के पहले संस्करण में किया था) लिखा था , तो बहु-बाइट स्थानों में जैसे यूटीएफ -8 वाले (आजकल आदर्श), जैसे इनपुट के साथ #1472047795<0x80>;reboot, जहां <0x80>बाइट का मूल्य 0x80 है जो एक वैध चरित्र नहीं बनता है, यह sआदेश date -d@1472047795<0x80>; rebootउदाहरण के लिए चल रहा है। अतिरिक्त के साथ $, उन लाइनों को प्रतिस्थापित नहीं किया जाएगा। एक वैकल्पिक तरीका यह होगा: s/^#([0-9])/date -d @\1 #/eकि #xxxएक टिप्पणी के रूप में तारीख के बाद भाग छोड़ दें


1
धारा-वार तरीके से सभी रूपांतरणों को करने केdate -f लिए केवल एक उदाहरण का उपयोग करने के बारे में क्या ?
डिजिटल ट्रामा

Perl कमांड $ 1 के बाद एक नई लाइन जोड़ रहा है और मुझे इसे हटाने का कोई तरीका नहीं मिल रहा है।
एलेक्स हार्वे

1
@Alex। सही। संपादित देखें। sध्वज जोड़ने से ऐसा होता है कि .*इसमें इनपुट पर नई पंक्ति भी शामिल होती है। आप भी इस्तेमाल कर सकते हैं strftime "%c", localtime $1
स्टीफन चेजलस

@ स्टीफनचैजेलस बहुत बहुत धन्यवाद। बहुत अच्छा जवाब है।
एलेक्स हार्वे

3

अन्य सभी उत्तर dateहर युग की तारीख के लिए एक नई प्रक्रिया की शुरुआत करते हैं जिसे परिवर्तित करने की आवश्यकता होती है। यदि आपका इनपुट बड़ा है, तो यह संभावित रूप से ओवरहेड जोड़ सकता है।

हालांकि जीएनयू तिथि के पास एक आसान -fविकल्प है जो एकल प्रक्रिया उदाहरण को dateनए कांटे की आवश्यकता के बिना लगातार इनपुट तिथियों को पढ़ने की अनुमति देता है । तो हम उपयोग कर सकते हैं sed, pasteऔर dateइस तरह से कि हर एक केवल एक बार (2x के लिए sed) spawned हो जाता है, भले ही इनपुट कितना बड़ा हो:

$ 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<( )निर्माणों हैं बैश प्रक्रिया प्रतिस्थापन कि प्रभावी ढंग से सोच यह दिया फ़ाइल नाम से पढ़ रही है जब यह तथ्य पढ़ने उत्पादन आदेश अंदर से पहुंचाया में है में पेस्ट चाल। एक नई रेखा के साथ विषम और यहां तक ​​कि आउटपुट लाइनों को अलग करने के लिए -d '\n'कहता pasteहै। आप इसे बदल सकते हैं (या हटा सकते हैं) यदि उदाहरण के लिए आप अन्य पाठ की तरह उसी लाइन पर टाइमस्टैम्प चाहते हैं।

ध्यान दें कि इस आदेश में कई GNUism और Bashism हैं। यह पॉज़िक्स-अनुरूप नहीं है और जीएनयू / लिनक्स दुनिया के बाहर पोर्टेबल होने की उम्मीद नहीं की जानी चाहिए। उदाहरण के लिए date -fOSXes BSD dateवैरिएंट पर कुछ और ।


date -d(प्रश्न से) भी गैर-पोर्टेबल है ... (फ्रीबीएसडी पर यह डीएसटी सेटिंग्स के साथ गड़बड़ करने की कोशिश करेगा, सोलारिस पर यह एक त्रुटि देगा ...) प्रश्न हालांकि एक ओएस निर्दिष्ट नहीं करता है ...
गर्ट वैन डेन बर्ग

@GertvandenBerg हाँ, यह इस उत्तर के अंतिम पैराग्राफ में दिया गया है।
डिजिटल ट्रामा

मेरा मतलब है कि पूछने वाले के नमूने में भी पोर्टेबिलिटी के मुद्दे हैं ... (उन्हें शायद एक ओएस टैग किया जाना चाहिए ...)
गर्ट वैन डेन बर्ग

1

अपनी पोस्ट में आपके पास जो दिनांक स्वरूप है, उसे मानकर, निम्नलिखित regex को आपकी आवश्यकताओं के अनुरूप होना चाहिए।

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

इस तथ्य से सावधान रहें यह केवल एक युग प्रति पंक्ति को प्रतिस्थापित करेगा।


मुझे उस कमांड के साथ निम्न त्रुटि मिल रही है: 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.