【问题标题】:Bash data processing on certain column对特定列进行 Bash 数据处理
【发布时间】:2014-08-29 00:33:55
【问题描述】:

我有一个这种格式的文本文件:

a0,b0,c0,27/Aug/2014:23:58,e0
a1,b1,c1,27/Aug/2014:23:58,e1
a2,b2,c2,27/Aug/2014:23:58,e2
a3,b3,c3,27/Aug/2014:23:58,e3
a4,b4,c4,27/Aug/2014:23:58,e4

最后我需要想出

a0,b0,c0,28 Aug 2014 05:58,e0
a1,b1,c1,28 Aug 2014 05:58,e1
a2,b2,c2,28 Aug 2014 05:58,e2
a3,b3,c3,28 Aug 2014 05:58,e3
a4,b4,c4,28 Aug 2014 05:58,e4

我必须这样做

sed 's/\//\ /g' | sed 's/:/\ /'

格式化日期

然后我需要做类似的事情:

date -d "19 Aug 2014 13:51:23 6 hours"

让时间提前 6 小时

但我偶然发现的问题是我如何才能仅在该行上执行所有这些操作?

【问题讨论】:

  • 在该行或列上?
  • sed 's/\//\ /g' | sed 's/:/\ /' 最好写成sed 's/\//\ /g;s/:/\ /'。如果您的 sed 方言不喜欢这个特殊的习语,也许可以使用多个 -e 选项或换行符而不是分号。但是,当然,任务的其余部分需要 Awk 或 Perl 之类的东西,所以在这种情况下最好在那里完成所有处理。

标签: bash awk sed


【解决方案1】:

Perl 的救援:

perl -MTime::Piece -naF, -e '$t = Time::Piece->strptime($F[3], "%d/%b/%Y:%H:%M") + 6 * 60 * 60;
                             $F[3] = $t->strftime("%d %b %Y %H:%M");
                             print join ",", @F;' input-file

说明:

  • Time::Piece 是一个处理日期和时间(包括格式和算术)的模块。
  • strptime 是将日期解析为对象的函数。
  • 6 * 60 * 60 是 6 小时内的秒数。
  • $F[3] 是使用 -a 调用 Perl 时的第四列,加上 F, 告诉它用逗号分隔列。
  • strftime 将对象格式化回字符串。

【讨论】:

  • 喜欢strptime 功能!我想我会看看我能不能扭动一些胳膊把它加到 gawk 上。
  • @EdMorton:不应该这么难:man7.org/linux/man-pages/man3/strptime.3.html
  • 唯一需要注意的是 Time::Piece 对象不知道时区。每年有几次,在世界的某些地区,23:58 + 6 小时不是 05:58
  • 基本上我会寻找一个函数来完成我的 awk 解决方案的字符串操作部分所做的事情,然后无论 mktime()/strftime() 做什么都可以感知时区。我已经开始在 comp.lang.awk 上滚动了,让我们看看它的去向...感谢您提供的信息。
【解决方案2】:

使用 GNU awk 进行时间函数:

$ cat tst.awk
BEGIN{ FS=OFS="," }
{
    split($4,t,/[\/:]/)
    mthNr = (match("JanFebMarAprMayJunJulAugSepOctNovDec",t[2])+2)/3
    secs  = mktime(t[3]" "mthNr" "t[1]" "t[4]" "t[5]" 0") + (6*60*60)
    $4    = strftime("%d %b %y %H:%M", secs)
    print
}
$
$ awk -f tst.awk file
a0,b0,c0,28 Aug 14 05:58,e0
a1,b1,c1,28 Aug 14 05:58,e1
a2,b2,c2,28 Aug 14 05:58,e2
a3,b3,c3,28 Aug 14 05:58,e3
a4,b4,c4,28 Aug 14 05:58,e4

【讨论】:

  • +1 表示 match 成语。非常聪明!我曾经做过this
【解决方案3】:

一些date通过awk格式化:

$ awk -F, -v OFS="," '{gsub("/"," ",$4); split($4,a,":"); cmd="date \"+%d %b %Y %H:%M\" -d \""a[1]" "a[2]":"a[3]" 6 hours \""; cmd | getline var; $4=var}1' file
a0,b0,c0,28 Aug 2014 05:58,e0
a1,b1,c1,28 Aug 2014 05:58,e1
a2,b2,c2,28 Aug 2014 05:58,e2
a3,b3,c3,28 Aug 2014 05:58,e3
a4,b4,c4,28 Aug 2014 05:58,e4

说明

  • -F, -v OFS="," 设置逗号作为输入和输出字段分隔符。
  • gsub("/"," ",$4); split($4,a,":") 删除日期上的 / 并使用 : 作为分隔符进行切片。
  • cmd="date \"+%d %b %Y %H:%M\" -d \""a[1]" "a[2]":"a[3]" 6 hours \""; cmd | getline var 计算 $date 6 hours 并相应地格式化。存储在var 中的值。
  • $4=var 存储在$4
  • 1 打印

【讨论】:

  • 设为if ( (cmd | getline var) > 0 ) $4=var; close(cmd) 并添加一些错误处理以避免/当输入文件包含无效的日期格式或其他内容时出现鼻恶魔。
【解决方案4】:

这可能对你有用(GNU sed & shell):

sed -r 'h;s/.*,.*,.*,(.*),.*/\1/;s/\// /g;s/:/ /;s/.*/date -d "& 6 hours" +"%d %b %Y %H:%M"/e;H;g;s/([^,]*)(,[^,]*)\n(.*)/\3\2/' file

从文件中提取日期,使用date 命令添加 6 小时并格式化返回日期,然后将新日期替换回原始行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    • 1970-01-01
    • 2019-08-31
    • 2021-04-16
    • 2021-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多