PowerShell keep multiple spaces - powershell

While implementing the suggestions in the topic Drag and Drop to a Powershell script we discovered that PowerShell consolidates multiple spaces into one. This is blocking us from dragging and dropping files through Windows Explorer into .bat/.ps1 constructs with multiple spaces.
This can easily be verified by the following command:
powershell -command "& echo {"a a"}"
This will result in:
a a
whereas the following result is expected:
a a
How can this be achieved?
Please note that your provided solution must fit into the Windows Explorer drag-and-drop .bat -> .ps1 flow. Currently in the .bat file we have:
powershell.exe -Command "& '%PSScript%' '%*'"
which obviously results in PowerShell stripping the duplicate spaces.

try this
powershell -command "& echo 'a a'"

Please Check if below solves your problem.
"Replace 10 with number of spaces you want."
powershell -command '& echo {"a"' ''.padleft(10, ' ') '"a"}'
output:
a a

works for me with a simple :
write-host a (" ") a

Related

How do I have to change PowerShell variables code so that I can run it via CMD?

How do I have to change PowerShell code so that I can run it via CMD?
I came up with the following code:
$text_auslesen = Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt
$text_auslesen.Replace("Count :","") > $env:APPDATA\BIOS-Benchmark\Count_only.txt
$text_auslesen = Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt
$text_auslesen.Replace("Average :","") > $env:APPDATA\BIOS-Benchmark\Durchschnitt_only.txt
If I copy and paste it completely into a powershell, it can run. But now I have to put the code next to other code in a batch file. How do I have to adjust the code so that the cmd.exe executes the whole thing?
I suspect setting the variables via Powershell code is problematic here.
Unfortunately, a PS1 file is out of the question for my project.
To execute PowerShell commands from a batch file / cmd.exe, you need to create a PowerShell child process, using the PowerShell CLI (powershell.exe for Windows PowerShell, pwsh for PowerShell (Core) 7+) and pass the command(s) to the -Command (-c) parameter.
However, batch-file syntax does not support multi-line strings, so you have two options (the examples use two simple sample commands):
Pass all commands as a double-quoted, single-line string:
powershell.exe -Command "Get-Date; Write-Output hello > test.txt"
Do not use quoting, which allows you to use cmd.exe's line continuations, by placing ^ at the end of each line.
powershell.exe -Command Get-Date;^
Write-Output hello ^> test.txt
Note:
In both cases multiple statements must be separated with ;, because ^ at the end of a batch-file line continues the string on the next line without a newline.
Especially with the unquoted solution, you need to carefully ^-escape individual characters that cmd.exe would otherwise interpret itself, such as & and >
See this answer for detailed guidance.
Powershell -c executes PowerShell commands. You can do this from cmd, however, it looks like it needs to be run as administrator.
PowerShell -c "$text_auslesen = Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt;
$text_auslesen.Replace('Count :','') > $env:APPDATA\BIOS-Benchmark\Count_only.txt;
$text_auslesen = Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt;
$text_auslesen.Replace('Average :','') > $env:APPDATA\BIOS-Benchmark\Durchschnitt_only.txt"
It is possible to execute the PowerShell code in a batch file, but technically what you are doing is pulling a copy of it out and executing it someplace else. Here are 3 methods that I know of.
mklement0's answer addresses executing a copy of it that is passed as a parameter to PowerShell.
You could build a ps1 file from CMD, and then execute that ps1 file by passing it as a parameter to PowerShell.
And the method I've worked with the most is to pass specially designed PowerShell code to PowerShell that, when it runs, will load all, or part, of the current CMD file into memory and execute it there as a ScriptBlock. I have tried loading parts of the current CMD file, but my experience has been that this gets too complicated and I just stick with loading the entire current CMD file.
That last method is what I'm presenting here. The trick is to make the batch/CMD portion of the script look like a comment that is ignored by PowerShell, but still runs without throwing error messages in CMD. I'm not sure where I first found this trick, but it goes like this:
First, place <# : at the start of script. PowerShell sees this as the start of a comment, but CMD seems to ignore this line. I think CMD is trying to redirect < the contents of a non-existing file : to a non-existing command. But what does CMD do with the #? It works, and that's the important thing.
Place your batch code in lines following the <# :.
You end the batch/CMD part with a GOTO :EOF.
You then end the PowerShell comment with #>, but visually I find it easier to find <#~#>, which does the same job.
The rest of the file is your PowerShell code.
This version treats the PowerShell code as a function with defined parameters. The batch part builds %ARGS% and passes, with double quotes intact, to a PowerShell ScriptBlock that in turn is wrapped in another ScriptBlock. The PowerShell function is called twice with the same SourceFile parameter, but different DestinationFile and TextToRemove parameters. Perhaps there is a simpler way to reliably pass double quotes " in arguments passed to a ScriptBlock from batch, but this is the method I got working.
<# :
#ECHO OFF
SET f0=%~f0
SET SourceFile=%APPDATA%\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt
SET ARGS="%SourceFile%" "%APPDATA%\BIOS-Benchmark\Count_only.txt" "Count :"
PowerShell -NoProfile -Command ".([scriptblock]::Create('.([scriptblock]::Create((get-content -raw $Env:f0))) ' + $Env:ARGS))"
SET ARGS="%SourceFile%" "%APPDATA%\BIOS-Benchmark\Durchschnitt_only.txt" "Average :"
PowerShell -NoProfile -Command ".([scriptblock]::Create('.([scriptblock]::Create((get-content -raw $Env:f0))) ' + $Env:ARGS))"
GOTO :EOF
<#~#>
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$SourceFile,
[Parameter(Mandatory = $true, Position = 1)]
[string]$DestinationFile,
[Parameter(Mandatory = $true, Position = 2)]
[string]$TextToRemove
)
(Get-Content $SourceFile).Replace($TextToRemove, '') > $DestinationFile
This script passes a single parameter that, in PowerShell, is used by the Switch command to decide which section of PowerShell you intend on executing. Since we are not passing double quotes " in the args, the PowerShell lines can be greatly simplified. Information could still be passed to PowerShell by defining environmental variables in batch and reading them in PowerShell.
<# :
#ECHO OFF
SET f0=%~f0
PowerShell -NoProfile -Command .([scriptblock]::Create((get-content -raw $Env:f0))) Script1
PowerShell -NoProfile -Command .([scriptblock]::Create((get-content -raw $Env:f0))) Script2
GOTO :EOF
<#~#>
switch ($args[0]) {
'Script1' {
(Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt).Replace("Count :", '') > $env:APPDATA\BIOS-Benchmark\Count_only.txt
break
}
'Script2' {
(Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt).Replace("Average :", '') > $env:APPDATA\BIOS-Benchmark\Durchschnitt_only.txt
break
}
default {}
}
The -c parameter is intended to solve this scenario.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_pwsh?view=powershell-7.2#-command---c
If possible, it would be more efficient to invoke PowerShell\Pwsh directly rather than using a cmd wrapper.

