I'm looking for a way to switch the last two fields of a tab-separated file. The number of fields vary among rows, so I can't specify the field number.

Most of what I could find online was field-specific scripts. E.g. swap two columns with awk


4 Answers 4

awk -F"\t" -v OFS="\t" 'NF>1 {t=$(NF-1); $(NF-1)=$NF; $NF=t} {print}' file

Do a standard swap between $NF and $(NF-1) only when we have at least 2 fields. It's fatal error to access field -1 (in case of empty line, NF==0).

See also: GNU awk fields


Using Raku (formerly known as Perl_6)

raku -ne 'join( "\t", .words[0..*-3], .words[*-1] // "", .words[*-2] // "").trim-leading.put ;' 

Basically, the code above creates a list of whitespace-separated elements (words) in the desired order (last two elements switched). The elements are joined with \t tab, any leading whitespace is trimmed away with trim-leading, and the result is output. This code works on tab-separated columns as well as on space-separated columns.

Note, Raku's // defined-OR operator is used to avoid issues with Nil values in String context--an "" empty string is 'inserted' where necessary.

Sample Input (first line is blank):

A   B
A   B   C
A   B   C   D
A   B   C   D   E
A   B   C   D   E   F

Sample Output (last two elements on each line are swapped--first line is still blank):

B   A
A   C   B
A   B   D   C
A   B   C   E   D
A   B   C   D   F   E



You can make use of the built-in variable NF to refer to the last field to print it before the second last field (NF-1):

awk 'NF <= 1; NF > 1 {for (i = 1; i <= NF-2; i++) printf "%s\t", $i; print $NF"\t"$(NF-1)}' file.tsv

One way can be :

awk -F '\t' '
  p = length-length($(NF-1)"x"$NF)
  print substr($0,1,p) $NF, $(NF-1)
}!f' file

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.