バックアップメディアは巨大なファイルをサポートしていないため、特定のディレクトリをtarし、その出力を暗号化し、結果のファイルを複数の小さなファイルに分割する非常に簡単なbashスクリプトを書いています。
Bashスクリプトの経験はあまりありません。パラメータにスペースを入れるために変数を適切に引用することに問題があると思います。スクリプトは次のとおりです。
#! /bin/bash
# This script tars the given directory, encrypts it, and transfers
# it to the given directory (likely a USB key).
if [ $# -ne 2 ]
then
echo "Usage: `basename $0` DIRECTORY BACKUP_DIRECTORY"
exit 1
fi
DIRECTORY=$1
BACKUP_DIRECTORY=$2
BACKUP_FILE="$BACKUP_DIRECTORY/`date +%Y-%m-%dT%H-%M-%S.backup`"
TAR_CMD="tar cv $DIRECTORY"
SPLIT_CMD="split -b 1024m - \"$BACKUP_FILE\""
ENCRYPT_CMD='openssl des3 -salt'
echo "$TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD"
$TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD
say "Done backing up"
このコマンドの実行は次のように失敗します:
split: "foo/2009-04-27T14-32-04.backup" aa:そのようなファイルまたはディレクトリはありません
$BACKUP_FILE
を設定した$SPLIT_CMD
の前後の引用符を削除することで修正できます。ただし、バックアップディレクトリの名前にスペースが含まれていると機能しません。また、「echo」コマンドからの出力を直接端末にコピーして貼り付けると、正常に動作します。明らかに、Bashが物事をどのように逃げているかについて、私には理解できないことがあります。
コマンド全体を変数に入れないでください。引用された引数を復元しようとすると、多くの問題が発生します。
また:
#! /bin/bash
if [ $# -ne 2 ]
then
echo "Usage: $(basename $0) DIRECTORY BACKUP_DIRECTORY"
exit 1
fi
directory=$1
backup_directory=$2
current_date=$(date +%Y-%m-%dT%H-%M-%S)
backup_file="${backup_directory}/${current_date}.backup"
tar cv "$directory" | openssl des3 -salt | split -b 1024m - "$backup_file"
よくわかりませんが、最初にコマンドでevalを実行する価値があるかもしれません。
これにより、bashは変数$ TAR_CMDなどをその全幅に展開します(ちょうどechoコマンドがコンソールに対して行うように、これは機能します)
その後、Bashは変数を展開して2行目を再度読み取ります。
eval $TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD
Google検索を行ったところ、このページは、なぜそれが必要なのかを説明する上でまともな仕事をしているようです。 http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F
信頼できないソースがディレクトリ名を生成できる場合、eval
は受け入れられません。 eval
を使用しない理由については BashFAQ#48 を、この問題の根本原因とその適切な解決策については BashFAQ#5 を参照してください。 、その一部を以下に触れます:
時間の経過とともにコマンドを作成する必要がある場合は、配列を使用します。
tar_cmd=( tar cv "$directory" )
split_cmd=( split -b 1024m - "$backup_file" )
encrypt_cmd=( openssl des3 -salt )
"${tar_cmd[@]}" | "${encrypt_cmd[@]}" | "${split_cmd[@]}"
あるいは、これがコマンドを1つの中央の場所で定義するだけの場合、関数を使用します。
tar_cmd() { tar cv "$directory"; }
split_cmd() { split -b 1024m - "$backup_file"; }
encrypt_cmd() { openssl des3 -salt; }
tar_cmd | split_cmd | encrypt_cmd
変数にコマンドとオプションだけを入れることにはポイントがあります。
#! /bin/bash
if [ $# -ne 2 ]
then
echo "Usage: `basename $0` DIRECTORY BACKUP_DIRECTORY"
exit 1
fi
. standard_tools
directory=$1
backup_directory=$2
current_date=$(date +%Y-%m-%dT%H-%M-%S)
backup_file="${backup_directory}/${current_date}.backup"
${tar_create} "${directory}" | ${openssl} | ${split_1024} "$backup_file"
コマンドをソースとなる別のファイルに再配置できるため、多くのスクリプトで同じコマンドとオプションを再利用できます。これは、多くのスクリプトがあり、それらがすべてツールを使用する方法を制御する場合に非常に便利です。そのため、standard_toolsには以下が含まれます。
export tar_create="tar cv"
export openssl="openssl des3 -salt"
export split_1024="split -b 1024m -"
シェルが物事を適切に再解釈するように変数内のスペースを引用するのはhardです。より強い言語に手を伸ばすよう促すのは、この種のことです。それがPerlであろうとpythonかRubyまたは何であれ(私はPerlを選択しますが、それは誰にとっても常にそうではありません)、それはただsomethingこれにより、引用のためにシェルをバイパスできます。
それは私がevalのリベラルな用量でそれを正しくすることができなかったということではありませんが、そのevalは私にeebie-jeebiesを与えます(この場合、代わりにあなたが書いたものを取り、それを評価します)、デバッグで頭痛の種になりました。
Perlを使用すると、私の例として、次のようなことができます。
@tar_cmd = ( qw(tar cv), $directory );
@encrypt_cmd = ( qw(openssl des3 -salt) );
@split_cmd = ( qw(split -b 1024m -), $backup_file );
ここで難しい部分はパイプを実行することですが、少しの IO :: Pipe 、fork、およびstdoutとstderrを再開することは悪くありません。シェルを適切に引用するよりも悪いと言う人もいますし、どこから来たのかはわかりますが、私にとっては、これは読みやすく、保守しやすく、書きやすいです。ヘック、誰かがこれから苦労してIO :: Pipelineモジュールを作成し、すべてを簡単にすることができます;-)