변수와 함께 sed 명령을 사용해보십시오

변수와 함께 sed 명령을 사용해보십시오

그래서 bash 스크립트에 다음 변수를 정의했습니다.

new_commit="back:h3912kk"
old_commit="back:1.0.1"

file = docker-compose.yml    

그런 다음 다음 yml이 있습니다 $file.

version: '3'


services:

  mongo:
    container_name: mongo
    image: mongo:3.4
    volumes:
      - /home/mongo_files:/data/db
    networks:
      my_network:


  back:
    container_name: back
    image: index.docker.io/gg/back:1.0.1
    networks:
      my_network

networks:
    my_network:

라인을 바꾸고 싶어요

image: index.docker.io/gg/back:1.0.1 

~을 위한

image: index.docker.io/gg/back:h3912kk

sedBash 스크립트에서 다음 명령을 사용합니다.

echo sed -i "s/$old_commit/$new_commit/" $file

하지만 다음과 같은 오류가 발생합니다.

sed: -e expression #1, char 12: unterminated `s' command

물론 docker-compose 파일은 업데이트되지 않습니다.

내가 뭘 잘못했나요?

echo 명령을 실행하면 다음과 같이 표시됩니다.

sed -i s/back:1.0.1
/back:h3912kk/ docker-compose.yml

echo가 같은 줄에 명령을 표시하지 않는 이유는 무엇입니까?

편집하다:

@Kusalananda가 말했듯이 old_commit 변수는 정적 파일이 아닙니다. 이것이 귀하가 제공한 관련 답변을 재현할 수 없는 주된 이유인 것 같습니다 $old_commit(그래도 문제가 있다고 가정하지는 않습니다).

매개 변수를 가져오기 위해 old_commit다음 명령을 사용하여 관심 있는 기준을 충족하는 yml 파일의 줄을 찾습니다. 전체 코드는 다음과 같습니다.

output='back:h3912kk'

# here I split the name of the repo and the commit by ':'
readarray -d ':' -t array_new_commit <<< "$output"

repo="${array_new_commit[0]}"

file=docker-compose.yml

# here I loop over the lines of the yml file:
while read -r line; do
  # if a line contains the repo name str and image str I generate a line with the new commit
  if [[ $line == *$repo* && $line == *"image"* ]]; then

    # old_line=$line

    readarray -d '/' -t array_old_commit <<< "$line"
    old_commit=${array_old_commit[-1]}


    # new_line="${array_old_commit[0]}:${array_old_commit[1]}:$new_commit"
  fi
done<"$file"

어쨌든 (왜인지는 모르겠지만 bash를 처음 사용하는 경우) 이 변수를 이런 방식으로 생성해도 실제로는 정적 변수처럼 사용할 수 없습니다. (나는 이것을 정말로 이해하고 싶다)

변수를 yml 파일의 변수 old_commit로 바꾸는 해결 방법을 알려주실 수 있나요 ?new_commit

이 점을 지적해주신 Kusalananda님께 다시 한 번 감사드립니다.

PD 저는 우분투 0.18.4 배포판을 사용하고 있습니다.

편집하다: 두 번째 코드 부분을 사용하십시오.

yq -Y --arg new "$new_commit" --arg reposit "$repo" \
'".services." + $reposit + ".image |=index.docker.io/gg/" + $new' docker-compose.yml 

내 결과는 다음과 같습니다

.services.back.image |=index.docker.io/gg/back:h3912kk 
...

답변1

YAML 파일이나 구조화된 문서 형식(YAML, JSON, XML 등)으로 작성된 파일을 사용해서는 안 되며 sed, 해당 문서 형식용으로 작성된 파서를 사용해야 합니다.

YAML의 경우 셸용으로 작성된 두 가지 좋은 도구가 있습니다. 둘 다 혼란스럽게 .fromyq 이라고 합니다.yqhttps://kislyuk.github.io/yq/사용하기 쉬운 jq래퍼(및 jqJSON 파서)입니다.

이 답변의 일부에서는 질문의 코드에 표시된 대로 old_commit두 쉘 변수를 모두 정의했다고 가정합니다 . new_commit나중에 표시되는 출력은 실제로 정적 문자열로 할당하는 것이 아니라 언급하지 않은 다른 프로세스( $old_commit후행 개행 문자를 얻는 것처럼 보이는 곳)를 통해 할당한다는 것을 알려주므로 많이 말할 수는 없습니다. 무시해.

이 구현을 사용하여 아래의 키 값을 변경하려면 image다음을 사용할 수 있습니다.backservicesyq

yq -Y '.services.back.image |= "index.docker.io/gg/back:h3912kk"' file.yml

또는 원하는 $new_commit값을 사용하세요.

new_commit='back:h3912kk'

yq -Y --arg new "$new_commit" \
    '.services.back.image |= 
        "index.docker.io/gg/" + $new' file.yml

우리는수입--argCreateVariable을 사용하면 yq쉘 변수 값을 $new표현식 문자열에 직접 삽입하는 대신 표현식에 추가할 수 있습니다. 이렇게 하면 값이 올바르게 인코딩 yq되고 코드에 코드 삽입 취약점이 없는지 확인할 수 있습니다.

또는 문자열을 언급하지 않고 다음을 수행합니다 index.docker.io/gg/.

new_commit='back:h3912kk'

yq -Y --arg new "$new_commit" \
    '.services.back.image |=
        sub("[^/]*$"; "") + $new' file.yml

또는 저장소에 대해 별도의 변수를 사용하십시오.

new_commit='back:h3912kk'

yq -Y --arg new "$new_commit" --arg repo 'back' \
    '.services[$repo].image |=
        sub("[^/]*$"; "") + $new' file.yml

$old_commit정적 경로를 사용하는 대신 문자열을 사용하여 교체할 올바른 이미지를 찾을 수도 있습니다 .

old_commit='back:1.0.1'
new_commit='back:h3912kk'

yq -Y --arg old "$old_commit" --arg new "$new_commit" \
    '(.services[].image | select(endswith($old))) |=
        (rtrimstr($old) + $new)' file.yml

여기서 우리는 선택합니다모두 .services.*.image의 임의의 문자열로 끝나는 경로의 $old_commit끝 부분을 잘라서 로 바꿉니다 $new_commit.

질문의 샘플 YAML 문서를 고려하면 위의 각 명령은 다음을 생성합니다.

version: '3'
services:
  mongo:
    container_name: mongo
    image: mongo:3.4
    volumes:
      - /home/mongo_files:/data/db
    networks:
      my_network: null
  back:
    container_name: back
    image: index.docker.io/gg/back:h3912kk
    networks: my_network
networks:
  my_network: null

출력을 새 파일로 쉽게 리디렉션할 수 있습니다(또는 YAML 문서의 내부 편집 사용 --in-place).

yqUbuntu 등에서 얻는 도구는 snap다릅니다. 저는 이것을 사용하여 YAML을 JSON으로 변환하고 다시 JSON으로 변환하는 경향이 있습니다.

yq -j e file.yml |
jq '.services.back.image |= "index.docker.io/gg/back:h3912kk"' |
yq -P e

예제 YAML 파일이 주어지면 위와 동일한 출력이 생성됩니다. 위의 표현은 jq동일한 의미를 지닌 이 답변의 상단에 있는 어떤 표현으로든 대체될 수 있습니다.

답변2

yaml 파일에 sed를 사용한다고 판단하지 않겠습니다. 모든 사람은 자신이 원하는 방식으로 일을 완수합니다.

나는 얼마 전에 같은 문제에 직면했고 sed 정규식에서 변수를 "중괄호"하면 모든 것이 작동하기 시작하는 것 같습니다.

$ cat $file
version: '3'


services:

  mongo:
    container_name: mongo
    image: mongo:3.4
    volumes:
      - /home/mongo_files:/data/db
    networks:
      my_network:


  back:
    container_name: back
    image: index.docker.io/gg/back:1.0.1
    networks:
      my_network

networks:
    my_network:

그 다음에:

$ sed s/${old_commit}/${new_commit}/ $file

다음 결과를 제공합니다.

version: '3'


services:

  mongo:
    container_name: mongo
    image: mongo:3.4
    volumes:
      - /home/mongo_files:/data/db
    networks:
      my_network:


  back:
    container_name: back
    image: index.docker.io/gg/back:h3912kk
    networks:
      my_network

networks:
    my_network:

도움이 되었기를 바랍니다..

MacOS 10.13(bash 5.1.4.1, gnu sed 4.8) 및 Ubuntu 16.04(bash 4.3.48, gnu sed 4.2.2)에서 테스트되었습니다.

답변3

yq -i -Y --arg new "$VERSION" '.tool.image.tag |= $new' values.yaml

관련 정보