sed -i (in-place editing)로 이식성을 어떻게 얻을 수 있습니까?


40

FreeBSD를 실행하는 공유 호스팅 인 서버용 셸 스크립트를 작성하고 있습니다. 또한 Linux를 실행하는 PC에서 로컬로 테스트하고 싶습니다. 따라서 이식 가능한 방식으로 작성하려고하지만 sed그렇게 할 방법이 없습니다.

내 웹 사이트의 일부는 생성 된 정적 HTML 파일을 사용하며이 sed 행은 각 재생성 후에 올바른 DOCTYPE을 삽입합니다.

sed -i '1s/^/<!DOCTYPE html> \n/' ${file_name.html}

sedLinux에서는 GNU와 함께 작동 하지만 FreeBSD sed는 -i옵션 이후의 첫 번째 인수 가 백업 사본의 확장이 될 것으로 예상합니다 . 이것이 다음과 같이 보일 것입니다 :

sed -i '' '1s/^/<!DOCTYPE html> \n/' ${file_name.html}

그러나 GNU sed는 그식이 바로 뒤에 이어질 것으로 예상합니다 -i. (또한 개행 처리로 수정해야하지만 이미 여기에 답변되어 있습니다 )

물론 서버의 스크립트 사본에이 변경 사항을 포함시킬 수 있지만 버전 관리를 위해 VCS를 사용하는 것은 혼란 스러울 것입니다. sed로 완전히 이식 가능한 방법으로 이것을 달성 할 수있는 방법이 있습니까?


제공 한 두 개의 sed 스 니펫은 동일합니다. 오타가없는 것입니까? 또한 백업 확장을 제공하는 GNU sed를 바로 실행할 수 있습니다.-i
— iruvar

이 문제를 찾아 주셔서 감사합니다. 내 질문을 수정했습니다. 두 번째 줄에서 내 sed에 오류가 발생하여 '1s / ^ / <! DOCTYPE html> \ n /'이 파일 일 것으로 예상하고 찾을 수 없다고 불평합니다.
— 레드

1
상호 참조 : Mac (BSD) 및 Linux on Stack Overflow에서 작동하는 sed 내부 플래그입니다 .
— MvG

답변:


39

GNU sed는 이후에 선택적 확장을 허용합니다 -i. 확장은 중간에 공백이없는 동일한 인수에 있어야합니다. 이 구문은 BSD sed에서도 작동합니다.

sed -i.bak -e '…' SOMEFILE

BSD에서는 -i입력 파일이 여러 개인 경우 동작이 변경됩니다. 파일은 독립적으로 처리됩니다 (예 : $각 파일의 마지막 행과 일치). 또한 이것은 BusyBox에서 작동하지 않습니다.

백업 파일을 사용하지 않으려면 사용 가능한 sed 버전을 확인할 수 있습니다.

case $(sed --help 2>&1) in
  *GNU*) set sed -i;;
  *) set sed -i '';;
esac
"$@" -e '…' "$file"

또는 대안으로 위치 매개 변수가 방해되지 않도록 함수를 정의하십시오.

case $(sed --help 2>&1) in
  *GNU*) sed_i () { sed -i "$@"; };;
  *) sed_i () { sed -i '' "$@"; };;
esac
sed_i -e '…' "$file"

귀찮게하지 않으려면 Perl을 사용하십시오.

perl -i -pe '…' "$file"

이식 가능한 스크립트를 작성하려면 사용하지 마십시오 -i. POSIX에는 없습니다. sed가 수행하는 작업을 수동으로 수행하십시오. 한 줄만 더하면됩니다.

sed -e '…' "$file" >"$file.new"
mv -- "$file.new" "$file"

2
GNU sed -i도 의미합니다 -s. GNU를 확인하는 가장 쉬운 방법은 GNU 에 유효한 noop이지만 다른 곳에서는 실패 sed하는 sed v명령을 사용하는 것입니다.
— mikeserv

