【问题标题】:curl: (26) couldn't open file when getting command arguments from a stringcurl: (26) 从字符串获取命令参数时无法打开文件
【发布时间】:2019-12-31 12:34:43
【问题描述】:

我正在尝试构建一个返回 curl 参数的函数:

get_common_curl_headers() {
  local crash_path="$1"

  echo -n \
    "-H 'Accept: text/plain'" \
    "-F 'model=<$crash_path/model'" \
    "-F 'version=<$crash_path/fw_version'"

  echo -n ' '
}

我希望将返回的文件路径引用为$crash_path 变量可能包含空格。然后我像这样使用这个函数:

send_app_crash() {
  local crash_path="$1"
  local common_headers

  common_headers=$(get_common_curl_headers "$crash_path")
  [ $? -ne 0 ] && return 1

  curl -vk -X POST \
    $common_headers\
    "$SERVER_URL"
}

我在这里使用未引用的变量$common_headers,因为我希望扩展参数,据我了解应该可以。但是,该命令会导致此错误:

curl: (6) Couldn't resolve host 'text'
curl: (26) couldn't open file "/tmp//model'"

奇怪的是,文件名末尾有一个额外的单引号,应该已被 shell 删除。我也不确定第一个错误是什么意思,因为 Accept: text/plain 似乎被正确引用(见下文)。

手动运行命令(即在命令出现在脚本中之前执行 set -x 并将其通过管道传送到 sh)按预期工作:

curl -vk -X POST -H 'Accept: text/plain' -F 'model=</tmp//model' -F 'version=</tmp//fw_version' 192.168.2.100:8888

【问题讨论】:

    标签: shell curl


    【解决方案1】:

    如何调试

    只需在 curl 命令之前添加 set -x,您就会看到:

    curl -vk -X POST -H ''\''Accept:' 'text/plain'\''' -F ''\''model=<some-crash-path/model'\''' -F ''\''version=<some-crash-path/fw_version'\''' example.com
    

    现在让我们做一些魔术:

    $ printf "%s\n" curl -vk -X POST -H ''\''Accept:' 'text/plain'\''' -F ''\''model=<some-crash-path/model'\''' -F ''\''version=<some-crash-path/fw_version'\''' example.com
    curl
    -vk
    -X
    POST
    -H
    'Accept:
    text/plain'
    -F
    'model=<some-crash-path/model'
    -F
    'version=<some-crash-path/fw_version'
    example.com
    

    printf 命令将每个参数打印在单独的行中,这可以帮助您看到,而不是例如Accept: test/plain,您生成了 2 个参数:'Accept:text/plain'

    所以 curl 将其视为-H 'Accept:(即标头以' 开头并且不包含预期值),并且下一个参数是text/plain',它没有任何选项前缀,因此curl认为它是URL,这就是为什么它说 text 不是主机。

    它还解释了为什么找不到/tmp/model'(末尾有')。

    如何解决

    而不是使用

    curl -vk -X POST $common_headers "$SERVER_URL"
    

    只需添加eval:

    eval curl -vk -X POST $common_headers "$SERVER_URL"
    

    出于您的目的,eval 等同于通过管道连接到sh

    正确的方法

    我通常建议不要将eval(或通过管道连接到sh)用于除一次性代码之外的任何内容。

    否则你会怎么做?

    选项 1 - 内联 get_common_curl_headers() 内联 send_app_crash()。这就是我更喜欢做的 - 引入包装命令的函数,而不是在单独的函数中生成参数。

    send_app_crash() {
        local crash_path="$1"
        curl -vk \
            -X POST \
            -H "Accept: text/plain" \
            -F "model=<$crash_path/model" \
            -F "version=<$crash_path/fw_version" \
            "$SERVER_URL"
    }
    

    选项 2 - 在全局数组中生成参数:

    get_common_curl_headers() {
        local crash_path="$1"
    
        COMMON_HEADERS=( \
            -H "Accept: text/plain" \
            -F "model=<$crash_path/model" \
            -F "version=<$crash_path/fw_version" \
        )
    }
    
    send_app_crash() {
        local crash_path="$1"
        local common_headers
    
        get_common_curl_headers "$crash_path"
        [ $? -ne 0 ] && return 1
    
        curl -vk -X POST "${COMMON_HEADERS[@]}" "$SERVER_URL"
    }
    

    注意数组解引用的引用,注意不能返回数组。

    这种方法的主要问题是它使用全局变量,这就是它不太可取的原因。

    【讨论】:

      猜你喜欢
      • 2014-07-06
      • 2021-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-24
      • 1970-01-01
      • 2016-10-06
      • 2012-03-15
      相关资源
      最近更新 更多