$ 1이 1이 아니어도 'if [$ 1 =“1”]'분기가 항상 선택된 이유는 무엇입니까?


10

다음과 같이 'teleport.sh'라는 쉘 스크립트가 있습니다.

if [ $1="1" ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ $1="2" ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ $1="3" ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

내가 실행할 때 :

sh teleport.sh 2 testfile

이 디렉토리 testfile로 이동 ~/lab/Sun하여 해당 스크립트에 1 또는 '1'을 전달하지 않았기 때문에 혼란 스럽습니다.

무슨 일이야?


1
+1 실험실 , 태양 , , 지구텔레포트 . 그러나 항상 큰 따옴표 확장 ( $var,, $(cmd)심지어 `cmd`[ $(cmd)바람직한 것]) 을 사용해야합니다. 인용 할 필요 없지만 항상 그렇게하는 것은 아닙니다.
nyuszika7 시간

@ nyuszika7h, 큰 따옴표는 "$ var"및 "$ cmd"를 의미해서는 안됩니까? 위에서 언급 한 둥근 괄호의 장점은 무엇입니까?
Zen

$(cmd)명령 교체 (거의) 동일 `cmd`. 참조 mywiki.wooledge.org/CommandSubstitutionmywiki.wooledge.org/BashFAQ/082
nyuszika7h

답변:


19

공백을 사용하면 문제가 해결됩니다.

if [ "$1" = 1 ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ "$1" = 2 ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ "$1" = 3 ];
    then
    shift
        mv "$@" ~/lab/Earth
fi

이것은 더 깔끔하지만 :

#!/bin/bash

action=$1
shift
files=("$@")
case $action in  
  1) mv -- "${files[@]}" ~/lab/Sun     ;;
  2) mv -- "${files[@]}" ~/lab/Moon    ;;
  3) mv -- "${files[@]}" ~/lab/Earth   ;;
esac

3
예, 공백이 필요하지만 원래 $1="1"구문이 왜 작동 하는지 궁금 합니다 (실제 결과 생성). 어떻게 않습니다 [/ test내장 실제로 그 표현을 해석?
echristopherson

5
@echristopherson 공백이 없으면 test하나의 인수 ( "l- 값") 만 표시됩니다. 다른 주장 ( "연산자"와 "r- 값")이 없으면 테스트 할 것이 없기 때문에 test"좋아요, 당신은 나에게 무언가를 주었고 그것은 명백히 사실이어야합니다"라고 말합니다.
감독

2
$1="1"됩니다 $1에 의해 연결된=1
jdh8

사례를 사용할 때 조건이 충족되지 않을 때 실행할 작업을 설정하는 방법은 무엇입니까?
Zen

11

우선 분명한 것은 당신의 인수 사이에 공간을 제공해야한다이다 [, test또는 [[:

if [ "$1" = 1 ];

Bash에서는 [[ ]]단어 분할 및 경로 이름 확장과 같은 조건식에 불필요한 작업을 수행하지 않으므로 사용하는 것이 좋습니다. 큰 따옴표를 인용 할 필요도 없습니다. 더 읽기 쉬운 연산자를 ==사용할 수도 있습니다.

if [[ $1 == 1 ]];

추가 참고 : 두 번째 피연산자는 변수가 포함되어 있으면 그와 같은 인식 가능한 문자가 포함되어있는 경우는, 패턴 매칭 될 수있는 바와 같이, 인용은 필요 *, ?, [], ...의 경우가 로빙 확장되거나 패턴 매칭으로 활성화 등 shopt -s extglob, 다른 형태처럼 @(), !()등 패턴으로 인식됩니다. 패턴 일치를 참조하십시오 .

같은 연산자 <>내가 한 번 다른 결과가 발생하는 두 번째 인수를 인용하지 버그가 발생했던대로 여전히 필요할 수 있습니다.

첫 번째 피연산자는 적용되지 않습니다.

이 간단한 변형도 고려하십시오.

case "$1" in
1)
    mv -- "${@:2}" ~/lab/Sun
    ;;
2)
    mv -- "${@:2}" ~/lab/Moon
    ;;
3)
    mv -- "${@:2}" ~/lab/Earth
    ;;
esac

또는 응축 :

case "$1" in
1) mv -- "${@:2}" ~/lab/Sun ;;
2) mv -- "${@:2}" ~/lab/Moon ;;
3) mv -- "${@:2}" ~/lab/Earth ;;
esac

