Invoke Windows copy from PowerShell - windows

I am busy with creating a PowerShell script where a folder needs to be copied to another folder as a part of the script.
For this I would like to use the standard Windows copy interface and leverage from the prompts and selections like “There is already a file with the same name in this location.”, “Do you want to merge this folder”, “Do this for all”, etc. instead of programming this all myself.
I investigated a view directions:
Using the IFileOperation::CopyItem method as e.g. HowTo: Display progress dialog using IFileOperation but I could find any hint of how to embed this in PowerShell
Using Verbs() Copy/Paste but although the example Invoke-FileSystemVerb -Path "C:\TestDir -Verb Cut; Invoke-FileSystemVerb -Path "\\server\share" -Verb Paste” suggests otherwise, I can paste files that are manually copied/cut but could not copy/cut files with using the CmdLet or simply using the Verb.DoIt() method (I suspect this for a security reason).
Simulate a drag-drop?
But could not find any working solution.
Any suggestion how to do this from PowerShell?

I use this to extract from ZIP files which seems to come up with all the prompts and progress bar:
$shell = New-Object -Com Shell.Application
$folder = $shell.NameSpace(“$path\Folder”)
$shell.NameSpace($otherpath).CopyHere($folder)

Related

Windows PowerShell default open location

I have been googling and trying different options but I didn't really find anything useful or that worked.
My question is how to set default open location for windows PowerShell, I want it to always open c:/programming/ folder when I start it from like start menu, instead what it opens now.
I'm using windows 10.
Thnx for help
the usual way to tell PoSh where to start up at is to add a Set-Location line to one of your powershell profile files. i added ...
Set-Location D:\Data\Scripts
... to my CurrentUserCurrentHost file. you can learn more about profiles with ...
Get-Help about_Profiles
you can find your version of the profile i used thus ...
$profile |
Select-Object -Property *
please note that none of these files exist by default. you will likely need to make one. if you do be sure it is a plain text file, not a .doc file! [grin]

Powershell Core to read file metadata

I need a way to read file metadata using Powershell Core 7.x on macOS.
In a MS Windows environment, I was able to use Shell.Application COM object and getDetailsOf() method to retrieve the information. However, I can't find this option in PowerShell Core on macOS.
I found someone who used external commands (I guess some are written in python) in PowerShell core to retrieve the information, but I would like to do that using PowerShell only.
Does anyone know if this is possible with Powershell core?
Thanks
Instead of using a "Shell.Application COM object" to execute an external tool, you can just use the Start-Process cmdlet and capture the output of this process:
$f = New-TemporaryFile
Start-Process exiftool -ArgumentList "myImage.jpg" -RedirectStandardOutput $f.FullName -Wait
$result = Get-Content -Path $f.FullName
Remove-Item -Path $f.FullName
# Now, the result of the exiftool is available in $result for further processing
Shell.Application is part of Win32, it's literally the Windows shell!
So when you load it via COM, you're basically telling Powershell do the same function the shell uses for the UI details tab, GetDetailsof. COM is just how you're communicating to the process, to tell it to run a particular method.
So could this work on Powershell Core? Only if you're running Powershell Core on a Windows system. That is because the dependency is a method provided by the platform. You have no other choice than to read the metadata yourself, via another library.
You might want to check out, metadata-extractor-dotnet
Something like that is what you'd need to implement the functionality. I don't have experiance using that lib myself, but at least it might provide a good jump off point for further research.

How can I get PowerShell current location every time I open terminal from file explorer

