Right usage of 2>&1 | tee or | tee with powershell - powershell

I want to see the output of the command in the console and save it in a file so I have these two options:
When I use command | tee output.txt somehow it generates no output file at all but it works as usual in the console.
When I use command 2>&1 | tee output.txt it generates a fine output file but the text in the console appears in red.
Is there any way to either fix the first option or let the text appear as usual in the second one? I am using Windows PowerShell (Windows 10) and the Programm I am using this for is liquibase 3.5.5. just for the case that this is important.

In PowerShell, redirecting stderr lines from an external program to PowerShell's success stream via 2>&1 wraps those lines in [System.Management.Automation.ErrorRecord] instances in case they are captured for further processing.
In Windows PowerShell, these captured instances render like PowerShell errors, which is why you're seeing the red output (by contrast, without the redirection, the stderr lines would be passed through to the console, without coloring).
A simple workaround is to convert these objects to strings explicitly, which outputs the original lines (PSv3+ syntax; built-in alias % for ForEach-Object used for brevity):
... 2>&1 | % ToString | Tee-Object output.txt
Note: This workaround is no longer necessary in the install-on-demand, cross-platform PowerShell (Core) 7+ edition, where even captured stderr lines now consistently render just as strings.

If you are in DOS then you can leverage powershell (from DOS) and use tee-object do more or less a tee like in Linux.
C:\> powershell some_command ^| tee-object -FilePath some_outputfile
The ^ escapes the pipe so that the pipe applies to powershell and not DOS.
Powershell and tee-object should come with Windows as standard so nothing to install!

Related

Convert bat file to powershell script

I have a simple .bat file that executes a exe file and also passes some parameters in txt file. How do i acheive the same in powershell script (.ps1 file) ?
.bat file content:
#echo on
C:\Windows\System32\cmd.exe /C "C:\Program Files\BMC Software\AtriumCore\cmdb\server64\bin\cmdbdiag.exe" -u test -p test -s remedyar -t 41900 < "C:\Program Files\BMC Software\ARSystem\diserver\data-integration\batch\CleanupInputs.txt" > "C:\Program Files\BMC Software\ARSystem\diserver\data-integration\batch\Snow_Output\DailyOutput.log"
Exit 0
Fundamentally, you invoke console applications the same way in PowerShell as you do in cmd.exe, but there are important differences:
# If you really want to emulate `#echo ON` - see comments below.
Set-PSDebug -Trace 1
# * PowerShell doesn't support `<` for *input* redirection, so you must
# use Get-Content to *pipe* a file's content to another command.
# * `>` for *output* redirection *is* supported, but beware encoding problems:
# * Windows PowerShell creates a "Unicode" (UTF-16LE) file,
# * PowerShell (Core, v6+) a BOM-less UTF-8 file.
# * To control the encoding, pipe to Out-File / Set-Content with -Encoding
# * For syntactic reasons, because your executable path is *quoted*, you must
# invoke it via `&`, the call operator.
Get-Content "C:\..\CleanupInputs.txt" |
& "C:\...\cmdbdiag.exe" -u test -p test -s remedyar -t 41900 > "C:\...\DailyOutput.log"
# Turn tracing back off.
Set-PSDebug -Trace 0
exit 0
Note:
For brevity I've replaced the long directory paths in your command with ...
Character-encoding caveats:
When PowerShell communicates with external programs, it only "speaks text" (and it generally never passes raw bytes through its pipelines (as of v7.2)), which therefore involves potentially multiple passes of encoding and decoding strings; specifically:
Get-Content doesn't just path a text file's raw bytes through, it decodes the content into .NET strings and then sends the content line by line through the pipeline. If the input file lacks a BOM, Windows PowerShell assumes the active ANSI encoding, whereas PowerShell (Core) 7+ assumes UTF-8; you can use the -Encoding parameter to specify the encoding explicitly.
Since the receiving command is an external program (executable), PowerShell (re)-encodes the lines before sending them to the program, based on the $OutputEncoding preference variable , which defaults to ASCII(!) in Windows PowerShell, and UTF-8 in PowerShell (Core) 7+.
Since > - effectively an alias for Out-File - is used to redirect the external program to a file, another round of decoding and encoding happens:
PowerShell first decodes the external program's output into .NET strings, based on the character encoding stored in [Console]::OutputEncoding, which defaults to the system's active OEM code page.
Then Out-File encodes the decoded strings based on its default encoding, which is UTF-16LE ("Unicode") in Windows PowerShell, and BOM-less UTF-8 in PowerShell (Core); to control the encoding, you need to use Out-File (or Set-Content) explicitly and use its -Encoding parameter.
See also:
about_Redirection
&, the call operator
This answer discusses default encodings in both PowerShell editions; the short of it: they vary wildly in Windows PowerShell, but PowerShell (Core) 7+ now consistently used BOM-less UTF-8.
Re execution tracing: use of #echo ON in your batch file and how it compares to PowerShell's
Set-PSDebug -Trace 1:
Batch files typically run with #echo OFF so as not to echo every command itself before printing its output.
#echo ON (or omitting an #echo ON/OFF statement altogether) can be helpful for diagnosing problems during execution, however.
Set-PSDebug -Trace 1 is similar to #echo ON, but it has one disadvantage: the raw source code of commands is echoed, which means that you won't see the value of embedded variable references and expressions - see this answer for more information.

