What is the dash ("-") when used with pipe ("|") in CMD? - powershell

I wanted to create some clickable PowerShell scripts, and I found this answer that I modified slightly to be:
;#Findstr -bv ;#F %0 | powershell -noprofile -command - & goto:eof
# PowerShell Code goes here.
I understand Findstr is passing all lines that don't begin with ;#F to the right-hand side of the pipe and the dash specifies where the input should go, but what is the dash character called and where is it documented?
I found an explanation of CMD's pipe operator on Microsoft's Using command redirection operators, but it doesn't mention anything about the dash character.

I presume you mean the - that precedes the &. It has nothing to do with the pipe operator, it is a directive for powershell.
Here is a description of the -Command option excerpted from powershell help (accessed by powershell /?)
-Command
Executes the specified commands (and any parameters) as though they were
typed at the Windows PowerShell command prompt, and then exits, unless
NoExit is specified. The value of Command can be "-", a string. or a
script block.
If the value of Command is "-", the command text is read from standard
input.
BTW - I did not realize FINDSTR accepted - as an option indicator until I saw your question. I've only seen and used /. Good info to know.

The - is to Powershell saying accept the command(s) from stdin rather than from arguments. This is not a feature in cmd / batch and piping. It would work with < as well.

Powershell version 2 adds a "Run with Powershell" right-click context menu item to run scripts . Here you'll find some enhanced shell extensions to run Powershell scripts with elevated privileges. However if you just want to run a Powershell script by double clicking a file, I recommend just calling the Powershell script from a batch script instead of trying to embed Powershell code in the batch script. In the batch script use this: powershell.exe -file "%~dp0MyScript.ps1" where %~dp0 expands to the current directory. This essentially creates a bootstrapper for your Powershell script that you can double click to launch your Powershell script.

Related

Powershell Script only opens as a .txt file when run