Powershell v2: Replace CRLF with LF

Using Powershell v2 called from a batch file, I want to replace each CRLF in a file with just an LF. If a file only has LF without any CR, then I want all the LF to be left alone.
I do not want a terminating CRLF in the resultant file, if possible.
I found this question here on Stack Overflow, that seems to be a close match, but it does not specify a Powershell version requirement, nor does it specify the other criteria above. Hence this question.
The accepted answer for that question recommends this code:
$in = "C:\Users\abc\Desktop\File\abc.txt"
$out = "C:\Users\abc\Desktop\File\abc-out.txt"
(Get-Content $in) -join "`n" > $out
I slightly modified it, and adjusted it to work from within a batch file, to read:
powershell -Command "(Get-Content file1.txt) -join '`n' > file2.txt"
Unfortunately, this does not work. All LF's are converted to the string `n.
How can I get this to work?
Those before me are right you should use "`n"
When using PowerShell I recommend executing it the following switches:
-noninteractive indicate you do not want to interact with the powershell
-NoProfile - speeds up the things considerably (skips loading profile)
-ExecutionPolicy Bypass - bypasses security issues if you are on companies environment
Edit:
Sorry about the mistake you mentioned. I now have PowerShell 2.0 testing facility.
The fixed your example (the mistake was that you have to escape the double quotes due to the powershell.exe interpreting them). This approach does not work completely as it leaves CRLF at the end of the file:
powershell.exe -noninteractive -NoProfile -ExecutionPolicy Bypass -Command "& {(Get-Content file_crlf.txt) -join \"`n\" > file_lfonly.txt};"
However, the completely correct solution needs different approach (via IO.file class):
powershell.exe -noninteractive -NoProfile -ExecutionPolicy Bypass -Command "& {[IO.File]::WriteAllText('file_lfonly.txt', ([IO.File]::ReadAllText('file_crlf.txt') -replace \"`r`n\", \"`n\"))};"
This completely converts your CRLF to LF. Just small piece of warning it converts to ASCII not Unicode (out of scope of this question).
All examples are now tested on PowerShell v2.0.50727.
A couple of things:
Single quotes ' are literal - use double quotes " so that PowerShell knows you mean new line
If you need to escape double quotes within double quotes, use "" or `"
Edit:
Your original post worked for me, so looks like this is related to PowerShell 5 v 2, and I am unable to test the solution. Instead of escape characters, here is a solution using scriptblock:
powershell -Command {(Get-Content file1.txt) -join "`n" > file2.txt}
It's worth noting that this is trivial in Notepad++ and can be done for multiple files at once:

Powershell equivalent of the "tail" command ALMOST works remotely

