Флаг sed in-place, который работает как на Mac (BSD), так и на Linux

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

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

15 ответов

Решение

Если вы действительно хотите просто использовать 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 файлы.

Это работает с 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 а также '',

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

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

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

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

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

Как спрашивает Noufal Ibrahim, почему вы не можете использовать 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.

Вот еще одна версия, которая работает на 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

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

Ответ: Нет.

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

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

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

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

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

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

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

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

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

  2. s замещать

  3. x сохрани и закрой

Портативный скрипт как для систем GNU, так и для OSX:

      if [[ $(uname) == "Darwin" ]]; then
    SP=" " # Needed for portability with sed
fi

sed -i${SP}'' -e "s/foo/bar/g" -e "s/ping/pong/g" foobar.txt

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

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

Sed Linux Man Page

#Linux
sed -i"" 

а также

Страница man sed OSX

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

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

Я столкнулся с этой проблемой. Единственным быстрым решением было заменить sed в mac на версию gnu:

brew install gnu-sed

Если вам нужно сделать 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""`

Следующее работает для меня на Linux и OS X:

sed -i' ' <expr> <file>

например, для файла f содержащий aaabbaaba

sed -i' ' 's/b/c/g' f

доходность aaaccaaca на Linux и Mac. Обратите внимание, что есть строка в кавычках, содержащая пробел, без пробела между -i и строка. Работают одинарные или двойные кавычки.

В Linux я использую bash версия 4.3.11 под Ubuntu 14.04.4 и на Mac версии 3.2.57 под OS X 10.11.4 El Capitan (Darwin 15.4.0).

Проблема в том, чтоsedявляется потоковым редактором, поэтому редактирование на месте не является расширением POSIX, и каждый может реализовать его по-своему. Это означает, что для редактирования на месте вы должны использоватьedдля лучшей переносимости. Например

      ed -s foobar.txt <<<$',s/foo/bar/g\nw'

Также смотрите https://wiki.bash-hackers.org/howto/edit-ed .

Вы можете использовать губку. Sponge - это старая Unix-программа, находящаяся в пакете moreutils (как в Ubuntu и, вероятно, в Debian, так и в Homebrew в Mac).

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

Со страницы руководства:

конспект

sed '...' file | grep '...' | губка

Другие вопросы по тегам