Hi I have a script on a memory stick that I want to be able to run in cmd line before a computer has windows fully installed. (the stage just after you've connected to a network).
Cmd used to run the script.
Start > Run D:\pscript\Intune.ps1
This only opens a .txt file, while researching I've found that the reason this happens is due to security, is there anyway to override this bar changing the default file type out.
Unlike batch files (.cmd, .bat) associated with cmd.exe (the legacy Command Prompt), PowerShell's .ps1 script files are by default not directly executable from outside PowerShell.
Instead, they are treated as documents that are by default associated with either Notepad or the (obsolescent) Windows PowerShell ISE, depending on the , and invoking them therefore opens them for editing, which applies to the following contexts:
Invoking a .ps1 file from cmd.exe
Invoking a .ps1 file from Start Menu's Run dialog, as in your case (which you can invoke with WinKey+R, for instance)
Opening (double-clicking) a .ps1 file from File Explorer or Desktop.
To execute a .ps1 script from outside PowerShell, you must therefore invoke it via PowerShell's CLI, powershell.exe for Windows PowerShell, pwsh for PowerShell (Core) 7+.
In the simplest case, using Windows PowerShell and the -File parameter, as also shown by Mathias R. Jessen in a comment; see the comments below and the linked docs for additional parameters:
# Note:
# * The effective execution policy applies; use -ExecutionPolicy Bypass to bypass.
# * Profiles are loaded; use -NoProfile to suppress.
# * The console window auto-closes when the script terminates; use -NoExit
# to keep the session open.
powershell.exe -File D:\pscript\Intune.ps1
For a comprehensive overview of PowerShell's CLI, see this post.
It is possible - though not advisable - to configure your system to execute .ps1 files by default - see this answer.

I am trying to run several PowerShell commands from a batch script, however the "%" symbol does not get transferred

I am trying to run several PowerShell commands from a batch script, however the "%" symbol does not get transferred to PowerShell.
For example, writing the following in a command prompt window:
powershell -Command "& {echo 'per%entage'}"
Will print:
per%entage
which is what I want, however if I save the same command into a .bat or .cmd file, it instead prints:
perentage
Why is it ignoring the "%" symbol? Is there a way to make it transfer properly? I'm especially confused that it works in a command prompt window, but not in a batch script. You'd think both would either work or not work.
Very unfortunately, cmd.exe's behavior differs with respect to command invocations from an interactive prompt vs. from a batch file with respect to how % characters are interpreted.
See this answer for background information.
Therefore, when calling from a batch file, a % char. that is to interpreted verbatim, must be escaped as %%:
:: From a *batch file*.
powershell -Command "'per%%entage'"
Note:
echo is a built-in alias for the Write-Output cmdlet, whose explicit use is rarely needed - see this answer for more information.
Invocation of commands (symbolized as ... here) in the form & { ... } is virtually never needed when using the PowerShell CLI - just use ... as-is.
Generally, for predictable invocation, it's worth using the -NoProfile switch as well - before the -Command parameter - so as to bypass loading of PowerShell's profile files, which are primarily meant for interactive sessions.

Command line arguments for msiexec break on PowerShell if they contain space

I'm trying to set a public property in an InstallShield installer with a value containing space. While running the MSI installer, I'm using below command on PowerShell prompt. Since the value contains a space so I used double quotes to pass the value
msiexec -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"
It breaks the command as the argument value C:\new folder\data.txt has a space in the string new folder. It results in showing up below error prompt of msiexec:
It suggests that arguments passed to the msiexec command has some problem.
But if I execute the same command on Windows default command prompt then it runs fine:
Few other options that I've tried to make things work on PowerShell prompt are as below:
Using single quote in place of double quotes
Using a back tick (`) character before space in the argument as per this answer.
Try with this
msiexec -i "myinstaller.msi" MYDIRPATH=`"C:\new folder\data.txt`"
The escape character in PowerShell is the grave-accent(`).
Note:
This answer addresses direct, but asynchronous invocation of msiexec from PowerShell, as in the question. If you want synchronous invocation, use Start-Process with the -Wait switch, as shown in Kyle 74's helpful answer, which also avoids the quoting problems by passing the arguments as a single string with embedded quoting.
Additionally, if you add the -PassThru switch, you can obtain a process-information object that allows you to query msiexec's exit code later:
# Invoke msiexec and wait for its completion, then
# return a process-info object that contains msiexec's exit code.
$process = Start-Process -Wait -PassThru msiexec '-i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"'
$process.ExitCode
Note: There's a simple trick that can make even direct invocation of msiexec synchronous: pipe the call to a cmdlet, such as Wait-Process
(msiexec ... | Wait-Process) - see this answer for more information.
To complement Marko Tica's helpful answer:
Calling external programs in PowerShell is notoriously difficult, because PowerShell, after having done its own parsing first, of necessity rebuilds the command line that is actually invoked behind the scenes in terms of quoting, and it's far from obvious what rules are applied.
Note:
While the re-quoting PowerShell performs behind the scenes in this particular case is defensible (see bottom section), it isn't what msiexec.exe requires.
Up to at least PowerShell 7.1, some of the re-quoting is downright broken, and the problems, along with a potential upcoming (partial) fix, are summarized in this answer.
Marko Tica's workaround relies on this broken behavior, and with the for now experimental feature that attempts to fix the broken behavior (PSNativeCommandArgumentPassing, available since Core 7.2.0-preview.5), the workaround would break. Sadly, it looks like then simply omitting the workaround won't work either, because it was decided not to include accommodations for the special quoting requirements of high-profile CLIs such as msiexec - see GitHub issue #15143.
To help with this problem, PSv3+ offers --%, the stop-parsing symbol, which is the perfect fit here, given that the command line contains no references to PowerShell variables or expressions: --% passes the rest of the command line as-is to the external utility, save for potential expansion of %...%-style environment variables:
# Everything after --% is passed as-is.
msiexec --% -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"
If you do need to include the values of PowerShell variables or expressions in your msiexec call, the safest option is to call via cmd /c with a single argument containing the entire command line; for quoting convenience, the following example uses an expandable here-string (see the bottom section of this answer for an overview of PowerShell's string literals).
$myPath = 'C:\new folder\data.txt'
# Let cmd.exe invoke msiexec, with the quoting as specified.
cmd /c #"
msiexec --% -i "myinstaller.msi" MYDIRPATH="$myPath"
"#
If you don't mind installing a third-party module, the ie function from the Native module (Install-Module Native) obviates the need for any workarounds: it fixes problems with arguments that have embedded " chars. as well as empty-string arguments and contains important accommodations for high-profile CLIs such as msiexec on Windows, and will continue to work as expected even with the PSNativeCommandArgumentPassing feature in effect:
# `ie` takes care of all necessary behind-the-scenes re-quoting.
ie msiexec -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"
As for what you tried:
PowerShell translated
MYDIRPATH="C:\new folder\data.txt" into
"MYDIRPATH=C:\new folder\data.txt" behind the scenes - note how the entire token is now enclosed in "...".
Arguably, these two forms should be considered equivalent by msiexec, but all bets are off in the anarchic world of Windows command-line argument parsing.
This is the best way to install a program in general with Powershell.
Here's an example from my own script:
start-process "c:\temp\SQLClient\sqlncli (x64).msi" -argumentlist "/qn IACCEPTSQLNCLILICENSETERMS=YES" -wait
Use Start-Process "Path\to\file\file.msi or .exe" -argumentlist (Parameters) "-qn or whatever" -wait.
Now -wait is important, if you have a script with a slew of programs being installed, the wait command, like piping to Out-Null, will force Powershell to wait until the program finishes installing before continuing forward.

Are all CMD commands available within powershell?

Similar to other questions, but are ALL CMD commands usable within Powershell? In short, can I use a Powershell window to supplant CMD prompts, and do both CMD work and Powershell work in the same terminal?
Those commands that are built into cmd.exe (e.g., dir, type) are not directly callable in a PowerShell session, but you can call them via cmd /c ...; e.g.:
PS> cmd /c ver
However, you'll find that most such commands have more powerful PowerShell counterparts.
To ease the transition, some PowerShell commands (called cmdlets) have aliases named for their cmd.exe predecessors (e.g., dir is aliased to Get-ChildItem; use Get-Command <name> to find out what command a given name refers to).
Note that PowerShell also provides superior replacements for external utilities; e.g., PowerShell's Select-String is a superior alternative to findstr.exe.
Unlike the built-in cmd.exe commands, you can invoke such external utilities directly from PowerShell.
You can run where.exe <name> to determine whether a given command name refers to a built-in cmd.exe command or an external utility: if it is the latter, its full path is printed.
(This technique works from both cmd.exe and PowerShell, but in PowerShell you must include the .exe extension, because just where means something different: it is an alias of the Where-Object cmdlet.)
In all these cases it's important to understand that PowerShell syntax works very differently and that the arguments you pass will be interpreted by PowerShell first:
Get-Help about_Parsing provides a high-level overview.
Notably, PowerShell has many more metacharacters than cmd.exe (more characters have special syntactical meaning).
Of particular interest when calling cmd /c or invoking an external utility is the PSv3+ stop-parsing token, --%, which treats the remainder of the command as if it had been invoked from cmd.exe; e.g.:
cmd /c --% echo %windir%
Caveat: --% has many limitations and pitfalls - see the bottom section of this answer.
Yes, kind of.
Powershell sometimes use different syntax for the commands, so if you have specific commands you often use in CMD, you might want to do a quick search for those first. Most commands are the same though. Be aware that a bunch of powershell commands can't be run without the window having admin privileges, and a PS window does not guarantee that it has those privileges.
The new update to Windows 10 did away with normal CMD windows in favor of PS. This does mean that it shows up electric blue, but you can always change that using the options in Defaults or Properties. I believe this change in Win10 also meant they set aliases for CMD within PS for us, but don't quote me on that one.
If you use cmd command, it converts a PowerShell session to a Command Prompt session.
Also, if you need Powershell or Command prompt commands, you can switch back and forth in either interface by typing powershell and cmd, respectively.

Execute batch file in Powershell

I want to execute the following from a batch file:
"C:\OpenCover\tools\OpenCover.Console.exe" -register:user -target:"%VS110COMNTOOLS%..\IDE\mstest.exe" -targetargs:"/testcontainer:\"C:\Develop\bin\Debug\MyUnitTests.dll\" ... "
PAUSE
Now I would like to log the output of the process to a file for which I came across the quite handy powershell usage of
powershell "dir | tee output.log"
but this does not take my batch file as first argument (powershell "my.bat | tee output.log") because it is not the name of a cmdlet or a function or a script file.
I could change my batch file so that is says powershell "OpenCover.Console.exe..." but I would have to adapt all quotes and change escape characters and so forth.
Is there a way to make a batch file execute in powershell? Or is there a way to drop in my line unchanged from the batch after some powershell command and it all executes "like it ought to"?
Unless your batch file is in a folder in the %PATH%, PowerShell won't find it [1], so you'll have to supply an explicit file path (whether relative or absolute).
For instance, if the batch file is in the current folder, run:
powershell -c ".\my.bat | tee output.log"
Consider adding -noprofile to suppress loading of the profile files, which is typically only needed in interactive sessions.
If your batch file path contains embedded spaces, enclose it in single quotes and prepend &:
powershell -c "& '.\my script.bat' | tee output.log"
Note: I've deliberately added the -c (short for: -Command) parameter name above; while powershell.exe - Windows PowerShell - defaults to this parameter, that is no longer true in PowerShell [Core] v6+ (whose executable name is pwsh), where -File is now the default - see about_PowerShell.exe and about_pwsh
[1] More accurately, PowerShell - unlike cmd.exe - will by design not execute scripts in the current folder by their mere filename (in an interactive PowerShell session you'll get a hint to that effect). This is a security feature designed to prevent accidental invocation of a different executable than intended.
Unless you have some purpose for doing so not stated in the OP, there isn't a reason to use both Powershell and a batch script. If you want to do this solely from PS, you can create a PS script that does everything the batch file does.
To avoid the escaping issues (or alternatively to take advantage of CMD.EXE's somewhat strange escaping behavior :-) you can use --%, introduced in PS 3.0. It is documented under about_escape_characters.