Bash 스크립트에서 XML 태그 값의 일부를 추출하는 방법

Bash 스크립트에서 XML 태그 값의 일부를 추출하는 방법

다음과 같은 XML 파일이 있습니다( A.xml).

<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RunParametersVersion>NextSeq_4_0_0</RunParametersVersion>
  <ReagentKitSerialWasEnteredInBaseSpace>false</ReagentKitSerialWasEnteredInBaseSpace>
  <ExperimentName>210913-RUN61-COCO</ExperimentName>
  <PurgeConsumables>false</PurgeConsumables>
  <MaxCyclesSupportedByReagentKit>92</MaxCyclesSupportedByReagentKit>
  <ModuleName />
  <ModuleVersion />
</RunParameters>

RUN61XML 태그 port 가 포함된 bash 변수를 설정하고 싶습니다 <ExperimentName>210913-RUN61-COCO</ExperimentName>. 태그 값은 항상 다음과 같은 구조를 가집니다.

중요하지 않아-관련된-무관하다

대시로 구분하세요.

grep나는 좋은 결과없이 시도했습니다 .

runNumber=$(grep -o '<ExperimentName>.*</ExperimentName>' | cut -d '-' -f2 A.xml)

무엇을 해야할지 아시나요?

답변1

구조화된 데이터를 다루기 때문에 전용 파서를 사용해야 합니다. 예를 들어 다음과 같은 xmlstarlet태그 값을 추출해야 합니다 cut.

xmlstarlet sel -t -c "string(/RunParameters/ExperimentName)" A.xml | cut -d- -f 2

그래서, 당신은 사용할 수 있습니다

runNumber=$(xmlstarlet sel -t -c "string(/RunParameters/ExperimentName)" A.xml | cut -d- -f 2)

답변2

다음만 사용하세요 xmlstarlet:

experiment_name=$(
    xmlstarlet sel -t \
        -m '/RunParameters/ExperimentName' \
        -v 'substring-before(substring-after(., "-"), "-")' file.xml
)

이는 우리가 관심 있는 노드와 일치한 다음 두 개의 함수를 사용하여 substring-after()해당 노드 값의 중간 부분을 제거합니다 subsring-before().

그런 다음 xmlstarlet출력을 변수에 할당합니다 experiment_name.

또는 다음 xq에서 사용하세요.https://kislyuk.github.io/yq/

experiment_name=$(
    xq -r '.RunParameters.ExperimentName | split("-")[1]' file.xml
)

이는 노드의 값을 대시로 분할하고 결과 배열의 두 번째 요소를 반환합니다.

답변3

Raku(이전 Perl_6) 사용

raku -MXML -e 'for open-xml($*ARGFILES) {.elements(:TAG<ExperimentName>)>>.contents.put};' < input.xml

입력 예:

<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RunParametersVersion>NextSeq_4_0_0</RunParametersVersion>
  <ReagentKitSerialWasEnteredInBaseSpace>false</ReagentKitSerialWasEnteredInBaseSpace>
  <ExperimentName>210913-RUN61-COCO</ExperimentName>
  <PurgeConsumables>false</PurgeConsumables>
  <MaxCyclesSupportedByReagentKit>92</MaxCyclesSupportedByReagentKit>
  <ModuleName />
  <ModuleVersion />
</RunParameters>

예제 출력:

210913-RUN61-COCO

다른 사람들이 언급했듯이 이 작업에는 전용 XML 파서를 사용하는 것이 좋습니다. 간단히 말해서 위 코드의 경우 Raku는 bash 명령줄에서 호출되고 해당 명령을 사용하여 -M모듈을 로드합니다. 위의 코드는 쉘 리디렉션에 의존합니다(리디렉션 없이 입력을 문자열화해야 함). XML 파일을 사용하여 필요한 콘텐츠를 열고 쿼리하고 추출하고 반환하는 데 사용합니다.XML-MXML<open-xml()$*ARGFILES.Stropen-xmlTAGcontentsput

실제로 OP는 출력 부분을 cut추출하기 위한 매우 좋은 코드를 제공했으며 위의 Raku 솔루션은 OP의 코드를 통해 간단히 파이프될 수 있습니다. 그러나 전체 Raku 솔루션을 위해서는 RUN61위의 Raku 코드에 .split("-")[1]호출 .contains을 삽입하기만 하면 됩니다..put

raku -MXML -e 'for open-xml($*ARGFILES.Str) {.elements(:TAG<ExperimentName>)>>.contents.split("-")[1].put};'

https://github.com/raku-community-modules/XML
https://www.raku.org

답변4

다음을 통해 이름을 추출할 수도 있습니다 grep(옵션에서 -E확장 정규식 허용).

runNumber=$(grep -Eo '[[:alnum:]]+-[[:alnum:]]+' A.xml | cut -d- -f2)

해당 라벨의 행을 확인하려면 grep다른 명령어를 통해 사전 필터링할 수 있습니다.

runNumber=$(
  grep '<ExperimentName>' A.xml \
  | grep -Eo '[[:alnum:]]+-[[:alnum:]]+' \
  | cut -d- -f2
)

노트:

XPath 표현식을 기반으로 한 솔루션:

  • 더 읽기 쉬움
  • 내결함성이 더 높을 수 있음
  • 하지만몇 가지 추가 종속성을 도입할 수 있습니다.

관련 정보