나는 쉘 스크립트 이식성을 선호합니다.
하지만 지금은 너무 멀리 갔는지 모르겠습니다.
이 예에는 confirmation
첫 번째 인수를 질문이 포함된 문자열로 받아들이고 다른 모든 인수는 유효한 답변이 가능한 함수가 있습니다.
confirmation ()
{
question=$1; shift; correct_answers=$*
printf '%b' "$question\\nPlease answer [ $( printf '%s' "$correct_answers" | tr ' ' / ) ] to confirm (Not <Enter>): "; read -r user_answer
# this part iterates through the list of correct answers
# and compares each as the whole word (actually as the whole line) with the user answer
for single_correct_answer in $correct_answers; do
printf '%s' "$single_correct_answer" | grep -i -x "$user_answer" > /dev/null 2>&1 && return 0
done
return 1
}
confirmation 'Do you hate me?' yes yeah kinda
보시다시피 핵심 부분이 Exploiting 되어서 grep
살펴봤습니다.매뉴얼 페이지, 그리고 이것을 발견했습니다:
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. Also see the -s or --no-messages option. (-q is specified by POSIX .)
-s, --no-messages
Suppress error messages about nonexistent or unreadable files. Portability note: unlike GNU grep, 7th Edition Unix grep did not conform to POSIX , because it lacked -q and its -s option behaved like GNU grep's -q option. USG -style grep also lacked -q but its -s option behaved like GNU grep. Portable shell scripts should avoid both -q and -s and should redirect standard and error output to /dev/null instead. (-s is specified by POSIX .)
이 부분을 강조해 보겠습니다.
이식성 참고 사항: GNU와 달리
grep
Unix의 일곱 번째 버전grep
은POSIX, 부족-q
하고 옵션이 GNU 의 옵션-s
과 유사하게 작동하기 때문입니다 . USG 에도 특징이 부족 하지만 옵션은 GNU와 유사하게 작동합니다 . 이식 가능한 쉘 스크립트는 및 사용을 피해야 하며 표준 출력 및 오류 출력을 .grep
-q
grep
-q
-s
grep
-q
-s
/dev/null
너무 멀리 갔습니까? 아니면 /dev/null
유일한 휴대용 방법으로 리디렉션하고 있습니까?
나아니요40년 전 운영체제 버전의 이식성에 집중하세요!
답변1
유닉스 V7은 1970년대 후반에 출시됐다. 이것은 Bourne 쉘을 도입한 버전입니다.
그러나 당시 추가된 기능 지원은 없었으며 read
no -r
및 printf
명령도 없었습니다. 대소 문자를 구분하지 않는 것은 Bourne이 grep
아닙니다 .grep -y
$(...)
그 이후로 유닉스 계열 시스템은 많이 발전하고 다양해졌습니다. 1990년대 초 POSIX는 어느 정도 통일성을 회복하려고 노력했습니다.
그러나 기본 구현에서 POSIX를 준수하지 않고 POSIX 호환 구현만 별도 유틸리티로 추가하는 일부 시스템이 여전히 있습니다.
예를 들어 Solaris는 POSIX보다 /bin/grep
V7에 더 가깝습니다 . Solaris의 POSIX를 사용할 수 있습니다 (Solaris의 최소 배포에서는 사용할 수 없음).grep
grep
grep
/usr/xpg4/bin/grep
/bin/grep
-q
솔라리스에는 , -E
, 가 없습니다 -F
.
/usr/xpg4/bin
Solaris에서 작업할 때 일반적으로 앞에 놓고 대신 $PATH
사용 하려고 합니다 (Solaris 11에서는 변경되었지만 /bin/sh는 이제 ksh93이므로 버그 기반 ksh88보다 평균적으로 POSIX 호환이 더 높습니다 )./usr/xpg4/bin/sh
/bin/sh
/usr/xpg4/bin/sh
코드에 대한 기타 이식성 참고 사항:
correct_answers=$*
또는 의 동작은read -r
의 현재 값에 따라 달라집니다$IFS
. ( 위치 매개변수는 입력을 단어로 분할 하는 데 사용되는 첫$*
번째 문자와 연결됩니다 .) 따라서 원하는 값으로 설정해야 합니다.$IFS
read
$IFS
위치 인수를 스칼라 문자열로 연결하면 구분 기호가 포함된 경우 NL을 구분 기호로 사용하지 않으면 제대로 작동하지 않습니다.
read
어쨌든 한 줄만 읽혀 답변이 포함되지 않기 때문입니다. 개행 문자.시퀀스를 확장
%b
하려는 의도가 아닐 수 있는 내용을 -formatted 매개변수에 포함시켰습니다 .\x
for single_correct_answer in $correct_answers
분할+글로브를 사용하세요. 나는 여기서 전역 부분을 원하지 않는다고 생각하며 나중에 다시 분할하기 위해 다시 연결하는 것은 약간 어리석은 일입니다(신뢰할 수 없음).grep -i -x "$user_answer"
대소문자를 구분하지 않고 비교하는 대신 정규식 패턴 일치를 수행합니다. 또한, 답변이 로 시작하는 경우에는 옵션이므로-
작동하지 않습니다 .grep
printf '%s' text
텍스트가 아닌 출력(줄 바꿈 누락)을 생성하므로 동작이grep
지정되지 않습니다(그리고 실제로 이식할 수 없습니다).
따라서 이러한 점을 염두에 두고 코드를 다음과 같이 변경합니다.
confirmation() {
question=$1; shift
printf '%s\nPlease answer [' "$question"
sep=
for answer do
printf %s "$sep$answer"
sep=/
done
printf '] to confirm (Not <Enter>): '
IFS= read -r user_answer
# this part iterates through the list of correct answers
# and compares each as the whole word (actually as the whole line)
# with the user answer
for single_correct_answer do
printf '%s\n' "$single_correct_answer" |
grep -ixFqe "$user_answer" && return
done
return 1
}