Solved Using sed to prefix a line, but only if it does not contain a pattern

I'm trying to simplify this sed expression as I think it is not the best way to represent what is going on. I have a sample shell script:

#!/bin/sh
lib include.sh
lib git:install/git.sh
lib git:git/include.sh
_git_init

cat /tmp/sample.sh | sed -e 's/^lib /lib git:install\//' -e 's/^lib git:install\/git:/lib git:/'

Is there a way to do this with a single sed expression, 1 -e?

So, line 2 shall become:
lib git:install/include.sh

And, the others shall remain as they were.
 
Yes, you're right about cat, my bad. That was a relic of my framework ... I'm running this after a find expression.

It might be a bad idea, but for the past 5 years, I've been working on a shell script framework so I can use the same cmds at work and home to do the same 'stuff'. If I program it in go or Java, that might be surely easier, but I wouldn't be able to use that at work.

Thanks for those references.
 
cat /tmp/sample.sh | sed -e 's/^lib /lib git:install\//' -e 's/^lib git:install\/git:/lib git:/'
[…] Is there a way to do this with a single sed expression, 1 -e?
You have to write only one ‑e command. sed(1) does not accept multiple commands via the ‑e parameter. Try reversing your commands:​
Bash:
sed -e 's/^lib /lib git:install\//' -e 's/^lib git:install\/git:/lib git:/' < /tmp/sample.sh
sed -e 's/^lib git:install\/git:/lib git:/' -e 's/^lib /lib git:install\//' < /tmp/sample.sh
They produce different results. Only the last ‑e is actually taken account of.​
Bash:
sed -E 's|^lib (git:install/git:)?|lib git:install/|' < /tmp/sample.sh # I ain’t sure what you want.
Use extended regular expressions (capital ‑E) and avoid escaping by using a different delimiter other than a forward slash.

PS: sed(1) understands multiple ‑e but evidently the order matters. So the question is: Do you want to edit the line multiple times?​
 
You have to write only one ‑e command. sed(1) does not accept multiple commands via the ‑e parameter. Try reversing your commands:​
Bash:
sed -e 's/^lib /lib git:install\//' -e 's/^lib git:install\/git:/lib git:/' < /tmp/sample.sh
sed -e 's/^lib git:install\/git:/lib git:/' -e 's/^lib /lib git:install\//' < /tmp/sample.sh
They produce different results. Only the last ‑e is actually taken account of.​
Bash:
sed -E 's|^lib (git:install/git:)?|lib git:install/|' < /tmp/sample.sh # I ain’t sure what you want.
Use extended regular expressions (capital ‑E) and avoid escaping by using a different delimiter other than a forward slash.

PS: sed(1) understands multiple ‑e but evidently the order matters. So the question is: Do you want to edit the line multiple times?​
Yes, I want to edit the line multiple times because in this case, what I'm doing is:

1. translate the "relative" import of lib include.sh to lib git:install/include.sh
2. translate remaining imports back to the original since I couldn't device a clever sed expression to ignore lines that already have git: on them
these imports first become "lib git:install/git:git.sh"
then, they get corrected to their original form "lib git:install/git.sh"
 
Is this your card?​
Bash:
sed -e 's|^lib \([^g][^i][^t][^:]\)|lib git:install/\1|' < /tmp/sample.sh # BRE
sed -E 's|^lib ([^g][^i][^t][^:])|lib git:install/\1|'   < /tmp/sample.sh # ERE

Nice, that works.

Now, that doesn't look terrible. I see the [^] hacks and I'm a bit split on that. I'm still pondering.

So, the question for me to decide is which style I prefer as both accomplish the same thing. Performance differences will be negligible here. I think this may be useful to have as a reference for more complicated use cases.
 
A variation to the solution provided by Kai Burghardt
So, line 2 shall become:

And, the others shall remain as they were.
I see the [^] hacks and I'm a bit split on that. I'm still pondering.
If I understand correctly, you want this behaviour:
sh:
$ cat sample.sh
#!/bin/sh
lib include.sh
lib git:install/git.sh
lib git:git/include.sh
_git_init

$ sed '/git:/! s/^lib /lib git:install\//' < sample.sh
#!/bin/sh
lib git:install/include.sh
lib git:install/git.sh
lib git:git/include.sh
_git_init

$
 
Yes.

I'm trying to digest that.

I have used the pattern matching syntax to delete lines, but this is new to me. If I understand correctly, the trailing !, means to invert that match, so:
1. must not contain git:
2. replace lines starting with 'lib ' with 'lib git:install/'

If so, then I think that is the solution I'm looking for because it is concise and easily explainable.
 
If I understand correctly, the trailing !, means to invert that match
Yes, you're correct. The address format: address command isn't explicitly explained in
https://www.grymoire.com/Unix/Sed.html and you have to really look for it in his Sed Reference Chart (pdf). sed(1):
Code:
       [2addr]!function
       [2addr]!function-list
	       Apply the function or function-list only	to the lines that  are
	       not selected by the address(es).
but these available options can be hard to find.
 
You have to write only one ‑e command. sed(1) does not accept multiple commands via the ‑e parameter.
Why do you say that?
-e option works as documented, each instance appends more sed commands.

Of course, tOsYZYny 's original command did not make sense because the first sed command would ensure that the second would not match anything.
 
Why do you say that?
-e option works as documented, each instance appends more sed commands.

Of course, tOsYZYny 's original command did not make sense because the first sed command would ensure that the second would not match anything.
Are you sure?

My sed command worked, but it was just clumsy.
 
No worries, it is a hack at best.

I'm not a sed expert and regular expressions are difficult to master to say the least.

This bit is a bit off-topic, but I enjoy conversing about writing code ...

I think with anything in life, it is an art to balance concepts such as simplicity, correctness, or succinctness. My view on coding standards has changed over the years. The thing that comes to mind that bit me in the butt more than once (not my code, but code I've worked on), so I avoid it at all costs is:

if(true)
doSomething;

I've been bitten by the above because it was not properly formatted and instead, there were lines of code directly below the doSomething that looked like they were meant to be included in the conditional execution, but weren't because there weren't any brackets.

I used to advocate for the above syntax, but instead, now advocate for:

if(true) {
doSomething;
}

Even for the above syntax came with the stipulation that we formatted our code so it would read consistently, but perhaps as I get older, my vision poorer, I need more visual queues to mentally recognize what is being called versus not.

The same holds true for me with ternary operators, the syntax to me does not visually read well. For me at least, it requires 2 or 3 passes to see what is going on whereas I want to look at code and say, oh yeah, without a doubt, that is what it is doing.
 
Back
Top