IT Share you

GNU sed 및 BSD / OSX sed 모두에서 작업하려면 내부 편집을 위해 sed -i 명령이 필요합니다.

shareyou 2020. 12. 9. 22:00
반응형

GNU sed 및 BSD / OSX sed 모두에서 작업하려면 내부 편집을 위해 sed -i 명령이 필요합니다.


gmakeMacOS로 포팅하려는 메이크 파일 ( Linux 으로 개발 됨 )이 있지만 sed협력하고 싶지 않은 것 같습니다 . 내가하는 일은 GCC종속성 파일을 자동 생성하는 데 사용하고 sed. 의 관련 부분 makefile:

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

GNU / Linux에서 문제없이 실행되지만 MacOS에서 빌드하려고 할 때 다음과 같은 오류가 발생합니다.

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캐릭터를 자르는 것처럼 보이지만 해결책이 보이지 않습니다.


OS XsedLinux 버전 -i과는 다르게 인수를 처리합니다 .

다음 -e과 같이 추가하여 두 가지 모두에 대해 "작동"할 수있는 명령을 생성 할 수 있습니다 .

#      vv
sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $@

OS X sed -i은 다음 항목을 내부 편집 -i백업 사본대한 파일 확장자로 해석합니다 . (리눅스 버전 은와-i 확장자 사이에 공백이없는 경우에만이 작업을 수행합니다 .) 분명히 이것을 사용하는 부수적 인 영향은 -e원하지 않을 수도있는 확장자로 백업 파일을 얻을 수 있다는 것입니다 . 자세한 내용과 대신 사용할 수있는 깔끔한 접근 방식은이 질문에 대한 다른 답변을 참조하십시오.

OS X가 있기 때문에 표시되는 동작은 sed소비하는 s|||확장 후, 해석 등 (!) 다음 명령으로 인수 - 그 시작이 경우 t, sed인수로 대상 레이블을 기대 지점 - 투 - 레이블 명령으로 인식을 - 따라서 오류가 발생합니다.

파일을 생성 test하면 오류를 재현 할 수 있습니다.

$ sed -i 's|x|y|' test
sed: 1: "test": undefined label 'est'

사실은

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

MacOS에서 기대하는 바를 수행하지도 않습니다. 대신 -e확장자 가있는 백업 파일을 생성 합니다.

MacOS에 적합한 명령은 다음과 같습니다.

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

Linux에서는 -i사이의 공백을 제거 ""하십시오 ( 관련 답변 참조 ).

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

현재 받아 들여지는 대답은 두 가지 중요한 방식으로 결함이 있습니다.

  1. BSD sed (OSX 버전)에서는 -e옵션이 파일 확장자로 해석되므로 확장자가있는 백업 파일을 생성합니다 -e.

  2. 제안 된대로 darwin 커널을 테스트하는 것은 GNU 또는 BSD sed가 여러 시스템에 존재할 수 있으므로 크로스 플랫폼 솔루션에 대한 신뢰할 수있는 접근 방식이 아닙니다.

훨씬 더 신뢰할 수있는 테스트는 --versionsed의 GNU 버전에서만 찾을 수 있는 옵션 을 간단히 테스트 하는 것입니다.

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

sed의 올바른 버전이 결정되면 적절한 구문으로 명령을 실행할 수 있습니다.

-i 옵션에 대한 GNU sed 구문 :

sed -i -- "$@"

-i 옵션에 대한 BSD sed 구문 :

sed -i "" "$@"

마지막으로 모든 것을 크로스 플랫폼 함수에 통합하여 in place edit 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

# Add to .bashrc / .zshrc
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

(이전에는에 대한 --with-default-names옵션이 brew install gnu-sed있었지만 최근에 제거되었습니다)


이 문제도 발견하고 다음 해결책을 생각했습니다.

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

저는 Windows, Linux 및 OS X에서 ppl이 빌드되는 다중 OS 팀에서 일합니다. 일부 OS X 사용자는 또 다른 오류가 발생하여 불평했습니다. sed의 GNU 포트가 설치되어 있으므로 전체 경로를 지정해야했습니다.


martin clayton의 유용한 답변 은 문제에 대한 좋은 설명을 제공 하지만 [1] 라고 말했듯이 잠재적으로 원치 않는 부작용이있는 솔루션입니다.

다음은 부작용이없는 솔루션입니다 .

Caveat: Solving the -i syntax problem alone, as below, may not be enough, because there are many other differences between GNU sed and BSD/macOS sed (for a comprehensive discussion, see this answer of mine).


Workaround with -i: Create a backup file temporarily, then clean it up:

With a non-empty suffix (backup-file filename extension) option-argument (a value that is not the empty string), you can use -i in a way that works with both BSD/macOS sed and GNU sed, by directly appending the suffix to the -i option.

This can be utilized to create a backup file temporarily that you can clean up right away:

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

Obviously, if you do want to keep the backup, simply omit the && rm file.bak part.


Workaround that is POSIX-compliant, using a temporary file and mv:

If only a single file is to be edited in-place, the -i option can be bypassed to avoid the incompatibility.

If you restrict your sed script and other options to POSIX-compliant features, the following is a fully portable solution (note that -i is not POSIX-compliant).

sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
  • This command simply writes the modifications to a temporary file and, if the sed command succeeds (&&), replaces the original file with the temporary one.

    • If you do want to keep the original file as a backup, add another mv command that renames the original first.
  • Caveat: Fundamentally, this is what -i does too, except that it tries to preserve permissions and extended attributes (macOS) of the original file; however, if the original file is a symlink, both this solution and -i will replace the symlink with a regular file.
    See the bottom half of this answer of mine for details on how -i works.


[1] For a more in-depth explanation, see this answer of mine.


I've corrected the solution posted by @thecarpy:

Here's a proper cross-platform solution for sed -i:

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

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

참고URL : https://stackoverflow.com/questions/2320564/i-need-my-sed-i-command-for-in-place-editing-to-work-with-both-gnu-sed-and-bsd

반응형