4

I have to replace the last word from each line with the first one. The code is:

$ sed "s/\(^a-z,0-9]*\)\(.*\)\([a-z,0-9]*$\)/\1\2\1\g". 

I don't understand this part \(^a-z,0-9]*\)\(.*\)\([a-z,0-9]*$\) especially \(.*\).

  • This is a Stack Overflow question more than it is an Ask Ubuntu question. You can ask anything about "tell me what my regex code does" at Stack Overflow, because Stack Overflow is there for questions like this. I know a user at Stack Overflow who has more than 100k reputation and a lot of it is only from answering regex questions. That's the kind of service you would probably get if you asked your regex question at Stack Overflow. Tag it with "regex" so it can be easily found there by anyone who subscribes to the regex tag. – karel Mar 26 '17 at 15:44
  • 4
    Close voters: text processing questions are absolutely on topic here. – Zanna Mar 26 '17 at 16:14
  • 2
    I'm a close voter with a conscience. I don't consider text processing questions to be at all off topic, but I do consider "tell me what my code does" posted by a Stack Overflow user to be off topic, because it's a Trojan horse for asking all sorts of code debugging questions where they don't belong. There's 13,554,938 questions on Stack Overflow and 258,465 questions on Ask Ubuntu, so Ask Ubuntu should not be used as a mirror for Stack Overflow. It's better to use Stack Overflow as a mirror for Ask Ubuntu because it's such a huge site that it can easily absorb a few Ubuntu questions. – karel Mar 26 '17 at 16:36
  • @karel you are awesome and I appreciate your conscience. But, I think questions should be closed (and otherwise voted on) on their own merits rather than in relation to who asks them or what might follow. Maybe I'm biased because these are the questions I like answering and I have no wish to move myself to SO :S – Zanna Mar 26 '17 at 17:23
10

After correcting the basic syntax mistakes, you have:

sed "s/\(^[a-z,0-9]*\)\(.*\)\([a-z,0-9]*$\)/\1\2\1/g"
  • s/old/new/ replace old with new
  • \(^[a-z,0-9]*\) save any number of lowercase letters or numbers at the start of the line (^ is start of line) for later (reference later with \1)
  • \(.*\) Save any number of any characters for later (to reference as \2)
  • \([a-z,0-9]*$\) save any number of lowercase letters or numbers at the end of the line ($ is end of line) for later (reference as \3)
  • \1\2\1 print the first pattern, then the second, then the first again
  • g this is inappropriate in this expression. It means act on multiple matches on the same line, but our expression has to read the whole line, so g makes no sense and should be omitted.

This still will not work, because regular expressions are greedy, so the middle \(.*\) matches everything after the first word, resulting in the first word being reprinted at the end of the line without replacing anything.

You could fix it (also adding I for case-insensitive search):

sed "s/\(^[a-z,0-9]*\) \(.*\) \([a-z,0-9]*$\)/\1 \2 \1/I"

If you wanted to include other characters besides letters and numbers:

sed -r 's/^([^ ]+) (.*) ([^ ]+)$/\1 \2 \1/'
  • -r use ERE (saves using all those backslashes)
  • [^ ]+ at least one of any characters except spaces
3

I don't think your code is even going to work here because it's simply malformed. I'd rewrite the entire expression. Assuming that all lines begin and end with words that contain only numbers and letters from the alphabet, you could try this:

$ echo -en "foo bar baz\nThe Good, the Bad and the Ugly\n" | \
> sed 's/^\(\<[[:alpha:]]\+\>\)\(.*\)\<[[:alpha:]]\+\>$/\1\2\1/g'
foo bar foo
The Good, the Bad and the The

Brief explanation:

s/PATTERN/SUBSTITUTION_STRING/g - PATTERN is what we're looking for and SUBSTITUTION_STRING is what we're going to substitute said pattern with. g means that the entire line will be scanned as opposed to going only for the first match found on the line.

^\(\<[[:alpha:]]\+\>\) - if a line begins with a word boundary that's followed by more than one alphanumeric character followed by another word boundary, save all that in the variable \1.

\<[[:alpha:]]\+\>$ - the same exact thing here, but we are only using this to identify the location of the last word.

\(.*\) - anything in between will be saved in the variable \2.

\1\2\1 - the same line with the last word replaced with the first one.

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.