I am successfully able to extract contents of a file using:
Expand-Archive -Force -LiteralPath c:\folder\my_file.zip -DestinationPath c:\dest_folder
However, it is not displaying the files which are overwritten.
How to display only the files which are overwritten?
I am getting an error while running the command:
Expand-Archive -LiteralPath .\my_file.zip -DestinationPath .\Destination\ -Force -Verbose 4>&1 |
Select-String 'Remove File'
Error:
The ampersand (&) character is not allowed. The & operator is reserved for future use; wrap an ampersand in double
quotation marks ("&") to pass it as part of a string.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingFileSpecification
If you really want to replace the existing files but also know which ones where replaced, an alternative would be to redirect the verbose stream to the success stream and filter by those lines containing "Remove File". Expand-Archive when using -Verbose, will output the following when replacing (removing first and then creating) a file:
Performing the operation "Remove File" on target .....
In that sense, you can do the following:
Expand-Archive -LiteralPath .\my_file.zip -DestinationPath .\Destination\ -Force -Verbose 4>&1 |
Select-String 'Remove File'
Related
I have been practicing PowerShell by dealing with some of the tasks I could do in the file explorer. I am organizing some files for a python project which I am doing. My goal was to copy all python files in the current directory into the "V0.0_noProgressBar" directory:
ls -Filter "*.py" | copy $_ "V0.0_noProgressBar"
but it fails:
Cannot bind argument to parameter 'Path' because it is null.
At line:1 char:26
+ ls -filter "*.py" | copy $_ "V0.0_noProgressBar"
+ ~~
+ CategoryInfo : InvalidData: (:) [Copy-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,
Microsoft.PowerShell.Commands.CopyItemCommand
I assume this should be sufficient information to figure this out, let me know if more is needed. I have run into similar issues a number of times, so there must be a fundamental problem with my understanding of the placeholder $_.
This can be simplified to:
Copy-Item *.py V0.0_noProgressBar
To answer original question, why $_ is not working:
$_ is only valid in script contexts, not just anywhere in the pipeline. E. g. you could use it in a script block of ForEach-Object:
Get-ChildItem -filter "*.py" | ForEach-Object { Copy-Item $_ "V0.0_noProgressBar" }
To augment zett42's succinct answer. $_ makes an appearance in several areas of PowerShell. Some cmdlets allow it's use in a script block the output of which is treated as the argument to the parameter. In keeping with the question the *-Item cmdlets can make use of $_.
Get-ChildItem -Filter "*.txt" | Copy-Item -Destination { "C:\"+ $_.Name }
Obviously that's just an example. Perhaps a more useful case is Rename-Item -NewName { ... $_ ... }. -NewName which also works this way.
Other common cmdlets that make use of $_ are Select-Object, Sort-Object, & Group-Object. Overlapping some of these $_ is used by many cmdlets to help define calculated properties. I strongly recommend reading this about topic. Along with the use of $_ calculated properties are extremely useful. I use them with Select-Object everyday!
If you really want to pass a collection of files through a pipeline,
you can do this:
ls -Filter *.py | Copy-Item -Destination "V0.0-NoProgressBar"
Here the Copy-Item cmdlet has no -Path parameter, so it gets the files from the pipeline. See Help Copy-Item -full to see that the -Path parameter can accept pipeline input.
This is not as simple as answers already given, but it does show an alternative to trying to use $_ in a context where it is unavailable.
I'm trying to make a copy of all the existing files in a folder to another folder. but I have problems with the names that have that blank
folder
-- new file.txt---->the problem
-- file2.txt
-- file3.csv
i apply this mybatch.bat
set FECHA=%date%
set FECHA=%FECHA:/=%
set FILE=D:\BACKUPS
for %%i in (*) do (
copy %cd%\%%i %FILE%\${%%~ni// /_}_%DATE%%%~xi
)
try to replace the blanks with "_" the following code, in a route
${%%~ni// /_}
but this does not run, it just comes out as string
D:\BACKUPS\${%%~ni// /_}_090519.txt
i want this
D:\BACKUPS\new_file_090519.txt
Given the PowerShell tag, here is a solution. When you are satisfied that the copy will be done correctly, remove the -WhatIf from the Copy-Item cmdlet.
Get-ChildItem -File -Path 'C:/src/t/sv' |
ForEach-Object {
Copy-Item -Path $_.FullName `
-Destination "C:/src/t/sv2/$($_.BaseName -replace ' ','_')_$(Get-Date -format 'ddMMyy')$($_.Extension)" -WhatIf
}
I'm trying to replicate this command into PowerShell:
grep -lR 'Find' ./src | while read filename; do sed -i".bak" 's/Find/Replace/g' "$filename"; done
What I have so far:
Get-ChildItem "src" -Recurse -File | ForEach-Object { $f = $_; (Get-Content $f) | ForEach-Object { $_ -replace "Find", "Replace" } | Set-Content "$f.tmp"; Move-Item "$f.tmp" $f -Force }
I'm getting an error saying "filename.tmp" does not exist. I thought the above would create the file while parsing. Any help would be appreciated.
Most likely you've fallen victim to Windows PowerShell's inconsistent stringification of the System.IO.FileInfo instances output by Get-ChildItem - see this answer.
The workaround is to use explicit stringification via the .FullName property, which explicitly returns an item's full path.
Applied to your command, alongside some optimizations:
Get-ChildItem -File -Recurse src | ForEach-Object {
$f = $_.FullName # !! Explicitly retrieve the full path
(Get-Content $f -Raw) -creplace 'Find', 'Replace' |
Set-Content -NoNewline "$f.tmp"
Move-Item "$f.tmp" $f -Force
}
Get-Content -Raw reads the entire file into memory as a single string, which is more efficient.
-creplace (which performs case-sensitive replacement, as sed would by default) is directly applied to the resulting multiline string and replaces all occurrences.
-NoNewline (PSv5+) ensures that Set-Content doesn't add an additional trailing newline to the multiline string being saved (the same applies to Out-File / >).
Note: Given that Get-Content -Raw reads the entire file up front, you could even write the modified content back to the very same file, without requiring an intermediate temporary file and a subsequent Move-Item call; that said, doing so bears a slight risk of data loss, if the process of writing back to the same file is interrupted.
Also, while your sed call retains the original file with extension .bak, your PowerShell command does not.
I have almost no powershell experience but was using it as a means to an end to replace possibly problematic characters in FAT32 filenames, such as: , . / ' : etc.
I discovered an article positing use of rename-item -NewName command, whichs works fine inside a specific directory containing files meeting said criteria, but when used at a level above this, I can't figure out how to make the script fully recursive.
I want to replace spaces, apostrophes, instances of periods outside of file extensions, and dollar signs in filenames of audio tracks inside a music library folder that's laid out like so**, running powershell script from music folder to hit everything inside and below that:
X:\home\audio\**music**\[artist_here]\[album_here]\FILE.mp3
Can someone explain the correct syntax to accomplish this?
I also tried using path and /s:
dir X\pathnamehere\ /s | rename-item -NewName {$_.name -replace " ","_"}
but receive another error:
dir : Second path fragment must not be a drive or UNC name.
Parameter name: path2
At line:1 char:1
+ dir X:\home\Audio\Music\ /s | rename-item -NewName {$_.name -repla ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (X:\home\Audio\Music:String)
[Get-ChildItem], ArgumentException
+ FullyQualifiedErrorId :
DirArgumentError,Microsoft.PowerShell.Commands.GetChildItemCommand
dir | rename-item -NewName {$_.name -replace " ","_"}
when running
dir | rename-item -NewName {$_.name -replace " ","_"}
at multiple levels above where files to be renamed are located, I receive the following error for EVERY directory inside \music:
"rename-item : Source and destination path must be different."
+ dir | rename-item -NewName {$_.name -replace " ","_"}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError:
(X:\home\Audio\Music\artistname:String) [Rename-Item], IOException
+ FullyQualifiedErrorId :
RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
Thank you!
P.S. if this can be accomplished with a simpler command prompt batch file, feel free to enlighten me.
Found a solution here:
Recursively renaming files with Powershell
Here's their example:
Get-ChildItem -File -Recurse | % { Rename-Item -Path $_.PSPath -NewName $_.Name.replace(".mkv.mp4",".mp4")}
In powershell Dir is an alias of Get-ChildItem and % is 'ForEach'. You were pretty close.
I have a directory which has about 50 files and I would like to prepend "CD1" to all the file names. After reading here and here. I came up with the following command (using PowerShell ISE on Windows 10):
Get-ChildItem | Rename-Item -NewName {'CD1' + $_.Name}
but that prepended "CD1" several times to each file name. I also got the error:
Rename-Item : Could not find a part of the path.
At line:1 char:17
+ Get-ChildItem | Rename-Item -NewName {'CD1' + $_.Name}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\Users\TOOdhm...CD1CD1CD149.mp3:String) [Rename-Item], DirectoryNotFoundException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
It seemed like it didn't know when to stop iterating through the directory so I used the ForEach-Object cmdlet and tried this:
Get-ChildItem | ForEach-Object { Rename-Item -NewName {('CD1' + $_.Name)} }
which did nothing and gave the error
Rename-Item : Cannot evaluate parameter 'NewName' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input.
Does any one have any clue why I am getting this issue? I could have manually renamed the files by now but both these commands should work. I even found a website where they used an example with almost exactly(I think in their example they may be appending) these commands in this format and it's bugging me that they don't work for me.
One way to avoid getting already renamed items processed again is to enclose Get-ChildItem in parentheses.
(Get-ChildItem) | Rename-Item -NewName {'CD1'+ $_.name}
To avoid another prepend on successive runs exclude files beginning with CD1
Get-ChildItem | Where {$_.Name -notmatch '^CD1'} | Rename-Item -NewName {'CD1'+ $_.name}
Or
gci -Exclude CD1* | Ren -NewName {'CD1'+ $_.name}
In your second attempt, you're using braces for a script-block where you don't need one.
The following worked for me
Get-ChildItem | % {Rename-Item -NewName ('CD1'+ $_.Name) -Path $_.Name}
Note the extra -Path to repeat the current path of the file.