"${@:2}"2오프셋 인 부분 문자열 확장 또는 배열 멤버 확장의 형식입니다 . 이렇게하면 두 번째 값에서 확장이 시작됩니다. 이것으로 우리는 사용할 필요가 없습니다 shift.

추가 --하면 mv대시 ( -)로 시작하는 파일 이름이 잘못된 옵션으로 인식 되지 않습니다.


당신은 각 경우에 휴식해서는 안됩니까?
Archemar

2
@ Archemar : 아니요, 다른 언어와는 달리 넘어지지 않습니다.
Mat

2
@Mat ;&대신 ;;(ksh, bash, zsh 전용) 대신 사용하면 넘어 질 수 있습니다 . 그러나 break여전히 넘어지는 것을 막지 못하고 break루프에서 벗어나는 것입니다.
Stéphane Chazelas

그것은 주장에 대한 주장이 if아니라 [명령에 대한 것입니다.
Stéphane Chazelas

1
수정 : 큰 따옴표는 왼쪽에만 필요하지 않습니다! [[ $foo == $bar ]]패턴 일치를 수행하지만 [[ $foo == "$bar" ]]그렇지 않습니다.
nyuszika7h

7

이런 일이 왜의 질문에 대답하려면,이 행동 [일명 test되어 POSIX에 설명 :

다음 목록에서 $ 1, $ 2, $ 3 및 $ 4는 테스트를 위해 제시된 인수를 나타냅니다.

[...]

1 개의 주장 :

$ 1이 null이 아닌 경우 true (0)를 종료합니다. 그렇지 않으면 false를 종료하십시오.

2=1null이 아닌 1 인수를 전달 하므로 test성공하면 종료됩니다.

다른 게시물 (및 AS shellcheck ) 당신이 어떤지를 비교하기를 원한다면, 대신 3 개 인수를 전달하는 것, 지적 2, =하고 1.


3
어떤 의미에서 이것은 (OP가 원했던 것을 수행하는 하나 이상의 레시피를 제공하는 대신) 질문에 대답 한 유일한 대답이며, 쉘은 그것이 일 을하는지 알 수 있는지 충분히 신비 할 수 있습니다. .
dmckee ---

1

휴대용 이지만 깔끔한 대안 을 추천하고 싶습니다 . 배쉬는 보편적 이지 않다 (그리고 만약 당신이 보편적이 필요하지 않다면 왜 쉘 스크립트를 작성 하는가?)

#! /bin/sh
action="$1"
shift
case "$action" in
    1) dest=Sun   ;;
    2) dest=Moon  ;;
    3) dest=Earth ;;
    *) echo "Unrecognized action code '$action' (must be 1, 2, or 3)" >&2; exit 1 ;;
esac
mv -- "$@" ~/lab/"$dest"

(의 pedants에 참고 : 그래, 난 주위에 따옴표를 알고 $actioncase "$action" in불필요한 라인,하지만 난 그게 미래의 독자가 기억 할 필요가 없습니다, 어쨌든 넣어하는 것이 가장 좋습니다 생각합니다.)


1
"Bash는 보편적이지 않다 (그리고 보편적이 필요하지 않다면 왜 쉘 스크립트를 작성 하는가?") — 이것은 bash 스크립팅에 유스 케이스가 없음을 의미하는 것으로 보인다.
Ruslan

@Ruslan 네, 제 생각은 제 생각입니다. /bin/sh필요한 경우 이식 가능한 스크립트를 작성하십시오 . 그렇지 않으면, 쉘보다 끔찍한 스크립팅 언어로 작성하십시오. 기본 펄 인터프리터는 사실에 가능성이 기존의 독점적 인 환경에 존재하고 배쉬보다는 임베디드 환경 컷 다운합니다.
zwol
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.