флаг sed на месте, который работает как на Mac (BSD), так и на Linux

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

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


person dnadlinger    schedule 17.04.2011    source источник
comment
Perl - это не вариант? Вы должны использовать sed?   -  person Noufal Ibrahim    schedule 17.04.2011
comment
Может быть, установите версию GNU и воспользуйтесь ею! topbug.net/blog/2013/04/14/? Сначала я попробую. С другой стороны, это тоже отстой.   -  person Dmitry Minkovsky    schedule 17.09.2014
comment
@dimadima: Может быть интересно другим людям, просматривающим этот вопрос, у которых есть личные скрипты, которые ломаются на их машине OS X. В моем случае, однако, он мне понадобился для системы сборки проекта с открытым исходным кодом, где указание вашему пользователю сначала установить GNU sed привело бы к поражению исходной цели этого упражнения (исправление нескольких файлов в стиле «работает везде»).   -  person dnadlinger    schedule 18.09.2014
comment
@klickverbot да, имеет смысл. Сначала я добавил комментарий как ответ, а затем удалил его, понимая, что это не ответ на ваш вопрос :).   -  person Dmitry Minkovsky    schedule 19.09.2014
comment
Перекрестная ссылка: Как добиться переносимости с помощью sed -i (редактирование на месте)? в стеке Unix и Linux Обмен.   -  person MvG    schedule 10.01.2017
comment
В частности, этот ответ: unix.stackexchange.com/a/92896/43390 не использует -i, а вместо этого записывает во временный файл, который затем && mv в исходный файл.   -  person Ioannis Filippidis    schedule 18.12.2020
comment
У меня есть решение для Linux / macOS / BSD в моем ответе stackoverflow.com/a/65497543/117471   -  person Bruno Bronosky    schedule 29.12.2020


Ответы (14)


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

person kine    schedule 28.02.2014
comment
Это путь. Добавьте несколько символов, и он переносится. Удаление файла резервной копии тривиально (у вас уже есть имя файла при вызове sed) - person slezica; 25.06.2015
comment
Это сработало на Mac, но на ubuntu 16.04 он перезаписывает исходный файл до 0 байтов и создает файл .bak также с 0 байтами? - person house9; 27.10.2016
comment
Следует отметить, что в macOS Sierra (и, возможно, ранее, у меня нет под рукой машины) sed не принимает позиционный command аргумент при вызове с -i - он должен быть снабжен другим флагом, -e . Таким образом, команда принимает вид: sed -i.bak -e 's/foo/bar/' filename - person ELLIOTTCABLE; 16.05.2017
comment
Это создает резервные копии, в то время как OP специально запрашивает редактирование на месте. - person Marc-André Lafortune; 22.09.2017
comment
Итак, полное решение: sed -i.bak 's/foo/bar/' file && rm file.bak - person joeytwiddle; 13.03.2018
comment
нам не нужен файл резервной копии ... я уверен, что мы все знаем, как работать с файлом резервной копии ... мы не хотим, чтобы файл резервной копии был всем ... так что это не ответ для меня - person uberrebu; 17.04.2021

Это работает с 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 и ''.
person dnadlinger    schedule 17.04.2011
comment
@AlexanderMills Сообщает sed, что следующий аргумент является командой - здесь также можно пропустить. - person Benjamin W.; 21.01.2017
comment
Обратите внимание, что -i и -i'' идентичны во время синтаксического анализа оболочки; sed не может вести себя по-разному при этих двух первых вызовах, потому что он получает одни и те же аргументы. - person wchargin; 05.06.2019

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

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

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

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

  • Измените свой PATH в соответствии с инструкциями после установки gnu-sed
  • Проверьте свои скрипты, чтобы выбрать между gsed или sed в зависимости от вашей системы
person Lewy    schedule 08.01.2015
comment
--with-default-names был удалено из homebrew-core, подробнее см. этот ответ. при установке gnu-sed сейчас в инструкции по установке указано, что вам нужно добавить gnubin в свой PATH: PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH" - person pgericson; 13.02.2019
comment
Страница пакета homebrew - formulae.brew.sh/formula/coreutils - рекомендует добавьте это в свой bashrc, если вы хотите, чтобы они были на вашем пути перед одноименными инструментами Mac: PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH" - person Alex Hall; 19.07.2020

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

