저는 작업 중인 XML 중 하나에 대한 입력으로 사용할 수 있도록 특정 형식의 문자열을 생성하는 셸 스크립트를 작성하고 있습니다.
다음 형식의 입력 파일이 제공됩니다.<attribute field>,<data_type>,<size>
instanceid,varchar,256
sysdate,date
status,number
notes,varchar,4000
created_on,date
"체크섬"을 변수(예: md5( INSTANCEID || STATUS || NOTES)
Or'd)에 있는 필드를 제외한 모든 속성 필드에 저장하고 싶습니다.
제가 쓴 스크립트는 이렇습니다
IFS=$'\n'
file=$(cat source.txt)
line_number=$(cat source.txt | wc -l)
checksum="md5( "
for line in $file
do
let line_number=line_number-1
data_field=$(echo $line | cut -f1 -d','| tr "a-z" "A-Z")
data_type=$(echo $line | cut -f2 -d',' | tr "a-z" "A-Z")
if [ $data_type != "DATE" ] && [ $line_number -gt 0 ]
then checksum+="$data_field || "
elif [ $data_type != "DATE" ] && [ $line_number -eq 0 ]
then checksum+=" $data_field "
fi
done
checksum+=")"
echo $checksum
이 스크립트는 날짜 유형의 속성이 있는 마지막 행을 제외한 모든 입력 시나리오에서 작동합니다.
이 경우 변수의 값은 다음과 같습니다.md5( INSTANCEID || STATUS || NOTES || )
마지막 행이 날짜인지 확인하기 위해 명령을 사용해 보았지만 tail
마지막 행이 날짜 유형이면 다시 실패합니다.
||
마지막 항목을 어떻게 제거할 수 있나요 ?
답변1
checksum="${checksum% || })"
대신 빠른 대답은 입니다 checksum+=")"
. 각 단계에서 무조건 문자열을 추가한 ||
다음 마지막에 불필요한 마지막 문자열을 제거하면 됩니다(그러므로 line_number
더 이상 계산이 필요하지 않습니다).
더 좋은 방법은
awk -F, 'BEGIN { printf "md5( " }
toupper($2) != "DATE" { printf "%s%s", sep, toupper($1); sep = " || " }
END { print ")" }' source.txt
답변2
cat
쉘 스크립트에서는 거의 유용하지 않다는 점은 주목할 가치가 있습니다.$(cat source.txt | wc -l)
고전이다쓸모없는 사용cat
; 이것은 파일의 줄 수를 계산해야 하는 경우$(wc -l < source.txt)
더 깔끔한 방법입니다 .- 그러나 계산에는 행 수가 필요하지 않습니다
source.txt
. file=$(cat source.txt)
파일을 읽는 추악한 방법입니다.읽는 동안… 하다 ︙ 완료<파일 이름
더 나은.read
이것의 이점은 행을 필드로 분할한다는 것입니다.tr
전체 파일에 대해 한 번만 실행하면 되지만 파일의 각 줄에 대해 두 번 실행하는 것은 어리석은 일입니다. 일부 경우에,트…<파일 이름|읽는 동안... 하다 ︙ 완벽한
좋은 결과. 그러나 문제가 있습니다.while
루프가 하위 쉘에서 실행되므로checksum
쉘 변수(예를 들어)에 대한 변경 사항은 루프가 끝난 후에 표시되지 않습니다. Terdon은 이 문제를 해결하는 한 가지 방법을 보여줍니다.트…<파일 이름|{읽는 동안… 하다 ︙ 변경될 수 있는 주문체크섬. ︙ 완벽한 ︙ 사용된 명령$체크섬. ︙ }
아시다시피, 마지막으로 발생한 문제가 어디인지 파악하는 것은 어려울 수 있습니다. 일반적으로 첫 번째 항목을 식별하는 것이 더 쉽습니다.
checksum="md5(" first=1 tr "a-z" "A-Z" < source.txt | { while IFS=, read data_field data_type size do if [ "$data_type" != "DATE" ] then if [ "$first" ] then first= else checksum+=" || " fi checksum+="$data_field" fi done checksum+=")" echo "$checksum" }
참고하시기 바랍니다진짜두 번 테스트할 필요가 없습니다
if [ "$data_type" != "DATE" ]
. 또한 타당한 이유가 없고 수행 중인 작업을 확실히 알고 있지 않는 한
항상 쉘 변수(예: )에 대한 참조를 인용해야 합니다 ."$data_type"
추가 최적화로 이
first
변수를 제거하고checksum
그 자체를 사용하여 루프의 첫 번째 반복을 식별할 수 있습니다.checksum= tr "a-z" "A-Z" < source.txt | { while IFS=, read data_field data_type size do if [ "$data_type" != "DATE" ] then if [ "$checksum" != "" ] then checksum+=" || " fi checksum+="$data_field" fi done checksum="md5($checksum)" echo "$checksum" }
답변3
당신이 작성하는 것만큼 복잡한 것은 필요하지 않습니다. 다음을 수행할 수 있습니다.
#!/usr/bin/env bash
checksum="md5("
## Read each line into the fields array (read -a fields), with fields
## separated by commas (IFS=,)
while IFS=, read -a fields
do
## If the 2nd element of the array is not "DATE"
if [ ${fields[1]} != "DATE" ]
then
## Add this to $checksum
checksum+="${fields[0]} || "
fi
## The tr is making everything upper case and then feeds
## directly into the while loop.
done < <(tr "a-z" "A-Z" < "$1")
## Get rid of the last || and add the closing ")"
checksum="${checksum% || })"
printf "OUT is: %s\n" "$checksum"
그런 다음 파일을 입력으로 사용하여 스크립트를 실행할 수 있습니다.
$ foo.sh file
OUT is: md5(INSTANCEID || STATUS || NOTES)