I have a Powershell script that takes two arguments.
param([string]$folder, [string]$filename)
When I open a Powershell shell, navigate to the folder that contains the script, and run it from there, the script is executed correctly:
C:\scriptfolder> ".\script.ps1" "C:\folder for argument" "filename.ext"
When I try to run the script from a different folder, I get an error message:
C:\otherfolder> "C:\scriptfolder\script.ps1" "C:\folder for argument" "filename.ext"
At line:1 char:nn
Unexpected token '"C:\folder for argument"' in expression or statement.
How can I run this script from another folder?
In order not to leave this question as 'Unanswered', here my comment as answer
There are two ways to do what you want. The first is by using dot-sourcing:
. "C:\scriptfolder\script.ps1" "C:\folder for argument" "filename.ext"
The second method would be by using the `& call operator
& "C:\scriptfolder\script.ps1" "C:\folder for argument" "filename.ext"
The difference between the two methods is that a script that is invoked using dot-sourcing runs in the current scope, while with the second method, using the call operator, scripts and functions run in a child scope.
You can find an excellent explanation in the answer here
Related
This is the command that I provided to the customer before:
iex ((New-Object System.Net.WebClient).DownloadString('URL'))
It downloads PS script as a string from URL location and executes the script.
Now I need to pass parameter to this script
iex (((New-Object System.Net.WebClient).DownloadString('URL')) -Parameter 'parameter')
And it is not working because string not accepts parameters
I need to create the simple command (it is important) that will download the script from URL and accept parameters. Otherwise I would save this string to ps1 file and execute it passing the parameter
Could you please help me with that? I am new to PS
Try the following approach:
& ([scriptblock]::Create(
(New-Object System.Net.WebClient).DownloadString('URL')
)) 'Parameter'
Note:
The above runs the script text in a child scope, due to use of &, the call operator, as would happen with local invocation of a script file, whereas Invoke-Expression (iex) runs it in the current scope, i.e. as if it were called with . , the dot-sourcing operator. Change & to . if you want this behavior.
To download the script, you could alternatively use PowerShell's Invoke-RestMethod (irm) cmdlet: Invoke-RestMethod 'URL' or irm 'URL'
[scriptblock]::Create() creates a PowerShell script block, which can then be invoked with & (or .), while also accepting arguments.
As for what you tried:
Invoke-Expression (iex) - which should generally be avoided - has several drawbacks when it comes to executing a script file's content downloaded from the web:
It doesn't support passing arguments, as you've discovered.
An exit statement in the script text would cause the current PowerShell session to exit as a whole.
As noted, the code executes directly in the caller's scope, so that its variables, functions, ... linger in the session after execution (however, it's easy to avoid that with & { iex '...' }).
GitHub issue #5909 discusses enhancing the Invoke-Command cmdlet to robustly support download and execution of scripts directly from the web.
I'm wondering what I'm missing here. I have a powershell script that calls another script with some parameters to execute as a way to keep things tidy. Here is what works:
C:\Scripts\Project\coolscript.ps1 -projname 'my.project' -domain 'work'
I want others to be able to use this script without having to change anything, so I thought I could make the path relative instead of the full one starting from C: so I thought I could execute the script like this:
$pathname = $PSScriptRoot + '\coolscript.ps1'
$pathname -projname 'my.project' -domain 'work'
however I get an error that says 'unexpected token in expression or statement for everything after $pathname
ANy ideas what I'm missing? Thank you
Use the Call operator (&) as follows:
& $pathname -projname 'my.project' -domain 'work'
Call operator &
Runs a command, script, or script block. The call operator, also known as the "invocation operator," lets you run commands that are
stored in variables and represented by strings or script blocks. The
call operator executes in a child scope. For more about scopes, see
about_scopes.
I wrote a script to build all .net projects in a folder.
Issue
The issue is I am getting a missing function error when I call Build-Sollution.
What I tried
I made sure that function was declared before I used it so I am not really sure why it saids that it is not defined.
I am new to powershell but I would think a function calling another functions should work like this?
Thanks in advance!
Please see below for the error message and code.
Error Message
Line |
3 | Build-Sollution $_
| ~~~~~~~~~~~~~~~
The term 'Build-Sollution' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Build-Sollution:
Code
param (
#[Parameter(Mandatory=$true)][string]$plugin_path,
[string]$depth = 5
)
$plugin_path = 'path/to/sollutions/'
function Get-Sollutions {
Get-ChildItem -File -Path $plugin_path -Include *.sln -Recurse
}
function Build-Sollution($solution) {
dotnet build $solution.fullname
}
function Build-Sollutions($solutions) {
$solutions | ForEach-Object -Parallel {
Build-Sollution $_
}
}
$solutions_temp = Get-Sollutions
Build-Sollutions $solutions_temp
From PowerShell ForEach-Object Parallel Feature | PowerShell
Script blocks run in a context called a PowerShell runspace. The runspace context contains all of the defined variables, functions and loaded modules.
...
And each runspace must load whatever module is needed and have any variable be explicitly passed in from the calling script.
So in this case, the easiest solution is to define Build-Sollution inside Build-Sollutions
As for this...
I am new to powershell but I would think a function calling another
functions should work like this?
... you cannot use the functions until you load your code into memory. You need to run the code before the functions are available.
If you are in the ISE or VSCode, if the script is not saved, Select All and hit use the key to run. In the ISE use F8 Selected, F5 run all. In VSCode, F8 run selected, crtl+F5 run all. YOu can just click the menu options as well.
If you are doing this from the consolehost, the run the script using dot sourcing.
. .\UncToYourScript.ps1
It's ok to be new, we all started somewhere, but it's vital that you get ramped up first. so, beyond what I address here, be sure to spend time on Youtube and search for Beginning, Intermediate, Advanced PowerShell for videos to consume. There are tons of free training resources all over the web and using the built-in help files would have given you the answer as well.
about_Scripts
SCRIPT SCOPE AND DOT SOURCING Each script runs in its own scope. The
functions, variables, aliases, and drives that are created in the
script exist only in the script scope. You cannot access these items
or their values in the scope in which the script runs.
To run a script in a different scope, you can specify a scope, such as
Global or Local, or you can dot source the script.
The dot sourcing feature lets you run a script in the current scope
instead of in the script scope. When you run a script that is dot
sourced, the commands in the script run as though you had typed them
at the command prompt. The functions, variables, aliases, and drives
that the script creates are created in the scope in which you are
working. After the script runs, you can use the created items and
access their values in your session.
To dot source a script, type a dot (.) and a space before the script
path.
See also:
'powershell .net projects build run scripts'
'powershell build all .net projects in a folder'
Simple build script using Power Shell
Update
As per your comments below:
Sure the script should be saved, using whatever editor you choose.
The ISE does not use PSv7 by design, it uses WPSv5x and earlier.
The editor for PSv7 is VSCode. If you run a function that contains another function, you have explicitly loaded everything in that call, and as such it's available.
However, you are saying, you are using PSv7, so, you need to run your code in the PSv7 consolehost or VSCode, not the ISE.
Windows PowerShell (powershell.exe and powershell_ise.exe) and PowerShell Core (pwsh.exe) are two different environments, with two different executables, designed to run side-by-side on Windows, but you do have to explicitly choose which to use or write your code to branch to a code segment to execute relative to the host you started.
For example, let's say I wanted to run a console command and I am in the ISE, but I need to run that in Pwsh. I use a function like this that I have in a custom module autoloaded via my PowerShell profiles:
# Call code by console executable
Function Start-ConsoleCommand
{
[CmdletBinding(SupportsShouldProcess)]
[Alias('scc')]
Param
(
[string]$ConsoleCommand,
[switch]$PoSHCore
)
If ($PoSHCore)
{Start-Process pwsh -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -PassThru -Wait}
Else {Start-Process powershell -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -PassThru -Wait}
}
All this code is doing is taking whatever command I send it and if I use the PoSHCore switch...
scc -ConsoleCommand 'SomeCommand' -PoSHCore
... it will shell out to PSCore, run the code, otherwise, it just runs from the ISE>
If you want to use the ISE with PSv7 adn not do the shell out thing, you need to force the ISE to use PSv7 to run code. See:
Using PowerShell Core 6 and 7 in the Windows PowerShell ISE
I have written a basic script to execute and send an email with an attachment, I have tested this in PowerShell. However when I try to create this as a task scheduler I get the following error:
However my exact same code executes without issue in PowerShell.
This is my view in task scheduler:
What am I missing? I have set the execution policy so that is not an issue and I've tested my code and there is no issue there.
Use the -File parameter instead:
-File "C:\path\to\your.ps1"
PowerShell's call operator (&) works only inside PowerShell, so you would need to put it inside the argument string:
"& 'C:\path\to\your.ps1'"
While that would work too, it has certain disadvantages, most importantly that it will return only 0 (success) or 1 (failure) as the exit code, but not the actual exit code of the script.
Your argument needs to include the -Command parameter, not just the code you want to run. Also, your code to run needs to be a string, not raw Powershell code, or the command line will try to interpret it. Set the argument to:
-Command '&"C:\Users\Martyn\Documents\Powershell Scripts\Working.ps1"'
Or, you can try the -File parameter directly, although there are known issues with it:
-File "C:\Users\Martyn\Documents\Powershell Scripts\Working.ps1"
If anyone else is getting this error, you need to install ProcessMitigation into PowerShell before you can use it. By default, it's not there.
Steps to install:
Run Powershell with administrative rights.
Run Save-Module -Name ProcessMitigations -Path <path> command in PS to download the component. Provide actual path instead of to download.
Run Install-Module -Name ProcessMitigations in PS to install it.
More here.
myscript.ps1
Param(
[Parameter(Mandatory=$true,Position=1)]
[string]$computerName
)
echo "arg0: " $computerName
CMD.exe
C:\> myscript.ps1 -computerName hey
Output:
cmdlet myscript.ps1 at command pipeline position 1
Supply values for the following parameters:
computerName: ddd
arg0:
ddd
I'm simply trying to work with Powershell parameters in CMD, and I can't seem to get a script to take one. I see sites saying to precede the script with .\ but that doesn't help. I added the mandatory line to see if Powershell was reading a parameter or not, and it's clearly not. The parameter computerName is obviously the word "hey". The Param block is the very first thing in the script. Powershell appears to recognize a parameter computerName, but no matter how I enter the command, it never thinks I'm actually entering parameter.
What the heck's wrong with my syntax?
By default, Powershell will not run scripts that it just happens to find in your current directory. This is intended by Microsoft as a security feature, and I believe that it mimics behavior found in unix shells.
Powershell will run scripts that it finds in your search path. Your search path is stored in $env:path.
I suspect that you have a script named "myscript.ps1" in some other directory that is on your search path.
I have had this happen to me before. The symptom I saw was that the parameter list seemed different than what I had defined. Each script had a different parameter list, so the script bombed when I fed it a parameter list intended for the other script. My habit is to not rely on parameter position, so this problem was easy to find.
The addition of ".\" to the script ".\myscript.ps1" should force the shell to use the .ps1 file in your current directory. As a test, I would specify the full path to the file you are trying to execute (If there are spaces in the path, be sure to wrap the path in "quotes") or change it to some totally crazy name that won't be duplicated by some other file (like "crazyfishpants.ps1") and see if the shell still finds the file.
You can get into similar problems if you have a function ("Get-Foo") that is loaded out of a module or profile with the same name as a script file ("Get-Foo.ps1"). You may wind up running something other than what you intend.
Position values should be 0-based (0 for the first parameter). That said, I can't duplicate what you're seeing on either PowerShell 2.0 or 3.0.
Thank you all for your very informative responses. It looks like my question was slightly edited before I submitted it, in that the text leads you to believe that I was entering this command directly in Powershell.
I was actually running the command for the script in CMD, which totally explains why it was not passing parameters to the Powershell script. Whoever green-lighted my question probably changed C:\> to PS> thinking that I made a typo.
I assumed that if I could run the script straight from CMD, I could send parameters to it on CMD's command line, but apparently that's not the case. If I run the script in Powershell, it indeed works just fine, I'm now seeing.
My ultimate goal was to allow users to run the Powershell script from CMD. It's looking like I can make a batch file that accepts parameters, and then start powershell and send those parameters to the PS script. And so, in the batch file, I should do something like:
powershell -File C:\myscript.ps -computerName %1
This enigma was probably solved 100 times over on this site, and I apologize for the confusion. Thank you again, for your responses.