4

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?

igor
  • 248
  • 4
  • 16

3 Answers3

2

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
TessellatingHeckler
  • 27,511
  • 4
  • 48
  • 87
1

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))
vonPryz
  • 22,996
  • 7
  • 54
  • 65
  • Encoding commands is certainly an option, but one that I personally would never use as it stops the script being read or understood by a human unless it's decoded. This technique is also often used by malware to hide code and will likely raise security concerns if found in a production environment. – henrycarteruk Nov 08 '17 at 12:13
  • 1
    @JamesC. and yet, this is why the encoding exists - to have a way to run PowerShell without having to fight another language's parsing rules along the way. Because it can be used for bad, isn't much reason to avoid it. All compiled code cannot be read by humans, and that can't even be decoded in any easy way, and that causes very little problem. And compiled code is also often used by malware... – TessellatingHeckler Nov 09 '17 at 04:53
0

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"
aschipfl
  • 33,626
  • 12
  • 54
  • 99