web-dev-qa-db-ja.com

forループのスペースを含むファイル名、findコマンド

複数のサブフォルダー内のすべてのファイルを検索し、tarにアーカイブするスクリプトがあります。私のスクリプトは

for FILE in `find . -type f  -name '*.*'`
  do
if [[ ! -f archive.tar ]]; then

  tar -cpf archive.tar $FILE
else 
  tar -upf archive.tar $FILE 
fi
done

Findコマンドを使用すると、次の出力が得られます

find . -type f  -iname '*.*'
./F1/F1-2013-03-19 160413.csv
./F1/F1-2013-03-19 164411.csv
./F1-FAILED/F2/F1-2013-03-19 154412.csv
./F1-FAILED/F3/F1-2011-10-02 212910.csv
./F1-ARCHIVE/F1-2012-06-30 004408.csv
./F1-ARCHIVE/F1-2012-05-08 190408.csv

しかし、FILE変数はパスの最初の部分のみを保存します./ F1/F1-2013-03-19そして次の部分160413.csv

Whileループでreadを使用してみましたが、

while read `find . -type f  -iname '*.*'`;   do ls $REPLY; done

しかし、次のエラーが表示されます

bash: read: `./F1/F1-2013-03-19': not a valid identifier

誰かが別の方法を提案できますか?

更新

以下の回答で示唆されているように、スクリプトを更新しました

#!/bin/bash

INPUT_DIR=/usr/local/F1
cd $INPUT_DIR
for FILE in "$(find  . -type f -iname '*.*')"
do
archive=archive.tar

        if [ -f $archive ]; then
        tar uvf $archive "$FILE"
        else
        tar -cvf $archive "$FILE"
        fi
done

私が得る出力は

./test.sh
tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors
31
Ubuntuser

forfindを使用することは、ここでは間違ったアプローチです。たとえば、 this writeup 開いているワームの缶についてを参照してください。

推奨されるアプローチは、 here の説明に従ってfindwhileおよびreadを使用することです。以下はあなたのために働くはずの例です:

find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done

このように、ファイル名をヌル(\0)文字で区切ることにより、スペースやその他の特殊文字の違いが問題を引き起こさないことを意味します。

findが見つけるファイルでアーカイブを更新するには、その出力をtarに直接渡すことができます。

find . -type f -name '*.*' -printf '%p\0' | 
tar --null -uf archive.tar -T -

アーカイブが存在するかどうかを区別する必要はないことに注意してください。tarはアーカイブを適切に処理します。アーカイブに-printfビットが含まれないように、ここで./を使用することにも注意してください。

35
Thor

次のようにforループを引用してみてください。

for FILE in "`find . -type f  -name '*.*'`"   # note the quotation marks

引用符がないと、bashはスペースと改行(\n)をうまく処理しません...

また設定してみてください

IFS=$'\n'
11
kiri

これは機能し、より簡単です:

find . -name '<pattern>' | while read LINE; do echo "$LINE" ; done

この回答は、Rupa( https://github.com/rupa/z )の功績によるものです。

8
ShawnMilo

適切な引用に加えて、findにNULLセパレーターを使用するように指示し、whileループで結果を読み取って処理することができます。

while read -rd $'\0' file; do
    something with "$file"
done < <(find  . -type f -name '*.*' -print0)

これは、POSIX準拠のファイル名を処理する必要があります-man findを参照してください

   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.
4
steeldriver

スペースを含む可能性のあるファイルを見つけるために、このようなことをしました。

IFS=$'\n'
for FILE in `/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf`; do
    file $FILE | tee -a $LOG
done

魅力のように働いた:)

1
Scott B
find . <find arguments> -print0 | xargs -0 grep <pattern>
1
user2802945

ファイル名に改行文字が含まれている場合、ほとんどの回答は中断します。私は15年以上もbashを使用していますが、インタラクティブのみです。

Pythonでは、os.walk()を使用できます。 http://docs.python.org/2/library/os.html#os.walk

また、tarfileモジュール: http://docs.python.org/2/library/tarfile.html#tar-examples

0
guettli

findの-​​execオプションを使用した方が良いと思います。

find . -type f -name '*.*' -exec tar -cpf archive.tar {} +

Findは、システムコールを使用してコマンドを実行します。その結果、スペースと改行が保持されます(パイプではなく、特殊文字の引用が必要になります)。 「tar -c」は、アーカイブがすでに存在するかどうかにかかわらず機能し、(少なくともbashでは){}も+も引用符で囲む必要がないことに注意してください。

0
Drake Clarris