主な質問:
バックアップを実行するスクリプトを書いています。次の方法でファイルのリストを作成します。
LISTOFFILES=$(find ~ \( -name '*.[pP][dD][fF]' -o -name '*.[oO][dD][tT]' \))
変数LISTOFFILES
は、多かれ少なかれ次のようなものを格納します。
/home/user/file1.pdf /home/user/file2.odt /home/user/Python Tutorial.pdf
私の次のステップは:
tar cvf backup.tar $LISTOFFILES
Bashの応答は次のとおりです。
/home/user/file1.pdf
/home/user/file2.odt
/home/user/Python: Cannot stat: No such file or directory
tar: Tutorial.pdf
BashはPython
とTutorial.pdf
が異なるファイルであると想定していることを理解しています。
find
にバックスラッシュが含まれているファイルのリストを作成する方法はありますか、それともパイプでls
を使用するように変更する必要がありますか?
ヒントやアドバイスを読みたいのですが。
私の他の質問:
この質問への答えはそれほど重要ではありませんが、1つのヒントが役立つ可能性があります
ファイルパスなしでファイル名のみを含むファイルを一覧表示するにはどうすればよいですか?
例:/home/user/file
の代わりにfile
。
find
およびPrune
オプションを指定してpath
コマンドを試しましたが、成功しませんでした。
あなたの主な質問に関しては、あなたは別のアプローチを取る必要があるでしょう。あなたの方法は、すべてのファイルを$ LISTOFFILESの単一の文字列に入れることです。次に、アクセスするときに、引用符を使用していないため、スペースで分割され、表示されている結果が得られます。
必要な結果を得る最も簡単な方法は次のとおりです。
find ~ \( -name '*.[pP][dD][fF]' -o -name '*.[oO][dD][tT]' \) -print0 | xargs -0 tar cvf backup.tar
そこで行ったのは、findを実行し、各結果をNULL文字(-print0部分)で区切ることでした。次に、findの出力がxargsにパイプされ、NULL文字で区切られたファイルのリストが読み込まれ(-0引数はxargsにこれを行うように指示します)、そのファイルのリストを引数として 'tar'に渡します。
2番目の質問:find
コマンドからのみファイル名を取得する場合は、printfを使用します
find /path/to -printf '%f\n'
file.txt
一般的に、findだけでなく、basename
を使用するのが最も簡単です。
# basename /path/to/file.txt
file.txt
basenameは、まさにそれを行うように設計されており、他には何もありません。
find
によって返されるファイル名のリストを蓄積できますが、ファイル名に改行がない場合に限り、予防策を講じる必要があります。
ここで_$LISTOFFILES
_のように、引用符で囲まれていないコマンド置換または変数展開を記述すると、シェルは結果に対して 単語分割 および グロブ(ワイルドカード展開) を実行します。
規則:コマンドの置換と変数の展開は常に二重引用符で囲みます。
例外:二重引用符を省略する必要がある理由と、そうしても安全な理由を理解している場合。
ここでは、二重引用符を省略する必要があります。これは、単語の分割が文字列を改行で区切られたビットに分割するためです。ただし、デフォルトでは、他の空白文字でも分割されます。したがって、それを安全にし、グロブもオフにする必要があります。前者は IFS
variable を単一の改行に設定し、後者は _set -f
_ を呼び出すことで実現します。
_IFS='
'
set -f
tar cvf backup.tar $LISTOFFILES
_
バックスラッシュは含まれないことに注意してください。バックスラッシュは、ファイル名を直接入力するときに使用されます。find
などのコマンドによって生成される場合は使用されません。
find
を使用する通常の方法find
の出力を解析するのではなく、_-exec
_アクションを使用して、ファイルに対して実行するコマンドを呼び出すようにする必要があります。これにより、ファイルが多すぎてコマンドラインの最大長を超えた場合に対処できます。プログラムは必要な回数だけ呼び出されます。
ここでは、_tar -c
_を複数回呼び出すことができないため、これは少し難しいです(呼び出しごとに新しい空のアーカイブから最初からやり直します)。ただし、 ディレクトリ内のすべてのPDFを切り上げ、ディレクトリ構造を保持する を参照してください
ファイルが多すぎず、ksh93またはbash≥4またはzshを実行している場合は、_**
_ワイルドカードを使用してサブディレクトリに再帰します。最初にいくつかのシェルオプションを設定する必要があります。
_set -o globstar # on ksh93
shopt -s globstar -s extglob # on bash 4
setopt ksh_glob # on zsh
tar cvf backup.tar ~/**/*.@(PDF|pdf|ODT|odt)
_
Zshでは、特別な設定なしで、そのパターンを~/**/*.(PDF|pdf|ODT|odt)
として記述できます。 _setopt extglob
_の後に、~/**/(#i)*.(pdf|odt)
と書くことができます。
または、 pax を使用できます:
_pax -w -x ustar -s '/\.pdf$/&/' -s '/\.PDF$/&/' -s '/\.odt$/&/' -s '/\.ODT$/&/' -s '/.*//' ~ >backup.tar
_
tar
とpax
にはいくつかの方法がありますが、最も簡単な方法は、最初にアーカイブするディレクトリに変更することです。
_( IFS='
'; set -f; cd ~ && tar cvf /path/to/backup.tar $(find . …) )
( cd ~ && pax -w … . ) >backup.tar
_
最初の質問に対する答えはquotes
です。
-> tar cvf backup.tar $LISTOFFILES
を実行する代わりに
これを行う-> tar cvf backup.tar "$LISTOFFILES"
例を見てみましょう(このデモ用にファイルの1つを使用しましたが、ファイルがコンピューターに存在しない場合でも、引用符の有無にかかわらず発生するエラーを確認してください)-
[jaypal:~] file="/home/user/Python Tutorial.pdf"
[jaypal:~] ls $file
ls: /home/user/Python: No such file or directory
ls: Tutorial.pdf: No such file or directory
/home/user/Python
とTutorial.pdf
の2つのファイルでエラーが発生しました。 quote
をvariable
に入れて、何が得られるか見てみましょう。
[jaypal:~] ls "$file"
ls: /home/user/Python Tutorial.pdf: No such file or directory
ファイル/home/user/Python Tutorial.pdf
でエラーが1回だけ発生しました
/path/to/file
を整理するには、いくつかの方法があります。私のお気に入りはawk
ですが、sed
用にも紹介します。
sed
メソッド:
[jaypal:~/Temp] echo "$filename"
/home/user/file1.pdf /home/user/file2.odt /home/user/Python Tutorial.pdf
[jaypal:~/Temp] echo "$filename" | sed 's@/home/user/@@g'
file1.pdf file2.odt Python Tutorial.pdf
このようにスクリプトに含めることができます-
LISTOFFILES=$(find ~ \( -name '*.[pP][dD][fF]' -o -name '*.[oO][dD][tT]' \) | sed 's@/home/user/@@g' )
ここで覚えておくべきことは、substitution
を実行しているということです。 /home/user/
をnothing
に置き換えています。複数のディレクトリからファイルをfind
ingしている場合は、何トンもの置換を行うか(yikes!)、awk
の方法で行うことができます。
awk
メソッド:
awk -F"/" '{print $NF}'
うん、それだけです。任意のディレクトリまたはそのサブディレクトリの下にあるfind
ファイルに対して機能します。
したがって、スクリプトは次のようにこれを行うことができます-
LISTOFFILES=$(find ~ \( -name '*.[pP][dD][fF]' -o -name '*.[oO][dD][tT]' \) | awk -F"/" '{print $NF}')
ここに私があなたに役立つかもしれないと思ういくつかのランダムなヒントがあります-
find
ファイル名insensitively
には、現在のファイル名の代わりにiname
を使用します。これをチェックしてください-[jaypal:~/Temp] find . -name "j*.txt"
./jP.txt
[jaypal:~/Temp] find . -iname "j*.txt"
./jj.TXT
./jP.txt
/path/to/file
をプルーニングする他の方法は、basename
を使用することです。例を挙げますが、快適に感じるようにスクリプトに組み込むことができます。[jaypal:~/Temp] filename="/usr/bin/Java.pdf"
[jaypal:~/Temp] echo "$filename"
/usr/bin/Java.pdf
[jaypal:~/Temp] basename "$filename"
Java.pdf
お役に立てれば!