Мне нужна моя команда sed-i для редактирования на месте для работы с GNU sed и BSD / OSX sed

у меня есть makefile (разработанный для gmake на Linux), который я пытаюсь портировать в OSX, но, похоже, sed не хочет сотрудничать. Я использую GCC для автогенерации файлов зависимостей, а затем немного настраиваю их с помощью sed. Соответствующая часть файла makefile:

$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp
  $(CPPC) -MM -MD $< -o $@
  sed -i 's|(.*).o:|$(OBJ_DIR)/.o $(OBJ_DIR)/.d $(TEST_OBJ_DIR)/_utest.o:|' $@

в то время как это работает без проблем под GNU/Linux, я получаю следующие ошибки при попытке построить на OSX:

sed: 1: "test/obj/equipmentConta ...": undefined label 'est/obj/equipmentContainer_utest.d'
sed: 1: "test/obj/dice_utest.d": undefined label 'est/obj/dice_utest.d'
sed: 1: "test/obj/color-string_u ...": undefined label 'est/obj/color-string_utest.d'

казалось бы, sed отрезает характер, но я не вижу решения.

7 ответов


OS X sed ручки


Actally.. делать

sed -i -e "s/blah/blah/" files

не делает то, что вы ожидаете в OS X либо.. вместо этого он создает резервные копии файлов с расширением "- e".

правильным для ОС является

sed -i "" -e "s/blah/blah/" files

принятый в настоящее время ответ имеет два очень важных недостатка.

  1. С BSD sed (версия OSX),-e опция интерпретируется как расширение файла и, следовательно, создает файл резервной копии с -e расширение.

  2. тестирование ядра Дарвина, как было предложено, не является надежным подход к кросс-платформенному решению, поскольку GNU или BSD sed могут присутствовать на любом количестве систем.

A гораздо более надежным тестом было бы просто проверить на --version опция, которая находится только в версии GNU sed.

sed --version >/dev/null 2>&1

как только будет определена правильная версия sed, мы сможем выполнить команду в правильном синтаксисе.

синтаксис GNU sed для опции-i:

sed -i -- "$@"

синтаксис BSD sed для опции-i:

sed -i "" "$@"

наконец, все это вместе в кросс-платформенной функции для выполнения на месте редактировать sed похвала:

sedi () {
    sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@"
}

пример использования:

sedi 's/old/new/g' 'some_file.txt'

это решение было протестировано на OSX, Ubuntu, Freebsd, Cygwin, CentOS, Red Hat Enterprise и Msys.


Это не совсем ответ на вопрос, но можно получить эквивалентное поведение linux через brew install gnu-sed --with-default-names


Я также столкнулся с этой проблемой и подумал о следующем решении:

darwin=false;
case "`uname`" in
  Darwin*) darwin=true ;;
esac

if $darwin; then
  sedi="/usr/bin/sed -i ''"
else
  sedi="sed -i"
fi

$sedi 's/foo/bar/' /home/foobar/bar

работает для меня ; -), YMMV

Я работаю в команде с несколькими ОС, где ppl строит на Windows, Linux и OS X. Некоторые пользователи OS X жаловались, потому что они получили другую ошибку - у них был установлен порт GNU sed, поэтому мне пришлось указать полный путь.


полезный ответ Мартина Клейтона обеспечивает хорошее объяснение проблемы[1], но решение, которое, как он утверждает, имеет потенциально нежелательный побочный эффект.

здесь решения без побочных эффектов:

предостережение: решение -i одной проблемы синтаксиса, как показано ниже, может быть недостаточно, потому что между GNU sed и BSD / macOS sed (для a всестороннее обсуждение см. ответ шахты).


обходной путь с -i создать резервную копию файла временно, затем очистите его:

С непустой суффикс (расширение файла резервной копии) параметр-аргумент (значение, которое не пустая строка), то can использовать -i таким образом, который работает как с BSD / macOS sed и GNU sed, by напрямую добавление суффикса к -i опции.

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

sed -i.bak 's/foo/bar/' file && rm file.bak

очевидно, если вы хотите сохранить резервную копию, просто опустите && rm file.bak часть.


обходной путь, совместимый с POSIX, с использованием временного файла и mv:

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

если вы ограничиваете свой sed скрипт и другие опции для POSIX-совместимые функции, далее портативный решение (Обратите внимание, что -i и не POSIX-совместимыми).

sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
  • эта команда просто записывает изменения во временный файл и, если преуспевает (&&), заменяет исходный файл временным.

    • если вы хотите сохранить исходный файл в качестве резервной копии, добавить еще один mv команда, которая сначала переименовывает оригинал.
  • предостережение: по сути, это -i тоже, за исключением того, что он пытается сохранить разрешения и расширенные атрибуты (macOS) исходного файла; однако, если исходный файл ссылка, и это решение и -i заменит символическую ссылку на обычный файл.
    смотрите нижнюю половину ответ моего для деталей о том, как -i строительство.


[1] для более подробного объяснения см. ответ шахты.


я исправил решение, опубликованное @thecarpy:

вот правильное кросс-платформенное решение для sed -i:

sedi() {
  case $(uname) in
    Darwin*) sedi=('-i' '') ;;
    *) sedi='-i' ;;
  esac

  LC_ALL=C sed "${sedi[@]}" "$@"
}