Rearrange columns using cut

后端 未结 8 1534
难免孤独
难免孤独 2020-11-28 03:53

I am having a file in the following format

Column1    Column2
str1       1
str2       2
str3       3

I want the columns to be rearranged. I tried below

8条回答
  •  情书的邮戳
    2020-11-28 04:27

    Using sed

    Use sed with basic regular expression's nested subexpressions to capture and reorder the column content. This approach is best suited when there are a limited number of cuts to reorder columns, as in this case.

    The basic idea is to surround interesting portions of the search pattern with \( and \), which can be played back in the replacement pattern with \# where # represents the sequential position of the subexpression in the search pattern.

    For example:

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

    yields:

    bar foo
    

    Text outside a subexpression is scanned but not retained for playback in the replacement string.

    Although the question did not discuss fixed width columns, we will discuss here as this is a worthy measure of any solution posed. For simplicity let's assume the file is space delimited although the solution can be extended for other delimiters.

    Collapsing Spaces

    To illustrate the simplest usage, let's assume that multiple spaces can be collapsed into single spaces, and the the second column values are terminated with EOL (and not space padded).

    File:

    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
    

    Transform:

    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
    

    Preserving Column Widths

    Let's now extend the method to a file with constant width columns, while allowing columns to be of differing widths.

    File:

    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
    

    Transform:

    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
    

    Lastly although the question's example does not have strings of unequal length, this sed expression support this case.

    File:

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

    Transform:

    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
    

    Comparison to other methods of column reordering under shell

    • Surprisingly for a file manipulation tool, awk is not well-suited for cutting from a field to end of record. In sed this can be accomplished using regular expressions, e.g. \(xxx.*$\) where xxx is the expression to match the column.

    • Using paste and cut subshells gets tricky when implementing inside shell scripts. Code that works from the commandline fails to parse when brought inside a shell script. At least this was my experience (which drove me to this approach).

提交回复
热议问题