フォルダを繰り返すと、とにかくループが発生します。

フォルダを繰り返すと、とにかくループが発生します。

フォルダを繰り返す場合

for f in $path 

ワイルドカードに一致するファイルがないにもかかわらず、ループが入力され続ける予期しない動作が発生しました。私はcygwinを使っていますが、これは私のコードです

#!/bin/sh
here=$(pwd)
release(){
    release="$1"
    targetdisk="$2"
    package="$3"
    searchPath="../$package/${package}Setup/*/*/*.msi"
    echo $searchPath
    ls $searchPath
    for f in $searchPath; do
        mode=$(echo $file | sed -e 's:.*/.*/\(.*\)/.*\.msi:\1:')
        version=$(echo $file | sed -e 's:.*/\(.*\)/.*/*.\.msi:\1:')
        PREVIOUSIFS=$IFS
        IFS=#
        targetPath="$targetdisk:\\Software and tools\\Install\\$release"
        newFile="$targetPath\\$package-$mode-$release.exe"
        if [ ! -d "$targetPath" ]; then
            mkdir -p "$targetPath"
        fi
        echo $newFile
        echo $f


        #cp $file $newFile
        IFS=$PREVIOUSIFS
    done;
}
release $1 $2 MyStuff

MyStuffをわざと入れたところ存在しません。

$ ./release.sh 1.0-dev G
../MyStuff/MyStuffSetup/*/*/*.msi
ls: ../MyStuff/MyStuffSetup/*/*/*.msi: No such file or directory
G:\Software and tools\Install\1.0-dev\MyStuff--1.0-dev.exe
../MyStuff/MyStuffSetup/*/*/*.msi

しかし、ご覧のとおり、両方のエコーが実行されます。一致するものがないのに、なぜこれが起こるのですか?

答え1

どのファイルとも一致しないワイルドカードパターンは拡張されません。その目的は、ワイルドカード文字を含むがパターンの一部ではないコマンドにパラメータを入力できるようにすることです。これは、コマンドラインで2つの引用符文字を時々保存できるため、ある程度意味があります。スクリプトでは、これは非常に迷惑なことです。残念ながら、レガシーshではこの動作を変更する方法はありません。パターンが拡張されていないことを確認できます。

for f in "../$package/${package}Setup"/*/*/*.msi; do
  if [ "$f" = "../$package/${package}Setup"/*/*/*.msi" ]; then
    # No match
    break
  fi
done

一致しないワイルドカードパターンを空のリストに拡張するようにBashに指示できます。 shebang行をに変更すると、次のことが#!/bin/bashできます。

shopt -s nullglob
for f in "../$package/${package}Setup"/*/*/*.msi; do

スクリプトに他の問題があります。searchPathにスペースやワイルドカード(バックスラッシュを含む)が含まれていると、変数の使用は中止されます$package。変数の置換を引用符なしでそのままにしないでください$searchPath。変数置換をスペースで区切られたパターンのリストとして扱う場合を除き、常に二重引用符を使用してください。しかし、ここではそうではありません。  $package文字列(二重引用符が必要です)なので、*バッチをワイルドカードパターンにすることは明らかです。上記のようにパターンをforループに直接置きます。

ロギングの場合、これらのコマンドを使用する代わりにecho(変数の置換に二重引用符がないため、パラメータが破損する可能性があります)、それを使用して実行されるとset -xstderrに各コマンドを表示します。

スクリプトのある時点で設定していますが、IFS=#決して分割しません#IFS使用する予定がない場合は設定しないでください。

いくつかの質問がありますmode=$(echo $file | sed -e 's:.*/.*/\(.*\)/.*\.msi:\1:')。上記のように二重引用符がありません"$file"。また、各ファイルに対する追加の処理呼び出しがsed遅く(Cygwinでの外部プロセスの実行が遅い)、複雑すぎます。シェルには文字列処理構造がありますが、ここでは十分です。あなたの

dir="${file%/*}"
mode="${dir##*/}"
dir="${dir%/*}"
version="${dir##*/}"

答え2

このスクリプトには多くの問題がありますが、あなたが尋ねる動作(ループの理由)は次のとおりです。

シェル構造から

for f in <expr>; do <commands>; done

ファイルパターンは通常「expr」に使用され、これはユーザーが実行するシェルファイル名のワイルドカードに置き換えられます。ただし、式が一致するものがないため、ワイルドカードは式をファイルパスのリストに拡張しません。ただし、他の多くの言語と同様に、長さゼロ(空白)のリストではなく、ループの上に表示されるアスタリスクを持つパス式です。したがって、ループは一度実行され、$ fは* .msiで終わる非拡張文字列と同じに設定されます。ループの前の位置をエコーすることでこれを確認できます。 lsと一緒に使用しているので、これがどんなものとも一致しないことを知っています。これが「該当するファイルやディレクトリがありません」というエラーが発生する理由です。

(また、ループで$ fileを使用したことに注意してください。私の考えでは、そこで$ fを使用しようとしていたようです。

関連情報