在脚本中使用 cURL 发布数据

在脚本中使用 cURL 发布数据

我正在尝试编写一个简单的替代脚本来将文件上传到传输文件服务。网站上的一个示例提到了一种在单个“会话”中上传多个文件的方法:

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

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 和OS X 上使用xsel --clipboard或将函数的输出通过管道传输到剪贴板可能是个好主意。xclippbcopy


@Jay 提供的解决方案也非常有效:

transfer() {
  printf -- "-F filedata=@%s\0" "$@" \
    | xargs -0 sh -c \ 
    'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}

答案1

尝试一下这个标准且安全的解决方案:

transfer() {
  printf -- "-F filedata=@%s\0" "$@" | xargs -0 sh -c 'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}

"$@"加双引号将使 shell 保持原来的选项不变。看2.5.2 特殊参数部分来自The Open Group 基本规范第 6 期:

@

扩展到位置参数,从 1 开始。当发生膨胀时双引号内,以及字段分割(参见场分裂)执行时,每个位置参数应扩展为单独的字段,前提是第一个参数的扩展仍应与原始单词的开头部分连接(假设扩展的参数嵌入在单词中),并且最后一个参数的扩展仍应与原始单词的最后部分连接。如果没有位置参数,则“@”的扩展将生成零字段,即使“@”被双引号引起来也是如此。

使用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

答案2

避免 bash 分词的一种方法是使用数组来携带每个参数,而不需要转义:

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
}

相关内容