有人可以帮我完成下面的转换 shell 脚本吗?
源文件文件1:例如
EXCHANGE_ID : 192, 410,
EXCHANGE_DTTM : 2015-06-11+02:18:40+0000, 2015-06-11+02:12:28+0000,
PART_NAME : MRT, LR04,
PART_TRANS_ID : 123, JAS04,
M_NAME : FAILED, FAILED,
M_DTTM : 2015-06-11T02:18:40+0000 2015-06-11T02:12:28+0000
输出为:
EXCHANGE _ID : 192
EXCHANGE_ DTTM : 2015-06-11T02:18:40+0000
PART_NAME : MRT
PART_TRANS_ID : 123
M_NAME : FAILED
M _DTTM : 2015-06-11T02:18:40+0000
EXCHANGE _ID : 410
EXCHANGE_DTTM : 2015-06-11T02:12:28+0000
PART_NAME : LR04
PART_TRANS_ID : JAS04
M_NAME : FAILED
M_DTTM : 2015-06-11T02:12:28+0000
到目前为止,这是我尝试过的:
awk '{ for (i = 1; i <= NF; i++) f[i] = f[i] " " $i ; if (NF > n) n = NF } END { for (i = 1; i <= n; i++) sub(/^ */, "", f[i]) ; for (i = 1; i <= n; i++) print f[i] } ' FAILED.csv > TGT_FAILED.out
但这只是打印出 CSV 格式的内容,而不是所需的格式。这是实际输出的示例,而不是上面的所需输出:
EXCHANGE_ID EXCHANGE_DTTM PART_NAME PART_TRANS_ID M_NAME M_DTTM
: : : : : :
192, 2015-06-11+02:18:40+0000, MRT, 123, FAILED, 2015-06-11T02:18:40+0000
410, 2015-06-11+02:12:28+0000, LR04, JAS04, FAILED, 2015-06-11T02:12:28+0000
答案1
怎么样:
awk -F'[ \t,]+' '{a=a$1"\t"$2"\t"$3"\n"; b=b$1"\t"$2"\t"$4"\n"} END {print a; print b}' data.txt
在这里,我们将一个或多个空格、制表符或逗号视为字段分隔符。然后我们在每一行构建输出。最后我们打印结果。这是一个非常脏的内衬;例如,它必须在打印任何内容之前读取整个文件,因此对于大文件,它会占用内存,但对于小文件,它应该可以解决问题。
根据您的输入,这应该导致:
EXCHANGE_ID : 192
EXCHANGE_DTTM : 2015-06-11+02:18:40+0000
PART_NAME : MRT
PART_TRANS_ID : 123
M_NAME : FAILED
M_DTTM : 2015-06-11T02:18:40+0000
EXCHANGE_ID : 410
EXCHANGE_DTTM : 2015-06-11+02:12:28+0000
PART_NAME : LR04
PART_TRANS_ID : JAS04
M_NAME : FAILED
M_DTTM : 2015-06-11T02:12:28+0000
如果您希望字段间隔良好,可以将 sprintf 添加到调用中,如下所示:
awk -F'[ \t,]+' '{label=sprintf("'%-10s'",$1); a=a""label"\t"$2" "$3"\n"; b=b""label"\t"$2" "$4"\n"} END {print a; print b}' data.txt
这给出了更漂亮的输出:
EXCHANGE_ID : 192
EXCHANGE_DTTM : 2015-06-11+02:18:40+0000
PART_NAME : MRT
PART_TRANS_ID : 123
M_NAME : FAILED
M_DTTM : 2015-06-11T02:18:40+0000
EXCHANGE_ID : 410
EXCHANGE_DTTM : 2015-06-11+02:12:28+0000
PART_NAME : LR04
PART_TRANS_ID : JAS04
M_NAME : FAILED
M_DTTM : 2015-06-11T02:12:28+0000
答案2
如果您尚未考虑如何sed
将其应用于您的数据,请允许我演示:
sed ' s/\([^, ]\{1,\}\),*/\1,/2
s//\1/3;H;y/,/\n/;P;/^M_D.*/!d
s///;x;s/[^, ]*, *//g
' <<\IN
EXCHANGE_ID: 192, 410,
EXCHANGE_DTTM: 2015-06-11+02:18:40+0000, 2015-06-11+02:12:28+0000,
PART_NAME: MRT, LR04,
PART_TRANS_ID: 123, JAS04,
M_NAME: FAILED, FAILED,
M_DTTM: 2015-06-11T02:18:40+0000 2015-06-11T02:12:28+000
IN
因此,第一轮我们P
只打印到第一个出现的逗号 - 如果所有字段都以逗号分隔,那会更简单。但事实并非如此,因此大约一半的工作都花在了确保它们确实如此。
无论如何,我们只打印标题和第一个后续的非空格字符序列。接下来,我们将当前行的副本附加到H
旧空间,然后d
将其删除 -除非它匹配^M_D
。在这种情况下,我们x
更改保留和模式空间,然后通过一次s///
替换同时从我们刚刚保存的每一行中删除第二个非空格字符序列以及所有后续空格。
结果打印到标准输出:
EXCHANGE_ID: 192
EXCHANGE_DTTM: 2015-06-11+02:18:40+0000
PART_NAME: MRT
PART_TRANS_ID: 123
M_NAME: FAILED
M_DTTM: 2015-06-11T02:18:40+0000
EXCHANGE_ID: 410
EXCHANGE_DTTM: 2015-06-11+02:12:28+0000
PART_NAME: LR04
PART_TRANS_ID: JAS04
M_NAME: FAILED
M_DTTM: 2015-06-11T02:12:28+000
答案3
处理输入文件1组6行,只是2 数据列,一种简单但有限且贪婪的资源方法使编码开销最小化,即不需要数组保存所有 6 行数据:
f='src.txt' # input fule
d=' +: +| +|, *' # field delimiter regex
set {2,3} # data columns - not label (which is column 1)
for c; do paste \
<(gawk -F"$d" '1,6{print $1"\t:"}' "$f") \
<(gawk -F"$d" '1,6{print $c}' c=$c "$f") |
column -t; echo; done
以下awk
方法处理无限重复的 6 行组以及每行任意数量的字段。
awk 'BEGIN{ FS=" +: +|, +|,| +"; OFS="\t"; maxw=length("EXCHANGE_DTTM") }
/^EXCHANGE_ID/,/^M_DTTM/{ rn++
if($NF=="") NF--
for(fn=1;fn<=NF;fn++) cell[rn"."fn]=$fn
if(rn==6){
for(fn=2;fn<=NF;fn++)
for(rn=1;rn<=6;rn++)
printf("%-"maxw"s : %s\n"(rn==6?"\n":""), cell[rn"."1], cell[rn"."fn])
rn=0 }}' <"$f"
输出:
EXCHANGE_ID : 192
EXCHANGE_DTTM : 2015-06-11+02:18:40+0000
PART_NAME : MRT
PART_TRANS_ID : 123
M_NAME : FAILED
M_DTTM : 2015-06-11T02:18:40+0000
EXCHANGE_ID : 410
EXCHANGE_DTTM : 2015-06-11+02:12:28+0000
PART_NAME : LR04
PART_TRANS_ID : JAS04
M_NAME : FAILED
M_DTTM : 2015-06-11T02:12:28+0000
这是上面脚本bash
的一个版本。awk
逻辑流程是相同的。我添加了一些错误检查和空行跳过。
set EXCHANGE_DTTM; maxw=${#1}; nl=0 # length of longest label; line number
set -f; declare -A cell; cm=" "; nf=0 # no-globbing; cells-array; cell-margin; number-of-fields
while IFS= read -r line; do ((nl+=1)) # increment line number
[[ $line =~ ^[[:blank:]]*$ ]] && continue # skip blank/empty lines
[[ $line =~ ^EXCHANGE_ID\ * ]] && rn=1 || ((rn+=1)) # reset/increment record number
IFS=" ,"; f=(${line/ : / }); IFS=; f=(${f[@]}) # split line into fields
(( nf )) && (( nf!=${#f[@]} )) && { echo ERROR: field count is not consistent; exit 1; } || nf=${#f[@]}
for (( fn=0; fn<nf; fn++ ));do cell[$rn.$fn]="${f[$fn]}"; done # build cells-array
(( rn==6 )) && {
[[ $line =~ ^M_DTTM\ .* ]] || { echo ERROR: unexpected label found - record $rn$'\n'"$line"; exit 2; }
for (( fn=1; fn<nf; fn++ )) ;do
for (( rn=1; rn<=6; rn++ )) ;do
(( rn==6 )) && b=$"\n" || b=""
printf "%-${maxw}s${cm}:${cm}%s\n$b" "${cell[$rn.0]}" "${cell[$rn.$fn]}"
done; done; } done <"$f"