web-dev-qa-db-ja.com

スクリプトでcURLを使用してデータを投稿する

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で説明されているように、配列を使用することで問題を解決することができました。 bashzshの両方で機能するソリューションは次のとおりです。

_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
}
_

zshbashの両方の出力は次のとおりです。

_$ 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
}
_
3
Bruno

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
}
_
2
meuh

この標準的で安全なソリューションを試してください:

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
3
Jay jargot