2

In the below code, I would like to have the output of pyfg() passed exactly as echoed (i.e. with the space between -htns and crl being interpreted literally, not as whitespace, by aoeu()) to aoeu(). Of course, the problem is that in aoeu(), $1 is -htns, $2 is crl, and $3, which I don't want at all in this case, is qjkx. I know this example is thoroughly useless, but the real application to which I'm trying to apply this calls an external program in place of the below aoeu(), so I do need something like what's below.

#!/bin/bash
# pass_space_function.sh

aoeu() {
echo "$1" "$2"
}

pyfg() {
echo "-htns crl" "qjkx"
} 

aoeu $(pyfg)

My running the above outputs:

$ ./pass_space_function.sh
-htns crl

My desired output is:

$ ./pass_space_function.sh
-htns crl qjkx

To be clear, I do understand exactly why my code isn't working, but that about which I'm not so sure is how to make it do what I want it to do.

EDIT:

#!/bin/bash

aoeu() {
echo 1:"$1" 2:"$2" 3:"$3"
}

pyfg() {
# These variables might be user-provided.
wvz="/usr/lib/scarychacacters_\"@#$:%^&:*(){}[]; a o ;u ;::e i y f.so.4"
bm="/space space space"
snt="/var/cache/normalpath"

printf "%q %q %q" "$wvz" "$bm" "$snt" 
} 

aoeu $(pyfg)

That code returns, for me, 1:/usr/lib/scarychacacters_\"@#\$:%\^\&:\*\(\)\{\}\[\]\;\ 2:a\ 3:o\. It's obviously splitting at the whitespace in $wvz.

catlover2
  • 251
  • 2
  • 15
  • Why doesn't `"-htns crl qjkx"` work for you (and for aoeu: `echo "$1 $2"`)? – Axel Nov 06 '13 at 12:08
  • Called as `someprogram -htns crl qjkx` from the command line, `someprogram` will receive `-htns` as `$1`, `crl` as `$2`, and `qjkx` as `$3`. It's not clear why you are trying to make `$1` include `-htns` and `crl`. Could you post the actual command you are trying to call in place of `aoeu`? – chepner Nov 06 '13 at 13:35
  • @chepner: the command in place of `aoeu` is another bash script; `-htns` stands for an option, `crl` stands for its argument, and `qjkx` stands for an argumentless option. – catlover2 Nov 06 '13 at 18:30
  • Arguments beginning with hyphens are not special. As far as `bash` is concerned, it passes three separate arguments to `someprogram`, and it is up to `someprogram` to treat `-htns` as an option which is further configured using the following argument. – chepner Nov 06 '13 at 18:36
  • I think I found a solution – thom Nov 07 '13 at 00:26

2 Answers2

2

The key to correct quoting lies in the understanding what happens.

That echo "-htns crl" "qjkx" for example will print just a byte stream to its stdout, so it will be just -htns crl qjkx in the end. The information that -htns crl were grouped more closely than qjkx is lost.

To avoid this loss you can use printf "%q":

pyfg() {
  printf "%q %q" "-htns crl" "qjkx"
}

This will generate quoted output: -htns\ crl qjkx which means to the shell the same as "-htns crl" "qjkx" (whether the space is escaped with a backslash or quoted with double quotes does not make a difference).

The next aspect is the use of $() to pass the output of one program to the next.

The typical way is to put that in double quotes:

aoeu "$(pyfg)"

This way everything is passed without interpretation which is desirable in most cases.

In your case, however, you might want to make the output of pyfg quoted instead of quote the output of pyfg; notice the important difference: The first means that pyfg produces quoted output (as shown above), the second means that pyfg produces output which gets quoted later. The second does not help if the output of pyfg already lost the information which parts belong together.

If you now just leave away the double quotes, the output unfortunately just gets split at the spaces (i. e. first character of $IFS) even if this space is escaped with a backslash. So, instead, you need to use eval in this case to force the shell to interpret the value of $(pyfg) with the normal shell evaluation mechanism:

eval aoeu "$(pyfg)"
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • Having inserted your printf command in the script, '-htns\' is `$1` and `crl` is still `$2`. I also realize I should have asked: what if the string I want to echo in place of `crl` is a variable, an arbitrary pathname, that could contain all sorts of dangerous characters? – catlover2 Nov 06 '13 at 18:48
  • Please update the question accordingly and explain in more detail what you now do to get the `-htns\` in $1 (I didn't quite understand that part). If you've got the values in variables, that's what the whole idea of `%q` is meant for of course: `printf "%q %q" "$var1" "$var2"` You need those double quotes around the variables. Maybe you just forgot those, then `$var1` might evaluate to two separate arguments already. – Alfe Nov 07 '13 at 12:22
  • See the example at the end of my question. – catlover2 Nov 07 '13 at 23:39
  • Sorry, I forgot that in this case you have to `eval` the expression once more; without it just a plain splitting at spaces is performed. See the edited answer. – Alfe Nov 08 '13 at 09:40
  • Thank you; that works. I would appreciate more of an explanation of `eval` because I can't quite make sense of its explanation in the bash manpage. It doesn't seem to mention that the output of the command that is being `eval`ed is changed, yet that seems to me its only effect here. – catlover2 Nov 08 '13 at 10:12
  • 1
    `eval` takes its string arguments, concatenates them with a space as separator and then evaluates the resulting string. Evaluation means that this string is handled as if it would be standing at this place directly, so all the shell syntax stuff like double quotes etc. are taken into account once more, and the resulting command is executed. The command string `aoeu "a b" "c" "d"` for example is hereby split into the parts `aoeu`, `a b`, `c`, and `d` because of the correct quoting. After splitting the command (`aoeu`) is executed and the rest is passed as arguments. – Alfe Nov 08 '13 at 10:54
1

EDIT: This works

#!/bin/bash
# pass_space_function.sh

aoeu() {
echo $1 x $2
}

pyfg() {
echo "'-htns crl' 'qjkx'"
}


eval aoeu $(pyfg)
thom
  • 2,294
  • 12
  • 9