How to pass bash script arguments to a subshell

I have a wrapper script that does some work and then passes the original parameters on to another tool:

#!/bin/bash
# ...
other_tool -a -b "$@"

This works fine, unless the "other tool" is run in a subshell:

#!/bin/bash
# ...
bash -c "other_tool -a -b $@"

If I call my wrapper script like this:

wrapper.sh -x "blah blup"

then, only the first orginal argument (-x) is handed to "other_tool". In reality, I do not create a subshell, but pass the original arguments to a shell on an Android phone, which shouldn't make any difference:

#!/bin/bash
# ...
adb sh -c "other_tool -a -b $@"

Bash's printf command has a feature that'll quote/escape/whatever a string, so as long as both the parent and subshell are actually bash, this should work:

[Edit: as siegi pointed out in a comment, if you do this the obvious way there's a problem when no arguments are supplied, where it acts like there actually was a single empty argument. I've added a workaround below, wrapping the format string with ${1+}, which only includes the format string if the first argument is defined. It's a bit of a kluge, but it does work.]

#!/bin/bash

quoted_args="$(printf "${1+ %q}" "$@")" # Note: this will have a leading space before the first arg
# echo "Quoted args:$quoted_args" # Uncomment this to see what it's doing
bash -c "other_tool -a -b$quoted_args"

Note that you can also do it in a single line: bash -c "other_tool -a -b$(printf "${1+ %q}" "$@")"


None of the solutions work well. Just pass x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\" as parameter and they fail.

Here is a simple wrapper that handles every case. Note how it escapes each argument twice.

#!/usr/bin/env bash
declare -a ARGS
COUNT=$#
for ((INDEX=0; INDEX<COUNT; ++INDEX))
do
    ARG="$(printf "%q" "$1")"
    ARGS[INDEX]="$(printf "%q" "$ARG")"
    shift
done

ls -l ${ARGS[*]}