PowerShell Rename-Item is creating a copy of the file - powershell

My script is supposed to rename c:\myfolder\myfile.txt to myfile.bak
Rename-Item "c:\myfolder\myfile.txt" -NewName "myfile.bak"
However, it leaves myfile.txt in place and creates a new file called myfile.bak instead
Does anyone know why?

If its a hidden or read-only file, you have to add the -force parameter:
Forces the cmdlet to rename items that cannot otherwise be changed,
such as hidden or read-only files or read-only aliases or variables.
The cmdlet cannot change constant aliases or variables. Implementation
varies from provider to provider. For more information, see
about_Providers.

Related

Rename files & append Number in Powershell [duplicate]

I've never used powershell or any cmd line to try and rename files, nor so I really know much about script writing in general.
I've already had some success in renaming the files in question but am stuck on the last piece of the puzzle.
Original file names:
NEE100_N-20210812_082245.jpg
NEE101_E-20210812_083782.jpg
NEE102_W-20210812_084983.jpg
I successfully change those to AT-###-N-......jpg using:
Rename-Item -NewName {$_.name -replace "NEE\d\d\d_", "AT-112-"}
And this is what they looked like after:
AT-112-N-20210812_082245.jpg
AT-112-E-20210812_083782.jpg
AT-112-W-20210812_084983.jpg
Now however, I have a few files that look like this:
AT-112-NewImage-20210812_083782.jpg
AT-112-NewImage-20210812_093722.jpg
and I want to change them to:
AT-112-D1-20210812_083782.jpg
AT-112-D2-20210812_093722.jpg
...and so on.
I've tried a few things here to try and do that. Such as replacing "NewImage" with "D" and then using something like this (not exact, just an example):
$i = 1
Get-ChildItem *.jpg | %{Rename-Item $_ -NewName ('19981016_{0:D4}.jpg' -f $i++)}
But this did not work. I have seen scripts that use sequential numbering either added as a suffix or a prefix. But I can't figure out how to do this if what I want to have sequence numbering in the middle of the name.
Hopefully this make sense, if I need more elaboration, let me know. Thanks!
You need to use an expression (inside (...)) as your -replace substitution operand in order to incorporate a dynamic value, such as the sequence number in your case.
In order to use a variable that maintains state across multiple invocations of a delay-bind script block ({ ... }, the one being passed to the -NewName parameter in your first attempt), you need to create the variable in the caller's scope and explicitly reference it there:
This is necessary, because delay-bind script blocks run in a child scope, unfortunately,[1] so that any variables created inside the block go out of scope after every invocation.
Use Get-Variable to obtain a reference to a variable object in the caller's (parent) scope[2], and use its .Value property, as shown below.
$i = 1
Get-ChildItem *.jpg | Rename-Item -NewName {
$_.Name -replace '-NewImage-', ('-D{0}-' -f (Get-Variable i).Value++)
} -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
Note: The above solution is simple, but somewhat inefficient, due to the repeated Get-Variable calls - see this answer for more efficient alternatives.
[1] This contrasts with the behavior of script blocks passed to Where-Object and ForEach-Object. See GitHub issue #7157 for a discussion of this problematic discrepancy.
[2] Without a -Scope argument, if Get-Variable doesn't find a variable in the current scope, it looks for a variable in the ancestral scopes, starting with the parent scope - which in this case the caller's. You can make the call's intent more explicitly with -Scope 1, which starts the lookup from the parent scope.

Powershell - How to replace dots on files exept the extension

