使用awk重新排列列


13

我正在尝试通过使用将csv文件的第7列移至末尾

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

其中$ file是目录中的.csv文件。但是,输出是

awk:                          ^ syntax error

有人知道如何解决此错误吗?


7
显示awk错误时,您需要显示整个内容。该^指示在那里遇到错误命令的特定部分。
terdon

Answers:


11

-F选项需要一个参数:-F,例如。

awk脚本的末尾必须用(space char)与其余参数分开。

如果字段分隔符是,并且希望保留它,并且列数是恒定的并且小于或等于11,请尝试以下操作:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"

8
@anuribs很少有程序允许这样做。标准方法是command file > newfile && mv newfile file。也就是说,较新版本的GNU awk支持此功能:gawk -i inplace '{blah blah}' file
terdon

1
或者,代替mv newfile file您可以使用cat newfile > file ; rm -f newfile-这将保留inode和的权限file
cas

通常,最好使用mktemp而不是将临时文件名硬编码到脚本中。例如tf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas

8

较短的解决方案是

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

我不确定是否,+可以在所有awk版本中使用,但至少在GNU awk中也可以使用-c兼容模式。

说明:

  • $(NF+1)=$7:首先我们在行尾添加第7个字段($12=$7在这种情况下可能是)
  • $7="":在下一步中,第7个字段将被删除(但周围的定界符会保留)
  • 要删除定界符,我们需要重新设置整个记录(通过$0=$0),将多个逗号作为字段分隔符(这是通过来完成的-F',+',这里+表示一次或多次),还需要通过重新排列当前记录$1=$1来强制使用先前设置的输出字段来重建行分隔符(由选项设置-v OFS=,
  • 在完成所有改组之后,我们准备打印结果 1

输入示例:

1,2,3,4,5,6,7,8,9,10,11

输出

1,2,3,4,5,6,8,9,10,11,7

如果其他列为空白怎么办?但是,是的,FS是POSIX中的正则表达式(如果它是多个字符),因此,+应该可以工作。
Random832 '16

(1)我知道使输入数据的第七列“消失”,而不仅仅是将其设置为null是这个问题的棘手部分。但是,正如Random832所说,您的解决方案会阻塞空白列(例如all,ball,call,,,fallall,ball,call,fall)。(2)  $(NF+1)=$7是一个聪明的方法。恕我直言,$0 = $0 OFS $7它更清晰一点,只有几个字符更长,而且似乎做同样的事情。您能想到$0 = $0 OFS $7与您的代码不同的情况吗?
G-Man说'Resstate Monica''July

@ Random832 @ G-Man是的,某些边缘情况,例如空白字段,空白行或NF <7,应分开处理,或者应重新排列代码。这只是一个想法,而不是针对所有一般情况的“完整解决方案”,应该清楚。$0=$0 OFS $7可能与相同$(NF+1)=$7,但仅在其余代码不变的情况下(一般而言)。
jimmij

5

如果使用进行打印OFS=,则字段之间没有分隔符,则只需将$7变量的值保存在变量中,设置$7为空即可直接打印行和变量。您无需指定所有字段:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687


3

您并没有明确表示要使用awk,而是确实要使用像所提供的就地编辑sed -i,所以这是一个sed -i变体。通常awk,使用列会更好,但这是我更喜欢的一种情况sed,因为它自然可以处理任意数量的列。

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

说明:

  • -r 选择扩展的正则表达式,因此我们避免了很多反斜杠
  • 第一组是$ N个以逗号结尾的字符串的重复,换句话说,我们要移动的字符串之前的列,最后一个逗号
  • 第二组是第N次重复,我们忘记了
  • 第三组是我们要移动的列,没有最后一个逗号
  • 第四组由我们要移动的一列之后的所有列组成,前面没有逗号
  • 我们用第一组,最后一组和提取的列替换,并根据需要插入逗号。

当然,这对于用引号将逗号隐藏(或更糟糕的是,将其转义)的文件将不起作用,但是awk在没有一些严重的杂技的情况下也无法解决。如果您有此问题,最好使用perlmodule Text:CSVpythonmodule csv


2

几个awk变体(假设您的文件位于变量中$file

  • 在这里,您可以循环显示所有列,使用字段分隔符(OFS)打印,并在行尾打印记录终止符(ORS)。

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • 在这里使用正则表达式和gensub()功能

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    杀死 7 字段并且在该行的末尾打印。

    • $0 是整个记录
    • $n是第n 记录
    • NF 是当前行的字段数
    • OFS 输出字段分隔符
    • ORS 输出记录终止符
    • 1说awk true并打印默认值($0)的技巧。

更新 ...

我差点忘了,它可以将所有继7列转移之一。

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"

(1)可以说,它OFS $7会比强大"," $7。(2)我认为这", " $7是错误的,因为问题表明OP不需要在逗号后加空格。(而且,如果输入数据在逗号后有空格,则该空格$7将以空格开头,并且您将添加一个空格。)
G-Man说'Reinstate Monica''Reinstate Monica'16

@ G-Man主要是提出一些想法,一些变体。谢谢,我对此表示同意OFS $7,不仅更强大,而且更一般(“浪费使浪费”
Hastur
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.