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

237

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

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

dnadlinger
источник
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и ''.
dnadlinger
источник
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
1
Это,
безусловно,
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

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

nwinkler
источник
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""`
sdaau
источник
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
vikrantt
источник
Это дублирует существующий ответ с гораздо более подробной информацией от 2015 года.
tripleee