but what about the quotes?
When calling PowerShell's CLI from cmd.exe
(a batch file) with powershell -command "...."
, use \"
to pass embedded "
.
(This may be surprising, given that PowerShell-internally you typically use `"
or ""
inside "..."
, but it is the safe choice from the outside.[1].)
Note:
- While
\"
works robustly on the PowerShell side, it can situationally break cmd.exe
's parsing. In that case, use "^""
(sic) with powershell.exe
(Windows PowerShell), and ""
with pwsh.exe
(PowerShell (Core) 7+), inside overall "..."
quoting. See this answer for details.
Here's an approach that matches and replaces everything between "..."
after #define VERSION
:
:: Define the new version *without* double quotes
set NEW_VERSION=0.2.0
powershell -Command "(gc BBB.iss) -replace '(?<=#define VERSION\s+\").+?(?=\")', '%NEW_VERSION%' | Set-Content -Encoding ascii BBB.iss"
Note that using Out-File
(as used in the question) to rewrite the file creates a UTF-16LE ("Unicode") encoded file, which may be undesired; use Set-Content -Encoding ...
to control the output encoding. The above command uses Set-Content -Encoding ascii
as an example.
Also note that rewriting an existing file this way (read existing content into memory, write modified content back) bears the slight risk of data loss, if writing the file gets interrupted.
(?<=#define VERSION\s+\")
is a look-behind assertion ((?<=...)
) that matches literal #define VERSION
followed by at least one space or tab (\s+
) and a literal "
- Note how the
"
is escaped as \"
, which - surprisingly - is how you need to escape literal "
chars. when you pass a command to PowerShell from cmd.exe
(a batch file).[1]
.+?
then non-greedily (?
) matches one or more (+
) characters (.
)...
...until the closing "
(escaped as \"
) is found via (?=\")
, a look-ahead assertion
((?<=...)
)
The net effect is that only the characters between "..."
are matched - i.e., the mere version number - which then allows replacing it with just '%NEW_VERSION%'
, the new version number.
A simpler alternative, if all that is needed is to replace the 1st line, without needing to preserve specific information from it:
powershell -nop -Command "@('#define VERSION \"%NEW_VERSION%\"') + (gc BBB.iss | Select -Skip 1) | Set-Content -Encoding ascii BBB.iss"
The command simply creates an array (@(...)
) of output lines from the new 1st line and (+
) all but the 1st line from the existing file (gc ... | Select-Object -Skip 1
) and writes that back to the file.
[1] When calling from cmd.exe
, escaping an embedded "
as ""
sometimes , but not always works (try
powershell -Command "'Nat ""King"" Cole'"
).
Instead, \"
-escaping is the safe choice.
`"
, which is the typical PowerShell-internal way to escape "
inside "..."
, never works when calling from cmd.exe
.