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.