Bash에서 문자열이 값으로 시작하는지 어떻게 확인할 수 있습니까?


745

문자열이 "node"(예 : "node001")로 시작하는지 확인하고 싶습니다. 같은 것

if [ $HOST == user* ]
  then
  echo yes
fi

어떻게 올바르게 할 수 있습니까?


HOST가 "user1"인지 "node"로 시작하는지 확인하기 위해 표현식을 결합해야합니다.

if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

> > > -bash: [: too many arguments

어떻게 올바르게 할 수 있습니까?


7
식을 결합하려는 유혹을받지 마십시오. 더 나은 오류 메시지를 제공하고 스크립트를 더 쉽게 디버깅 할 수 있지만 두 개의 개별 조건이있는 것이 더 나빠 보일 수 있습니다. 또한 bash 기능을 피할 것입니다. 스위치는 갈 길입니다.
hendry

답변:


1073

Advanced Bash Scripting Guide 의이 코드 는 다음과 같습니다.

# The == comparison operator behaves differently within a double-brackets
# test than within single brackets.

[[ $a == z* ]]   # True if $a starts with a "z" (wildcard matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

그래서 당신은 거의 맞았습니다. 단일 괄호가 아닌 이중 괄호가 필요했습니다 .


두 번째 질문과 관련하여 다음과 같이 작성할 수 있습니다.

HOST=user1
if  [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes1
fi

HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes2
fi

어떤 에코

yes1
yes2

Bash의 if구문은 익숙하지 않습니다 (IMO).


12
정규식의 경우 [[$ a = ~ ^ z. *]]을 의미합니까?
JStrahl

3
[[ $a == z* ]]와 사이에 기능적인 차이가 [[ $a == "z*" ]]있습니까? 다시 말해, 다르게 작동합니까? "$ a는 z *와 같습니다"라고 말할 때 구체적으로 무엇을 의미합니까?
Niels Bom 2016 년

5
명령문 구분 기호 ";"가 필요하지 않습니다. "그럼"을 그 줄에 넣으면
Yaza

6
완전성을 위해 : 문자열 ENDS가 ...인지 확인하려면 :[[ $a == *com ]]
lepe

4
ABS는 불행히도 참고 문헌 선택입니다. 구식의 W3Schools는 오래된 내용과 나쁜 사례로 가득합니다. freenode #bash 채널은 2008 년부터는 사용을 권장하지 않습니다 . BashFAQ # 31 에서 다시 지정할 수 있습니까? (또한 Bash-Hackers의 위키를 제안했지만 지금은 약간 다운되었습니다.)
Charles Duffy

207

최신 버전의 Bash (v3 +)를 사용하는 경우 Bash 정규식 비교 연산자를 제안합니다 ( =~예 :

if [[ "$HOST" =~ ^user.* ]]; then
    echo "yes"
fi

this or that정규식에서 일치 시키려면 |예를 들어

if [[ "$HOST" =~ ^user.*|^host1 ]]; then
    echo "yes"
fi

참고-이것은 '적절한'정규식 구문입니다.

  • user*수단 use과의 제로 또는-이상의 발생은 r, 그래서 useuserrrr일치합니다.
  • user.*수단 user및 모든 문자의 제로 또는-이상의 발생, 그래서 user1, userX일치합니다.
  • ^user.*user.*$ HOST의 시작 부분에서 패턴과 일치 함을 의미합니다 .

정규식 구문에 익숙하지 않은 경우이 리소스를 참조 하십시오 .


고마워요, 브 래스터! if cluase에서 표현식을 결합하는 방법에 대한 새로운 질문을 원래 게시물에 추가했습니다.
Tim

2
허용 된 답변이 정규 표현식의 구문에 대해 아무 말도하지 않는 것이 유감입니다.
CarlosRos

20
참고로 Bash =~연산자는 오른쪽이 UNQUOTED 일 때만 정규 표현식 일치를 수행합니다. 오른쪽을 인용하면 "패턴의 일부를 인용하여 문자열로 일치시킬 수 있습니다." (1.) 항상 정규 표현식을 인용 부호가없는 오른쪽에 배치하고 (2.) 정규 표현식을 변수에 저장하는 경우 매개 변수 확장을 수행 할 때 오른쪽을 인용하지 않아야합니다.
Trevor Boyd Smith

145

sh스크립팅의 주요 포인트 중 하나는 이식성 ( 프로그램을 바꾸는 것이 아니라 연결 하는 것 외에)이기 때문에 항상 Bash 확장을 사용하는 대신 POSIX를 고수하려고 노력합니다 .

에서 sh"is-prefix"상태를 확인하는 쉬운 방법이 있습니다.

case $HOST in node*)
    # Your code here
esac

어리 석고 잔인한 sh가 몇 살인지 (그리고 Bash는 치료법이 아닙니다 : 더 복잡하고 일관성이 떨어지고 이식성이 떨어집니다) 매우 훌륭한 기능적 측면을 지적하고 싶습니다 : 같은 구문 요소 case는 내장되어 있지만 결과 구성은 다른 작업과 다르지 않습니다. 그것들은 같은 방식으로 구성 될 수 있습니다 :

if case $HOST in node*) true;; *) false;; esac; then
    # Your code here
