I have a variable with the value of 6

$ echo $line_number

When I use sed to remove all lines except 0 - 6, it works as expected

$ sed "0,6!d" ~/myfile

When I try to substitute the number 6 with my variable, I get an error

$ sed "0,$line_number!d" ~/myfile
bash: !d": event not found

The problem here is the ! character which in bash, when interactive, even inside double quotes triggers history expansion.

That feature comes from csh (where it's actually worse) and is also found in bash and zsh but otherwise not a standard sh feature.

In bash and zsh, contrary to csh, history expansion doesn't occur inside single quotes, so sed '0,6!d' would be OK. But sed "0,6!d" is not OK. In interactive bash shells, !d expands to the last command that contained d.

You can remove !'s special meaning with:

sed "0,$line_number"'!d'

I personally disable the history expansion feature as modern shells have much better interactive equivalents. I disable it in zsh with set +o banghist, you can disable it in bash with set +o histexpand (actually, zsh added histexpand as an alias to banghist in 3.1.2 in 1997 for compatibility with bash, so set +o histexpand will work in zsh as well). In (t)csh, you can disable history expansion with set histchars. The histchars= equivalent actually also works in bash and zsh.

Note that the 0 address is a GNU sed extension. Portably, you can use head instead (which is also going to be more efficient):

head -n "$line_number"

With sed, portably, you'd use:

sed "${line_number}q"

Which is also more efficient as sed stops reading after the $line_numberth line instead of carrying on reading but discarding all the other lines.

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.