230

I'm writing a Bash script where I need to pass a string containing spaces to a function in my Bash script.

For example:

#!/bin/bash

myFunction
{
    echo $1
    echo $2
    echo $3
}

myFunction "firstString" "second string with spaces" "thirdString"

When run, the output I'd expect is:

firstString
second string with spaces
thirdString

However, what's actually output is:

firstString
second
string

Is there a way to pass a string with spaces as a single argument to a function in Bash?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Grant Limberg
  • 20,913
  • 11
  • 63
  • 84
  • 1
    Works for me... I use full syntax for functions though "function bla() { echo $1; }", can't make short one into a one liner. Not sure it makes a difference. What version of bash? – Eugene Dec 30 '09 at 23:28
  • 13
    try `echo "$@"` or `for i in "$@"; do echo $i ; done` for using correctly quoted parameters containing spaces. This is the very clearly mentioned in all `bash` documentation under `positional parameters` section. – Samveen Jun 14 '13 at 07:06
  • 2
    I was having a similar problem, trying to pass one quoted string as a parameter and only the first word of the string being recognized as part of the parameter. Samveen's suggestion to change $1 to $@ worked for me. Note that I was only passing one parameter to the function, but if I'd been passing more using the for statement would have been necessary. – Erin Geyer Apr 20 '14 at 08:01
  • 1
    See also http://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-variable – tripleee Feb 08 '17 at 04:31
  • 3
    try `myFunction "$@"` – vimjet Sep 22 '17 at 06:03

8 Answers8

214

You should add quotes and also, your function declaration is wrong.

myFunction()
{
    echo "$1"
    echo "$2"
    echo "$3"
}

And like the others, it works for me as well.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • 3
    This works very well for me also. If we are calling another function inside myFunction then pass arguments with quotes. Cheers :) – minhas23 May 08 '14 at 11:04
  • 4
    Can you explain why one needs quotes? I tried both with and without, and it did not work for me. I'm using Ubuntu 14.04, GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu). What *does* work for me is using $@ (with or without quotes). – Kyle Baker Nov 22 '16 at 20:52
  • @KyleBaker, without the quotes, `$@` behaves just like unquoted `$*` -- results are string-split and then individually glob-expanded, so if you have tabs they'll be converted to spaces, if you have words that can be evaluated as glob expressions they will be, etc. – Charles Duffy Mar 29 '18 at 20:45
30

Another solution to the issue above is to set each string to a variable, call the function with variables denoted by a literal dollar sign \$. Then in the function use eval to read the variable and output as expected.

#!/usr/bin/ksh

myFunction()
{
  eval string1="$1"
  eval string2="$2"
  eval string3="$3"

  echo "string1 = ${string1}"
  echo "string2 = ${string2}"
  echo "string3 = ${string3}"
}

var1="firstString"
var2="second string with spaces"
var3="thirdString"

myFunction "\${var1}" "\${var2}" "\${var3}"

exit 0

Output is then:

    string1 = firstString
    string2 = second string with spaces
    string3 = thirdString

In trying to solve a similar problem to this, I was running into the issue of UNIX thinking my variables were space delimeted. I was trying to pass a pipe delimited string to a function using awk to set a series of variables later used to create a report. I initially tried the solution posted by ghostdog74 but could not get it to work as not all of my parameters were being passed in quotes. After adding double-quotes to each parameter it then began to function as expected.

Below is the before state of my code and fully functioning after state.

Before - Non Functioning Code

#!/usr/bin/ksh

#*******************************************************************************
# Setup Function To Extract Each Field For The Error Report
#*******************************************************************************
getField(){
  detailedString="$1"
  fieldNumber=$2

  # Retrieves Column ${fieldNumber} From The Pipe Delimited ${detailedString} 
  #   And Strips Leading And Trailing Spaces
  echo ${detailedString} | awk -F '|' -v VAR=${fieldNumber} '{ print $VAR }' | sed 's/^[ \t]*//;s/[ \t]*$//'
}

while read LINE
do
  var1="$LINE"

  # Below Does Not Work Since There Are Not Quotes Around The 3
  iputId=$(getField "${var1}" 3)
done<${someFile}

