-1

I am trying to understand cut better and moving cut data to another file. I have a file called numbers with two columns. Each column is separated with one tab.

I am trying to swap the columns by using the cut command and saving the cut command output to another file. I have no problem cutting field 2 and saving the output to the copynumber file. But I don't know how to cut field 1 to field 2 in the output file.

I am looking for solutions that only use bash shell scripting rather than any other languages like awk.

#I have tried the following commands:
$cat numbers
1       2
10      20
100     200
1000    2000
10000   20000
100000  300000
1000000 3000000

cat numbers | cut -f 2 > copynumbers

#How do I get field 1 from the original file into field 2 of the output file?
$cat copynumbers
2
20
200
2000
20000
300000
3000000
  • 2
    First, you don't need cat. Second, awk is lightyears better for what you are trying to do if you just put in the effort. – Nasir Riley Apr 21 at 2:51
  • Couldn't agree more on "lightyears better" – Weijun Zhou Apr 21 at 4:47
1

If you are comfortable with perl, one of the collection of such utilities might be useful. Here the TSV input is on file z5:

$ recut 2,1 z5
2       1
20      10
200     100
2000    1000
20000   10000
300000  100000
3000000 1000000

Some information on recut in The Missing Textutils collection:

recut   Process fields like cut, allow repetitions and re-ordering. (what)
Path    : ~/bin/recut
Version : - ( local: RepRev 1.1, ~/bin/recut, 2010-06-10 )
Length  : 56 lines
Type    : Perl script, ASCII text executable
Shebang : #!/usr/bin/perl
Home    : http://www1.cuni.cz/~obo/textutils/ (doc)
Modules : (for perl codes)
 Getopt::Long   2.42

Our shop generalized tac to deal with situations like this:

$ my-tac --field=0 z5
2 1
20 10
200 100
2000 1000
20000 10000
300000 100000
3000000 1000000

We have not released our library as of yet, but if you wanted to re-create it, this is the help:

my-tac - reverse any one property: lines (like tac), fields, characters.

The default is to reverse the lines in a file, so a file like:
        a
        b
        c
will be printed as:
        c
        b
        a

usage: my-tac [options] -- [files]

options:

--help (or -h)
  print this message and quit.

--character
  Reverse order of characters in each line.  That is, given:
    abc
  the result is:
    cba

--field=0
  Reverse order of fields.  That is, given:
    Now is the time
  the result is:
    time the is Now

--field=i,j,k
  Reverse content of specific fields i,j,k.  That is given
    Now is the time
  my-tac --field=1,3  wil result in:
    woN is eht time

--para
  Reverse order of paragraphs, which are groups of lines
  separated by one of more empty lines.  If the last paragraph is
  not followed by an enpty line, one is supplied.

--number=n
  Print only n lines for a file reversal. <no limit>.

--debug
  Print internal debugging information.  <off>.
  (Must be first option.)

--separator=",re,string"
  Set the input separator to regular expression re, <\s+>, and
  the output separator to string, < >.  So the default is
  ",\s+, ".  Any character may be used in place of the comma, so
  you could specify:
    --separator=';\s+;|'

Best wishes ... cheers, drl

0

Using process substitution and paste:

$ paste <(cut -f2 numbers) <(cut -f1 numbers)
2        1
20       10
200      100
2000     1000
20000    10000
300000   100000
3000000  1000000
0

I would argue that using awk to solve this task would fall under what's generally considered to be "shell scripting":

awk -F '\t' 'BEGIN { OFS=FS } { print $2, $1 }'

This first sets the input delimiter to a tab using -F '\t', and then the BEGIN block sets the output delimiter to the same character. The body of the only block simply outputs the two fields in opposite order.

Testing:

$ awk -F '\t' 'BEGIN { OFS=FS } { print $2, $1 }' numbers
2       1
20      10
200     100
2000    1000
20000   10000
300000  100000
3000000 1000000

A more general approach for reversing all columns, no matter how many there are:

BEGIN { OFS=FS }
{
    for (i = 1; i <= NF/2; ++i ) { 
        t=$i; $i=$(NF-i+1); $(NF-i+1)=t
    }
    print
}

This walks through the columns from start to halfway through the line, swapping each with the corresponding column from the end. For input data with an odd number of columns, the middle column would remain untouched.


Using your initial approach of creating a temporary file with one of the columns would also work:

cut -f 2 numbers >tmpfile

Pasting the original file to this would create a data set with three columns (column 2, 1, and 2 again):

paste tmpfile numbers

We can then remove the last column with cut:

paste tmpfile numbers | cut -f 1,2

Or, we can do it without using that temporary file to start with:

cut -f 2 numbers | paste - numbers | cut -f 1,2

Note that all solutions using cut would by necessity have to read the original data twice (in general, as many times as there are columns, if you want to reverse all columns).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.