다음과 같은 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>
RUN61
XML 태그 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.Str
open-xml
TAG
contents
put
실제로 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 표현식을 기반으로 한 솔루션:
- 더 읽기 쉬움
- 내결함성이 더 높을 수 있음
- 하지만몇 가지 추가 종속성을 도입할 수 있습니다.