Avoid Line break at end of cmd output? - powershell

when I use this command pwsh -c echo hello in cmd I get the following output:
C:\>pwsh -c echo hello
hello
C:\>
I do not get that line break at the end
when I run it on powershell:
PS C:\> pwsh -c echo hello
hello
PS C:\>
So I think the problem is in cmd. I know this is not such a problem and have an easy fix but I have some programs uses cmd to access powershell and removing that line break is not that fun.
So is there any fix to prevent cmd to add that line ?

Mofi has provided the crucial pointers in comments:
When executing a command interactively, cmd.exe unconditionally appends a a newline (line break) to the command's output, presumably for readability and perhaps also to ensure that the next prompt always starts on a new line.
This applies irrespective of what that command is. In other words: It doesn't matter that your command happens to be a PowerShell command.
However, that trailing newline does not become part of the command's output, therefore programmatic processing of a command's output is not affected, such as when you redirect > to a file or process the output lines one by one with for /f.
In other words: for programmatic processing you need not remove the trailing newline, because it isn't part of the actual command output.
Conversely, if you really need to in effect suppress the trailing newline for display, you'll have to modify the command's output - if that is even an option - so that the output itself doesn't end in a newline, as shown in this SuperUser answer for cmd.exe's own echo command; for PowerShell, you could do pwsh -c Write-Host -NoNewLine hello.
Edge case:
When capturing output from a batch file that is running without #echo off (or with echo on) - in which case the trailing newlines do become part of the output - you can filter out empty lines by piping to findstr /r /v /c:"^$" (as also shown in the linked answer); e.g.
foo.cmd | findstr /r /v /c:"^$"
However, note that all empty lines are filtered out this way - potentially including actual empty lines in the output from commands executed by the batch file.
If preventing that is required, a more sophisticated approach is required, which, however (a) relies on the standard prompt string (e.g., C:\>) being used and (b) can still yield false positives:
foo.cmd | powershell -nop -c "#($Input) -join \"`n\" -replace '\n(?=[a-z]:\\.*?>)'"
Finally note that if you execute the above commands without capturing or redirecting their output, their overall output in the cmd.exe console will again have a trailing newline.

Related

Passing a semicolon as a parameter to a .bat file from both cmd and PowerShell