I have a question about the replacement of dots on multiple files, i have the next code:
Dir | Rename-item -NewName{ $_.basename.replace(".","-") + $_.extension }
This code works, but i have folders with dots and the problem is when i run the code, the folders repeat the words after the point like a "file extension":
like this:
How can i resolve this problem, i need just replace the dot on folder name with another word or space or everything i like and the files on the folder just replace before the extension.
thanks!
Unfortunately, the .BaseName ETS (Extended Type System) property that PowerShell adds to System.IO.DirectoryInfo instances, i.e. directories, by - unfortunate - design, unconditionally reports the directory name as-is.
It is only for System.IO.FileInfo instances, i.e. files, that .BaseName strips the extension, i.e., the last .-separated component.[1]
You can work around the problem by calling the System.IO.Path.GetFileNameWithoutExtension .NET method, which does not make this distinction (similarly, the type-native .Extension property doesn't make this distinction either, so it can be used as-is).
Get-ChildItem | Rename-Item -NewName {
[IO.Path]::GetFileNameWithoutExtension($_.Name).Replace('.', '-') + $_.Extension
}
[1] You can verify this as follows:
(Get-TypeData System.IO.DirectoryInfo).Members.BaseName vs.
(Get-TypeData System.IO.FileInfo).Members.BaseName

How to return full path to both folders AND files?

I have been tasked with auditing a file server at work, to review directory and file ownership, and subsequent meta data. I have scowered the internet to enable me to string together a script that returns the data required. The script I have created so far is as follows:
Get-ChildItem -r -Path '\\SERVERNAME.wan.net\launshared\Training' |
Select DirectoryName, UNC_Path_Parent_DIR, Name, Type, Extension,
#{N='Owner';E={$_.GetAccessControl().owner}},
#{N='SizeInKb';E={$_.Length/1kb}}, CreationTime, LastAccessTime,
LastWriteTime |
epcsv 'C:\Users\USERNAME\Desktop\Information Management\TrainingTEST - 2018- SCRIPT_RESULT.xls' -En UTF8 -NoType -Delim ','
Please ignore the UNC_Path_Parent_DIR entry, this simply inserts a field header with no returned data in the exported CSV file.
The script works fine, albeit with one exception...it does not return the full UNC path for the directories! Please note, the full UNC path is returned for the files but NOT the directories!
I have searched the internet for a solution, enquired with our IT guys, however cannot for the life of me locate a solution that will list the full UNC path to both directories AND files.
The UNC path is not a property of the objects coming from Get-ChildItem. What this means is that it needs to be added manually. One can do this, just like you did, by adding a custom property with an expression in Select-Object.
Please keep in mind that when others read your code it's always nicer to format things and use the full parameter name. It makes it easier to read and understand, also for you when you read it again in 10 years time ;)
I'll help you on your way by letting you know that you can find the code that gets the UNC path here and here. The only thing left is to implement it in the correct place within the Select-Object. I leave that as an exercise for you:
Get-ChildItem -Recurse -Path '\\fsabbey12.wan.net\launshared\Training' | Select-Object FullName, DirectoryName,
Name, Type, Extension,
#{N='UNC';E= {
# Code to get the UNC Path
}},
#{N='Owner';E= {$_.GetAccessControl().owner}},
#{N='SizeInKb';E={$_.Length/1kb}},
CreationTime, LastAccessTime, LastWriteTime
I think this will get you started.
I'll leave it here too as additional possible way (although #DarkLite1 answer covers the question quite fully).
It's possible that FullName will fulfill your needs without any custom expression.
If you use Get-ChildItem like this:
Get-ChildItem -Path \\servername.domain.com\C$\foldername\
Then using | Select FullName will give you path in the UNC format:
\\servername.domain.com\C$\foldername\\FirstSubfolder
Keep in mind that it applies only to the situation when you reference the remote folder, not folder from the machine you run the script on. If you use FullName on local folder you'll get format like this:
C:\FolderName\FirstSubfolder

PowerShell guidelines for -Confirm, -Force, and -WhatIf