fi

또는 더 짧은

if case $HOST in node*) ;; *) false;; esac; then
    # Your code here
fi

또는 심지어 더 짧습니다 ( !언어 요소로 표현하기만하지만 이것은 나쁜 스타일입니다)

if ! case $HOST in node*) false;; esac; then
    # Your code here
fi

명시 적 인 것을 좋아한다면 자신의 언어 요소를 작성하십시오.

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }

실제로 이것이 좋지 않습니까?

if beginswith node "$HOST"; then
    # Your code here
fi

그리고 sh기본적으로 작업과 문자열 목록 (및 작업이 구성된 내부 프로세스) 만 있기 때문에 이제는 가벼운 기능적 프로그래밍을 수행 할 수 있습니다.

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; }

all() {
    test=$1; shift
    for i in "$@"; do
        $test "$i" || return
    done
}

all "beginswith x" x xy xyz ; checkresult  # Prints TRUE
all "beginswith x" x xy abc ; checkresult  # Prints FALSE

이것은 우아하다. 나는 sh진지한 것을 사용하여 옹호하지는 않습니다 . 실제 요구 사항에서 너무 빨리 깨집니다 (람다가 없으므로 문자열을 사용해야합니다. 그러나 문자열로 중첩 함수 호출은 불가능하며 파이프는 불가능합니다)


12
+1 이식성이 뛰어날뿐만 아니라 읽기 쉽고 관용적이며 우아합니다 (쉘 스크립트 용). 또한 자연스럽게 여러 패턴으로 확장됩니다. case $HOST in user01 | node* ) ...
tripleee

이 유형의 코드 형식에 대한 이름이 있습니까? if case $HOST in node*) true;; *) false;; esac; then 나는 여기저기서 그것을 보았는데, 내 눈에는 다소 뭉친 것처럼 보인다.
Niels Bom 2016 년

@NielsBom 나는 정확히 포맷하여 무슨 뜻인지 모르겠지만, 나의 점은 쉘 코드가 매우 많이 있었다 작성 가능 . Becaues case명령은 명령, 그들은 안에 갈 수 있습니다 if ... then.
Jo So

나는 그것이 왜 구성 가능한지조차 알지 못한다. 나는 그것을 위해 충분한 쉘 스크립트를 이해하지 못한다. 이전에 본 쉘 스크립트와 비슷하지 않지만 sh 스크립트보다 bash 스크립트를 보는 데 익숙 할 수 있습니다.
Niels Bom

참고 : beginswith() { case "$2" in "$1"*) true;; *) false;; esac; }그렇지 않으면 $1리터럴이 *있거나 ?잘못된 답변을 줄 수 있습니다.
LLFourn

80

확인하려는 문자열 부분 만 선택할 수 있습니다.

if [ "${HOST:0:4}" = user ]

후속 질문의 경우 OR을 사용할 수 있습니다 .