위의 팁에서 영감을 받아 여기에 실제로 원하는 사람들을위한 단일 행 (추악한 경우) 휴대용 버전이 있습니다. 하위 쉘을 생성합니다 .GNU sed -i$(sed v < /dev/null 2> /dev/null || echo -n " ''") -e '...' "$file"sed가 아닌 경우 공백을 삽입 한 다음 두 개의 따옴표로 묶 -i습니다. BSD에서 작동합니다. GNU sed 만 가져옵니다 -i.
— Ivan X

2
@IvanX vGNU sed를 테스트하기 위해 명령 이 있다는 것에주의를 기울입니다 . FreeBSD가 그것을 구현하기로 결정했다면?
— Gilles 'SO- 악마 그만'

@Gilles Fair point, 그러나 GNU sed의 man 페이지는 v를 그 목적을위한 것으로 정확하게 설명합니다 (GNU sed가 아닌 다른 것을 감지 함). * BSD가이를 존중하기를 바랍니다. -i 자체를 사용하는 것 이외의 BSD sed에 오류를 발생시키는 동시에 GNU sed에 대한 조치를 취하지 않는 다른 테스트는 생각할 수 없지만 먼저 더미 파일을 작성해야합니다. 위의 sed에 대한 테스트는 정상이지만 인라인에 적합하지 않습니다. -i를 피하는 것은 전적으로 당신이 제안하는 것처럼 가장 안전한 내기처럼 보이지만 sed v기존의 목적으로 주어진 것을 사용 하는 것이 좋습니다.
— Ivan X

1
@lapo 대안은 함수를 정의하는 것입니다. 내 편집을 참조하십시오.
— Gilles 'SO- 악한 중지'

10

sed플레이를 멋지게 만드는 요령을 찾지 못하면 다음을 시도해보십시오.

  1. 사용하지 마십시오 -i:

    sed '1s/^/<!DOCTYPE html> \n/' "${file_name.html}" > "${file_name.html}.tmp" &&
      mv "${file_name.html}.tmp" "${file_name.html}"
  2. 펄 사용

    perl -i -pe 'print "<!DOCTYPE html> \n" if $.==1;' "${file_name.html}"

8

에드

항상 ed기존 파일 앞에 줄을 추가하는 데 사용할 수 있습니다 .

$ printf '0a\n<!DOCTYPE html>\n.\nw\n' | ed my.html

세부

주위의 비트 <!DOCTYPE html>는 ed해당 행을 파일에 추가 하도록 지시하는 명령 my.html입니다.

sed

이 명령을 sed사용할 수도 있다고 생각합니다 .

$ sed -i '1i<!DOCTYPE html>\n` testfile.csv

나는 결국 Perl에 의지했지만 ed를 사용하는 것이 Unix와 같은 사용자에게는 인기가없는 좋은 대안입니다.
— Red

@Red-문제가 해결되었다 니 다행입니다. 그래, 나는 전에 그것을 보지 못했다. 인터넷 검색은 그것을 돌리고 실제로 이것을 수행하는 가장 휴대용, 적절한 방법처럼 보였다.
— slm

7

perl -i후드에서 수행하는 작업을 수동으로 수행 할 수도 있습니다 .

{ rm -f file && { echo '<!DOCTYPE html>'; cat; } > file;} < file

마찬가지로 perl -i백업이 없으며 여기에 제공된 대부분의 솔루션과 마찬가지로 파일의 권한, 소유권에 영향을 줄 수 있으며 심볼릭 링크를 일반 파일로 바꿀 수 있습니다.

와:

sed '1i\
<!DOCTYPE html>' file 1<> file

sed파일 자체를 덮어 쓰므로 소유권과 권한 또는 심볼릭 링크에 영향을 미치지 않습니다. 일반적으로 명령으로 덮어 쓰기 전에 (내 경우에는 4k)에서 데이터로 가득 찬 버퍼를 읽었 sed으므로 GNU와 함께 작동합니다 . 파일이 출력을 버퍼링 한다는 사실을 제외하고 파일이 4k를 초과하면 작동하지 않습니다 .sedfileised

