0

I have this file that simply prints one line. I'm working on manipulating this one line with different sed commands.

apple orange.5678 dog cat 009 you

I'm wanting to grab 'orange.5678' and include 'you' and ignore everything else. I want it to look like below

orange.5678 you

I'm not sure where to start and how to exclude everything except for 'orange.5678' and 'you'. Any help would be great!

  • 6
    What is the logic that needs to be applied? Is it based on certain strings or by the column it's in? We can't guess what you want to do and answering these will move you closer to a solution. – Julie Pelletier Jan 21 '17 at 3:09
  • For starters, how would I exclude the period '.' to get '5678', but not '009'. How would I use the period as a marker to get the stuff I want etc...? – Nack Jan 21 '17 at 3:13
  • That is still very far from solving your whole problem. Work on it and edit the question with the required clarifications. FYI, cut -d'.' -f 2 might give you clues on how to do it. stackoverflow.com/questions/918886/… might also give you other approaches. – Julie Pelletier Jan 21 '17 at 3:23
5
$ sed -r 's/.* ([^ ]+\.[^ ]+).* ([^ ]+)$/\1 \2/' orange
orange.5678 you

Explanation

  • -r use extended regular expressions
  • s/old/new replace old with new
  • .* any number of any characters
  • (some characters) save some characters to reference later in replacement
  • [^ ]+ some characters that are not a space
  • \. literal dot
  • $ end of line
  • \1 backreference to saved pattern

so s/.* ([^ ]+\.[^ ]+).* ([^ ]+)$/\1 \2/ means, match anything on the line up to a space that precedes some non-space characters up to a . and then some non space characters after it (saving those characters either side of the .), then match any characters and save the last set of non-space characters on the line, and replace the whole match with the two saved patterns separated by a space

|improve this answer|||||
  • I am not nitpicking. Just a suggestion. How about adding a \s instead of a space. Make's it more readable. – fossil Jan 22 '17 at 5:13
  • @fossil more readable maybe, but uglier ;) – Zanna Jan 22 '17 at 11:39
  • Absolutely. I was wondering, if people don't copy paste, they may miss the space. BTW, your regex was inspiring and set's bar for everyone. – fossil Jan 22 '17 at 11:42
  • @fossil thanks a lot! I'm just a beginner really :) – Zanna Jan 22 '17 at 11:45
4

Simplest approach:

awk '{print $2, $6}' file.txt

If your actual use case is more complex than your question indicates, and you require additional logic (for example if it's not always the 2nd and 6th fields that you need), edit your question to clarify.

|improve this answer|||||
  • Also comment here if you edit your question; I won't get any notification if your question is updated. – Wildcard Jan 21 '17 at 5:21
1

One should look at another answer by @Zanna. Very elegant and shows the power of regular expressions.

Try this expression with gawk. Plain awk won't work with grouping.

^(?:\w+\s){0,}(\w+\.\w+)(?:\s\w+){0,}\s(\w+)$

It worked for following variations

apple orange.5678 dog cat 009 you
apple apple grape.9991 pig cat piegon owl
grape.9991 pig cat piegon owl

Here is the description of expression.

/
^(?:\w+\s){0,}(\w+\.\w+)(?:\s\w+){0,}\s(\w+)$
/
g
^ asserts position at start of the string

Non-capturing group (?:\w+\s){0,}
{0,} Quantifier — Matches between zero and unlimited times, as many times as possible, giving back as needed (greedy)
\w+ matches any word character (equal to [a-zA-Z0-9_])
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
\s matches any whitespace character (equal to [\r\n\t\f\v ])

1st Capturing Group (\w+\.\w+)
\w+ matches any word character (equal to [a-zA-Z0-9_])
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
\. matches the character . literally (case sensitive)
\w+ matches any word character (equal to [a-zA-Z0-9_])
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)

Non-capturing group (?:\s\w+){0,}
{0,} Quantifier — Matches between zero and unlimited times, as many times as possible, giving back as needed (greedy)
\s matches any whitespace character (equal to [\r\n\t\f\v ])
\w+ matches any word character (equal to [a-zA-Z0-9_])
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
\s matches any whitespace character (equal to [\r\n\t\f\v ])

2nd Capturing Group (\w+)
\w+ matches any word character (equal to [a-zA-Z0-9_])
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
$ asserts position at the end of the string, or before the line terminator right at the end of the string (if any)
|improve this answer|||||
  • This is terrific! Very good explanation! I was struggling with excluding characters after a certain character (in this case a period), but you simplified it. Thank you! – Nack Jan 21 '17 at 3:39
  • Let's us know if this worked for you. – fossil Jan 21 '17 at 3:41
  • 1
    That's a very overcomplicated regex that makes a lot of assumptions about the logic required. And makes zero use of Awk's specialty, which is handling delimited fields. – Wildcard Jan 21 '17 at 5:24
0

If it has to be sed with regex then above answer will cover you. If you are open to alternatives:

gv@debian: $ read -r a b c d e f<<<"apple orange.5678 dog cat 009 you" && echo "$b $f" 
orange.5678 you

If this is a line in a file replace <<<"...." with <file

This method to work requires default IFS = space. If in doube apply IFS=" " in the beginning.

|improve this answer|||||

Not the answer you're looking for? Browse other questions tagged or ask your own question.