w
내 명령 (macOS 13.1의 sed)은 (bash 3.2)을 사용하여 입력 파일을 편집 sed
할 수 있는 것 같습니다 .cat
printf "hello\nworld\n" > foo.txt
cat foo.txt | sed 's/l/L/g' | sed -n 'w foo.txt'
cat foo.txt
> heLLo
> worLd
나는 보았다https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html하지만 리디렉션 등을 foo.txt
사용할 때와 달리 위 파이프라인을 성공적으로 편집할 수 있는 이유가 무엇인지 잘 모르겠습니다.cat foo.txt | sed 's/l/L/g' > foo.txt
POSIX 비지정 플래그나 임시 파일을 사용할 수 있다는 것을 알고 있지만 (쓰기) 명령을 사용하여 입력 파일을 편집하는 것이 안전한지 알고 싶습니다 -i
.w
편집하다:
나는 노력했다
printf "%d hello world\n" {1..100000} > foo.txt
cat foo.txt | sed 's/l/L/g' | sed -n 'w foo.txt'
더 이상 제대로 작동하지 않는다는 것을 발견했습니다. 결과는 foo.txt
4000-8000 행에 불과합니다.
답변1
사용 sponge
(부터더 많은 유틸리티,또는임시 파일로 리디렉션하고 원본 파일로 이름을 바꿉니다.또는사용편집하다(또는 ex
vi/vim/nvi에서) sed
- 기억하세요. 이것은 sed
스트림 지향 버전입니다 ed
. ed
= editor
, sed
=개울편집하다.
참고: ed, sed 및 ex(그리고 vi - vi는 원래육ed의 정식 버전)은 모두 공통 루트를 가지고 있기 때문에 공통 명령 하위 집합을 공유합니다. 그러나 각각은 다른 방향으로 개발되었으며 서로 다른 향상 기능을 가지고 있습니다. 각기 다른 기능을 가진 여러 가지 버전이 있습니다. 다른 많은 프로그램은 최소한 몇 가지 공통 명령을 차용했습니다(예를 들어, rogue와 nethack은 모두 hjkl 이동 키를 차용했습니다). 명확하지 않은 경우에도 주목할 가치가 있습니다. ex
명령은 :
vi 내의 명령이며 명령의 상위 집합입니다 ( 사용하는 구현 ed
에 따라 다름 ).vi
세 가지 방법 모두의 예입니다.
sed -e 's/l/L/g' foo.txt | sponge foo.txt
sed -e 's/l/L/g' foo.txt > foo.new && mv foo.new foo.txt
printf '%s\n' %s/l/L/g w q | ed -s foo.txt
printf '%s\n' %s/l/L/g w q | ex foo.txt
그건 그렇고, 출처 man sponge
:
sponge
표준 입력을 읽고 지정된 파일에 씁니다. 쉘 리디렉션과 달리 스폰지는 출력 파일에 쓰기 전에 모든 입력을 흡수합니다. 이를 통해 동일한 파일을 읽고 쓰는 파이프라인을 구축할 수 있습니다.출력 파일이 이미 존재하는 경우 Sponge는 파일의 권한을 유지합니다.
노트:
Sponge는 기본적으로 메서드를 리디렉션하고 이름을 바꾸는 편리한 도구입니다.
리디렉션 및 이름 변경은 원본 출력 파일의 권한을 유지하지 않습니다. 사용자가 결정한 권한으로 새 파일을 생성합니다
umask
(생성된 다른 새 파일과 마찬가지로). umask에 따라 이러한 권한은 원래 권한과 동일할 수도 있고 동일하지 않을 수도 있습니다.차이점은 다음과 같습니다.
sponge
확실하게 하다새 파일에는 원본 파일과 동일한 권한이 있지만 단순 리디렉션에서는 그렇지 않습니다.and 를 사용
ed
하면ex
각 명령( write 및 finally quit 로s///
대체됨 )이 한 줄에 하나씩 인쇄되고 or 로 파이프 되어 foo.txt를 열고 명령을 실행합니다.w
q
printf '%s\n'
ed
ex
또한 참고: ed
둘 ex
다 원본 파일을 덮어씁니다(원본 파일의 inode 번호를 유지하므로 해당 파일에 대한 하드 링크가 끊어지지 않습니다). sponge
임시 파일에 쓰기 및 이름 바꾸기는 다른 inode 번호를 가진 새 파일을 생성하므로 하드 링크가 끊어집니다. 대부분의 경우(즉, 하나 이상의 파일에 대한 하드 링크가 없는 경우) 이는 전혀 중요하지 않지만 알아야 할 사항입니다.
예를 들어 다음과 같이 inode 번호가 어떻게 변경되는지 확인하세요 sponge
.
$ printf "hello\nworld\n" > foo.txt
$ ls -li foo.txt
2251637 -rw-rw-r-- 1 cas cas 12 Feb 6 18:07 foo.txt
$ sed -e 's/l/L/g' foo.txt | sponge foo.txt
$ ls -li foo.txt
2251985 -rw-rw-r-- 1 cas cas 12 Feb 6 18:07 foo.txt
리디렉션으로 파일을 다시 덮어써도 inode 번호는 변경되지 않으며 ex(또는 ed)로 편집하지도 않습니다.
$ printf "hello\nworld\n" > foo.txt
$ ls -li foo.txt
2251985 -rw-rw-r-- 1 cas cas 12 Feb 6 18:08 foo.txt
$ printf '%s\n' %s/l/L/g w q | ex foo.txt
$ ls -li foo.txt
2251985 -rw-rw-r-- 1 cas cas 12 Feb 6 18:09 foo.txt
필요한 경우 다음과 같이 리디렉션 및 이름 바꾸기 방법을 사용하여 원래 인덱스 노드를 보존할 수 있습니다.
sed -e 's/l/L/g' foo.txt > foo.new
cat foo.new > foo.txt
rm foo.new
cat
네, 그럴 필요는 없다는 걸 압니다 . <
작업도 리디렉션합니다. 나는 명령줄 시작 부분에서 리디렉션하거나 실제 명령 없이 리디렉션하는 것이 역겹도록 추악하고 두려움이나 수치심이 없다고 생각합니다.연합 대학
그리고 Stephen Kitt가 댓글에서 지적했듯이 cp foo.new foo.txt
이 기능도 작동하며 원래 권한을 그대로 유지합니다.
답변2
이 w
sed
명령은 처음 호출될 때 출력 파일을 엽니다(여기서는 sed
파이프에서 데이터 블록을 읽은 후 첫 번째 줄을 처리할 때) O_WRONLY | O_TRUNC
. 따라서 이 시점에서 파일은 비워집니다(자르기ated) 따라서 명령이 파일을 읽는 중이라면(귀하의 경우 cat
아직 읽기가 완료되지 않은 경우) 나머지 부분을 읽을 수 없습니다.
대신 다음과 같이 할 수 있습니다.
sed 's/l/L/g' < file 1<> file
쉘은 stdin에서 sed를 사용 O_RDONLY
하고 sed의 stdout에서 독립적으로 파일을 열지 O_RDWR
만 더 중요한 것은 O_TRUNC
이를 사용하지 않으면 sed
자체 입력을 덮어쓰게 된다는 것입니다.
이는 여기에서와 같이 항상 읽은 행과 정확히 동일한 크기(바이트 단위)의 출력 행을 쓰는 경우에만 작동합니다 sed
. 그렇지 않으면 아직 읽지 않은 행을 덮어쓰게 될 수도 있습니다.
또한 작성된 내용이 읽은 내용보다 짧은 경우 파일 끝에 오래된 데이터를 남겨 둡니다. 이 문제는 끝 부분이 잘린 표준 출력의 내용을 호출하여 해결할 수 있습니다. 예를 들면 다음과 같습니다.
{ sed 's/hello/hi/g'; perl -e 'truncate STDOUT, tell STDOUT'; } < file 1<> file
하지만 이를 사용하려면 이를 복사한 일부 구현을 perl
사용하는 것이 좋습니다 .-i
sed
perl -pi -e 's/hello/hi/g' file