2009-04-27 21 views
15

私は非常に単純なbashスクリプトを書いています。このディレクトリは、指定されたディレクトリをtarsし、その出力を暗号化し、バックアップメディアが巨大なファイルをサポートしていないため、複数の小さなファイルに分割します。bashスクリプトのコマンドとしての変数

私はbashスクリプトに関する多くの経験がありません。私はパラメータのスペースを許すために変数を正しく引用することに問題があると考えています。スクリプトは次のとおりです。

スプリット: "FOO/2009-04-27T14-32-04.backup" AA:そのようなファイルやディレクトリ

このコマンドを実行

#! /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_CMDを設定した$BACKUP_FILEの引用符を削除して修正できます。しかし、私のバックアップディレクトリの名前にスペースがある場合、それは動作しません。また、 "echo"コマンドの出力をコピーして端末に直接貼り付けるとうまく動作します。明らかに、バッシュが物事をどのようにエスケープしているか分かりません。

+0

は、なぜあなたはあなただけの後にそれを置くことができるときSPLIT_CMDで$ BACKUP_FILEを埋め込みます$ SPLIT_CMD/t彼はパイプライン? –

+0

さて、私はそれをすることができましたが、その時点で私のコマンドを格納する変数を持つことにはあまり意味がありませんし、下のJulianoの答えのようにすべてを展開することもできます。 – wxs

+0

http://mywiki.wooledge.org/BashFAQ/050 – tripleee

答えて

35

単純にすべてのコマンドを変数に入れないでください。引用された引数を回復しようとすると、多くの問題が発生します。また

:すべて大文字スクリプトで変数名を使用して

  1. は避けてください。足で自分を撃つ簡単な方法。
  2. 逆引用符は使用しないでください。代わりに$(...)を使用すると、より良いネストが得られます。シェルは物事を再解釈するように、変数内のスペースを引用

#! /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" 
+0

そうですね、おそらく私はこのようにします。変数の中にコマンドを置くよりもあまり優雅ではないようですが、それはbashスクリプトの性質だと思います。 – wxs

+4

@wxs:コマンドを変数に入れることについては何もエレガントではありません。どんなタイプの柔軟性も得られません。それどころか、あなたは単語分割のためにバグを引き起こすだけです。あなたが意図したことは、コマンドを関数に入れることです。関数を実行します。可変コンテンツを決して実行しないでください。これまで – lhunath

+7

私は理解していない1.説明する気に?どのように/なぜそれは足で自分自身を撃つことがより簡単になりますか? – ata

5

わかりませんが、最初にコマンドの評価を実行する価値があります。

これは、bashが変数$ TAR_CMDとその全範囲にそのように展開するようになる(echoコマンドを使用して、作品を言うコンソールに同じよう)

バッシュ、その後でラインをもう一度読みます変数が拡張されました。

eval $TAR_CMD | $ENCRYPT_CMD | $SPLIT_CMD 

私はGoogleの検索を行いましたが、このページは、それが必要な理由を説明するうえでうまくやっているようです。 http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion%3F

+0

それも同様ですが、すべてが少し複雑になるような気がします。まあ、私はなぜ人々が他のスクリプト言語を発明したのか分かります。 – wxs

+0

これは大きなセキュリティ上のリスクです。 'eval'がリスクを負う理由については、http://mywiki.wooledge.org/BashFAQ/050 - とBashFAQ#48を参照してください。http://mywiki.wooledge.org/BashFAQ/048 –

+0

良い捕獲@CharlesDuffy、他のユーザーに昇格された権限で実行するスクリプトへのアクセスが許可されている共有システムでこれを使用している場合、危険です。 – Eddie

1

が正しくハードです。これは、より強固な言語に到達するよう促す、このタイプのものです。それがperlかpythonかrubyかどうか(私はperlを選択しますが、それはいつも誰にでもあるわけではありません)、ちょうどの何かで、引用用にシェルをバイパスすることができます。

リバースなevalで正しかったことはありませんでしたが、その評価は私にeBie-jeebiesを与えます(ユーザーの入力を受け取り、評価する際には全く新しい頭痛になります)。この場合、代わりに書いて評価したものを取っています)、そして私はデバッグに頭を悩ませました。しかしIO::Pipeのビット、フォーク、およびstdoutとstderrを再開 - ここでは、ハードの部分はパイプをやっている

@tar_cmd = (qw(tar cv), $directory); 
@encrypt_cmd = (qw(openssl des3 -salt)); 
@split_cmd = (qw(split -b 1024m -), $backup_file); 

:のようにPerlで

は、私の例のように、私が何かをすることができるだろうそれは悪くない。シェルを適切に引用するよりも悪いと言う人もいますし、どこから来ているのか理解していますが、私にとっては読みやすく、管理し、書くのが簡単です。ちょっと、誰かがこれから苦労してIO :: Pipelineモジュールを作成して全体を自明にすることができました;-)

+0

'eval'を使って難しい問題にするのは難しい問題です。それをする正当な理由はない。 –

4

変数とコマンドだけを置くことがポイントです。

#! /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 -" 
+0

'tar_create'が欠けていますが、それでも助けられました –

+1

これは実際には複雑な引数の問題を解決しません。あなたの 'tar_create'が' tar cv --exclude = "* *" 'だった場合、元のものとほとんど同じように失敗してしまいます。そして、 'export'はここでは何も役に立ちません。これらの変数は同じシェルで使われるので、汚染プロセスの環境空間は単純に無駄です。 –

5

evalが許容練習ではありません。あなたが時間をかけて、あなたのコマンドを構築する必要がある場合は

:そのうちのいくつかを以下に触れている、ルートこの問題の原因とその適切なソリューションの詳細についてはevalを使用すべきでない理由の詳細についてBashFAQ #48、およびBashFAQ #50を参照してください。 、使用アレイ:

tar_cmd=(tar cv "$directory") 
split_cmd=(split -b 1024m - "$backup_file") 
encrypt_cmd=(openssl des3 -salt) 
"${tar_cmd[@]}" | "${encrypt_cmd[@]}" | "${split_cmd[@]}" 

代わりには、これはただ一つの中央の場所であなたのコマンドの定義についてであれば、使用機能:

tar_cmd() { tar cv "$directory"; } 
split_cmd() { split -b 1024m - "$backup_file"; } 
encrypt_cmd() { openssl des3 -salt; } 
tar_cmd | split_cmd | encrypt_cmd 
関連する問題