122

All I want is the last two columns printed.

codeforester
  • 39,467
  • 16
  • 112
  • 140
vehomzzz
  • 42,832
  • 72
  • 186
  • 216
  • 6
    Not quite sure why this has 87 upvotes, it can certainly be improved with an example at the very least. – Arj May 11 '17 at 12:00
  • 1
    Probably because the question is extremely simple by nature and easily understood without an example, which is rare, but in this case it appears to work. The problem here is not lack of information I think, but more that it shows a lack of independent research. – DryLabRebel Aug 06 '20 at 22:57
  • This question is also a duplicate of [this](https://stackoverflow.com/questions/2096490/print-second-last-column-field-in-awk?rq=1) question. – DryLabRebel Aug 06 '20 at 23:14
  • 1
    Does this answer your question? [Print second last column/field in awk](https://stackoverflow.com/questions/2096490/print-second-last-column-field-in-awk) – DryLabRebel Aug 06 '20 at 23:15

6 Answers6

224

You can make use of variable NF which is set to the total number of fields in the input record:

awk '{print $(NF-1),"\t",$NF}' file

this assumes that you have at least 2 fields.

codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 1
    You need a comma - since we are being picky today: space concatenates fields, comma separates fields in a print statement. That will merge the two fields – jim mcnamara Nov 29 '10 at 15:06
  • 20
    Now you're printing "field-OFS-tab-OFS-field". It should be `awk '{print $(NF-1) "\t" $NF}' file` or `awk '{print $(NF-1), $NF}' file` or `awk 'BEGIN{OFS="\t"} {print $(NF-1), $NF}' file`. – Dennis Williamson Nov 29 '10 at 17:06
  • 1
    Just to add to the previous comment, the problem with using `'{print $x,"\t",$y}'` is that awk interprets each comma separated variable as it's own field, so the result will actually be `field1field2`, (because it will use white space delimiter by default) as opposed to `field1field2` which is probably what you're expecting. using Output Field Separator (OFS) is almost always what you want. – DryLabRebel Aug 06 '20 at 22:51
17
awk '{print $NF-1, $NF}'  inputfile

Note: this works only if at least two columns exist. On records with one column you will get a spurious "-1 column1"

jim mcnamara
  • 16,005
  • 2
  • 34
  • 51
  • 3
    Try and see. It does work Solaris 9 awk & nawk. The alternative is $(NF-1) – jim mcnamara Nov 29 '10 at 15:04
  • 1
    @coaddict - I'm guessing you haven't worked with different awk implementations. Old awks behaviors have been (maybe erroneously) brought forward. I do not have gawk to test against -- which is probably what you are referencing. So I don't know for sure why your comment came abnput. Linux awk out of the box usually is gawk. I will test and post back. Meanwhile try Soalris or HPUX or DGX or whatever to see what I mean by old awk. – jim mcnamara Nov 29 '10 at 15:14
  • 6
    You probably got tricked into thinking it worked because you tried `echo 1 2 3 | awk ...`. `$NF-1` is `($NF) - 1` in every `awk` implementation. – Stephane Chazelas Feb 09 '14 at 15:55
  • The "One True Awk" source code has more than 40 conflicts in the `yacc` grammar, which is ironic given what the A stands for in awk. Different versions of awk parsing things differently? Big surprise! – Kaz Feb 21 '14 at 06:01
  • The fact that you state that with one-column input you get `-1` as the first output token indicates that @StephaneChazelas is right. If your claim about "old" awk's behavior were correct, the first token should effectively be `$0` i.e., the entire input line. Since the entire input line is just one token in this case - the first and only column - you should get that one column value *twice*. – mklement0 Feb 24 '14 at 01:55
  • awk -V | head -n1 ; echo '1 2 3 4 5' | awk '{print $NF-1,$NF}' ....... GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.3, GNU MP 6.1.0) ....... 4 5 .... this is working on this awk version, I tried this piece of code some years ago and didn't worked, so, if this answer is right or wrong depends on awk version – THESorcerer Apr 27 '16 at 08:33
  • 1
    @THESorcerer, try with `echo '5 4 3 2 1' | awk '{print $NF-1,$NF; print $(NF-1), $NF}'` -- or any other input where the 2nd last field is not one less than the last field. – glenn jackman Apr 30 '18 at 20:24
  • `nawk`, `mawk` and `gawk` on Ubuntu and `awk` on macOS all need the parens. Even [Heirloom `oawk`](http://heirloom.sourceforge.net/tools.html) does. – Dennis Williamson Oct 09 '18 at 21:19
6

@jim mcnamara: try using parentheses for around NF, i. e. $(NF-1) and $(NF) instead of $NF-1 and $NF (works on Mac OS X 10.6.8 for FreeBSD awkand gawk).

echo '
1 2
2 3
one
one two three
' | gawk '{if (NF >= 2) print $(NF-1), $(NF);}'

# output:
# 1 2
# 2 3
# two three
Zombo
  • 1
  • 62
  • 391
  • 407
guzo
  • 69
  • 1
  • 1
  • We had already considered () earlier. I thought we were discussing where the original old awk behavior came from. – jim mcnamara Oct 19 '13 at 15:44
  • +1 for an answer with explicit `$(NF-1)` - which at the very least is more portable than `$NF-1`; it's definitely less ambiguous. `$(NF)` is overkill, though - just `$NF` will do. Guarding against lines with fewer than 2 columns is worthwhile too, as with one-column lines you'd get the first column value *twice*, and with zero-column - i.e., empty - lines the awk command would fail altogether, due to an attempt to access a field with index -1. – mklement0 Feb 24 '14 at 02:33
  • We can make it, I think, a little shorter: `gawk 'NF>=2 {print $(NF-1), $NF}'` – SergioAraujo Nov 08 '20 at 10:05
1

using gawk exhibits the problem:

 gawk '{ print $NF-1, $NF}' filename
1 2
2 3
-1 one
-1 three
# cat filename
1 2
2 3
one
one two three

I just put gawk on Solaris 10 M4000: So, gawk is the cuplrit on the $NF-1 vs. $(NF-1) issue. Next question what does POSIX say? per:

http://www.opengroup.org/onlinepubs/009695399/utilities/awk.html

There is no direction one way or the other. Not good. gawk implies subtraction, other awks imply field number or subtraction. hmm.

mklement0
  • 382,024
  • 64
  • 607
  • 775
jim mcnamara
  • 16,005
  • 2
  • 34
  • 51
  • 1
    The first 2 lines of your sample input file are not helpful in that they produce the same output with _either_ behavior. Can you please reconfirm that Solaris awk does indeed NOT behave like gawk in this case? – mklement0 Feb 24 '14 at 02:46
  • As for your link to the awk spec: The anecdotal argument for using `$(NF-1)` is that the two examples of computing the field index in the spec both use that form: `$(NF-1)` and `$(NF+2)`. Then there's the "Expressions in awk" section, which lists `$expr` as having [much] higher precedence than `expr - expr`. Since `NF` is an expression itself, `$NF-1` should evaluate to `($NF)-1`. Even IF, after all, there are indeed awk implementations out there that evaluate `$NF-1` as `$(NF-1)`, the lesson learned here is that using `$(NF-1)` is the safe and portable choice. – mklement0 Feb 24 '14 at 02:54
1

Please try this out to take into account all possible scenarios:

awk '{print $(NF-1)"\t"$NF}'  file

or

awk 'BEGIN{OFS="\t"}' file

or

awk '{print $(NF-1), $NF} {print $(NF-1), $NF}' file
DryLabRebel
  • 8,923
  • 3
  • 18
  • 24
Marios
  • 26,333
  • 8
  • 32
  • 52
0

try with this

$ cat /tmp/topfs.txt
/dev/sda2      xfs        32G   10G   22G  32% /

awk print last column
$ cat /tmp/topfs.txt | awk '{print $NF}'

awk print before last column
$ cat /tmp/topfs.txt | awk '{print $(NF-1)}'
32%

awk - print last two columns
$ cat /tmp/topfs.txt | awk '{print $(NF-1), $NF}'
32% /