Мне нужна моя команда sed -i для редактирования на месте для работы как с GNU sed, так и с BSD/OSX
У меня есть makefile (разработанный для gmake в Linux), который я пытаюсь подключить к OSX, но похоже, что sed не хочет сотрудничать. Я использую GCC для автогенерации файлов зависимостей, а затем немного настраиваю их с помощью sed. Соответствующая часть файла makefile:
$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp
$(CPPC) -MM -MD $< -o [email protected]
sed -i 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' [email protected]
В то время как это выполняется без проблем в 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 отрубает персонажа, но я не вижу решения.
Ответы
Ответ 1
OS X sed
по- -i
аргумент -i
для версии Linux.
Вы можете сгенерировать команду, которая может "работать" для обоих, добавив -e
следующим образом:
# vv
sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' [email protected]
OS X sed -i
интерпретирует следующую вещь после -i
как расширение файла для резервной копии редактирования на месте. (Версия для Linux делает это только в том случае, если между -i
и расширением нет пробела.) Очевидно, что побочным -i
использования этого является то, что вы получите файл резервной копии с -e
в качестве расширения, которое вам может не понадобиться. , Пожалуйста, обратитесь к другим ответам на этот вопрос для получения более подробной информации и более чистых подходов, которые можно использовать вместо этого.
Поведение, которое вы видите, связано с тем, что OS X sed
потребляет s|||
поскольку расширение (!) затем интерпретирует следующий аргумент как команду - в этом случае он начинается с t
, который sed
распознает как команду перехода к метке, ожидающую метку назначения в качестве аргумента - отсюда и ошибка, которую вы видите.
Если вы создаете файл test
вы можете воспроизвести ошибку:
$ sed -i 's|x|y|' test
sed: 1: "test": undefined label 'est'
Ответ 2
Собственно.. делая
sed -i -e "s/blah/blah/" files
не выполняет то, что вы ожидаете в OS X.. вместо этого он создает файлы резервных копий с расширением "-e".
Для ОС подходит
sed -i "" -e "s/blah/blah/" files
Ответ 3
В настоящее время принятый ответ ошибочен двумя очень важными способами.
-
С BSD sed (версия OSX) опция -e
интерпретируется как
расширение файла и, следовательно, создает файл резервной копии с -e
расширение.
-
Тестирование ядра darwin, как было предложено, не является надежным
подход к межплатформенному решению, поскольку GNU или BSD sed могли
присутствовать на любом количестве систем.
Более надежным тестом было бы просто проверить параметр --version
, который можно найти только в версии sed.
sed --version >/dev/null 2>&1
После определения правильной версии sed мы можем выполнить команду в ее правильном синтаксисе.
Синтаксис GNU sed для опции -i:
sed -i -- "[email protected]"
Синтаксис BSD sed для опции -i:
sed -i "" "[email protected]"
Наконец, соедините все это в кросс-платформенную функцию, чтобы выполнить редактирование sed sed:
sedi () {
sed --version >/dev/null 2>&1 && sed -i -- "[email protected]" || sed -i "" "[email protected]"
}
Пример использования:
sedi 's/old/new/g' 'some_file.txt'
Это решение было протестировано на OSX, Ubuntu, Freebsd, Cygwin, CentOS, Red Hat Enterprise и Msys.
Ответ 4
Это не совсем ответ на вопрос, но можно получить Linux-эквивалентное поведение через
brew install gnu-sed
# Add to .bashrc / .zshrc
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
(ранее была опция --with-default-names
для brew install gnu-sed
но недавно она была удалена)
Ответ 5
Я столкнулся с этой проблемой и подумал о следующем решении:
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, поэтому мне пришлось указать полный путь.
Ответ 6
полезный ответ martin clayton дает хорошее объяснение проблемы [1] но решение, которое, как он утверждает, имеет потенциально нежелательный побочный эффект.
Вот решения без побочных эффектов:
Предостережение: решить только синтаксическую проблему -i
, как показано ниже, может быть недостаточно, потому что существует много других различий между GNU sed
и BSD/macOS sed
(для всестороннего обсуждения см. этот ответ).
Обходной путь с -i
: временно создайте резервный файл, а затем очистите его:
С аргументом-аргументом непустого суффикса (расширение файла файла резервной копии) (значение, которое не является пустой строкой), вы можете использовать -i
таким образом, который работает как с BSD/macOS sed
, так и с GNU sed
, добавив суффикс к опции -i
.
Это можно использовать для временного создания файла резервной копии, который вы можете очистить сразу:
sed -i.bak 's/foo/bar/' file && rm file.bak
Очевидно, что если вы хотите сохранить резервную копию, просто опустите часть && rm file.bak
.
Обходной путь, совместимый с POSIX, с использованием временного файла и mv
:
Если только один файл нужно редактировать на месте, параметр -i
можно обойти, чтобы избежать несовместимости.
Если вы ограничите sed
script и другие параметры функции, совместимые с POSIX, следующее полностью портативное решение (обратите внимание, что -i
не совместим с POSIX).
sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
-
Эта команда просто записывает изменения во временный файл и, если команда sed
выполнена успешно (&&
), заменяет исходный файл на временный.
- Если вы хотите сохранить исходный файл в качестве резервной копии, добавьте еще одну команду
mv
, которая сначала переименовывает оригинал.
-
Предостережение. По сути, это то, что делает -i
, за исключением того, что он пытается сохранить разрешения и расширенные атрибуты (macOS) исходного файла; однако, если исходный файл является символической ссылкой, и это решение, и -i
заменят символическую ссылку на обычный файл.
См. нижнюю половину этого answer моего подробного описания работы -i
.
[1] Для более подробного объяснения см. этот ответ.
Ответ 7
Я исправил решение, отправленное @thecarpy:
Здесь подходящее кросс-платформенное решение для sed -i
:
sedi() {
case $(uname) in
Darwin*) sedi=('-i' '') ;;
*) sedi='-i' ;;
esac
LC_ALL=C sed "${sedi[@]}" "[email protected]"
}
Ответ 8
Обходной путь ниже может быть лучше:
sed '/foo/bar/' файл | tee file