sed搜索并仅替换特定列

时间:2014-02-15 03:45:45

标签: sed

我从wunderground.com和我的天气一天,然后削减数据用于gnuplot。我无法将第二列数据从数字缩写为月份缩写。只对第二栏感兴趣。

我想离开这个;

>2013 08 02 23 37 00 73.3
>2013 08 02 23 42 00 73.4
>2013 08 02 23 45 00 73.3
>2013 08 02 23 47 00 73.1
>2013 08 02 23 52 00 73.1
>2013 08 02 23 57 00 73.1

对此:

>2013 AUG 02 23 37 00 73.3
>2013 AUG 02 23 42 00 73.4
>2013 AUG 02 23 45 00 73.3
>2013 AUG 02 23 47 00 73.1
>2013 AUG 02 23 52 00 73.1
>2013 AUG 02 23 57 00 73.1

我正在尝试使用sed将数字更改为正确的月份,我会继续这样做。我只希望正确的sed表达式不是全部执行。这是我试图使用的命令。

sed -e 's/01/JAN/' -e 's/02/FEB/' -e 's/03/MAR/' -e 's/04/APR/' -e 's/05/MAY/' -e 's/06/JUN/' -e 's/07/JUL/' -e 's/08/AUG/' -e 's/09/SEP/' -e 's/10/OCT/' -e 's/11/NOV/' -e 's/12/DEC/'

我将如何解决这个问题。

5 个答案:

答案 0 :(得分:2)

这可能适合你(GNU sed):

sed -nri 'G;s/$/01JAN02FEB03MAR04APR05MAY06JUN07JUL08AUG09SEP10OCT11NOV12DEC/;s/ (..)(.*)\1(...)/ \3\2/;P' file

这会在每行的末尾添加一个查找表,并用键替换该值。

答案 1 :(得分:1)

适用于此问题的解决方法(因为您的第一列非常容易预测),但不是一般性问题:

sed -E -e 's/^([0-9]{4}) 01/\1 JAN/' -e 's/^([0-9]{4}) 02/\1 FEB/' etc.

awk有一个sub函数,对于您在此处的许多选项而言可能会变得难以处理。

Perl脚本可能是最好的方法。

答案 2 :(得分:1)

我会使用awk

$ awk 'BEGIN{split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",a)} {$2=a[$2+0]}1' a
>2013 Aug 02 23 37 00 73.3
>2013 Aug 02 23 42 00 73.4
>2013 Aug 02 23 45 00 73.3
>2013 Aug 02 23 47 00 73.1
>2013 Aug 02 23 52 00 73.1
>2013 Aug 02 23 57 00 73.1

要使用新内容更新字段,只需重定向然后移动:

awk .... file > temp_file && mv temp_file file

解释

我们所做的是为awk提供一个包含月份名称的字符串列表。一旦我们将其转换为数组,a[1]将是Jan,a[2] 2月,依此类推。那么这只是用a[2nd field]替换第二个字段的问题。

  • BEGIN{split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",a)}获取数据并插入a[]数组。
  • {$2=a[$2+0]}将第二个字段设为a[2nd field]。完成$2+0以将08转换为8
  • 最后1评估为真,并awk执行默认操作:{print $0}

答案 3 :(得分:1)

使用gnu awk的函数strftime()和mktime()

awk '{$2=strftime("%b",mktime("2014 " $2 " 1 1 0 0"))}1' file

>2013 Aug 02 23 37 00 73.3
>2013 Aug 02 23 42 00 73.4
>2013 Aug 02 23 45 00 73.3
>2013 Aug 02 23 47 00 73.1
>2013 Aug 02 23 52 00 73.1
>2013 Aug 02 23 57 00 73.1

解释

  • mktime("2014 " $2 " 1 1 0 0")假冒纪元时间,将第2列用作月份
  • strftime("%b",mktime("2014 " $2 " 1 1 0 0"))将纪元转换回日期,%b,导出缩写的月份名称(Jan,Feb等)

这个awk的好处:

当然,它更短。其次,您可以在strftime()中控制/调整格式以导出您喜欢的任何日期格式。

例如,如果更改为full month name %B。您无需重写代码。

awk '{$2=strftime("%B",mktime("2014 " $2 " 1 1 0 0"))}1' file

答案 4 :(得分:1)

$ awk '{$2=substr("JanFebMarAprMayJunJulAugSepOctNovDec",(3*$2)-2,3)}1' file
>2013 Aug 02 23 37 00 73.3
>2013 Aug 02 23 42 00 73.4
>2013 Aug 02 23 45 00 73.3
>2013 Aug 02 23 47 00 73.1
>2013 Aug 02 23 52 00 73.1
>2013 Aug 02 23 57 00 73.1

因为它出现在评论中:

从月份编号映射到名称的惯用awk方式是:

number = (match("JanFebMarAprMayJunJulAugSepOctNovDec",<name>)+2)/3

以上就是这个的自然反转:

name = substr("JanFebMarAprMayJunJulAugSepOctNovDec",(3*<number>)-2,3)

与awk中的任何内容一样,有各种方法可以获得您想要的输出,但恕我直言,这里的对称性使它成为一个有吸引力的解决方案:

awk 'BEGIN{

    months = "JanFebMarAprMayJunJulAugSepOctNovDec"

    name = "Jul"

    number = (match(months,name)+2)/3

    print name " -> " number

    name = substr(months,(3*number)-2,3)

    print number " -> " name

}'
Jul -> 7
7 -> Jul

请注意,无论转换的方向是什么,脚本都会使用相同的定义,并且在两个方向上都是类似的数学计算。

当然这样做也没有错:

awk 'BEGIN{

    split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",num2name)

    for (number in num2name) {
        name2num[num2name[number]] = number
    }

    name = "Jul"

    number = name2num[name]

    print name " -> " number

    name = num2name[number]

    print number " -> " name

}'
Jul -> 7
7 -> Jul

还有几行代码,nbd。