web-dev-qa-db-ja.com

シェルスクリプトにワイルドカード付きのファイルが存在するか確認してください

ファイルが存在するかどうかをチェックしようとしていますが、ワイルドカードを使用しています。これが私の例です:

if [ -f "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi

二重引用符なしで試してみました。

248
Danny

最も簡単なのはlsの戻り値に頼ることです(ファイルが存在しないときは0以外を返します):

if ls /path/to/your/files* 1> /dev/null 2>&1; then
    echo "files do exist"
else
    echo "files do not exist"
fi

私はlsの出力を完全に沈黙させるためにリダイレクトしました。


編集:この答えは少し注目を集めているので(そしてコメントとして非常に有用な批評的なコメント)、これはglob展開にも頼るがlsの使用を避けた最適化です。

for f in /path/to/your/files*; do

    ## Check if the glob gets expanded to existing files.
    ## If not, f here will be exactly the pattern above
    ## and the exists test will evaluate to false.
    [ -e "$f" ] && echo "files do exist" || echo "files do not exist"

    ## This is all we needed to know, so we can break after the first iteration
    break
done

これは@ grok12の答えと非常によく似ていますが、リスト全体を不必要に繰り返すことを避けます。

366
Costi Ciudatu

シェルにnullglobオプションがあり、それがオンになっている場合、どのファイルとも一致しないワイルドカードパターンは、コマンドラインから完全に削除されます。これにより、lsはパス名の引数を参照せず、現在のディレクトリの内容をリストして成功しますが、これは誤りです。 GNUstatは、引数を指定しない場合、または存在しないファイルを指定する引数を指定した場合は常に失敗します。また、&>リダイレクション演算子は バシズムです

if stat --printf='' /path/to/your/files* 2>/dev/null
then
    echo found
else
    echo not found
fi

GNUfindの方がより良いです。これはワイルドカード検索を内部で処理し、一致するファイルが見つかるとすぐに終了し、巨大なリストの処理に時間を浪費するのではなくなります。それらのうちの1つはシェルによって拡張されました。これにより、シェルがコマンドラインバッファをオーバーフローさせる危険性も回避されます。

if test -n "$(find /dir/to/search -maxdepth 1 -name 'files*' -print -quit)"
then
    echo found
else
    echo not found
fi

findの非GNUバージョンでは、findのみを検索するためにここで使用されている - maxdepthオプションがない場合があります。そこに根ざしたディレクトリツリー全体ではなく、/ dir/to/search

54
flabdablet

これが私の答えです -

files=(xorg-x11-fonts*)

if [ -e "${files[0]}" ];
then
    printf "BLAH"
fi
36
Pankaj Parashar
for i in xorg-x11-fonts*; do
  if [ -f "$i" ]; then printf "BLAH"; fi
done

これは複数のファイルとファイル名の空白で動作します。

19
grok12

次のことができます。

set -- xorg-x11-fonts*
if [ -f "$1" ]; then
    printf "BLAH"
fi

これはshとその派生語:kshとbashで動作します。サブシェルは作成されません。 $(..)と `...`コマンドはサブシェルを作成します:それらはプロセスをフォークし、そしてそれらは非効率的です。もちろん、それはいくつかのファイルで動作します、そしてこの解決策は最速であるか、または最速のものに次ぐことができます。

一致しない場合も機能します。コメントの一人が言うようにnullglobを使う必要はありません。 $ 1には元のテスト名が含まれます。したがって、$ 1ファイルが存在しないため、test -f $ 1は成功しません。

19
joseyluis

更新:

さて、今私は間違いなく解決策を持っています。

files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l)
if [ "$files" != "0" ]
then
   echo "Exists"
else
    echo "None found."
fi

> Exists
14
Swift

多分これは誰かを助けるでしょう:

if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi
13
Marian

質問はLinux/Bashに固有のものではなかったので、私はPowershellの方法を追加することを考えました - ワイルドカードを異なって扱う - あなたはそれをinのように引用符で囲みます:

