1
$ cat test.sh
#! /bin/bash

run="/Applications/YubiKey\ Manager.app/Contents/MacOS/ykman openpgp"

$run info

$ ./test.sh: line 5: /Applications/YubiKey\: No such file or directory

Is there a way to handle space in path to executable (/Applications/YubiKey\ Manager.app/Contents/MacOS/ykman) and space between path and argument (…/ykman openpgp)?

sunknudsen
  • 6,356
  • 3
  • 39
  • 76
  • 1
    The problem isn't the _storing_, the problem is the _using_. You're storing your string just fine, but `code='command "with quotes" and spaces'; $code` is not the same as `command "with quotes" and spaces`. – Charles Duffy Aug 31 '21 at 16:16
  • 1
    See [BashFAQ #50](http://mywiki.wooledge.org/BashFAQ/050). – Charles Duffy Aug 31 '21 at 16:16
  • 2
    BTW, while there are doubtless "answers" on the linked duplicate that suggest `eval`, _do not_ use `eval`; see [BashFAQ #48](http://mywiki.wooledge.org/BashFAQ/048) describing why. Either an array or a function is the right thing (BashFAQ #50 describes when to prefer either). – Charles Duffy Aug 31 '21 at 16:18

1 Answers1

2

You need the quotes or the backslash, not both.

run="/Applications/YubiKey Manager.app/Contents/MacOS/ykman openpgp"

or

run=/Applications/YubiKey\ Manager.app/Contents/MacOS/ykman \openpgp

Update: as this is a command name and an argument, not a single path name, you should be using either an array:

run=("/Applications/YubiKey Manager.app/Contents/MacOS/ykman" openpgp)
"${run[@]}" info

or better yet, define a function instead of a variable:

run () {
    "/Applications/YubiKey Manager.app/Contents/MacOS/ykman" openpgp "$1"
}

run info
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thanks for helping out. I tried that second option and I get `test.sh: line 5: /Applications/YubiKey: No such file or directory`. – sunknudsen Aug 31 '21 at 15:47
  • Somehow, this problem is harder to solve than it looks… – sunknudsen Aug 31 '21 at 15:51
  • 1
    What's difficult? Spaces separate words unless escaped; `foo=bar baz` is two words, `foo=bar` and `baz`, while `foo=bar\ baz` is one single word containing a space. Quotes just escape every contained character, rather than requiring a backslash for every character. `foo="bar"` is effectively the same as `foo=\b\a\r`; most escaped characters simply retain their unescaped meaning. – chepner Aug 31 '21 at 15:56
  • `foo="bar\ baz"`, after quote removal, is a single word that contains a backslash *and* a space, because the quotes escape both the characters. – chepner Aug 31 '21 at 15:57
  • How do one handle space between “YubiKey” and “Manager” (which is a space in filename) vs space between “ykman” “openpgp” (which is a space between executable path and argument)? – sunknudsen Aug 31 '21 at 16:09
  • Oh, that's an entirely separate problem. See update. – chepner Aug 31 '21 at 16:14
  • Thanks for helping out! Btw, tried using a function, but that approach doesn’t work when arguments use quotes (for example: `$run foo bar "hello world"`. – sunknudsen Aug 31 '21 at 17:21
  • Btw, would you happen to know whey question was closed? – sunknudsen Aug 31 '21 at 17:22
  • That's because I used `"$1"`, not `"$@"`, in the definition, as you hadn't indicated that `run` could take some arbitrary number of arguments. The question was closed because this is substantially similar to the linked question. – chepner Aug 31 '21 at 17:42
  • Using an array worked. Thanks! – sunknudsen Sep 01 '21 at 07:49