bash를 사용하여 NMEA 0183 xor 체크섬을 생성하기 위해 스크립트에서 외부 명령을 제거하는 방법

bash를 사용하여 NMEA 0183 xor 체크섬을 생성하기 위해 스크립트에서 외부 명령을 제거하는 방법

최근에 bash를 사용하여 NMEA 0183 체크섬을 생성하고 확인해야 했는데 필요한 것을 정확히 달성하기 위해 작성된 문서를 많이 찾을 수 없었습니다.

NMEA 0183 문장은 $로 시작하여 *와 $와 * 사이의 모든 바이트의 16진수 XOR인 두 문자로 끝납니다. 예:

$INGGA,230501.547,2118.97946,N,15752.60495,W,2,08,1.1,5.17,M,,,0,0001*02

이 유틸리티는 문자열을 16진수로 변환하고 xor로 변환합니다. 이미 존재하는 체크섬을 확인하거나 생성 중인 NMEA 문장의 끝에 대한 체크섬을 생성하는 데 사용할 수 있습니다(공급한 문자열에서 $ 및 *..를 제거합니다).

#!/bin/bash

# =========================================================
# Reads a NMEA 0183 sentence and calculates the proper
# XOR checksum for the end.

# Will accept a string with or without a checksum on
# the end or $ on the front and calculate what the checksum
# should be.

# Sentence can be read as an argument but must be single quoted
# or preceded by a \ or the shell will try to interpret the
# talker as a variable and the result will be incorrect.
# Examples:

#     xor '$INHDT,207.7,T*27'
#     xor \$INHDT,207.7,T*27
#     xor INHDT,207.7,T

# If run with no arguments, will prompt user for data.  No
# quotes or backslash is needed then.

# Depends: xxd sed

# ===T.Young 09/2016=======================================

set -o pipefail
set -o errexit
set -o nounset

# Functions
# =========

depcheck() { # Checks that necessary external commands are present
             # and executable
    local DEPENDS="sed xxd"
    for PROG in $DEPENDS; do
        [[ -x "$(command -v $PROG)" ]] || {
            echo "$PROG MISSING!  Exiting."
            exit 0
            }
    done
    }

x_or() { # Here is where the magic happens
    # The next two lines strip out $ characters, or an
    # * and anything after it (checksum)
    HEAD="${SENTENCE%\**}"
    TAIL="${HEAD//\$}"

    # Convert ASCII string into hex and read into an array.
    # Each element in the array gets preceded by "0x"
    HEXVAL="$(xxd -pu <<< ${TAIL})"
    HEXARRAY=($(printf '%s' "${HEXVAL%0a}" | sed -e 's/../0x& /g'))

    # Loop through the array and do the xor, initially start $XOR at 0
    for (( x=0; x<"${#HEXARRAY[@]}"; x++ )); do
        XOR=0x$(printf '%02x' "$(( ${XOR:-0} ^ ${HEXARRAY[$x]} ))")
    done

    # Strip off the 0x from the result
    CLEAN=${XOR#0x}
    printf '%s\n' "${CLEAN^^}"
    }

main() {
    case "${1:-}" in
        "")  # No input specified, read from stdin
            depcheck
            read -r SENTENCE
            x_or
            ;;

        *) # Input was provided, use that
            depcheck
            SENTENCE="$1"
            x_or
            ;;
    esac
}

# Main
# ====

main "$@"

쉘 스크립팅을 할 때 나는 항상 외부 프로그램, 심지어 sed나 xxd와 같은 일반적인 프로그램의 사용을 제거하는 방법을 찾으려고 노력합니다. 쉘 내장 기능만 사용하여 위의 작업을 수행하는 방법을 아는 사람이 있으면 알려주십시오.

업데이트: Sato의 방법을 고려한 새로운 기능이 있습니다. 이를 통해 외부 프로그램 호출과 위의 관련 depcheck 기능을 완전히 제거할 수 있습니다.

x_or() { # Create a hex XOR checksum of all the bytes
    # Clean the line of $ character and anything before it
    TAIL="${SENTENCE##*$}"
    HEAD=${TAIL%\**}
    LEN=${#HEAD}

    # Loop through the string and do the xor
    # initially start $XOR at 0
    XOR=0
    for (( x=0; x<$LEN; x++ )); do
        (( XOR^=$(printf '%d' "'${HEAD:$x:1}'") ))
    done

    printf '%02X\n' "${XOR}"
    }

"LC_CTYPE=C"를 사용하여 함수를 호출합니다. 여기에서 수행할 수 있는 작업이 더 많이 있을 수 있지만 이는 매우 간단합니다.

답변1

개인적으로 나는 다음과 같이 할 것입니다 :

#! /usr/bin/env bash

log() {
    {
        printf '%s: ' "${0##*/}"
        printf "$@"
        printf '\n'
    } >&2
}


cksum() {
    tot=${#1}
    let len=tot-4

    let res=0
    while [ $len -gt 0 ]; do
        let res^=$( LC_CTYPE=C printf '%d' "'${1:$len:1}'" )
        let len--
    done

    let ptr=tot-2
    if [ x"$( printf '%s' "${1:$ptr}" | tr a-f A-F )" != x"$( printf '%02X' $res )" ]; then
        log '%s: invalid checksum (found %02X)' "$1" $res
    fi
}


check () {
    if expr "$2" : '\$.*\*[0-9a-fA-F][0-9a-fA-F]$' >/dev/null; then
        cksum "$2"
    else
        log 'invalid input on line %d: %s' "$1" "$2"
    fi
}


let cnt=0
if [ $# -ne 0 ]; then
    while [ $# -gt 0 ]; do
        let cnt++
        check $cnt "$1"
        shift
    done
else
    while read -r str; do
        let cnt++
        check $cnt "$str"
    done
fi

Shebang 라인은 을 주장 bash하지만 여전히 ksh93rand 에서 작동해야 합니다 zsh. 에 의존하지 않습니다 xxd. 따라야 할 스크립팅 스타일의 예라고 주장할 수도 없습니다. :)

관련 정보