쉘 변수를 문자열 리터럴과 비교할 때 양쪽에 접두사를 추가하는 목적은 무엇입니까?

쉘 변수를 문자열 리터럴과 비교할 때 양쪽에 접두사를 추가하는 목적은 무엇입니까?

수년에 걸쳐 변수와 리터럴 앞에 문자가 오는 문자열 리터럴과 변수를 비교하는 경우가 여러 번 있었습니다.

if [ "x$A" = "xtrue" ]; then

$A인지 확인하기 위해 "true".

이는 쉘 호환성을 위해 또는 장기적인 버그, 직관적이지 않은 동작 등을 해결하기 위해 수행된 것이라고 생각합니다. 명백한 아이디어가 없습니다.

오늘 나는 그 이유를 알고 싶다고 생각했지만, 조사 결과 아무 것도 나오지 않았습니다. 아니면 내가 희귀한 사건에 자주 노출되어 만들어낸 것일 수도 있습니다.

이 접근 방식이 여전히 유용합니까, 어쩌면 최고일까요?

답변1

여기서 이해해야 할 중요한 점은 대부분의 셸에서 [이는 다른 일반 명령과 마찬가지로 셸에서 구문 분석되는 일반 명령일 뿐이라는 것입니다.

그런 다음 쉘은 list 인수를 사용하여 (또는 호출되는) 명령을 호출하고 [이는 조건식으로 해석됩니다.test[

그 당시에는 이것은 단지 문자열 목록이었고 어떤 문자열이 어떤 형태의 확장으로 생성되었는지에 대한 정보는 내장 [쉘(지금은 Bourne과 같은 쉘임)에서도 손실되었습니다.

유틸리티 [는 어떤 인수가 연산자인지 피연산자(연산자가 처리한 것)인지 구별하는 데 어려움을 겪었습니다. 구문이 본질적으로 모호하다는 것은 도움이 되지 않습니다. 예를 들어:

  • [ -t ][stdout이 터미널인지 테스트하는 데 사용되었습니다 (그리고 여전히 일부 쉘에 있습니다 ).
  • [ x ][ -n x ]: x비어 있지 않은 문자열 약어를 테스트합니다(따라서 위와 충돌을 확인할 수 있습니다).
  • 일부 쉘에서는 [, -a및 둘 다 단 항일 -o수 있습니다([ -a file ]접근 가능한 파일(이제 로 대체 됨 [ -e file ])[ -o option ]이 옵션이 활성화되어 있습니까?) 및 이항 연산자(그리고그리고또는). 마찬가지로 또는 ! -a x가 될 수 있습니다 .and(nonempty("!"), nonempty("x"))not(isaccessible("x"))
  • (, 더 많은 질문을 추가 )하세요 !.

C 또는 와 같은 일반 프로그래밍 언어에서는 다음 perl과 같습니다.

if ($a eq $b) {...}

$aor의 내용은 $b조건식이 구문 분석되고 확장되기 때문에 연산자로 간주 $a$b수 없습니다. 그러나 셸에서는 다음 위치에 있습니다.

[ "$a" = "$b" ]

쉘 확장 변수첫 번째².예를 들어, $a포함 ($b포함하는 경우 )모든 명령은 [, [(매개변수를 확인합니다. 따라서 이는 (어휘적으로 같음) 또는 (비어 있지 않은 문자열)을 의미합니까?=)]"(" = ")"()( -n = )=

역사적 구현( test1970년대 후반 Unix V7에 등장)은 단순히 인수를 처리하는 순서 때문에 명시적인 경우에도 실패하곤 했습니다.

PDP11 에뮬레이터의 Unix 버전 7은 다음과 같습니다.

$ ls -l /bin/[
-rwxr-xr-x 2 bin      2876 Jun  8  1979 /bin/[
$ [ ! = x ]
test: argument expected
$ [ "(" = x ]
test: argument expected

대부분의 셸과 [구현에는 이러한 문제나 변형에 문제가 있거나 있었습니다.현재 버전은 bash4.4입니다.

bash-4.4$ a='(' b=-o c=x
bash-4.4$ [ "$a" = "$b" -o "$a" = "$c" ]
bash: [: `)' expected, found =

POSIX.2(90년대 초반 출시) 디자인알고리즘이렇게 하면 가장 일반적인 사용 패턴(예: 아직 지정되지 않음)으로 전달될 때 [최대 4개의 인수( 및 제외 [)의 동작이 명시적이고 결정적으로 만들어집니다. , 및 를 더 이상 사용하지 않으며 피연산자 없이 제거합니다. 알고리즘은 실제로 2.0에서 구현되었습니다(또는 적어도 시도되었습니다).][ -f "$a" -o "$b" ]()-a-o-tbashbash

따라서 POSIX 호환 [구현에서는 비교된 합계의 내용이 [ "$a" = "$b" ]무엇이든 상관없이 동일하다는 것이 보장됩니다. 그렇지 않은 경우 다음과 같이 작성합니다.$a$b-o

[ "$a" = "$b" ] || [ "$a" = "$c" ]

[즉, 매번 5개 미만의 매개변수를 사용하여 두 번 호출됩니다 .

그러나 모든 구현이 규정을 준수하는 데는 상당한 시간이 걸렸습니다 [. bash4.4까지는 호환되지 않습니다. (마지막 문제는 [ '(' ! "$var" ')' ]실제 생활에서 실제로 아무도 사용하지 않는다는 것입니다.)

Solaris 10 및 이전 버전의 Solaris 쉘은 /bin/shPOSIX 쉘이 아니지만 Bourne 쉘에는 여전히 문제가 있습니다 [ "$a" = "$b" ].

$ a='!' b='!'
$ [ "$a" = "$b" ]
test: argument expected

로 시작하는 연산자가 [ "x$a" = "x$b" ]없기 때문에 를 사용하면 이 문제를 해결할 수 있습니다 . 또 다른 옵션은 다음을 사용하는 것입니다 .[xcase

case "$a" in
  "$b") echo same;;
     *) echo different;;
esac

$b( around 가 아닌 around 를 인용해야 함 $a).

어쨌든 null 값에 관한 것은 아니며, 그런 적도 없습니다. 사람들은 변수를 인용하는 것을 잊었을 때 null 값에 문제가 있지만 [그것은 문제가 되지 않습니다 [.

$ a= b='-o x'
[ $a = $b ]

기본값은 $IFS다음과 같습니다.

[ = -o x ]

=이것은 x비어 있지 않은 문자열인지 여부에 대한 테스트이지만 접두사가 아무리 많아도 도움이 되지 않습니다. [ x$a = x$b ]여전히 다음과 같습니다. [ x = x-o x ]이로 인해 오류가 발생하고 DoS 및 다른 값을 사용한 임의 명령 주입을 포함하여 상황이 더 악화될 수 있습니다. 예 bash:

bash-4.4$ a= b='x -o -v a[`uname>&2`]'
bash-4.4$ [ x$a = x$b ]
Linux

올바른 해결책은항상 인용됨:

[ "$a" = "$b" ]   # OK in POSIX compliant [ / shells
[ "x$a" = "x$b" ] # OK in all Bourne-like shells

expr비슷한(또는 더 나쁜) 문제가 있다는 점에 유의하세요 .

expr=두 피연산자가 십진 정수처럼 보일 때 동일한 정수인지, 그렇지 않을 때 같은 순서인지 테스트하는 데 사용되는 연산자도 있습니다 .

많은 구현에서 expr + = +, 또는 expr '(' = ')'또는 는 expr index = index동등성을 비교하지 않습니다. expr "x$a" = "x$b"이는 문자열 비교 문제를 해결하지만 접두사를 사용하면 x순서에 영향을 줄 수 있으며( x예: 로 시작하는 대조 요소가 있는 로캘에서) 음수 비교가 아닌 숫자 비교에는 사용할 수 없습니다 expr "0$a" = "0$b". expr " $a" = " $b" 일부 구현에서는 정수 비교에 작동하지만 다른 구현에서는 작동하지 않습니다( a=01 b=1일부는 true를 반환하고 일부는 false를 반환함).


1은 ksh93예외입니다. In 은 ksh93예약어 [in으로 간주될 수 있으며 이는 실제로 , 또는 또는 [ -t ]와 다릅니다 . 이는 이전 버전과의 호환성을 유지하면서도 중요한 경우 POSIX 규격을 유지하기 위한 것입니다. 리터럴인 경우에만 여기에서는 연산자로 간주되어 명령을 호출하고 있음을 감지합니다 .var=-t; [ "$var" ]""[ -t ]cmd='['; "$cmd" -t ]-tksh93[

² ksh는 [[...]]이 문제를 해결하기 위해 자체 구문 분석 규칙(및 자체 문제)을 사용하여 조건식 연산자를 추가합니다(다른 일부 쉘에서도 발견되지만 약간의 차이점이 있음).

zsh³제외분할+전역매개변수 확장 시에는 호출되지 않지만빈 제거여전히 true이거나 다른 쉘에서 분할+glob이 전역적으로 비활성화된 경우set -o noglob; IFS=

답변2

사람들은 종종 접두어가 빈 문자열의 문제라고 생각하지만 그것이 원인은 아닙니다. 문제는 매우 간단합니다. 변수의 확장은 tests 연산자 중 하나일 수 있으며, 갑자기 이진 동등성 테스트를 다른 표현식으로 바꿀 수 있습니다.

대부분의 플랫폼에서 이 명령의 최근 구현은 표현식 파서의 예측 트랩을 피하여 파서가 충분한 토큰이 있는 한 이진 연산자의 첫 번째 피연산자를 피연산자가 아닌 다른 것으로 인식하지 못하도록 합니다.물론 이진 연산자:

%a=-n
% /bin/test "$a" = -n ;echo $?
0
% /bin/test "$a" = ;echo$?
0
% /bin/test x"$a" = ;echo$?
테스트:=:예상 매개변수
2
%a='('
% /bin/test "$a" = "(" ; 에코 $?
0
% /bin/test "$a" = ;echo$?
테스트: 예상되는 닫는 괄호
2
%

관련 정보