배쉬++ 파싱
소개
오늘 아침에 아들과 산책을 하다가 개선이 필요한 Bash 문제를 제안해 달라고 했습니다. 그는 정규식 구문 분석에 대해 언급했는데, 나는 그것이 잡일이라는 데 동의합니다. 그 후 나는 앉아서 이것을 위해 API와 같은 편리한 '읽기'를 제공하는 'regex_read' 함수를 작성했습니다. 아래에서 'regex_read' 기능을 더 소개하겠지만 먼저 내장된 'read' 명령을 사용하여 구문 분석을 검토해 보겠습니다.
'읽기'로 구문 분석
Bash에서 입력을 구문 분석하는 일반적인 방법은 내장 '읽기' 명령을 사용하는 것입니다. 이 명령은 한 번에 한 줄(또는 레코드)을 읽은 다음 토큰을 변수로 분할합니다. 다음과 같은 일반적인 예를 고려하십시오.
while read TOK1 TOK2 ALL_ELSE; do
# Do something with $TOK1 and $TOK2
done </some/input/file
여기에서 파일 '/some/input/file'이 열리고 이 루프의 범위 내에서 쉘의 표준 입력에 연결되어 '읽기'에 대한 입력을 제공합니다. 이 예에는 다음과 같은 묵시적 기본값이 있습니다.
일반적으로 $IFS에는 인쇄할 수 없는 문자가 포함됩니다. 해당 문자가 무엇인지 알고 싶다면 'printf' 내장 명령이 도움이 됩니다.
# Issue command
printf '%q\n' "$IFS"
$' \t\n'
이제 $IFS에 공백, 탭 및 줄 바꿈이 포함되어 있음을 알 수 있습니다. CSV 파일을 구문 분석해야 하는 경우 다음과 같은 것이 필요합니다.
IFS=$',\n'
read COL1 COL2 COL3 COL4 ALL_ELSE
이 구문은 상당히 간결하고 편리합니다. 당면한 문제에 대해 작동한다면 수정할 것이 없습니다. 그러나 때때로 구분 문자와 $IFS의 내용을 지정할 수 있는 것만으로는 충분하지 않습니다. 이 문제에 직면했다면 정규식 구문 분석이 최선의 선택입니다.
정규식으로 구문 분석
정규식을 사용한 구문 분석은 복잡한 입력 데이터에 대한 일반적인 접근 방식입니다. Bash에서 정규식으로 구문 분석하는 것은 약간 투박하며 다음과 같습니다.
while IFS= read; do
[[ $REPLY =~ systemd\[([^:]*)\]:\ (.*)$ ]] || continue
full_match="${BASH_REMATCH[0]}"
pid="${BASH_REMATCH[1]}"
msg="${BASH_REMATCH[2]}"
echo "systemd message: pid= '$pid', msg= '$msg'"
done </var/log/syslog
보시다시피 $BASH_REMATCH[]에서 토큰을 낚시하는 것은 일상적인 일입니다. 이 예에서 '읽기'가 토큰을 분할하는 것을 방지하기 위해 $IFS가 없음으로 설정되어 있다는 점은 주목할 만합니다. $REPLY는 레코드를 검색하는 데 사용됩니다. 제공되지 않은 경우 문서화된 기본 반환 변수입니다.
'regex_read'로 구문 분석
이전 게시물에서 Bash++를 소개했습니다. 이제 그 기능을 사용하여 번거로운 작업을 제거하겠습니다.
#!/bin/bash
############################################################
# Example script to demonstrate bash++ regex_read function
#
# John Robertson <[email protected]>
# Initial release: Mon Sep 14 10:29:20 EDT 2020
#
# Halt on error, no globbing, no unbound variables
set -efu
# import oop facilities and other goodies
source ../bash++
###################################
### Execution starts here #########
###################################
# Open file to supply input on file descriptor $FD.
# Use recent bash syntax to assign next unused file descriptor to variable FD.
# We do this so all standard streams remain available inside of loop for
# interactive commands.
exec {FD}</var/log/syslog
# Loop until no more data available from $FD
# Regex matches 'systemd' syslog entries, breaks out date stamp, pid, and message
while regex_read '^([^ ]+ [^ ]+ [^ ]+) .*systemd\[([^:]*)\]: (.*)' -u $FD; do
# First fetch the number of matches from the return stack.
RTN_pop n_matches
# Not interested in less than perfect match
(( n_matches == 4 )) || continue
# Pop match results into variables
RTN_pop full_match dateStamp pid msg
# Clear the terminal
clear
# "Full match is: '$full_match'"
echo "systemd message: pid= '$pid', dateStamp= '$dateStamp', msg= '$msg'"
# Use builtin bash menuing to branch on user's choice
PS3='Action? '
select action in 'ignore' 'review' 'quit'; do
case $action in
ignore) ;; # no worries
review) read -p 'Chase up all relevant information, present to user. [Return to continue] ';;
quit) exit 0;;
esac
# go get another line from syslog
break
done # End of 'select' menu loop
done
이 예에는 다음과 같은 몇 가지 관심 사항이 있습니다.
'regex_read' 구현
'regex_read'의 구현은 본질적으로 '읽기' 자체에서 파생되므로 간단합니다. 여기있어:
function regex_read ()
############################################################
# Similar to bash 'read' builtin, but parses subsequent
# read buffer using the supplied regular expression.
# Arguments:
# regex pattern
# Returns:
# logical TRUE if read was successful
# or logical FALSE on end-of-file condition.
# Return stack:
# Full string match (if any)
# token1_match (if any)
# ...
# Last argument is _always_ number of matches found. Pop it first.
#
{
# Stash the regular expression
local ndx count regex="$1"
# All other args are for 'read'
shift
# Call read with other supplied args. Fails on EOF
IFS= read $@ || return 1
# Apply regular expression parsing to read buffer
if [[ $REPLY =~ $regex ]]; then
# Place results on return stack
count=${#BASH_REMATCH[@]}
for (( ndx= 0; ndx < count; ++ndx )); do
RTN_push "${BASH_REMATCH[$ndx]}"
done
# Last stack arg is number of match results
RTN_push $count
else
# regex failed to match
RTN_push 0
fi
}
결론
Bash는 정규식을 사용하여 데이터를 구문 분석할 수 있습니다. 이 작업은 source'ingbash++으로 가져오는 'regex_read' 함수를 사용하여 훨씬 간단해집니다. 게시물의 모든 파일은 동일한Github repository에서 사용할 수 있습니다.
Reference
이 문제에 관하여(배쉬++ 파싱), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/jrbrtsn/bash-parsing-3opi텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)