나는 여기서 이 질문을 하는 망치 주머니만큼 어리석은 느낌이지만, 긴 하루가 지났고 여기서 내가 뭘 잘못하고 있는지 전혀 모릅니다.
파일이 있습니다 textfile.txt
. 그 내용은(이 대표적인 예이지만 조작된 예에서는) 다음과 같습니다.
문서 내용
SOME=1
TRIED="THEIR BEST AT"
INSERTING='A VALUE'
BE=4
이제 두 개의 변수가 있다고 가정 $KEY
하고 $VAL
파일에서 업데이트하려는 키와 값을 나타내는 가 있다고 가정해 보겠습니다.내레이터: 둘 다 따옴표["]나 아포스트로피[']를 포함하지 않을 것이라고 보장할 수 있습니다.):
새로운 가치
KEY="TRIED"
VAL="THEIR HAND AT"
알았어, 좋아. 이제 나는 늪지 표준 정규식 대체처럼 보이는 것을 트리거하고 있습니다(교체된 값이 무엇이든 포함하려고 한다는 점에 유의하세요).임의로 선택할 수 있는현재 주변에 구분 기호가 있습니다):
sed -E "s/$KEY=([\"']?).*([\"']?)/$KEY=\1$VAL\2/g" textfile.txt > textfile.txt
예상되는 결과
SOME=1
TRIED="THEIR HAND AT"
INSERTING='A VALUE'
BE=4
실제 결과
(빈 파일)
알았어, 알았어. 내 대상이 내가 읽고 있는 파일이 아닌 새 파일이라면 어떻게 될까요?
sed -E "s/$KEY=([\"']?).*([\"']?)/$KEY=\1$VAL\2/g" textfile.txt > textfile2.txt
새로운 결과
SOME=1
TRIED="THEIR HAND AT
INSERTING='A VALUE'
BE=4
수도!
AT
...이제 값 주위에 두 번째 구분 기호(후행 큰따옴표)가 누락되었습니다. 재사용할 수 \1
있고 작동하지만 깨지기 쉬운 느낌이 들고 공이 어디에 떨어지는지 알 수 없습니다.
그럼...질문:
- 첫 번째 시도에서 파일이 완전히 지워지는 이유는 무엇입니까?
- 두 번째 구분 기호에서 후자 구분 기호가 생략된 이유는 무엇입니까?
나는 이 접근 방식을 전혀 좋아하지 않으며 다른 길을 갈 수도 있지만 누군가 이 두 가지 사항도 설명해 주시면 매우 감사하겠습니다. 어제 나에게 물으셨다면 RegEx를 알고 있다고 맹세했을 것입니다.분Shell에서 사용했기 때문입니다.
저는 Ventura 13.0의 2021 MacBook M Chip Pro에서 GNU bash 버전 5.2.12를 실행하고 있습니다(도움이 된다면).
답변1
-i
스위치가 없고 스위치 sponge
가 있는 경우moreutils
, 넌 할 수있어:
sed 's///g' file | sponge file
이 도구는 이러한 목적을 위해 특별히 설계되었습니다.
답변2
글쎄, 이것은 약간의 파기가 필요했습니다. don_crissti(출력을 지우는 타당한 이유를 지적해 주신)와 Gilles Quenot(이해하려고 매우 어려운 토끼굴로 보내주신 sponge
)에게 감사드립니다.
그러나 최종 최종 결과는 다음과 같습니다.
>
이는 / 리디렉션을 사용할>>
때 예상되는 동작 이므로 파일 내용이 지워집니다 . 나머지 명령보다 먼저 리디렉션이 실행되므로 읽고 있는 파일을 지우고 정리한 다음(아무 것도 리디렉션하여) 명령 결과를 해당 파일에 쓸 수 없습니다. 파일입니다(읽은 파일이 이제 비어 있고 리디렉션이 발생했기 때문입니다).이 경우 해결 방법은 임시 파일을 만들고 즉시 소스 파일을 덮어쓰는 것입니다.
sed -E "s/$KEY=([\"']?).*([\"']?)/$KEY=\1$VAL\2/g" textfile.txt > textfile.tmp && mv textfile.tmp textfile.txt
이는 GNU의 덜 강력한 바이너리 버전을 사용하는 MacOS의 불행한 결과이며
sed
, GNU 버전을 얻더라도brew install gsed
기본 버전을 안전하게 대체할 수 없으므로 이식성과 미래 보장이 불가능해집니다....하지만 여전히 "사라지는 문자열 구분 기호 케이스"라는 무거운 부담을 안고 있습니다. 그러나 조사하는 동안 나는 Darwin 버전의 도구에서 정규 표현식의 확장 변수를 사용하는 것과 관련된 흥미로운 사실을 우연히 발견했습니다 . 확장에서 변수 이름을 감싸서 '를
sed
피해야 했습니다 .$
따라서
sed -E "s/$KEY=([\"']?)
[...] 등 대신에 필요한 구문은sed -E "s/\$(KEY)=([\"']?)
[...] 등입니다.$
추가 조사 결과에 따르면 다음과 같이 각 변수를 간단히 이스케이프 처리하면sed -E "s/\$KEY=([\"']?)
[...] 등이 가능해졌습니다 ....나는 또한 원하는 결과를 얻을 수 있지만 나보다 더 솔직한 Linux 베테랑은
$(VARIABLE)
이 경우 구문이 더 안전하다고 제안할 것입니다(비록 이유는 모르겠지만).
이 후속 기사가 같은 어려움을 겪고 있는 다른 사람들에게 도움이 되기를 바랍니다. 올바른 방향을 알려준 don_crissti와 Gilles Quenot에게 다시 한 번 감사드립니다! <3