Shell : 문자열에 지정된 문자가 포함되어 있는지 확인 [중복]

Dec 22 2020
#!/bin/sh

TMP=$(cd Folder && ls) for name in $TMP; do

  if [[ "${name}" != *"a"* -a ${name} == *"b"* ]] ;then
   echo $name
  fi

done

나는 'a'가 없지만 대신 'b'가있는 이름을 출력하려고했습니다. 이것은 내가 얻은 오류입니다.

./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found
./aufg7.sh: 6: [[: not found

내가 여기서 뭘 잘못하고 있니?

답변

5 Kusalananda Dec 22 2020 at 02:19

주요 문제는 이러한 유형의 테스트를 지원하지 않는 [[ ... ]]스크립트에서 패턴 일치를 사용하고 있다는 것 /bin/sh입니다. sh 파일에서 실행할 때 쉘 스크립트가 찾을 수 없음 오류를 발생 시키는 질문을 참조하십시오 . 그러나 수동으로 입력하면 명령이 작동합니다.

다른 문제는의 출력 ls을 공백, 탭 및 줄 바꿈의 단어로 분할하기 전에 단일 문자열로 결합 하고 생성 된 단어에 파일 이름 globbing을 적용한다는 것입니다. 그런 다음이 결과를 반복합니다.

대신 :

#!/bin/sh

cd Folder || exit 1

for name in *b*; do
    [ -e "$name" ] || [ -L "$name" ] || continue
    case $name in (*a*) ;; (*) printf '%s\n' "$name"; esac
done

Folder문자가 포함 b된 디렉토리의 모든 파일을 반복 한 다음 a. a캐릭터 테스트 는 case ... esac. *a*패턴은 일치하는 a경우 아무 변화가없는 한, 존재하는 경우 파일 이름에 문자를. printf문이 더있을 경우 실행되지됩니다 "기본 경우"에 a문자열에.

와 시험 -e과는 -L확실히 우리가 루프 패턴이 아무것도 일치하지 않는 가장자리 케이스를 잡는 것을 위해 존재한다. 이 경우 패턴은 확장되지 않은 상태로 유지되므로 확장되지 않은 패턴과 실제 기존 파일을 구분할 수 있는지 확인하기 위해 -e. -L테스트는 루프 패턴과 동일한 이름을 가진 심볼 링크의 더 엣지 케이스를 잡으려고하지만, 그 어디 유효를 가리 키지 않습니다. bash가 사용하기 때문에 아래 코드는이를 필요로하지 않는 nullglob대신 (사용할 수있는 쉘 옵션 /bin/sh).

출력이 *b*아닌 패턴 확장에 대해 루프를 실행하면 ls공백, 탭, 줄 바꿈 및 파일 이름 globbing 문자를 포함하는 파일 이름도 처리 할 수 ​​있다는 이점이 있습니다 (파일 이름은 절대로 단일 문자열에 넣지 않습니다. 반복).

에서 bash쉘, 당신은과 동일한 기능을 수행 할 수있는 [[ ... ]]당신이 자신을하려고 노력처럼, 패턴 매칭 테스트 :

#!/bin/bash

cd Folder || exit 1

shopt -s nullglob

for name in *b*; do
    [[ $name != *a* ]] && printf '%s\n' "$name"
done

또한보십시오:

  • 셸 스크립트가 공백이나 기타 특수 문자로 인해 질식하는 이유는 무엇입니까?
  • 큰 따옴표는 언제 필요합니까?
  • 'ls'를 구문 분석하지 않는 이유는 무엇입니까 (대신 수행 할 작업)?
  • printf가 에코보다 나은 이유는 무엇입니까?
6 StéphaneChazelas Dec 22 2020 at 02:35

Folder포함 b및 포함 하지 않는 파일 이름을 인쇄하려면 ain zsh및 해당 ~( / -not 제외 ) glob 연산자 :

#! /bin/zsh -
set -o extendedglob
print -rC1 -- Folder/(*b*~*a*)(N:t)

( 일치하는 파일이없는 곳에 오류를보고 하려면 N(for nullglob,)을 제거하십시오 .

에서는 ksh93그 도입 (쉘 [[...]]표준의 일부가되는 (오퍼레이터 sh구) 및 &( ) 및 !(...)( 하지 ) 연산자 :

#! /bin/ksh93 -
(
  CDPATH= cd Folder &&
    set -- ~(N)@(*b*&!(*a*)) &&
    (($# > 0)) && printf '%s\n' "$@"
)

bash(같은 쉘 zsh도 KSH의 복사했습니다 [[...]]사용하여 연산자), extglobksh88 글로브 사업자를 지원하고 nullglobzsh을의의 효과를 얻을 N글로브 규정 또는 ksh93~(N)글로브 연산자와 일부 이중 부정 |( 또는 달성) :

#! /bin/bash -
(
  shopt -s extglob nullglob
  cd ./Folder &&
    set -- !(!(*b*)|*a*) &&
    (($# > 0)) && printf '%s\n' "$@"
)

표준 sh구문에서 :

#! /bin/sh -
(
  CDPATH= cd Folder || exit
  set -- [*]b[*] *b*
  [ "$1/$2" = '[*]b[*]/*b*' ] && exit # detect the case where the pattern
                                      # expands to itself because there's
                                      # no match as there's no nullglob
                                      # equivalent in sh
  shift
  for i do
    case $i in (*a*) ;; (*) set -- "$@" "$i" esac shift done [ "$#" -gt 0 ] && printf '%s\n' "$@"
)

사용하는 솔루션 cd은 읽기 권한은 Folder있지만 검색 권한 은없는 경우 작동 하지 않습니다.

보다 일반적으로 문자열에 다른 문자열이 포함되어 있는지 확인하는 방법에 대한 일반적인 질문에 대답하려면에서 패턴 일치를 수행하는 방법을 사용하는 것이 sh가장 좋습니다 . 도우미 기능을 사용할 수도 있습니다.casesh

contains()
  case "$1" in
    (*"$2"*) true;;
    (*) false;;
  esac

다음과 같이 사용하려면 :

if contains foobar o; then
  echo foobar contains o
fi
if ! contains foobar z; then
  echo foobar does not contain o
fi
if
  contains "$file" b &&
    ! contains "$file" a then printf '%s\n' "$file contains b and not a "
fi