I can open PowerShell window in any directory using Windows File Explorer.
I want to run a script every time a new PowerShell window is open and use current directory where it was open in the script.
Using $profile let me for automatic script execution but $pwd variable does not have directory used to open PowerShell window but has C:\WINDOWS\system32. I understand PowerShell starts in C:\WINDOWS\system32, run $profile and next change location used with File Explorer. How can I get file explorer current directory it when my script is executes from $profile or maybe there is another way to automatic execute my script after PowerShell window is open?
Note: The answer below provides a solution based on the preinstalled File Explorer shortcut-menu commands for Window PowerShell.
If modifying these commands - which requires taking ownership of the registry keys with administrative privileges - or creating custom commands is an option, you can remove the NoWorkingDirectory value from the following registry keys (or custom copies thereof):
HKEY_CLASSES_ROOT\Directory\shell\Powershell
HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
Doing so will make the originating folder the working directory before PowerShell is invoked, so that $PROFILE already sees that working directory, as also happens when you submit powershell.exe via File Explorer's address bar.[1]
Shadowfax provides an important pointer in a comment on the question:
When you hold down Shift and then invoke the Open PowerShell window here shortcut-menu command on a folder or in the window background in File Explorer, powershell.exe is initially started with C:\Windows\System32 as the working directory[1], but is then instructed to change to the originating folder with a Set-Location command passed as a parameter; e.g., a specific command may look like this:
"PowerShell.exe" -noexit -command Set-Location -literalPath 'C:\Users\jdoe'
As an aside: The way this shortcut-menu command is defined is flawed, because it won't work with folder paths that happen to contain ' chars.
At the time of loading $PROFILE, C:\Windows\System32 is still effect, because any command passed to -command isn't processed until after the profiles have been loaded.
If you do need to know in $PROFILE what the working directory will be once the session is open, use the following workaround:
$workingDir = [Environment]::GetCommandLineArgs()[-1] -replace "'"
[Environment]::GetCommandLineArgs() returns the invoking command line as an array of arguments (tokens), so [-1] returns the last argument, assumed to be the working-directory path; -replace "'" removes the enclosing '...' from the result.
However, so as to make your $PROFILE file detect the (ultimately) effective working directory (location) irrespective of how PowerShell was invoked, more work is needed.
The following is a reasonably robust approach, but note that a fully robust solution would be much more complex:
# See if Set-Location was passed and extract the
# -LiteralPath or (possibly implied) -Path argument.
$workingDir = if ([Environment]::CommandLine -match '\b(set-location|cd|chdir\sl)\s+(-(literalpath|lp|path|PSPath)\s+)?(?<path>(?:\\").+?(?:\\")|"""[^"]+|''[^'']+|[^ ]+)') {
$Matches.path -replace '^(\\"|"""|'')' -replace '\\"$'
} else { # No Set-Location command passed, use the current dir.
$PWD.ProviderPath
}
The complexity of the solution comes from a number of factors:
Set-Location has multiple aliases.
The path may be passed positionally, with -Path or with -LiteralPath or its alias -PSPath.
Different quoting styles may be used (\"...\", """...""", '...'), or the path may be unquoted.
The command may still fail:
If the startup command uses prefix abbreviations of parameter names, such as -lit for -LiteralPath.
If a named parameter other than the path follows set-location (e.g., -PassThru).
If the string set-location is embedded in what PowerShell ultimately parses as a string literal rather than a command.
If the startup command is passed as a Base64-encoded string via -EncodedCommand.
[1] When you type powershell.exe into File Explorer's address bar instead, the currently open folder is made the working directory before PowerShell is started, and no startup command to change the working directory is passed; in that case, $PROFILE already sees the (ultimately) effective working directory.
1.open the registry (command:regedit)
2.find out the path \HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell\command (not \HKEY_CLASSES_ROOT\Directory\Background\shell\cmd\command)
3.the default value should be powershell.exe -noexit -command Set-Location -literalPath "%V"
4.you can change some param,
ps: you change the command to cmd.exe /s /k pushd "%V". if you do so, shift & right button in the explorer will open the cmd, not powershell

Batch file - pull commands from web resource

I've tried several solutions to no avail, but am looking for a solution as to create a batch file that will run any commands located in a remote .txt file located on a website.
For example, somebody clicks on a .bat and the .bat retrieves a list of commands from www.example.com/command.txt, and then runs the contents of the .txt as if the commands were specified explicitly in the original .bat file. I am not opposed to using PowerShell or VBScript.
In PowerShell you can do this: iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
i.e. this downloads and runs the PowerShell script: https://chocolatey.org/install.ps1
Explanation
New-Object System.Net.WebClient creates a new instance of the .Net web client.
DownloadString('https://chocolatey.org/install.ps1') invokes the web client's Download method to pull back the text from this script (see https://msdn.microsoft.com/en-us/library/fhd1f0sw(v=vs.110).aspx).
iex is an alias for invoke-expression. This runs the downloaded string as if it were a command; i.e. causes PowerShell to execute the remote script.
Simpler Version
While the Chocolatey install script's an impressive 1 liner, the expanded version below may be simpler to understand (though ultimately does exactly the same as the above; just split over several lines).
$scriptUri = 'https://chocolatey.org/install.ps1'
$webClient = New-Object -TypeName 'System.Net.WebClient'
$scriptAsString = $webClient.DownloadString($scriptUri)
Invoke-Expression -Command $scriptAsString
The above example's taken from https://chocolatey.org/install

%~dp0 equivalent in powershell (using Expand-Archive cmdlet)

I'm pretty new to scripting (especially powershell) and new to Stack Overflow, so please, excuse my ignorance and please bear with me! I will do my best to specifically explain what I'm looking to do and hopefully someone can give a detailed response of what I could do to make it work..
Intended Process/Work Flow: A co-worker downloads "Install.zip" file that has all the necessary files. This "Install.zip" file contains "Setup.bat" file (for computer config), "Fubar.zip" file, 2 powershell scripts, and a custom powerplan (.pow) file. Once downloaded they will run the "Setup.bat" file and it will pretty much do all the work. Inside that batch file it calls 2 powershell scripts. 1)"Download.ps1" - Downloads some other files from the web. 2.) "Unzip.ps1" - Unzips "Fubar.zip" and places contents in another folder - C:\TEST\
Issue: I've recently gotten familiar with using %~dp0 in batch files. I want to make sure that the location where my co-worker initially downloads the Install.zip doesn't throw off my batch file. So for example.. some people will download .zip files to the "Downloads" folder, then extract contents to proper destination. Others will download the .zip to a specific folder, then extract it within that folder. [Ex: C:\Alex\Install.zip --Extraction-- C:\Alex\Install\((Content))] So I tried to not pre-define file locations due to the variables. I've gotten the %~dp0 to work everywhere I need it to in my batch file. The only issue I have is getting my powershell scripts to use same working directory that my batch file is in. *My batch file and my powershell scripts will always be in the same directory. (Wherever that may be)
Goal: I want my powershell script ("Unzip.ps1") to look for my "Fubar.zip" file in the same directory that its currently running in. (Again - Wherever that may be) I basically want to remove any variables that may throw off the powershell script. I want it to always use it's current working directory to locate Fubar.zip. I basically need powershell to either use its current working directory OR figure out a way to have it pull its current working directory and use that to look for "Fubar.zip".
my current "Unzip.ps1" powershell script is extremely basic.
Unzip.ps1:Expand-Archive -Force c:\ALEX\Install.zip\Fubar.zip -dest c:\TEST\
Batch File Command that calls the Unzip.ps1 script: Powershell.exe -executionpolicy remotesigned -File %~dp0UNZIP.ps1
Please keep in mind, I'm just learning scripting and I'm teaching myself. My knowledge is limited, but I've made it this far and this is the only part I'm stuck on. Please give clear responses. Any help or advice would be extremely appreciated! Using PowerShell 5.0
Thanks in advance!
The equivalent of cmd.exe's %dp0 in PowerShell v3+ is $PSScriptRoot:
Both constructs expand to the absolute path of the folder containing the batch file / PowerShell script at hand.
(The only difference is that %dp0 contains a trailing \, unlike $PSScriptRoot).
Thus, to make your Unzip.ps1 script reference Fubar.zip in the same folder that the script itself is located in, use:
Expand-Archive -Force $PSScriptRoot\Fubar.zip -dest c:\TEST\
Constructing the path as $PSScriptRoot\Fubar.zip - i.e., blindly joining the variable value and the filename with \ - is a pragmatic shortcut for what is more properly expressed as (Join-Path $PSScriptRoot Fubar.zip). It is safe to use in this case, but see the comments for a discussion about when Join-Path should be used.
The automatic $PSScriptRoot variable requires PS v3+; in v2-, use
Expand-Archive -Force ((Split-Path $MyInvocation.Mycommand.Path) + '\Fubar.zip') -dest c:\TEST\
From your description, I gather that Fubar.zip and Unzip.ps1 are in the same directory. We'll pretend this directory is C:\Users\Me\Temp; although I understand that may vary.
Powershell's working directory will be the directory you're in (if called from CMD) when you launch; otherwise, it'll be from $env:UserProfile. Since the .bat file always call Unzip.ps1 from the directory that it's in (C:\Users\Me\Temp), powershell.exe will find it with this command (you can still use %~dp0 here; it's not hurting anything):
powershell.exe -ExecutionPolicy RemoteSigned -File Unzip.ps1
Inside of Unzip.ps1, you'll use Get-Location:
Expand-Archive -Force "$(Get-Location)\Fubar.zip" -dest c:\TEST\
However, if the .bat file does a cd into another directory, this won't work. From your %~dp0UNZIP.ps1 example, I assume this isn't the case, but let's address it anyway. If this is the case, you need to process from where the location of the script is. So for this call the full/relational path to the .ps1:
powershell.exe -ExecutionPolicy RemoteSigned -File C:\Users\Me\Temp\Unzip.ps1
Then, your Unzip.ps1 will need to look like this:
Expand-Archive -Force "${PSScriptRoot}\Fubar.zip" -dest 'C:\TEST\'
Alternatively, you can also do some fancy path splitting, as #JosefZ suggested. The $PSCommandPath and $MyInvocation variables contain the full path to your script; which you should familiarize yourself with:
$location = Split-Path $PSCommandPath -Parent
$location = Split-Path $MyInvocation.MyCommand.Path -Parent
Expand-Archive -Force "${location}\Fubar.zip" -dest 'C:\TEST\'
Note: Of course, you wouldn't set $location twice. I'm just showing you two ways to set it.
I hope this helps!
Prior to Powershell 3
$currentScriptPath = Split-Path ((Get-Variable MyInvocation -Scope 0).Value).MyCommand.Path
Otherwise, $PsScriptRoot will work. If you're going to depend on it, though, I'd make sure you mark the script with #Requires -version 3
I'd advise against changing the PWD unless you must. Which is to say, reference the variable directly.

Resources