I have the following code as the beginning of a longer script:
$ScriptPath = Split-Path $MyInvocation.MyCommand.Path
& $ScriptPath\build_functions.ps1
& $ScriptPath\build_builddefs.ps1
The idea is to get the path of the script being run and use that path to call some supporting scripts. However when I went to test this out in isolation to make sure it could work (by highlighting that block and running just that code), I got the following error:
Split-Path: Cannot bind argument to parameter 'Path' because it is null.
Interestingly enough, when I run the entire script it seems to run these files separately. Is there something I'm missing about how the ISE handles running a selection rather than the full script? Does it not establish a file system context when you run a selection?
$MyInvocation is an automatic variable populated at script run time, then if you execute $MyInvocation.MyCommand.Path in a powershell console or ISE isn't populated;
that's why in your test the $ScriptPath has no value ($null)
I don't if what happened to me was why some where seeking null in $MyInvocation.MyCommand.Path, but I will explain how I found the solution.
I had scripts that were working in production, yet when I loaded the .ps1 file up and tried to get $MyInvocation.MyCommand.Path it was null. My version of Powershell was 4.0, but 1.0 for the ISE (%windir%\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe).
But it didn't occur to me at first why they should work, yet when I manually inspected $MyInvocation.MyCommand.Path or $MyInvocation in PowerShell why it was null, and why I was getting a null error for Split-Path -parent $MyInvocation.MyCommand.Path.
So I thought I needed to upgrade the powershell to 5.1 on my Windows 2012 R2 Server like on my desktop NUC PC.
The real problem was that I found that if I set a break point in my .ps1 file and ran it to the spot where I was doing:
$ScriptDir = Split-Path -parent $MyInvocation.MyCommand.Path
That it worked. Of course it worked, I had been using it for a while. Why didn't I see it before?
What went wrong? I was trying to manually run the PowerShell in pieces using the run step command, when I had never run the script before since PowerShell was open!
I have to say this is probably a No Duh moment.
But we had recently had a server crash and had it restored (VSphere Clustered) and reseeded, so I thought maybe I have an older version of PowerShell.
PowerShell allows you to have multiple files/windows open inside it, yet the variables are shared between them. Apparently until you actually try to run the script (not step by step run), it has no script file executing and can't get you the path.
I hope this post saves someone from wasting a bunch of time like I did!
i think you need write just:
& .\MySuperDuperScript.ps
Related
I am trying to run a VBScript using some code I already have written for generically running executables. I have used it successfully with EXE & BAT files, but VBS files are giving me headaches.
I found this that does it with CMD and Invoke-Expression, and this that does it with &. But I have found no references to doing it with Start-Process, and when I try something simple like
Start-Process wscript.exe -ArgumentList:"UNC PATH TO HelperTest.vbs"
I get
Execution of the Windows Script Host failed. (Not enough storage is
available to complete ths operation. )
But I can run the super simple VBS file directly no problem. This only happens when the VBS is on a network share. Running it locally works fine. Also, not including a fully qualified path works fine, as in
Start-Process wscript.exe -ArgumentList:"HelperTest.vbs"
So it's not an actual permissions issue on the share. Am I just bumping in to some obscure bug and I am not going to get this to work, or am I doing something wrong and getting confused by a not very helpful error message?
introduction
I've written my first PowerShell script aimed for retrieving detailed information from a Windows Setup ISO file. Once the basic features achieved, I've started to convert the ps1 script into a psm1 module. I hoped the result would be the module just work like the script but I'm facing issue I'm not able to solve.
You can download my work here, script version and module (roughly translated to English from French).
I successfully installed the module in PSModulePath in:
[Environment]::GetFolderPath("mydocuments")\WindowsPowerShell\Modules
Command usage is very simple. You call it like that:
WinIsoInfo [[-Path] <String>] [<CommonParameters>]
Help is provided by module: man WinIsoInfo
Usage Example:
WinIsoInfo -Path "E:\Win 10\Installation\ISO\Windows 10 x64 fr.iso"
The ps1 script version is the exact same code as the psm1 module but there are commands examples at the end of the file that you can un-comment and edit before running the script.
Current Status
All the tests are and need to be run as admin, in console or PowerShell ISE.
The ps1 script works as expected but the psm1 module doesn't produce the same result.
At line 108 of the code, there is a Test-Path in a Switch statement:
{(Test-Path "$wimPath\sources\install.wim") -or (Test-Path "$wimPath\sources\install.esd")}
In the ps1 script, this Test-Path return True and user get the expected info.
But in the psm1, it seems to return False since Switch statement jump to the next test after this one. So at the end the user gets that the ISO doesn't contain windows setup. I can assure that the Test-Path should return True because I manually checked it while the function was paused by breakpoints.
Hint and lead
There are 2 cases where I manage to get the module work as expected. But only using in PowerShell ISE, NOT in console.
Using Automatic Variable $? in console pane while debugging module
Step to reproduce:
PowerShell ISE is not running.
Open PowerShell ISE as admin.
In console pane, run import-module Get-WinIsoInfo -Force -Global -Verbose or import-module -path X:\Path\To\Modules\Get-WinIsoInfo -Force -Global -Verbose
In console pane, run WinIsoInfo -Path "X:\path\to\AnyWindowsSetup.iso"
In my case, at this point, the command returns there is no Windows Setup in ISO file.
Now open the Get-WinIsoInfo.psm1 and put a breakpoints anywhere between line 90-108.
do step 4 again
While the script is paused at breakpoints, run $? in the console pane then press F10 then F5
And "voilĂ !" the module return the expected result and will keep working but only during PowerShell ISE session and inside PowerShell ISE. Command run in console still won't work. And the next time I run PowerShell ISE, the module won't find the setup image path again.
Previously run the ps1 script version in PowerShell ISE
Step to reproduce:
PowerShell ISE is not running.
Open PowerShell ISE as admin.
In console pane, run import-module Get-WinIsoInfo -Force -Global -Verbose or import-module -path X:\Path\To\Modules\Get-WinIsoInfo -Force -Global -Verbose
In console pane, run WinIsoInfo -Path "X:\path\to\AnyWindowsSetup.iso"
In my case, at this point, the command returns there is no Windows Setup in ISO file.
Now open the Get-WinIsoInfo.ps1 script, edit a valid command at the end of the code then press F5 to run it.
Note: Since the command in script has the same name as the module previously imported, at this point I don't know if the triggered function is the one from the ps1 script or the one from the module. Tell me if you know.
The script returns the expected result as Windows Setup info.
Close the ps1 file (it is no longer needed in PowerShell ISE for the next to work)
do step 4 again
And "voilĂ !" the module return the expected result and will keep working but only during PowerShell ISE session and inside PowerShell ISE. Command run in console still won't work. And the next time I run PowerShell ISE, the module won't find the setup image path again.
Conclusion
After the Hint and lead tests, I found out that they were some differences from modules imported in session before and after success. These key modules loaded by PowerShell ISE are Storage and Microsoft.WSMan.Management. I thought I found the solution and added this line to manifest:
RequiredModules = #("Storage";"Microsoft.PowerShell.Management";"Microsoft.PowerShell.Security";"Microsoft.PowerShell.Utility";"Microsoft.WSMan.Management")
I added all the modules that was present after the module works as expected, just to be sure.
I did the same for assemblies but 2 of them could not be imported: Microsoft.Management.Infrastructure.UserFilteredExceptionHandling and Microsoft.Management.Infrastructure.resources
Resulting in this new manifest line:
RequiredAssemblies = #("Microsoft.Management.Infrastructure.Native";"Microsoft.WSMan.Runtime";"System.Security")
Unfortunately, it seems it is not enough to solve the issue.
Maybe other things has to be imported or it's a wrong lead.
I really hope you could reproduce the bug or at least I hope the Hint and lead section will lead you to find the cause and a solution. I'm too novice to understand why this happens on my system.
My setup uses PowerShell v5.0 with Win 8.1 Pro.
I am trying to automate updating Powershell on Windows 7 using Windows6.1-KB2506143-x64.msu, and having a heck of a time. The following code works fine in a standalone ps1 file. And it works in my main ps1 file. But when run from a module it fails with exit code -2145124341. This is in PS v2, where negative exit codes are handled wrong, so that number is perhaps useless, and FWIW I have a good 40 other installers of various types that work from this module. However, this is my first attempt at automating msu files, so maybe there is a known interaction here that I haven't discovered yet? There's thousands of lines of code between the root ps1 file where this works and the module where it doesn't, so tracking down what is triggering the error is going to be a beast without some sort of trail to follow at the very least. So, anyone have an idea where I should start?
$filePath = 'wusa.exe'
$argumentList = '"\\PX_SERVER\Rollouts\Microsoft\Windows6.1-KB2506143-x64.msu" /quiet /norestart'
$exitCode = (Start-Process -filePath:$filePath -argumentList:$argumentList -wait -errorAction:stop -passThru).exitCode
Also, running wusa.exe leaves some detritus in the script folder, but only when it is run from the module. Is this an issue with the msu file, or just a bug in wusa? Or does it point at what is causing the issue perhaps?
I had hoped to get this update to work to enable some new features, but between not being able to automate and garbage being left behind, I am very close to abandoning that path and juts continuing to target v2. But hopefully someone can point me in the right direction as that is not my preferred solution at all.
a few toughts on first reading :
The ArgumentList parameter for Start-process needs an ARRAY to work well :
$argumentList = #( "\\PX_SERVER\Rollouts\Microsoft\Windows6.1-KB2506143-x64.msu", "/quiet", "/norestart" )
wusa.exe takes a log parameter : /log:c:\fso\install.log can you had it to your script for this particular package to check what happens ?
a powershell script trying to update powershell ... I'm not quite sure this is meant to work ... it's the only case in wich i'll backup on another scrpting language (people, please correct me if i'm wrong ... )
Please let me know the result of the wusa.exe /log command, thanks
I want to get the current directory where the script is executed as opposed to the current executing directory of the script.
The script lives in 'd:\projects\code\development.tools' and I want the directory of the project that lives in 'd:\projects\code\development\ace\ace.testing'
Now, here is the thing when I do 'Write-Host $Env:PWD' or just '$Env:PWD' outside to Psake Properties I actually get what I want but when I try to get it from within the Properties it returns what $PWD returns and that is the executing directory of the script.
Write-Host $Env:PWD # returns d:\projects\code\development\ace\ace.testing
Properties {
$baseDir = $Env:PWD # returns d:\projects\code\development\.tools
}
I'm not sure what's happening here but I can't figure it out.
I tried to store the correct path in a variable but it didn't really work.
UPDATE:
After spending few hours on that I realized it's actually working! I was working on that script on and off and put a throw statement somewhere and forgot that it's there! so I thought Psake is doing something weird and because of that it doesn't work but yeah Psake is pure! ;)
So if you want to get the current directory in which the script is being executed $Env:PWD seems to do it! :)
To get the location of the script being executed:
Split-Path $myInvocation.MyCommand.Path
To get the path of the present working directory:
(Get-Item -Path '.\' -Verbose).FullName
After spending few hours on that I realized it's actually working! I was working on that script on and off and put a throw statement somewhere and forgot that it's there!
So if you want to get the current directory in which the script is being executed $Env:PWD seems to do it! :)
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()]