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. – Mark Stewart Jan 26 at 18:54
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 at 23:29
9

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 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. – Steve Summit Jan 23 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 – D. Ben Knoble Jan 24 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 :). – berndbausch Jan 24 at 0:55
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
2
$ 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
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 at 0:12
  • because it's more understandable by a beginner @Ruslan – Grump Jan 24 at 12:40

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.