Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

In bash how do you avoid a specific space from being expanded in a variable?

Lets say I have this

JAVA_OPTS="-Xmx1g"
JAVA_OPTS="$JAVA_OPTS -XX:OnError='/path/to/a/script.sh %p'"

function args() {
    printf "%d :" $#
    printf " <%s> " $@
    echo
}

args $JAVA_OPTS

You get this

3 : <-Xmx1g> <-XX:OnError='/path/to/a/script.sh> <%p'>

I would like this

2 : <-Xmx1g> <-XX:OnError='/path/to/a/script.sh %p'>
share|improve this question

3 Answers 3

up vote 9 down vote accepted

First note that args will show two arguments even if you only give it one:

$ args "abc def"
1 : <abc>  <def> 

To get it to display correctly, double-quotes need to be added:

$ function args() { printf "%d :" $#; printf " <%s> " "$@"; echo; }
$ args "abc def"
1 : <abc def> 

However, there are still issues with the definition of JAVA_OPTS. Observe:

$ args $JAVA_OPTS
3 : <-Xmx1g>  <-XX:OnError='/path/to/a/script.sh>  <%p'> 

This is because, when $JAVA_OPTS appears on a command line, the shell will do word splitting on the contents of JAVA_OPTS but it does not respect or process the quotes contained therein.

For this type of application, you are much better off with JAVA_OPTS defined as a bash array:

$ JAVA_OPTS="-Xmx1g"
$ JAVA_OPTS=("$JAVA_OPTS" "-XX:OnError=/path/to/a/script.sh %p")
$ args "${JAVA_OPTS[@]}"
2 : <-Xmx1g>  <-XX:OnError=/path/to/a/script.sh %p>

By the way, when working with arrays, a handy way to see what is in them is with declare -p:

$ declare -p JAVA_OPTS
declare -a JAVA_OPTS='([0]="-Xmx1g" [1]="-XX:OnError=/path/to/a/script.sh %p")'
share|improve this answer
    
I had tried everything except the bash array, that works thank you –  Greg Bowyer Aug 15 at 7:32

You have to use an array instead:

declare -a JAVA_OPTS
JAVA_OPTS+=("-Xmx1g")
JAVA_OPTS+=("-XX:OnError='/path/to/a/script.sh %p'")
args "${JAVA_OPTS[@]}"

Once something's in a string you don't get to pick which spaces will be used for word splitting, but with an array you can decide when you put them in and when you get them back out again. You'll want to quote $@ inside args, too, or it will break.

If you really must define it using a single string, you can use a different character to split elements and reset IFS:

JAVA_OPTS="-Xmx1g"
JAVA_OPTS="$JAVA_OPTS|-XX:OnError='/path/to/a/script.sh %p'")
IFS="|"
args $JAVA_OPTS

will also do the right thing in this case, although it's pretty fragile. As ever, you'd want to save and reset IFS, or only make the change in a subshell.

share|improve this answer

You can give the shell's parser another pass at it:

var="'-Xmx1g' \"-XX:OnError='/path/to/a/script.sh %p'\"" 
eval "echo $var"

OUTPUT

-Xmx1g -XX:OnError='/path/to/a/script.sh %p'

But an array probably is best:

set -- '-Xmx1g "-XX:OnError='/path/to/a/script.sh %p'"

echo "item count $#"
for i do 
    echo "item#$((n=$n+1)): $i"
done
echo "$@" 

OUTPUT

item count 2
item#1: -Xmx1g
item#2: -XX:OnError='/path/to/a/script.sh %p'
-Xmx1g -XX:OnError='/path/to/a/script.sh %p'

Though they're not necessarily mutually exclusive:

eval "set $var"

item count 2
item#1: -Xmx1g
item#2: -XX:OnError='/path/to/a/script.sh %p'
-Xmx1g -XX:OnError='/path/to/a/script.sh %p'

From within a function - like args() - if you wanted to concatenate two arguments you would just reset its argument array:

args() {
    [ $# -gt 2 ] && {
        a=$1; shift 
        set -- "$a" "$*" 
} ; printf "%d :" $#
    printf " <%s> " "$@"
    echo
}

But that's backwork. If you want those variables in an array:

args() {
    printf "%d :" $#
    printf " <%s> " "$@"
    echo
}

args "$JAVA_OPTS" "-XX:OnError='/path/to/a/script.sh %p'"

or for the current shell array..

set -- "$JAVA_OPTS" "-XX:OnError='/path/to/a/script.sh %p'"

All forms output:

2 : <-Xmx1g>  <-XX:OnError='/path/to/a/script.sh %p'> 

And if at some point you wish to add to the array:

set -- "$@" "some ne
w arg"
args "$@"
3 : <-Xmx1g>  <-XX:OnError='/path/to/a/script.sh %p'>  <some ne
w arg> 

The array comes with the shell. You should use it.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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