Bash, curl, eval and whitespace

独自空忆成欢 提交于 2020-02-05 02:38:33

问题


I'm writing a script to execute CURL commands for a given user input. The script has multiple helper function to create the list of parameters (arguments) that will eventually be passed to CURL.

A stripped out example, is as follows :

#!/bin/bash
function create_arg_list
{
    # THIS HTTP HEADER VALUE COMES FROM THE USER and MAY CONTAIN SPACES
 local __hdr="x-madhurt-test:madh urt"
 local __params="http://google.co.in -X GET -H '$__hdr'"

 local __resultvar="$1"
 eval $__resultvar="\"$__params\""
 echo "params after eval : $__resultvar"
}


create_arg_list arg_list
echo "params after eval in main code : $arg_list"

echo "Running command : curl -v $arg_list"
curl -v $arg_list

The script works great when the input parameters (file path, url etc..) have (quoted) white space in them. However, when the arguments that are supposed to be passed as HTTP Headers to CURL contain spaces, the script fails miserably.

Here is what I've tried :

  1. Use single quotes around the header value (e.g. '$__hdr'). However, with this the value that is passed to CURL is :
    curl -v http://google.co.in -X GET -H 'x-madhurt-test:madh urt'
    , which is treated as-as by CURL and the actual header that is sent is like this :
    'x-madhurt-test:madh
  2. Double escape the header value (e.g. \\"$__hdr\\"), but this does seem to work as well. In this case CURL gets "urt" as a separate parameter and treats it as a URL
    curl: (6) Couldn't resolve host 'urt"'
  3. Escape the space in the header value (i.e. use "madh\ urt" instead of "madh urt"), but this turns out to be the same as option 2.

Does someone have insights as to what is happening wrong here?


回答1:


This code works but it's not intended to use as-is. I'm posting it to give you some ideas for how you might proceed. They key to making what you want to do work is to use an array. Unfortunately, Bash can't return arrays from functions. What you probably ought to do is use a global array. The code below, however, creates a string out of a declare statement and passes that through your indirect variable. It's a seriously bad kludge.

#!/bin/bash
function create_arg_list
{
    # THIS HTTP HEADER VALUE COMES FROM THE USER and MAY CONTAIN SPACES
 local __hdr="x-madhurt-test:madh urt"
 local __params="http://google.co.in -X GET -H"
 __params=($__params "$__hdr")

 local __resultvar="$1"
 eval $__resultvar=$(printf "%q" "$(declare -p __params)")
 echo "params after eval : $__resultvar"
}

create_arg_list arg_list
echo "params after eval in main code : $arg_list"

echo "Running command : curl -v $arg_list"

eval $arg_list

curl -v "${__params[@]}"



回答2:


Dennis's answer is good, so I'll focus on why your code does not work. Let's use an auxiliar function to show the arguments received by a function:

$ args() { for x in "$@"; do echo $x; done }
$ args 1 '2 b' 3
1
2 b
3

Ok, as expected the quotes are solely used to delimit arguments. But now let's use a variable as you did:

$ var="1 '2 b' 3"
$ args $var
1
'2
b'
3

Bash expands the variable and the function (or command) gets the quotes. That's not what you wanted, of course.

Solution1: Use eval to re-interpret quotes.

$ eval args $var
1
2 b
3

Solution2: Use an array and expand it with "${MYARRAY[@]}" as Dennis showed.

More ideas: a technique I've sometimes used is doing the eval outside:

$ eval "$(create_args_list VARNAME)"

In this case otherfun would return a string that, when evaled, would create a variable with name VARNAME (that can be even local). This variable (or variables, if needed) can be string or arrays. Again, I'd use an array so it can be easily used afterwards:

$ curl "${VARNAME[@]}"


来源:https://stackoverflow.com/questions/3187614/bash-curl-eval-and-whitespace

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!