回显而不修剪awk命令中的空格

时间:2019-01-02 15:56:57

标签: linux bash

我有一个包含多行的文件

10|EQU000000001|12345678|3456||EOMCO042|EOMCO042|31DEC2018|16:51:17|31DEC2018|SHOP NO.5,6,7 RUNWAL GRCHEMBUR      MHIN|0000000010000.00|6761857316|508998|6011|GL

我必须使用字符计数将第11列拆分并替换为4个不同的列。

这是第11列,也包含多余的空格。

SHOP NO.5,6,7 RUNWAL GRCHEMBUR      MHIN

这是我已经完成的

ls *.txt *.TXT| while read line
do
subName="$(cut -d'.' -f1 <<<"$line")"
 awk -F"|"   '{ "echo  -n "$11" | cut -c1-23" | getline ton;
             "echo -n  "$11" | cut -c24-36" | getline city;
             "echo -n  "$11" | cut -c37-38" | getline state;
             "echo -n  "$11" | cut -c39-40" | getline country;
$11=ton"|"city"|"state"|"country; print $0

}' OFS="|" $line > $subName$output

完成

但是在执行第11列的回显时,它会修剪多余的空格,从而导致字符计数不匹配。有什么方法可以在不修剪空格的情况下进行回显?

实际输出

10|EQU000000001|12345678|3456||EOMCO042|EOMCO042|31DEC2018|16:51:17|31DEC2018|SHOP NO.5,6,7 RUNWAL GR|CHEMBUR MHIN|||0000000010000.00|6761857316|508998|6011|GL

预期产量

10|EQU000000001|12345678|3456||EOMCO042|EOMCO042|31DEC2018|16:51:17|31DEC2018|SHOP NO.5,6,7 RUNWAL GR|CHEMBUR|MH|IN|0000000010000.00|6761857316|508998|6011|GL

2 个答案:

答案 0 :(得分:4)

到目前为止,我发现的最讨厌的编码方法是:

perl -F'\|' -lane '$F[10] = join "|", unpack "a23 A13 a2 a2", $F[10]; print join "|", @F'

这很简单:

  • 遍历输入行;在|上拆分每一行,并将字段放入@F
  • 对于第11个字段($F[10]),使用unpack将其拆分为固定宽度的子字段(并修剪第二个字段(A而不是a的尾随空格))。
  • 通过与|结合来重组子字段。
  • 通过与|合并并打印来整行。

我还没有对它进行基准测试,但是它可能比原始代码快得多,因为它是在一个过程中完成的,所以原始代码每条输入行产生多个shell和cut进程。

完整的解决方案会将其包装在shell循环中:

for file in *.txt *.TXT; do
    outfile="${file%.*}$output"
    perl -F'\|' -lane '...' "$file" > "$outfile"
done

或者如果您不需要修剪.txt部分(并且您没有太多文件可容纳在命令行中):

perl -i.out -F'\|' -lane '...' *.txt *.TXT

这只是将每个输入文件foo.txt的输出放在foo.txt.out中。

答案 1 :(得分:2)

所有逻辑的纯bash实现

#!/usr/bin/env bash
shopt -s nocaseglob extglob
for f in *.txt; do
  subName=${f%.*}
  while IFS='|' read -r -a fields; do
    location=${fields[10]}
    ton=${location:0:23}; ton=${ton%%+([[:space:]])}
    city=${location:23:12}; city=${city%%+([[:space:]])}
    state=${location:36:2}
    country=${location:38:2}
    fields[10]="$ton|$city|$state|$country"
    printf -v out '%s|' "${fields[@]}"
    printf '%s\n' "${out:0:$(( ${#out} - 1 ))}"
  done <"$f" >"$subName.out"
done

它比 pure awk慢(如果我做得好,大约10倍),但是比问题中提出的awk / shell组合要快得多。


进入使用的结构:

  • 所有${varname%...}和相关构造都是parameter expansion。特定的${varname%pattern}构造会从pattern中的值中删除varname的最短匹配项,或者将%替换为%%时的最长匹配项。
  • 使用extglob启用扩展的glob语法,例如+([[:space:]]),它等效于正则表达式语法[[:space:]]+