How to enter a multi-line command - powershell

Is it possible to split a PowerShell command line over multiple lines?
In Visual Basic I can use the underscore (_) to continue the command in the next line.

You can use a space followed by the grave accent (backtick):
Get-ChildItem -Recurse `
-Filter *.jpg `
| Select LastWriteTime
However, this is only ever necessary in such cases as shown above. Usually you get automatic line continuation when a command cannot syntactically be complete at that point. This includes starting a new pipeline element:
Get-ChildItem |
Select Name,Length
will work without problems since after the | the command cannot be complete since it's missing another pipeline element. Also opening curly braces or any other kind of parentheses will allow line continuation directly:
$x=1..5
$x[
0,3
] | % {
"Number: $_"
}
Similar to the | a comma will also work in some contexts:
1,
2
Keep in mind, though, similar to JavaScript's Automatic Semicolon Insertion, there are some things that are similarly broken because the line break occurs at a point where it is preceded by a valid statement:
return
5
will not work.
Finally, strings (in all varieties) may also extend beyond a single line:
'Foo
bar'
They include the line breaks within the string, then.

I just found out that there must not be any character between the back tick and the line break. Even whitespace will cause the command to not work.

In most C-like languages I am deliberate about placing my braces where I think they make the code easiest to read.
PowerShell's parser recognizes when a statement clearly isn't complete, and looks to the next line. For example, imagine a cmdlet that takes an optional script block parameter:
Get-Foo { ............ }
if the script block is very long, you might want to write:
Get-Foo
{
...............
...............
...............
}
But this won't work: the parser will see two statements. The first is Get-Foo and the second is a script block. Instead, I write:
Get-Foo {
...............
...............
...............
}
I could use the line-continuation character (`) but that makes for hard-to-read code, and invites bugs.
Because this case requires the open brace to be on the previous line, I follow that pattern everywhere:
if (condition) {
.....
}
Note that if statements require a script block in the language grammar, so the parser will look on the next line for the script block, but for consistency, I keep the open brace on the same line.
Simlarly, in the case of long pipelines, I break after the pipe character (|):
$project.Items |
? { $_.Key -eq "ProjectFile" } |
% { $_.Value } |
% { $_.EvaluatedInclude } |
% {
.........
}

To expand on cristobalito's answer:
I assume you're talking about on the command-line - if it's in a script, then a new-line >acts as a command delimiter.
On the command line, use a semi-colon ';'
For example:
Sign a PowerShell script on the command-line. No line breaks.
powershell -Command "&{$cert=Get-ChildItem –Path cert:\CurrentUser\my -codeSigningCert ; Set-AuthenticodeSignature -filepath Z:\test.ps1 -Cert $cert}

In PowerShell and PowerShell ISE, it is also possible to use Shift + Enter at the end of each line for multiline editing (instead of standard backtick `).

Just add a corner case here. It might save you 5 minutes. If you use a chain of actions, you need to put "." at the end of line, leave a space followed by the "`" (backtick). I found this out the hard way.
$yourString = "HELLO world! POWERSHELL!". `
Replace("HELLO", "Hello"). `
Replace("POWERSHELL", "Powershell")