This question seems to be rather simple, but even after searching the web for a couple of hours, I was not able to find a solution...
I have a batch file test.bat
set MY_VARIABLE=%~1
echo %MY_VARIABLE%
The point is that I want to call this programm with a semicolon as input parameter, i.e.,
.\test.bat ";",
from both cmd and Windows PowerShell. While this works fine from cmd, PowerShell does not seem to get anything as an input. Is there any way to make this work for both simultaneously?
This is because of command line syntax. The semicolon is one of multiple delimiters, that split the command line into words: a;b would be interpreted as two separate arguments a (%1) and b (%2).
Therefore, quotes are required. Since Powershell uses quotes for string literals (Powershell does its own re-quoting behind the scenes when passing arguments), you need to include them in the string:
.\test.bat '";"'
# or
.\test.bat "`";`""
Or as #mklement0 pointed out, the stop-parsing symbol --% would also be an option:
.\test.bat --% ";"
Note that this is specific to Powershell syntax.
In CMD, this will suffice:
test.bat ";"

Use of echo commands in batch script

I have to convert a batch script to PowerShell. I came across these few lines of code and I have no idea what they do. Could someone explain it, especially the echo statements and robocopy?
set logdir=D:\Internal\Log
set runlog=%logdir%\run.log
set roboexe=robocopy /NJH /XX /NP
echo ^<!-- 1>>%runlog% 2>&1
echo Move-Copy - %account% %Time:/=% 1>>%runlog% 2>&1
echo Found files for %account% IN 1>>%runlog% 2>&1
%roboexe% "%fromdir%" "%historydir%" %wildcard% 1>>%runlog%
echo in shell just prints output to the screen. The command also works in PowerShell, but it's really an alias of Write-Output, which sends output to the standard success stream/pipeline (which if there's nowhere else for it to go, is the console/screen by default).
Robocopy is an advanced copying utility. It has a range of abilities beyond the standard copy function of Windows. It is documented here.
To explain the tokens in this line:
echo ^<!-- 1>>%runlog% 2>&1
<, >, and >> are redirection operators. The ^ character escapes the < character so that it's being used literally rather than as a redirect. !-- are also just being printed literally I believe.
1>> is using the append redirect to send the append the standard output to file specified in %runlog%.
2>&1 redirects the error output stream to the standard output stream, so basically errors would also be written to the runlog.

Why does Powershell get executed 2 or 4 times?

I am making a API for Batch. One of its functions is a colortext, which uses PowerShell (and findstr if PowerShell is not supported on the machine) to do its work. (It simply converts the batch color given, like 3C, converts it to PowerShell-friendly names, like DarkCyan and Red, and prints the text given in color using more things.)
However, when I try to set the foreground as F (White), for some reason it tries to execute it 2 or 4 times (depending if I have Delayed Expansion enabled or not).
It sometimes fails on the last run/try showing the same thing if the supposed %fcn% and %bcn% never were applied. (this also depends if I have Delayed Expansion enabled or not).
I only want this to execute one time (and not fail for whatever reason), but it's executing 2 or 4 times for some reason. How would I do/fix this program?
Here's the minimal code needed to reproduce this problem, assuming %bcn%, %fcn% and %text% are already set (let's say Cyan as %bcn% and White as %fcn%, and Should not happen as %text%):
#echo off
set bcn=Cyan
set fcn=White
set text="This should not work"
setlocal EnableDelayedExpansion
set script=write-host %text% -ForegroundColor %fcn% -BackgroundColor %bcn%
if EXIST colortest.ps1 ( del colortest.ps1 )
echo %script% >> colortest.ps1
#echo on
#powershell -executionpolicy remotesigned -file colortest.ps1
#echo off
The output of this becomes:
If I do a color that works (like CD), with the same text:
EDIT: Since no one seems to say "Hey it's working perfectly", I meant about the second bogus execution, I know this works fine, I'm just trying to get rid of the bogus second execution.
Here is the entire code of the batch file (save it as bapi.bat and try to run "bapi colortext "This should not work" BF") in a pastebin:
http://pastebin.com/WyayVUVS
The problem can't be reproduced with the posted batch code and the values for the environment variables.
However some suggestions as partly also posted by aschipfl in his comment.
1. Deletion of a single file
The deletion of a single file can be done with either
if exist colortest.ps1 del colortest.ps1
or with
del colortest.ps1 2>nul
The first command line checks first if the file to delete exists and runs command DEL only if the file really exists.
The second command line runs the command DEL and redirects an error message written to handle STDERR to device NUL to suppress it. Command DEL outputs an error message if the file to delete does not exist at all.
On running just a single command after an IF or in a FOR loop it is really not necessary to write this command line in round brackets which define a command block. FOR and IF are designed for execution of a single command and using ( ... ) which defines a command block is just an extension of Windows command processor to be able to run multiple commands where a single command is expected by design.
2. Redirection to file with overwriting existing file
It is possible here to use just > instead of >> to create or overwrite colortest.ps1. This avoids the need to separately delete the file before.
See also the Microsoft article Using command redirection operators.
3. Space character between output text and redirection operator
On the command line
echo %script% >> colortest.ps1
there is a space character between environment variable reference %script% and redirection operator >>. This space character is also output by command ECHO and therefore also written into the file as trailing space.
This does not matter here, but often trailing spaces are not wanted in produced file. One solution is removing the space character and use:
echo %script%>> colortest.ps1
But this can result in an unexpected behavior if the string of environment variable script ends with a space and a single digit number in range of 1 to 9 as this results after preprocessing this command line before execution in 1>> colortest.ps1 or 2>> colortest.ps1, ... which is a redirection of handle 1 to 9 to file colortest.ps1 instead of printing 1 to 9 to handle STDOUT and finally to the file.
The solution is writing the redirection first and next the command
>>colortest.ps1 echo %script%
or use delayed expansion which would be here even better in case of script PowerShell script line contains special characters.
echo !script!>>colortest.ps1
The space character between >> and file name colortest.ps1 is ignored by Windows command interpreter.
4. Quoting parameter strings with special characters
The environment variable text could hold a text which requires double quoting this parameter string for correct processing by Windows command interpreter and by PowerShell. A space character (delimiter on command line) and the characters &()[]{}^=;!'+,`~<>| require often enclosing the entire parameter string in double quotes for being interpreted completely as literal text.
set "script=write-host "!text!" -ForegroundColor %fcn% -BackgroundColor %bcn%"
See also answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? why set "script=script line" is used instead of just set script=script line and why it does not matter how many double quotes are specified in the script line on assigning it to the environment variable.
5. Double quotes to output in text in PowerShell script file
To output also text containing 1 or more double quotes by the PowerShell script it is necessary to escape each double quote character with one more " before writing the text string into the script file colortest.ps1.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "bcn=Cyan"
set "fcn=White"
set "text="This should not happen^^!""
set "text=!text:"=""!"
set "script=write-host "!text!" -ForegroundColor %fcn% -BackgroundColor %bcn%"
echo !script!>colortest.ps1
echo on
#powershell -executionpolicy remotesigned -file colortest.ps1
#echo off
endlocal
It can be seen here on this example that with delayed expansion already enabled on definition of text with a string containing also an exclamation mark to output as character additionally to the also to output two double quotes that the exclamation mark must be escaped with two caret characters ^^ for assigning the exclamation mark as literal character to environment variable text. That would not be necessary on definition of text before enabling delayed expansion.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "text="This should not happen!""
setlocal EnableDelayedExpansion
set "bcn=Cyan"
set "fcn=White"
set "text=!text:"=""!"
set "script=write-host "!text!" -ForegroundColor %fcn% -BackgroundColor %bcn%"
echo !script!>colortest.ps1
endlocal
echo on
#powershell -executionpolicy remotesigned -file colortest.ps1
#echo off
endlocal
For details about the commands SETLOCAL and ENDLOCAL see this answer which explains in detail how these two commands work.
This is because, for some random reason which didn't happen to the rest, the call command executed 2 times, the second without setting any %fcn% and %bcn% parameters, when something like call :label was used.
However, this does not happen with goto label.

