I have a simple task of getting a file from one path and copying it to another using PowerShell script. Now, I understand that using Get-ChildItem, if-Path is not specified, it will get the current directory. I'm using this code, setting the directory the file is in, but it is still getting the current directory:
$file = Get-ChildItem -Path "\\w102xnk172\c$\inetpub\wwwroot\PC_REPORTS\exemplo\DCT\Files\STC" | Sort-Object LastWriteTime | Select-Object -Last 1
Copy-Item $file -Destination "\\Brhnx3kfs01.vcp.amer.dell.com\brh_shipping$\DEMAND_MONITOR\"
cmd /c pause | out-null
It returns this error when it pauses:
This is script follow the rules in the documentation of both Get-ChildItem and Copy-Item. Am I missing something? Why is it still getting the current directory? I tried mutiple syntaxes differences, getting the paths out of commas, not setting -Path or -Destination, setting the file directly in the Copy-Item without using the $file variable...
Copy-Item expects a [string] as the first positional argument - so it attempts to convert $file to a string, which results in the name of the file.
Either reference the FullName property value (the full path) of the file:
Copy-Item $file.FullName -Destination "\\Brhnx3kfs01.vcp.amer.dell.com\brh_shipping$\DEMAND_MONITOR\"
Or pipe the $file object to Copy-Item and let pipeline binding do its magic for you:
$file |Copy-Item -Destination "\\Brhnx3kfs01.vcp.amer.dell.com\brh_shipping$\DEMAND_MONITOR\"
If you want to see for yourself how this is processed internally by powershell for yourself, use Trace-Command:
Trace-Command -Name ParameterBinding -Expression {Copy-Item $file -Destination "\\Brhnx3kfs01.vcp.amer.dell.com\brh_shipping$\DEMAND_MONITOR\"} -PSHost
Related
I have some files in a folder with no extension file. I want to copy all the files to another folder and change their extension to .txt.
I tried this code, but it still errors for creating the destination file.
$JOB = Copy-Item -Path C:\Users\XX\Documents\Folder1* -Destination "C:\Users\XX\Documents\Folder2"
Rename-Item -Path C:\Users\XX\Documents\Folder2\* -NewName *.TXT
Get-ChildItem -Path .\Folder1 -File |
ForEach-Object { Copy-Item -Path $_.FullName -Destination ".\Folder2\$($_.BaseName).txt" }
# Alternatively without ForEach-Object (see note below)
Get-ChildItem -Path .\Folder1 -File |
Copy-Item -Path $_.FullName -Destination { ".\Folder2\$($_.BaseName).txt" }
That should do the job. Keep in mind it will work only for one level, if you want recursive copy of folder structure, you'll have to modify the script slightly.
Basically, what's happening here is you find all the files and then pipe them to Copy-Item constructing destination path with BaseName property of source file (which doesn't have extension included, in comparison to Name property).
NOTE: as -Path accepts pipeline input (see docs here), you don't need to use ForEach-Object. However, it might still be useful for visibility (depending on your preferences).
Credits to #LotPings for noticing the above.
Trying with Windows PowerShell to delete all directories that contain a XML-file with a specific content. Getting as far as listing all the files containing the correct pattern with
ls -r -Filter *.xml | ?{ $_ | Select-String -Pattern "ACC_cont"}
but can´t get the paths from the output and delete the directory and all it´s content. There might be more than one direcotry with a XML-file with the right pattern, and I like to delete them all.
Try the following:
Get-ChildItem -Recurse -Filter *.xml |
Select-String -List -Pattern "ACC_cont" |
Remove-Item -Recurse -LiteralPath { Split-Path -Parent $_.Path } -WhatIf
-WhatIf previews the operation; remove it to perform actual deletion.
Select-String directly accepts file-info objects output by Get-ChildItem from the pipeline.
-List makes Select-String stop after the 1st match in a file, which improves efficiency.
The match-info objects output by Select-String have a .Path property that contains the input file path, so Split-Path -Parent $_.Path gets a matching file's director path.
Passing Split-Path -Parent $_.Path inside a script block ({ ... }) to Remove-Item's -LiteralPath parameter is instance of a delay-bind script block that provides parameter values derived dynamically from each input object, so that Remove-Item -Recurse removes every directory in which a matching XML file was found.
I have a folder that contains several thousand files. I would like to write a Powershell script that loops through the files and copies each file whose filename contains a specific keyword. In pseudocode:
For each file in C:\[Directory]
If filename contains "Presentation" Then
copy file in C:\[Directory 2]
Simply like this ?
copy-item "C:\SourceDir\*Presentation*" "C:\DestinationDir"
or like this :
copy-item "C:\SourceDir\*" "C:\DestinationDir" -Filter "*rrrr*"
But a risk exist if you have a directory with "presentation" in his name into the source directory. Then take all method proposed here and add -file in get-childitem command.
Like in this short version of Robdy code :
gci "C:\SourceDir" -file | ? Name -like "*Presentation*" | cpi -d "C:\DestinationDir"
That code should do the trick:
$files = Get-ChildItem -Path "C:\path\to\source\folder"
$files | Where-Object Name -Like "*Presentation*" | Copy-Item -Destination "C:\path\to\destination\folder"
Of course can be written in one line but I put in two for visibility.
Edit: as Esperento57 pointed out, you might want to add -ItemType File to Get-ChildItem cmdlet to not include folders with 'Presentation' in their name. Also, depending on your needs you might also want to use -Recurse param to include files in subfolders.
If you have files in subfolders and you want to keep the path in destination folder you'll have to change the script a bit to something like:
Copy-Item -Destination $_.FullName.Replace('C:\path\to\source\folder','C:\path\to\destination\folder')
And for the above you'll have to make sure that folders are actually created (e.g. by using -Force for Copy-Item.
This seems to work:
$src = "Dir1"
$dst = "Dir2"
Get-ChildItem $src -Filter "*Presentation*" -Recurse | % {
New-Item -Path $_.FullName.Replace($src,$dst) -ItemType File -Force
Copy-Item -Path $_.FullName -Destination $_.FullName.Replace($src,$dst) -Force
}
Try something like this:
Get-ChildItem "C:\Your\Directory" -File -Filter *YourKeyWordToIsolate* |
Foreach-Object { Copy-Item $_.FullName -Destination "C:\Your\New\Directory" }
... but, of course, you'll need to fill in some of the blanks left open by your pseudocode example.
Also, that's a one-liner, but I inserted a return carriage for easier readability.
I want to move the file "file_to_move.txt"in each folder to their respective "done"-folder.
so the file_to_move.txt in C:\Temp\test\folder1 is moved to C:\Temp\test\folder1\done
and file_to_move.txt in C:\Temp\test\folder2 is moved to C:\Temp\test\folder2\done
...and so on, preferably with a %date%_%time% added to the file-name.
if a folder (like folder4 in the example below) does not have a file_to_move.txt, the script should just ignore it and move on.
folder structure example:
C:\Temp\test\DONE
C:\Temp\test\folder1
C:\Temp\test\folder1\done
C:\Temp\test\folder1\some_other_folder
C:\Temp\test\folder1\some_other_file.txt
C:\Temp\test\folder1\file_to_move.txt
C:\Temp\test\folder2
C:\Temp\test\folder2\done
C:\Temp\test\folder2\some_other_folder
C:\Temp\test\folder2\some_other_file.txt
C:\Temp\test\folder2\file_to_move.txt
C:\Temp\test\folder3
C:\Temp\test\folder3\done
C:\Temp\test\folder3\some_other_folder
C:\Temp\test\folder3\some_other_file.txt
C:\Temp\test\folder3\file_to_move.txt
C:\Temp\test\folder4
C:\Temp\test\folder4\done
C:\Temp\test\folder4\some_other_folder
C:\Temp\test\folder4\some_other_file.txt
I have experimented with a Powershell script even if I'm not very good at it and I dont know it can be done in a standard batch-script.
I have tried this so far:
In a batch-script:
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%bin\movescript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
in the movescript.ps1:
Move-Item C:\Temp\test\*\file_to_move.txt C:\Temp\test\*\done\file_to_move_$(get-date -f yyyyMMdd_HHmmss).txt
But this is not working.
I guess it's not precise enough to work.
As a bonus, can the whole thing be done within the basic script or must we use the external .PS1-file?
You can use the Get-ChildItem cmdlet with a filter to retrieve all file_to_move.txt files recursively from a path. Use the Foreach-Object (alias foreach) to iterate over them and combine the new path using the Join-Path cmdlet. To Copy the Item, you can use the Copy-Item cmdlet:
$itemsToCopy = Get-ChildItem -Path c:\Temp\Test -Filter file_to_move.txt -Recurse
$itemsToCopy | foreach {
$newPath = Join-Path $_.DirectoryName 'done'
New-Item -Path $newPath -ItemType directory -Force | out-null
$_ | Copy-Item -Destination $newPath
}
If you want to add a Timestamp, you could use the Get-Date cmdlet and invoke the ToString method with your desired format on it, example:
(Get-Date).ToString("yyyy-dd-M_HH-mm-ss")
Output:
2016-05-4_15-06-02
You can now concat the filenames using a format string and the $_.Basename and $_.Extension property within your foreach loop. I will leave this as an exercise to you.
I'm using the following command to copy a directory tree from one folder to another.
Copy-Item $SOURCE $DEST -Filter {PSIsContainer} -Recurse -Force -Verbose
The verbose option is correctly showing each folder that is copied. However, I would like to tell the Verbose option to only shows the first level of the subfolders that are copied. Hence the subfolders/subfolders/... etc wouldn't appear.
Is it possible?
Instead of using the -Verbose option, you could use the -PassThru option to process the successfully processed items via the pipeline. In the following example, I am assuming that $DEST is the existing directory in which the newly copied directory will appear. (You cannot call Get-Item on non-existant objects.)
$SOURCE = Get-Item "foo"
$DEST = Get-Item "bar"
Copy-Item $SOURCE $DEST -Filter {PSIsContainer} -Recurse -Force -PassThru | Where-Object {
# Get the parent object. The required member is different between
# files and directories, which makes this a bit more complex than it
# might have been.
if ($_.GetType().Name -eq "DirectoryInfo") {
$directory = $_.Parent
} else {
$directory = $_.Directory
}
# Select objects as required, in this case only allow through
# objects where the second level parent is the pre-existing target
# directory.
$directory.Parent.FullName -eq $DEST.FullName
}
Count the number of backslashes in the path and add logic to select first level only perhaps. Something like this perhaps?
$Dirs=get-childitem $Source -Recurse | ?{$_.PSIsContainer}
Foreach ($Dir in $Dirs){
$Level=([regex]::Match($Dir.FullName,"'b")).count
if ($Level -eq 1){Copy-Item $Dir $DEST -Force -Verbose}
else{Copy-Item $Dir $DEST -Force}}
*Edited to include looping and logic per requirements
I would suggest using robocopy instead of copy-item. Its /LEV:n switch sounds like it's exactly what you're looking for. Example (you'll need to test & tweak to meet your requirements):
robocopy $source $dest /LEV:2
robocopy has approximately 7 gazillion options you can specify to get some very useful and interesting behavior out of it.