Ответ 1
TL;DR:
С BSD Sed, например, в macOS, вы должны использовать -i ''
вместо -i
(для не создание файла резервной копии), чтобы ваши команды работали; например:.
sed -i '' 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='"$RABBITMQ_HOST"'/' "$Deploy_path"
Чтобы ваша команда работала как с GNU, так и с BSD Sed, укажите непустой параметр-аргумент (который создает резервную копию) и прикрепляет его непосредственно к -i
sed -i'.bak' 's/RABBITMQ_HOST=.*/RABBITMQ_HOST='"$RABBITMQ_HOST"'/' "$Deploy_path" &&
rm "$Deploy_path.bak" # remove unneeded backup copy
Фоновая информация, (более) переносные решения и уточнение ваших команд можно найти ниже.
Дополнительная справочная информация
Похоже, вы используете BSD/macOS sed
, для параметра -i
требуется параметр-параметр, который указывает суффикс создаваемого файла резервной копии.
Таким образом, ваш sed
script (в соответствии с вашими ожиданиями) интерпретируется как параметр -i
option-argument (суффикс резервного копирования), а ваше имя входного файла интерпретируется как script, который явно не работает.
В отличие от вас, ваши команды используют синтаксис GNU sed
, где -i
может использоваться сам по себе, чтобы указать, что файл резервной копии входного файла не обновляется на месте.
Эквивалентная опция BSD sed
- -i ''
- обратите внимание на техническую необходимость использования отдельного аргумента для указания аргумента option ''
, поскольку это пустая строка (если вы использовали -i''
, оболочка просто разделил бы ''
до того, как sed
когда-либо увидит это: -i''
фактически будет таким же, как только -i
).
К сожалению, это не будет работать с GNU sed
, поскольку он распознает только аргумент option, когда он напрямую привязан к -i
, и интерпретирует отдельный ''
как отдельный аргумент, а именно как script.
Эта разница в поведении проистекает из принципиально отличающегося дизайнерского решения за реализацией опции -i
, и, вероятно, она не исчезнет из соображений обратной совместимости. [1]
Если вы не хотите создавать резервный файл, не существует синтаксиса -i
, который работает как для BSD, так и для GNU sed
.
Существует четыре основных варианта:
-
(a) Если вы знаете, что используете только GNU или BSD
sed
, постройте опцию-i
соответственно:-i
для GNUsed
,-i ''
для BSDsed
. -
(b) Укажите непустой суффикс как параметр
-i
option-argument, который, если вы привязываете его непосредственно к опции-i
, работает с обеими реализациями; например,-i'.bak'
. Хотя это неизменно создает резервный файл с суффиксом.bak
, вы можете просто удалить его позже. -
(c) Определите во время выполнения, с какой реализацией
sed
вы имеете дело, и постройте опцию-i
соответственно. -
(d) полностью исключить
-i
(который не совместим с POSIX) и использовать временный файл, который заменяет оригинал на успех:sed '...' "$Deploy_path" > tmp.out && mv tmp.out "$Deploy_path"
.
Обратите внимание, что это по существу то, что-i
делает за кулисами, что может иметь неожиданные побочные эффекты, особенно входной файл, который является заменой символической ссылки на обычный файл;-i
, однако, сохраняет определенные атрибуты исходного файла: см. нижнюю половину этого answer моего.
Здесь a bash
реализация (c), которая также упрощает исходный код (одиночный вызов sed
с двумя подстановками) и делает его более надежным (переменные двойные кавычки):
#!/bin/bash
RABBITMQ_HOST='rabbitmq1'
RABBITMQ_PASS='12345'
Deploy_path="test.env"
# Construct the Sed-implementation-specific -i option-argument.
# Caveat: The assumption is that if the `sed` is not GNU Sed, it is BSD Sed,
# but there are Sed implementations that don't support -i at all,
# because, as Steven Penny points out, -i is not part of POSIX.
suffixArg=()
sed --version 2>/dev/null | grep -q GNU || suffixArg=( '' )
sed -i "${suffixArg[@]}" '
s/^\(RABBITMQ_HOST\)=.*/\1='"$RABBITMQ_HOST"'/
s/^\(RABBITMQ_PASS\)=.*/\1='"$RABBITMQ_PASS"'/
' "$Deploy_path"
Обратите внимание, что с определенными значениями, указанными выше для $RABBITMQ_HOST
и $RABBITMQ_PASS
, можно безопасно связать их непосредственно в sed
script, но если значения содержат экземпляры &
, /
, \
или новые строки, требуется предварительное экранирование, чтобы не нарушить команду sed
.
См. этот ответ о том, как выполнить общее предварительное экранирование, но вы можете также рассмотреть другие инструменты в этой точке, такие как awk
и perl
.
[1] GNU Sed рассматривает параметр-аргумент -i optional, тогда как BSD Sed считает его обязательным, что также отражается в спецификациях синтаксиса. на соответствующих страницах man
: GNU Sed: -i[SUFFIX]
и BSD Sed -i extension
.