What is the use of -recurse in powershell? - powershell

Sorry it's probably a dumb question: what is the difference between
Remove-Item -recurse -Force -Verbose and Remove-Item -Force -Verbose
it seems if we use -recurse for a folder and subfolders powershell delete file one by one inside the folder. and if we remove the -recurse powershell simply delete the main folder without checking inside the folder. technically isn't script will run faster without -recurse?

tl;dr
Pass both -Recurse and -Force to Remove-Item in order to (more) predictably delete (remove) a given folder, which - barring any permission and timing problems - deletes the folder and any contents it may have.
Caveat: This instantly deletes any contents inside the target folder, and, given that deleted items are not placed in the operating system's recycle bin, can only be recovered, potentially, with specialty software.
it seems if we use -recurse for a folder and subfolders powershell delete file one by one inside the folder.
More accurately, it deletes the target folder's subtree, i.e. it recursively deletes all files and subfolders located in the target folder, including their files and subfolders, recursively, before deleting the target folder itself. In effect, it deletes the target folder and all of its contents, if any, but note the caveats:
re "protected" items, which additionally require -Force, discussed below.
re intermittent failures due to the file-system APIs being asynchronous in older Windows versions, discussed in the bottom section.
In fact, deleting all the contents of a folder before deleting the folder itself is the only way to delete a nonempty folder, technically: the file-system APIs do not offer deletion of a nonempty folder as a single operation.
Because inadvertently deleting an entire subfolder tree can have disastrous consequences, as a safety mechanism PowerShell requires you to signal the intent to delete a nonempty folder explicitly - by passing -Recurse.
If you neglect to pass -Recurse and the target folder is nonempty, you get an interactive confirmation prompt - irrespective of whether -Force is specified or not. Choose [A] Yes to All (type a) to delete the folder and all its contents - but see the situational additional need for -Force below.
That said, you do also need -Force in order to (more) predictably remove a nonempty target folder, because -Force makes PowerShell also delete "protected" files and folders, which are hidden files and folders and files that have the ReadOnly and/or System attributes set (on Windows).
If you neglect to pass -Force when you use -Recurse or interactively choose [A] Yes to All in response to the confirmation prompt, the presence of at least one protected item will prevent removal of the target folder as a whole, though unprotected items inside the subtree will get deleted right away.
Each protected item will cause a non-terminating error to be emitted, concluded by a non-terminating error that the target folder cannot be removed, because it isn't empty (yet). Perhaps confusingly, in Windows PowerShell the per-protected-item error messages only talks about "[in]sufficient access rights", even though the real problem in this case isn't one of permissions; the error message has been amended in _PowerShell (Core) 7+ to explicitly mention hidden, system, and readonly items.
if we remove the -recurse powershell simply delete the main folder without checking inside the folder.
No: It follows from the above that you cannot delete a given nonempty folder unless you delete its contents first.
If you attempt that without -Recurse, you'll invariably get the confirmation prompt (or, in non-interactive scenarios, the call will fail outright).
technically isn't script will run faster without -recurse?
It also follows from the above that only an empty folder can be removed without -Recurse without triggering the confirmation prompt.
If you do also specify -Recurse when targeting an empty folder, hypothetically unnecessary work of testing whether child items exist could be performed. In practice, Remove-Item's implementation always performs this test, whether or not you pass -Recurse.
Even with both -Recurse and -Force specified, overall removal may fail:
... due to insufficient file-system permission held by the current user relative to the target folder and its contents.
... intermittently, due to running on Windows versions older than Windows 10 20H2 (I don't know that Windows Server version that corresponds to), because file-system item deletion there was inherently asynchronous(!), resulting in intermittent failure to fully delete a given target folder, namely if deletion of an item inside the folder hadn't completed yet by the time deletion of the folder itself was attempted: see this answer for details and a workaround.

when you use Remove-Item on a dir tree that holds files ... and do NOT use -Recurse, you will get the standard confirmation prompt. so using that parameter makes it run without the delay from the "do you really want to do this?" prompt.
this is one of the reasons that some folks prefer to pipe the output of Get-ChildItem -Recurse to Remove-Item.

Related

In PowerShell, how to tell if a directory "delete" op will succeed?

I'm writing a PowerShell script that will, if possible, delete an occasional extraneous directory if a subtask (over which I have no control, apart from invoking it) fails to properly delete it.
The catch is that the directory (which has a lot of files and recursive hierarchy) may or may not be in use by another (large, 3rd party) program. Normally, I'm doing this in Windows Explorer: I hit the "delete" key, I get a "in use" dialog, shrug and move on, no harm done. (It's a big process and it usually cleans up after itself.)
What I don't want to do is call Remove-Item -Recursive and have it purge half a directory and then discover that some random file is in use. Nor am I even sure it's a file-in-use issue; maybe it's because the directory (or a subdirectory) is some process's current directory or, for some reason, the directory (or subdirectory) itself is open (I'm just making stuff up at this point), or some entirely different and mysterious cause.
I'm hoping to duplicate the current manual process.
Can anyone point me in the right direction? Basicaly, I'm looking for Remove-Directory-If-You-Can-But-Don't-Do-Anything-At-All-If-You-Can't.
(Edit)
Note that I don't want to remove anything in the directory if the directory is still "in use", in some sense. That will corrupt the large process using the directory. If the large process has finished and moved on, I can delete the directory.
(Edit)
This? https://github.com/pldmgg/misc-powershell/blob/master/MyFunctions/PowerShellCore_Compatible/Get-FileLockProcess.ps1
Called recursively? (Not sure if it'll work for subdirs, but I can experiment.)
Could try moving the items away somewhere safe first, and restore items that were successfully moved if anything goes wrong.
If you move things to a folder in the path for temp files, you don't have to delete them - the system will delete them sometime after your process releases the handle on the new folder.
Simple case:
$tempRoot = Split-Path (New-TemporaryFile).FullName
$tempDir = New-Item "$tempRoot\$(New-Guid)" -Type Directory
Move-Item "$HOME\Downloads\SomeFolder\" $tempDir
If anything is holding a file handle in the folder being moved, it will fail and the move won't occur.
Deleting several files and folders:
$tempRoot = Split-Path (New-TemporaryFile).FullName
$tempDir = New-Item "$tempRoot\$(New-Guid)" -Type Directory
try {
Move-Item "$HOME\Downloads\*" $tempDir.FullName -ErrorAction Stop
Remove-Variable tempDir
} catch {
Move-Item "$($tempDir.FullName)\*" "$HOME\Downloads"
}
Move-Item will move whole folders as given above, so no need for recursion.
The move operation should be quick, assuming the files/folders you're moving are on the same drive as the temp folder. The files themselves don't have to be physically moved on disk. The OS just modifies an entry in a table. Moving an entire folder only requires one update (not one for each file within it).
If the above doesn't satisfy your needs, maybe check out this article: How to Manage Open File Handles with PowerShell.

Can't remove directory passed as argument to the powershell script runs asynchronously

I have a powershell script which needs to delete the directory passed as argument and the script should be started asynchronously.
Example:
Script_1.ps1
$Path = "C:\XYZ"
$tempDir = "C:\Temp"
Start-Process -FilePath "powershell.exe" -ArgumentList "$($tempDir)\Script_2.ps1","-PathToDelete","$Path"
Script_2.ps1
param(
# The path to delete
[Parameter(Mandatory=$True,ValueFromPipeline=$False,HelpMessage="The path to delete")]
[string]$PathToDelete,
)
Remove-Item -path $PathToDelete -Recurse -Force
exit 0
Currently ,Remove_Item throws exception saying path cannot deleted because it's in use. Process explorer says the path to be deleted is used by powershell process started in script_1.ps.
You say that the process running Script_1.ps1 is preventing the removal, and I'm assuming you've already ruled out the following causes:
You're running that script from a directory located inside the directory tree you're trying to remove.
Inside that script, before calling Script_2.ps1:
you're changing the current directory to a directory located inside the directory tree.
or you've opened a file located in that directory tree without having closed it yet.
This leaves the following potential - and obscure - cause:
The process-level working directory - which is distinct from PowerShell's current location - may be a directory inside the directory tree you're trying to remove, thereby preventing removal.
To rule that out, change it to C:\ from inside Script_2.ps1, before calling Remove-Item, as follows:
[Environment]::CurrentDirectory = 'C:\'
Note:
That PowerShell's working directory (as reflected in the automatic $PWD variable and the return value from Get-Location) differs from the process' is unfortunate, but cannot be avoided, because a single PowerShell process can host multiple runspaces (sessions) simultaneously, each of which must maintain their own working directory (location) - see GitHub issue #3428.
A more common pitfall that results from this discrepancy is owed to the fact that .NET APIs use the process' working directory, which means that you cannot pass relative paths to .NET methods without risking their resolution relative to a different working directory than PowerShell's - see this answer for more information.

How to run a powershell script at active directory login

I created a group policy in In Group Policy Management Editor, in the navigation pane, expand User Configuration, expand Policies, expand Windows Settings, and then click Scripts (Logon/Logoff). I made a logon script as a ps1 file:
copy-item "\\server1\Pictures\background.jpg" -Destination "C:\screensaver\" -Recurse
I added that ps1 file in the powershell scripts part of the group policy and set it to run powershell scripts first.
I didn't use any parameters which may be causing the issue?
I need each computer to have that c:\screensaver\background.jpg image when they login.
It's the only group policy applied to that OU, all the PCs are Windows 10, and the domain controllers are Windows 2012 r2.
In my opinion creating a (PowerShell-) logon-script for copying a file is not a great solution and out-of-date nowadays.
Make your life easier and use group-policy-preferences for this task. You don't have to create scripts for that.
Open the Group Policy Management Console, select your policy, open the "Preferences"-Node and select "Files". Create a new element and select the source- and the target (as shown below).
After that reboot the client and the file should get copied without coding.
Sounds like there's two parts to implementing your request. Before doing any of the following, make sure that you can log in as one of the users, and manually perform the steps you want the script to complete (to make sure any user restrictions aren't holding you up). So, make sure you can navigate to the remove image location \\server1\Pictures\background.jpg, and copy it to the local folder C:\screensaver.
Copying the file to the target machine. You provided the contents of your PS1 file as copy-item "\server1\Pictures\background.jpg" -Destination "C:\screensaver\" -Recurse. I believe you'll want to have two slashes "\\" at the beginning of your \server1 string, resulting in "\\server1\Pictures\background.jpg" (the two slashes make this a valid UNC path). Additionally, you included the -Recurse parameter. I don't understand the need for this parameter, based off of the documentation, accessible via the command Get-Help Copy-Item -Full.
-Recurse [<SwitchParameters>]
Indicates that this cmdlet performs a recursive copy.
I would suggest that you include the -Force parameter. If you ever update that image, the next time a user logs on, they'll receive the updated image. Without the -Force parameter, the command might not overwrite the existing image on disk. Also, you shouldn't need the trailing slash on the -Destination parameter. I would suggest the command resemble:
Copy-Item "\\server1\Pictures\background.jpg" -Destination "C:\screensaver\" -Force
Configuring the wallpaper via Group Policy The first link I found via Google Search set windows 10 wallpaper via group policy with anything that looked like useful steps was some grouppolicy.biz website. I haven't been able to test them, but the main point being that you'll need to make sure that you're actually telling the computer to use the wallpaper you've copied into place.
If you make sure that you've addressed the above items, then it should work for you. There may be some considerations for the first time a user logs in, if the image isn't copied over, then the wallpaper may not display until the second time they log in.

Delete complete folder using a PowerShell script

I am using the following expression to delete a folder from PowerShell. I need to delete a complete folder (including all the files and sub folders).
Remove-Item -Recurse -Force $DesFolder
But this gives me the exception "The directory is not empty"
I am not getting this exception every time when I run the program; it happens randomly. What would be the reason for this and how do I fix this? Because I was failing to reproduce this.
We cannot delete an non-empty directory using commands like rmdir or Remove-Item, this is to avoid accidental deletion of important system files by users during programming.
Therefore before trying to delete the directory, empty it. Clear the contents and then delete it. :)
Remove-Item -Recurse always deletes the directory and all its contents recursively. But it may still fail if directory is modified (i.e. new files are created) by some third-party activity in middle of remove process.
Also, if some of the files cannot be deleted (e.g. due to permission restrictions) Remove-Item will also fail.
So, I'd recommend you to check what exactly is laying inside the directory after exception.