Are there any official guidelines from Microsoft about when to add -Confirm, -Force, and -WhatIf parameters to custom PowerShell cmdlets? There doesn't seem to be a clear consensus about when/how to use these parameters. For example this issue.
In the absence of formal guidelines, is there a best practice or rule of thumb to use? Here is some more background, with my current (possibly flawed) understanding:
-WhatIf
The -WhatIf flag displays what the cmdlet would do without actually performing any action. This is useful for a dry run of a potentially destabilizing operation, to see what the actual results would be. The parameter is automatically added if the cmdlet's Cmdlet attribute has the SupportsShouldProcess property set to true.
It seems like (but I'd love to see more official guidance here) that you should add -WhatIf if you are ever adding or removing resources. (e.g. deleing files.) Operations that update existing resources probably wouldn't benefit from it. Right?
-Force
The -Force switch is used to declare "I know what I'm doing, and I'm sure I want to do this". For example, when copying a file (Copy-File) the -Force parameter means:
Allows the cmdlet to copy items that cannot otherwise be changed, such as copying over a read-only file or alias.
So to me it seems like (again, I'd love some official guidance here) that you should add an optional -Force parameter when you have a situation where the cmdlet would otherwise fail, but can be convinced to complete the action.
For example, if you are creating a new resource that will clobber an existing one with the same name. The default behavior of the cmdlet would report an error and fail. But if you add -Force it will continue (and overwrite the existing resource). Right?
-Confirm
The -Confirm flag gets automatically added like -WhatIf if the cmdlet has SupportsShouldProcess set to true. In a cmdlet if you call ShouldProcess then the user will be prompted to perform the action. And if the -Confirm flag is added, there will be no prompt. (i.e. the confirmation is added via the cmdlet invocation.)
So -Confirm should be available whenever a cmdlet has a big impact on the system. Just like -WhatIf this should be added whenever a resource is added or removed.
With my potentially incorrect understanding in mind, here are some of the questions I'd like a concrete answer to:
When should it be necessary to add -WhatIf/-Confirm?
When should it be necessary to add -Force?
Does it ever make sense to support both -Confirm and -Force?
I haven't researched whether the documentation is this detailed, but the following are based on my observations:
You should use -WhatIf for anything that makes a change. Updates are changes that can benefit from -WhatIf (e.g., what if you want to make a lot of updates?).
-Force means "force overwrite of an existing item" or "override a read-only file system attribute". In either case the success of the action depends on the user having permission.
-Confirm and -Force are not mutually exclusive. For example, you can confirm an action to write a file, but the file might be protected with the read-only attribute. In this case the action would fail unless you also specify -Force.
If you want to validate that your implementation of these common parameters is compliant to the guidelines (for example, Set-Xxx cmdlets should have -Confirm and -WhatIf), then you can use the excellent PsScriptAnalyzer module (which is based on code analysis).
Make sure the module is installed:
PS E:\> Install-Module -Name 'PsScriptAnalyzer'
Then run PowerShell Code Analysis as follows:
PS E:\> Invoke-ScriptAnalyzer -Path . | FL
RuleName : PSUseShouldProcessForStateChangingFunctions
Severity : Warning
Line : 78
Column : 10
Message : Function 'Update-something' has verb that could change system state.
Therefore, the function has to support 'ShouldProcess'.
Documentation (and sources) can be found on GitHub:
https://github.com/PowerShell/PSScriptAnalyzer
As an added observation, -Force should not overrule -WhatIf. Or in other words: -WhatIf has priority over -Force.
If you use:
Get-ChildItem -Recurse | Remove-Item -Recurse -Force -WhatIf
it will result in the following output:
What if: Performing the operation "Remove Directory" on target "E:\some directory\".
It will not actually remove the items, even when -Force is specified.
This means that you should never write:
if($Force -or $Pscmdlet.ShouldProcess($)) {
...
}

How to Remove the Hidden attribute from a text file

Powershell Version: 4.0
Operating System: Windows 7
"Error Report" | Set-Content $errorText
$getError = Get-Item $errorText
$getError.Attributes ="Hidden"
I've created an error log file which will remain hidden unless their is an error. How would I remove the hidden attribute so that I could see my file? In other words what is the equivalent of right clicking the file, selecting properties, and unchecking hidden attribute in powershell?
Attributes property is of type [System.IO.FileAttributes]. Looking at TechNet you can see the valid options for setting attributes on file. As per the comments one option you have is no remove all attributes from the file/folder.
Normal - The file is a standard file that has no special attributes. This attribute is valid only if it is used alone.
Be aware that this would potentially remove other attributes like read-only or system. In your case that might not be a concern but we need to be aware of the possibility.
The related second issue about using Get-Item on hidden files is solved by using the switch -Force and explained on TechNet as well.
Allows the cmdlet to get items that cannot otherwise be accessed, such as hidden items.
Knowing that we could do something like this:
$getError = Get-Item $errorText -Force
$getError.Attributes ="Normal"