If (Test-Path "./output/test-pdf-docx/Text-Book-Part-I*"){
  Remove-Item -force -v -path ./output/test-pdf-docx/*.pdf
  Remove-Item -force -v -path ./output/test-pdf-docx/*.docx
}

元の質問の概念はBashやLinuxだけでなく一般的に「シェル」をカバーしており、同じ質問を持つPowershellユーザーにも当てはまるので、私はこれが役に立つと思います。

6
Jeremy Hajek

厳密に言えば、「Blah」だけを印刷したいのであれば、これが解決策です。

find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 'BLAH' -quit

これは別の方法です:

doesFirstFileExist(){
    test -e "$1"
}

if doesFirstFileExist xorg-x11-fonts*
then printf "BLAH"
fi

しかし、ファイル名をソートしようとはしないので、最も最適な方法は次のようになると思います。

if [ -z `find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 1 -quit` ]
then printf "BLAH"
fi
4
Vouze

私が使っているbashコード

if ls /syslog/*.log > /dev/null 2>&1; then 
   echo "Log files are present in /syslog/; 
fi

ありがとうございます。

3
CapitanBlack

これはforループやlsfindなどの外部コマンドを必要としないあなたの特定の問題に対する解決策です。

if [ "$(echo xorg-x11-fonts*)" != "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi

お分かりのように、それはあなたが望んでいたものよりほんの少し複雑であり、シェルがglobを展開できない場合、そのglobを持つファイルが存在しないことを意味し、echoはglobを出力します。is is、これは私達がそれらのファイルのどれかが全く存在するかどうかチェックするために単なる文字列比較をすることを可能にします。

手続きを一般化する場合は、にしますが、ファイルにはその名前やパス内にスペースが含まれている可能性があります。 glob charは、当然のことながら何も展開できません(この例では、名前が正確に xorg-x11-fontsのファイルの場合です)。

これは、bashにある以下の関数によって実現できます。

function doesAnyFileExist {
   local arg="$*"
   local files=($arg)
   [ ${#files[@]} -gt 1 ] || [ ${#files[@]} -eq 1 ] && [ -e "${files[0]}" ]
}

あなたの例に戻ると、それはこのようにして呼び出すことができます。

if doesAnyFileExist "xorg-x11-fonts*"; then
    printf "BLAH"
fi

関数を正しく機能させるには、関数自体の中でGlob展開を行う必要があります。そのため、引数を引用符で囲み、関数本体の最初の行が次のようになるようにします。関数の外側の拡張は、偽のパラメータと同様に1つにまとめられます。もう1つの方法は、複数の引数がある場合にエラーを発生させることですが、もう1つの方法は、最初の引数以外のすべてを無視することです。

関数本体の2行目は、files varを、各配列要素に対して1つずつ、globが展開したすべてのファイル名で構成されるarrayに設定します。ファイル名にスペースが含まれている場合は問題ありません。各配列要素にはスペースも含めてそのままという名前が含まれます。

関数本体の3行目は、2つのことを行います。

  1. 最初に、配列内に複数の要素があるかどうかを確認します。もしそうなら、それはglob確かにが何かに展開されたことを意味します(最初の行で行ったことのために)。知りたかった。

  2. 1.ステップで、配列内の要素数が2未満であることがわかった場合は、1つ取得したかどうかを確認し、存在するかどうかを確認します(通常の方法)。関数引数グロブ文字なしで考慮するには、この追加のチェックを行う必要があります。この場合、配列に含まれる要素は1つのみ、unspandedです。

2
Fabio A.

私はこれを使う:

filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'`  
if [ $filescount -gt 0 ]; then  
    blah  
fi
1
Alexandre Vr.

私見は、ファイル、グロブ、またはディレクトリをテストするときは、常にfindを使うほうがいいでしょう。そうすることにおけるつまずきのブロックはfindの終了ステータスです:すべてのパスがうまくトラバースされたなら0、そうでなければ> 0。 findに渡した式は、その終了コードにエコーを作成しません。

次の例では、ディレクトリにエントリがあるかどうかをテストします。

$ mkdir A
$ touch A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty'
not empty

Aにファイルがない場合grepは失敗します。

$ rm A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo 'empty'
empty

Aが存在しない場合grepはstderrにのみ出力されるため、findは再び失敗します。

$ rmdir A
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty' || echo 'empty'
find: 'A': No such file or directory
empty

-not -emptyを他のfind式に置き換えますが、-execをstdoutに出力するコマンドにする場合は注意してください。そのような場合にはもっと具体的な式をgrepしたいと思うかもしれません。

この方法はシェルスクリプトでうまく機能します。最初の質問はglob xorg-x11-fonts*を探すことでした。

if find -maxdepth 0 -name 'xorg-x11-fonts*' -print | head -n1 | grep -q .
then
    : the glob matched
else
    : ...not
fi

xorg-x11-fonts*が一致しなかった場合、またはfindがエラーを検出した場合は、else分岐に達します。ケースを区別するために$?を使用してください。

1
if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi
1
Dan Elias

他のファイルも切り取ることができます

if [ -e $( echo $1 | cut -d" " -f1 ) ] ; then
   ...
fi
0
McCloud

どうですか?

if ls -l  | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a Shell glob
then
     # do something
else
     # do something else
fi 
0
abc

Ksh、bash、およびzshシェルで新しい派手なshmancy機能を使用する(この例では、ファイル名のスペースは処理されません)。

# Declare a regular array (-A will declare an associative array. Kewl!)
declare -a myarray=( /mydir/tmp*.txt )
array_length=${#myarray[@]}

# Not found if the 1st element of the array is the unexpanded string
# (ie, if it contains a "*")
if [[ ${myarray[0]} =~ [*] ]] ; then
   echo "No files not found"
Elif [ $array_length -eq 1 ] ; then
   echo "File was found"
else
   echo "Files were found"
fi

for myfile in ${myarray[@]}
do
  echo "$myfile"
done

はい、これはPerlのような匂いがします。私がそれに参加しなかったことを嬉しく思います;)

0
Ben Slade

これを試して

fileTarget="xorg-x11-fonts*"

filesFound=$(ls $fileTarget)  # 2014-04-03 edit 2: removed dbl-qts around $(...)

編集2014-04-03(dbl-quotesを削除し、テストファイル 'Charlie 22.html'(2スペース)を追加しました

case ${filesFound} in
  "" ) printf "NO files found for target=${fileTarget}\n" ;;
   * ) printf "FileTarget Files found=${filesFound}\n" ;;
esac 

テスト

fileTarget="*.html"  # where I have some html docs in the current dir

FileTarget Files found=Baby21.html
baby22.html
charlie  22.html
charlie21.html
charlie22.html
charlie23.html

fileTarget="xorg-x11-fonts*"

NO files found for target=xorg-x11-fonts*

これは現在のディレクトリ、またはvar fileTargetに検査したいパスが含まれている場合にのみ機能することに注意してください。

0
shellter

共有する価値があるいくつかのきちんとした解決策を見つけました。最初のものはまだ「あまりにも多くの一致がある場合これは壊れます」問題に苦しんでいます:

pat="yourpattern*" matches=($pat) ; [[ "$matches" != "$pat" ]] && echo "found"

[ ]構文なしで配列を使用すると、配列の最初の要素が得られることを思い出してください。)

スクリプトに "shopt -s nullglob"がある場合は、単純に次のようにします。

matches=(yourpattern*) ; [[ "$matches" ]] && echo "found"

ディレクトリに大量のファイルを格納することが可能であれば、findを使用してうまく動かなくなります。

find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'
0
Chris Cogdon

ワイルドカードを使用してネットワークフォルダに大量のファイルがある場合は問題があります(速度、またはコマンドライン引数のオーバーフロー)。

私はとなった:

if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then
  echo Such file exists
fi
0
luxigo