Hi everyone! I'm having some trouble with a sed command that I want to use in a function. The command I'm working with is: `sed "/$PATTERN1/,/$PATTERN2/{/$PATTERN1/n;/$PATTERN2/!d;}`. This works fine for deleting lines between two patterns without removing the lines containing those patterns. However, when I run it, I encounter the error: `bash: !d}: event not found`. This happens because `!` is used for history expansion in bash. I found that using single quotes avoids the problem, but then I can't expand my shell variables, which I need to do. I also tried escaping the exclamation mark like this `!`, but that resulted in an `unknown command:` error. Is there a way to protect the exclamation point inside the sed command while keeping double quotes so that my variables can expand? Thanks!
4 Answers
For this kind of line manipulation, you might want to consider using AWK instead of sed. It's often more powerful for text processing tasks like this.
If you're using bash v4.x or higher, you can disable history expansion entirely by running `set +o histexpand` before your sed command. To turn it back on later, simply use `set -o histexpand`. Here's how you might structure it in a function:
```
foo () {
set +o histexpand
sed "/$PATTERN1/,/$PATTERN2/{/$PATTERN1/n;/$PATTERN2/!d;}" $FILE
set -o histexpand
}
```
Remember, quoting isn’t strictly about starting or ending strings. Think of quotes as toggling a switch for special character interpretation. If you want to pass arguments with spaces for sed, consider structuring your quotes wisely:
```
sed -n 's/'"$1"'/&/p'
```
You can just single-quote the static parts of your sed expression and double-quote the parts that need to be expanded. Here's a revised version of your command:
```
sed '"$PATTERN1"'"/$PATTERN2/{"$PATTERN1"'"/n;"$PATTERN2"'!d;}' $FILE
```
It's a good idea to check if `histexpand` is already off before changing it. This way you won't inadvertently mess with the shell's settings unnecessarily. Here's a refined approach:
```
foo() {
local hist_on; [[ $- = *H* ]] && hist_on=1
set +o histexpand
sed "/$PATTERN1/,/$PATTERN2/{/$PATTERN1/n;/$PATTERN2/!d;}" $FILE
[[ -n $hist_on ]] && set -o histexpand
}
```