Home > OS >  Powershell subexpression doesn't recognize "pkg-config --cflags --libs opencv4" comma
Powershell subexpression doesn't recognize "pkg-config --cflags --libs opencv4" comma

Time:11-09

I want to compile an OpenCV C/C (MSYS2) program on Windows using solely the CLI. The GNU Bash way to do this is (which runs fine on MSYS2 MINGW64 Shell):

g   main.cc -o main `pkg-config --cflags --libs opencv4`

Bash perfectly recognizes the backticks as a subexpression, however PowerShell doesn't:

> g   main.cc -o main `pkg-config --cflags --libs opencv4`
g  .exe: error: unrecognized command-line option '--cflags'
g  .exe: error: unrecognized command-line option '--libs'

I've done some research, and the equivalent on PowerShell would be

g   main.cc -o main $(pkg-config --cflags --libs opencv4)

Despite $(pkg-config --cflags --libs opencv4) being the right way to write a subexpression in PowerShell as an echo command shows it produces the right output (in this case compiler include and linker flags I need):

> echo $(pkg-config --cflags --libs opencv4)
-IC:/msys64/mingw64/bin/../include/opencv4 -LC:/msys64/mingw64/bin/../lib -lopencv_gapi -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_ccalib -lopencv_cvv -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm (...)

It simply won't work:

> g   main.cc -o main $(pkg-config --cflags --libs opencv4)
main.cc:1:10: fatal error: opencv2/imgcodecs.hpp: No such file or directory
    1 | #include <opencv2/imgcodecs.hpp>
      |          ^~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

Therefore, I need to use a verbose workaround to compile my file:

> g   main.cc -o main -IC:/msys64/mingw64/bin/../include/opencv4 -LC:/msys64/mingw64/bin/../lib -lopencv_gapi -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_ccalib -lopencv_cvv -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hdf -lopencv_hfs -lopencv_img_hash (...)

My PowerShell version:

> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.22621.608
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.22621.608
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

My question is: how can I make this particular subexpression work in PowerShell so that I don't have to resort to the verbose fallback of copying and pasting the output of pkg-config --cflags --libs opencv4 after g main.cc -o main?

CodePudding user response:

The equivalent of your bash command is:

g   main.cc -o main (-split (pkg-config --cflags --libs opencv4))

That is:

  • (pkg-config --cflags --libs opencv4) executes your pkg-config call, and, by enclosing it in (...), the grouping operator, its output can participate in a larger expression.

  • Using the unary form of -split, the string splitting operator, then splits the pkg-config call's output into an array of whitespace-separated tokens, which is (part of) the equivalent of what bash implicitly does via its shell expansion called word splitting.

  • Passing an array as an argument to an external program in PowerShell causes its elements to be passed as individual arguments.


Note:

  • Using $(...), PowerShell's subexpression operator does not work in this case, because its output would be passed as a whole, as a single argument to the target executable (or, in the case of an external-program call that outputs multiple lines, each line in full would be passed as an argument).

  • In general, where bash requires $(...) (or its legacy form `...`), in PowerShell (...) is usually sufficient (and in assignments not even it is needed, e.g., $var = Get-Date); the primary use case for $(...) in PowerShell is inside "...", i.e. inside expandable (double-quoted) strings

  • More fundamentally, PowerShell has no equivalent of bash's shell expansions:

    • Notably, parameter expansions (e.g. ${var//foo/bar}) are not supported, and require use of expressions instead (e.g. ($var -replace 'foo', 'bar'))

    • Unquoted arguments are not generally subject to special interpretation, except if they contain metacharacters, which, if they are to be used verbatim, for syntactic reasons then must either be individually `-escaped (`, the so-called backtick, is PowerShell's escape character), or require the whole argument to be enclosed in quotes.

    • It is up to each target command to interpret special characters such as an initial ~ as referring to the home directory, or to interpret arguments such as *.txt as wildcard patterns - whether such an argument was passed in quotes or not.

    • Variable references (e.g., $var) and simple expressions based on them (e.g, $var.Property), (grouping) expressions (e.g. ((1 2)), as well as subexpressions ($(...)) and array subexpressions (@(...)) can be used as-is as arguments, even if what they evaluate to contains spaces - in the case of calling external programs, on Windows, PowerShell will double-quote the result on demand, behind the scenes; as stated, multiple outputs are each passed as an argument.

  • Related