rmはコマンドラインでは機能しますが、スクリプトでは機能しません


11

私が行うとrm *.old.*、コマンドライン上では正常に削除しますが、私は私のスクリプトの以下の部分でそれを行うとき、それはすべてのRMいない*.old.*ファイルを。

私のbashスクリプトの何が問題になっています:

 for i in ./*; do
    if [[ -f $i ]];  then

        if [[ $i  ==  *.old.* ]]; then
                oldfile=$i
                echo "this file is to be removed: $oldfile"
                rm $oldfile
                exec 2>errorfile
            if [ -s $errorfile ]
            then
                echo "rm failed"
            else
                echo "removed $oldfile!"
            fi
        else
            echo "file with old extension  does not exist"
        fi

        orig=$i
        dest=$i.old
        cp $orig $dest
        echo "Copied $i"

    else
        echo "${i} is not a file"
    fi 
done

回答:


4

私があなたが何をしているのか理解している場合(.old接尾辞を持つファイルを削除し、接尾辞を持つ既存のファイルのコピーを作成する.old)、代わりにfindを使用できます:

#!/bin/sh

find . -maxdepth 1 -name \*.old -type f -printf "deleting %P\n" -delete
find . -maxdepth 1 -type f -printf "copying %P to %P.old\n" -exec cp '{}' '{}.old' \;

-maxdepth 0サブディレクトリ内の検索コマンドを停止し、-type f通常のファイルのみを検索します。-printfメッセージを作成します(%P見つかったファイル名です)。-exec cpコピー機能を呼び出して、'{}'ファイル名です


14

スクリプトにはさまざまな障害点が考えられます。まず、グロビングrm *.old*使用して、一致するすべてのファイルのリストを作成します。これにより、空白を含むファイル名を処理できます。ただし、スクリプトはグロブの各結果に変数を割り当て、引用符なしでそれを行います。ファイル名に空白が含まれている場合、これは壊れます。例えば:

$ ls
'file name with spaces.old.txt'  file.old.txt
$ rm *.old.*   ## works: both files are deleted

$ touch "file.old.txt" "file name with spaces.old.txt"
$ for i in ./*; do oldfile=$i; rm -v $oldfile; done
rm: cannot remove './file': No such file or directory
rm: cannot remove 'name': No such file or directory
rm: cannot remove 'with': No such file or directory
rm: cannot remove 'spaces.old.txt': No such file or directory
removed './file.old.txt'

ご覧のとおり、名前にスペースが含まれているファイルのループは失敗しました。正しく行うには、変数を引用する必要があります。

$ for i in ./*; do oldfile="$i"; rm -v "$oldfile"; done
removed './file name with spaces.old.txt'
removed './file.old.txt'

同じ問題は$i、スクリプトのほとんどすべての使用に当てはまります。常に変数を引用する必要があります

次に考えられる問題は*.old.*、拡張子がのファイルに一致することを期待しているように見えることです.old。そうではありません。「0個以上の文字」(*)、a .、次に「old」、次に別の文字、次に「0個以上の文字」に一致し.ます。これは、のようなものに一致せずfile.old、 `file.old.fooのようなものにのみ一致することを意味します。

$ ls
file.old  file.old.foo
$ for i in *; do if [[ "$i" == *.old.* ]]; then echo $i; fi; done
file.old.foo     

そのため、敵は一致しませんfile.old。いずれにせよ、スクリプトは必要以上に複雑です。代わりにこれを試してください:

#!/bin/bash

for i in *; do
    if [[ -f "$i" ]];  then
        if [[ "$i"  ==  *.old ]]; then
            rm -v "$i" || echo "rm failed for $i"
        else
            echo "$i doesn't have an .old extension"
        fi
        cp -v "$i" "$i".old
    else
        echo "$i is not a file"
    fi 
done

and cp echo`ステートメントに追加-vしたことに注意してください。rmwhich does the same thing as what you were doing with your

これは完全ではありません。たとえば、を見つけるfile.oldと削除され、後でスクリプトはそれをコピーしようとし、ファイルが存在しないため失敗します。ただし、スクリプトが実際に何をしようとしているのか説明していないので、実際に何を達成しようとしているのかを教えてくれない限り、それを修正することはできません。

i).old拡張子を持つすべてのファイルを削除し、ii).old拡張子を持たない既存のファイルに拡張子を追加する場合、本当に必要なのは次のとおりです。

#!/bin/bash

for i in *.old; do
    if [[ -f "$i" ]]; then
        rm -v "$i" || echo "rm failed for $i"
    else
        echo "$i is not a file"
    fi 
done
## All the ,old files have been removed at this point
## copy the rest
for i in *; do
    if [[ -f "$i" ]]; then
        ## the -v makes cp report copied files
        cp -v "$i" "$i".old
    fi
done

ファイルをfile.oldにバックアップしようとしていますが、同時に.old.oldまたは.old.old.oldまたは.old.old.oldなどで終わるファイルをrmします。rm *.old.*これらのファイルを削除するコマンドラインでfile.oldバックアップファイルではありません。私はスクリプトでそれをしようとしています。ありがとう
ドン

1
@Donは質問を編集して、これをさらに詳しく説明してください。ファイル名の例と、スクリプトの実行後にファイルに何をしたいかを含めます。理想的には、チャットに参加して、そこに私にpingを送信して、話し合うことができます。
テルドン

8

唯一の例は、rm $oldfile失敗する可能性があり、あなたのファイル名は任意の文字が含まれる場合ですIFS(スペース、タブ、改行)、または任意のグロブ文字(*?[])。

の文字IFSが存在する場合、シェルは単語の分割を行い、グロビング文字の存在に基づいて、変数展開のパス名展開を行います。

したがって、たとえば、ファイル名がの場合foo bar.old.、変数にoldfileはが含まれますfoo bar.old.

行うとき:

rm $oldfile

最初の分割での拡張シェルoldfile二つの単語にスペース上を、foobar.old.。したがって、コマンドは次のようになります。

rm foo bar.old.

これは明らかに予期しない結果につながるでしょう。あなたが任意のグロブ演算子を持っている場合ちなみに、( 、*?[]拡張で、その後、パス名展開も行われます。

目的の結果を得るには、変数を引用符で囲む必要があります。

rm "$oldfile"

これで、単語の分割やパス名の展開は行われないため、目的の結果、つまり目的のファイルが削除されるはずです。ファイル名がで始まる場合は-、次のようにします。

rm -- "$oldfile"

私たちはときに使用される内部変数を引用する必要はありません、なぜあなたは、頼むかもしれない[[、ビーイングが理由[[であるbashキーワードと、それは内部的に拡張リテラルを維持する変数の展開を処理します。


今、いくつかのポイント:

  • コマンドのexec 2>errorfile前にSTDERR()をリダイレクトする必要があります。rmそうしないと、[[ -s errorfile ]]テストで誤検知が発生します

  • を使用[ -s $errorfile ]しました。変数がどこにも定義されていない$errorfile場合、NULになる変数展開を使用していますerrorfile。おそらく、あなたは、ただ[ -s errorfile ]、STDERRリダイレクトに基づいて

  • 変数がいる場合errorfileに定義されて使用している間、[ -s $errorfile ]それは再びの説明した場合上記にチョークだろうIFSとは違っているためとグロブ[[[によって内部的に処理されていませんbash

  • スクリプトの後半ではcp、既に変数を引用せずに、既に削除されたファイルを試行していますが、これは意味がありません。そのチャックをチェックし、ターゲットに基づいて必要な修正を行う必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.