![使用 sed 或 awk 重新格式化文本](https://linux22.com/image/197301/%E4%BD%BF%E7%94%A8%20sed%20%E6%88%96%20awk%20%E9%87%8D%E6%96%B0%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%96%87%E6%9C%AC.png)
我有一个输入,例如第一行是标题,是否可以将文本重新格式化为下面的输出?
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