I am using a bash script that expects command line arguments preceded by a dash followed by a file name:
./script -abc file_name
In the script, I try to print out all of the arguments to the script using:
echo "$#"
which displays:
-abc file_name
which is expected. Everything prints fine except for when I use the letter "n" by itself, i.e.:
./script -n file_name
The resulting echo statement inside the script only prints out the file_name argument and not the "-n" argument at all. This behavior only occurs with the letter "n" by itself. When I try "-nx" or 'n' with any other letter(s) the echo statement prints out fine...
Why does this echo statement not work with "-n" by itself?
The -n or -e are valid options for the echo command.
Therefore it's consumed by the echo command and only the remaining text is shown.
That's a known problem of echo and to avoid this you can use printf.
printf "%s\n" "$*"
Some interesting behaviour here:
when
$ set -- -n foo
then you have observed that you don't see the "-n" argument, nor the trailing newline:
$ echo "$#" | od -c
0000000 f o o
0000003
but using the "$*" parameter expansion does give the expected results:
$ echo "$*" | od -c
0000000 - n f o o \n
0000007
Without reading the source code, I suspect that when echo is given only a single argument, it knows there are no options to be parsed.
Nevertheless, use printf so you have complete control over your output.
Related
I've looked at the similar posts about this problem, but cannot figure out how to get the executed code to be in the correct format, which needs to be foo --bar "a='b'". My best attempt at this was
#!/bin/bash -x
bar='--bar ''"''a='"'"'b'"'"'"'
cmd=(foo $bar)
echo ${cmd[#]}
eval ${cmd[#]}
The output from this is correct for the echo, but incorrect for eval
+ bar='--bar "a='\''b'\''"'
+ cmd=(foo $bar)
+ echo foo --bar '"a='\''b'\''"'
foo --bar "a='b'"
+ eval foo --bar '"a='\''b'\''"'
++ foo --bar 'a='\''b'\'''
What is the correct way to execute the command with the option?
If you must store command fragments, use functions or arrays, not strings.
An example of best-practice code, in accordance with BashFAQ #50:
#!/usr/bin/env bash
bar=( --bar a="b" )
cmd=(foo "${bar[#]}" )
printf '%q ' "${cmd[#]}" && echo # print code equivalent to the command we're about to run
"${cmd[#]}" # actually run this code
Bonus: Your debug output doesn't prove what you think it does.
"a='b'" and 'a='\''b'\''' are two different ways to quote the exact same string.
To prove this:
printf '%s\n' "a='b'" | md5sum -
printf '%s\n' 'a='\''b'\''' | md5sum -
...emits as output:
7f183df5823cf51ec42a3d4d913595d7 -
7f183df5823cf51ec42a3d4d913595d7 -
...so there's nothing at all different between how the arguments to echo $foo and eval $foo are being parsed in your code.
Why is this true? Because syntactic quotes aren't part of the command that's actually run; they're removed by the shell after it uses them to determine how to interpret a command line character-by-character.
So, let's break down what set -x is showing you:
'a='\''b'\'''
...consists of the following literal strings concatenated together:
a= (in a single-quoted context that is entered and ended by the single quotes surrounding)
' (in an unquoted context, escaped by the backslash that precedes it)
b (in a single-quoted context that is entered and ended by the single quotes surrounding)
' (in an unquoted context)
...everything else is syntactic, meaningful to the shell but not ever passed to the program foo.
If you want exactly the same expansion to happen as in echo ${cmd[#]}, just run the command then:
${cmd[#]}
It will execute:
+ foo --bar '"a='\''b'\''"'
Note that because it is unquoted, for example * will be expanded according to filename expansion.
Echoing without quotes... 1 line. Fine.
$ echo $(ls -1dmb /bin/*) > test
$ wc -l test
1 test
Echoing with quotes... 396 lines. Bad.
$ echo "$(ls -1dmb /bin/*)" > test
$ wc -l test
396 test
The problem comes when using echo for writing a file and expanding a long variable.
Why does this happen? How to fix it?
ls is detecting that your stdout is not a terminal.
check the output of ls -1dmb /bin/* | cat vs ls -1dmb /bin/*. It's ls, who is splitting the output.
Similarly, for ls --color=auto case, color option is used, based on whether the stdout is terminal or not.
When quotes are used, echo is provided with a single arguments, which has embedded newlines, spaces, which are echoed as-is to file.
When quotes are not used, echo is provided multiple arguments, which are split by the IFS. Thus echo prints all of them in a single line.
But, don't skip these quotes...
How to fix it:
I think, the splitting always occurs at the end of some file name & never in between a filename. So one of these 2 options may work for you:
ls -1dmb /bin/* | tr '\n' ' ' >test
ls -1dmb /bin/* | tr -d '\n' >test
#anishsane correctly answers the topic question (that ls is doing the wrapping and ways to remove them) and covers the quoting issue as well but the quoting issue is responsible for the line count difference and not ls.
The issue here is entirely one of quoting and how command lines, echo, and command substitution all work.
The output from "$(ls ...)" is a single string with embedded newlines protected from the shell via the quotes. Hand that value to echo and echo spits it back out literally (with the newlines).
The output from $(ls ...) is a string that is unprotected from the shell and thus undergoes word splitting and whitespace normalization. Command substitution cannot terminate your command line early (you wouldn't want echo $(ls -1) in a directory with two files to run echo first_file; second_file would you?) the newlines are left as word separators between the arguments to echo. The shell then word splits the result on whitespace (including newlines) and gives echo a list of arguments at which point echo happily executes echo first_file second_file ... which, as you can guess, only outputs a single line of output.
Try this to see what I mean:
$ c() {
printf 'argc: %s\n' "$#";
printf 'argv: %s\n' "$#"
}
$ ls *
a.sh b.sh temp
$ ls -1dmb *
a.sh, b.sh, temp
$ c "$(ls -1dmb *)"
argc: 1
argv: a.sh, b.sh, temp
$ c $(ls -1dmb *)
argc: 3
argv: a.sh,
argv: b.sh,
argv: temp
The question is simple. I want to evaluate current value of PS1 in my bash script.
All materials on google point to tutorials on pimping it up, but I want to evaluate to see how would it be rendered by my current terminal, or at least by some terminal.
Is there any software/function that would help me achieve that? Of course I'd like to have all escaped characters evaluated, so echo $PS1 is not that useful in my case.
Bash 4.4+ solution using parameter transformation for a prompt string: echo "${PS1#P}"
[adamhotep#tabasco ~]$ echo "the prompt is '${PS1#P}'"
the prompt is '[adamhotep#tabasco ~]$'
[adamhotep#tabasco ~]$ TEST_STRING='\u is dining at \t using \s \V'
[adamhotep#tabasco ~]$ echo "${TEST_STRING}"
\u is dining at \t using \s \V
[adamhotep#tabasco ~]$ echo "${TEST_STRING#P}"
adamhotep is dining at 21:45:10 using bash 5.0.3
[adamhotep#tabasco ~]$
From the Bash Reference Manual page on Shell Parameter Expansion:
${parameter#operator}
Parameter transformation. The expansion is either a transformation of the value of parameter or information about parameter itself, depending on the value of operator.
Each operator is a single letter:
Q The expansion is a string that is the value of parameter quoted in a
format that can be reused as input.
E The expansion is a string that is the value of parameter with backslash
escape sequences expanded as with the $'…' quoting mechanism.
P The expansion is a string that is the result of expanding the value of
parameter as if it were a prompt string (see PROMPTING below).
A The expansion is a string in the form of an assignment statement or
declare command that, if evaluated, will recreate parameter with its
attributes and value.
a The expansion is a string consisting of flag values representing
parameter's attributes.
If parameter is # or *, the operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with # or *, the operation is applied to each member of the array in turn, and the expansion is the resultant list.
(See also this answer from duplicate question Echo expanded PS1.)
Z Shell (zsh) can do this with ${(%%)PS1} or with its print builtin's -P flag:
[adamhotep#tabasco ~]% echo "the prompt is '${(%%)PS1}'"
the prompt is '[adamhotep#tabasco ~]%'
[adamhotep#tabasco ~]% print -P "the prompt is '$PS1'"
the prompt is '[adamhotep#tabasco ~]%'
[adamhotep#tabasco ~]% TEST_STRING="%n is dining at %* using %N $ZSH_VERSION"
[adamhotep#tabasco ~]% echo "$TEST_STRING"
%n is dining at %* using %N 5.7.1
[adamhotep#tabasco ~]% echo "${(%%)TEST_STRING}"
adamhotep is dining at 11:49:01 using zsh 5.7.1
[adamhotep#tabasco ~]% print -P "$TEST_STRING"
adamhotep is dining at 11:49:07 using zsh 5.7.1
[adamhotep#tabasco ~]%
The Zsh Expansion and Subsitution manual tells us:
Parameter Expansion Flags. If the opening brace is directly followed by an opening parenthesis, the string up to the matching closing parenthesis will be taken as a list of flags. In cases where repeating a flag is meaningful, the repetitions need not be consecutive; for example, (q%q%q) means the same thing as the more readable (%%qqq). The following flags are supported:…
% Expand all % escapes in the resulting words in the same way as in
prompts (see Prompt Expansion). If this flag is given twice, full
prompt expansion is done on the resulting words, depending on the
setting of the PROMPT_PERCENT, PROMPT_SUBST and PROMPT_BANG options.
From the Zsh Builtins documentation for print:
-P Perform prompt expansion (see Prompt Expansion). In combination with -f, prompt escape sequences are parsed only within interpolated arguments, not within the format string.
I would get it like this:
echo $PS1
And then edit it with an editor. After that for the test (this is set while the session is active):
PS1='\[\033[1m\]\[\033[34m\]\u\[\033[90m\]#\[\033[01;35m\]\h:\[\033[01;32m\]\W\[\033[0m\]$ '
(\u is for user, \h is for host, \w is for full path and \W is for short path)
And if I like it I will make it permanent by changing the value of PS1 in ~/.bashrc
P.S.:
If you want to see all global variables:
printenv
OR:
printenv <name_of_var_to_see>
One more possibility, using script utility (part of bsdutils package on ubuntu):
$ TEST_PS1="\e[31;1m\u#\h:\n\e[0;1m\$ \e[0m"
$ RANDOM_STRING=some_random_string_here_that_is_not_part_of_PS1
$ script /dev/null <<-EOF | awk 'NR==2' RS=$RANDOM_STRING
PS1="$TEST_PS1"; HISTFILE=/dev/null
echo -n $RANDOM_STRING
echo -n $RANDOM_STRING
exit
EOF
<prints the formatted prompt properly here>
script command generates a file specified & the output is also shown on stdout. If filename is omitted, it generates a file called typescript.
Since we are not interested in the log file in this case, filename is specified as /dev/null. Instead the stdout of the script command is passed to awk for further processing.
The entire code can also be encapsulated into a function.
Also, the output prompt can also be assigned to a variable.
This approach also supports parsing of PROMPT_COMMAND...
EDIT:
It appears that the new version of script echoes the piped stdin in the typescript. To handle that, the above mechanism can be changed to:
$ TEST_PS1="\e[31;1m\u#\h:\n\e[0;1m\$ \e[0m"
$ RANDOM_STRING=some_random_string_here_that_is_not_part_of_PS1
$ script /dev/null <<-EOF | awk '{old=current; current=$0;} END{print old}' RS=$RANDOM_STRING
PS1="$TEST_PS1"; HISTFILE=/dev/null
alias $RANDOM_STRING=true
$RANDOM_STRING
$RANDOM_STRING
EOF
<prints the formatted prompt properly here>
Explanation:
Try entering these commands manually on the terminal. Copy these commands under the heredoc as they are and paste with mouse middle click. The script command's stdout would contain something very similar.
e.g. With above case, the output of the script command gives this:
PS1="\e[31;1m\u#\h:\n\e[0;1m$ \e[0m"; HISTFILE=/dev/null
alias some_random_string_here_that_is_not_part_of_PS1=true
some_random_string_here_that_is_not_part_of_PS1
some_random_string_here_that_is_not_part_of_PS1
\e[0m"; HISTFILE=/dev/nullhsane-dev : ~/Desktop $ PS1="\e[31;1m\u#\h:\n\e[0;1m$
anishsane#anishsane-dev:
$ alias some_random_string_here_that_is_not_part_of_PS1=true
anishsane#anishsane-dev:
$ some_random_string_here_that_is_not_part_of_PS1
anishsane#anishsane-dev:
$ some_random_string_here_that_is_not_part_of_PS1
anishsane#anishsane-dev:
$ exit
Split that stdout with "some_random_string_here_that_is_not_part_of_PS1" as delimiter (record separator of awk) and print the last but one record.
EDIT2:
Another mechanism (using bash source code and gdb):
$ gdb -batch -p $$ -ex 'call bind_variable("expanded_PS1", decode_prompt_string (get_string_value ("PS1")), 0)'
$ echo "$expanded_PS1"
<prints the formatted prompt properly here>
There is a tiny issue here though. The \[ or \] strings in PS1 will get printed as \1/\2 respectively. You can remove those with tr -d '\1\2' <<< "$expanded_PS1"
If you get error like gdb failed to attach to the process (seems to happen in ubuntu :-\ ), run gdb with sudo.
Another way to do this would be to eval echoing your prompt to handle any expansion (was not sure why the brackets remain). This is most likely less robust than #anishsane's method, but may be a little quicker:
show-prompt() {
eval 'echo -en "'$PS1'"' | sed -e 's#\\\[##g' -e 's#\\\]##g'
}
# To show it in a function registered with `complete -F` on
# a single tab, and keep the user's input:
show-prompt
echo -n "${COMP_WORDS[#]}"
Had tinkered with that regarding this GitHub issue.
Try the below command
echo $PS1 |
sed -e s/'\\d'/"$(date +'%a %b %_d')"/g |
sed -e s/'\\t'/"$(date +'%T')"/g |
sed -e s/'\\#'/"$(date +'%r')"/g |
sed -e s/'\\T'/"$(date +'%r'| awk {'print $1'})"/g |
sed -e s/'\\e'//g | sed -e s/'\\h'/"$HOSTNAME"/g |
sed -e s/'\\h'/"$HOSTNAME"/g |
sed -e s/'\\H'/"$HOSTNAME"/g |
sed -e s/'\\u'/"$USER"/g |
sed -e s#'\\W'#"$(pwd)"#g |
sed -e s/'\\w'/"$(pwd | sed -e s#$HOME#'~'#g )"/g |
sed -e s/"\\\\"//g |
sed -e s/"\\["//g |
sed -e s/"\\]"/*/g |
cut -d'*' -f2 |
cut -d';' -f2 |
sed s/\ //g |
sed -e s/[a-z]$/"$([ "$USER" != "root" ] && echo \$ || echo \#)"/g
Edit the /etc/bashrc file
you can use this as example and check the output
# If id command returns zero, you’ve root access.
if [ $(id -u) -eq 0 ];
then # you are root, set red colour prompt
PS1="\\[$(tput setaf 1)\\]\\u#\\h:\\w #\\[$(tput sgr0)\\]"
else # normal
PS1="[\\u#\\h:\\w] $"
fi
How to print "-n", "-e" or "-neeenen" from bash (without a newline at the end, without invoking of external programs)?
Q="-n"
echo -n "$Q" # fail
echo -- "$Q" # fail
cat <<< "$Q" # fail, also starts external program
printf -- '%s' "$Q" # success, but starts external program
In bash, printf is a builtin, so no external program is invoked.
$ help printf
printf: printf [-v var] format [arguments]
printf formats and prints ARGUMENTS under control of the FORMAT.
FORMAT
is a character string which contains three types of objects: plain
characters, which are simply copied to standard output, character
escape
sequences which are converted and copied to the standard output, and
format specifications, each of which causes printing of the next
successive
argument. In addition to the standard printf(1) formats, %b means
to
expand backslash escape sequences in the corresponding argument, and %q
means to quote the argument in a way that can be reused as shell input.
If the -v option is supplied, the output is placed into the value of the
shell variable VAR rather than being sent to the standard output.
echo -en \\x2Dn
Of course, the correct and more portable way is to use printf, however this works:
$ Q='-n'
$ echo -ne '\0'"$Q"
but it fails if you have backslash sequences that you want to print literally:
$ Q='-n\nX'
$ echo -ne '\0'"$Q"
-n
X
when what was wanted was "-n\nX". In this situation, this works:
$ echo -n $'\0'"$Q"
-n\nX$ # (no newline, so prompt follows on the same line)
but it doesn't for Q='-n'!
What if we want to print the literal string with printf?
$ Q='-n\nX'
$ printf "$Q"
-bash: printf: -n: invalid option
$ printf -- "$Q"
-n
X
$ printf "%s" "$Q"
-n\nX$ # (no newline, so prompt follows on the same line)
This question already has answers here:
How can I preserve quotes in printing a bash script's arguments
(7 answers)
Closed 3 years ago.
I have a Bash script where I want to keep quotes in the arguments passed.
Example:
./test.sh this is "some test"
then I want to use those arguments, and re-use them, including quotes and quotes around the whole argument list.
I tried using \"$#\", but that removes the quotes inside the list.
How do I accomplish this?
using "$#" will substitute the arguments as a list, without re-splitting them on whitespace (they were split once when the shell script was invoked), which is generally exactly what you want if you just want to re-pass the arguments to another program.
Note that this is a special form and is only recognized as such if it appears exactly this way. If you add anything else in the quotes the result will get combined into a single argument.
What are you trying to do and in what way is it not working?
There are two safe ways to do this:
1. Shell parameter expansion: ${variable#Q}:
When expanding a variable via ${variable#Q}:
The expansion is a string that is the value of parameter quoted in a format that can be reused as input.
Example:
$ expand-q() { for i; do echo ${i#Q}; done; } # Same as for `i in "$#"`...
$ expand-q word "two words" 'new
> line' "single'quote" 'double"quote'
word
'two words'
$'new\nline'
'single'\''quote'
'double"quote'
2. printf %q "$quote-me"
printf supports quoting internally. The manual's entry for printf says:
%q Causes printf to output the corresponding argument in a format that can be reused as shell input.
Example:
$ cat test.sh
#!/bin/bash
printf "%q\n" "$#"
$
$ ./test.sh this is "some test" 'new
>line' "single'quote" 'double"quote'
this
is
some\ test
$'new\nline'
single\'quote
double\"quote
$
Note the 2nd way is a bit cleaner if displaying the quoted text to a human.
Related: For bash, POSIX sh and zsh: Quote string with single quotes rather than backslashes
Yuku's answer only works if you're the only user of your script, while Dennis Williamson's is great if you're mainly interested in printing the strings, and expect them to have no quotes-in-quotes.
Here's a version that can be used if you want to pass all arguments as one big quoted-string argument to the -c parameter of bash or su:
#!/bin/bash
C=''
for i in "$#"; do
i="${i//\\/\\\\}"
C="$C \"${i//\"/\\\"}\""
done
bash -c "$C"
So, all the arguments get a quote around them (harmless if it wasn't there before, for this purpose), but we also escape any escapes and then escape any quotes that were already in an argument (the syntax ${var//from/to} does global substring substitution).
You could of course only quote stuff which already had whitespace in it, but it won't matter here. One utility of a script like this is to be able to have a certain predefined set of environment variables (or, with su, to run stuff as a certain user, without that mess of double-quoting everything).
Update: I recently had reason to do this in a POSIX way with minimal forking, which lead to this script (the last printf there outputs the command line used to invoke the script, which you should be able to copy-paste in order to invoke it with equivalent arguments):
#!/bin/sh
C=''
for i in "$#"; do
case "$i" in
*\'*)
i=`printf "%s" "$i" | sed "s/'/'\"'\"'/g"`
;;
*) : ;;
esac
C="$C '$i'"
done
printf "$0%s\n" "$C"
I switched to '' since shells also interpret things like $ and !! in ""-quotes.
If it's safe to make the assumption that an argument that contains white space must have been (and should be) quoted, then you can add them like this:
#!/bin/bash
whitespace="[[:space:]]"
for i in "$#"
do
if [[ $i =~ $whitespace ]]
then
i=\"$i\"
fi
echo "$i"
done
Here is a sample run:
$ ./argtest abc def "ghi jkl" $'mno\tpqr' $'stu\nvwx'
abc
def
"ghi jkl"
"mno pqr"
"stu
vwx"
You can also insert literal tabs and newlines using Ctrl-V Tab and Ctrl-V Ctrl-J within double or single quotes instead of using escapes within $'...'.
A note on inserting characters in Bash: If you're using Vi key bindings (set -o vi) in Bash (Emacs is the default - set -o emacs), you'll need to be in insert mode in order to insert characters. In Emacs mode, you're always in insert mode.
I needed this for forwarding all arguments to another interpreter.
What ended up right for me is:
bash -c "$(printf ' %q' "$#")"
Example (when named as forward.sh):
$ ./forward.sh echo "3 4"
3 4
$ ./forward.sh bash -c "bash -c 'echo 3'"
3
(Of course the actual script I use is more complex, involving in my case nohup and redirections etc., but this is the key part.)
Like Tom Hale said, one way to do this is with printf using %q to quote-escape.
For example:
send_all_args.sh
#!/bin/bash
if [ "$#" -lt 1 ]; then
quoted_args=""
else
quoted_args="$(printf " %q" "${#}")"
fi
bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_args}"
send_fewer_args.sh
#!/bin/bash
if [ "$#" -lt 2 ]; then
quoted_last_args=""
else
quoted_last_args="$(printf " %q" "${#:2}")"
fi
bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_last_args}"
receiver.sh
#!/bin/bash
for arg in "$#"; do
echo "$arg"
done
Example usage:
$ ./send_all_args.sh
$ ./send_all_args.sh a b
a
b
$ ./send_all_args.sh "a' b" 'c "e '
a' b
c "e
$ ./send_fewer_args.sh
$ ./send_fewer_args.sh a
$ ./send_fewer_args.sh a b
b
$ ./send_fewer_args.sh "a' b" 'c "e '
c "e
$ ./send_fewer_args.sh "a' b" 'c "e ' 'f " g'
c "e
f " g
Just use:
"${#}"
For example:
# cat t2.sh
for I in "${#}"
do
echo "Param: $I"
done
# cat t1.sh
./t2.sh "${#}"
# ./t1.sh "This is a test" "This is another line" a b "and also c"
Param: This is a test
Param: This is another line
Param: a
Param: b
Param: and also c
Changed unhammer's example to use array.
printargs() { printf "'%s' " "$#"; echo; }; # http://superuser.com/a/361133/126847
C=()
for i in "$#"; do
C+=("$i") # Need quotes here to append as a single array element.
done
printargs "${C[#]}" # Pass array to a program as a list of arguments.
My problem was similar and I used mixed ideas posted here.
We have a server with a PHP script that sends e-mails. And then we have a second server that connects to the 1st server via SSH and executes it.
The script name is the same on both servers and both are actually executed via a bash script.
On server 1 (local) bash script we have just:
/usr/bin/php /usr/local/myscript/myscript.php "$#"
This resides on /usr/local/bin/myscript and is called by the remote server. It works fine even for arguments with spaces.
But then at the remote server we can't use the same logic because the 1st server will not receive the quotes from "$#". I used the ideas from JohnMudd and Dennis Williamson to recreate the options and parameters array with the quotations. I like the idea of adding escaped quotations only when the item has spaces in it.
So the remote script runs with:
CSMOPTS=()
whitespace="[[:space:]]"
for i in "$#"
do
if [[ $i =~ $whitespace ]]
then
CSMOPTS+=(\"$i\")
else
CSMOPTS+=($i)
fi
done
/usr/bin/ssh "$USER#$SERVER" "/usr/local/bin/myscript ${CSMOPTS[#]}"
Note that I use "${CSMOPTS[#]}" to pass the options array to the remote server.
Thanks for eveyone that posted here! It really helped me! :)
Quotes are interpreted by bash and are not stored in command line arguments or variable values.
If you want to use quoted arguments, you have to quote them each time you use them:
val="$3"
echo "Hello World" > "$val"
As Gary S. Weaver shown in his source code tips, the trick is to call bash with parameter '-c' and then quote the next.
e.g.
bash -c "<your program> <parameters>"
or
docker exec -it <my docker> bash -c "$SCRIPT $quoted_args"
If you need to pass all arguments to bash from another programming language (for example, if you'd want to execute bash -c or emit_bash_code | bash), use this:
escape all single quote characters you have with '\''.
then, surround the result with singular quotes
The argument of abc'def will thus be converted to 'abc'\''def'. The characters '\'' are interpreted as following: the already existing quoting is terminated with the first first quote, then the escaped singular single quote \' comes, then the new quoting starts.
Yes, seems that it is not possible to ever preserve the quotes, but for the issue I was dealing with it wasn't necessary.
I have a bash function that will search down folder recursively and grep for a string, the problem is passing a string that has spaces, such as "find this string". Passing this to the bash script will then take the base argument $n and pass it to grep, this has grep believing these are different arguments. The way I solved this by using the fact that when you quote bash to call the function it groups the items in the quotes into a single argument. I just needed to decorate that argument with quotes and pass it to the grep command.
If you know what argument you are receiving in bash that needs quotes for its next step you can just decorate with with quotes.
Just use single quotes around the string with the double quotes:
./test.sh this is '"some test"'
So the double quotes of inside the single quotes were also interpreted as string.
But I would recommend to put the whole string between single quotes:
./test.sh 'this is "some test" '
In order to understand what the shell is doing or rather interpreting arguments in scripts, you can write a little script like this:
#!/bin/bash
echo $#
echo "$#"
Then you'll see and test, what's going on when calling a script with different strings