기본적으로 sed읽기 및 쓰기를 위해 4k 블록에서 작동합니다. 삽입 할 줄이 4k보다 작 으면 sed아직 읽지 않은 블록을 덮어 쓰지 않습니다.

나는 그것에 의존하지 않을 것입니다.


echo '<!DOCTYPE html>'""따옴표없이 이스케이프 해야 합니다.
— AD

@ AD 좋은 지적. 나는 대화식 bash / zsh의 기능을 일반적으로 스스로 비활성화하기 때문에 버그를 잊는 경향이 있습니다.
— Stéphane Chazelas

이것은 이미 존재하는지 확인하지 않고 예측 가능하고 정적 이름을 가진 "임시 파일"으로 리디렉션하는 보안 구멍이 넓지 않은 매우 적은 답변 중 하나입니다 . 나는 이것이 받아 들여야 할 답변이라고 생각합니다. 그룹 명령 사용에 대한 아주 좋은 데모.
— 와일드 카드

3

Mac OS X에서도 사용되는 FreeBSD sed 는 다음 (regex) 명령을 정확하고 명확하게 정의하고 인식하기 -e위해 -i스위치 뒤에 옵션이 필요합니다 .

즉, sed -i -e ...FreeBSD & GNU 모두에서 작동해야합니다 sed.

보다 일반적으로 FreeBSD 이후 백업 확장을 생략 sed -i하려면 명령 행 인수를 구문 분석하는 동안 FreeBSD의 일부에서 혼동을 피하기 위해 sed다음과 같은 명시적인 옵션 또는 스위치 가 필요합니다 .-ised

그러나, sed내부 파일 편집은 파일 inode 변경으로 이어집니다 (파일의 "제자리"편집 참조 ).

일반적인 힌트로, 최신 버전의 FreeBSD sed에는 -rGNU와의 호환성을 높이는 스위치가 sed있습니다.

echo a > testfile.txt
ls -li testfile.txt
#gsed -i -e 's/a/A/' testfile.txt
#bsdsed -i 's/a/A/' testfile.txt  # does not work
bsdsed -i -e 's/a/A/' testfile.txt
ls -li testfile.txt
cat testfile.txt

5
아니요, 내부 편집 bsdsed -i -e 's/a/A/'은 아니며 원본을 "-e"접미사 ( testfile.txt-e) 로 저장하여 편집하고 있습니다.
— Stéphane Chazelas

GNU sed는 FreeBSD와의 호환성을 위해 -r과 더불어 -E도 지원합니다. -E는 다음 POSIX 버전에서 지정 될 가능성이 높으므로 우리는 -r이 말도 안되는 존재를 잊어 버리고 존재하지 않는 척해야합니다.
— Stéphane Chazelas

2

sed -i경쟁 조건을 최대한 피하면서 단일 파일을 이식 가능하게 에뮬레이트하려면 다음을 수행하십시오.

sed 'script' <<FILE >file
$(cat file)
FILE

그건 그렇고, 이것은 sed -i디렉토리 및 파일 권한에 따라 sed -i사용자가 편집 권한이없는 파일을 덮어 쓸 수있게하는 가능한 문제를 처리합니다 .

다음과 같은 백업을 수행 할 수도 있습니다.

sed 'script' <<FILE >file
$(tee file.old <file)
FILE

2

Ex 모드에서 Vim을 사용할 수 있습니다 :

ex -sc '1i|<!DOCTYPE html>' -cx file
  1. 1 첫 줄을 선택하십시오

  2. i 텍스트와 개행 삽입

  3. x 저장하고 닫습니다

또는 주석에서와 같이 평범한 오래된 표준 ex :

printf '%s\n' 1i '<!DOCTYPE html>' . x | ex file 

여기에는 Vim에만 해당되는 것이 없습니다. 이는 구현이 다중 플래그 를 지원할 필요 가 없다는 점을 제외하고 POSIX 스펙을ex 완전히 준수 합니다. 명확한 이식성을 위해 다음을 사용합니다-cprintf '%s\n' 1i '<!DOCTYPE html>' . x | ex file
— Wildcard
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.