나는 다음을하고 싶다
- 패턴( )을 사용하여
^[ \t]*services:[ \t]*
파일 내용 검색 - 발견되면 패턴을 문자열로 바꿉니다(변수에서).
- 그렇지 않으면 문자열(변수에)을 파일 끝에 추가합니다.
2단계에서 멈췄습니다. 스크립트의 2단계가 services:
파일에서 로 대체됩니다 ${serviceLoopBack}
. 내 명령에 무슨 문제가 있나요 sed
?
테스트 스크립트
#!/bin/bash
set -e
destfile=config.yml
serviceLoopBack="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route"
serviceLoopBackWithFrontSpace="|NL||NL|$serviceLoopBack"
if grep -E "^[ \t]*services:[ \t]*" $destfile
then
# service tag found, replace target string
sed -i -e 's/^[ \t]*services:[ \t]*/${serviceLoopBack}/g' $destfile
else
# service tag not found, append at end of file
echo $serviceLoopBackWithFrontSpace >> $destfile
fi
# replace separator 1 to new line
sed -i -e 's/|NL|/\n/g' $destfile
# replace separator 2 to space
sed -i -e 's/|FS|/ /g' $destfile
답변1
문제는 두 번째 sed
호출에서 프로그램을 작은따옴표( )로 묶는다는 것입니다 ' ... '
. 이는 권장되는 방법이지만 작은따옴표를 사용하면 셸이 셸 변수(예: )를 확장하는 것을 방지할 수 ${serviceLoopBack}
있으므로 텍스트가 셸 변수의 내용으로 대체되지 않고 그대로 유지됩니다.
한 가지 가능성은 다음과 같이 작은 따옴표 대신 큰 따옴표를 사용하는 것입니다.
sed -i -e "s,^[ \t]*services:[ \t]*,${serviceLoopBack}," "$destfile"
또는 sed
다음 명령을 사용하여 표현식을 쉘 변수로 어셈블하십시오 printf
.
printf -v prog 's,^[ \t]*services:[ \t]*,%s,' "$serviceLoopBack"
sed -i -e "$prog" "$destfile"
노트대체 텍스트도 포함되어 있기 때문에 ,
구분 기호로 사용하고 있습니다 . 그렇지 않으면 혼란스러울 수 있습니다 . 또한 행당 일치 항목이 하나만 있다고 가정하기 때문에 해당 옵션을 생략했습니다 ./
/
sed
g
어쨌든 쉘 스크립트의 참조를 보고 싶을 수도 있습니다.예를 들어 이 답변이유에 대해.
그러나 sed
엄격하게 요구되지 않는 경우 거의 전체 쉘 스크립트를 프로그램으로 대체할 수 있습니다 awk
(더 빠른).
awk -v slb="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route" \
'/^[ \t]*services:/{$0=slb;f=1}
{gsub(/\|NL\|/,"\n"); gsub(/\|FS\|/," ")} 1; END{if (!f) {print "\n\n" slb}}' "$destfile"
awk
그러면 변수에 "서비스 루프백" 사양이 가져옵니다slb
.- 그런 다음 패턴과 일치하는 줄이 있는지 확인하고
services:
, 그렇다면 전체 줄을 저장된 문자열로 바꿉니다slb
(내 생각에는 이것이 실제로 달성하려는 문자열입니다). 동시에 플래그를f
1로 설정합니다. - 모든 개행 문자와 모든 공백 발생을
gsub()
바꾸려면 , 를 사용하십시오 .|NL|
|FS|
- 겉보기에 "지저분한" 명령은 지금까지 수정된 모든 내용을 포함하여 현재 줄을 인쇄하도록
1
지시합니다 .awk
- 마지막으로
f
아직 설정되지 않은 경우 선행 패턴을 "Service Loopback" 줄에 추가합니다(단, 로 변환됨\n\n
).
awk
이 옵션을 이해하는 구현이 없으면 파일은 내부에서 편집되지 않습니다 . 출력을 임시 파일로 리디렉션한 다음 이를 임시 파일로 바꿔야 할 수도 있습니다.awk
-i inplace
$destfile
그러나 요약하자면 구조화된 언어(YAML)로 작업하고 있으므로 yq
전용 파서를 사용하는 것이 좋습니다(예: 줄 기반 텍스트 처리 도구 대신).
답변2
명령의 문제점은 sed
변수가 작은따옴표로 확장되지 않는다는 것입니다. 따라서 sed 스크립트를 큰따옴표로 묶어야 합니다. 표시하려면:
$ var="foo"
$ echo bar | sed 's/bar/$var/'
$var
$ echo bar | sed "s/bar/$var/"
foo
다음 문제는 대체 문자열에 포함되어 있으므로 /
다른 구분 기호를 사용해야 한다는 것입니다. 그것은 다음과 같습니다:
sed -i -e "s#^[ \t]*services:[ \t]*#${serviceLoopBack}#g" "$destfile"
점은 무엇인가 g
? 행당 둘 이상의 일치를 기대합니까?
다음은 몇 가지 개선 사항이 포함된 작업 버전의 스크립트입니다(인용문, grep -qm1
출력 제거 및 첫 번째 일치에서 중지).
#!/bin/bash
set -e
destfile=config.yml
serviceLoopBack="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route"
serviceLoopBackWithFrontSpace="|NL||NL|$serviceLoopBack"
if grep -Eqm 1 "^[ \t]*services:[ \t]*" "$destfile"
then
# service tag found, replace target string
sed -i -e "s#^[ \t]*services:[ \t]*#${serviceLoopBack}#g" "$destfile"
else
# service tag not found, append at end of file
printf '%s\n' "$serviceLoopBackWithFrontSpace" >> "$destfile"
fi
# replace separator 1 with newline and separator 2 with space
sed -i -e 's/|NL|/\n/g' -e 's/|FS|/ /g' "$destfile"