YAML 파일을 읽고 특정 값을 출력하는 awk 문이 있습니다. 값 목록에서 키 값을 읽고 해당 키를 awk에 전달하는 루프에서 이 awk를 반복해야 합니다.
YAML 파일의 구조는 다음과 같습니다.
abc:
NAME: Bob
OCCUPATION: Technician
def:
NAME: Jane
OCCUPATION: Engineer
abc
OCCUPATION
키 값을 얻고 싶다고 가정 TECHNICIAN
하고 인터넷 검색을 통해 내가 원하는 것을 제공하는 awk 문을 구성했습니다.
> awk 'BEGIN{OFS=""} /^[^ ]/{ f=/^abc:/; next } f{ if (sub(/:$/,"")) abc=$2; else print abc,$1 $2}' test.yml| grep "OCCUPATION:" | cut -d':' -f2
Technician
그러나 이 루프를 사용하면 -v 옵션을 awk에 전달해도 결과가 나오지 않는 것 같습니다.
items="abc,def"
for item in $(echo $items | sed "s/,/ /g");
do
echo $item;
awk -v name="$item" 'BEGIN{OFS=""} /^[^ ]/{ f=/^\name:/; next } f{ if (sub(/:$/,"")) name=$2; else print name,$1 $2}' test.yml| grep "OCCUPATION:" | cut -d':' -f2;
done
내가 설정한 디버그 에코만 나타납니다.
abc
def
내가 어디서 잘못됐나요? 변수가 awk에서 올바르게 해석되어야 한다고 생각합니까?
편집: Steeldrivers의 의견을 바탕으로 입력 내용을 일부 변경했습니다.
items="abc,def"
for item in $(echo $items | sed "s/,/ /g");
do
echo $item;
awk -v name="$item" 'BEGIN{OFS=""} /^[^ ]/{ f=name; next } f{ if (sub(/:$/,"")) name=$2; else print name,$1 $2}' test.yml| grep "OCCUPATION:" | cut -d':' -f2;
done
하지만 이제 OCCUPATION
모든 값이 인쇄됩니다.
abc
Technician
Engineer
def
Technician
Engineer
연산자 를 사용해 보았지만 ~
오류가 발생하여 올바르게 사용하지 않는 것 같아서 값을 직접 구문 분석하기로 결정했지만 이로 인해 중복이 생성됩니다.
답변1
YAML, JSON 또는 XML과 같은 구조화된 텍스트로 작업할 때 구조를 "이해"하는 파서를 사용해야 합니다. 다양한 종류의 구조화된 텍스트(예: xmlstarlet
xml, jq
json 및yqyaml의 경우) 대부분의 프로그래밍/스크립팅 언어에는 구조화된 텍스트를 구문 분석하고 처리하기 위한 라이브러리가 있습니다.
Perl 코어 YAML 모듈을 사용하여 Perl에서 이 작업을 수행하는 방법은 다음과 같습니다.
(YAML 모듈이 핵심 모듈 배포의 표준 부분으로 포함되었을 때 perl >= 5.14 버전이 필요합니다. Perl 5.14는 2013년에 출시되었습니다. 이전 버전의 Perl의 경우 를 사용하여 YAML을 설치할 수 있습니다 cpan
.
#!/usr/bin/perl
use strict;
use YAML qw(LoadFile);
my $file = shift; # first arg is the input filename
my $data = LoadFile($file); # load the yaml data into a hashref variable
# loop over the remaining args (i.e. the keys)
foreach my $item (@ARGV) {
print "$item\n";
print $$data{$item}{'OCCUPATION'}, "\n";
}
예를 들어 다른 이름으로 저장 yaml.pl
하고 실행 가능하게 만듭니다 chmod +x yaml.pl
.
yaml 데이터가 라는 파일에 저장된 경우 input.yaml
다음과 같이 실행할 수 있습니다.
$ ./yaml.pl input.yaml abc def
abc
Technician
def
Engineer
awk 또는 sed처럼 이는 이해하기 어려운 한 줄로 압축될 수 있습니다.
$ perl -MYAML=LoadFile -E '$data=LoadFile(shift);foreach (@ARGV) {say $_;say $$data{$_}{"OCCUPATION"}}' input.yaml abc def
abc
Technician
def
Engineer
Perl은 자동으로 매개변수를 분할할 수도 있습니다. 예를 들어, foreach
루프를 다음과 같이 변경 하면 :
foreach my $item (split /\s*,\s*/,join(",",@ARGV)) {
다음과 같이 실행할 수 있습니다.
$ ./yaml.pl input.yaml abc def
또는
$ ./yaml.pl input.yaml "abc,def"
또는 임의의 조합(ghi 및 jkl 키가 사용된다고 가정):
$ ./yaml.pl input.yaml "abc,def" ghi jkl
답변2
사용 yq
( jq
래퍼https://kislyuk.github.io/yq/) 명령줄(또는 스크립트)에서 YAML을 구문 분석하려면 다음을 수행하세요.
$ yq -r '.abc.OCCUPATION' file.yml
Technician
쉘 루프에 제공하십시오 abc
.def
$ for thing in abc def; do yq -r --arg node "$thing" '$node,.[$node].OCCUPATION' file.yml; done
abc
Technician
def
Engineer
또는 탭으로 구분된 열의 경우:
$ for thing in abc def; do yq -r --arg node "$thing" '[$node,.[$node].OCCUPATION] | @tsv' file.yml; done
abc Technician
def Engineer
yq
즉, with 호출 --arg
뒤에는 yq
설정할 변수 이름과 설정할 값이 옵니다. 그런 다음 해당 변수를 표현식에 사용하십시오 yq
. 이게 효과가 있어비슷하게존재하다 jq
.
셸 루프가 없으며 대신 최상위 키에서 값을 가져옵니다.
$ yq -r 'foreach keys[] as $node (.;.;[$node,.[$node].OCCUPATION]|@tsv)' file.yml
abc Technician
def Engineer
yq
YAML 구문 분석에 사용할 수 있는 다른 도구가 있습니다 . Ubuntu에 설치하는 경우 yq
Mike snap
Farah라는 사람으로부터 버전을 받게 됩니다. 다르게 작동합니다. 저는 이를 사용하여 JSON으로 변환한 다음 데이터를 다음으로 파이프하는 경향이 있습니다 jq
.
$ yq -j e file.yml | jq -r '.abc.OCCUPATION'
Technician
$ for thing in abc def; do yq -j e file.yml | jq -r --arg node "$thing" '$node,.[$node].OCCUPATION'; done
abc
Technician
def
Engineer
또는 탭으로 구분된 열의 경우:
$ for thing in abc def; do yq -j e file.yml | jq -r --arg node "$thing" '[$node,.[$node].OCCUPATION] | @tsv'; done
abc Technician
def Engineer
답변3
적절한 텍스트 처리 도구가 있는 경우 간단한 텍스트를 처리하기 위해 쉘 루프가 필요하지 않습니다.앗; 아래에서는 GNU를 사용합니다.앗이를 위해 현재 일치하는 RS에 대한 역참조인 다중 문자 RS 및 RT를 정의할 수 있습니다.
$ awk -v RS='(^|\n)[a-z]+:\n' 'rt ~ /^abc:\n$/ { print $NF; exit } { rt=RT }' infile
Technician
신고된 값이 사실인지 엄격히 확인"직업"키를 지정하고 변수에서 키/헤더를 하드코딩하는 대신 전달하면 다음을 수행할 수 있습니다.
$ awk -v hdr='abc' -v key='OCCUPATION' -v RS='(^|\n)[a-z]+:\n' -F'\n' \
'rt ~ ("^" hdr ":\n") {
for(i=1; i<=NF; i++)
if(match($i, "^\\s*" key ":\\s*" )) { print substr($i, RSTART+RLENGTH); exit }
}
{ rt=RT }' infile
Technician
답변4
또한 사용 awk
:
awk -F'[[:space:]]+' '$1 == "" {if (s == "abc:" && $2 == "OCCUPATION:") print $3; next} {s=$1}' file
Technician
직업이 "네트워크 기술자"이거나 공백이 포함된 직업인 경우에는 실패합니다. 이를 방지하려면 다음을 수행하십시오.
awk -F'[[:space:]]+' '$1 == "" {if (s == "abc:" && $2 == "OCCUPATION:") { sub(/[^:]*:[[:space:]]*/,""); print }; next} {s=$1}' file
Technician
Ed Morton의 솔루션은 { sub(/[^:]*:[[:space:]]*/,""); print }
여기서도 작동합니다 print $3
.