exit 0

After - Functioning Code

#!/usr/bin/ksh

#*******************************************************************************
# Setup Function To Extract Each Field For The Report
#*******************************************************************************
getField(){
  detailedString="$1"
  fieldNumber=$2

  # Retrieves Column ${fieldNumber} From The Pipe Delimited ${detailedString} 
  #   And Strips Leading And Trailing Spaces
  echo ${detailedString} | awk -F '|' -v VAR=${fieldNumber} '{ print $VAR }' | sed 's/^[ \t]*//;s/[ \t]*$//'
}

while read LINE
do
  var1="$LINE"

  # Below Now Works As There Are Quotes Around The 3
  iputId=$(getField "${var1}" "3")
done<${someFile}

exit 0
TheBanjoMinnow
  • 309
  • 3
  • 2
16

A more dynamic way would be:

function myFunction {
   for i in "$*"; do echo "$i"; done;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
remykarem
  • 2,251
  • 22
  • 28
  • 3
    Ah great! this is exactly what I was looked for for some time on many questions. Thank you! – prosoitos Nov 23 '19 at 06:15
  • What do you mean by *"more dynamic"*? Can you elaborate? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/52418968/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Sep 16 '21 at 18:33
  • As a supplement, here is the reference for syntax `$*` : https://www.gnu.org/software/bash/manual/bash.html#Special-Parameters, "($*) Expands to the positional parameters, starting..." – Michael Lee Jul 09 '22 at 02:35
8

The simplest solution to this problem is that you just need to use \" for space separated arguments when running a shell script:

#!/bin/bash
myFunction() {
  echo $1
  echo $2
  echo $3
}
myFunction "firstString" "\"Hello World\"" "thirdString"
kenorb
  • 155,785
  • 88
  • 678
  • 743
Piyush Aggarwal
  • 215
  • 3
  • 12
7

Your definition of myFunction is wrong. It should be:

myFunction()
{
    # same as before
}

or:

function myFunction
{
    # same as before
}

Anyway, it looks fine and works fine for me on Bash 3.2.48.

R Samuel Klatchko
  • 74,869
  • 16
  • 134
  • 187
4

Simple solution that worked for me -- quoted $@

Test(){
   set -x
   grep "$@" /etc/hosts
   set +x
}
Test -i "3 rb"
+ grep -i '3 rb' /etc/hosts

I could verify the actual grep command (thanks to set -x).

0

You could have an extension of this problem in case of your initial text was set into a string type variable, for example:

function status(){    
  if [ $1 != "stopped" ]; then
     artist="ABC";
     track="CDE";
     album="DEF";
     status_message="The current track is $track at $album by $artist";
     echo $status_message;
     read_status $1 "$status_message";
  fi
}

function read_status(){
  if [ $1 != "playing" ]; then
    echo $2
  fi
}

In this case if you don't pass the status_message variable forward as string (surrounded by "") it will be split in a mount of different arguments.

"$variable": The current track is CDE at DEF by ABC

$variable: The

helmedeiros
  • 487
  • 6
  • 4
  • OP used `myFunction "firstString" "second string with spaces" "thirdString"` and it didn't work for him. So what you propose does not apply to this question. – doubleDown Jun 15 '13 at 22:55
-2

I had the same kind of problem and in fact the problem was not the function nor the function call, but what I passed as arguments to the function.

The function was called from the body of the script - the 'main' - so I passed "st1 a b" "st2 c d" "st3 e f" from the command line and passed it over to the function using myFunction $*

The $* causes the problem as it expands into a set of characters which will be interpreted in the call to the function using whitespace as a delimiter.

The solution was to change the call to the function in explicit argument handling from the 'main' towards the function: the call would then be myFunction "$1" "$2" "$3" which will preserve the whitespace inside strings as the quotes will delimit the arguments... So if a parameter can contain spaces, it should be handled explicitly throughout all calls of functions.

As this may be the reason for long searches to problems, it may be wise never to use $* to pass arguments...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jan
  • 1
  • 1
    The correct answer is `"$@"`, not all quoted `"$1"`, `"$2"`, ... positional paramters nor `$*`. – Samveen Jun 14 '13 at 07:04