외부 종속성 없이 bash에서 yaml 파일 수정

외부 종속성 없이 bash에서 yaml 파일 수정

YAML 파일에 여러 구성이 있고 Bash 스크립트를 사용하여 일부 매개변수를 변경해야 합니다. 가능합니까? 외부 종속성 사용을 피하고 싶습니다.

내 YAML은 다음과 같습니다

%YAML 1.2
---
name: mic
components:
- name: Mic
  parameters:
    period_count: 4
    alsa_device_name: "pulse"

---
name: speaker
components:
- name: Speaker
  parameters:
    period_duration_ms: 20
    period_count: 4
    alsa_device_name: "pulse"

내가 제공한 것과 같이 작동하기를 원하면 내 구성에서 다음 과 같이 --mic logitec --speaker hk34수정해야 합니다.alsa_device_namemicspeaker

%YAML 1.2
---
name: mic
components:
- name: Mic
  parameters:
    period_count: 4
    alsa_device_name: "logitec"

---
name: speaker
components:
- name: Speaker
  parameters:
    period_duration_ms: 20
    period_count: 4
    alsa_device_name: "hk34"

Bash만 사용하여 이 작업을 수행할 수 있습니까? 가능하다면 어떻게 달성할 수 있나요? 지금은 Python 스크립트를 사용하고 있지만 이렇게 하면 have에 대한 추가 종속성이 추가되므로 python3이를 피하고 싶습니다.

답변1

첫째, 잘 정의된 구문이 있는 YAML과 같은 언어를 구문 분석하기 위해 줄 기반 도구를 사용하는 것은 좋지 않습니다. 프로덕션에서는 사용하지 마세요! 안 돼요.

명령줄을 통해 YAML을 구문 분석할 수 있는 몇 가지 구문 인식 도구가 있습니다.파이썬yq그리고가다yq. 이는 표준 줄 기반 도구로 이해되지 않는 앵커 및 블록 리터럴과 같은 YAML의 구조를 지원합니다.

위와 같은 일회성 처리의 경우 awk다음과 같은 것을 사용할 수 있습니다.

BEGIN {
    map["name: mic"] = "\"logitec\""
    map["name: speaker"] = "\"hk34\""
}

match($0, "name: mic") || match($0, "name: speaker") {
    key = substr($0, RSTART, RLENGTH)
}

/alsa_device_name/ {
    sub(/:.*/, ": " map[key])
}

{ print }

awk위 내용을 스크립트( .awk) 에 넣고 awk -f script.awk yaml명령줄의 일부로 실행할 수 있습니다. 명령줄에서 값을 다음과 같이 정의할 수도 있습니다.

awk -v mic='"logitec"' -v speaker='"hk34"' -f script.awk yaml

블록의 매개변수를 다음 BEGIN과 같이 처리합니다.

BEGIN {
    map["name: mic"] = mic
    map["name: speaker"] = speaker
}

또한 입력이 유효한 YAML 파일이 아니며 헤더 행으로 %YAML 1.2인해 대부분의 표준 YAML 파서가 입력 구문에 오류를 발생시킵니다.

답변2

yq이 답변을 작성하기 전에 다른 답변을 수락할 때까지 기다렸습니다.https://kislyuk.github.io/yq/

#!/bin/bash

unset mic
unset speaker

while getopts m:s: opt; do
        case $opt in
                m)
                        mic=$OPTARG
                        ;;
                s)
                        speaker=$OPTARG
                        ;;
                *)
                        echo 'invalid option' >&2
                        exit 1
        esac
done

shift "$(( OPTIND - 1 ))"

yaml_update () {
        key=${1,,}    # lower-case, e.g. "mic"
        Key=${key^?}  # title-case, e.g. "Mic"
        value=$2

        yq -Y --arg key "$key" --arg Key "$Key" --arg value "$value" '
                (select(.name == $key).components[] |
                select(.name == $Key).parameters.alsa_device_name) |= $value'
}

if [ "${mic+set}" = "set" ]; then
        yaml_update mic "$mic"
else
        cat -
fi |
if [ "${speaker+set}" = "set" ]; then
        yaml_update speaker "$speaker"
else
        cat -
fi

스크립트에 있는 대부분의 코드는 주어진 명령줄 옵션과 관련된 명령줄 구문 분석 및 논리입니다. 이는 편리한 옵션 세트와 함께 명령줄에서 솔루션을 사용할 수 있어야 한다는 질문의 요구 사항 또는 적어도 제안 때문입니다. 실제 코드는뭔가를 해라yaml_update함수에 위치하며 세 줄의 코드로 구성됩니다( yq명령, 한 줄의 코드가 너무 길어서 세 줄만 있습니다).

스크립트는 다음과 같이 사용됩니다.

./script -m logitec -s hk34 <file.yaml >file-new.yaml

두 가지 선택적 명령줄 옵션이 필요하며 -m( -s질문에 제안된 긴 옵션은 표준이 아니며 getopts내장에서 지원되지 않습니다 bash)마이크로폰그리고/또는스피커각각 Alsa 장치 이름입니다.

YAML 문서는 표준 입력으로 읽혀지고 결과 문서는 표준 출력에 기록됩니다.

업데이트는 yq키를 기반으로 올바른 최상위 개체를 선택하는 표현식을 사용한 name다음 다음을 사용 components하여 배열에서 올바른 배열 요소를 선택하여 수행됩니다.그것은 name열쇠. 그런 다음 alsa_device_name선택한 요소 키 parameters아래의 값을 업데이트합니다 .

편의를 위해 yq호출은 쉘 함수로 이동되었습니다(매우 유사한 호출을 두 번 수행할 수 있으므로 yq이것이 합리적으로 보입니다).

위와 같이 주어진 문서를 변경하면 출력은 다음과 같습니다.

name: mic
components:
  - name: Mic
    parameters:
      period_count: 4
      alsa_device_name: "logitec"
---
name: speaker
components:
  - name: Speaker
    parameters:
      period_duration_ms: 20
      period_count: 4
      alsa_device_name: "hk34"

관련 정보