파일 콘텐츠에서 일치하는 정규식을 변수로 바꿉니다.

파일 콘텐츠에서 일치하는 정규식을 변수로 바꿉니다.

나는 다음을하고 싶다

  1. 패턴( )을 사용하여 ^[ \t]*services:[ \t]*파일 내용 검색
  2. 발견되면 패턴을 문자열로 바꿉니다(변수에서).
  3. 그렇지 않으면 문자열(변수에)을 파일 끝에 추가합니다.

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"

노트대체 텍스트도 포함되어 있기 때문에 ,구분 기호로 사용하고 있습니다 . 그렇지 않으면 혼란스러울 수 있습니다 . 또한 행당 일치 항목이 하나만 있다고 가정하기 때문에 해당 옵션을 생략했습니다 .//sedg

어쨌든 쉘 스크립트의 참조를 보고 싶을 수도 있습니다.예를 들어 이 답변이유에 대해.

그러나 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(내 생각에는 이것이 실제로 달성하려는 문자열입니다). 동시에 플래그를 f1로 설정합니다.
  • 모든 개행 문자와 모든 공백 발생을 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"

관련 정보