if [[ "$HOST" == user1 || "$HOST" == node* ]]

8
당신은 큰 따옴표로${HOST:0:4}
Jo So

@Jo So : 이유가 무엇인가요?
피터 Mortensen

@PeterMortensen, 시도HOST='a b'; if [ ${HOST:0:4} = user ] ; then echo YES ; fi
Jo So

60

이미 게시 된 다른 방법을 선호하지만 일부 사람들은 다음을 사용하기를 좋아합니다.

case "$HOST" in 
    user1|node*) 
            echo "yes";;
        *)
            echo "no";;
esac

편집하다:

위의 사례에 대체 제품을 추가했습니다.

편집 한 버전에서 대괄호가 너무 많습니다. 다음과 같아야합니다.

if [[ $HOST == user1 || $HOST == node* ]];

고마워, 데니스! if cluase에서 표현식을 결합하는 방법에 대한 새로운 질문을 원래 게시물에 추가했습니다.
Tim

11
"어떤 사람들은 ...":이 버전과 셸에서보다 이식성이 뛰어납니다.
carlosayam

case 문을 사용하면 단어 분할이 발생하지 않으므로 변수 주위에 따옴표를 남겨 둘 수 있습니다. 나는 그것이 무의미하고 일관성이 없다는 것을 알고 있지만, 현지에서보다 시각적으로 매력적으로 보이기 위해 따옴표를 남기고 싶습니다.
Jo So

그리고 제 경우에는) : "/ *")가 작동하지 않고 / *) 전에 따옴표를 남겨 두어야했습니다. (나는 절대 경로로 시작하는 문자열을 찾고 있습니다)
Josiah Yoder

36

여기서 대부분의 답변이 꽤 정확하지만 많은 답변에 불필요한 Bashism이 포함되어 있습니다. POSIX 매개 변수 확장 은 필요한 모든 것을 제공합니다.

[ "${host#user}" != "${host}" ]

[ "${host#node}" != "${host}" ]

${var#expr}일치하는 가장 작은 접두사를 expr제거 ${var}하고 반환합니다. 따라서 ( )로 시작 ${host}하지 않으면 ( )는와 같습니다 .usernode${host#user}${host#node}${host}

exprfnmatch()와일드 카드를 허용 하므로 ${host#node??}친구도 작업 할 수 있습니다.


2
나는 bashism [[ $host == user* ]]이 훨씬 더 읽기 쉽기 때문에 필요할 것이라고 주장했다 [ "${host#user}" != "${host}" ]. 따라서 스크립트가 실행되는 환경 (최신 버전의 대상)을 제어 할 수 bash있으므로 전자가 바람직합니다.
x-yuri

2
@ x-yuri 솔직히, 나는 이것을 단순히 has_prefix()함수 로 묶고 다시는 보지 않을 것입니다.
dhke

30

#Bash에 의미가 있기 때문에 다음 솔루션에 도달했습니다.

또한 공백 등을 극복하기 위해 문자열을 ""로 묶는 것이 좋습니다.

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "Skip comment line"
fi

이것이 바로 내가 필요한 것입니다. 감사!
Ionică Bizău

덕분에으로 시작하는 문자열을 일치시키는 방법이 궁금합니다. blah:이것이 답입니다!
Anentropic

12

Mark Rushakoff의 최고 순위 답변에 구문 세부 정보를 조금 더 추가합니다.

표현식

$HOST == node*

다음과 같이 쓸 수도 있습니다

$HOST == "node"*

효과는 같습니다. 와일드 카드가 인용 된 텍스트 밖에 있는지 확인하십시오. 와일드 카드가 인용 부호 안에 있으면 문자 그대로 해석됩니다 (즉, 와일드 카드가 아님).


8

@OP, 두 가지 질문 모두 case / esac를 사용할 수 있습니다.

string="node001"
case "$string" in
  node*) echo "found";;
  * ) echo "no node";;
esac

두 번째 질문

case "$HOST" in
 node*) echo "ok";;
 user) echo "ok";;
esac

case "$HOST" in
 node*|user) echo "ok";;
esac

