シェル:文字列に指定された文字が含まれているかどうかを確認する[重複]

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ファイルから実行すると、シェルスクリプトがnotfoundエラーをスローするというタイトルの質問も参照してください。ただし、手動で入力した場合、コマンドは機能します

もう1つの問題は、の出力lsを1つの文字列に結合してから、スペース、タブ、改行で単語に分割し、生成された単語にファイル名のグロブを適用することです。次に、この結果をループします。

代わりに:

#!/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も何も起こらない。その場合には1が、存在する場合、ファイル名の文字を。printf文は何がある場合は実行されません取得し、「デフォルトの場合」にあるa文字列に。

テスト-e-L確認我々はループパターンが何も一致していないエッジケースをキャッチすることを作ることがあります。その場合、パターンは展開されないままになるため、展開されていないパターンと実際の既存のファイルを区別できることを確認するために、を使用してテストし-eます。-Lテストでは、ループパターンと同じ名前を持つシンボリックリンクの更なるエッジケースをキャッチすることですが、それはどこにも有効に指していません。bash以下のコードでは、nullglob代わりにシェルオプションを使用しているため、これは必要ありません(では使用できません/bin/sh)。

出力の*b*代わりにパターンの展開に対してループを実行すると、lsスペース、タブ、改行、およびファイル名グロブ文字を含むファイル名も処理できるという利点があります(ファイル名が単一の文字列に入れられることはありません。繰り返します)。

ではbash、シェル、あなたが同じことを行うことができ[[ ... ]]、あなたが自分で行うことを試みたように、パターンマッチングテスト:

#!/bin/bash

cd Folder || exit 1

shopt -s nullglob

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

参照:

  • シェルスクリプトが空白やその他の特殊文字で詰まるのはなぜですか?
  • 二重引用符はいつ必要ですか?
  • なぜ `ls`を解析しないのですか(そして代わりに何をすべきか)?
  • printfがechoよりも優れているのはなぜですか?
6 StéphaneChazelas Dec 22 2020 at 02:35

、inおよびその(/および-notを除く)glob演算子Folderを含むbおよび含まないファイル名を出力するには:azsh~

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

((一致するファイルがない場合にエラーを報告したい場合は、N)を削除しnullglobます。

In ksh93(その[[...]]演算子(標準sh構文の一部ではない)とその&および)および!(...)not)演算子を導入したシェル:

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

bash(のようなシェルzshもkshののコピーした[[...]]使用して、オペレータ)をextglobksh88ではグロブ演算子とサポートするためnullglobのzshのの効果を得るために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であるcase構文を使用するのが最適ですsh。ヘルパー関数を使用することもできます。

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