I have an issue with Start-Job and [System.IO.Directory]::CreateDirectory. I'm using PowerShell version 5.1.22621.608, so the working directory for jobs is set to $HOME\Documents on Windows.
To see the issue, open PowerShell and run the following snippet. Notice that [System.IO.Directory]::CreateDirectory creates new-dir in the working directory of the PowerShell terminal. This is quite surprising, as it should have been created inside $HOME\Documents, as New-Item does. Now, change directory in the PowerShell terminal and run the snippet again. Notice that [System.IO.Directory]::CreateDirectory creates new-dir in the directory first opened in the PowerShell terminal, and not the current working directory!
Start-Job -ScriptBlock {
"Current working directory: $PWD"
# `new-dir` will be created inside the folder where PowerShell was first started,
# regardless of where the script is called from and the current working directory.
$CreatedDir = [System.IO.Directory]::CreateDirectory("new-dir")
"Created directory (CreateDirectory): $($CreatedDir.FullName)"
Remove-Item $CreatedDir.FullName
# New-Item works as expected
$CreatedDir = New-Item -Path "new-dir" -ItemType Directory
"Created directory (New-Item): $($CreatedDir.FullName)"
Remove-Item $CreatedDir.FullName
} | Wait-Job | Receive-Job
How can I set the root directory for [System.IO.Directory]::CreateDirectory?
Related
My Situation:
I have to run a PowerShell script as Administrator (because of accessing a privileged folder) and my script is also referencing files in the same directory as the script. I need to use a relative file path but I can't due to PowerShell switching the directory to C:\WINDOWS\system32 when ran as admin.
In PowerShell, is there a way to restore the directory to the current directory that the script is located?
My Script: (will be ran as admin)
Copy-Item -Path .\file1.txt -Destination 'C:\Users\privileged_folder'
Directory Structure:
MyDir\
file1.txt
myscript.ps1 <- ran as admin
By Changing Location
In your script, you can set the location to the script folder. From within your script, either of the following techniques will work:
# PowerShell 3+
cd $PSScriptRoot
# PowerShell 2
cd ( Split-Path -Parent $MyInvocation.MyCommand.Definition )
Changing to $PSScriptRoot works in all currently supported versions of PowerShell, but using Split-Path to get the script directory is useful if you for some reason still have nodes running PowerShell 2.
If you want to change back to the previous directory after your script is done executing (probably a good move) you can also make use of Push-Location and Pop-Location instead of cd or Set-Location:
# Also aliased to pushd
Push-Location $PSScriptRoot
# My script stuff
# Also aliased to popd
Pop-Location
These two cmdlets treat locations as a stack - Push-Location changes your location and adds the directory to the location stack while Pop-Location will remove the current directory from the stack and return you to the previous location. It works much like push and pop operations on arrays.
By Prefixing the Relative Paths
You could also prefix your relative paths in your script with either $PSScriptRoot or ( Split-Path -Parent $MyInvocation.MyCommand.Definition ) as shown in the previous section. If we attach the prefix to the otherwise relative path of file1.txt:
$filepath1 = "${PSScriptRoot}\file1.txt"
Now you have an absolute path to file1.txt. Note that this technique will work with any relative path to $PSScriptRoot, it does not have to be in the same folder as your ps1 script.
Why is this code not working as was planned?
What is powershell's auto variables for place where the target directory the script is running in?
Set-Location -Path $PSScriptRoot -PassThru
$file1.text="$PSScriptRoot\MIK_Autokontinent.xml
During startup, the program return error cannot find the path from c:\MIK_Autokontinent.xml
but the file is located in c:\program\MIK_Autokontinent.xml
This line is returning the error
$inputpecentw1.Text = [xml](Get-Content $file1.text) | ForEach-Object { $_.SelectNodes(' //FieldCostOptions/IncreaseCost') | ForEach-Object { $_.GetAttribute("Percent") } } | Out-String
You are trying to find the file in your root directory but the program is in the program folder. If your script is in the same folder as the file itself, just use the filename as reference like this $file1.text="MIK_Autokontinent.xml"
But in case you want to do this the way you are doing and from the root folder do it like this $file1.text="$PSScriptRoot\program\MIK_Autokontinent.xml".
i would like to copy the license folder and overwrite the existing folder, since it is program file (x86), i have to run the elevated powershell, i am able to copy it when i launch it manually, just wonder is it possible to get all run at one line (all at once) ? really appreicated
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive("R:", "\\roa\smdd\Software\Mest", $false)
Start-process Powershell.exe -ArgumentList " Copy-Item "R:\Licenses\" "C:\Program Files `(x86`)\Mest Research S.L\Mest\licenses"" -force -recurse -wait
You don't need to map a drive or invoke powershell.exe. The code is PowerShell, so you don't need to spin up a new copy of PowerShell to run the Copy-Item cmdlet to copy files. You only need one PowerShell command:
Copy-Item "\\roa\smdd\Software\Mest\Licenses\*" "${Env:ProgramFiles(x86)}\Mest Research S.L\Mest\licenses" -Force -Recurse
Note that you will likely need to open PowerShell as administrator (elevated) to be able to copy items into that directory.
When running a powershell script which changes a directory via cd (or set-location/push-location/etc.), the console that the script was run from also ends up in that directory.
Example:
script.ps1
cd c:\tmp
# do stuff
If I run this from c:\users\me, i end up in c:\tmp.
PS C:\users\me> .\script.ps1
PS C:\tmp> |
Now I know that I could use push-location and later do pop-location. However this wouldn't work if the script stopped somewhere in the middle (via Exit).
How can I solve this? Why doesn't the script have it's own location stack?
You could always use Try/Catch/Finally. Set the current directory path to a variable and then cd c:\tmp before the Try, and have the directory changed to variable in the Finally?
Example 1
$path = (Get-Item -Path ".\" -Verbose).FullName
cd c:\temp
Try
{
#Do stuff
#exit is fine to use
#this will output the directory it is in, just to show you it works
Write-Host (Get-Item -Path ".\" -Verbose).FullName
}
Catch [system.exception]
{
#error logging
}
Finally
{
cd $path
}
Example 2 using popd and pushd
pushd c:\temp
Try
{
#Do stuff
#exit is fine to use
#this will output the directory it is in, just to show you it works
Write-Host (Get-Item -Path ".\" -Verbose).FullName
}
Catch [system.exception]
{
#error logging
}
Finally
{
popd
}
I'd also recommend looking at what arco444 suggested, which is calling the powershell script via the -File parameter. Depending on the scenario that might work as an option.
I have created a PowerShell script for copying files to a directory, the script, first creates a folder , or forces a new folder event if it exists. Then copies a directory from another location. After copying, the files I then need to copy the correct web config depending on a value given by the user execturing the script. The issue I am having is I can copy the files, but all the files are set to read-only meaning when I try and copy the correct web.config, the script fails as access is denied.
This is a cut down version of script for simplicity.
$WebApp_Root = 'C:\Documents and Settings\user\Desktop\Dummy.Website'
$Preview_WebApp_Root = 'c:\applications\Preview\'
$Choice = read-host("enter 'preview' to deploy to preview, enter Dummy to deploy to Dummy, or enter test to deploy to the test environment")
if (($Choice -eq 'Preview') -or ($Choice -eq 'preview'))
{
$Choice = 'Preview'
$Final_WebApp_Root = $Preview_WebApp_Root
}
write-host("Releasing Build to " + $Choice +'...')
write-host("Emptying web folders or creating them if they don't exist... ")
New-Item $Final_WebApp_Root -type directory -force
write-host("Copying Files... ")
Copy-Item $WebApp_Root $Final_WebApp_Root -recurse
write-host("Copy the correct config file over the top of the dev web config...")
Copy-Item $Final_WebApp_Root\Config\$Choice\Web.configX $Final_WebApp_Root\web.config
write-host("Copying correct nhibernate config over")
Copy-Item $Final_WebApp_Root\Config\$Choice\NHibernate.config $Final_WebApp_Root\NHibernate.config
write-host("Deployed full application to environment")
Try to use -Force parameter to replace read-only files. From documentation:
PS> help Copy-Item -Par force
-Force [<SwitchParameter>]
Allows the cmdlet to copy items that cannot otherwise be changed,
such as copying over a read-only file or alias.