Флаг sed in-place, который работает как на Mac (BSD), так и на Linux - PullRequest
201 голосов
/ 17 апреля 2011

Есть ли вызов sed редактирования на месте без резервного копирования, который работает как в Linux, так и в Mac? В то время как BSD sed, поставляемый с OS X, похоже, требуется sed -i '' …, дистрибутивы GNU sed Linux обычно поставляются с интерпретацией кавычек как пустого имени входного файла (вместо расширения резервной копии), и вместо него требуется sed -i ….

Существует ли какой-либо синтаксис командной строки, который работает с обоими вариантами, поэтому я могу использовать один и тот же сценарий в обеих системах?

Ответы [ 13 ]

170 голосов
/ 28 февраля 2014

Если вы действительно хотите просто использовать sed -i «простым» способом, следующие функции работают как на GNU, так и на BSD / Mac sed:

sed -i.bak 's/foo/bar/' filename

Обратите внимание на недостаток места иточка.

Доказательство:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

Очевидно, что вы можете просто удалить файлы .bak.

99 голосов
/ 17 апреля 2011

Это работает с GNU sed, но не в OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

Это работает в OS X, но не в GNU sed:

sed -i '' -e 's/foo/bar/' target.file

На OS X вы

  • не может использовать sed -i -e, поскольку расширение файла резервной копии будет установлено на -e
  • не может использовать sed -i'' -e по тем же причинам - ему нужно пространство между -i и ''.
40 голосов
/ 08 января 2015

В OSX я всегда устанавливаю версию GNU sed через Homebrew, чтобы избежать проблем в сценариях, потому что большинство сценариев были написаны для версий GNU sed.

brew install gnu-sed --with-default-names

Тогда ваш BSD sed будет заменен на GNU sed.

Кроме того, вы можете установить без имен по умолчанию, но затем:

  • Измените PATH в соответствии с инструкциями после установки gnu-sed
  • Проверьте свои скрипты, чтобы выбрать между gsed или sed в зависимости от вашей системы
17 голосов
/ 19 февраля 2013

Нет способа заставить его работать.

Одним из способов является использование временного файла, такого как:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

Это работает на обоих

15 голосов
/ 07 марта 2014

Как Нуфал Ибрагим спрашивает, почему вы не можете использовать Perl?Любой Mac будет иметь Perl, и очень мало дистрибутивов Linux или BSD, которые не включают какую-либо версию Perl в базовую систему.Одной из единственных сред, в которых может отсутствовать Perl, может быть BusyBox (который работает как GNU / Linux для -i, за исключением того, что не может быть указано расширение для резервного копирования).

Как ismail рекомендует,

Поскольку perl доступен везде, я просто делаю perl -pi -e s,foo,bar,g target.file

, и это кажется лучшим решением почти в любом случае, чем сценарии, псевдонимы или другие обходные пути дляиметь дело с фундаментальной несовместимостью sed -i между GNU / Linux и BSD / Mac.

12 голосов
/ 04 июля 2012

Ответ: Нет.

Первоначально принятый ответ на самом деле не выполняет то, что запрашивается (как отмечено в комментариях). (Я нашел этот ответ, когда искал причину, по которой file-e появлялось "случайно" в моих каталогах.)

По-видимому, нет способа заставить sed -i согласованно работать как на MacOS, так и на Linux.

Моя рекомендация не стоит обновлять на месте с помощью sed (который имеет сложные режимы сбоев), а создавать новые файлы и впоследствии переименовывать их. Другими словами: избегайте -i.

8 голосов
/ 24 ноября 2016

Параметр -i не является частью POSIX Sed .Более переносимый метод - использовать Vim в режиме Ex:

ex -sc '%s/alfa/bravo/|x' file
  1. % выбрать все строки

  2. s заменить

  3. x сохранить и закрыть

6 голосов
/ 04 октября 2012

Ответ Стива Пауэлла вполне корректен, обращаясь к странице MAN для sed в OSX и Linux (Ubuntu 12.04), подчеркивается несовместимость внутри использования sed на месте для двух операционных систем.

JFYI, не должно быть пробела между -i и любыми кавычками (которые обозначают пустое расширение файла) при использовании Linux-версии sed, таким образом

sed Linux Man Page

#Linux
sed -i"" 

и

sed Страница OSX Man

#OSX (notice the space after the '-i' argument)
sed -i "" 

Я обошел это в сценарии с помощью команды alias'dи вывод имени ОС ' uname ' в bash 'if'.Попытка сохранить зависящие от ОС строки команд в переменных была неудачной при интерпретации кавычек.Использование ' shopt -s expand_aliases ' необходимо для расширения / использования псевдонимов, определенных в вашем скрипте.Использование магазина рассматривается здесь .

5 голосов
/ 27 июня 2018

Вот еще одна версия, которая работает в Linux и macOS без использования eval и без необходимости удаления файлов резервных копий.Он использует массивы Bash для хранения параметров sed, что чище, чем использование eval:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

Это не создает файл резервной копии и файл с добавленными кавычками.

1 голос
/ 02 октября 2017

Если вам нужно выполнить sed на месте в скрипте bash, и вы НЕ хотите, чтобы на месте возникали файлы .bkp, и у вас есть способ обнаружить ОС (скажем, используя ostype.sh ), - тогда должен работать следующий хак со встроенной оболочкой bash eval:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`
...