168

I've tried various forms of the following in a bash script:

#!/bin/bash
svn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W $COLUMNS"

But I can't get the syntax to correctly expand the COLUMNS environment variable.

I've tried various forms of the following:

svn diff $@ --diff-cmd /usr/bin/diff -x '-y -w -p -W $COLUMNS'

and

svn diff $@ --diff-cmd /usr/bin/diff -x '-y -w -p -W ${COLUMNS}'

and

eval svn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W $COLUMNS"

Suggestions?

5
  • so what do those examples produce in your case? And what do you want them to produce? Commented May 8, 2009 at 16:14
  • the command outside the script is working? Commented May 8, 2009 at 16:24
  • Have you tried svn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W ""$COLUMNS" Commented Nov 30, 2015 at 13:08
  • BTW, using $@ unquoted makes it exactly the same as $* -- which is to say that it breaks "foo bar" into two separate arguments, foo and bar. If you want to preserve the original argument list just as it was given to you, you want "$@". Commented Sep 21, 2017 at 4:47
  • See Difference between single and double quotes in Bash. Commented Dec 21, 2017 at 18:50

7 Answers 7

550

Just a quick note/summary for any who came here via Google looking for the answer to the general question asked in the title (as I was). Any of the following should work for getting access to shell variables inside quotes:

echo "$VARIABLE"
echo "${VARIABLE}"

Use of single quotes is the main issue. According to the Bash Reference Manual:

Enclosing characters in single quotes (') preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash. [...] Enclosing characters in double quotes (") preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !. The characters $ and ` retain their special meaning within double quotes (see Shell Expansions). The backslash retains its special meaning only when followed by one of the following characters: $, `, ", \, or newline. Within double quotes, backslashes that are followed by one of these characters are removed. Backslashes preceding characters without a special meaning are left unmodified. A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ! appearing in double quotes is escaped using a backslash. The backslash preceding the ! is not removed. The special parameters * and @ have special meaning when in double quotes (see Shell Parameter Expansion).

In the specific case asked in the question, $COLUMNS is a special variable which has nonstandard properties (see lhunath's answer above).

Sign up to request clarification or add additional context in comments.

2 Comments

In the case of single quotes, maybe this workaround helps: 'before'"$variable"'after' (as stated in this answer: stackoverflow.com/a/13802438/2254346)
Great answer! I had the same issue of referring my env variable within single quotes. Once I changed my single quote to double quotes, my env variable was being expanded as expected.
23

If unsure, you might use the 'cols' request on the terminal, and forget COLUMNS:

COLS=$(tput cols)

Comments

17

Note that COLUMNS is:

  1. NOT an environment variable. It is an ordinary bash parameter that is set by bash itself.
  2. Set automatically upon receipt of a SIGWINCH signal.

That second point usually means that your COLUMNS variable will only be set in your interactive shell, not in a bash script.

If your script's stdin is connected to your terminal you can manually look up the width of your terminal by asking your terminal:

tput cols

And to use this in your SVN command:

svn diff "$@" --diff-cmd /usr/bin/diff -x "-y -w -p -W $(tput cols)"

(Note: you should quote "$@" and stay away from eval ;-))

Comments

8

Another approach towards keeping single quotes itself in that case you can just wrap around your variable in single quote instead of how you would usually do with double quotes

X='abc'
>echo 'preceeding text ---"${X}"---- succeesing text'
preceeding text ---"${X}"---- succeesing text
>echo 'preceeding text ---"$X"---- succeesing text'
preceeding text ---"$X"---- succeesing text
>echo 'preceeding text ---'$X'---- succeesing text'
preceeding text ---abc---- succeesing text
>echo 'preceeding text ---'${X}'---- succeesing text'
preceeding text ---abc---- succeesing text

Comments

4

1 - Make sure the variable is correctly set.

COLUMNS=60  #example, no '$' here and no spaces!!
# or...
COLUMNS=`something that prints a number`

2- In shell script, 'single quote kills $dollar variables' but "double quotes keep $dollar $variables"

variable="Hello"
# You should not use inside apostrophes:
echo 'bla $variable'     # <-- single quote kills $variable
# Output:  'bla $variable'

# But 
echo "bla $variable"     # <-- double quoted!
# Produces: "bla Hello"

Comments

2

The following script works for me for multiple values of $COLUMNS. I wonder if you are not setting COLUMNS prior to this call?

#!/bin/bash
COLUMNS=30
svn diff $@ --diff-cmd /usr/bin/diff -x "-y -w -p -W $COLUMNS"

Can you echo $COLUMNS inside your script to see if it set correctly?

1 Comment

Boo, hiss re: unquoted $@ -- that splits a single parameter "two words" up into two separate parameters, two and words. Should be "$@" to pass the argument list through exactly as it was given.
0

You are doing it right, so I guess something else is at fault (not export-ing COLUMNS ?).

A trick to debug these cases is to make a specialized command (a closure for programming language guys). Create a shell script named diff-columns doing:

exec /usr/bin/diff -x -y -w -p -W "$COLUMNS" "$@"

and just use

svn diff "$@" --diff-cmd  diff-columns

This way your code is cleaner to read and more modular (top-down approach), and you can test the diff-columns code thouroughly separately (bottom-up approach).

2 Comments

The commandline is parsed before the process is forked, so not exporting COLUMNS can not be the problem.
He says his code in part of a bash script, thus it can be the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.