"wmic memorychip get | clip" in cmd

Hello i am not able to run this command:
wmic memorychip get | clip
This command does not returns any output or error.
I am using windows 7.
Kindly tell me what to do?
Thanks
This happens, as the output is piped to clip.exe. In other words, the output is there, but is sent to clipboard instead of printing it into the console.
As a side note, consider using Set-Clipboard instead of clip.exe, if you have Powershell 5.1 or newer.
If there's a need to send output to clipboard and see it too, use Tee-Object like so,
wmic memorychip get | tee-object -variable foo | set-clipboard; $foo
As vonPryz answered that the ouput is there but is redirected to clip.exe(A program that redirects output of command line tools to the Windows clipboard).but as you have described that you have windows 7,vonPryz solution won't work.you can use wmic memorychip to show output on console but it will mess up the output. you should echo the output in a file to see it(i.ewmic memorychip >>output.txt) or use the following batch file:
#echo off
wmic memorychip >>output.txt
notepad output.txt
pause >nul

In Windows power shell, how do you extract a properties file value and save it to an env var?

I have a properties file with entries like the below ...
...
USERNAME=myuser
...
In my Makefile, I have the below which uses Unix like commands to get the value of the variables ...
export USERNAME=$(shell grep USERNAME my_properties.txt | cut -d'=' -f 2-)
However, in a Windows power shell (maybe command prompt is the right phrase?), the above doesn't work because "grep" is not a standard command (among others). What's the equivalent way to extract a property from a properties file in a Windows power shell environment?
We could achieve this in PowerShell by following the below steps
Read the contents of the file
Convert the contents into key-value pairs
Create environment variable with the required value
(you can combine the steps if you like, I've kept them separate for better understanding)
Here's the script
$content = Get-Content .\user.properties -raw
$hashTable = ConvertFrom-StringData -StringData $content
$Env:USERNAME = $hashTable.USERNAME
Assuming that cmd.exe is the default shell:
export USERNAME=$(shell powershell -noprofile -c "(Select-String 'USERNAME=(.+)' my_properties.txt).Matches.Group[1]")
Note: -NoProfile suppresses loading of PowerShell's profiles, which unfortunately happens by default. Should you need the -File parameter to execute a script file, you may additionally need -ExecutionPolicy Bypass, unless your effective execution policy allows script execution.
The above uses the PowerShell CLI's -c (-Command) parameter to pass a command that uses the Select-String cmdlet, PowerShell's grep analog.
A closer analog to your command would be the following, which additionally uses -split, the string-splitting operator (showing the raw PowerShell command only; place it inside the "..." above):
((Select-String USERNAME my_properties.txt) -split '=', 2)[-1]

Getting stdin into the Powershell stream

The following script works well when the filename is specified on the command line.
tail.bat
#echo off
set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"
powershell "Get-Content %FILENAME% -Last %COUNT%"
However, what I need is to be able to pipe the text into Get-Content from stdin. I would like to write the following to get the last three Subversion tags assigned to the project. What can I do to get the source to Get-Content to be stdin?
svn ls svn://ahost/arepo/aproject/tags | call tail.bat -3
NB: I am not permitted to install any helpful tools like tail from the outside. It has to be done with the programs already available on the machine.
Update:
#mklement0 provided the answer. From that, I added code to use a default COUNT value of 10 if it is not provided. This matches the UNIX/Linux way.
#echo off
SET "COUNT=%~1"
IF "%COUNT:~0,1%" == "-" (
SET "COUNT=%COUNT:~1%"
SHIFT
) ELSE (
SET "COUNT=10"
)
SET "FILENAME=%~1"
if "%FILENAME%" == "" (
powershell -noprofile -command "$Input | Select-Object -Last %COUNT%"
) else (
powershell -noprofile -command "Get-Content \"%FILENAME%\" -Last %COUNT%"
)
EXIT /B
Rewrite tail.bat as follows:
#echo off
set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"
if "%FILENAME%"=="" (
powershell -noprofile -command "$Input | Select-Object -Last %COUNT%"
) else (
powershell -noprofile -command "Get-Content \"%FILENAME%\" -Last %COUNT%"
)
This will make the PowerShell CLI read stdin input via the automatic $input variable, if no filename argument was passed, courtesy of this answer.
Example:
C:> (echo one & echo two & echo three) | tail.bat -2
two
three
Note:
While PowerShell generally sends through the pipeline and outputs objects of any kind, its interface to the outside world invariably involves strings.
Thus, given that $Input is an enumerator that represents outside stdin input, we can be sure that it enumerates the input text lines (as strings) one by one, so all we need is to select the lines of interest, which is why piping to Select-Object is sufficient.
By contrast, reading a file by name in PowerShell requires Get-Content (which, incidentally, also sends the input file's lines one by one through the pipeline, unless you also specify -Raw); since Get-Content has tail functionality built in, via parameter -Tail (and its alias -Last), it is all that is needed here.
CAVEAT: Character decoding on input and re-encoding on output is involved when PowerShell talks to the outside world:
If you're only ever dealing with ASCII-encoded input (single-byte characters with code points ranging between 0 - 127), you needn't worry.
Otherwise, prepare for a world of pain - see below for details.
Character decoding/re-encoding issues:
Assuming that PowerShell recognizes your input encoding (see below), the output encoding is invariably what the console window's assigned encoding is; by default, unfortunately, that is the OEM codepage (e.g., the "DOS" code page CP437 on US-English systems), reflected in PS as [Console]::OutputEncoding.
Thus, with properly recognized input, if you print to the console, things will look OK, but if you capture the output in a file, you'll end up with an OEM-codepage-encoded file, which is probably undesired.
If feasible, you could fundamentally set up your console windows to use your codepage (input and output encoding) of choice (using chcp), but trying to change the encoding ad-hoc in your script is, unfortunately, not an option.
Note that using UTF-8 - codepage 65001 - only works if you configure your console windows to use one of the TT (TrueType) fonts.
As written above, the set of input encodings that are properly recognized is unfortunately limited to the following, based on the default input encoding (which is also the OEM codepage, reflected in PS as [Console]::InputEncoding; remember: input will be re-encoded on output):
ASCII input (re-encoding on output will by default preserve this encoding)
UTF-16 LE input with a BOM (which is what PowerShell calls Unicode, subject to re-encoding to something potentially different on output)
You could hard-code an expected input encoding by adding -Encoding <enc> to the Get-Content call (which expects the Windows default codepage encoding by default), but to do the same for stdin input (as reflected in $Input) would be non-trivial.
E.g., with the default input encoding, if you explicitly wanted to interpret the input as UTF-8 (again, note that on output [Console]::OutputEncoding encoding is applied):
powershell -noprofile -command "$Input | % { [text.encoding]::utf8.GetString([Console]::InputEncoding.GetBytes($_)) } | Select-Object -Last %COUNT%"

PowerShell Tee-Object command with -Append

I run into an issue with the PowerShell 4.0 Tee-Object command (alias tee) and the command that I'm using is as follows:
powershell "cmd /c dir | Tee-Object -filepath C:\1.txt -Append"
When 1.txt is an empty file, it writes the output as you see on the screen (as-is). But if 1.txt has some existing content before you run the command, it appends the output with no new lines and the files looks completely messed up.
I'm surprised to see this behavior and any thoughts would help me to proceed further. Note that I have to use the Tee-Object command as I would like to see the output on the screen when a command is running and append it to the existing log file that has some contents.