views:

120

answers:

2

I call my script:

$ ./script 'a!#*`*&
^$' "sdf sdf\"qw sdsdf" 1 -- 2 3

It gets arguments:

1: a!#*`*&
^$
2: sdf sdf"qw sdsdf
3: 1
4: --
5: 2
6: 3

If I need to call something with the same arguments locally, I do this:

someprogram "$@"

But how can I put all that array to a string (to store in file or in environment variable or pass over TCP eaisly) and then turn it back to command line arguments somewhere? I want it to be simple, short and secure.

export CMDLINE="$@"
# What is in CMDLINE now? Escaped or not?

sh -c "someprogram $CMDLINE"
# Will it do what I mean?

Ideally I want two programs: the first turns turns command line paramerets into a [a-zA-Z0-9_]* string, the other turns it back to command-line parameters I can use.

Update: written 2 versions of pairs of scripts. Which is more useful?

+1  A: 

Created two scripts: one serializes it's arguments to a [a-ZA-Z0-9=_]* strings http://vi-server.org/vi/bin/serialize.sh, other starts this command line (with optional prepended arguments) http://vi-server.org/vi/bin/deserialize.sh.

Serialize:

#!/bin/bash
n=$#;
for ((i=0; i<$n; ++i)); do
    if [ -z "$1" ]; then
        echo 1
    else
        printf '%s' "$1" | base64 -w 0
        echo
    fi
    shift
done | tr '\n' '_'
echo -n "0"

Deserialize:

#!/bin/bash
if [ -z "$1" ]; then
    echo "Usage: deserialize data [optional arguments]"
    echo "Example: \"deserialize cXFx_d3d3_0 eee rrr\""
    echo "    will execute \"eee rrr qqq www\""
    exit 1;
fi

DATA="$1"; shift

i=0

for A in ${DATA//_/' '}; do
    if [ "$A" == "0" ]; then 
    break;
    fi              
    if [ "$A" == "1" ]; then 
    A=""
    fi              
    ARR[i++]=`base64 -d <<< "$A"`
done
exec "$@" "${ARR[@]}"

Example:

deserialize `serialize qqq www` echo
Vi
You can do `ARR+=($(echo -n "$A" | base64 -d))` or `ARR[i++]=$(echo -n "$A" | base64 -d)` and eliminate the intermediate variable and the `i=$((i+1))` (which could have been `((i++))`, by the way). Also, you can eliminate `tr` by doing `DATA=${DATA//_/$'\n'}` and the reverse. This: `echo -en "\n"` can be replaced by this: `echo`
Dennis Williamson
@Dennis Williamson There were 2 more serious problems: 1. http://stackoverflow.com/questions/3070246/how-to-print-value-of-a-variable-which-can-be-n-or-e-in-bash 2. loop in deserializer forked out with no chance for launched program to read stdin. Now will apply your advices.
Vi
@Dennis Williamson No, `ARR[i++]` doesn't work. We need `ARR[$((i++))]`.
Vi
Fixed aforementioned problems, waiting for another ones.
Vi
What version of Bash? `ARR[i++]` *does* work at least since version 2.04.
Dennis Williamson
`GNU bash, version 4.0.33(1)-release (i486-pc-linux-gnu)`
Vi
@Dennis Williamson BTW do current scripts work for you?
Vi
The reason that `ARR[i++]` isn't working is that you needlessly declared `ARR` to be an associative array so it thinks that the literal "i++" is an index instead of the value of `i` (then post increment it). If you want to declare a regular array use `-a`, but it's not necessary to declare it. In the first script, this line: `echo 1` will never be executed. As a result the corresponding part of the second script won't either. I was pleasantly surprised to see that this worked: `deserialize $(serialize abc def ghi '123 456' "hello goodbye" 111 zzz) printf "%s\n"`
Dennis Williamson
@Dennis Williamson, 1. OK, removing declaration, 2. `echo 1` is to handle empty arguments: `serialize 1 2 '' 3 '' ''`
Vi
@Vi: Ah, I missed that.
Dennis Williamson
@Dennis Williamson, Looks like Perl version is much smaller.
Vi
A: 

Incompatible with Bash script in the other answer

Script to serialize command-line arguments to [a-zA-Z0-9=_]* string: http://vi-server.org/vi/bin/serialize

#!/usr/bin/perl
use MIME::Base64;
map {print encode_base64($_,'')."_" } @ARGV;

Script to deserialize it back (optionally prepending other arguments): http://vi-server.org/vi/bin/deserialize

#!/usr/bin/perl
use MIME::Base64;
if($#ARGV<0) {
    print << "EOF";
Usage: deserialize data [optional prepended arguments]
Example: deserialize \$(serialize 3 4 " 5 " "" "'6'" '`8`') echo 1 2
EOF
    exit
}
my @A = map {decode_base64($_)} split '_', shift @ARGV;
exec (@ARGV,@A);

Also there are similar scripts for environment: http://vi-server.org/vi/bin/envserialize http://vi-server.org/vi/bin/envdeserialize

Vi