Start process, wait for it, and throw away all output

I'd like to do this in PowerShell:
myprogram.exe arg1 arg2 >nul 2>nul
How does it work? There are like a million options to start processes in PowerShell, it's impossible to know the right one you need at a time.
nul is a special device (see this answer on SuperUser for instance) that's available in CMD, but not in PowerShell. In PowerShell you can use $null instead. Starting with PowerShell v3 you can use *> to redirect all output streams, but since you want to redirect output from an external program there should only be output on STDOUT and STDERR (Success and Error output stream in PowerShell terms), so >$null 2>$null should be fine.
A notable difference between CMD and PowerShell is that PowerShell doesn't include the current working directory in the PATH (the list of directories that is searched when you call a program/script without a path). If you want to run myprogram.exe from the current directory you need to prepend it with the path to the current directory (./).
You may also want to use the call operator (&). Although it's not required in this particular case I consider using it good practice. If you specify the command as a string (for instance because the path or filename contains spaces, or you want to use a variable instead of a literal) you MUST use the call operator, otherwise the statement would throw an error.
Something like this
& ./myprogram.exe arg1 arg2 >$null 2>$null
or like this
& ./myprogram.exe arg1 arg2 *>$null
should work.

Powershell fails to run multi-line commands from stdin?

I'm wanting to pass arbitrary scripts to Powershell via stdin.
(In practice, I'd like to avoid having to put the script into a temporary file, but for the purposes of this question I will pipe the contents of a file to powershell.)
So I'm doing something like so (in this example, from a Windows cmd shell):
type myfile.txt | powershell -
It works if myfile.txt contains something like this:
1..3 | % { $_ *2 }
echo done
(It outputs 2\n4\n6\ndone.)
However, if I split this first statement across multiple lines like so, then Powershell simply exists without generating any output at all:
1..3 |
% { $_ *2 }
echo done
This seems to fail for any multiline statement. For example, this also fails to produce output:
1..3 | % {
$_ *2 }
echo done
I'm surprised by this since each are legal Powershell scripts that would work normally if placed into a .ps1 file and run as normal.
I've tried various things including escaping the EOL using line continuation chars, to no avail. The same effect occurs if the parent shell is Powershell, or even Python (using subprocess.Popen with stdin=PIPE). In each case, Powershell exits without any error, and the exit code is 0.
Interestingly, if I run the following, only "before.txt" gets created.
"before" | out-file before.txt
1..3 |
% { $_ *2 }
"after" | out-file after.txt
echo done
Any ideas why Powershell would have trouble reading a multi-line command, if read from stdin?
I'm going to consider this answered by this:
How to end a multi-line command in PowerShell since it shows that an extra newline is required.
However, I'm going to raise this to MS as a bug since this should not be required when reading from a non-tty, or when -NonInteractive switch is specified.
Please vote on my bug report to the Powershell team.
This is not a complete answer, but from what I can tell, the problem has to do with the input being sent in line by line.
To demonstrate the line-by-line issue, I invoke powershell this way:
powershell.exe -command "gc myfile.txt" | powershell.exe -
vs
powershell.exe -command "gc myfile.txt -raw" | powershell.exe -
The first example replicates what you see with type, the second reads the entire contents of the file, and it works as expected.
It also works from within PowerShell if you put the script contents in a string and pipe it into powershell.exe -.
I had a theory that it had to do with line-by-line input lacking line breaks, but it's not so clear cut. If that were the case, why would the first option work but not the second (removing the line break splitting the single pipeline should have no effect, while removing the line break between the pipeline and the echo should make it fail). Maybe there's something unclear about the way powershell is handling the input with or without line breaks.