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따라야 할 스크립팅 스타일의 예라고 주장하지도 않습니다. :)

관련 정보