답변:
이것은 실제로 Yuzem의 대답에 대한 설명 일뿐 이지만 다른 사람에게 많은 편집을해야한다고 생각하지 않았으며 주석은 형식을 허용하지 않습니다.
rdom () { local IFS=\> ; read -d \< E C ;}
"rdom"대신 "read_dom"이라고 부르고 약간 간격을두고 더 긴 변수를 사용하십시오.
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
}
이제 read_dom이라는 함수를 정의합니다. 첫 번째 행은 IFS (입력 필드 구분 기호)를이 함수의 로컬로 만들고>로 변경합니다. 즉, 공간, 탭 또는 줄 바꿈에서 자동으로 분할되는 대신 데이터를 읽을 때 '>'에서 분할됩니다. 다음 줄은 stdin에서 입력을 읽도록 지시하고 개행에서 멈추지 말고 '<'문자 (deliminate 플래그의 경우 -d)가 표시되면 중지하십시오. 읽은 내용은 IFS를 사용하여 분할되고 변수 ENTITY 및 CONTENT에 지정됩니다. 따라서 다음을 수행하십시오.
<tag>value</tag>
read_dom
'<'가 첫 번째 문자이므로 빈 문자열 을 얻는 첫 번째 호출 입니다. '>'문자가 없기 때문에 IFS에 의해 ''로 분할됩니다. 그런 다음 읽기는 빈 문자열을 두 변수 모두에 할당합니다. 두 번째 호출은 문자열 'tag> value'를 가져옵니다. 그런 다음 IFS에 의해 두 필드 'tag'와 'value'로 나뉩니다. 읽기는 다음과 같은 변수를 할당합니다 ENTITY=tag
및 CONTENT=value
. 세 번째 호출은 문자열 '/ tag>'를 가져옵니다. 그것은 IFS에 의해 '/ tag'와 ''의 두 필드로 나뉩니다. 읽기는 다음과 같은 변수를 할당합니다 ENTITY=/tag
및 CONTENT=
. 파일 끝에 도달했기 때문에 네 번째 호출은 0이 아닌 상태를 반환합니다.
이제 그의 while 루프는 위와 일치하도록 약간 정리했습니다.
while read_dom; do
if [[ $ENTITY = "title" ]]; then
echo $CONTENT
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
첫 번째 줄은 "read_dom 함수가 0 상태를 반환하는 동안 다음을 수행합니다."라고 말합니다. 두 번째 줄은 방금 본 엔터티가 "title"인지 확인합니다. 다음 줄은 태그의 내용을 에코합니다. 네 줄이 나옵니다. 제목 엔터티가 아닌 경우 루프는 여섯 번째 줄에서 반복됩니다. "xhtmlfile.xhtml"을 표준 입력 ( read_dom
함수의 경우)으로 리디렉션하고 표준 출력을 "titleOfXHTMLPage.txt"(루프의 앞부분에서 나오는 에코)로 리디렉션합니다.
이제 다음에 대해 (S3에 버킷을 나열하여 얻는 것과 비슷한) 다음을 제공합니다 input.xml
.
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>sth-items</Name>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>item-apple-iso@2x.png</Key>
<LastModified>2011-07-25T22:23:04.000Z</LastModified>
<ETag>"0032a28286680abee71aed5d059c6a09"</ETag>
<Size>1785</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
그리고 다음 루프 :
while read_dom; do
echo "$ENTITY => $CONTENT"
done < input.xml
당신은 얻을 것이다 :
=>
ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/" =>
Name => sth-items
/Name =>
IsTruncated => false
/IsTruncated =>
Contents =>
Key => item-apple-iso@2x.png
/Key =>
LastModified => 2011-07-25T22:23:04.000Z
/LastModified =>
ETag => "0032a28286680abee71aed5d059c6a09"
/ETag =>
Size => 1785
/Size =>
StorageClass => STANDARD
/StorageClass =>
/Contents =>
따라서 while
Yuzem과 같은 루프를 작성하면
while read_dom; do
if [[ $ENTITY = "Key" ]] ; then
echo $CONTENT
fi
done < input.xml
S3 버킷에있는 모든 파일 목록이 표시됩니다.
편집
어떤 이유로 든 local IFS=\>
당신을 위해 작동하지 않고 전역으로 설정하면 다음과 같이 기능 끝에서 재설정해야합니다.
read_dom () {
ORIGINAL_IFS=$IFS
IFS=\>
read -d \< ENTITY CONTENT
IFS=$ORIGINAL_IFS
}
그렇지 않으면 스크립트에서 나중에 행을 분할하면 엉망이됩니다.
편집 2
속성 이름 / 값 쌍을 분리하려면 다음 read_dom()
과 같이 기능을 보강 할 수 있습니다 .
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local ret=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $ret
}
그런 다음 함수를 작성하여 다음과 같이 원하는 데이터를 구문 분석하십시오.
parse_dom () {
if [[ $TAG_NAME = "foo" ]] ; then
eval local $ATTRIBUTES
echo "foo size is: $size"
elif [[ $TAG_NAME = "bar" ]] ; then
eval local $ATTRIBUTES
echo "bar type is: $type"
fi
}
그런 다음 read_dom
전화 하는 동안 parse_dom
:
while read_dom; do
parse_dom
done
그런 다음 다음 예제 마크 업이 제공됩니다.
<example>
<bar size="bar_size" type="metal">bars content</bar>
<foo size="1789" type="unknown">foos content</foo>
</example>
이 출력을 가져와야합니다.
$ cat example.xml | ./bash_xml.sh
bar type is: metal
foo size is: 1789
EDIT 3 다른 사용자 는 FreeBSD에서 문제가 있다고 말했으며 읽기에서 종료 상태를 저장하고 read_dom의 끝에서 그것을 반환하도록 제안했습니다.
read_dom () {
local IFS=\>
read -d \< ENTITY CONTENT
local RET=$?
TAG_NAME=${ENTITY%% *}
ATTRIBUTES=${ENTITY#* }
return $RET
}
왜 작동하지 않는지 알 수 없습니다.
IFS=\< read ...
만 읽기 호출에 대한 IFS를 설정할 것이다. (나는 read
XML을 파싱하기 위해 사용하는 관습을 결코지지하지 않으며 , 그렇게하는 것은 위험에 처해있어서 피해야한다고 믿는다.)
bash 만 사용하면 매우 쉽게 할 수 있습니다. 이 기능 만 추가하면됩니다 :
rdom () { local IFS=\> ; read -d \< E C ;}
이제 rdom을 읽기와 비슷하지만 HTML 문서에 사용할 수 있습니다. rdom을 호출하면 요소를 변수 E에, 내용을 var C에 할당합니다.
예를 들어 원하는 작업을 수행하려면
while rdom; do
if [[ $E = title ]]; then
echo $C
exit
fi
done < xhtmlfile.xhtml > titleOfXHTMLPage.txt
셸 스크립트에서 호출 할 수있는 명령 줄 도구는 다음과 같습니다.
또한 XSL 변환 스크립트가 거의없는 xmllint 및 xsltproc을 사용하여 명령 줄 또는 셸 스크립트에서 XML 처리를 수행합니다.
xpath 유틸리티를 사용할 수 있습니다. Perl XML-XPath 패키지와 함께 설치됩니다.
용법:
/usr/bin/xpath [filename] query
또는 XMLStarlet 입니다. opensuse에 설치하려면 다음을 사용하십시오.
sudo zypper install xmlstarlet
또는 cnf xml
다른 플랫폼에서 시도하십시오 .
xpath
에서 사전 설치된 시스템 은 스크립트의 구성 요소로 사용하기에 부적합합니다. 자세한 내용은 stackoverflow.com/questions/15461737/… 을 참조하십시오 .
apt-get install xmlstarlet
이것으로 충분합니다 ...
xpath xhtmlfile.xhtml '/html/head/title/text()' > titleOfXHTMLPage.txt
apt-get install libxml-xpath-perl
.
XML을 라인 지향 형식으로 변환하는 http://www.ofb.net/~egnor/xml2/ 에서 XML2 를 확인하십시오 .
차드의 대답에서 시작하여, 여기에는 주석을 적절하게 처리하고 2 개의 작은 기능 (2 개 이상을 모두 혼합 할 수 있음)으로 UML을 구문 분석하는 완벽한 작업 솔루션이 있습니다. 나는 chad의 것이 전혀 작동하지 않았다고 말하지는 않지만 형식이 잘못된 XML 파일과 관련하여 너무 많은 문제가있었습니다.
이 답변의 목적은 perl, python 또는 다른 것을 사용하는 복잡한 도구없이 UML을 구문 분석 해야하는 모든 사람에게 즉시 사용 가능한 즉시 bash 함수를 제공하는 것입니다. 나에 관해서는 cpan을 설치할 수 없으며 이전에 사용중인 이전 운영 체제 용 펄 모듈을 설치할 수 없으며 파이썬을 사용할 수 없습니다.
먼저,이 포스트에서 사용 된 UML 단어의 정의 :
<!-- comment... -->
<tag attribute="value">content...</tag>
편집 : 업데이트 된 기능, 처리 :
xml_read_dom() {
# /programming/893585/how-to-parse-xml-in-bash
local ENTITY IFS=\>
if $ITSACOMMENT; then
read -d \< COMMENTS
COMMENTS="$(rtrim "${COMMENTS}")"
return 0
else
read -d \< ENTITY CONTENT
CR=$?
[ "x${ENTITY:0:1}x" == "x/x" ] && return 0
TAG_NAME=${ENTITY%%[[:space:]]*}
[ "x${TAG_NAME}x" == "x?xmlx" ] && TAG_NAME=xml
TAG_NAME=${TAG_NAME%%:*}
ATTRIBUTES=${ENTITY#*[[:space:]]}
ATTRIBUTES="${ATTRIBUTES//xmi:/}"
ATTRIBUTES="${ATTRIBUTES//xmlns:/}"
fi
# when comments sticks to !-- :
[ "x${TAG_NAME:0:3}x" == "x!--x" ] && COMMENTS="${TAG_NAME:3} ${ATTRIBUTES}" && ITSACOMMENT=true && return 0
# http://tldp.org/LDP/abs/html/string-manipulation.html
# INFO: oh wait it doesn't work on IBM AIX bash 3.2.16(1):
# [ "x${ATTRIBUTES:(-1):1}x" == "x/x" -o "x${ATTRIBUTES:(-1):1}x" == "x?x" ] && ATTRIBUTES="${ATTRIBUTES:0:(-1)}"
[ "x${ATTRIBUTES:${#ATTRIBUTES} -1:1}x" == "x/x" -o "x${ATTRIBUTES:${#ATTRIBUTES} -1:1}x" == "x?x" ] && ATTRIBUTES="${ATTRIBUTES:0:${#ATTRIBUTES} -1}"
return $CR
}
그리고 두 번째 것 :
xml_read() {
# /programming/893585/how-to-parse-xml-in-bash
ITSACOMMENT=false
local MULTIPLE_ATTR LIGHT FORCE_PRINT XAPPLY XCOMMAND XATTRIBUTE GETCONTENT fileXml tag attributes attribute tag2print TAGPRINTED attribute2print XAPPLIED_COLOR PROSTPROCESS USAGE
local TMP LOG LOGG
LIGHT=false
FORCE_PRINT=false
XAPPLY=false
MULTIPLE_ATTR=false
XAPPLIED_COLOR=g
TAGPRINTED=false
GETCONTENT=false
PROSTPROCESS=cat
Debug=${Debug:-false}
TMP=/tmp/xml_read.$RANDOM
USAGE="${C}${FUNCNAME}${c} [-cdlp] [-x command <-a attribute>] <file.xml> [tag | \"any\"] [attributes .. | \"content\"]
${nn[2]} -c = NOCOLOR${END}
${nn[2]} -d = Debug${END}
${nn[2]} -l = LIGHT (no \"attribute=\" printed)${END}
${nn[2]} -p = FORCE PRINT (when no attributes given)${END}
${nn[2]} -x = apply a command on an attribute and print the result instead of the former value, in green color${END}
${nn[1]} (no attribute given will load their values into your shell; use '-p' to print them as well)${END}"
! (($#)) && echo2 "$USAGE" && return 99
(( $# < 2 )) && ERROR nbaram 2 0 && return 99
# getopts:
while getopts :cdlpx:a: _OPT 2>/dev/null
do
{
case ${_OPT} in
c) PROSTPROCESS="${DECOLORIZE}" ;;
d) local Debug=true ;;
l) LIGHT=true; XAPPLIED_COLOR=END ;;
p) FORCE_PRINT=true ;;
x) XAPPLY=true; XCOMMAND="${OPTARG}" ;;
a) XATTRIBUTE="${OPTARG}" ;;
*) _NOARGS="${_NOARGS}${_NOARGS+, }-${OPTARG}" ;;
esac
}
done
shift $((OPTIND - 1))
unset _OPT OPTARG OPTIND
[ "X${_NOARGS}" != "X" ] && ERROR param "${_NOARGS}" 0
fileXml=$1
tag=$2
(( $# > 2 )) && shift 2 && attributes=$*
(( $# > 1 )) && MULTIPLE_ATTR=true
[ -d "${fileXml}" -o ! -s "${fileXml}" ] && ERROR empty "${fileXml}" 0 && return 1
$XAPPLY && $MULTIPLE_ATTR && [ -z "${XATTRIBUTE}" ] && ERROR param "-x command " 0 && return 2
# nb attributes == 1 because $MULTIPLE_ATTR is false
[ "${attributes}" == "content" ] && GETCONTENT=true
while xml_read_dom; do
# (( CR != 0 )) && break
(( PIPESTATUS[1] != 0 )) && break
if $ITSACOMMENT; then
# oh wait it doesn't work on IBM AIX bash 3.2.16(1):
# if [ "x${COMMENTS:(-2):2}x" == "x--x" ]; then COMMENTS="${COMMENTS:0:(-2)}" && ITSACOMMENT=false
# elif [ "x${COMMENTS:(-3):3}x" == "x-->x" ]; then COMMENTS="${COMMENTS:0:(-3)}" && ITSACOMMENT=false
if [ "x${COMMENTS:${#COMMENTS} - 2:2}x" == "x--x" ]; then COMMENTS="${COMMENTS:0:${#COMMENTS} - 2}" && ITSACOMMENT=false
elif [ "x${COMMENTS:${#COMMENTS} - 3:3}x" == "x-->x" ]; then COMMENTS="${COMMENTS:0:${#COMMENTS} - 3}" && ITSACOMMENT=false
fi
$Debug && echo2 "${N}${COMMENTS}${END}"
elif test "${TAG_NAME}"; then
if [ "x${TAG_NAME}x" == "x${tag}x" -o "x${tag}x" == "xanyx" ]; then
if $GETCONTENT; then
CONTENT="$(trim "${CONTENT}")"
test ${CONTENT} && echo "${CONTENT}"
else
# eval local $ATTRIBUTES => eval test "\"\$${attribute}\"" will be true for matching attributes
eval local $ATTRIBUTES
$Debug && (echo2 "${m}${TAG_NAME}: ${M}$ATTRIBUTES${END}"; test ${CONTENT} && echo2 "${m}CONTENT=${M}$CONTENT${END}")
if test "${attributes}"; then
if $MULTIPLE_ATTR; then
# we don't print "tag: attr=x ..." for a tag passed as argument: it's usefull only for "any" tags so then we print the matching tags found
! $LIGHT && [ "x${tag}x" == "xanyx" ] && tag2print="${g6}${TAG_NAME}: "
for attribute in ${attributes}; do
! $LIGHT && attribute2print="${g10}${attribute}${g6}=${g14}"
if eval test "\"\$${attribute}\""; then
test "${tag2print}" && ${print} "${tag2print}"
TAGPRINTED=true; unset tag2print
if [ "$XAPPLY" == "true" -a "${attribute}" == "${XATTRIBUTE}" ]; then
eval ${print} "%s%s\ " "\${attribute2print}" "\${${XAPPLIED_COLOR}}\"\$(\$XCOMMAND \$${attribute})\"\${END}" && eval unset ${attribute}
else
eval ${print} "%s%s\ " "\${attribute2print}" "\"\$${attribute}\"" && eval unset ${attribute}
fi
fi
done
# this trick prints a CR only if attributes have been printed durint the loop:
$TAGPRINTED && ${print} "\n" && TAGPRINTED=false
else
if eval test "\"\$${attributes}\""; then
if $XAPPLY; then
eval echo "\${g}\$(\$XCOMMAND \$${attributes})" && eval unset ${attributes}
else
eval echo "\$${attributes}" && eval unset ${attributes}
fi
fi
fi
else
echo eval $ATTRIBUTES >>$TMP
fi
fi
fi
fi
unset CR TAG_NAME ATTRIBUTES CONTENT COMMENTS
done < "${fileXml}" | ${PROSTPROCESS}
# http://mywiki.wooledge.org/BashFAQ/024
# INFO: I set variables in a "while loop" that's in a pipeline. Why do they disappear? workaround:
if [ -s "$TMP" ]; then
$FORCE_PRINT && ! $LIGHT && cat $TMP
# $FORCE_PRINT && $LIGHT && perl -pe 's/[[:space:]].*?=/ /g' $TMP
$FORCE_PRINT && $LIGHT && sed -r 's/[^\"]*([\"][^\"]*[\"][,]?)[^\"]*/\1 /g' $TMP
. $TMP
rm -f $TMP
fi
unset ITSACOMMENT
}
마지막으로, rtrim, trim 및 echo2 (stderr로) 기능 :
rtrim() {
local var=$@
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
trim() {
local var=$@
var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
echo2() { echo -e "$@" 1>&2; }
아 그리고 처음에는 깔끔한 채색 동적 변수를 정의하고 내 보내야합니다.
set -a
TERM=xterm-256color
case ${UNAME} in
AIX|SunOS)
M=$(${print} '\033[1;35m')
m=$(${print} '\033[0;35m')
END=$(${print} '\033[0m')
;;
*)
m=$(tput setaf 5)
M=$(tput setaf 13)
# END=$(tput sgr0) # issue on Linux: it can produces ^[(B instead of ^[[0m, more likely when using screenrc
END=$(${print} '\033[0m')
;;
esac
# 24 shades of grey:
for i in $(seq 0 23); do eval g$i="$(${print} \"\\033\[38\;5\;$((232 + i))m\")" ; done
# another way of having an array of 5 shades of grey:
declare -a colorNums=(238 240 243 248 254)
for num in 0 1 2 3 4; do nn[$num]=$(${print} "\033[38;5;${colorNums[$num]}m"); NN[$num]=$(${print} "\033[48;5;${colorNums[$num]}m"); done
# piped decolorization:
DECOLORIZE='eval sed "s,${END}\[[0-9;]*[m|K],,g"'
함수를 작성하고 FPATH (ksh) 또는 FPATH (bash) 에뮬레이션을 통해로드하는 방법을 알고 있습니다.
그렇지 않은 경우 명령 행에서 모든 것을 복사 / 붙여 넣기 만하면됩니다.
xml_read [-cdlp] [-x command <-a attribute>] <file.xml> [tag | "any"] [attributes .. | "content"]
-c = NOCOLOR
-d = Debug
-l = LIGHT (no \"attribute=\" printed)
-p = FORCE PRINT (when no attributes given)
-x = apply a command on an attribute and print the result instead of the former value, in green color
(no attribute given will load their values into your shell as $ATTRIBUTE=value; use '-p' to print them as well)
xml_read server.xml title content # print content between <title></title>
xml_read server.xml Connector port # print all port values from Connector tags
xml_read server.xml any port # print all port values from any tags
디버그 모드 (-d)를 사용하면 주석 및 구문 분석 된 속성이 stderr에 인쇄됩니다.
./read_xml.sh: line 22: (-1): substring expression < 0
.
[ "x${ATTRIBUTES:(-1):1}x" == "x?x" ] ...
또 다른 명령 줄 도구는 새로운 Xidel 입니다. 또한 이미 언급 된 xpath / xmlstarlet과 달리 XPath 2 및 XQuery를 지원합니다.
제목은 다음과 같이 읽을 수 있습니다.
xidel xhtmlfile.xhtml -e /html/head/title > titleOfXHTMLPage.txt
또한 여러 변수를 bash로 내보내는 멋진 기능도 있습니다. 예를 들어
eval $(xidel xhtmlfile.xhtml -e 'title := //title, imgcount := count(//img)' --output-format bash )
파일 $title
의 제목과 $imgcount
이미지 수로 설정 합니다. 이는 bash에서 직접 파싱하는 것만 큼 유연해야합니다.
XML 파일에서 Linux와 Windows 형식의 파일 경로 변환에 대한 연구를 한 후 흥미로운 자습서와 솔루션을 찾았습니다.
원하는 기능을 수행 할 수있는 기성품 콘솔 유틸리티는 상당히 많지만, 쉽게 확장하고 적용 할 수있는 Python과 같은 범용 프로그래밍 언어로 몇 줄의 코드를 작성하는 데 시간이 덜 걸릴 것입니다 너의 요구.
다음은 lxml
파싱에 사용하는 python 스크립트 입니다. 파일 또는 URL의 이름을 첫 번째 매개 변수로, XPath 표현식을 두 번째 매개 변수로 가져와 주어진 표현식과 일치하는 문자열 / 노드를 인쇄합니다.
#!/usr/bin/env python
import sys
from lxml import etree
tree = etree.parse(sys.argv[1])
xpath_expression = sys.argv[2]
# a hack allowing to access the
# default namespace (if defined) via the 'p:' prefix
# E.g. given a default namespaces such as 'xmlns="http://maven.apache.org/POM/4.0.0"'
# an XPath of '//p:module' will return all the 'module' nodes
ns = tree.getroot().nsmap
if ns.keys() and None in ns:
ns['p'] = ns.pop(None)
# end of hack
for e in tree.xpath(xpath_expression, namespaces=ns):
if isinstance(e, str):
print(e)
else:
print(e.text and e.text.strip() or etree.tostring(e, pretty_print=True))
lxml
와 함께 설치할 수 있습니다 pip install lxml
. 우분투에서는을 사용할 수 있습니다 sudo apt install python-lxml
.
python xpath.py myfile.xml "//mynode"
lxml
URL을 입력으로 허용합니다.
python xpath.py http://www.feedforall.com/sample.xml "//link"
참고 : XML에 접두사가없는 기본 네임 스페이스 (예 :)가 있으면 식에서 접두어 ( 'hack'으로 제공)
xmlns=http://abc...
를 사용해야합니다p
(예 : 파일//p:module
에서 모듈 가져 오기)pom.xml
. 경우에p
접두사가 이미 XML에 매핑되는, 당신은 다른 접두사를 사용하는 스크립트를 수정해야합니다.
아파치 메이븐 파일에서 모듈 이름을 추출하는 좁은 목적을 제공하는 일회용 스크립트입니다. 노드 이름 ( module
)에 기본 네임 스페이스가 접두어로 붙는 방법에 유의하십시오 {http://maven.apache.org/POM/4.0.0}
.
pom.xml :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modules>
<module>cherries</module>
<module>bananas</module>
<module>pears</module>
</modules>
</project>
module_extractor.py :
from lxml import etree
for _, e in etree.iterparse(open("pom.xml"), tag="{http://maven.apache.org/POM/4.0.0}module"):
print(e.text)
pip install
오버 apt-get
또는 yum
콜을 정당화 할 수 있습니다 . 감사!
"적절한 도구없이 bash에서 XML, JSON을 구문 분석하지 마십시오"는 건전한 조언이지만, 나는 동의하지 않습니다. 이것이 부수적 인 일이라면, 적절한 도구를 찾아서 배우는 것은 허리가 가득합니다 ... Awk는 몇 분 안에 할 수 있습니다. 내 프로그램은 위에서 언급 한 모든 종류의 데이터에서 작동해야합니다. 지옥, 나는 몇 분 안에 문제를 해결할 수 있다면 5-7-10 개의 다른 형식을 구문 분석하는 30 가지 도구를 테스트하고 싶지 않습니다. XML, JSON 등은 신경 쓰지 않습니다! 나는 그들 모두를위한 단일 솔루션이 필요합니다.
예를 들어 : 나의 SmartHome 프로그램은 우리 집을 운영합니다. 그것을하는 동안 제어 할 수없는 너무 많은 다른 형식으로 많은 양의 데이터를 읽습니다. 필요한 데이터를 읽는 데 몇 분 이상 걸리지 않기 때문에 전용 도구를 사용하지 않습니다. FS 및 RS 조정을 통해이 awk 솔루션은 모든 텍스트 형식에 완벽하게 작동합니다. 그러나 기본 작업이 주로 해당 형식의 많은 데이터로 작업하는 경우에는 정답이 아닐 수도 있습니다!
어제 bash에서 XML을 파싱하는 문제. 다음은 모든 계층 적 데이터 형식에 대한 방법입니다. 보너스로-bash 스크립트의 변수에 직접 데이터를 할당합니다.
얇은 글씨를보다 쉽게 읽을 수 있도록 단계별로 솔루션을 제시하겠습니다. OP 테스트 데이터에서 test.xml 파일을 작성했습니다.
bash에서 XML을 구문 분석하고 90 문자로 데이터를 추출합니다.
awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml
나는 종종 다르게 테스트해야하기 때문에 실제 생활에서 수정하기가 더 쉽기 때문에 일반적으로 더 읽기 쉬운 버전을 사용합니다.
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml
형식이 어떻게 호출되는지는 중요하지 않습니다. 나는 가장 간단한 해결책만을 찾습니다. 이 특별한 경우, 데이터에서 개행이 레코드 구분 기호 (RS) 및 <> 구분 필드 (FS)임을 알 수 있습니다. 원래의 경우 두 레코드 내에서 6 개의 값을 복잡한 색인화하여 관련 시켰습니다. 데이터가 존재하는 시점과 필드 (레코드)가 존재하거나 존재하지 않는 시간을 찾으십시오. 문제를 완벽하게 해결하려면 4 줄의 awk가 필요했습니다. 따라서 아이디어를 사용하기 전에 각각의 필요에 맞게 조정하십시오!
두 번째 부분은 단순히 줄에 원하는 문자열 (RS)이 있고 필요한 경우 필요한 필드 (FS)를 인쇄하는 것입니다. 위의 방법으로 내가 사용한 마지막 명령 (4 배 더 길다)을 복사하고 적용하는 데 약 30 초가 걸렸습니다. 그리고 그게 다야! 90 자로 완료했습니다.
그러나 항상 스크립트의 변수에 데이터를 깔끔하게 가져와야합니다. 먼저 구문을 다음과 같이 테스트합니다.
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml
어떤 경우에는 인쇄 대신 printf를 사용합니다. 모든 것이 잘 보이면 변수에 값을 할당하는 것만으로 끝납니다. 나는 많은 사람들이 "eval"이 "evil"이라고 생각한다는 것을 알고 있습니다. 그러나 이것이 왜 나쁜 습관인지 이해할 수 없다면 계속 배우십시오! bash 변수 할당과 충분한 간격을 포함하여 모든 솔루션을 수행하려면 120자가 필요합니다.
eval $( awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml ); echo "host: $host, username: $username, password: $password dbname: $dbname"