Natalie Lloyd
Natalie Lloyd

Reputation: 33

Cut command to get second last word in each line | bash

I have a bash script where I am using grep to get every line that contains a certain date:

(grep -h "$date" $1) > requests.txt

an example line being..

95.81.72.148 - - [22/Jan/2019:08:01:41 +0330] "GET /image/5384/productModel/100x100 HTTP/1.1" 200 2280 "https://link" "Mozilla/5.0 (Windows NT 6.1; rv:60.0) IPADRESS Firefox/60.0" "-"

However I want to use piping and cut to somehow only store the IPADRESS of each line in the text file (the second to last word in the line)

How would I do this?

Upvotes: 2

Views: 2187

Answers (4)

Bastian Bittorf
Bastian Bittorf

Reputation: 117

A pure portable POSIX shell solution is to reverse the words in the line and then output the second word:

#!/bin/sh
LINE="foo * bar * myword lastword"
REV= && for _ in $LINE; do REV="$_ $REV"; done
set -f && set +f -- $REV
echo $2

This will output myword.
A handy oneliner function is:

#!/bin/sh
x() { local rev="" line="$1"; for _ in $line; do rev="$_ $rev"; done; set -f && set +f -- $rev; echo $2; }

so you can call it like:

x "AA BB CC DD"
CC

Hint: the $_ is just used as a throwaway variable.
Hint: the idea is from https://stackoverflow.com/a/71129132/5688306

Upvotes: 0

Ljm Dullaart
Ljm Dullaart

Reputation: 4969

You ask how to do it with cut, and you get answers that tell you to use awk or perl :-). Of course you can do this with cut too:

rev |cut -d' ' -f3 | rev

This fails if the last two "words" contain spaces. But then, so do the perl and awk solutions.

Upvotes: 3

Timur Shtatland
Timur Shtatland

Reputation: 12347

Use this Perl one-liner, which prints field number 3 counting from the end (last field being $F[-1]):

perl -lane 'print $F[-3]'

Example:

echo '95.81.72.148 - - [22/Jan/2019:08:01:41 +0330] "GET /image/5384/productModel/100x100 HTTP/1.1" 200 2280 "https://link" "Mozilla/5.0 (Windows NT 6.1; rv:60.0) IPADRESS Firefox/60.0" "-"' | \
  perl -lane 'print $F[-3]'

Output:

IPADRESS

The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-n : Loop over the input one line at a time, assigning it to $_ by default.
-l : Strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
-a : Split $_ into array @F on whitespace or on the regex specified in -F option.

SEE ALSO:
perldoc perlrun: how to execute the Perl interpreter: command line switches


BTW, cut is not the right tool for this task, because in cut, fields are numbered from the start. Meanwhile, in many scripting languages, such as Perl and awk, there is a method to split the line on whitespace, and count the fields from either the start or the end.

Upvotes: 0

chepner
chepner

Reputation: 531165

awk would be simpler, and it also subsumes the use of grep.

awk -v d="$date" '$0 ~ d { print $(NF - 2) }' "$1" > requests.txt

Upvotes: 3

Related Questions