1

I am working on the raspberry pi (Raspberry Pi OS):

I run a Bash #!/bin/bash script where I need to save a file (a picture that is taken) under a directory which has spaces in its name.

In my case the directory is: /media/pi/test 1 2 3

And the command I run and does what I want:

raspistill -awb greyworld -vf -hf -o /media/pi/"test 1 2 3"/test.jpg

Which takes a picture and saves it there, works fine.

My issue is, I want the "test 1 2 3" to be inside a variable, like this:

usbToSave="\"test 1 2 3\"" #usbToSave is now "test 1 2 3"
raspistill -awb greyworld -vf -hf -o /media/pi/$usbToSave/test.jpg

But the above will throw me this error:

Invalid command line option (1)

Which I do not understand why. I am sure something like this has already been answered somewhere in the forum, but I cannot find it. I guess I don't use the right keywords to search for it.

Note:

If, my directory has no spaces for example /media/pi/test the Bash script:

usbToSave="test"
raspistill -awb greyworld -vf -hf -o /media/pi/$usbToSave/test.jpg

Works with no errors.

3 Answers3

4
usbToSave="\"test 1 2 3\""
raspistill -awb greyworld -vf -hf -o /media/pi/$usbToSave/test.jpg

Here, you're putting quotes into the value of the variable, after the assignment, it'll contain "test 1 2 3". Then, you're expanding the variable without double quotes, hitting the gotcha of word splitting (see links below). The result is that raspistill gets /media/pi/"test, 1, 2, and 3"/test.jpg as distinct command line arguments.

Note that the quotes you put in the variable are literal in the resulting word, the argument to raspistill. Variable expansion is not plain text replacement or macro expansion: whatever comes out of a variable is not treated as shell syntax in general. In addition to quotes, shell operators like &&, >, |, ; etc. are also just regular characters when expanded from a variable. Word splitting and filename generation are the only exceptions here.

What you need to do is:

usbToSave="test 1 2 3"
raspistill -awb greyworld -vf -hf -o "/media/pi/$usbToSave/test.jpg"

That is, do the variable expansion within double quotes, which prevents word splitting (and filename generation).

See:

ilkkachu
  • 138,973
1

Learn how variables and quoting work in the shell. Read about expansions in man bash.

usbToSave="test 1 2 3"
raspistill -awb greyworld -vf -hf -o /media/pi/"$usbToSave"/test.jpg

Variables in the shell are not real variables as you know them from programming languages. They are somehow similar to macros that expand to the value. If you don't quote the variable, word splitting is going to happen on the result, double quotting it prevents that.

choroba
  • 47,233
  • 2
    They're not macros in that they'd just expand as text at the point of expansion, not nearly. If they were, what they tried in the question, about putting the quotes inside usbToSave would have worked. But it doesn't. In the same way as shell operators or other expansions from the results of a variable expansion don't work. Which is actually exactly the opposite of how I recall macro processors working, the ones I know about anyway. – ilkkachu Mar 22 '21 at 10:35
-1

Alternatively, you may escape the spaces in defining the variable: Equiavlent to usbToSave="test 1 2 3"in the preceding answer is usbToSave=test\ 1\ 2\ 3 where the \ character escapes the following space (or another special character you might want in a string, like $)

EDIT: I have added double-quoting and quoting around the escaped string the make explicit how these all perform:

$ usbToSave_A="test 1 2 3"
$ usbToSave_B=test\ 1\ 2\ 3
$ echo $usbToSave_A
test 1 2 3
$ echo $usbToSave_B
test 1 2 3
$ echo /media/pi/"$usbToSave_A"/test.jpg
/media/pi/test 1 2 3/test.jpg
$ echo /media/pi/"$usbToSave_B"/test.jpg
/media/pi/test 1 2 3/test.jpg
$ echo "/media/pi/$usbToSave_A/test.jpg"
/media/pi/test 1 2 3/test.jpg
$ echo "/media/pi/$usbToSave_B/test.jpg"
/media/pi/test 1 2 3/test.jpg

$ usbToSave_C='"test 1 2 3"' $ usbToSave_D="test\ 1\ 2\ 3" $ echo $usbToSave_C "test 1 2 3" $ echo $usbToSave_D test\ 1\ 2\ 3 $ echo "/media/pi/$usbToSave_C/test.jpg" /media/pi/"test 1 2 3"/test.jpg $ echo "/media/pi/$usbToSave_D/test.jpg" /media/pi/test\ 1\ 2\ 3/test.jpg $ echo "/media/pi/"$usbToSave_C"/test.jpg" /media/pi/'test 1 2 3'/test.jpg $ echo "/media/pi/"$usbToSave_D"/test.jpg" /media/pi/test\ 1\ 2\ 3/test.jpg

  • 2
    Changing the quoted string to backslash escapes in the variable assignment doesn't do anything about what happens when the variable is expanded without being double-quoted. – ilkkachu Mar 22 '21 at 11:21
  • @ilkkachu yes, the problem of not double-quoting is a problem, but the equivalence I described is still valid - see edited answer – DoctorSoup Mar 22 '21 at 11:42