Why won't this powershell script accept parameters? - powershell

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.

Related

Alternatives to invoke-expression

I have this function:
function traced()
{
write-host "$args"
invoke-expression -Command "$args"
}
and I use it in several places like traced cp "$($_.FullName)" (join-path $directory $newfile) so that I have a log of all of the places that get copied (or removed, or whatever)
But when the directory contains spaces and dashes, it results in invoke-expression throwing.
I guess I could just define traced-cp and traced-rm, but if I have a lot of functions I want to trace, what's the generic answer? I just want a function that prints, then evaluates, the exact command its given. From what I understand, the & operator isn't what I want here-- It won't work for shell builtins.
[...] so that I have a log of all of the places that get copied (or removed, or whatever)
I'd strongly recommend you use transcript logging for this!
You can start a transcript interactively with the Start-Transcript cmdlet, but if you want to keep a transcript of every single instance of PowerShell you launch by default, I'd suggest turning it on by default!
Open the local policy editor (gpedit.msc) on your Windows box and navigate to:
Computer Configuration
> Administrative Templates
> Windows Components
> Windows PowerShell
Select the policy setting named "Turn on PowerShell Transcription", set it to Enabled, and optionally configure your preferred output directory (defaults to your home folder).
This way, you'll always have a full transcript of your interactions in PowerShell :)
Consider using argument splatting to build your command instead of building a string-based command with Invoke-Expression. I also don't know where you heard that & doesn't work with shell built-ins but it works with both commands and cmdlets.
Here is the official Microsoft documentation on splatting in Powershell.
This approach also eliminates the difficulty in crafting a command string correctly, having to escape characters, and dealing with path spaces - using splatting with named or positional arguments takes care of most of this for you.
I would suggest using -verbose with copy-item or remove-item, and also -passthru with copy-item.

Get-Process : A positional parameter cannot be found that accepts argument 'Get-ChildItem'

I am not a scripter at all. Someone else had created this script for me and it has previously worked. The only thing that has changed is the drive letter (which I did change in the script - it is currently drive E). But it is not working now. All it is supposed to do is pull back a list of files in a specified folder and save it as a text file in that directory; in this case, it's my karaoke song collection.
When I run the script now, I get:
Get-Process : A positional parameter cannot be found that accepts argument Get-ChildItem.
Here is the original script:
PS C:\Users\Tina> Get-ChildItem "F:\My Music\Karaoke\*.*" | Set-Content "F:\My Music\Karaoke\test.txt"
I'd like to make it so that it just pulls back all .mp3's, if that's possible, too. Thanks in advance for your help!
Since you appear to be copying and pasting this to the command line I will assume there was a typo that caused this issue. After a couple of quick tests to try and guess what the accident was I was unable to replicate exactly. Not being a scripter might make this harder but I recommend saving this code to a ps1 file so that you can just double click on it.
Get-ChildItem "F:\My Music\Karaoke\*.mp3" | Set-Content "F:\My Music\Karaoke\test.txt"
Warning
In order for the this file to work for you you have to allow PowerShell to execute it. If you run the shell as administrator once and run this code
Set-ExecutionPolicy remotesigned
It will allow your script to run. Keep in mind this is a site for scripters to get help. You should expect answers like this.

Is it possible to/how do you stop powershell using certain cmdlets?

Powershell is clearly a lot better than cmd but it hides basic functionality. I normally use it to figure out how to use commands that I want in scripts but it breaks a large number of basic things and I end up using both side by side if I hit a sticky spot.
Today this was removing a directory - rd or rmdir - both of which are broken in powershell in favour of one it's undocumented (from the commandline) cmdlets Remove-Item. I seem to run into it all the time though - sc (for mucking around with services); where for finding what program is being called when you type a command etc etc.
Hilariously I actually got the problem with sc and then googled to find out the command where only to discover that didnt work in powershell either! That was a confusing day
In some cases once you know this is what's going on you can type the full exe name (for instance 'where.exe' will work whereas 'where' on its own wont).
This isn't the case with rmdir however. Although interestingly typing 'where rmdir' in cmd doesnt work.
So... my question is this - Is there a way of turning off (preferably all) cmdlets in powershell and just getting the normal stuff instead?
There is no need to turn off cmdlets in powershell as that would destroy the very reason for having it.
Most of the "normal" stuff is there anyway, which you can find by using the get-alias command.
C:\> get-alias
CommandType Name
----------- ----
Alias % -> ForEach-Object
Alias ? -> Where-Object
Alias ?? -> Invoke-NullCoalescing
Alias ac -> Add-Content
Alias asnp -> Add-PSSnapin
Alias cat -> Get-Content
Alias cd -> Set-Location
Alias chdir -> Set-Location
.....
..... AND A WHOLE LOT MORE!
If you are missing a command that you really, really want to have, then you can easily add a new alias:
Set-Alias python2 "C:\Python27\python.exe"
In order to avoid having to do this every single time, you can simply add this into your startup profile. Just type in $PROFILE into the command prompt and it will show you the file path. If it doesn't exist, simply create it, and any powershell commands you add to the top will be automatically invoked when you start a new session.
And last thing. All of the commands are documented, and you can get to them easily using just two.
Just type this into your command prompt and you will be on your way to Powershell enlightenment!
get-help get-command
get-command -noun Item
get-command -verb get
I just realised the answer to my question was buried in the comments to the other answer:
To remove a cmdlet in powershell you run
Remove-Item alias:something.
I can confirm you can do this by using the profile mentioned in Josh's post, however there are a couple more steps:
By default you cant run scripts in powershell. You have to change this using set-ExecutionPolicy.
I changed this by using an admin powershell and typing
set-executionpolicy bypass
This will let you run any script you like
Then in my profile script I have lines like:
Remove-Item -force alias:sc
You wont see errors from this script when it runs and it wont do anything unless you have force.

