问题
I will like to call the command find from within a execute_process.
The format of the find
command is:
find [/v] [/c] [/n] [/i] [/off[line]] "<String>" [[<Drive>:][<Path>]<FileName>[...]]
So, the string has to be double quoted. However, if in cmake I do:
execute_process(COMMAND <firstCommandPipingSomethingToFind>
COMMAND find "<myString>" /c
OUTPUT_VARIABLE MY_COUNT
OUTPUT_STRIP_TRAILING_WHITESPACE)
I get an error FIND: Parameter format not correct
.
If I escape the double quotes with \
, I get an error Access denied - \
.
Also using double escaping \\
or double double quotes ""
does not help.
So my question is:
Is there a way to escape the double quotes when calling execute_process
so that find
receives correctly its argument?
I could use findstr, which doesn't need the double quotes; however it doesn't provide a counting feature. I could use find
by calling find /v /c "", but again, I need double quotes!
And I would like to count the output lines outside cmake, and store directly the result in MY_COUNT
variable.
Note: I am using CMake 3.4.1
To reproduce the problem you can use the following code, in which cmake -E echo
is used to feed a string to findstr
and find
:
#This works
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND findstr ell OUTPUT_VARIABLE DUMMY)
message (STATUS "0) DUMMY=${DUMMY}")
#All of the following don't work
set(MyCommand "find \"ell\" /c")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND "${MyCommand}" OUTPUT_VARIABLE DUMMY)
message (STATUS MyCommand=${MyCommand})
message (STATUS "1) DUMMY=${DUMMY}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND ${MyCommand} OUTPUT_VARIABLE DUMMY)
message (STATUS "2) DUMMY=${DUMMY}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND MyCommand OUTPUT_VARIABLE DUMMY)
message (STATUS "3) DUMMY=${DUMMY}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND "find \"ell\" /c" OUTPUT_VARIABLE DUMMY)
message (STATUS "4) DUMMY=${DUMMY}")
Which outputs:
0) DUMMY=hello
MyCommand=find "ell" /c
1) DUMMY=
2) DUMMY=
3) DUMMY=
4) DUMMY=
I also tried
set(MyArgument "\"ell\" /c")
message (STATUS MyArgument=${MyArgument})
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND find "${MyArgument}" OUTPUT_VARIABLE DUMMY)
message (STATUS "1) DUMMY=${DUMMY}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND find ${MyArgument} OUTPUT_VARIABLE DUMMY)
message (STATUS "2) DUMMY=${DUMMY}")
set(MyArgument "\"ell\"")
message (STATUS MyArgument=${MyArgument})
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND find "${MyArgument}" /c OUTPUT_VARIABLE DUMMY)
message (STATUS "1) DUMMY=${DUMMY}")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND find ${MyArgument} /c OUTPUT_VARIABLE DUMMY)
message (STATUS "2) DUMMY=${DUMMY}")
Which outputs:
MyArgument="ell" /c
File not found - \ELL\
1) DUMMY=
File not found - \ELL\
2) DUMMY=
MyArgument="ell"
Access denied - \
1) DUMMY=
Access denied - \
2) DUMMY=
CMake Error at CMakeRules.cmake:404 (execute_process):
execute_process given COMMAND argument with no value.
Call Stack (most recent call first):
CMakeLists.txt:24 (include)
The problem persists also if a remove the /c
option.
This:
execute_process(COMMAND ${CMAKE_COMMAND} -E echo hello COMMAND find \"ell\" OUTPUT_VARIABLE DUMMY)
message (STATUS "DUMMY=${DUMMY}")
outputs this:
Access denied - \
DUMMY=
Following Tsyvarev suggestion to retrieve which string is effectively passed to the command line, this:
execute_process(COMMAND cmd /c echo \"ell\")
execute_process(COMMAND cmd /c echo "ell")
execute_process(COMMAND cmd /c echo ""ell"")
outputs this:
\"ell\"
ell
"" ell\"\"
With a warning for the third command:
Argument not separated from preceding token by whitespace.
This warning is for project developers. Use -Wno-dev to suppress it.
回答1:
According to CMake developers,
Windows's find
cannot be called directly by cmake from within execute_process
, because of a limitation of the CreateProcess API and of the non-standard way find
parses arguments.
It can be called indirectly though, by creating a temporary batch script.
My problem of counting occurrences of a string from the output of a program in Windows is solved in the following way:
file(WRITE ${CMAKE_BINARY_DIR}/countLines.bat [[@find /v /c "" %1]])
execute_process(COMMAND <firstCommandPipingSomethingToFind> COMMAND findstr <regular expression> COMMAND countLines.bat WORKING_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE N_MATCHES OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "N_MATCHES=${N_MATCHES}")
file(REMOVE ${CMAKE_BINARY_DIR}/countLines.bat)
The advantage of findstr
in between is that it supports regular expressions. find
is only used to count matching lines.
The @
in front of find
is to prevent the command invocation within the script from ending up in the output variable.
来源:https://stackoverflow.com/questions/34905194/cmake-how-to-call-execute-process-with-a-double-quote-in-the-argument-a-k-a-u