Get-ChildItem is not showing all files in a folder

When I call Get-ChildItem in PowerShell it is only returning a few of the files that exist in the directory. This is the driver folder, so I tried using the -Force parameter in case they were hidden, but with no luck.
It's interesting though because it works perfect on my Windows 7 32 bit, but not 64 bit. Any ideas?
I believe PowerShell is showing you everything however the folder you're looking at in the x86 PowerShell prompt isn't what you think. The directory you're actually looking at is under C:\Windows\SysWow64\Drivers and not actually C:\Windows\System32\Drivers. This is due to a Windows feature (Vista and higher) for 32-bit processes running on 64-bit OS called virtualization (specifically the File System Redirector). When you run a 64-bit PowerShell prompt virtualization is not used so you see the real C:\Windows\System32\Drives dir.
From a 32-bit PowerShell prompt, you can see the "real" C:\windows\system32\drivers dir by using this path:
Get-ChildItem C:\Windows\SysNative\Drivers
I ran across this while searching for a similar issue. I want to list user folder usage, but run into issues with folder ownership/permissions. Even though I am a local admin, I need to explicitly give myself access. Not ideal. Below gives accurate usage, despite permissions. (Of course, if you want to know more detailed usage, you need to use a for loop or something.)
Get-ChildItem "C:\Users" -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object Length -Sum
As for what files are included, hidden and system files are not shown by default: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-6
Hidden parameter: Hidden files only
System parameter: System files only
Force parameter: Include hidden and system files, with regular files
To confirm, I granted myself permission to one of the user directories. I compared size reported from PowerShell (before granting permission) and that reported in File Explorer (after granting permission). Size and count was the same.