user1677770
user1677770

Reputation: 63

Capturing verbatim command line (including quotes!) to call inside script

I'm trying to write a "phone home" script, which will log the exact command line (including any single or double quotes used) into a MySQL database. As a backend, I have a cgi script which wraps the database. The scripts themselves call curl on the cgi script and include as parameters various arguments, including the verbatim command line.

Obviously I have quite a variety of quote escaping to do here and I'm already stuck at the bash stage. At the moment, I can't even get bash to print verbatim the arguments provided:

Desired output:

$ ./caller.sh -f -hello -q "blah"
-f hello -q "blah"

Using echo:

caller.sh:

echo "$@"

gives:

$ ./caller.sh -f -hello -q "blah"
-f hello -q blah

(I also tried echo $@ and echo $*)

Using printf %q:

caller.sh:

printf %q $@
printf "\n"

gives:

$ ./caller.sh -f hello -q "blah"
-fhello-qblah

(I also tried print %q "$@")

I would welcome not only help to fix my bash problem, but any more general advice on implementing this "phone home" in a tidier way!

Upvotes: 3

Views: 1529

Answers (4)

that other guy
that other guy

Reputation: 123460

You can get this info from the shell history:

function myhack {
  line=$(history 1)
  line=${line#*  }
  echo "You wrote: $line"
}
alias myhack='myhack #'

Which works as you describe:

$ myhack --args="stuff" * {1..10}    $PATH
You wrote: myhack --args="stuff" * {1..10}    $PATH

However, quoting is just the user's way of telling the shell how to construct the program's argument array. Asking to log how the user quotes their arguments is like asking to log how hard the user punched the keys and what they were wearing at the time.

To log a shell command line which unambiguously captures all of the arguments provided, you don't need any interactive shell hacks:

#!/bin/bash
line=$(printf "%q " "$@")
echo "What you wrote would have been indistinguishable from: $line"

Upvotes: 2

l0b0
l0b0

Reputation: 58788

@Flimm is correct, there is no way to distinguish between arguments "foo" and foo, simply because the quotes are removed by the shell before the program receives them. What you need is "$@" (with the quotes).

Upvotes: 0

mcoolive
mcoolive

Reputation: 4205

I understand you want to capture the arguments given by the caller.

Firstly, quotes used by the caller are used to protect during the interpretation of the call. But they do not exist as argument.

An example: If someone call your script with one argument "Hello World!" with two spaces between Hello and World. Then you have to protect ALWAYS $1 in your script to not loose this information.

If you want to log all arguments correctly escaped (in the case where they contains, for example, consecutive spaces...) you HAVE to use "$@" with double quotes. "$@" is equivalent to "$1" "$2" "$3" "$4" etc.

So, to log arguments, I suggest the following at the start of the caller:

i=0
for arg in "$@"; do
    echo "arg$i=$arg"
    let ++i
done

## Example of calls to the previous script
#caller.sh '1' "2" 3 "4 4" "5  5"
#arg1=1
#arg2=2
#arg3=3
#arg4=4 4
#arg5=5  5

Upvotes: 1

Flimm
Flimm

Reputation: 150603

There is no possible way you can write caller.sh to distinguish between these two commands invoked on the shell:

./caller.sh -f -hello -q "blah"
./caller.sh -f -hello -q blah

There are exactly equivalent.

If you want to make sure the command receives special characters, surround the argument with single quotes:

./caller.sh -f -hello -q '"blah"'

Or if you want to pass just one argument to caller.sh:

./caller.sh '-f -hello -q "blah"'

Upvotes: 5

Related Questions