9

I thought it would be easy, but I already wasted a few hours on this.

I want to run the following CMake command from inside a Bash script. In a terminal I would type

cmake -G "Unix Makefiles" .

and it works. If I copy this exactly inside the Bash script it works as well.

But the script is meant to work on multiple platforms, and it could be "MSYS Makefiles" instead of "Unix Makefiles". Therefore, I want to put the command in a variable, where the contents depends on the platform and execute it. However, this is where I got stuck. I tried every combination of single/double quotes I could think of, but I got nowhere.

I want something along the lines

c="cmake . -G \"Unix Makefiles\""
exec $c

but it always results in some variation of the following:

CMake Error: Could not create named generator "Unix

I realize that I could do

if test [... this is Unix ...]
    cmake . -G "Unix Makefiles"
else
    cmake . -G "MSYS Makefiles
fi

but since this call has to be made several times I'd rather avoid it.

Any suggestion?

1
  • The accepted answer from there did the trick for me. Commented Mar 13, 2017 at 19:25

5 Answers 5

5

It is best not to use eval unnecessarily. Try not to put the command inside a variable. You can put the options as a variable though:

if [ ... ]
  string="Unix makefiles"
else
  string="MSYS Makefiles"
else
  string="...."
fi
cmake -G "$string" # Just call the command normally
Sign up to request clarification or add additional context in comments.

Comments

3

Use eval to tell the shell to parse the command-line anew:

c="cmake . -G \"Unix Makefiles\""
eval "$c"

Alternatively, I like using arrays to avoid the unnecessary backslashes and eval:

# Store command in 4-element array: ["cmake", ".", "-G", "Unix Makefiles"].
# No backslash escapes needed.
c=(cmake . -G "Unix Makefiles")

# Ugly syntax for expanding out each element of an array, with all the spaces and
# quoting preserved to ensure that "Unix Makefiles" remains a single word.
"${c[@]}"

2 Comments

Thanks for the quick answers. Turns out that eval $c was all I needed. Next step is to read about eval.
@Alain: avoid eval whenever possible -- it's an easy solution for problems like this, but it tends to cause more (and nastier) problems down the line.
3

The Bash FAQ to the rescue: The quotes are syntactical (meaning the quotes are not part of the name), so you should get the expected result with this:

if test [.... this is Unix ...]
    target="Unix Makefiles"
else
    target="MSYS Makefiles"
fi
cmake . -G "$target"

PS: eval is evil

1 Comment

Thanks. This is probably the easiest.
0

Calling exec on your string, you actually end up executing cmake with the following arguments:

1: .
2: -G
3: "Unix
4: Makefiles"

exec itself doesn't interpret quotes, but just spaces, and passes the arguments this way to the execve system call. You need to let Bash interpret the quotes by using a built-in like eval.

Comments

0

You may also use ... | xargs bash -c '...' to reparse a string as command line arguments. (Using xargs may, however, not be the ideal solution for multiple platforms.)

# Example: find file names with a space
c=". -maxdepth 3 -name \"* *\""
printf '%s' "$c" | xargs bash -c 'set -xv; find "$@"' arg0 2>&1| less

Another option would be to use a shebang helper like shebang.c!

1 Comment

The link is broken ("404. That’s an error. ").

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.