읽을 수 있는 Bash 스크립트
14316 단어 shellprogramming
그러나 작성하기 좋은 파이프라인을 유지 관리하기가 어렵다는 것을 쉽게 알 수 있습니다. 한 달 뒤에 읽는 사람이라고 해도 각 조각의 목적이 무엇인지 기억하기 어렵습니다. 이것은 더 읽기 쉬운 스크립트를 위해 파이프라인을 중간 파일로 나누는 방법을 배웠습니다.
작업
내가 작업하고 있는 제품에는 최종 사용자에게 경고해야 하는 조건인 "경보"목록이 있습니다. 우리는 또한 알람 이름을 사람이 읽을 수 있는 이름, 설명 등에 매핑하는 지원되는 각 언어에 대한 현지화 파일을 가지고 있습니다. 이미 번역된 알람을 추적하기 어려울 만큼 많은 알람이 있습니다. 이것은 스크립트를 요구합니다!
알람 파일은 다음과 같습니다.
<!-- Alarms.xml -->
<?xml version="1.0" encoding="utf-8"?>
<config name="AlarmConfig">
<AlarmList name="IngredientAlarms" version="1.0">
<Alarm name="MozzarellaTooWarm">
<!-- some alarm-specific stuff here -->
</Alarm>
<Alarm name="PizzaTooCold">
</Alarm>
<!-- ... -->
현지화는 다음과 같습니다.
[{ stringId: "alarm_id_MozzarellaTooWarm",
localString: "MozzarellaTooWarm" },
{ stringId: "alarm_title_MozzarellaTooWarm",
localString: "Mozzarella Too Warm" },
{ stringId: "alarm_description_MozzarellaTooWarm",
localString: "The mozzarella has become too warm and must be used within the next five minutes" },
//...
]
계획을 세우다
xpath
를 사용하여 Alarms.xml에서 알람 이름을 추출합니다. alarm_id_
, alarm_title_
, alarm_description_
, alarm_operator_actions_
접두사를 붙여야 합니다. 그 방법을 찾아보세요. "stringId"
를 추출합니다. 아, 하지만 현지화해야 하고 전혀 경보가 아닌 항목에 대한 다른 항목이 있습니다. 그것들을 걸러내는 방법을 찾으십시오. 아마도 jq
를 사용하십시오. diff
를 사용하여 예상 키를 실제 키와 비교합니다. 이것은 bash 파이프라인을 만드는 방법에 대한 게시물이 아니므로 해당 부분에 대해 설명하겠습니다. 제가 생각해낸 것은 다음과 같습니다.
diff \
<(join -j 99999 \
<(echo 'alarm_id_
alarm_title_
alarm_description_
alarm_operator_actions_') \
<(xpath -q -e config/AlarmList/Alarm/@name Alarms.xml |
cut -d= -f2 | tr -d '"') \
| tr -d ' ' | sort) \
<(jq '.[] | select(.stringId | startswith("alarm_")) |
.stringId ' i18n/en.json | sort)
복잡하고 그런 면에서 인상적입니다. 그러나 당신이 유지하고 싶은 코드는 아닙니다.
한 번에 한 단계 씩
이전 단계는 우리가 필요로 하는 것을 달성했지만 무슨 일이 일어나고 있는지 보는 것은 꽤 어려웠습니다. 내 생각에 그 이유는
중간 파일을 사용하여 각 부분을 놀릴 수 있는지 봅시다.
cat > alarm_attrs << EOF
alarm_id_
alarm_title_
alarm_description_
alarm_operator_actions_
EOF
xpath -q -e config/AlarmList/Alarm/@name Alarms.xml |
cut -d= -f2 | tr -d '"' > expected_alarm_names
join -j 999 alarm_attrs expected_alarm_names |
tr -d ' ' | sort > expected_localization_keys
jq -r '.[] | select(.stringId | startswith("alarm_")) |
.stringId' en.json | sort > actual_localization_keys
이게 낫다! 각 부분은 개별적으로 이해할 수 있으며 단계 간의 종속성은 명명된 파일로 명시적으로 추적됩니다. 남은 것은 주석과 약간의 오류 검사를 추가하고 중간 파일을 정리하는 것뿐입니다.
우리 자신을 청소하는 것은 약간 까다 롭습니다. 이상적으로는 스크립트가 정상적으로 종료되는지, 충돌이 발생하는지 또는 ctrl-c로 취소되는지 여부에 관계없이 중간 결과 파일을 방치하는 것을 피하고 싶습니다.
trap
를 사용하여 이를 처리할 수 있습니다. trap
"신호"가 발생할 때 실행할 명령을 설정합니다. 모든 중간 파일을 디렉토리에 넣고 trap
를 사용하여 스크립트가 종료된다는 신호가 발생할 때 해당 디렉토리를 삭제합니다.#!/bin/bash
set -ef -o pipefail
readonly script_name=`basename "$0"`
usage() {
cat >&2 << EOF
Usage: $script_name <path-to-Alarms.xml> <path-to-en.json>
Compare the alarms specified in Alarms.xml against the
localizations keys provided in a [language-code].json file
(eg, en.json). Warn if any keys are missing or unexpected.
EOF
exit 2
}
if [[ $# != 2 ]]; then
echo "error: expected 2 arguments, received $#" 1>&2
usage
fi
readonly ALARM_CONFIG_XML=$1
if [[ ${ALARM_CONFIG_XML: -4} != ".xml" || ! -e $ALARM_CONFIG_XML ]]; then
echo "error: unable to find XML file at $ALARM_CONFIG_XML" 1>&2
usage
fi
readonly LOCALIZATION_JSON=$2
if [[ ${LOCALIZATION_JSON: -5} != ".json" || ! -e $LOCALIZATION_JSON ]]; then
echo "error: unable to find JSON file at $LOCALIZATION_JSON" 1>&2
usage
fi
# Make a directory for intermediate results
tmpdir=$(mktemp -d -p .)
# ensure it's removed when this script exits
trap "rm -rf $tmpdir" EXIT HUP INT TERM
# note: when debugging, turn off that `trap` line to keep
# intermediate results around
# The plan is ultimately to use diff to compare names between
# Alarms.xml and en.json. diff will tell us if any names
# appear in one file but are missing in the other (checks
# both directions for a mismatch).In pursuit of this, we need
# to create a couple of temporary files:
# 1) all the alarm names we expect to find (based on Alarms.xml)
# 2) all the names actually present in the localization file.
# A complication: the location file uses a flat format to
# store the id, title, description, and operator_actions:
# { "stringId": "alarm_id_MozzarellaTooWarm", ... },
# { "stringId": "alarm_title_MozzarellaTooWarm", ... },
# { "stringId": "alarm_description_MozzarellaTooWarm", ... },
# { "stringId": "alarm_operator_actions_MozzarellaTooWarm", ... },
# We want to check that *all* of these keys are present, so
# we use a cross product of (alarm names) X (those attributes)
cat > $tmpdir/alarm_attrs << EOF
alarm_id_
alarm_title_
alarm_description_
alarm_operator_actions_
EOF
xpath -q -e config/AlarmList/Alarm/@name $ALARM_CONFIG_XML |
cut -d= -f2 | tr -d '"' > $tmpdir/expected_alarm_names
# trick to compute cross product: use `join` with a join
# field that doesn't exist (999). since both files lack a
# field at 999, they will compare equal for every key, and
# each line of the left file will be joined with each line
# of the right file--a cross product.
join -j 999 $tmpdir/alarm_attrs $tmpdir/expected_alarm_names |
tr -d ' ' | sort > $tmpdir/expected_localization_keys
jq -r '.[] | select(.stringId | startswith("alarm_")) |
.stringId' $LOCALIZATION_JSON |
sort > $tmpdir/actual_localization_keys
if diff $tmpdir/expected_localization_keys $tmpdir/actual_localization_keys; then
echo "Success! Found all" $(wc -l $tmpdir/expected_localization_keys) "expected localization keys" 1>&2
else
echo "Failure. Found discrepancies between expected alarm names and actual localization keys" 1>&2
exit 1
fi
Reference
이 문제에 관하여(읽을 수 있는 Bash 스크립트), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/bgschiller/readable-bash-scripts-41d3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)