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


237

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

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


Perl не вариант? Вы должны использовать Sed?
— Нуфал Ибрагим

Может быть, установить версию GNU и использовать это! topbug.net/blog/2013/04/14/… ? Я бы попробовал это первым. Опять же, это тоже отстой.
— Дмитрий Минковский

2
@dimadima: Может быть интересно другим людям, просматривающим этот вопрос, у которых есть личные сценарии, которые ломаются на их машине с OS X. В моем случае, однако, он мне понадобился для системы сборки проекта с открытым исходным кодом, где указание вашему пользователю установить GNU sed в первую очередь побеждало бы первоначальную цель этого упражнения (исправление нескольких файлов способом «работает везде») ,
— dnadlinger

@klickverbot да, имеет смысл. Сначала я добавил комментарий как ответ, а затем удалил его, понимая, что это не ответ на ваш вопрос :).
— Дмитрий Минковский

Ответы:


212

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


2
Это путь. Добавьте несколько символов, и это переносимо. Удаление файла резервной копии является тривиальным (у вас уже есть имя файла при вызове sed)
— slezica

Это работает на Mac, но на Ubuntu 16.04 он перезаписывает исходный файл до 0 байтов и создает файл .bak также с 0 байтами?
— house9

1
Следует отметить, что на MacOS Sierra (и , возможно , раньше, я не имею машину под рукой), sedне принимает позиционное commandаргумент при вызове с -i- он должен быть снабжен другим флагом, -e. Таким образом, команда становится:sed -i.bak -e 's/foo/bar/' filename
— ELLIOTTCABLE

5
Это создает резервные копии, в то время как OP специально запрашивает редактирование на месте.
— Марк-Андре

8
Итак, полное решение:sed -i.bak 's/foo/bar/' file && rm file.bak
— joeytwiddle

112

Это работает с 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и ''.

1
@AlexanderMills Сообщает sed, что следующим аргументом является команда - ее также можно пропустить здесь.
— Бенджамин В.

4
Обратите внимание, что -iи -i''идентичны во время разбора оболочки; sed не может вести себя по-разному на тех двух первых вызовах, потому что он получает точно такие же аргументы.
— wchargin

44

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

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

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

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

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

4
--with-default-namesбыло удалено из доморощенного-ядра , больше информации в этом ответе. при установке gnu-sedсейчас в инструкции по установке указано, что вам нужно добавить gnubinв свой PATH:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
— pgericson

18

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

Как рекомендует Исмаил ,

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

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


Я люблю это решение. perlявляется частью стандартной спецификации Linux с 1 мая 2009 г. refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/…
— OregonTrail


17

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

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

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

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


Есть ли причина, по которой SOME_FILE = "$ (sed -s" s / abc / def / "some / file)"; echo "$ SOME_FILE"> some / file; не будет работать вместо этого
— Джейсон Гросс

Похоже, вы были бы ограничены максимальным размером переменной bash, нет? Не уверен, что он будет работать с файлами ГБ.
— аналог

3
Если вы создаете временный файл, почему бы просто не дать расширение для создания файла резервной копии (который затем можно удалить), как предлагает @kine ниже?
— Алекс Дюпуй

13

Ответ: Нет.

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

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

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


9

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

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

  2. s замещать

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


Это правильный ответ. Ключевой особенностью POSIX является портативность.
— Kajukenbo

9

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

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


2
Это правильный ответ. Я бы немного упростил, но идея работает отлично sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
— Алексей Скрипник,

Хорошая вещь! Я предпочитаю более длинную версию для удобства чтения.
— nwinkler

1
Это лучший способ для меня быть совместимым с другой системой.
— Виктор Чой

6

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

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

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 рассматривается здесь .


1

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

0

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

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

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

конспект

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


3
Хороший подход - к сожалению, не очень помогает в исходной ситуации, потому что причиной обращения к sed было то, что нужно было использовать что-то в межплатформенном вспомогательном скрипте, используемом на клиентских машинах, где вы не можете зависеть ни от чего, кроме системы инструменты устанавливаются. Может быть полезно для кого-то еще, хотя.
— dnadlinger

0

Следующее работает для меня на 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).


1
Вау, я рада, что не слушала всех выше сказавших, что это невозможно, и продолжала читать! Это действительно просто работает. Спасибо!
— Человек

4
У меня не работает в Mac OS ... новый файл с пробелом, добавленным в конец имени файла, создан
— Frédéric

У меня на Mac тоже не работает (/ usr / bin / sed) - теперь у меня есть дополнительный файл резервной копии с пробелом, добавленным к имени файла :-(
— paul_h

Попробуйте убрать пробел из одинарных кавычек, у меня это работает.
— дпс

0

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

brew install gnu-sed

Это дублирует существующий ответ с гораздо более подробной информацией от 2015 года.
— tripleee
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.