Invoke-Expression cannot execute multi-line functions - powershell

I was trying to run a PowerShell script saved in a text document by using the following:
cat myScript.txt | Invoke-Expression
It works when each command is on its own line but throws an error saying I am missing syntax:
Invoke-Expression: At line:1 char:14
+ function foo {
+ ~
Missing closing '}' in statement block or type definition.
The code in myScript.txt is as follows:
function foo {
param([int]$a, [int]$b)
echo "$a + $b"
}
foo 1 2
Is there a way to run this script without changing the text file to merge all functions to one line?

You can also use out-string to convert output into string.
cat myScript.txt | out-string | Invoke-Expression

Related

Using subexpression with & in powershell

I am completely new to powershell.
I have a requirement to have a set of commands as a subexpression $() because I want the output of the command to be sent to Out-Host and without $() the if loops create an issue.
Now there might also be a possibility that the command has filenames with spaces and to handle that we append & before the file name in command.
Bacially, $(& file_name) | Out-Host fails saying & here is invalid.
How to go about using $() with &
Works ok for me. You'll need to supply a counterexample. In this case you couldn't pipe from "if" without $() or &{}.
$( If (1 -eq 1) { & echo hi } ) | out-host > file
hi
# no output until it's finished
$( If (1 -eq 1) { & 'c:\program files\internet explorer\iediagcmd' } ) |
out-host > file

How to add a text in a file with an argument in Powershell

I am trying to add the text passed as an argument in powershell to config file. Below is the sample of the code. What I am expecting that below command will read the config file & search for parameter1 & when it find the parameter it will add the value (passed as an argument) after "="
(gc config.params) -replace "Parameter1 =", "$&` $1" | sc inifile.params
So the output supposed to be like:
Parameter1 = hostname
when the following command will be executed:
powershell.exe Untitled1.ps1 hostname
Please suggest.
$1 is not how arguments are passed to PowerShell scripts; they get an array $args or you specify parameter names. And the array does not have the script path as the first element.
So, for your code:
(gc config.params) -replace "Parameter1 =", "$&` $($args[0])" | sc inifile.params
or
param($text)
(gc config.params) -replace "Parameter1 =", "$&` $text" | sc inifile.params

How to add a command line argument to powershell in profile.ps1 for Alias?

I am still quite new to Powershell, but I would like to add my favourite editor into an Alias in Powershell.
I edited the profile.ps1 in C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1 which will run automatically when PowerShells starts.
I tried enter New-Alias np notepad.exe which works perfectly everytime I launch PowerShell.
However, I would like to use Sublime Text 3 as my editor. I followed the instructions in this SO page: How can I write a PowerShell alias with arguments in the middle?
The command line I need for Sublime Text is "C:\Program Files\Sublime Text 3\sublime_text.exe" -n [FirstArg]
Which I come out something like this: function sublime { 'C:\Program Files\Sublime Text 3\sublime_text.exe' -n $args }
It does not work and I got the error like this:
At C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1:5 char:72
+ ... lime { 'C:\Program Files\Sublime Text 3\sublime_text.exe' -n $args }
+ ~~
Unexpected token '-n' in expression or statement.
At C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1:5 char:75
+ ... lime { 'C:\Program Files\Sublime Text 3\sublime_text.exe' -n $args }
+ ~~~~~
Unexpected token '$args' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : UnexpectedToken
Any helps would be appreciated. Thanks!
Without being a Sublime user, I suspect this should work:
function Start-Sublime {
param([string]$args)
$limeArgs = [string]::Empty
if ($args -ne $null) {
$limeArgs = $args
}
Start-Process "$env:ProgramFiles\Sublime Text 3\sublime_text.exe" -n $limeArgs
}
Set-Alias lime Start-Sublime
Not the prettiest PowerShell code, but I imagine it will do what you're after. It's a little easier to understand than the cryptic & operator is.

How to pass a script block as parameter and execute multiple times?

I wish to pass "Write-Host mm" as script block to function "f", and I hope "f" will execute it 10 times, so I tried:
function f([ScriptBlock]$s)
{
1..10|$s
}
f(Write-Host mm)
Unfortunately, powershell gives error:
At C:\Users\engineer\Documents\Untitled1.ps1:3 char:11
+ 1..10|$s
+ ~~
Expressions are only allowed as the first element of a pipeline.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ExpressionsMustBeFirstInPipeline
How to correct my script?
Thanks for Jason's first answer, but seems doesn't work:
Thanks, but seems this doesn't work, I've got PS 4.0 and
function f([ScriptBlock]$s)
{
1..10 | & $s
}
f { Write-Host mm }
Execute this script and it prints out:
Thanks, but seems this doesn't work, I've got PS 4.0 and
d:\ > function f([ScriptBlock]$s)
{
1..10 | & $s
}
f { Write-Host mm }
mm
This is strange! A script prints out itself! I've got PS4.0 and running ISE.
Why is that?
You are trying to execute your scriptblock 10 times, but instead you try to pipe in an array from 1 to 10. You should pipe that array to foreach-object instead.
function f([ScriptBlock]$s)
{
1..10 | % {& $s}
# % is an alias for Foreach-Object
}
f { Write-Host mm }
You need to use the invocation operator &. Also, script blocks are enclosed in curly braces, so your example would be:
function f([ScriptBlock]$s)
{
1..10 | & $s
}
f { Write-Host mm }
Note that you don't call PowerShell functions like you would a C# method - you don't use parentheses around the arguments, you call PowerShell functions like commands, with spaces between arguments.

Piping from a variable instead of file in Powershell

Is ther any way in Powershell to pipe in from an virable instead of a file?
There are commands that I need to pipe into another command, right now that is done by first creating a file with the additional commands, and then piping that file into the original command. Code looks somehting like this now:
$val = "*some command*" + "`r`n" + "*some command*" + "`r`n" + "*some command*"
New-Item -name Commands.txt -type "file" -value $val
$command = #'
db2cmd.exe /C '*custom db2 command* < \Commands.txt > \Output.xml'
'#
Invoke-Expression -Command:$command
So instead of creating that file, can I somehow just pipe in $val insatead of Commands.txt?
Try this
$val = #("*some command*1","*some command2*","*some command3*")
$val | % { db2cmd.exe /C $_ > \Output.xml }
You should be able to pipe in from $val provided you use Write-Output or its shorthand echo, but it may also be worth trying passing the commands directly on the command line. Try this (and if it doesn't work I can delete the answer):
PS C:\> filter db2cmd() { $_ | db2cmd.exe ($args -replace '(\\*)"','$1$1\"') }
PS C:\> $val = #"
>> *custom db2 command*
>> *some command*
>> *some command*
>> *some command*
>> "#
>>
PS C:\> db2cmd /C $val > \Output.xml
What happens here is that Windows executables receive their command line from a single string. If you run them from cmd.exe you cannot pass newlines in the argument string, but Powershell doesn't have that restriction so with many programs you can actually pass multiple lines as a single argument. I don't know db2cmd.exe so it might not work here.
The strange bit of string replacement is to handle any double quotes in the arguments: Powershell doesn't quote them and the quoting rules expected by most exe files are a bit bizarre.
The only limitation here would be that $val must not exceed about 32,600 characters and cannot contain nulls. Any other restrictions (such as whether non-ascii unicode characters work) would depend on the application.
Failing that:
echo $val | db2cmd.exe /C '*custom db2 command*' > \Output.xml
may work, or you can use it in combination with the filter I defined at the top:
echo $val | db2cmd /C '*custom db2 command*' > \Output.xml