91

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?

slm
  • 369,824
Schwagmister
  • 1,053

3 Answers3

132

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'
John1024
  • 74,655
  • 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 03: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 06: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 06: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 07: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 07:23
  • I see now, thank you for all your help. Works exactly how I need! – Schwagmister Apr 22 '15 at 08:04
  • How to concatenate arguments with slashes instead of spaces ? – Vicky Dev May 21 '16 at 05:14
  • @VickyDev OK. I just added an example that uses slashes instead of spaces. – John1024 May 21 '16 at 06:03
  • great script example, I removed the single quotes. It echoes just fine. so that it can concatenated further into another string without singlequotes included. – Michael Dimmitt Mar 16 '18 at 11:55
8

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
  • 3
    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 04 '21 at 12:45
4

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.

Izana
  • 171
  • 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 01: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 02: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 03: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 03: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 03:48
  • I modified my answer, just let me know if you think it's still unclear! Thanks! – Izana Jan 29 '20 at 04:03
  • I agree. wrapping string with has special meanings, which will disable all variable expansions, and that's why I escape it. But I realize I can put ' inside of ". I have to admit that mixing and " is quite confusing. – Izana Jan 29 '20 at 04:08
  • (1) Well, now you’ve broken it by leaving out the ${ and the } from the Tl;dr part.  (2) My eyes are glazing over from reading your answer so many times.  So maybe that’s why I have trouble seeing what point(s) it is making.  For example, I feel that you haven’t explained why you’re looking at $#.  (3) The linuxconfig.org site seems not to be very good, and (IMHO) your answer suffers by association. – Scott - Слава Україні Jan 29 '20 at 05:13
  • Thanks for reviewing this answer carefully! Thanks a lot! – Izana Jan 29 '20 at 18:59
  • Thanks, @Izana. This not only gives answers to the questions the OP asked in the same way that the OP started their attempt, but adds valuable information. I think the accepted answer also works great and has good accompanying explanation, but I very much appreciate the elucidation you gave. It helps me understand all the answers and better prepares me to go forward using $@, $*, and things with arrays. Again, Thanks. – bballdave025 Dec 18 '21 at 23:01
  • Thanks, glad it's helpful! @bballdave025 – Izana Dec 19 '21 at 04:26