Мне нужна моя команда 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 ответов
Actally.. делать
sed -i -e "s/blah/blah/" files
не делает то, что вы ожидаете в OS X либо.. вместо этого он создает резервные копии файлов с расширением "- e".
правильным для ОС является
sed -i "" -e "s/blah/blah/" files
принятый в настоящее время ответ имеет два очень важных недостатка.
С BSD sed (версия OSX),
-e
опция интерпретируется как расширение файла и, следовательно, создает файл резервной копии с-e
расширение.тестирование ядра Дарвина, как было предложено, не является надежным подход к кросс-платформенному решению, поскольку 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[@]}" "$@"
}