Escape characters when executing powershell command in batch script

Viewed 1535

I want to execute powershell command and put it in batch variable

The command:

for /f "delims=" %%i in ('powershell ^(Get-ChildItem ""\dir\"" -Name | sort-object {\[regex\]::Replace^($_,^'\d+^',{$args\[0\].Value.PadLeft^(20^)}^)} | select -last 1^) ') do set output=%%i

It can't be executed due special character. In addition, I can't pause the window so it closes before I can see what is the problem.

I think the problem is with the pipe "|", because the following command does work(the part before the pipe)

for /f "delims=" %%i in ('powershell ^(Get-ChildItem ""\dir\"" -Name^) ') do set output=%%i

I tried to add ^, ^^^ and "" before pipe, doesn't work. Can you spot the problem?

3 Answers

This seems to work with putting the PowerShell script inside " quotes, without all that escaping everything:

for /f "delims=" %%i in ('powershell.exe "Get-ChildItem c:\temp -Name | Sort-Object -Property { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) }| select-object -first 1"') do set output=%%i

Powershell can accept Base64-encoded commands, which is a handy way to overcome special characters in cmd scripts. Like so,

$command = 'gci -param | stuff | ? { something }'
$bytes = [Text.Encoding]::Unicode.GetBytes($command)
$encoded = [Convert]::ToBase64String($bytes)
$encoded # Get output
ZwBjAGkAIAAtA...

Now that you've got a base64 string, pass it in batch file

set encoded=ZwBjAGkAIAAtA...
for /f "delims=" %%i in ('powershell -encodedCommand %encoded' ...)

To reverse the Base64 encoding, use

[Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($encoded))

Have you tried to escape the pipe symbols and the commas, like ^| and ^,, respectively?

for /F "delims=" %%i in ('powershell ^(Get-ChildItem "\"dir\"" -Name ^| sort-object {\[regex\]::Replace^($_^,'\d+'^,{$args\[0\].Value.PadLeft^(20^)}^)} ^| select -last 1^)') do set "output=%%i"

Without escaping (or quotation), the pipes are exposed to the parser too early (both when the for /F loop is read), and the commas become replaced by spaces (because they constitute standard token separators, just like SPACE, TAB, ; and =).

As you may have noticed, I changed ""\dir\"" to "\"dir\"" as it looked strange to me, although I am not sure whether I made it worse.

Moreover, I removed escaping of single-quotes/apostrophes ' since they do not need it. Besides the fact that ^-escaping is not understood by the specific for /F parser, it anyway seems to be smart enough to take the outer-most pair of apostrophes within the parentheses behind in. If you want to alter the command line for legibility reasons, use the usebackq option, together with back-ticks `:

for /F "usebackq delims=" %%i in (`powershell ^(Get-ChildItem "\"dir\"" -Name ^| sort-object {\[regex\]::Replace^($_^,'\d+'^,{$args\[0\].Value.PadLeft^(20^)}^)} ^| select -last 1^)`) do set "output=%%i"
Related