Just use ` character to separate command on multiline

If you are trying to separate strings into multiple lines, you can use the "+". For example:
$header = "Make," +
"ComputerName," +
"Model," +
"Windows Version"
Will look just like:
$header = "Make,ComputerName,Model,Windows Version"

I started by doing
if ($true) {
"you can write multiple lines here, and the command doesn't run untill you close the bracket"
"like this"
}
Recently found out I could just
&{
get-date
"more stuff"
}

I assume you're talking about on the command-line - if it's in a script, then a new-line acts as a command delimiter.
On the command line, use a semi-colon ';'

Use a semi-colon ; to separate command
Replace double backslash \\ on any backslashes \.
Use "' for passing safe address to switch command like "'PATH'".
This ps1 command install locale pfx certificate.
powershell -Command "$pfxPassword = ConvertTo-SecureString -String "12345678" -Force -AsPlainText ; Import-PfxCertificate -FilePath "'C:\\Program Files\\VpnManagement\\resources\\assets\\cert\\localhost.pfx'" Cert:\\LocalMachine\\My -Password $pfxPassword ; Import-PfxCertificate -FilePath "'C:\\Program Files\\VpnManagement\\resources\\assets\\cert\\localhost.pfx'" Cert:\\LocalMachine\\Root -Password $pfxPassword"

This is an old post, so here's the modern method.
If you're not using legacy powershell, the cleanest way to continue lines is the pipe at the start of the line.
Note: The command doesn't break with some lines commented out. This is great on the command line.
> Get-ChildItem -path 'c:\' -Depth 1
| Sort-Object LastWriteTime
# | Sort-Object Length -Descending
| Select-Object -First 3 -Skip 3
| Foreach-Object {
$_.Name, $_.Length | Join-String -Separator ' = '
}
output:
explorer.exe = 4826160
procexp.old.exe = 2925760
RtlExUpd.dll = 2839488
Windows Powershell ( Version < 6 )
Unfortunately windows powershell does not support it. A bunch of alternatives are linked above. You can remove the backtick completely: 2017/07/bye-bye-backtick-natural-line

There's sooo many ways to continue a line in powershell, with pipes, brackets, parentheses, operators, dots, even with a comma. Here's a blog about it: https://get-powershellblog.blogspot.com/2017/07/bye-bye-backtick-natural-line.html
You can continue right after statements like foreach and if as well.

$scriptBlock = [Scriptblock]::Create(#'
echo 'before'
ipconfig /all
echo 'after'
'#)
Invoke-Command -ComputerName AD01 -ScriptBlock $scriptBlock
source
don't use backquote

In windows terminal (powershell profile) I can simply click Shift-Enter works fine for me.
PS C:\xxx2021> Get-ChildItem -Include *remote* -Recurse |
>> Sort-Object -Property LastWriteTime -Descending |
>> Select-Object LastWriteTime, Name -First 25
LastWriteTime Name
------------- ----
12/5/2021 5:04:02 PM remote-control-car-BatteryPack-Number-2021-12-03.pdf
PS C:\xxx2021>enter code here

Related

An empty pipe element is not allowed, Get-WinEvent -FilterHashtable

I'm new to Powershell (v5) but found on the web a way to list all non empty logs e.g. in the last 30 minutes.
However, I cannot redirect the output to a file.
Any suggestions gratefully received.
$time1 = New-TimeSpan -Minutes 30
$logs = Get-WinEvent -ListLog * | Where-Object {$_.RecordCount} |
Select-Object -ExpandProperty LogName
Get-WinEvent -FilterHashtable #{LogName=$logs; Level=1,2,3; StartTime=(Get-Date)-$time1}
| out-file c:\temp\test1.txt
There are two ways to make PowerShell look for the continuation of a pipeline on the next line:
Place | at the end of a line.
More generally: any statement that invariably needs more tokens to be syntactically valid causes PowerShell to keep parsing on the next line.
Update: In PowerShell [Core] v7+ you may alternative place the | at the start of the (very) next line.
Use an explicit line continuation by placing ` (PowerShell's escape character) at the very end of a line (not even whitespace is allowed after).
What you tried:
Update: As implied above, your command now does work in PowerShell [Core] v7+.
The following explanation therefore applies only to PowerShell 6.x and below, including Windows PowerShell.
PowerShell reads the following as two statements:
Get-WinEvent -FilterHashtable #{LogName=$logs; Level=1,2,3; StartTime=(Get-Date)-$time1}
| out-file c:\temp\test1.txt
The Get-WinEvent line is syntactically complete by itself, so PowerShell considers it its own statement.
Thus, to PowerShell, the next statement starts with |, which prompts the error message you saw (there must be a command or expression before a statement's first |).

Find Carriage Returns in Files Using Powershell

I want to see all the instances of files containing the Windows-style crlf instead of Unix-style lf in a set of files. Here's what I have so far:
sls -Path src/*.cs -Pattern "`r`n" | group Path | select name
This works if I search for any normal text, but it's not finding the carriage returns, even though (according to everything I can find online) that's the proper Powershell escape sequence for carriage returns and newlines. For the record \r\n doesn't work either.
sls (an alias for Select-String) works line by line, so it's already processing (consuming) the line breaks during the file reading process before it gets to the regex matching.
Use something that reads the entire file, and then look for it:
Get-ChildItem -Path src/*.cs | ForEach-Object {
$contents = [System.IO.File]::ReadAllText($_.FullName)
if ($contents -cmatch '\r\n') {
$_
}
} | Group-Object Directory | Select-Object Name
\r\n is used here instead of the backticks because you're escaping them for the regex engine, not for powershell.

Replace `set` statements to `setx` in a batch script using powershell

Using powershell, I want to take a .bat file and replace all lines in which set is called to set an environment variable and change it to a corresponding setx call. Unfortunately it's not as simple as just doing a search and replace on the file, replacing set for setx, because the syntax is diffferent: set ENVNAME=abc vs setx ENVNAME abc.
Is there a simple way to do this in powershell? To just do the set for setx replacement, I have:
(Get-Content $orig_filename ) | ForEach-Object {$_ -replace "set", "setx"} | Set-Content $new_filename
Any pointers for a powershell novice would be appreciated.
Did some pretty limited testing and I'm not sure what your bat file looks like, but this may work for you.
(Get-Content $orig_filename ) | ForEach-Object {
$_ -replace 'set (.*)\=(.*)','setx $1 $2'
} | Set-Content $new_filename
You could also just chain another -replace to do the work for you. Also made the set > setx replacement a little more less error prone by ensuring you are replacing the word set at the beginning of the string. Since -replace functions as an array operator you do not need the foreach loop.
(Get-Content $orig_filename ) -replace "^set\b", "setx" -replace "="," " | Set-Content $new_filename

Script to replace line that contains text

I found another discussion on this, where there were numerous different options available but most of them didn't seem to work for myself or the original poster. I did however find an example that is confirmed working, however I am struggling with getting it to work and hoping for help.
I need to find a line that begins with "ServerName=" and replace this with my own line. I have used the example that was found and modified it, but I am getting errors when using it.
PowerShell Command:
powershell -Command "(Get-Content 'KFGame\Config\PCServer-KFGame.ini') | Foreach-Object {$_ -replace '^ServerName.$', ('ServerName=Network BUF 12345 Normal')} | Set-Content 'KFGame\Config\PCServer-KFGame.ini'"
Error Message:
) was unexpected at this time
If you want to run complex PowerShell statements from CMD you need to put them in quotes so that CMD just sees a string and doesn't try to handle special characters (like pipes):
powershell -Command "(Get-Content 'C:\Host400.txt') | Foreach-Object {$_ -replace '^workstationID.*$', (""WorkstationID=$computerName""} | Set-Content 'C:\Host400.txt'"
Note that you need to either escape or replace double quotes within the command string.
A better approach is to put the PowerShell statement(s) into a .ps1 script and run that via the -File parameter:
powershell -File "C:\path\to\your.ps1"

Run a program in a ForEach loop

I'm trying to get this simple PowerShell script working, but I think something is fundamentally wrong. ;-)
ls | ForEach { "C:\Working\tools\custom-tool.exe" $_ }
I basically want to get files in a directory, and pass them one by one as arguments to the custom tool.
If you still need quotes around the command path (say, if you've got spaces), just do it like this:
ls | % { &"C:\Working\tools\custom-tool.exe" $_.FullName }
Notice the use of & before the string to force PowerShell to interpret it as a command and not a string.
ls | %{C:\Working\tools\custom-tool.exe $_}
As each object comes down the pipeline the tool will be run against it. Putting quotes around the command string causes it to be... a string! The local variable "$_" it then likely doesn't know what to do with so pukes with an error.
I'm betting your tool needs the full path. The $_ is each file object that comes through the pipeline. You likely need to use an expression like this:
ls | %{C:\Working\tools\custom-tool.exe $_.fullname}
Both Jeffrery Hicks and slipsec are correct. Yank the double quotes off.
$_ or $_.fullname worked in my test script (below). YMMV with your custom-tool.
gci | % { c:\windows\notepad.exe $_.fullname }
or
gci | % { c:\windows\notepad.exe $_ }