person Alex Dupuy    schedule 07.03.2014
comment
Мне нравится это решение. perl является частью спецификации Linux Standard Base с 1 мая 2009 г. refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/ - person OregonTrail; 28.03.2019
comment
Это лучшее решение для портативности. - person ecnepsnai; 25.07.2019

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

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

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

Это работает как на

person analogue    schedule 18.02.2013
comment
Есть ли причина, по которой SOME_FILE = $ (sed -s s / abc / def / some / file); echo $ SOME_FILE ›некоторый / файл; вместо этого не сработает - person Jason Gross; 12.12.2013
comment
Похоже, вы были бы ограничены максимальным размером переменной bash, не так ли? Не уверен, что он будет работать с файлами GB. - person analogue; 01.01.2014
comment
Если вы создаете временный файл, почему бы просто не дать расширение для создания файла резервной копии (который затем можно удалить), как предлагает @kine ниже? - person Alex Dupuy; 07.03.2014

Ответ: Нет.

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

Очевидно, нет никакого способа заставить sed -i работать одинаково как на MacOS, так и на Linuces.

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

person Steve Powell    schedule 04.07.2012

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

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

  2. s заменить

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

person Steven Penny    schedule 24.11.2016
comment
Это правильный ответ. Ключевой особенностью POSIX является переносимость. - person Kajukenbo; 04.11.2019

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

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

person nwinkler    schedule 27.06.2018
comment
Это правильный ответ. Я бы немного упростил его, но идея работает отлично sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file - person Alex Skrypnyk; 31.01.2019
comment
Хорошая вещь! Однако я предпочитаю более длинную версию для удобства чтения. - person nwinkler; 31.01.2019
comment
Для меня это лучший способ быть совместимым с другой системой. - person Victor Choy; 22.08.2019

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

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

Справочная страница sed Linux

#Linux
sed -i"" 

и

Справочная страница sed OSX

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

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

person Big Rich    schedule 03.10.2012

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

brew install gnu-sed
person vikrantt    schedule 20.07.2017
comment
Это дублирует существующий ответ с гораздо более подробной информацией из 2015 года. - person tripleee; 13.12.2018

Если вам нужно выполнить 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""`
person sdaau    schedule 02.10.2017

Можно использовать губку. Sponge - это старая программа для Unix, которая находится в пакете moreutils (как в ubuntu и, возможно, в debian, так и в homebrew в Mac).

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

На странице руководства:

Синопсис

sed '...' файл | grep '...' | напильник для губки

person Guillermo    schedule 06.08.2013
comment
Хороший подход - к сожалению, не очень помогает в исходной ситуации, поскольку причина обращения к sed заключалась в том, чтобы иметь что-то, что можно использовать в кроссплатформенном вспомогательном скрипте, используемом на клиентских машинах, где вы не можете зависеть ни от чего другого, кроме системы устанавливаемые инструменты. Хотя может быть полезен кому-то другому. - person dnadlinger; 07.08.2013

Переносимый скрипт как для систем 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
person Jon    schedule 09.01.2021
comment
Нет причин помещать "Darwin" в кавычки - он не может разбираться ни с чем, кроме самого себя. Если бы не специальная семантика [[ ]], вам понадобились бы кавычки вокруг $(uname), чтобы гарантировать правильное поведение со всеми возможными возвращаемыми значениями. - person Charles Duffy; 28.06.2021

В 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).

person David G    schedule 19.04.2016
comment
Вау, я рад, что не послушал всех, кто говорил, что это невозможно, и продолжил читать! Это действительно просто работает. Спасибо! - person Personman; 18.05.2016
comment
У меня не работает в Mac OS ... создается новый файл с пробелом, добавленным в конец имени файла - person Frédéric Camblor; 29.08.2016
comment
У меня тоже не работает на Mac (/ usr / bin / sed) - теперь у меня есть дополнительный файл резервной копии с пробелом, добавленным к имени файла :-( - person paul_h; 05.02.2018
comment
Попробуйте убрать пробел из одинарных кавычек, у меня это работает. - person dps; 26.06.2019