transfer.sh サービスにファイルをアップロードするための簡単な代替スクリプトを作成しようとしています。 Webサイトの例の1つは、単一の「セッション」で複数のファイルをアップロードする方法について言及しています。
_$ curl -i -F filedata=@/tmp/hello.txt \
-F filedata=@/tmp/hello2.txt https://transfer.sh/
_
私は、任意の数の引数(ファイル)を受け取り、そのような方法でそれらをcURLに渡す関数を作成しようとしています。機能は次のとおりです。
_transfer() {
build() {
for file in $@
do
printf "-F filedata=@%q " $file
done
}
curl --progress-bar -i \
$(build $@) https://transfer.sh | grep https
}
_
ファイル名にスペースが含まれていない限り、関数は期待どおりに機能します。 _printf "-f filedata=@%q " "hello 1.txt"
_の出力は_-F filedata=@test\ 1.txt
_なので、特殊文字が正しくエスケープされることを期待していました。ただし、関数にスペースを含むファイル名を指定して呼び出すと、次のようになります。
_$ transfer hello\ 1.txt
_
cURLはエスケープを解釈していないようで、エラーを報告します。
_curl: (26) couldn't open file "test\"
_
また、コマンドの一部を引用してみました。 _printf "-F filedata=@\"%s\" " "test 1.txt"
_、これは_-F filedata=@"test 1.txt"
_を生成します。このような場合、cURLは同じエラーcurl: (26) couldn't open file ""test"
を返します。引用符はまったく気にしないようです。
何がそのような振る舞いを引き起こしているのか、私にはよくわかりません。空白を含むファイル名でも機能するように関数を修正するにはどうすればよいですか?
@meuhで説明されているように、配列を使用することで問題を解決することができました。 bash
とzsh
の両方で機能するソリューションは次のとおりです。
_transfer () {
if [[ "$#" -eq 0 ]]; then
echo "No arguments specified."
return 1
fi
local -a args
args=()
for file in "$@"
do
args+=("-F filedata=@$file")
done
curl --progress-bar -i "${args[@]}" https://transfer.sh | grep https
}
_
zsh
とbash
の両方の出力は次のとおりです。
_$ ls
test space.txt test'special.txt
$ transfer test\ space.txt test\'special.txt
######################################################################## 100.0%
https://transfer.sh/z5R7y/test-space.txt
https://transfer.sh/z5R7y/testspecial.txt
$ transfer *
######################################################################## 100.0%
https://transfer.sh/4GDQC/test-space.txt
https://transfer.sh/4GDQC/testspecial.txt
_
Linuxでは_xsel --clipboard
_またはxclip
、OSXではpbcopy
を使用して、関数の出力をクリップボードにパイプすることをお勧めします。
@Jayが提供するソリューションも完全に機能します。
_transfer() {
printf -- "-F filedata=@%s\0" "$@" \
| xargs -0 sh -c \
'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}
_
BashのWord分割を回避する1つの方法は、配列を使用して、エスケープする必要なしに各引数を運ぶことです。
_Push(){ args[${#args[*]}]="$1"; }
build() {
args=()
for file
do Push "-F"
Push "filedata=@$file"
done
}
build "$@"
curl --progress-bar -i "${args[@]}" https://transfer.sh | grep https
_
build
関数は配列args
を作成し、Push
関数は配列の最後に新しい値を追加します。 curl
は単に配列を使用します。
Push
は単にargs+=("$1")
と書くこともできるので、最初の部分は単純化できます。そのため、それを削除してbuild
を次のように変更できます。
_build() {
args=()
for file
do args+=("-F" "filedata=@$file")
done
}
_
この標準的で安全なソリューションを試してください:
transfer() {
printf -- "-F filedata=@%s\0" "$@" | xargs -0 sh -c 'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}
"$@"
二重引用符を使用すると、シェルは元のオプションを変更せずに保持します。 2.5.2特殊パラメータのセクションを参照してください。OpenGroup Base Specifications Issue 6:
@
1から開始して位置パラメータに展開します。展開が二重引用符内で発生し、フィールド分割( Field Splitting を参照)が実行される場合、各位置パラメーターは個別のフィールドとして展開されます。最初のパラメーターの展開は引き続き元のWordの最初の部分と結合され(展開されたパラメーターがWord内に埋め込まれていると想定)、最後のパラメーターの展開は引き続き最後の部分と結合されるという条件付き元の言葉の。定位置パラメーターがない場合、「@」が二重引用符で囲まれている場合でも、「@」を展開するとゼロのフィールドが生成されます。
この方法でprintf
を使用することは、オプションを処理するための標準的な方法です。あなたはそれをテストして、結果が-F filedata=@
オプションの数としての文字列。
名前に特別な文字が含まれる2つのファイルを使用したテスト:
$ ls -b
f\ rst s'cond
$ transfer() { printf -- "-F filedata=@%s\0" "$@" | xargs -0 sh -c 'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop ;}
$ transfer *
######################################################################## 100.0%
https://transfer.sh/fnzuh/f-rst
https://transfer.sh/fnzuh/scond