59

I'm trying to join all of the arguments to a Bash function into one single string with spaces separating each argument. I also need to have the string include single quotes around the whole string.

Here is what I have so far...

$array=("$@")
str="\'"
for arg in "${array[@]}"; do
    let $str=$str+$arg+" "
done
let $str=$str+"\'"

Obviously this does not work but I'm wondering if there is a way to achieve this?

93

I believe that this does what you want. It will put all the arguments in one string, separated by spaces, with single quotes around all:

str="'$*'"

$* produces all the scripts arguments separated by the first character of $IFS which, by default, is a space.

Inside a double quoted string, there is no need to escape single-quotes.

Example

Let us put the above in a script file:

$ cat script.sh 
#!/bin/sh
str="'$*'"
echo "$str"

Now, run the script with sample arguments:

$ sh script.sh one two three four 5
'one two three four 5'

This script is POSIX. It will work with bash but it does not require bash.

A variation: concatenating with slashes instead of spaces

We can change from spaces to another character by adjusting IFS:

$ cat script.sh 
#!/bin/sh
old="$IFS"
IFS='/'
str="'$*'"
echo "$str"
IFS=$old

For example:

$ sh script.sh one two three four       
'one/two/three/four'
9
  • Okay this worked great and did exactly what I needed. I am trying to use this to change directory to a directory with spaces in the name just by using the arguments. What I ultimately needed this for was to create a function that will change directories without having to type the apostrophes in if the directory has words spaced out. What I just tried was the following: cdm(){ str=" '$*' " cd $str } It only accesses the first argument in str So example: cdm test 2 Result: 'test: No such file or directory – Schwagmister Apr 22 '15 at 3:07
  • @Schwagmister: You can save it into a (string) variable if you want, but, if you don't specifically need to do that, I think cd "$*" will be good enough. – Scott Apr 22 '15 at 6:40
  • @Schwagmister The single quotes are not helpful there. Try: cdm(){ str="$*"; cd "$str"; } or, as Scott suggests, cdm(){ cd "$*"; }. Also, be aware that $* replaces multiple consecutive spaces with a single space. If your directory name may have multiple consecutive spaces or tabs or newlines, then those characters really need to be escaped before they are passed to the cdm function. – John1024 Apr 22 '15 at 6:52
  • So if it is something like test 2 trial 1, how would I make this cd command work correctly? Also this is amazing help I really appreciate it guys. @Scott – Schwagmister Apr 22 '15 at 7:08
  • @Schwagmister The best way is to put single-quotes on the command line when calling cdm as in: cdm 'test 2 trial 1'. This will handle all manor of strange names. However, as long as the white spaces are limited to single-spaces, then the cdm then it can be run as cdm test 2 trial 1. – John1024 Apr 22 '15 at 7:23
6

It's easier than you think:

#!/bin/bash
array="${@}"

echo $array

chmod +x that, and run it:

$ ./example.sh --foo bar -b az 
--foo bar -b az
1
  • This does not seem to produce the output that the user wants. There are no single quotes around the outputted string. Also, "$@" is a list of individually quoted entries. To get the positional parameters as a single string, use "$*" as is shown in the accepted answer. – Kusalananda Jan 4 at 12:45
3

update Tl;dr, use

"'${array[*]}'"

To be clear, I don't intend to replicate this answer. I just found there are minor differences to use @ and * to dereference all values from an array.

Under the hood, $* and $@ are all arrays, referring to the argv list.

From the question,

I'm trying to join all of the arguments to a Bash function into one single string with spaces separating each argument.

It has 2 sub-questions:

  1. concatenate the array input arguments into a string.
  2. pass the concatenated string as a single argument for the shell function.

First, concat array into a string,

array=("$@")
str="'${array[@]}'"
# or
str="'${array[*]}'"
# or
str=\'"${array[*]}"\'

Second, when you pass str to a function, let's count the number of arguments that function received,

#!/usr/bin/env bash

arr=(a b c d)

function count_args() {
  echo '$#' $#
}

count_args "'${arr[@]}'"
count_args \'"${arr[@]}"\'
count_args "'${arr[*]}'"
count_args \'"${arr[*]}"\'

output is

$# 4
$# 4
$# 1
$# 1

only arr[*] wraps the array into 1 argument for the shell function, why?

Cite from How to use arrays in bash script, which I found it's useful for me,

echo ${array[*]}
echo ${array[@]}

Both syntax let us access all the values of the array and produce the same results, unless the expansion it's quoted. In this case a difference arises: in the first case, when using @, the expansion will result in a word for each element of the array.

Whereas using * will group the entire array into one single argument during expansion.

9
  • This doesn’t look like an answer. It looks like a comment on the existing answers, but I’m not sure what it is. Can you clarify this?  Please do not respond in comments; edit your answer to make it clearer and more complete. – Scott Jan 27 '20 at 1:36
  • The accepted answer is str="'$*'".  Now that you have provided an answer to the question, it is the same as the accepted answer, but unnecessarily made more complicated.  And except for the fact that the first $ is an error. – Scott Jan 29 '20 at 2:36
  • The first $ is a copy-paste error from my terminal. Thanks for pointing it out. I'm using array, which is passed into the function, instead of global * and @ used by the shell script. Why do you think that's wrong? Based on the question, I think array is the expected way to be used in a shell function. – Izana Jan 29 '20 at 3:10
  • I didn’t say that your answer was wrong (aside from the $ at the beginning).  I said that it was equivalent to the old answer, but unnecessarily complicated.  But I need to backpedal a little.  I read the question two days ago, and not again (until now), so I thought that you were using the array variable just because you wanted to.  I had forgotten that the question already said array=("$@").  So, while I still don’t believe that this is worth an up vote, I have removed my down vote. … (Cont’d) – Scott Jan 29 '20 at 3:48
  • (Cont’d) … There is another thing, but I guess this is basically a matter of personal preference. I don’t like the fact that you’re using "…" already, but then you use \' to get a quoted ' rather than just putting the ' inside the double quotes, as John1024 did. I doubt that I’m the only person who sometimes finds backslashes confusing. – Scott Jan 29 '20 at 3:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.