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に加えて、他の多くのファイル転送プロトコルもサポートされており、それらのいくつかはより安定しています。