Unixシェルスクリプト、空白のパラメータ

Unixシェルスクリプト、空白のパラメータ

というシェルスクリプトを作成する必要がありますchExt.sh。ファイル拡張子を受け入れてから、複数のファイルを受け入れます。次に、ファイル拡張子を変更するか、ファイルが存在しないと宣言する必要があります。

./chExt.sh txt ocelot.cpp ../otherFolder/file.H cat.dog.TXT king cobra.dat自動テスターテストの一般的なアイデアです。問題は、名前にスペースが含まれているファイルを処理する方法がわからないことです。

私のコードは次のとおりです

newExt="$1"
shift
for x in $*
do
    fileName="$x"
    if test -f "$fileName";
    then
        name=$(echo "$fileName" | rev | cut -f 2- -d '.' | rev)
        newName="$name.$newExt"
        if test "$newName" != "$fileName";
        then
            mv "$x" "$newName"
        fi
    else
        echo "$fileName: No such file"
    fi
done

答え1

ファイル名を引用しないと、シェルはこれが2つの引数(および)foo bar.txtではなく1つの引数であることを知りません。したがって、次のようにスクリプトを呼び出す必要があります。foobar.txt

./chExt.sh txt ocelot.cpp ../otherFolder/file.H cat.dog.TXT "king cobra.dat"

次の問題は、変数が展開されると結果の値が空のスペースで分割(または$IFS変数に設定された値)。したがってfor i in $*、を書くと$*スペースが拡張され分割されます。だからそれは一度の繰り返しと次の繰り返しになります$i。回避策は、拡張したい変数が分割されないように引用することです。kingcobra.txt

これは次の質問につながります。$*間の違い$@。使用すると、"$*"引数はすべて1つの長い文字列として扱われます。

$ cat foo.sh
#!/bin/sh
for i in "$*"; do
    echo "$i"
done
$ foo.sh foo "bar baz"
foo bar baz

上記を以下と比較してみてください。

#!/bin/sh
for i in "$@"; do
    echo "$i"
done
$ foo.sh foo "bar baz"
foo
bar baz

ご覧のとおり、使用すると"$@"予想される効果があり、スペースを含む引数を含む各入力引数は個別に処理されます。

最後に、スクリプトは-オプションに対応する文字と次に始まるファイル名もブロックします(たとえば、名前付きechoファイルを試してみてください)。-new拡張を削除するより良い方法は、シェルのデフォルトの拡張を使用することです。文字列操作関数。変数の末尾から、${var%.*}aとゼロ以上の文字に一致する最も短い文字列を削除します。.つまり、拡張です。次に、次から始まるファイル名も処理できるように、オプションの終わりと引数の始まりを示すためにto--を追加する必要があります(ガイド10を参照)。mv-ここ)。

したがって、改善され動作するスクリプトのバージョンは次のとおりです。

#!/bin/sh
newExt="$1"
shift
for fileName in "$@"
do
    if test -f "$fileName";
    then
    #    name=$(echo "$fileName" | rev | cut -f 2- -d '.' | rev)
    name=${fileName%.*}
        newName="$name.$newExt"
        if test "$newName" != "$fileName";
        then
            mv -- "$fileName" "$newName"
        fi
    else
        printf '%s: No such file\n' " $fileName"
    fi
done

追加資料:

答え2

"$*"とのもう一つの注目すべきプロパティは、シェル引数の現在のコレクションである"$@"シェル配列を表すことです。これはset組み込み関数を介して変更できますが、より一般的には組み込み配列関数のデフォルトは配列を含む配列で機能することですfor

: "${2?no file arguments!}"
for x 
do     if    [ "${2+:}" ]
       then  set ".$1"
       else  [ ".${x##*.}" != "$1" ] &&
             [ -f "$x" ] &&
             mv -- "$x" "${x%.*}$1"
       fi
done

これはスクリプトが実行する操作と非常によく似ています。echo通常のファイルではなく引数では機能しないという点で若干異なります。しかし、私はあなたがそこでやっていると思うことをしているかどうかはわかりません。あなたの主張が存在するか、アクセス可能か、一般ファイルしかし、echostdoutに次のメッセージを送信しています。パラメーター:対応するファイルがありません。。 Unixシリーズシステムでは、すべてがファイルです。パイプはファイル、ディレクトリはファイル、デバイスはファイルです。したがって、あなたのテストはあなたの情報と一致しません。さらに、mv存在しないファイルを移動するように要求すると、メッセージは自動的に印刷され、stderrにも印刷されます。

私が気づいたもう一つのことは、新しい名前が以前の名前と同じかどうかをテストしていることです。これはmv、ファイル自体を移動することに対する苦情を避けるためのものだと思います。偶然にもmvこの状況を沈黙させるオプションがあります。

私は次のようにあなたの仕事をしたいと思います:

: "${2?no file arguments!}"
for x 
do     [ "${2+:}" ] &&
       set -- ".$1" && continue
       set -- "${x%.*}$1" "$1"
{      [  -e "$1" ] && printf "%s: exists!\n" "$1"; } ||
{    ! [  -f "$x" ] && printf "%s: not a regular file.\n" "$x"; } ||
       mv -f --    "$x" "$1"
       shift
done   >&2

関連情報