シェル:文字列に指定された文字が含まれているかどうかを確認する[重複]
#!/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
私はここで何が間違っているのですか?
回答
あなたの主な問題は、これらのタイプのテストをサポート[[ ... ]]
しないによって実行されるスクリプトでパターンマッチを使用していることです/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よりも優れているのはなぜですか?
、inおよびその(/および-notを除く)glob演算子Folder
を含むb
および含まないファイル名を出力するには:a
zsh
~
#! /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ののコピーした[[...]]
使用して、オペレータ)をextglob
ksh88ではグロブ演算子とサポートするため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