How to run a PowerShell script with verbose output? - windows

I'm wondering if there's a way to run a PowerShell script such that both the commands and the output of each line of the script are printed. For example, in Bash you would write bash -x myscript or place a set -x at the top of your script. In Batch, you would omit the #echo off traditionally left at the top of your script. Does PowerShell have an equivalent of these constructs?
Things I've tried: Running powershell -? | sls verbose, which turned up nothing.

Just goes to show, #JamesKo, if you ask the wrong question you get the wrong answer :-(. Several people put forth good-faith answers here based on (a) lack of Linux exposure and (b) your use of the term verbose. In the following I will walk you through how Linux relates to PowerShell on this topic, but feel free to jump to the answer at the end if you are in a hurry. :-)
Background
In PowerShell, verbose has a very specific meaning which the PowerShell man page is even rather vague about:
Displays detailed information about the operation performed by the
command. This information resembles the information in a trace or in a
transaction log. This parameter works only when the command generates
a verbose message.
It even sounds like what you want... but let's compare that to the Linux documentation for set -x which, depending on your flavor of Linux, could be this (from man-pages project)...
The shell shall write to standard error a trace for each command after
it expands the command and before it executes it.
or this (from gnu)...
Print a trace of simple commands, for commands, case commands, select
commands, and arithmetic for commands and their arguments or
associated word lists after they are expanded and before they are
executed.
The very first line of your question clearly and concisely agrees with these. But verbose in PowerShell is different. In a nutshell, turning on verbose mode (be it with the -Verbose command line switch or the $VerbosePreference variable) simply enables output from the verbose stream to the console. (Just like Linux offers two streams, stdout and stderr, PowerShell offers multiple streams: output stream, error stream, warning stream, verbose stream, and debug stream. You work with these streams in an identical fashion to that of Linux--you can even use, e.g., commands 4>&1 to merge the verbose stream to stdout, for example. (You can read more about PowerShell's multiple output streams in the Basic Writing Streams section of PowerShell One-Liners: Accessing, Handling and Writing Data and a good quick reference is the Complete Guide to PowerShell Punctuation.)
The Answer
The Set-PSDebug command will give you bash-equivalent tracing. You can even adjust the tracing detail with the -Trace parameter. First, here's the control, before using Set-PSDebug:
PS> Get-PSDepth
0
With a value of 1 you get each line of code as it executes, e.g.:
PS> Set-PSDebug -Trace 1
PS> Get-PSDepth
DEBUG: 1+ >>>> Get-PSDepth
DEBUG: 141+ >>>> {
DEBUG: 142+ >>>> $nest = -1
DEBUG: 143+ >>>> $thisId = $pid
DEBUG: 144+ while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
DEBUG: 145+ >>>> $thisId = (gwmi win32_process -Filter "processid='$thisId'").ParentProcessId
DEBUG: 146+ >>>> $nest++
DEBUG: 144+ while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
DEBUG: 148+ >>>> $nest
0
DEBUG: 149+ >>>> }
With a value of 2 you also get variable assignments and code paths:
PS> Set-PSDebug -Trace 2
PS> Get-PSDepth
DEBUG: 1+ >>>> Get-PSDepth
DEBUG: ! CALL function '<ScriptBlock>'
DEBUG: 141+ >>>> {
DEBUG: ! CALL function 'Get-PSDepth' (defined in file 'C:\Users\msorens\Documents\WindowsPowerShell\profile.ps1')
DEBUG: 142+ >>>> $nest = -1
DEBUG: ! SET $nest = '-1'.
DEBUG: 143+ >>>> $thisId = $pid
DEBUG: ! SET $thisId = '9872'.
DEBUG: 144+ while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
DEBUG: 145+ >>>> $thisId = (gwmi win32_process -Filter "processid='$thisId'").ParentProcessId
DEBUG: ! SET $thisId = '10548'.
DEBUG: 146+ >>>> $nest++
DEBUG: ! SET $nest = '0'.
DEBUG: 144+ while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
DEBUG: 148+ >>>> $nest
0
DEBUG: 149+ >>>> }
Those are traces of a simple cmdlet I wrote called Get-PSDepth. It prints the commands, assignments, etc. with the DEBUG prefix, intermixed with the actual output, which in this case is the single line containing just 0.

You can always use the below in your script.
$VerbosePreference="Continue"
Note: You have to open the shell in elevated mode.
Below screenshot is for reference.
Hope it helps.

To let PowerShell script able to receive parameters from arguments/command line, need to add [CmdletBinding()] param () even though do not have any parameter.
Example script: Test-Output.ps1
[CmdletBinding()] param ()
Write-Host "Test output on OS $($Env:OS)"
Write-Verbose "Test VERBOSE output on OS $($Env:OS)"
Execute the script in PowerShell:
PS C:\> .\Test-Output.ps1 -Verbose
Execute the script in PowerShell on Linux:
/$ pwsh
PS /> ./Test-Output.ps1 -Verbose
Execute the script using PowerShell.exe on Windows:
C:\> powershell.exe Test-Output.ps1 -Verbose
Execute the script using pwsh.exe PowerShell Core on Windows:
C:\> pwsh.exe Test-Output.ps1 -Verbose
Execute the script using pwsh PowerShell Core on Linux:
/$ pwsh Test-Output.ps1 -Verbose
Sample output on Windows:
Test output on OS Windows_NT
VERBOSE: Test VERBOSE output on OS Windows_NT
Sample output on Linux:
Test output on OS
VERBOSE: Test VERBOSE output on OS

This actually is very easy every PowerShell CMDLET has a built in Verbose tag. All you have to do for example:
Test-Connection -ComputerName www.google.com -Verbose
That is it. I hope this helps

Note: This answer was originally posted in response to the duplicate question at Windows Powershell needs to print out information for a particular command regardless of whether it successfully executed or Not.
To complement and elaborate on Michael Sorens' helpful answer:
With limitations, you can use Set-PSDebug -Trace 1 to let PowerShell echo script statements for you before they are executed; however, this is more like bash's set -o verbose / set -v option rather than set -o xtrace / set -x, in that the commands echoed are unexpanded; details follow:
# Start tracing statements.
Set-PSDebug -Trace 1
try
{
# Sample command
cmd /c echo 'hi there' $HOME
}
finally {
Set-PSDebug -Trace 0 # Turn tracing back off.
}
The above yields:
DEBUG: 4+ >>>> cmd /c echo 'hi there' $HOME
"hi there" C:\Users\jdoe
DEBUG: 6+ >>>> Set-PSDebug -Trace 0 # Turn tracing off.
While this approach requires little extra effort, its limitations are:
You don't get to control the prefix of the tracing statements. (e.g. DEBUG: 4+ >>>> , where 4 is the line number.
Turning tracing back off invariably also produces a tracing statement.
There's no way to capture or suppress the tracing output - it invariably prints to the host (console); however, you can capture it from outside PowerShell, via PowerShell's CLI - see this answer.
As of PowerShell 7.2, commands that span multiple lines using line continuation only have their first line echoed (see GitHub issue #8113).
Perhaps most importantly, the statements being echoed are their literal source-code representations and can therefore contain unexpanded variable references and expressions, such as $HOME in the example above.
GitHub issue #9463 proposes that expanded values (i.e., variables and expressions replaced by their values), as you would get with set -x in bash, for instance.
While this is feasible for calls to external programs - whose arguments are invariably strings anyway, the challenge is that calls to PowerShell commands supports arguments of arbitrary .NET types, not all of which have a faithful string-literal representation; that said, even a for-human-eyeballs-only string representation is arguably preferable to unexpanded values.
If you need to see expanded argument values and/or control the output format / target:
Note:
While Invoke-Expression (iex) should generally be avoided, due to its inherent security risks and because better, safer options are usually available, it does offer a solution here - as always, be sure that you fully control what goes into the string passed to Invoke-Expression, to avoid potential injection of unwanted commands.
The solution requires you to construct the string to pass to Invoke-Expression with up-front expansion (string-interpolation), so that the resulting command line, when executed, only contains literal arguments, so that echoing the command line paints the full picture of what executable is invoked and what its arguments are.
As noted above, this is only robustly possible if you're calling an external program, such as msbuild.
First, define a helper function that accepts a command-line string, echoes it, and then executes it via Invoke-Expression:
# !! IMPORTANT:
# !! Only pass *trusted* strings to this function - the
# !! argument is blindly passed to Invoke-Expression.
function Invoke-AfterEchoing {
param([string] $commandLine)
# Echo the command line to be executed,
# using the verbose output stream in this example:
Write-Verbose -Verbose "Executing: $commandLine"
Invoke-Expression $commandLine
}
Now you can construct your command-line strings and pass them to the helper function:
Invoke-AfterEchoing #"
& "$msbuild" .\blabBlah.csproj /t:Clean
"#
Invoke-AfterEchoing #"
& "$msbuild" .\blabBlah.csproj /t:Build /p:Configuration=Dev
"#
Note:
A expandable here-string (#"<newline>...<newline>"#) is used to simplify the string-internal quoting.
The expandable form is chosen to ensure that the executable path and all arguments are expanded up front and therefore embedded as their literal values in the resulting string, so that echoing the string will show all actual values.
&, the call operator, is used to invoke msbuild, which is syntactically necessary, given that its path is passed quoted, which in turn is necessary if $msbuild contains a path with spaces.
The output will look something like this:
VERBOSE: Executing: & "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" .\blabBlah.csproj /t:Clean
# ... (output)
VERBOSE: Executing: & "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" .\blabBlah.csproj /t:Build /p:Configuration=Dev
# ... (output)

If you're using write-verbose in your scripts this will happen automatically,
However, if you need to manually write verbose output from your functions, you'll need to manually check that each function is called with the verbose flag. This can be done by checking$PSCmdlet.MyInvocation.BoundParameters["Verbose"]
from inside your function.

Related

How to supply both named and free_form arguments to ansible ad-hoc?

I ran across one of Ansible's modules that take free_form arguments along with named arguments - win_command. A specific example is given, where a powershell script is provided on stdin:
- name: Run an executable and send data to the stdin for the executable
win_command: powershell.exe -
args:
stdin: Write-Host test
I want to use this as a one-off task, so I want to use ad-hoc execution in the style of
ansible <host> -m <module> -a <args...>
Unfortunately, I see no info in the documentation on how to deal with a module that requires specifying both free_form and named arguments. Does anyone know?
Putting the named arguments after the free_form argument puts everything in the free_form argument, resulting in powershell complaining about extraneous arguments
... -m win_command -a 'powershell - stdin=C:\some\script.ps1 -arg1 value_1 -arg2 value_2'
PS: I'm aware I could probably stuff both the script path and arguments in the free_form argument, but I am more interested in learning whether this is possible with ad-hoc, as the docs don't say either way.
I can't test the win_command module directly, but with the command module, which is syntactically very similar, you can reproduce this:
- command: some_command
args:
chdir: /tmp
creates: flagfile
Like this:
ansible -m command -a 'chdir=/tmp creates=flagfile some_command'
Update
Upon investigation...the problem you've encountered with stdin isn't a quoting issue;
it's that when using the k1=v1 k2=v2 somecommand format of passing parameters to e.g. the command module, Ansible only handles specific keys. In lib/ansible/parsing/splitter.py we see:
if check_raw and k not in ('creates', 'removes', 'chdir', 'executable', 'warn'):
raw_params.append(orig_x)
else:
options[k.strip()] = unquote(v.strip())
That is, it only recognizes creates, removes, chdir, executable, and warn as module arguments. I would argue that this is a bug in Ansible. Adding support for the stdin argument is trivial, of course:
if check_raw and k not in ('stdin', 'creates', 'removes', 'chdir', 'executable', 'warn'):
With this change, we can include stdin with spaces as expected:
$ ansible localhost -m command -a 'chdir=/tmp stdin="Hello world" sed s/Hello/Goodbye/'
[WARNING]: Unable to parse /home/lars/.ansible_hosts as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | CHANGED | rc=0 >>
Goodbye world

Converting unix script to windows script - emulating a Sed command in PowerShell

I have a unix script (korn to be exact) that is working well and I need to convert it windows batch script. So far I have tried inserting a powershell command line on my code, but it doesn't work. Please help, I am just new to both unix scripting and windows scripting so any help will do.
This is the line of code that I need to convert:
#create new file to parse ; exclude past instances of timestamp
parsefile=/tmp/$$.parse
sed -e "1,/$TIMESTAMP/d" -e "/$TIMESTAMP/d" $DSTLOGFILE > $parsefile
So far I have tried a powershell command line to be called on my script but it didn't work:
:set_parse_file
#powershell -Command "Get-Content $SCHLOGFILE | Foreach-Object {$_ -replace('1,/"$TIMESTAMP"/d' '/"$TIMESTAMP"/d'} | Set-Content $PARSEFILE"
Any suggestions please?
PowerShell has no sed-like constructs for processing ranges of lines (e.g., sed interprets 1,/foo/ as referring to the range of consecutive lines from line 1 through a subsequent line that matches regex foo)
Emulating this feature with line-by-line processing would be much more verbose, but a comparatively more concise version is possible if the input file is processed as a whole - which is only an option with files small enough to fit into memory as a whole, however (PSv5+ syntax).
Here's the pure PowerShell code:
$escapedTimeStamp = [regex]::Escape($TIMESTAMP)
(Get-Content -Raw $SCHLOGFILE) -replace ('(?ms)\A.*?\r?\n.*?' + $escapedTimeStamp + '.*?\r?\n') `
-replace ('(?m)^.*?' + $escapedTimeStamp + '.*\r?\n') |
Set-Content -NoNewline $PARSEFILE
Note that [regex]::Escape() is used to make sure that the value of $TIMESTAMP is treated as a literal, even if it happens to contain regex metacharacters (chars. with special meaning to the regex engine).
Your ksh code doesn't do that (and it's nontrivial to do in ksh), so if - conversely - $TIMESTAMP should be interpreted as a regex, simply omit that step and use $TIMESTAMP directly.
The -replace operator is regex-based and uses the .NET regular-expression engine.
It is the use of Get-Content's -Raw switch that requires PSv3+ and the use of Set-Content's -NoNewline switch that requires PSv5+. You can make this command work in earlier versions, but it requires more effort.
Calling the above from cmd.exe (a batch file) gets quite unwieldy - and you always have to be wary of quoting issues - but it should work:
#powershell.exe -noprofile -command "$escapedTimeStamp = [regex]::Escape('%TIMESTAMP%'); (Get-Content -Raw '%SCHLOGFILE%') -replace ('(?ms)\A.*?\r?\n.*?' + $escapedTimeStamp + '.*?\r?\n') -replace ('(?m)^.*?' + $escapedTimeStamp + '.*\r?\n') | Set-Content -NoNewline '%PARSEFILE%'"
Note how the -command argument is passed as a single "..." string, which is ultimately the safest and conceptually cleanest way to pass code to PowerShell.
Also note the need to embed batch variables as %varname% in the command, and since they are enclosed in embedded '...' above, the assumption is that their values contain no ' chars.
Therefore, consider implementing your entire script in Powershell - you'll have a much more powerful scripting language at your disposal, and you'll avoid the quoting headaches that come from bridging two disparate worlds.

Powershell where-object return code

I'm returning to powershell from bash after a long time, and I've found the where object behavior to be quite confusing.
Why does the following snippet return success? Nothing is found! Why does this not return failure like a grep would?
C:> Get-Process | ?{$_.name -like "laksdjfajsdfkjasdkf"}
C:> echo $?
True
tl;dr
# Run the command and, in addition to outputting to the console,
# collect the results in variable $result, via common parameter -OutVariable / -ov
# If you do NOT need to output to the console, simply use:
# $result = Get-Process | ...
Get-Process | ? { $_.name -like "laksdjfajsdfkjasdkf" } -ov result
# Test if the result is empty (using implicit Boolean conversion)
if (-not $result) { Write-Warning "Nothing matched." }
PowerShell's automatic (Boolean) $? variable in PowerShell is not the (abstract) equivalent of exit codes in traditional shells, as PetSerAl points out.
$? just tells you whether the last statement succeeded and the rules surrounding it are complicated, as the GitHub discussion that Owain Esau links to shows.
Succeeded means that no errors occurred, and a filtering operation not returning anything is success by that definition.
In short: $? is of limited usefulness in PowerShell.
However, the exit code of the most recently executed external program is reflected in automatic variable $LASTEXITCODE, so had you actually invoked grep, its exit code would be reflected there.
(And while $? is set immediately after execution of an external program to reflect $True if the exit code was 0 and $False otherwise, $? may already reflect something else by the time the statement finishes, depending on the specifics of the statement, such as enclosing the call in (...))
In the case at hand you're looking to determine whether the filtering operation performed by the call to the Where-Object cmdlet (invoked via its built-in alias ?) returned any matches, but in PowerShell that status is not reflected anywhere separately.
Therefore, you must examine the output itself to determine whether anything matched, as shown in the snippet at the top.
There are no errors in this scenario, but for the sake of completeness:
PowerShell's error handling is sophisticated, but complex, and again unlike that of traditional shells; you can find an overview here.

When to use set -e

I come across set -e a some time ago and I admit I love it.
Now, after some time I'm back to write some bash scripting.
My question is if there are some best practices when to use set -e and when not to use it (.e.g. in small/big scripts etc.) or should I rather use a pattern like cmd || exit 1 to track errors?
Yes, you should always use it. People make fun of Visual Basic all the time, saying it's not a real programming language, partly because of its “On Error Resume Next” statement. Yet that is the default in shell! set -e should have been the default. The potential for disaster is just too high.
In places where it's ok for a command to fail, you can use || true or its shortened form ||:, e.g.
grep Warning build.log ||:
In fact you should go a step further, and have
set -eu
set -o pipefail
at the top of every bash script.
-u makes it an error to reference a non-existent environment variable such as ${HSOTNAME}, at the cost of requiring some gymnastics with checking ${#} before you reference ${1}, ${2}, and so on.
pipefail makes things like misspeled-command | sed -e 's/^WARNING: //' raise errors.
If your script code checks for errors carefully and properly where necessary, and handles them in an appropriate manner, then you probably don't ever need or want to use set -e.
On the other hand if your script is a simple sequential list of commands to be run one after another, and if you want the script to terminate if any one of those fail, then sticking set -e at the top would be exactly what you would want to do to keep your script simple and uncluttered. A perfect example of this would be if you're creating a script to compile a set of sources and you want the compile to stop after the first file with errors is encountered.
More complex scripts can combine these methods since you can use set +e to turn its effect back off again and go back to explicit error checking.
Note that although set -e is supposed to cause the shell to exit IFF any untested command fails, it is wise to turn it off again when your code is doing its own error handling as there can easily be weird cases where a command will return a non-zero exit status that you're not expecting, and possibly even such cases that you might not catch in testing, and where sudden fatal termination of your script would leave something in a bad state. So, don't use set -e, or leave it turned on after using it briefly, unless you really know that you want it.
Note also that you can still define an error handler with trap ERR to do something on an error condition when set -e is in effect, as that will still be run before the shell exits.
You love it!?
For my self, I prefer in a wide, having in my .bashrc a line like this:
trap '/usr/games/fortune /usr/share/games/fortunes/bofh-excuses' ERR
( on debian: apt-get install fortunes-bofh-excuses :-)
But it's only my preference ;-)
More seriously
lastErr() {
local RC=$?
history 1 |
sed '
s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/;
s/$/", rc: '"$RC/"
}
trap "lastErr" ERR
Gna
bash: Gna : command not found
cmd: "Gna", args: "", rc: 127
Gna gna
cmd: "Gna", args: "gna", rc: 127
"Gna gna" foo
cmd: "Gna gna", args: "foo", rc: 127
Well, from there, you could:
trap "lastErr >>/tmp/myerrors" ERR
"Gna gna" foo
cat /tmp/myerrors
cmd: "Gna gna", args: "foo", rc: 1
Or better:
lastErr() {
local RC=$?
history 1 |
sed '
s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/;
s/$/", rc: '"$RC/
s/^/$(date +"%a %d %b %T ")/"
}
"Gna gna" foo
cat /tmp/myerrors
cmd: "Gna gna", args: "foo", rc: 1
Tue 20 Nov 18:29:18 cmd: "Gna gna", args: "foo", rc: 127
... You could even add other informations like $$, $PPID, $PWD or maybe your..
When this option is on, if a simple command fails for any of the reasons listed in Consequences of Shell Errors or returns an exit status value >0, and is not part of the compound list following a while, until, or if keyword, and is not a part of an AND or OR list, and is not a pipeline preceded by the ! reserved word, then the shell shall immediately
exit.

Extracting a 7-Zip file "silently" - command line option

I want to extract a 7-Zip archive in a Python script. It works fine except that it spits out the extraction details (which is huge in my case).
Is there a way to avoid this verbose information while extracting? I did not find any "silent" command line option to 7z.exe.
My command is
7z.exe -o some_dir x some_archive.7z
I just came across this when searching for the same, but I solved it myself! Assuming the command is processed with Windows / DOS, a simpler solution is to change your command to:
7z.exe -o some_dir x some_archive.7z > nul
That is, direct the output to a null file rather than the screen.
Or you could pipe the output to the DOS "find" command to only output specific data, that is,
7z.exe -o some_dir x some_archive.7z | FIND "ing archive"
This would just result in the following output.
Creating archive some_archive.7z
or
Updating archive some_archive.7z**
My final solution was to change the command to
... some_archive.7z | FIND /V "ing "
Note double space after 'ing'. This resulted in the following output.
7-Zip 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
Scanning
Updating some_archive.7z
Everything is Ok
This removes the individual file processing, but produces a summary of the overall operation, regardless of the operation type.
One possibility would be to spawn the child process with popen, so its output will come back to the parent to be processed/displayed (if desired) or else completely ignored (create your popen object with stdout=PIPE and stderr=PIPE to be able to retrieve the output from the child).
Like they said, to hide most of the screen-filling messages you could use ... some_archive.7z | FIND /V "Compressing" but that "FIND" would also remove the error messages that had that word. You would not be warned. That "FIND" also may have to be changed because of a newer 7-zip version.
7-zip has a forced verbose output, no silence mode, mixes stderr and stdout(*), doesn't save Unix permissions, etc. Those anti-standards behaviors together put "7-zip" in a bad place when being compared to "tar+bzip2" or "zip", for example.
(*) "Upstream (Igor Pavlov) does not want to make different outputs for messages, even though he's been asked several times to do so :(" http://us.generation-nt.com/answer/bug-346463-p7zip-stdout-stderr-help-166693561.html - "Igor Pavlov does not want to change this behaviour" http://sourceforge.net/tracker/?func=detail&aid=1075294&group_id=111810&atid=660493
7zip does not have an explicit "quiet" or "silent" mode for command line extraction.
One possibility would be to spawn the child process with popen, so its output will come back to the parent to be processed/displayed (if desired) or else completely ignored (create your popen object with stdout=PIPE and stderr=PIPE to be able to retrieve the output from the child).
Otherwise Try doing this:
%COMSPEC% /c "%ProgramFiles%\7-Zip\7z.exe" ...
Expanding on #Matthew 's answer and this answer https://superuser.com/questions/194659/how-to-disable-the-output-of-7-zip
I'm using FINDSTR instead of find so I can chain multiple lines to exclude and blank lines as well:
7za.exe a test1.zip .\foldertozip | FINDSTR /V /R /C:"^Compressing " /C:"Igor Pavlov" /C:"^Scanning$" /C:"^$" /C:"^Everything is Ok$"
/V: exclude
/R: regex
/C:"^Compressing " : begining of line, Compressing, 2 spaces
/C:"^Scanning$" : the word Scanning on its own on a line (begining/end)
/C:"^$" : a begining and end without anything in between, ie, a blank line
I'm using /C so that a space is a space, otherwise it's a separator between multiple words to exlude as in this simpler version:
FINDSTR /V "Compressing Pavlov Scanning Everytyhing"
(the same caveats exist, if the wording changes in a new version, or if a useful line starts with the word "Compressing ", it will not work as expected).
If you're running 7-zip.exe from Powershell, and you only want to see errors, then you could try something like this:
7-zip.exe u <Target> <Source> | Select-String "Error" -Context 10
This will only display the "Error" message line and the surrounding 10 lines (or whatever number) to capture the error specific output.
The | FIND is a good alternative to show what happened without displaying insignificant text.
Examining 7zip source I found hidden -ba switch that seems to do the trick. Unfortunately it is not finished. I managed to make it work with several modifications of sources but it's just a hack. If someone's interested, the option variable is called options.EnableHeaders and changes are required in CPP/7zip/UI/Console/Main.cpp file.
Alternatively you can poke 7Zip's author to finish the feature in tracker. There are several requests on this and one of them is here.
7-zip has not such an option. Plus the lines printed at each file compressed are supposed to display at the same spot without newline, erasing the previous one, which has a cool effect. Unfortunatly, in some contexts (Jenkins...) it produced several lines ☹️ flooding the console.
NUL (windows) is maybe one solution.
7-zip.exe -o some_dir x some_archive.7z>NUL
To show just the last 4 lines...
7z x -y some_archive.7z | tail -4
gives me:
Everything is Ok
Size: 917519
Compressed: 171589
The switch -y is to answer yes to everything (in my case to override existing files).
On Unix-like operating systems (Linux, BSD, etc.) the shell command 7z ... >/dev/null will discard all text written by 7z to standard output. That should cover all the status/informational messages written by 7z.
It seems that 7z writes error messages to standard error so if you do >/dev/null, error messages will still be shown.
As told by Fr0sT above, -ba switch outputs only valid things (at least in list option on which I was trying).
7z.exe l archive_name.zip
7z.exe l -ba archive_name.zip
made great difference, esp for parsing the output in scripts.
There is no need to modify anything, just use -ba switch in version19. This was also told bysomeone above. I'm putting as answer as I can't comment.
You can stop 7-Zip from displaying prompts by using the -y switch. This will answer yes to all prompts. Use this only when you are confident.

Resources