Replacing the shebang in a script
In a Bash script and/or at the command line, given that $file is the path to an executable text file with a shebang, and $env is a valid shebang, I want to replace the existing shebang in the file with the new one. Is there a standard or well-known way?
I thought of echoing the new shebang and then tailing the rest of the file, but this requires explicitly making a temporary file and replacing the original with it (and possibly fixing mode/owner).
I also thought of using sed, but I can't quite figure it out given that shebangs generally contain multiple forward slashes which interfere with the regex syntax. It also seems like overkill since only the first line needs to be considered for replacement.
2 answers
Sed has more commands than just s! You can replace the first line of the input with a new value using the c command, without having to write any regular expressions.
sed "1c\\$env" "$file"
0 comment threads
If there is a standard or well-known way way to accomplish this, someone else will have to provide it. But if you're open to more... hacky alternatives...
Escaping the delimiter character / can certainly be a pain, especially when that character is used extensively throughout one or both of the patterns in sed 's/regex/replacement/'. That being said, we're not actually limited to using /. According to man sed, it can be any character (almost, anyway):
/regexp/
Match lines matching the regular expression regexp. Matching is performed on the current pattern space, which can be modified with commands such as ‘‘s///''.
\cregexpc
Match lines matching the regular expression regexp. The c may be any character.
So, instead of this:
sed 's/\/path\/to\/some\/file/\/path\/to\/some\/other\/file/' infile.txt
we can instead use:
sed 's|/path/to/some/file|/path/to/some/other/file|' infile.txt
(It's beyond the scope of this answer, but this also works with sed's y/source/dest/ command, meaning sed 'y#abc#XYZ#' would be just as valid as sed 'y/abc/XYZ/')
Additionally, you can force sed to do this for only the first line, by prepending a 1 to the command, such as:
sed '1s/regex/replacement/'
I took the liberty of crafting a sed command to accomplish the task you described in your question. It matches the first line only if it starts with #! and replaces it with ${shebang}, writing the entirety of the output to STDOUT as normal.
sed "1s|#!.*|${shebang}|" infile.txt
If you want to match the first line unconditionally, then simply remove the #!.*. If you want to match a specific subset of shebangs, a more involved regex pattern could be used.

0 comment threads