lftp経由でアップロードするためのスクリプトを作成しました。
#! /usr/bin/bash
set -xe
if [ -n "$1" ]; then
# ...
else
source="."
# target=name of current local folder
target="${PWD##*/}"/
cmd="mirror --reverse --continue --parallel=5 "$source" "$target""
fi
lftp -u $user,$pass $host << EOF
set log:enabled true
eval "$cmd"
quit
EOF
現在のディレクトリにスペースがない場合は正常に動作します。この問題は、現在のディレクトリにスペースがある場合に発生します。デバッグ情報は次のように表示されます。
➜ upload.sh
+ '[' -n '' ']'
+ source=.
+ target='Two Words/'
+ cmd='mirror --reverse --continue --parallel=5 . Two Words/'
Two
この問題により、スクリプトは代わりに名前付きディレクトリにアップロードされますTwo Words
。
特に、どの行が私が間違っているのかわからないので、この問題をどのように解決するのかわかりませんcmd=...
。eval "$cmd"
?どれでもない?両方とも?
とにかくデバッグ出力は次のようになると予想しましたが(二重引用符を+ cmd='mirror --reverse --continue --parallel=5 "." "Two Words/"'
参照)、なぜそうでないのかわかりません。
この問題は、これまで私が調査した他の多くの問題に似ています。 Googleでこれが異なる点/難しいのは、拡張が「ここの文字列」内で発生することです。私が知っている限り、それは重要ではありませんが、私が知っている限り、それは非常にユニークです。
コメントでは、すべてを配列に保存するよう提案しました。私の試みは次のとおりです。
#! /usr/bin/bash
set -xe
if [ -n "$1" ]; then
cmd=(mput -c -P 5 "$1")
if [ -n "$2" ]; then
cmd=(mkdir -p "$2"
mput -c -P 5 -O "$2" "$1") #*
fi
else
source="."
# target=name of current local folder
target="${PWD##*/}"/
cmd=(mirror --reverse --continue --parallel=5 "$source" "$target")
fi
lftp -u $user,$pass $host << EOF
"${cmd[@]}"
quit
EOF
(*サブシェルに複数行を含める方法がわからないので、実際の改行を入れましたが、それが正しいのかどうかわかりません。Googleで検索した結果はすべてコマンドの置き換えに関連しています。サブシェルではありません。)
これを実行すると、次のエラーが発生します。Unknown command 'mirror --reverse --continue --parallel=5 . Two Words/'.
これをコピーしてシェルに貼り付けると、他のエラーが発生するため、何が起こっているのか理解できません。
電話でもlftp -u $user,$pass $host -e "${cmd[@]}"
接続できません。このエラーが発生します。
open: unrecognized option '--reverse'
Usage: lftp [-e cmd] [-p port] [-u user[,pass]] <host|url>
lftp ... -e "$("${cmd[@]}")"
このエラーは、次の実行時に発生します。
open: option requires an argument -- 'e'
Usage: lftp [-e cmd] [-p port] [-u user[,pass]] <host|url>
ご覧のとおり、現在私はランダムに試しています。これは良い戦略ではありません。
答え1
sコマンドの言語構文からこれらのパラメーターを引用する必要があります(または少なくともlftp
s言語の対応するスペースまたはその他の特殊文字をエスケープする必要があります)。lftp
mirror
lftp
lftp
簡単な引用フォームをサポートします。ほとんどのシェルと同様に、単一引用符、二重引用符、およびバックスラッシュを使用できますが、構文は異なります。lftp
のコマンドを使って実験してみることができますecho
。
lftp :~> echo \'
'
lftp :~> echo \a
\a
lftp :~> echo \
> a
a
\
エスケープ演算子ですが、特定の文字に対してのみ機能します。改行文字が後に続く場合は行連続文字で、改行文字をエスケープしません。
lftp :~> echo "a\'"
a\'
lftp :~> echo "a\"b"
a"b
lftp :~> echo "a\$b"
a\$b
二重引用符内でエスケープする文字のリストはさらに減ります。"
そこにゴミの欠片があるかもしれません"..."
。\"
lftp :~> echo 'a\'b'
a'b
lftp :~> echo 'a\\b'
a\b
一重引用符内でも同様です。'...'
そこにある引用符は、shなどのシェルの強い引用符とは異なります。
lftp :~> echo $'a\nb'
$a\nb
$'...'
ksh93スタイルの引用フォームはサポートされていません。
lftp :~> echo 'foo
foo
lftp :~> echo 'a\
b'
ab
引用符はペアで表示する必要はありません。引用符内にあるかどうかにかかわらず、コマンドパラメータに改行文字を含めることはできません。
'...'
コードを見てエスケープする必要がある文字は、"..."
バックスラッシュとその引用符文字だけです。 lftp
バイトレベルで作業すると、バイトシーケンスは文字としてデコードされません。
したがって、構文内の文字列を適切に引用する最善の方法lftp
は、すべて'
および\
その中(実際には0x5cバイトまたは他のマルチバイト文字エンコーディングのバイト)\
とPackaged inをエスケープすることです'...'
。または"..."
。
ここで使用するeval
ことは不要で、2つのレベルの参照を管理する必要があるため、より複雑になります。
#! /usr/bin/bash -
set -xe
lftp_quote() {
local LC_ALL=C
local -n var
for var do
if [[ $var = *$'\n'* ]]; then
printf >&2 '%s\n' "\"$var\" contains newline characters. That's not supported by lftp"
exit 1
fi
var=${var//'\'/'\\'}
var=${var//"'"/"\'"}
var="'$var'"
done
}
if (( $# > 0 )); then
...
else
source="."
# target=name of current local folder
target="${PWD##*/}"/
lftp_quote source target
cmd="mirror --reverse --continue --parallel=5 -- $source $target"
fi
lftp_quote user pass host
lftp << EOF
set log:enabled true
open --user $user --password $pass -- $host && $cmd
EOF
(パスワードは公開なので、ここからコマンドラインにパスワードを渡さないでください。)
これらの文字列をそのままコマンドに渡すことができますが、FTPプロトコルのコマンドでこれらのファイル名を適切にエスケープするかどうかは別の問題lftp
ですmirror
。lftp
FTPはこの点で信頼できないものとして悪名高いもので、サーバーごとに異なる動作をします。lftp
FTPに加えて、他の多くのファイル転送プロトコルもサポートされており、それらのいくつかはより安定しています。
答え2
特に、どの行が私が間違っているのかわからないので、この問題をどのように解決するのかわかりません
cmd=...
。eval "$cmd"
?どれでもない?両方とも?
とにかくデバッグ出力は次のようになると予想しましたが(二重引用符を
+ cmd='mirror --reverse --continue --parallel=5 "." "Two Words/"'
参照)、なぜそうでないのかわかりません。
あなたが持っているものは次のとおりです。
cmd="mirror --reverse --continue --parallel=5 "$source" "$target""
最初の"
(at cmd="
)は引用符付き文字列を開始し、2番目の(at "$source
)はそれを終了し、次の文字列にも同じように適用されます。結果文字列には引用符はありません。使用する必要があります
cmd="mirror ... \"$source\" \"$target\""
文字列に二重引用符を追加するか、lftpに一重引用符を使用できる場合は、次のようにします。
cmd="mirror ... '$source' '$target'"
cmd=(mirror --reverse --continue --parallel=5 "$source" "$target") lftp -u $user,$pass $host << EOF "${cmd[@]}"
このコマンドを実行すると、Unknown command 'mirror --reverse --continue --parallel=5 エラーが発生します。 2つの単語/ '。
素晴らしい。拡張機能は、"${array[@]}"
配列要素を複数の異なるシェルワード(文字列)に拡張し、ファイル名にスペースが残っているため、シェルで役立ちます。しかし、ここではhere-docにあるので、複数の単語/文字列を生成することは不可能です。完全なhere-docはただ1つの文字列です。したがって、配列要素をスペースで連結し、その値に拡張します。区切り文書と同様に、引用符が保存されます。
lftpで取得したコマンドは次のとおりです。
"mirror --reverse --continue ..."
引用符は単一コマンドとして扱われます。
lftp :~> "echo foo"
Unknown command `echo foo'.
lftp :~> echo foo
foo
質問タイトルでは、「区切られた文書の変数をパラメータとして[拡張]」について尋ねますが、そうすることは不可能なので質問ではありません。とにかくここに文書があります)。いいえ、問題はlftpが正しく引用することです。
これをコピーしてシェルに貼り付けると、他のエラーが発生するため、何が起こっているのか理解できません。
おそらくこのようなものはありませんかbash: mirror: command not found
? lftpのコマンドがあるので、mirror
それをシェルのコマンドラインに貼り付けるのが役に立つかどうかはわかりません。
シェルコマンドを保存するために配列を使用するように提案を理解します。いくつかありますとても良い理由。しかし、ここにはシェルコマンドがないので不要です。
電話でも
lftp -u $user,$pass $host -e "${cmd[@]}"
接続できません。このエラーが発生します。open: unrecognized option '--reverse' Usage: lftp [-e cmd] [-p port] [-u user[,pass]] <host|url>
ここでは、コマンドラインの配列の内容を lftp に渡します。[@]
配列要素は以下を使用したので、別の引数に渡されます。
lftp -u USER,PASSWORD HOSTNAME -e mirror --reverse --continue --parallel=5 SOURCE TARGET
(もちろん、、$user
も拡張されました。$host
)$source
$target
この-e
オプションはコマンドを引数として使用します。この場合、コマンドはですmirror
。次のオプションは--reverse
他のコマンドラインオプションとして扱われ、lftpでは認識されません。ここであなたが"${cmd[*]}"
使いたい避ける-e
lftpのオプションはとにかく文字列のみを使用できるため、配列要素を一意の引数として使用します。
一方、単一の文字列としてのみ機能できるため、配列を使用することは最初は役に立ちません。
lftp ... -e "$("${cmd[@]}")"
このエラーは、次の実行時に発生します。
$(...)
シェルから内部的にコマンドとして実行されるコマンド置換があります。どこで入手したのかわかりませんが、たとえば試してみてください。
$ cmd=(date)
$ lftp ... -e "$("${cmd[@]}")"
(おそらくそこにないという事実に感謝しなければなりませんrm something
。)
Stéphaneの答えは、ほとんどの特殊文字の引用を処理する解決策を提供しますが、もっと簡単なことをしたい場合あなたは空白文字についてだけ心配すればよいと思います。、私の考えにはこれが効果があるようです:
#! /usr/bin/bash
set -xe
source="."
target="Two Words/"
cmd="mirror --reverse --continue --parallel=5 '$source' '$target'"
user=foo
pass=secret
lftp -u "$user,$pass" http://localhost << EOF
set log:enabled true
$cmd
quit
EOF
とにかく配列は最終的に文字列になるため、配列は必要ありません。結果文字列では、ファイル名の周りに引用符が必要です。なぜなら、その中に一重引用符を使用した$cmd
からです。これは怠惰な解決策です、ファイル名に引用符が含まれているかどうかは関係ありません。代わりに二重引用符を使用できます"... \"$source\"..."
。ここにある文書は次のように表示されます。
set log:enabled true
mirror --reverse --continue --parallel=5 '.' 'Two Words/'
quit
これはlftpに渡す合理的な命令セットのようです。
もちろん、lftpガラガラをチェックする前に引用符なしでファイル名をテストするのは比較的簡単です。
case $target in
*[\'\"]*) echo "ugh, can't have quotes in filename!"; exit 1;;
esac
または、より厳密に受け入れられる文字をリストします。
case $target in
*[![:alnum:]._" "/]*) echo "ugh, disallowed characters in filename!"; exit 1;;
esac