My PowerShell script only works when running from ISE

I can't post all of the script contenet, but the basic idea is that it downloads JSON and converts it to objects using the ConvertFrom-Json cmdlet. Some objects are filtered out, and the rest are written to an XML/XLS document (in the Excel 2003 format). This file is then attached to an email and sent to various people.
The problem I'm having is that it only works when run from the Powershell ISE. Once I try setting up a scheduled task, calling it from cmd, or even calling it from powershell, the attached file is completely empty. It is as if some functions do not run (the one that loops through and creates all rows).
I can continue to run from ISE for the time being, but the idea of this script is to send out an automatic email that will require no intervention. Any ideas as to what could be causing this?
You need to run the script "dot sourced"
which can be done like this
powershell.exe -noexit -file c:\test.ps1
or
pwershell.exe -noexit ". c:\test.ps1"
See this link under the -File section
http://technet.microsoft.com/en-us/library/hh847736.aspx
Based on the answer from the comments to the original post, it seems that the script was (unexpectedly?) working in the ISE because of the bug/quirk/feature that allows scripts run in the ISE to be aware of variables used in the console window and vice-versa.
This can often cause logic problems or unexpected results when a script runs with a variable already defined (and not initialized carefully in the script).
Ways to avoid this problem:
Try to test your code in as clean an environment as possible.
http://powershell.com/cs/blogs/tips/archive/2015/02/12/getting-a-clean-powershell-environment.aspx
To make sure a script runs in a completely clean test environment, you
could of course restart the PowerShell ISE. A more convenient way is
to open another PowerShell tab: in the PowerShell ISE, choose File/New
PowerShell Tab.
Use Set-StrictMode 2 in your script to catch undefined variables, etc.
https://technet.microsoft.com/en-us/library/hh849692.aspx
Set-StrictMode -Version 2.0
Prohibits references to uninitialized variables (including uninitialized variables in strings).
Prohibits references to non-existent properties of an object.
Prohibits function calls that use the syntax for calling methods.
Prohibits a variable without a name (${}).
I have had this problem be for and for me executing the scrip using single-threaded function from powershell worked.
You could also try some other options, go to this link to find more info.
Example
powershell.exe -noexit c:\test.ps1 -sta
Change the missing variable or function to global.
function global:mycall{}
Start your Script with:
[cmdletbinding()]

Powershell.exe running the script in cli, or a wrapper?

I have a third-party application that's extensible by adding exe-files that perform dataconversion etc. I've written an extension in Powershell that does the conversion I want, but I'm unable to get the third-party app to run my ps1, as it will only accept an .exe file as an extension.
The app adds a filename as the first (and only) commandline argument to the extension, so the command it runs looks like:
someprogram.exe somefile.xml
I tried to get it to run Powershell.exe with my script as an argument, but I haven't been able to figure out how and if that's possible. Some stuff I tried like
powershell.exe myscript.ps1
won't work. I tried getting the script to find the correct XML file itself, but still somehow I couldn't get Powershell to run off the commandline and take a script as an argument and run it.
Next I thought about writing a small .exe file that only runs the Powershell script, but is that even possible? If it is, could someone nudge me in the right direction?
Powershell wants to have a qualified path to script files before it will run them. So you need to either use
powershell.exe .\myscript.ps1
if it lies in the current working directory (unlikely and prone to break for this use case) or use the full path to the script:
powershell.exe C:\Users\Foo\Scripts\myscript.ps1
or similar.
You can also try Powershell.exe -Command "& your-app.exe ${your-arguments}