使用 sed 或 awk 重新格式化文本

使用 sed 或 awk 重新格式化文本

我有一个输入,例如第一行是标题,是否可以将文本重新格式化为下面的输出?

awk '{if ($2=="b" || $3 == "c" || $4 == "d" || $5 == "e" || $6 == "f" || $7 == "g" || $8 == "9" )'}' 

我上面的尝试不起作用,我是 Linux 的新初学者,任何想法将不胜感激。

输入 :

Name    Date          Time          Mxam     Mxterm
Maxus   Date:su,mo    Time:12,3:00  mxam:20  Mxterm:10
Feros   Time:12,3:00  Mxterm:19
Michel  Mxterm:16       

所需输出

Name    Date           Time              Mxam     Mxterm
Maxus   Date:su,mo     Time:12,3:00      mxam:20  Mxterm:10
Feros                  Time:12,3:00               Mxterm:19
Michel                                            Mxterm:16

答案1

GNU AWK:

awk -v IGNORECASE=1 '
FNR==1  {n = split($0, col)}
        {printf("%s ", $1); k=2
        for(i=2; i<=n; i++)
                printf("%s ", $0 ~ "\\<"col[i]"\\>"?$(k++):"")
        print ""}
' file | column -ts' '

IGNORECASE=1-忽略模式中的大小写
column -ts' '-输入分隔符是一个空格字符,这大大简化了awk中的程序。

GNU SED:

sed -r '
s/\s+/ /g
1{h;b};G
:1;s/( \S*)(:\S*)(.*)\1/\3\1\2/i;t1
s/\n\S*//
:2;s/ [^: ]+( |$)/ \1/;t2
' file | column -ts' '

第一个标题行添加到每行中,由\n分隔符分隔。除第一列之外的左侧列将替换右侧相应的列。其余没有该:符号的列将替换为空格。

调试 sed 脚本的建议:
添加-n标志sed -nr并将l命令放在第 3 行 - 的末尾1{h;b};G;l。运行脚本,然后重复 4 行,依此类推。l命令 - 显示缓冲区的内容(模式空间)以及缓冲区的锚端$

[awk 更新]:

awk '
NR==1   {n = split($0, col)}
        {k=1; for(i=1; i<=n; i++)
                printf( "%s ", $0 ~ "\\<"col[i]?$(k++):"")
        print ""}
' file | column -ts' '

适用于初始标题匹配,但最好编写完整标题(例如insert_job days_somthing start_somting window term max_run_alarm must_somthing)并使用词尾锚点"\\<"col[i]"\\>"

如果第一列从不为空并且使用唯一名称作为标识符,那么您可以将其保留原样:

awk '
NR==1   {n = split($0, col)}
        {printf("%s ", $1); k=2
        for(i=2; i<=n; i++)
                printf("%s ", $0 ~ "\\<"col[i]?$(k++):"")
        print ""}
' file | column -ts' '

col[]- 带有列名的数组;col[1] == "Name"; col[2] == "Date";col[3] == "Time"等等。"\\<"- 单词开始锚点。示例 -"\\<"col[2]等于"\\<Date"

三元运算符 -condition expression ? statement1 : statement2
当条件表达式返回true时,statement1被执行;否则执行statement2。

$0 ~ "\\<"col[i]?$(k++):""- 因此,如果当前行$0包含"\\<"col[2],则下一个字段$(k++)将按照其在当前行中出现的顺序打印(例如,$2在 中$0),如果不包含,则打印空字段""

[awk Updated2]:删除尾随空格。

awk '
NR==1   {n = split($0, col)}
        {printf("%s ", $1); k=2
        for(i=2; i<=n; i++)
            printf("%s%c", ($0~"\\<"col[i]?$(k++):""), (n>i?OFS:ORS))}
' file | column -ts' '

[awk update3]:用于重新排列字段。

awk '
NR==1   {n = split($0, col)}
        {k=1; for(i=1; i<=n; i++)
                A[i] = ($0~"\\<"col[i]?$(k++):"")
        for(i in A) $i = A[i]
        }
1' file | column -ts' '

答案2

每当您的数据像您一样包含 tag=value 对时(例如,在Date:su,mo标记 isData和 value is中su,mo),最好首先构造一个数组来保存该映射(tag2val[]如下),然后您可以仅通过它们来访问/打印/比较这些值标签(又名名称)按您喜欢的任何顺序排列。在本例中,我们只是按照标签在第一个输入行中出现的顺序打印值,但通过这种方法,我们可以轻松地执行更多操作:

$ cat tst.awk
BEGIN { OFS="\t" }
NR==1 {
    for (fldNr=1; fldNr<=NF; fldNr++) {
        val = $fldNr
        tag = tolower(val)
        tag2val[tag] = val
        tags[++numTags] = tag
    }
}
NR > 1 {
    tag2val[tags[1]] = $1
    for (fldNr=2; fldNr<=NF; fldNr++) {
        val = $fldNr
        tag = tolower(val)
        sub(/:.*/,"",tag)
        tag2val[tag] = val
    }
}
{
    for (tagNr=1; tagNr<=numTags; tagNr++) {
        tag = tags[tagNr]
        val = tag2val[tag]
        printf "%s%s", val, (tagNr<numTags ? OFS : ORS)
    }
    delete tag2val
}

$ awk -f tst.awk file | column -s$'\t' -t
Name    Date        Time          Mxam     Mxterm
Maxus   Date:su,mo  Time:12,3:00  mxam:20  Mxterm:10
Feros               Time:12,3:00           Mxterm:19
Michel                                     Mxterm:16

答案3

这是一个sed解决方案,用于#可视化空间:

$ sed -E '1s/\s+/#/' file | \
  tr -s ' ' '#' | \
  sed -E '2,$s/#*(D[^#]*)*#*(T[^#]*)*#*(mx[^#]*)*#*(Mx[^#]*)*$/#\1#\2#\3#\4/' | \
  tr '#' ' ' |\
  column -nt
Name    Date        Time          Mxam     Mxterm
Maxus   Date:su,mo  Time:12,3:00  mxam:20  Mxterm:10
Feros               Time:12,3:00           Mxterm:19
Michel                                     Mxterm:16

答案4

这是重新格式化数据的另一种方法。

我们使用 awk 为实用程序生成代码,tbl然后通过管道传输到groff文本处理器以将数据制成表格。

< file \
awk -v OFS='@' '
{ split($0, a) }
NR==1{
  for (idx in a) b[tolower(a[idx])]=idx
  print ".TS"; print "tab(", ");"
  for (i=1; i<=NF; i++)
    printf "%s%s", "l", (i<NF ? OFS : ".")
  $1 = ORS $1
}
NR>1{
  $0=""; $1=a[1]
  for (i=2; i in a; i++) $(b[tolower(substr(a[i],1,-1+index(a[i],":")))]) = a[i]
}1
END { print ".TE" }
' | tbl | groff -Tascii | grep .

输出:

Name     Date         Time           Mxam      Mxterm
Maxus    Date:su,mo   Time:12,3:00   mxam:20   Mxterm:10
Feros                 Time:12,3:00             Mxterm:19
Michel                                         Mxterm:16

相关内容