또는 배쉬 4.0

case "$HOST" in
 user) ;&
 node*) echo "ok";;
esac

참고 ;&강타> = 4에서만 사용할 수 있습니다
추가 공지가있을 때까지 일시 중지.

6
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

때문에 모든 작동하지 않습니다 [, [[그리고 test같은 재귀 문법을 인식하고 있습니다. Bash 매뉴얼 페이지의 조건 표현 섹션을 참조 하십시오.

옆으로, SUSv3는 말합니다

KornShell 파생 조건부 명령 (더블 괄호 [[]] )은 초기 제안에서 쉘 명령 언어 설명에서 제거되었습니다. 실제 문제가 테스트 명령 ( [ )을 잘못 사용하고 있으며, 쉘에 넣는 것이 문제를 해결하는 잘못된 방법 이라는 반대 의견이 제기 되었습니다. 대신 적절한 문서와 새로운 쉘 예약어 ( ! )로 충분합니다.

다중 테스트 조작 이 필요한 테스트 는 error-prone -o flag of test를 사용하지 않고 테스트 명령 및 쉘 논리 의 개별 호출을 사용하여 쉘 레벨에서 수행 할 수 있습니다 .

이런 식으로 작성해야하지만 테스트 는 지원하지 않습니다.

if [ $HOST == user1 -o $HOST == node* ];
then
echo yes
fi

테스트 는 문자열 동등성을 위해 = 를 사용 하며, 더 중요한 것은 패턴 일치를 지원하지 않습니다.

case/ esac패턴 매칭을 잘 지원합니다 :

case $HOST in
user1|node*) echo yes ;;
esac

Bash에 의존하지 않으며 구문이 이식 가능하다는 추가 이점이 있습니다. 로부터 단일 유닉스 규격 , 쉘 명령 언어 :

case word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac

1
[testBash 내장 및 외부 프로그램입니다. 시도하십시오 type -a [.
추후 공지가있을 때까지 일시 중지되었습니다.

@compound 또는 "단지 누군가"문제를 설명해 주셔서 감사합니다. 건배! PS 참고 (OP와 관련이 없음) : if [ -z $aa -or -z $bb ]; ... " bash : [:-또는 : 이항 연산자 예상 " "; 그러나 if [ -z "$aa" -o -z "$bb" ] ; ...통과합니다.
sdaau

2

grep

성능을 잊어 버린 POSIX이며 case솔루션 보다 멋지게 보입니다 .

mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
  echo matches
fi

설명:


2

@markrushakoff의 답변을 조정하여 호출 가능한 함수로 만들었습니다.

function yesNo {
  # Prompts user with $1, returns true if response starts with y or Y or is empty string
  read -e -p "
$1 [Y/n] " YN

  [[ "$YN" == y* || "$YN" == Y* || "$YN" == "" ]]
}

다음과 같이 사용하십시오.

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] Y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] yes
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n]
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] ddddd
false

지정된 기본값을 제공하는보다 복잡한 버전은 다음과 같습니다.

function toLowerCase {
  echo "$1" | tr '[:upper:]' '[:lower:]'
}

function yesNo {
  # $1: user prompt
  # $2: default value (assumed to be Y if not specified)
  # Prompts user with $1, using default value of $2, returns true if response starts with y or Y or is empty string

  local DEFAULT=yes
  if [ "$2" ]; then local DEFAULT="$( toLowerCase "$2" )"; fi
  if [[ "$DEFAULT" == y* ]]; then
    local PROMPT="[Y/n]"
  else
    local PROMPT="[y/N]"
  fi
  read -e -p "
$1 $PROMPT " YN

  YN="$( toLowerCase "$YN" )"
  { [ "$YN" == "" ] && [[ "$PROMPT" = *Y* ]]; } || [[ "$YN" = y* ]]
}

다음과 같이 사용하십시오.

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N]
false

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N] y
true

$ if yesNo "asfd" y; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

-5

당신이 할 수있는 또 다른 일은 cat 이 에코하고 파이프하는 것을 밖으로하는 것입니다.inline cut -c 1-1

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.