2014-07-17 61 views
1

我試圖用時間減去7小時來替換格式爲YYYYMMDDHHMMSS的日期的出現次數。用date-n小時替換日期

的文件應該是這樣的

File with text and some dates 20140716223000 20140716013000 

,我想輸出看起來像

File with text and some dates 20140716153000 20140715183000 

我能想出了一個perl subsitition最好的:

perl -ape 's/(-*\d+.\d+)/$1-70000/ge' file.txt 

但顯然這是不做日期扣除,但簡單的算術。任何bash或perl語句都適用於我。也許是sed或awk聲明?謝謝!

+0

我認爲,正則表達式不會做輸出的任何處理。正則表達式不適用於編程。 – Braj

+0

Perl將允許您在替換的輸出中使用函數。也許awk更適合這個? – braskie

+0

@Braj:Perl不是Java,它可以完全不同的東西 – Borodin

回答

0

使用GNU AWK的gensub()和時間功能:

$ cat tst.awk 
{ 
    head = "" 
    tail = $0 
    while (match(tail,/\<[[:digit:]]{14}\>/)) { 
     oldTime = substr(tail,RSTART,RLENGTH) 

     secs = mktime(gensub(/(....)(..)(..)(..)(..)(..)/,"\\1 \\2 \\3 \\4 \\5 \\6","",oldTime)) 
     newTime = strftime("%Y%m%d%H%M%S", secs - (7 * 60 * 60)) 

     head = head substr(tail,1,RSTART-1) newTime 
     tail = substr(tail,RSTART+RLENGTH) 
    } 
    print head tail 
} 
$ 
$ awk -f tst.awk file 
File with text and some dates 20140716153000 20140715183000 
+0

我做了同樣的事情!除了我忽略的字段外,在$ 0上使用'match'並用'substr'(和'RSTART','match'中的'RLENGTH')重新構建它。 – ooga

+0

我開始在字段上循環,然後切換到一個匹配,以避免垃圾空白。看起來我在發表評論時完成了這個過程。偉大的頭腦... :-) –

+1

很酷。我實際上在''gensub'中使用了一堆'[0-9] {n}',但是你的'''想法是完美的,因爲我們已經確定它們是'match'的數字。你甚至在這裏有單詞邊界。而且你也得到了低調的推動!好吧。 C'est le vie sur le SO。 – ooga

-1

並非所有的問題都應該使用正則表達式。

如何使用Date :: Calc?它是正是你所需要的功能:

($year,$month,$day, $hour,$min,$sec) = Add_Delta_DHMS($year,$month,$day, $hour,$min,$sec, $Dd,$Dh,$Dm,$Ds); 

你也可以使用一個簡單的正則表達式來打破日期字符串的年/月/日/時/分/秒variables.and的$ DX參數可以是負面的。

請注意,我希望當你說「減去7小時」時,你並不是想要做時區數學。因爲用不同的解決方案這是一個不同的問題。

+0

是的,但Time :: Piece也很容易做到這一點,並且幾年來一直是核心Perl模塊。我看不到有任何理由在Time :: Piece或DateTime上使用Date :: Calc。 –

4

您可以利用Time::Piece來解析日期時間字符串並進行算術運算。它是Perl 5版本10以來的核心模塊,因此不需要安裝。

相關的Time::Seconds模塊以秒爲單位提供各種時間間隔的有用常量。

該程序按照您的要求使用可執行替換,就像您在問題中所做的那樣。請注意,它不會正確處理夏令時,因爲它只是簡單地從每個值中減去25,200秒。

use strict; 
use warnings; 
use 5.010; 

use Time::Piece; 
use Time::Seconds qw/ ONE_HOUR /; 

my $text = 'File with text and some dates 20140716223000 20140716013000'; 

say "Before: $text"; 

$text =~ s{(\d{14})}{ 
    my $dt = Time::Piece->strptime($1, '%Y%m%d%H%M%S'); 
    $dt -= 7 * ONE_HOUR; 
    $dt->strftime('%Y%m%d%H%M%S'); 
}ge; 

say " After: $text"; 

輸出

Before: File with text and some dates 20140716223000 20140716013000 
After: File with text and some dates 20140716153000 20140715183000 
0

既然你有GNU bash中,你最有可能有GNU Coreutils的太多,所以你爲什麼不使用GNU date

$ cat file 
File with text and some dates 20140716223000 20140716013000 

$ cat script 
#!/bin/bash 

while read -a LINE; do 
    for i in "${!LINE[@]}"; do 
     [[ ${LINE[$i]} =~ ^[0-9]{14}$ ]] && { 
      DATE="${LINE[$i]}" 
      YEAR="${DATE:0:4}" 
      MONTH="${DATE:4:2}" 
      DAY="${DATE:6:2}" 
      HOUR="${DATE:8:2}" 
      MINUTE="${DATE:10:2}" 
      SECOND="${DATE:12:2}" 
      NEWDATE="$(date -d \ 
       "-7 hours ${YEAR}-${MONTH}-${DAY}T${HOUR}:${MINUTE}:${SECOND}" \ 
       +'%Y%m%d%H%M%S')" 
      LINE[$i]="$NEWDATE" 
     } 
    done 
    echo "${LINE[@]}" 
done < "$1" 

$ ./script file 
File with text and some dates 20140716153000 20140715183000 
+0

除此之外,它會將所有空格轉換爲單個空白字符,並從文本中去除反斜槓。它也無法識別被標點符號而不是空格所包圍的日期/時間戳。嘗試添加一個字面選項卡字符和反斜槓,並結束與OPs原始輸入的句點,並注意每個不希望的效果,然後將其與我發佈的awk腳本進行比較。 Shell只是一個調用工具的環境,用於處理文本的標準UNIX工具是awk。 –