수년에 걸쳐 변수와 리터럴 앞에 문자가 오는 문자열 리터럴과 변수를 비교하는 경우가 여러 번 있었습니다.
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) {...}
$a
or의 내용은 $b
조건식이 구문 분석되고 확장되기 때문에 연산자로 간주 $a
될 $b
수 없습니다. 그러나 셸에서는 다음 위치에 있습니다.
[ "$a" = "$b" ]
쉘 확장 변수첫 번째².예를 들어, $a
포함 (
및 $b
포함하는 경우 )
모든 명령은 [
, [
및 (
매개변수를 확인합니다. 따라서 이는 (어휘적으로 같음) 또는 (비어 있지 않은 문자열)을 의미합니까?=
)
]
"(" = ")"
(
)
( -n = )
=
역사적 구현( test
1970년대 후반 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
대부분의 셸과 [
구현에는 이러한 문제나 변형에 문제가 있거나 있었습니다.현재 버전은 bash
4.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
-t
bash
bash
따라서 POSIX 호환 [
구현에서는 비교된 합계의 내용이 [ "$a" = "$b" ]
무엇이든 상관없이 동일하다는 것이 보장됩니다. 그렇지 않은 경우 다음과 같이 작성합니다.$a
$b
-o
[ "$a" = "$b" ] || [ "$a" = "$c" ]
[
즉, 매번 5개 미만의 매개변수를 사용하여 두 번 호출됩니다 .
그러나 모든 구현이 규정을 준수하는 데는 상당한 시간이 걸렸습니다 [
. bash
4.4까지는 호환되지 않습니다. (마지막 문제는 [ '(' ! "$var" ')' ]
실제 생활에서 실제로 아무도 사용하지 않는다는 것입니다.)
Solaris 10 및 이전 버전의 Solaris 쉘은 /bin/sh
POSIX 쉘이 아니지만 Bourne 쉘에는 여전히 문제가 있습니다 [ "$a" = "$b" ]
.
$ a='!' b='!'
$ [ "$a" = "$b" ]
test: argument expected
로 시작하는 연산자가 [ "x$a" = "x$b" ]
없기 때문에 를 사용하면 이 문제를 해결할 수 있습니다 . 또 다른 옵션은 다음을 사용하는 것입니다 .[
x
case
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 ]
-t
ksh93
[
² ksh는 [[...]]
이 문제를 해결하기 위해 자체 구문 분석 규칙(및 자체 문제)을 사용하여 조건식 연산자를 추가합니다(다른 일부 쉘에서도 발견되지만 약간의 차이점이 있음).
zsh
³제외분할+전역매개변수 확장 시에는 호출되지 않지만빈 제거여전히 true이거나 다른 쉘에서 분할+glob이 전역적으로 비활성화된 경우set -o noglob; IFS=
답변2
사람들은 종종 접두어가 빈 문자열의 문제라고 생각하지만 그것이 원인은 아닙니다. 문제는 매우 간단합니다. 변수의 확장은 test
s 연산자 중 하나일 수 있으며, 갑자기 이진 동등성 테스트를 다른 표현식으로 바꿀 수 있습니다.
대부분의 플랫폼에서 이 명령의 최근 구현은 표현식 파서의 예측 트랩을 피하여 파서가 충분한 토큰이 있는 한 이진 연산자의 첫 번째 피연산자를 피연산자가 아닌 다른 것으로 인식하지 못하도록 합니다.예물론 이진 연산자:
%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 %