使用削减重新排列列

我有一个以下格式的文件

Column1    Column2
str1       1
str2       2
str3       3

我要重新排列纵队,我试过以下命令

Cut-f2,1 file. txt

命令没有重新排列列序。知道为什么它不工作吗?

141948 次浏览

对于 cut(1)手册页:

使用-b、-c 或-f 中的一个,每个 LIST 由 一 范围,或许多范围被逗号分隔。选择的输入被写入 按照它被读取的顺序,并且只被写入一次。

它首先到达字段1,因此被打印出来,然后是字段2。

改为使用 awk:

awk '{ print $2 " " $1}' file.txt

仅仅使用外壳,

while read -r col1 col2
do
echo $col2 $col1
done <"file"

你也可以把 cutpaste结合起来:

paste <(cut -f2 file.txt) <(cut -f1 file.txt)

通过评论: 通过以下方式避免抨击和删除一个实例是可能的:

paste file.txt file.txt | cut -f2,3

只是一直在做一些非常相似的工作,我不是一个专家,但我想我会分享我使用的命令。我有一个多列 csv,我只需要4列出来,然后我需要重新排序他们。

我的文件是管道’|’分隔,但可以交换出来。

LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv

不可否认,它真的是粗糙和准备,但它可以调整到适合!

您可以使用 Perl:

perl -ane 'print "$F[1] $F[0]\n"' < file.txt
  • - e 选项意味着在它之后执行命令
  • N 表示逐行读取(打开文件,在本例中为 STDOUT,并在行上循环)
  • - a 意味着将这些线分割成一个向量@F (“ F”-like Field)。Perl 从0开始索引向量,而 cut 从1开始索引字段。
  • 在读取文件时,可以添加 -F 模式(在 -F 和 模式之间没有空格) ,以使用 模式作为字段分隔符,而不是默认的空格

运行 Perl 的好处是(如果您了解 Perl) ,您可以在 F 上执行比重新排列更多的计算。

使用 join:

join -t $'\t' -o 1.2,1.1 file.txt file.txt

备注:

  • GNUjoin中,更直观的 -t '\t'没有中,$失败了(CoreutilsV8.28和更早的?); 这可能是一个 bug,像 $这样的工作区应该是必要的。见: Unix 连接分隔符字符

  • 尽管只处理 文件,但是 join语法需要 文件名。重复文件名允许 join执行所需的操作。

  • 对于资源较少的系统,join比其他答案中使用的一些工具占用的空间更小:

     wc -c $(realpath `which cut join sed awk perl`) | head -n -1
    43224 /usr/bin/cut
    47320 /usr/bin/join
    109840 /bin/sed
    658072 /usr/bin/gawk
    2093624 /usr/bin/perl
    

使用 sed

使用 sed和基本正则表达式的嵌套子表达式来捕获和重新排序列内容。这种方法最适合于重新排序列的切割次数有限的情况,如本例所示。

基本思想是用 \(\)包围搜索模式中有趣的部分,这些部分可以在用 \#替换的模式中播放,其中 #代表搜索模式中子表达式的顺序位置。

例如:

$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"

收益率:

bar foo

扫描子表达式外部的文本,但不保留以便在替换字符串中进行回放。

虽然这个问题没有讨论固定宽度的列,但是我们将在这里讨论,因为这是对所提出的任何解决方案的有价值的度量。为了简单起见,我们假设文件是空格分隔符,尽管解决方案可以扩展为其他分隔符。

塌缩空间

为了说明最简单的用法,让我们假设多个空格可以折叠成单个空格,并且第二列值以 EOL (而不是空格填充)结束。

资料:

bash-3.2$ cat f
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  nl
0000040    s   t   r   2  sp  sp  sp  sp  sp  sp  sp   2  nl   s   t   r
0000060    3  sp  sp  sp  sp  sp  sp  sp   3  nl
0000072

转变:

bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  nl
0000020    1  sp   s   t   r   1  nl   2  sp   s   t   r   2  nl   3  sp
0000040    s   t   r   3  nl
0000045

保留列宽

现在让我们将该方法扩展到具有常量宽度列的文件,同时允许列具有不同的宽度。

资料:

bash-3.2$ cat f2
Column1    Column2
str1       1
str2       2
str3       3
bash-3.2$ od -a f2
0000000    C   o   l   u   m   n   1  sp  sp  sp  sp   C   o   l   u   m
0000020    n   2  nl   s   t   r   1  sp  sp  sp  sp  sp  sp  sp   1  sp
0000040   sp  sp  sp  sp  sp  nl   s   t   r   2  sp  sp  sp  sp  sp  sp
0000060   sp   2  sp  sp  sp  sp  sp  sp  nl   s   t   r   3  sp  sp  sp
0000100   sp  sp  sp  sp   3  sp  sp  sp  sp  sp  sp  nl
0000114

转变:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1       str1
2       str2
3       str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   2  sp  sp  sp  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl
0000114

最后,尽管问题的示例没有不等长的字符串,但是这个 sed表达式支持这种情况。

资料:

bash-3.2$ cat f3
Column1    Column2
str1       1
string2    2
str3       3

转变:

bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1
1       str1
2       string2
3       str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000    C   o   l   u   m   n   2  sp   C   o   l   u   m   n   1  sp
0000020   sp  sp  nl   1  sp  sp  sp  sp  sp  sp  sp   s   t   r   1  sp
0000040   sp  sp  sp  sp  sp  nl   2  sp  sp  sp  sp  sp  sp  sp   s   t
0000060    r   i   n   g   2  sp  sp  sp  nl   3  sp  sp  sp  sp  sp  sp
0000100   sp   s   t   r   3  sp  sp  sp  sp  sp  sp  nl
0000114

壳层下列重排序方法的比较

  • 令人惊讶的是,对于文件操作工具,awk并不适合从字段到记录末尾的切割。在 sed中,这可以通过使用正则表达式来实现,例如 \(xxx.*$\),其中 xxx是与列匹配的表达式。

  • 在实现 shell 内部脚本时,使用 pastecut子 shell 会很棘手。从命令行工作的代码在引入 shell 脚本时无法解析。至少这是我的经验(驱使我采取这种方法)。

同样使用 Perl 的@Met 扩展了答案:
如果输入和输出以 TAB 分隔:

perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file

如果输入和输出以空格分隔:

perl -lane 'print join " ", @F[1, 0]' in_file

给你,
-e告诉 Perl 在内联中查找代码,而不是在单独的脚本文件中,
-n每次读取输入1行,
-l在读取行(类似于 chomp)之后删除输入记录分隔符(\n on * NIX) ,并将输出记录分隔符(\n on * NIX)添加到每个 print,
-a将空格上的输入行拆分为数组 @F,
-F'\t'-a组合在一起将 TABs 上的输入行分割成数组 @F,而不是将空格分割成数组 @F

@F[1, 0]是由数组 @F的第2个和第1个元素组成的数组,顺序如下。请记住,Perl 中的数组是零索引的,而 cut中的字段是1索引的。因此,@F[0, 1]中的字段与 cut -f1,2中的字段相同。

请注意,这种表示法比上面提到的其他一些答案(对于一个简单的任务来说是很好的)能够更灵活地处理输入。例如:

# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file


# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file

只是作为一个附加的答案,建议复制列,然后做 cut。对于复制,paste等仅适用于文件,但不适用于流。在这种情况下,改用 sed

cat file.txt | sed s/'.*'/'&\t&'/ | cut -f2,3

这同时适用于文件和流,如果您不仅仅使用 cat读取文件,而是使用 之前重新排列列,那么这将非常有趣。

相比之下,以下方法并不奏效:

cat file.txt | paste - - | cut -f2,3

在这里,双 stdin 占位符 paste并不重复 stdin,而是读取下一行。