1

I have a file that contains may tab-separated columns in the following format

AAA BBB;CCC;DDD;E=10;F=20 GGG
XXX YYY;ZZZ;DDD;E=50;F=40 PPP

I need to print the values E (or F) using an UNIX command.

  10
  50
1
  • What have you tried? Do all lines have an = character? Off the top of my head, awk would be an easy tool to use for this as it allows you both process only lines with a certain text pattern, and extract data from delimited fields. Looks like you could specify a blank, ;, and = as field separators. Jan 26, 2021 at 18:54

5 Answers 5

10

Use cut to obtain the fourth field, then sed to remove everything up to the equals sign:

cut -f 4 -d\; | sed 's/.*=//'

However, you mention tab-separated fields, though your sample lines separate fields by semicolons.

4
  • 1
    Why was this downvoted? It may be considered less elegant than some of the other solutions because it launches two processes, but it certainly works, and it is short and simple.
    – wrtlprnft
    Jan 23, 2021 at 14:04
  • 2
    Indeed. This is exactly the sort of solution I would use. The input consists of columns based on a certain structure, and then certain columns have their own substructure. It's entirely appropriate -- for both clarity and generality -- to treat the structure and the substructure separately. Jan 23, 2021 at 15:24
  • Agreed with the above, only to note that cut -f4 -d\; | cut -f2 -d= exploits the similarities between structures. Handling the 5th original column is harder Jan 24, 2021 at 0:00
  • 1
    Well, I provided a narrow solution tailored to the question as an alternative to the awk and grep solutions (which are at least as valid, useful and elegant as mine). I agree that my solution doesn't address more general problems, but since this was not its goal, I stand by it :). Jan 24, 2021 at 0:55
9

With GNU grep, using PCRE (-P) mode:

$ grep -Po '(?<=E=)\d+' file
10
50

or

$ grep -Po '(?<=F=)\d+' file
20
40
1
  • 2
    Also convenient to write as E=\K\d+. The \K resets the start of the match.
    – JoL
    Jan 23, 2021 at 23:29
3
$ echo 'AAA BBB;CCC;DDD;E=10;F=20 GGG' | awk -F';' '{ print $4 }' | awk -F= '{ print $2 }'
10
$ echo 'XXX YYY;ZZZ;DDD;E=50;F=40 PPP' | awk -F';' '{ print $4 }' | awk -F= '{ print $2 }'
50

Or even easier:

$ echo 'XXX YYY;ZZZ;DDD;E=50;F=40 PPP' | awk 'match($0, /=[0-9]+/) { print substr($0, RSTART + 1, RLENGTH - 1) }'
50
$ echo 'AAA BBB;CCC;DDD;E=10;F=20 GGG' | awk 'match($0, /=[0-9]+/) { print substr($0, RSTART + 1, RLENGTH - 1) }'
10
3

Also with awk you could try this"

cat file
AAA BBB;CCC;DDD;E=10;F=20 GGG
XXX YYY;ZZZ;DDD;E=50;F=40 PPP
  • print E
awk -F'[=; ]' '{ print $6}' file
10
50
  • or F
awk -F'[=; ]' '{ print $8}' file
20
40
0

If you don't care about the rest of the line, if you just want to find E= or F= wherever they appear, you could use sed:

sed -e 's/.*E=//' -e 's/;.*//'

This deletes everything up to and including "E=", and then deletes the ; and everything following it. It assumes that every line does contain "E=".

If you know that the E= field always appears in a certain column (with the columns separated by tabs, or semicolons, or whatever), it might be preferable to first extract that column using cut or the equivalent, then work on separating the E= part from the value. You could do that using sed as in @berndbausch's answer, or use a second invocation of cut assuming =-separated columns.

2
  • 1
    Why not simply sed 's/.*;E=\([0-9]\+\);.*/\1/', without the second -e?
    – Ruslan
    Jan 24, 2021 at 0:12
  • because it's more understandable by a beginner @Ruslan
    – Grump
    Jan 24, 2021 at 12:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .