Here is my test.env
RABBITMQ_HOST=127.0.0.1
RABBITMQ_PASS=1234
And I want to use test.sh to replace the value in test.env to :
RABBITMQ_HOST=rabbitmq1
RABBITMQ_PASS=12345
here is my test.sh
#!/bin/bash
echo "hello world"
RABBITMQ_HOST=rabbitmq1
RABBITMQ_PASS=12345
Deploy_path="./config/test.env"
sed -i 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='$RABBITMQ_HOST'/' $Deploy_path
sed -i 's/RABBITMQ_PASS=.*/RABBITMQ_PASS='$RABBITMQ_HOST'/' $Deploy_path
But I have error
sed: 1: "./config/test.env": invalid command code .
sed: 1: "./config/test.env": invalid command code .
How can I fix it?
tl;dr:
With BSD Sed, such as also found on macOS, you must use
-i ''instead of just-i(for not creating a backup file) to make your commands work; e.g.:To make your command work with both GNU and BSD Sed, specify a nonempty option-argument (which creates a backup) and attach it directly to
-i:Background information, (more) portable solutions, and refinement of your commands can be found below.
Optional Background Information
It sounds like you're using BSD/macOS
sed, whose-ioption requires an option-argument that specifies the suffix of the backup file to create.Therefore, it is your
sedscript that (against your expectations) is interpreted as-i's option-argument (the backup suffix), and your input filename is interpreted as the script, which obviously fails.By contrast, your commands use GNU
sedsyntax, where-ican be used by itself to indicate that no backup file of the input file to updated in-place is to be kept.The equivalent BSD
sedoption is-i ''- note the technical need to use a separate argument to specify the option-argument'', because it is the empty string (if you used-i'', the shell would simply strip the''beforesedever sees it:-i''is effectively the same as just-i).Sadly, this then won't work with GNU
sed, because it only recognizes the option-argument when directly attached to-i, and would interpret the separate''as a separate argument, namely as the script.This difference in behavior stems from a fundamentally differing design decision behind the implementation of the
-ioption and it probably won't go away for reasons of backward compatibility.[1]If you do not want a backup file created, there is no single
-isyntax that works for both BSD and GNUsed.There are four basic options:
(a) If you know that you'll only be using either GNU or BSD
sed, construct the-ioption accordingly:-ifor GNUsed,-i ''for BSDsed.(b) Specify a nonempty suffix as
-i's option-argument, which, if you attach it directly to the-ioption, works with both implementations; e.g.,-i'.bak'. While this invariably creates a backup file with suffix.bak, you can just delete it afterward.(c) Determine at runtime which
sedimplementation you're dealing with and construct the-ioption accordingly.(d) omit
-i(which is not POSIX-compliant) altogether, and use a temporary file that replaces the original on success:sed '...' "$Deploy_path" > tmp.out && mv tmp.out "$Deploy_path".Note that this is in essence what
-idoes behind the scenes, which can have unexpected side effects, notably an input file that is a symlink getting replaced with a regular file;-i, does, however, preserve certain attributes of the original file: see the lower half of this answer of mine.Here's a
bashimplementation of (c) that also streamlines the original code (singlesedinvocation with 2 substitutions) and makes it more robust (variables are double-quoted):Note that with the specific values defined above for
$RABBITMQ_HOSTand$RABBITMQ_PASS, it is safe to splice them directly into thesedscript, but if the values contained instances of&,/,\, or newlines, prior escaping would be required so as not to break thesedcommand.See this answer of mine for how to perform generic pre-escaping, but you may also consider other tools at that point, such as
awkandperl.[1] GNU Sed considers the option-argument to -i optional, whereas BSD Sed considers it mandatory, which is also reflected in the syntax specs. in the respective
manpages: GNU Sed:-i[SUFFIX]vs. BSD Sed-i extension.