Команда sed с опцией -i (редактирование на месте) отлично работает на Ubuntu, но не на Mac

Я ничего не знаю о Sed, но мне нужна эта команда (которая прекрасно работает в Ubuntu) для работы на Mac OSX:

sed -i "/ $domain .*#drupalpro/d" /etc/hosts

Я собираюсь:

sed: 1: "/etc/hosts": extra characters at the end of h command

4 ответа

Ubuntu поставляется с GNU sedгде суффикс для -i опция не обязательна. OS X поставляется с BSD sedгде суффикс обязателен. Пытаться sed -i ''

В дополнение к полезному ответу микротериона:

  • с портативным решением
  • с исходной информацией

тл; др:

Эквивалент этого GNU sed (стандарт в большинстве дистрибутивов Linux):

sed -i    's/foo/bar/' file

это BSD/ MacOS sed команда:

sed -i '' 's/foo/bar/' file  # Note the '' as a *separate argument*

С BSD/macOS sed следующие команды не работают вообще или не работают так, как задумано:

sed -i    's/foo/bar/' file  # Breaks; script is misinterpreted as backup-file suffix
sed -i''  's/foo/bar/' file  # Ditto
sed -i -e 's/foo/bar/' file  # -e is misinterpreted as backup-file suffix

Для обсуждения всех различий между GNU sed и BSD/macOS sed, посмотрите этот мой ответ.

Портативный подход:

Примечание: Portable здесь означает, что команда работает с обеими реализациями, которые обсуждались. Он не является переносимым в смысле POSIX, потому что -i опция не совместима с POSIX.

# Works with both GNU and BSD/macOS Sed, due to a *non-empty* option-argument:
# Create a backup file *temporarily* and remove it on success.
sed -i.bak 's/foo/bar/' file && rm file.bak

Для объяснения см. Ниже; альтернативные решения, в том числе POSIX-совместимые, см. в этом моем ответе.


Исходная информация

В GNU sed (стандартно для большинства дистрибутивов Linux) и BSD/macOS sed, -i option, который выполняет обновление на месте [1] своих входных файлов, принимает параметр-аргумент, который указывает, какой суффикс (расширение имени файла) использовать для файла резервной копии обновляемого файла.

Например, в обеих реализациях следующее сохраняет исходный файл, file в качестве резервного файла file.bak:

sed -i.bak 's/foo/bar/' file  # Keep original as 'file.bak'; NO SPACE between -i and .bak

Хотя с GNU sed аргумент суффикса является необязательным, тогда как для BSD/macOS sed обязательно, приведенный выше синтаксис работает с обеими реализациями, поскольку непосредственно примыкает к параметру-аргументу ( .bak ) к варианту ( -i ) - -i.bak в отличие от -i .bak - работает как необязательный и обязательный параметр-аргумент:

  • Синтаксис -i.bak это единственная форма, которая работает для необязательного параметра-аргумента.
  • Синтаксис -i.bak также работает как обязательный параметр-аргумент, как альтернатива -i .bak т.е. указав опцию и ее аргумент отдельно.

Отсутствие указания суффикса - что часто имеет место - означает, что файл резервной копии не должен храниться, и именно здесь возникает несовместимость:

  • С GNU sed не указывать суффикс означает просто -i само собой.

  • С BSD/macOS sed не указание суффикса означает указание пустой строки в качестве суффикса - обязательно - и по техническим причинам пустая строка может быть передана только как отдельный аргумент: -i '' не -i'',

-i'' не работает, потому что sed это неотличимо от всего -i потому что оболочка эффективно удаляет пустые кавычки (она объединяет -i а также '' и удаляет кавычки с синтаксической функцией), и просто проходит -i в обоих случаях.

С (эффективно) просто -i указан, это следующий аргумент, который интерпретируется как опция-аргумент:

sed -i 's/foo/bar/' file # BREAKS with BSD/macOS Sed

's/foo/bar/' - предназначен для сценария (команды) Sed - теперь интерпретируется как суффикс, так и слово file интерпретируется как сценарий.
Интерпретация такого слова, как сценарий, приводит к неясному сообщению об ошибке, такому как
sed: 1: "file": invalid command code f,
поскольку f интерпретируется как команда Sed (функция).

Аналогично с:

sed -i -e 's/foo/bar/' file # CREATES BACKUP FILE 'file-e'

-e интерпретируется как суффиксный аргумент, а НЕ как Sed -e опция (которая может использоваться для указания нескольких команд, если это необходимо).
В результате вместо сохранения резервной копии вы получаете файл резервной копии с суффиксом -e ,

То, что эта команда не работает должным образом, менее очевидно, потому что обновление на месте происходит успешно, учитывая, что синтаксическое требование аргумента суффикса удовлетворяется -e аргумент.

То, что случайное создание этих файлов резервных копий легко остается незамеченным, является наиболее вероятным объяснением неправильного ответа Crt и этого неправильного ответа на аналогичный вопрос, получивший так много голосов "за" (на момент написания статьи).


[1] Строго говоря, временный файл создается за кулисами, который затем заменяет оригинальный файл; такой подход может быть проблематичным: см. нижнюю половину моего ответа.

Человек твой друг.

OS X

 -i extension
         Edit files in-place, saving backups with the specified extension.
         If a zero-length extension is given, no backup will be saved.  It
         is not recommended to give a zero-length extension when in-place
         editing files, as you risk corruption or partial content in situ-
         ations where disk space is exhausted, etc.

В OS X вы можете использовать GNU-версию sed: gsed,

# if using brew
brew install gnu-sed

#if using ports
sudo port install gsed

Затем, если ваш скрипт должен быть переносимым, в зависимости от вашей ОС вы можете определить, какую команду использовать.

SED=sed
unamestr=`uname`
if [[ "$unamestr" == "Darwin" ]] ; then
    SED=gsed
    type $SED >/dev/null 2>&1 || {
        echo >&2 "$SED it's not installed. Try: brew install gnu-sed" ;
        exit 1;
    }
fi
# here your sed command, e.g.:
$SED -i "/ $domain .*#drupalpro/d" /etc/hosts
Другие вопросы по тегам