I have this command that works ok on powershell
Compare-Object (Get-Content "tex1.txt") (Get-Content "tex2.txt") | Where-Object{$_.SideIndicator -eq "<="} | select inputobject | ft -hidetableheaders
I'm trying to running in cmd by doing this:
powershell -Command " & {Compare-Object (Get-Content "tex1.txt") (Get-Content "tex2.txt") | Where-Object{$_.SideIndicator -eq "<="} | select inputobject | ft -hidetableheaders}"
but it says something like: the name, the directory or the volume syntax is incorrect (is in spanish so i dont know the exact translation)
I think the problem is the pipes, since running everything before the pipe: Compare-Object (Get-Content "tex1.txt") (Get-Content "tex2.txt") works
PD: I also tried to write ^ before the pipes but I haven't succeeded.
tl;dr
When calling the PowerShell CLI (powershell.exe for Windows PowerShell, pwsh for the cross-platform PowerShell [Core] 6+ edition):
Using embedded " in an overall "..." string comes with escaping challenges.
If feasible for a given command, formulating it without embedded " is the easiest solution:
powershell -Command "Compare-Object (Get-Content tex1.txt) (Get-Content tex2.txt) | Where-Object {$_.SideIndicator -eq '<='} | select inputobject | ft -hidetableheaders"
Read on, if you do need to use embedded ".
eryksun points out that your problem is your lack of escaping of embedded " chars. inside the overall "..." string, which causes cmd.exe to see multiple strings, including parts it considers unquoted, which causes problems with special characters such as | and < - they never reach PowerShell.
Nesting double-quote strings from cmd.exe is tricky business:
To make cmd.exe happy, you need to double the embedded " chars. ("")
Separately, to make powershell.exe happy, you need to \-escape " chars.
Note: PowerShell [Core] 6+, the cross-platform edition, on Windows now also accepts "" by itself, which is the most robust choice.
Generally, dealing with quoting and escaping arguments when calling from cmd.exe is a frustrating experience with no universal solutions, unlike in the Unix world. Sadly, PowerShell has its own challenges, even in the Unix world.[1]
In short: Escape embedded " chars. when calling the Windows PowerShell CLI, powershell.exe, from cmd.exe as follows (for the PowerShell (Core) 7+ CLI, pwsh.exe, "" is the robust choice):
Use "^"" (sic) when using powershell.exe -Command, which works robustly.
Caveat: "^"" does not work for calling other programs.
This saves you from additional escaping, as would be necessary if you used \"
Example: powershell -command " Write-Output "^""a & b"^"" " yields a & b, as expected, and the & didn't need escaping.
If you use the simpler - and customary - \", you may need to perform additional escaping: Specifically, you must individually ^-escape the following cmd.exe metacharacters with ^ inside \"...\" runs: & | < > ^Thanks, LotPings.
Example: powershell -command " Write-Output \"a ^& b\" " yields a & b; that is, the & needed escaping with ^.
Additionally, to treat % (and, with enabledelayedexpansion , !) verbatim, the escaping syntax unfortunately depends on whether you're calling from the command line or a batch file: use %^USERNAME% (!^USERNAME) from the former, and %%USERNAME%% (^!USERNAME^! / ^^!USERNAME^^! inside \"...\" runs) from the latter - see this answer for the gory details.
It is unfortunate that cmd.exe makes use of \" treacherous, given that it is supported by virtually all programs (except batch files), and if it weren't for these extra escaping requirements, command lines that use it have the potential to work across different platforms and shells - with the notable exception of calling from PowerShell, where, sadly, an additional layer of escaping is needed and " inside "..." must be escaped as \`" (sic); see this answer.
See the bottom section for ways to ease the escaping pain by avoiding use of nested ".
Other programs, including PowerShell Core:
Use just "" for programs compiled with Microsoft compilers and, on Windows, also Python and Node.js as well as PowerShell Core (pwsh.exe).
Regrettably, this robust option does not work with powershell.exe, i.e. Windows PowerShell.
Use \" for programs with Unix heritage, such as Perl and Ruby - which comes with the escaping headaches discussed above.
Avoiding embedded ":
When you call PowerShell's CLI, you can often get away without needing to embed double quotes:
There may be arguments in your string that don't require quoting at all, such as text1.txt and text2.txt
You can alternatively use single-quoting ('...') inside the overall command string, which require no escaping; note that such strings, from PowerShell's perspective, are string literals.
To put it all together:
powershell -Command "Compare-Object (Get-Content tex1.txt) (Get-Content tex2.txt) | Where-Object {$_.SideIndicator -eq '<='} | select inputobject | ft -hidetableheaders"
Note that I've also removed the & { ... } around your command, as it isn't necessary.
[1] eryksun puts it as follows: "This is the inescapable frustration of the Windows command line. Every program parses its own command line, using whatever rules it wants. So the syntax of a command line has to work with not only the shell (CMD) but also all programs invoked in the pipeline. In the Unix world the shell parses the command line into argv arrays, so typically you only have to get the syntax right to make the shell happy."
The problems with PowerShell Core, even on Unix, stem from how it re-quotes arguments behind the scenes before passing them on - see this GitHub docs issue.
Related
This self-answered question aims to give a systematic overview of the PowerShell CLI (command-line interface), both for Windows PowerShell (powershell.exe) and PowerShell (Core) v6+ (pwsh.exe on Windows, pwsh on Unix).
While official help topics exist (see the links in the answer), they do not paint the full picture and lack systematic treatment (as of this writing).
Among others, the following questions are answered:
How do the edition-specific CLIs differ?
How do I pass PowerShell code to be executed to the CLIs? How do -Command (-c) and -File (-f) differ?
How do the arguments passed to these parameters need to be quoted and escaped?
What character-encoding issues come into play?
How do the PowerShell CLIs handle stdin input, and what stdout / stderr do they produce, and in what format?
PowerShell CLI fundamentals:
PowerShell editions: The CLI of the legacy, bundled-with-Windows Windows PowerShell edition is powershell.exe, whereas that of the cross-platform, install-on-demand PowerShell (Core) 7+ edition is pwsh.exe (just pwsh on Unix-like platforms).
Interactive use:
By default, unless code to execute is specified (via -Command (-c) or -File (-f, see below), an interactive session is entered. However, unlike in POSIX-compatible shells such as bash, you can use -NoExit to still enter an interactive session after executing code. This is especially handy for troubleshooting command lines when the CLI is called without a preexisting console window.
Use -NoLogo to suppress the startup text that is shown when entering an interactive session (not needed if code to execute is passed). GitHub issue #15644 suggest not showing this startup text by default.
To opt out of telemetry / update notifications, define the following environment variables before entering an interactive session: POWERSHELL_TELEMETRY_OPTOUT=1 / POWERSHELL_UPDATECHECK=Off
Parameters and defaults:
All parameter names are case-insensitive (as PowerShell generally is); most parameters have short aliases, such as -h and -? for -Help, which shows command-line help, which with pwsh (but not powershell.exe) also lists these short aliases.
Caveat: For long-term stability of your code, you should either use the full parameter names or their official aliases. Note that PowerShell's "elastic syntax" also allows you to use prefixes of parameter names ad hoc, as long as such a prefix unambiguously identifies the target parameter; e.g., -ver unambiguously targets -version currently, but - at least hypothetically - such a call could break in the future if a new parameter whose name also starts with ver were to be introduced.
pwsh supports more parameters than powershell.exe, such as -WorkingDirectory (-wd).
There are two (mutually exclusive) ways to pass code to execute, in which case the PowerShell process exits automatically when execution ends; pass -NonInteractive to prevent use of interactive commands in the code or -NoExit to keep the session open after execution:
-Command (-c) is for passing arbitrary PowerShell commands, which may be passed either as a single string or as individual arguments, which, after removing (unescaped) double-quotes, are later joined with spaces and then interpreted as PowerShell code.
-File (-f) is for invoking script files (.ps1) with pass-through arguments, which are treated as verbatim values.
These parameters must come last on the command line, because all subsequent arguments are interpreted as part of the command being passed / the script-file call.
See this answer for guidance on when to use -Command vs. -File, and the bottom section for quoting / escaping considerations.
It is advisable to use -Command (-c) or -File (-f) explicitly, because the two editions have different defaults:
powershell.exe defaults to -Command (-c)
pwsh defaults to -File (-f), a change that was necessary for supporting shebang lines on Unix-like platforms.
Unfortunately, even with -Command (-c) or -File (-f), profiles (initialization files) are loaded by default (unlike POSIX-compatible shells such as bash, which only do so when starting interactive shells).
Therefore, it is advisable to routinely precede -Command (-c) or -File (-f) with -NoProfile (-nop), which suppresses profile loading for the sake of both avoiding extra overhead and a more predictable execution environment (given that profiles can make changes that affect all code executed in a session).
GitHub proposal #8072 discusses introducing a separate CLI (executable) that does not load profiles in combination with these parameters and could also improve other legacy behaviors that the existing executables cannot change for the sake of backward-compatibility.
Character encoding (applies to both in- and output streams):
Note: The PowerShell CLIs only ever process text[1], both on input and output, never raw byte data; what the CLIs output by default is the same text you would see in a PowerShell session, which for complex objects (objects with properties) means human-friendly formatting not designed for programmatic processing, so to output complex objects it's better to emit them in a structured text-based format, such as JSON.
Note what while you can use -OutputFormat xml (-of xml) to get CLIXML output, which uses XML for object serialization, this particular format is of little use outside of PowerShell; ditto for accepting CLIXML input via stdin (-InputFormat xml / -if xml).
On Windows, the PowerShell CLIs respect the console's code page, as reflected in the output from chcp and, inside PowerShell, in [Console]::InputEncoding. A console's code page defaults to the system's active OEM code page.
Caveat: OEM code pages such as 437 on US-English systems are fixed, single-byte character encodings limited to 256 characters in total. To get full Unicode support, you must switch to code page 65001 before calling a PowerShell CLI (from cmd.exe, call chcp 65001); while this works in both PowerShell editions, powershell.exe unfortunately switches the console to a raster font in this case, which causes many Unicode characters not to display properly; however, the actual data is not affected.
On Windows 10, you may switch to UTF-8 system-wide, which sets both the OEM and the ANSI code page to 65001; note, however, that this has far-reaching consequences, and that the feature is still in beta as of this writing - see this answer.
On Unix-like platforms (pwsh), UTF-8 is invariably used (even if the active locale (as reported by locale) is not UTF-8-based, but that is very rare these days).
Input-stream (stdin) handling (received via stdin, either piped to a CLI call or provided via input redirection <):
To process stdin input as data:
Explicit use of the automatic $input variable is required.
This in turn means that in order to pass stdin input to a script file (.ps1), -Command (-c) rather than -File (-f) must be used. Note that this makes any arguments passed to the script (symbolized with ... below) subject to interpretation by PowerShell (whereas with -File they would be used verbatim):
-c "$Input | ./script.ps1 ..."
To process stdin input as code (pwsh only, seems to be broken in powershell.exe):
While passing PowerShell code to execute via stdin works in principle (by default, which implies -File -, and also with -Command -), it exhibits undesirable pseudo-interactive behavior and prevents passing of arguments: see GitHub issue #3223; e.g.:
echo "Get-Date; 'hello'" | pwsh -nologo -nop
Output-stream (stdout, stderr) handling:
(Unless you use a script block ({ ... }), which only works from inside PowerShell, see below), all 6 PowerShell's output streams are sent to stdout, including errors(!) (the latter are normally sent to stderr).
However, when you apply an - external - stderr redirection you can selectively suppress error-stream output (2>NUL from cmd.exe, 2>/dev/null on Unix) or send it to a file (2>errs.txt).
See the bottom section of this answer for more information.
Quoting and escaping of the -Command (-c) and -File (-f) arguments:
When calling from PowerShell (rarely needed):
There is rarely a need to call the PowerShell CLI from PowerShell, as as any command or script can simply be called directly and, conversely, calling the CLI introduces overhead due to creating a child process and results in loss of type fidelity.
If you still need to, the most robust approach is to use a script block ({ ... }), which avoids all quoting headaches, because you can use PowerShell's own syntax, as usual. Note that using script blocks only works from inside PowerShell, and that you cannot refer to the caller's variables in the script block; however, you can use the -args parameter to pass arguments (based on the caller's variables) to the script block, e.g., pwsh -c { "args passed: $args" } -args foo, $PID; using script blocks has additional benefits with respect to output streams and supporting data types other than strings; see this answer.
# From PowerShell ONLY
PS> pwsh -nop -c { "Caller PID: $($args[0]); Callee PID: $PID" } -args $PID
When calling from outside PowerShell (the typical case):
Note:
-File (-f) arguments must be passed as individual arguments: the script-file path, followed by arguments to pass to the script, if any. Both the script-file path and the pass-through arguments are used verbatim by PowerShell, after having stripping (unescaped) double quotes on Window[2].
-Command (-c) arguments may be passed as multiple arguments, but in the end PowerShell simply joins them together with spaces, after having stripped (unescaped) double quotes on Windows, before interpreting the resulting string as PowerShell code (as if you had submitted it in a PowerShell session).
For robustness and conceptual clarity, it is best to pass the command(s) as a single argument to -Command (-c), which on Windows requires a double-quoted string ("...") (although the overall "..." enclosure isn't strictly necessary for robustness in no-shell invocation environments such as Task Scheduler and some CI/CD and configuration-management environments, i.e. in cases where it isn't cmd.exe that processes the command line first).
Again, see this answer for guidance on when to use -File (-f) vs. when to use -Command (-c).
To test-drive a command line, call it from a cmd.exe console window, or, in order to simulate a no-shell invocation, use WinKey-R (the Run dialog) and use -NoExit as the first parameter in order to keep the resulting console window open.
Do not test from inside PowerShell, because PowerShell's own parsing rules will result in different interpretation of the call, notably with respect to recognizing '...' (single-quoting) and potential up-front expansion of $-prefixed tokens.
On Unix, no special considerations apply (this includes Unix-on-Windows environments such as WSL and Git Bash):
You only need to satisfy the calling shell's syntax requirements. Typically, programmatic invocation of the PowerShell CLI uses the POSIX-compatible system default shell on Unix, /bin/sh), which means that inside "..." strings, embedded " must be escaped as \", and $ characters that should be passed through to PowerShell as \$; the same applies to interactive calls from POSIX-compatible shells such as bash; e.g.:
# From Bash: $$ is interpreted by Bash, (escaped) $PID by PowerShell.
$ pwsh -nop -c " Write-Output \"Caller PID: $$; PowerShell PID: \$PID \" "
# Use single-quoting if the command string need not include values from the caller:
$ pwsh -nop -c ' Write-Output "PowerShell PID: $PID" '
On Windows, things are more complicated:
'...' (single-quoting) can only be used with -Command (-c) and never has syntactic function on the PowerShell CLI command line; that is, single quotes are always preserved and interpreted as verbatim string literals when the parsed-from-the-command-line argument(s) are later interpreted as PowerShell code; see this answer for more information.
"..." (double-quoting) does have syntactic command-line function, and unescaped double quotes are stripped, which in the case of -Command (-c) means that they are not seen as part of the code that PowerShell ultimate executes. " characters you want to retain must be escaped - even if you pass your command as individual arguments rather than as part of a single string.
powershell.exe requires " to be escaped as \"[3] (sic) - even though inside PowerShell it is ` (backtick) that acts as the escape character; however \" is the most widely established convention for escaping " chars. on Windows command lines.
Unfortunately, from cmd.exe this can break calls, if the characters between two \" instances happen to contain cmd.exe metacharacters such as & and |; the robust - but cumbersome and obscure - choice is "^""; \" will typically work, however.
:: powershell.exe: from cmd.exe, use "^"" for full robustness (\" often, but not always works)
powershell.exe -nop -c " Write-Output "^""Rock & Roll"^"" "
:: With double nesting (note the ` (backticks) needed for PowerShell's syntax).
powershell.exe -nop -c " Write-Output "^""The king of `"^""Rock & Roll`"^""."^"" "
:: \" is OK here, because there's no & or similar char. involved.
powershell.exe -nop -c " Write-Output \"Rock and Roll\" "
pwsh.exe accepts \" or "".
"" is the robust choice when calling from cmd.exe ("^"" does not work robustly, because it normalizes whitespace; again, \" will typically, but not always work).
:: pwsh.exe: from cmd.exe, use "" for full robustness
pwsh.exe -nop -c " Write-Output ""Rock & Roll"" "
:: With double nesting (note the ` (backticks)).
pwsh.exe -nop -c " Write-Output ""The king of `""Rock & Roll`""."" "
:: \" is OK here, because there's no & or similar char. involved.
pwsh.exe -nop -c " Write-Output \"Rock and Roll\" "
In no-shell invocation scenarios, \" can safely be used in both editions; e.g., from the Windows Run dialog (WinKey-R); note that the first command would break from cmd.exe (& would be interpreted as cmd.exe's statement separator, and it would attempt to execute a program named Roll on exiting the PowerShell session; try without -noexit to see the problem instantly):
pwsh.exe -noexit -nop -c " Write-Output \"Rock & Roll\" "
pwsh.exe -noexit -nop -c " Write-Output \"The king of `\"Rock & Roll`\".\" "
See also:
Quoting headaches also apply in the inverse scenario: calling external programs from a PowerShell session: see this answer.
When calling from cmd.exe, %...%-enclosed tokens such as %USERNAME% are interpreted as (environment) variable references by cmd.exe itself, up front, both when used unquoted and inside "..." strings (and cmd.exe has no concept of '...' strings to begin with). While typically desired, sometimes this needs to be prevented, and, unfortunately, the solution depends on whether a command is being invoked interactively or from a batch file (.cmd, .bat): see this answer.
[1] This also applies to PowerShell's in-session communication with external programs.
[2] On Unix, where no process-level command lines exist, PowerShell only ever receives an array of verbatim arguments, which are the result of the calling shell's parsing of its command line.
[3] Use of "" is half broken; try powershell.exe -nop -c "Write-Output 'Nat ""King"" Cole'" from cmd.exe.
I'm trying to run a PowerShell script from Windows cmd.exe. The input to the PowerShell script is a string, which contains newline characters using PowerShell backtick escaping - i.e:
`r`n
For demonstration purposes, the input string is then written to the console, and also dumped to a file.
The issue I have is that when the script is run from cmd.exe using the syntax
powershell.exe script.ps1 "TEST`r`nTEST"
The newline characters in the string are not treated as newline, and are included literally in both the console output and the output text file.
TEST`r`nTEST
However, if I run this from a PowerShell environment, I get the expected result (i.e. the newline characters are parsed correctly, and a newline is inserted in the appropriate location).
TEST
TEST
Similarly, if I pass in \r\n instead of the escaped newline characters through Windows cmd.exe, and do a .replace in the PowerShell script
$date = $data.replace("\r\n","`r`n")
I get the expected output:
TEST
TEST
Is anyone able to shed some light on why this happens?
The test script is as follows:
param([string]$data) # data to send
Write-Host $data
[IO.File]::WriteAllText('d:\temp.txt', $data)
return 0
And the file is called from the command line as:
powershell.exe script.ps1 "TEST`r`nTEST"
The script is running on Windows Server 2012 R2, using PowerShell v4.0
tl;dr
Use -Command and pass the entire PowerShell command as a single string; e.g.:
C:\> powershell -NoProfile -Command "script.ps1 \"TEST`r`nTEST\""
TEST
TEST
Note how the internal " instances are escaped as \", which PowerShell requires when called from the outside (alternatively, for full robustness, use "^"" (sic) in Windows PowerShell and "" in PowerShell (Core) v6+).
In your specific case,
powershell -NoProfile -Command script.ps1 "TEST`r`nTEST" would have worked too, but generally that only works as intended if the string has no embedded spaces.
Given that -Command is the default up to PSv5.1, your command - as currently posted - should work as-is.
As of PowerShell v5.1, arguments passed to powershell.exe from the outside:
ARE subject to interpretation by PowerShell, including string interpolation, by default and when you use -Command (i.e., specifying neither -File nor -Command currently defaults to -Command).
Caveat: The default behavior will change in v6: -File will be the default then - see the relevant change on GitHub.
are NOT subject to interpretation if you use -File to invoke a script - (after potential interpretation by cmd.exe) PowerShell treats all arguments as literal strings.
Caveat: This behavior is currently being discussed with respect to v6, given that it is overtly problematic in at least one case: trying to pass Boolean values.
Optional reading: Why you should pass the entire PowerShell command as a single argument when using -Command:
When you use -Command with multiple arguments, PowerShell essentially assembles them into a single command line behind the scenes before executing it.
Any "..."-quoting around the individual arguments is lost in the process, which can have unexpected results; e.g.:
C:\> powershell -NoProfile -Command "& { $args.count }" "line 1`r`nline 2"
3 # !! "line 1`r`nline 2" was broken into 3 arguments
Given that the outer "..." quoting was removed in the process of parsing the command line, the actual command line that PowerShell ended up executing was:
C:\ PS> & { $args.Count } line 1`r`nline 2
3
To illustrate why, let's look at an equivalent command that uses explicit quoting:
C:\ PS> & { $args.Count } "line" "1`r`nline" "2"
In other words: After the enclosing " were removed, the resulting token was broken into multiple arguments by spaces, as usual.
The parameter will need to be reinterpreted as a PowerShell string. Will this get you down the road?
The reason your -replace did not work is that the original string actually contains a backtick. It needs to be escaped in the search string.
C:\src\t>type p1.ps1
Param([string]$s)
Write-Host $s
$p = Invoke-Expression `"$s`"
Write-Host $p
$p2 = $s -replace "``r``n","`r`n"
Write-Host $p2
C:\src\t>powershell -noprofile -file .\p1.ps1 "TEST`r`nTEST"
TEST`r`nTEST
TEST
TEST
TEST
TEST
Carriage return and Linefeed are bytes with values 13 and 10, you can't write them, you can't see them.
As a convenience, when writing PowerShell code, the language will let you write:
"`r`n"
in a double quoted string, and when processing PowerShell source code (and at no other time), it will read those and replace them with bytes value 13 and 10.
It is this line of code in the PowerShell tokenizer which does it.
There is nothing special about backtick-n to the cmd.exe interpreter, and nothing special about having it in a string - you can put it there in a single quoted string
'`n'
or replacing it in a string - except that you have to note when the replacement happens. e.g. in your comment:
For example, if you pass in 'r'n and then replace 'r'n with 'r'n, the 'r'n is still output literally
Because your code
-replace "`r`n"
becomes
-replace "[char]13[char]10"
and your string passed in from outside contains
`r`n
and they don't match. Backtick-n in a string isn't magic, strings are not all interpreted by the PowerShell engine as PowerShell code, nor are parameters, or anything. And it's only in that context - when you write your -replace code, that is when the swap for actual newline characters happens.
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:
I am trying to execute the following powershell commands from CMD, for example:
powershell Get-WmiObject Win32_PnPSignedDriver
powershell Get-WmiObject Win32_PnPSignedDriver > test.txt
which both work correctly.
But when I do a query, for example:
powershell (Get-WmiObject Win32_PnPSignedDriver | where {$_.location -like "*PCI bus 0, device 22, function 0*"}).DeviceName
I am getting this message the cause of which I can not pin down:
INFO: Could not find files for the given pattern(s)
This seems to work for me (I was seeing the same error as you originally):
powershell -command "(Get-WmiObject Win32_PnPSignedDriver | where {$_.location -like '*PCI bus 0, device 22, function 0*'}).DeviceName"
To complement the existing answers with general guidelines for passing commands to powershell.exe from cmd.exe (a Command Prompt):
Enclose the entire PowerShell command in "..." (double quotes).
This protects its contents from unwanted up-front interpretation by cmd.exe - as happened with | in this case, as explained in aschipfl's answer.
However, cmd.exe-style environment-variable references (e.g., %USERNAME%) are still expanded.
For quoting that is embedded in the PowerShell command:
Use '...' (single quotes) where feasible; this is what Mark Wragg's helpful answer does, but it is only an option if the quoted string is meant to be a literal (doesn't contain variable references or subexpressions).
If you do need embedded "...", escape it as \"...\"
Note that while inside PowerShell it is the ` (backtick) that serves as the escape character, when passing strings from the outside PowerShell requires \.
Simplified examples (run from cmd.exe):
Note that passing a command without a parameter name implies the -Command parameter; run powershell -? to see the command-line syntax.
You may also want to use -NoProfile so that the user profile isn't loaded every time.
Commands that don't need embedded quoting - simply double-quote:
powershell -noprofile "get-date"
powershell -noprofile "(get-date).Date"
powershell -noprofile "get-date | get-member"
Commands with embedded quoting - use '...' or \"...\":
powershell -noprofile "(get-date).Year -like '*17'"
powershell -noprofile "$v=17; (get-date).Year -like \"*$v\""
Commands that incorporate the value of a cmd.exe environment variable:
:: # Nothing special to do: cmd.exe expands the reference irrespective of quoting.
set "v=17"
powershell -noprofile "(get-date).Year -like '*%v%'"
:: # More robust variant that passes the value as an argument to a script block.
set "v=17"
powershell -noprofile ". { param($v) (get-date).Year -like \"*$v\" }" "%v%"
Optional reading: calling powershell from POSIX-like shells such as bash:
The above rules apply analogously, except that you'll typically use '...' (single quotes) to enclose the entire PowerShell command, which categorically prevents up-front interpretation by the shell.
Using "..." is an option if up-front expansions of shell-variable references and command substitutions are explicitly desired, but the potential for confusion is great, because both POSIX-like shells and PowerShell use sigil $ to refer to variables - it may not be obvious what is expanded when.
POSIX-like shells categorically do not support embedding ' instances in side '...' strings, which necessitates a somewhat awkward workaround (see below).
Simplified examples (run from a POSIX-like shell such as bash):
Commands that don't need embedded quoting - simply single-quote:
powershell -noprofile 'get-date'
powershell -noprofile '(get-date).Date'
powershell -noprofile 'get-date | get-member'
Commands with embedded quoting - replace embedded ' instances with '\'' (sic) and use embedded " as-is:
powershell -noprofile '(get-date).Year -like '\''*17'\'''
powershell -noprofile '$v=17; (get-date).Year -like "*$v"'
Commands that incorporate the value of a shell/environment variable:
# By using a double-quoted string, the shell expands $v up front.
v=17
powershell -noprofile "(get-date).Year -like '*$v'"
# It gets trickier if you want to reference PS variables too.
# Note how the PS variable's $ is \-escaped so that the shell doesn't interpret it.
v=$HOME
powershell -noprofile "\$HOME -eq '$v'"
# More robust variant that passes the value as an argument to a script block.
v=17
powershell -noprofile '. { param($v) (get-date).Year -like "*$v" }' "$v"
I am pretty sure that the pipe character | is the problem here, because cmd tries to process it.
Simply escape it by preceding with ^:
powershell (Get-WmiObject Win32_PnPSignedDriver ^| where {$_.location -like "*PCI bus 0, device 22, function 0*"}).DeviceName
If this code is used within a parenthesised block of code in cmd, you may need to escape the closing ) as well (the opening ( can be escaped too, but there is no need):
powershell ^(Get-WmiObject Win32_PnPSignedDriver ^| where {$_.location -like "*PCI bus 0, device 22, function 0*"}^).DeviceName
How can I get PowerShell to understand this type of thing:
Robocopy.exe | Find.exe "Started"
The old command processor gave a result, but I'm confused about how to do this in PowerShell:
&robocopy | find.exe "Started" #error
&robocopy | find.exe #("Started") #error
&robocopy #("|", "find.exe","Started") #error
&robocopy | &find #("Started") #error
&(robocopy | find "Started") #error
Essentially I want to pipe the output of one external command into another external command. In reality I'll be calling flac.exe and piping it into lame.exe to convert FLAC to MP3.
Cheers
tl;dr
# Note the nested quoting. CAVEAT: May break in the future.
robocopy.exe | find.exe '"Started"'
# Alternative. CAVEAT: doesn't support *variable references* after --%
robocopy.exe | find.exe --% "Started"
# *If available*, use PowerShell's equivalent of an external program.
# In lieu of `findstr.exe`, you can use Select-String (whose built-in alias is scs):
# Note: Outputs are *objects* describing the matching lines.
# To get just the lines, pipe to | % ToString
# or - in PowerShell 7+ _ use -Raw
robocopy.exe | sls Started
For an explanation, read on.
PowerShell does support piping to and from external programs.
The problem here is one of parameter parsing and passing: find.exe has the curious requirement that its search term must be enclosed in literal double quotes.
In cmd.exe, simple double-quoting is sufficient: find.exe "Started"
By contrast, PowerShell by default pre-parses parameters before passing them on and strips enclosing quotes if the verbatim argument value doesn't contain spaces, so that find.exe sees only Started, without the double quotes, resulting in an error.
There are three ways to solve this:
PS v3+ (only an option if your parameters are only literals and/or environment variables): --%, the stop-parsing symbol, tells PowerShell to pass the rest of the command line as-is to the target program (reference environment variables, if any, cmd-style (%<var>%)):
robocopy.exe | find.exe --% "Started"
This answer details the limitations of --%.
PS v2 too, or if you need to use PowerShell variables in the parameters: apply an outer layer of PowerShell quoting (PowerShell will strip the single quotes and pass the contents of the string as-is to find.exe, with enclosing double quotes intact):
robocopy.exe | find.exe '"Started"'
Caveat: It is only due to broken behavior that this technique works. If this behavior gets fixed (the fix may require opt-in), the above won't work anymore, because PowerShell would then pass ""Started"" behind the scenes, which breaks the call - see this answer for more information.
If an analogous PowerShell command is available, use it, which avoids all quoting problems. In this case, the Select-String cmdlet, PowerShell's more powershell analog to findstr.exe can be used, as shown above.
#Jobbo: cmd and PowerShell are two different shells. Mixing them is sometimes possible but as you realized from Shay's answer, it won't get you too far. However, you may be asking the wrong question here.
Most of the time, the problem you are trying to solve like piping to find.exe are not even necessary.
You do have equivalent of find.exe, in fact more powerful version, in Powershell: select-string
You can always run a command and assign results to a variable.
$results = Robocopy c:\temp\a1 c:\temp\a2 /MIR
Results are going to be STRING type, and you have many tools to slice and dice it.
PS > $results |select-string "started"
Started : Monday, October 07, 2013 8:15:50 PM
Invoke it via cmd:
PS> cmd /c 'Robocopy.exe | Find.exe "Started"'