I am looking for a Windows 7 equivalent of the "tail" command and thought I had found it with this Powershell equivalent -
C:\>powershell -command "& {Get-Content file.txt | Select-Object -last 100}"
If I use this in the CMD prompt on my own Windows 7 PC, returns the info just fine. I can even input/append it into another file.
However, when I log on remotely to another PC (via openSSH), the command works, but it never drops me back to a command prompt - just hangs after showing me the last 100 lines of the file. Which means this won't work for a batch file I'm trying to edit for about 300 remote Windows 7 PCs.
Any ideas?
After trying MANY different suggestions found all over online, FINALLY found one that worked!
And the answer is within the Batch file itself. My batch file to call this Powershell line was just this:
Powershell.exe -noprofile -executionpolicy Bypass C:\log\Tail.ps1
:end
Again, works great if you're using it on the very PC from which you want it to run/get the information. But not remotely. Finally found you just need to add "< nul" to the end of your call to Powershell in your batch file, just like this
Powershell.exe -noprofile -executionpolicy Bypass C:\log\Tail.ps1 <nul
:end
What the other person wrote is what finally made sense: "My research has shown that PowerShell runs the commands in the script indicated through the -File switch and then waits for additional PowerShell commands from the standard input (my brief experimentation with the -Command switch demonstrated similar behavior). By redirecting the standard input to nul, once PowerShell finishes executing the script and 'reads end-of-file' from the standard input, PowerShell exits."
Found here at this page - Powershell script gets stuck, doesn't exit when called from batch file
so credit actually goes to #Gordon Smith
Since your running the command with -command "...", according to the docs, you need to specify the -noexit flag to stop powershell from exiting after the command is run.
powershell -command "& {Get-Content file.txt | Select-Object -last 100}" -noexit
When you add this to a batch file you'll probably need -noprofile and -noninteractive as well - though for remote commands you might want to spawn a process for better control and error handling. Also, if this doesn't work the problem would probably be with how OpenSSH is handling something (this worked for me on a test-server with remote connect)

TeamCity Powershell Runner - Unable to run Source Code

Im trying to run some PS scripts using the Powershell Runner in TC and defining my own script as "Source Code" instead of a script file.
My script is as simple as:
"Hello World!"
Im running on Windows Server 2008 R2 and ive tried with to:
Run it as x86 + x64
Using "Execute .ps1 with '-File' argument" + "Put script into powershell stdin with "-Command -" arguments.
Ive set the security policy to Unrestricted in an attempt to get it to work, but no luck.
If I instead use a Command Line runner and for example writes:
powershell -Command Get-ExecutionPolicy
It works fine.
The errors im getting (depending on which of the 2 execution modes im using) are:
Starting: C:\...\cmd.exe /c C:\...\powershell.exe -NonInteractive -Command
- "<C:\...\powershell3889347351955805274.ps1" && exit /b %ERRORLEVEL%
in directory: C:\...\e18dda4054c166c7
'-' was specified with the -Command parameter; no other arguments to -Command are permitted.
OR
Starting: C:\...\cmd.exe /c C:\...\powershell.exe -NonInteractive -File
"C:\...\powershell8264270201473986040.ps1" && exit /b %ERRORLEVEL%
in directory: C:\...\e18dda4054c166c7
The term 'f' is not recognized as the name of a cmdlet, function, script file,
It looks to me like TC puts something in the actual script itself, but im not sure. Im stuck and I cant figure out what point im missing here :S.
Can anyone help?
I wasn't able to reproduce this, but I noticed something pretty weird with the command that TeamCity was trying to run:
-NonInteractive -Command - "<C:\...\powershell3889347351955805274.ps1"
I did not see it adding the quotes for me, so I thought maybe TeamCity is trying to quote a path with space(s) in it ( would have helped if you hadn't redacted your path)
So I switched my agent to a path with a space in it and I got the same command, and yes, the same error. So TeamCity is quoting the path wrongly. It is including the < in the quotes while it should have been <"c:\path with\space"
I will see if I can file a bug for this ( if there isn't one)
Try moving your agent to a non-space path as a workaround.

Preparting environment for running a powershell script

I needed to schedule a powershell script and before doing it, i needed to prep the environment by running some other powershell script.
Basically i wanted to run
PowerShell.exe -noexit " & ' C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\CONFIG\POWERSHELL\Registration\sharepoint.ps1' "
PowerShell.exe -command " & ' E:\PerfCounters\Powershell\RunPerf.ps1' "
The first statement will perp my environment and i want to execute my own script after that.
Issue is, if i use the -noexit command, the next script never gets executed. If i use -command instead of -noexit, the commands seems to be executed in different powershell instances so my second script is erroring out.
I am a newbee in powershell and i did my best tring to find a answer. Any help here would be really appreciated. thanks!
If you didn't solve this on your own, or if anyone else needs the syntax, here you go:
powershell -noprofile -command "& { & .\x.ps1; & .\z.ps1 }"