PowerShell: how to copy only selected files from source directory? - powershell

I'm a Powershell newbie, trying to get a simple script to run.
I have a list of files that I want to copy from some src_dir to a dst_dir. I wrote a simple script (which is obviously wrong since it didnt do anything when I executed it).
Could someone please help check to see what I'm doing wrong?
# source and destionation directory
$src_dir = "C:\Users\Pac\Desktop\C4new"
$dst_dir = "C:\Users\Pac\Desktop\csci578-hw3\Prism\C4new"
# list of files from source directory that I want to copy to destination folder
# unconditionally
$file_list = "C4newArchitecture.java", "CustomerData.java"
# Copy each file unconditionally (regardless of whether or not the file is there
for($i=0; $i -le $file_list.Length - 1; $i++)
{
Copy-Item -path $src_dir+$file_list[$i] -dest $dst_dir -force
}

Assuming that the files are directly unde $src_dir you can do this a bit more simply ie the copy can be a one-liner:
$file_list | Copy-Item -Path {Join-Path $src_dir $_} -Dest $dst_dir -ea 0 -Whatif
-ea is the alias for the -ErrorAction parameter and 0 value corresponds to SilentlyContinue. This causes the Copy-Item to ignore errors like you would get if one of the source files didn't exist. However, if you are running into problems temporarily remove this parameter so you can see the error messages.
When typing this stuff interactively I tend to use shortcuts like this but in scripts it is better to spell it out for readability. Also notice that the -Path parameter can take a scriptblock i.e. script in curly braces. Technically the Copy-Item cmdlet doesn't ever see the scriptblock, just the results of its execution. This works in general for any parameter that takes pipeline input. Remove the -WhatIf to have the command actually execute.

Oh..that was actually pretty easy:
# source and destionation directory
$src_dir = "C:\Users\Pac\Desktop\C4new\"
$dst_dir = "C:\Users\Pac\Desktop\csci578-hw3\Prism\C4new"
# list of files from source directory that I want to copy to destination folder
# unconditionally
$file_list = "C4newArchitecture.java",
"CustomerData.java",
"QuickLocalTransState.java",
"QuickLocalTransState_AbstractImplementation.java",
"SaveSessionOK.java",
"SessionID.java",
"UserInterface.java",
"UserInterface_AbstractImplementation.java"
# Copy each file unconditionally (regardless of whether or not the file is there
foreach ($file in $file_list)
{
Copy-Item $src_dir$file $dst_dir
}
As a programmer, I love the foreach!

Related

Powershell Script: Search for BATs with specific name and run them

back with another request to try and make my life a little easier. The problem: one of the programs I use deposits BMPs (yes, bitmaps, this is an ancient app, and no, I can't configure it not to make BMPs) where I don't need them. I've got a BAT file that can sweep a folder and remove them, but what I'd really like to do is put a copy of said BAT file in each folder where it leaves them, and then every time I run a backup cycle, have it search for those BAT files, and wherever it finds one, run it. (I'd also need to know how to tell it "look in the same folder you're in"--I think I can do that by something like $searchfolder = "." but please correct me if I'm wrong)
I'm guessing this is a Get-Childitem and ForEach, but I've taken a few stabs at it and it won't work. Does anyone have an idea how to go about it?
This is what I've got so far for the parent script to find all instances of "Clear_BMPs.bat":
Get-ChildItem $sourceDir -Include Clear_BMPs.bat -Recurse | ForEach-Object { call "Clear_BMPs.bat" }
And this is what I've got in the child script, to get rid of the BMPs themselves (the filename for it is "Clear_BMPs.bat":
$searchfile = "*.bmp"
$targetdir = ".\"
Get-ChildItem $targetdir -Include $searchfile | foreach{ "Removing file $($_.FullName)"; Remove-Item -force $_}
I'm still trying to get the Clear_BMPs.bat files to work properly but in my vision it will only search the root of the folder it's in, and not recurse through subdirectories.
Since you're calling from PowerShell, there's no reason to involve batch files, given that the code is under your control.
Indeed, what you show as the content of a Clear_BMPs.bat batch file is PowerShell code, which means you need to store it in a .ps1 file, not a .bat file.
Therefore, your recursive invocation that executes all .ps1 files should look like this:
# Find all 'Clear_BMPs.ps1' scripts in the subdir. tree of $sourceDir
# and invoke them.
Get-ChildItem -Recurse -LiteralPath $sourceDir -Filter Clear_BMPs.ps1 |
ForEach-Object { & $_.FullName }
And the Clear_BMPs.ps1 files in the various directories should contain:
# Remove all *.bmp files from the same dir. in which this .ps1 script is located.
Remove-Item -Path "$PSScriptRoot/*.bmp"
Note the use of the automatic $PSScriptRoot variable, which refers to the directory in which the enclosing .ps1 file is located.

Using a filename for a variable and move similar files with Powershell

I am trying to write a powershell script that will look at a directory(c:\Temp) and look for files with exensions of .fin.
If it finds a file with the fin extension I need it to move all files that have the same first seven characters of that fin file to another directory(c:\Temp\fin).
There could be multiple .fin files at a time in that directory with files that need to be moved.
I have tried a few different things but I am new to anything like this. I have only used very basic powershell scripts or commands. While this might be basic(not sure) I am lost as to where to start.
You'll need to call Get-ChildItem twice. The first time to get the pattern you want to match the file names to and the second to get the files. You can then pipe the results of the second command to Move-Item. Here's an example of how to do that:
[CmdletBinding()]
param(
$DirectoryToScan,
$ExtensionToMatch = ".fin",
$TargetDirectory
)
$Files = Get-ChildItem -Path $DirectoryToScan -File
foreach ($File in $Files) {
if($File.Extension -eq $ExtensionToMatch) {
$PatternToMatch = "$($File.BaseName.Substring(0, 7))*$ExtensionToMatch"
Write-Verbose "PatternToMatch: $PatternToMatch"
$MatchedFiles = Get-ChildItem -Path $DirectoryToScan -Filter $PatternToMatch
}
$MatchedFiles | Move-Item -Destination $TargetDirectory
}
If you save the above into a file called Move-MatchingFiles.ps1 you can pass in your parameters Moving-MatchingFiles.ps1 -DirectoryToScan C:\Temp -TargetDirectory C:\Temp\fin. The ExtensionToMatch parameter is optional and only needed if you wanted to move a different type of file.

Run powershell script for each folder with subfolders

I have a few scripts that I need to run against a dozen folders all with a relative path. I'm trying to solve this with a master script to run for each folder in that path, one folder at a time. The folders are all children of the "here" folder in the below path. I can't seem to get the syntax right, but I think I'm close :)
Is there a more efficient way to run a script against the contents of every folder in a directory, one folder at a time?
$pdfFolder = 'C:\path\to\folders\here'
$Completed = Get-ChildItem $pdfFolder -Recurse
ForEach-Object ($Completed){
Invoke-Expression -Command "C:\path\where\scriptis\script.ps1"
}`
$pdfFolder = 'C:\path\to\folders\here'
# Get all subfolders - note the -Directory switch (PSv3+)
$Completed = Get-ChildItem $pdfFolder -Recurse -Directory
# Pipe the subfolders to ForEach-Object, invoke the
# script with & (avoid Invoke-Expression), and pass the subfolder
# at hand as an argument.
$Completed | ForEach-Object {
& "C:\path\where\scriptis\script.ps1" $_
}
As for what you tried:
Get-ChildItem $pdfFolder -Recurse
This command returns not just folders (directories), but also files. To limit the output to folders, pass switch -Directory (PSv3+).
ForEach-Object ($Completed) { ... }
You're confusing the syntax of the foreach loop with the syntax of the pipeline-based ForEach-Object cmdlet.
The cmdlet expects input from the pipeline, so you must use $Completed | ForEach-Object { ... } instead.
Also note that unless you truly need to collect all subfolders in an array first, you can simply pipe your Get-ChildItem call directly to ForEach-Object.
Invoke-Expression -Command "C:\path\where\scriptis\script.ps1"
Invoke-Expression should be avoided, because it is rarely the right tool and can be a security risk.
All you need to invoke a script by its quoted and/or stored-in-a-variable file path is to use &, the call operator, as shown above.

How can I use spaces in a fully qualified pathname in Powershell?

I have a script that copies a number of files from different sources to a single directory for backup. The only step of the script the errors out has a space in both the path and file names: \\server\Network Shares\Transfer\tu3\tu3 Code.mdb
I get the error copy-item : Cannot find path '\\server\Network Shares\Transfer\tu3\tu3 Code.mdb' because it does not exist. and I'm assuming it's because of the spaces in either the path or filename. Does PowerShell allow spaces in a fully qualified path? If not, how can I get at the file?
Here's the relevant code (My$Destis defined as a global variable for the script):
$TU3CodeUpdatedPathname = "\\server\Network Shares\Transfer\tu3\"
$TU3CodeUpdatedFilename = "tu3 Code.mdb"
$TU3CodeUpdated = $TU3CodeUpdatedPathname + $TU3CodeUpdatedFilename
#
$Source = $TU3CodeUpdated
$Dest = $VMShareSpacePathname
#
copy-item $Source $Dest
Try being more explicit, and wrap the parameter values in quotes. Adding -Verbose might help with debugging. If it's complaining the file doesn't exist, maybe double check that the file is indeed accessible when your script runs under the account, if it's not the same as your user account.
Copy-Item -Path "$Source" -Destination "$Dest"
Just to ensure, you might have mixed up the variable names TU3/HS3?
$TU3CodeUpdatedPathname = "\\server\Network Shares\Transfer\tu3\"
$TU3CodeUpdatedFilename = "tu3 Code.mdb"
$TU3CodeUpdated = Join-Path -Path $TU3CodeUpdatedPathname -ChildPath $TU3CodeUpdatedFilename
Otherwise I can't see anything wrong with your code.
Spaces are just fine within quotes as you did write it.
I would guess the running user from the script does not have access rights to the file/share.
This post might help in that case.
This worked for me to copy folder with space in its name. I am using powershell 4.0
$Source = "D:\test\Test cases"
$Dest = "D:\bck\Test cases"
Copy-Item -Path "$Source" "$Dest" -Recurse

Delete directory and their contents and log to custom function

Assume the following directory tree:
NATIVES
\VOL001
file1.txt
file2.txt
\VOL002
file3.txt
\OTHERDIR
file4.txt
I am trying to delete all folder VOL* folders and their children (only files for now) and log the results. Removing the folders and files is easy:
# Remove textfiles
Remove-Item -path C:\Sandbox\NATIVES\VOL* -Recurse
But I am unable to log this as-is. I am using a custom log Function:
Function Log-Write
{
Param([string]$logstring)
Add-Content "log.txt" -value $logstring
}
How can I use it to fill log.txt with every file and folder that has been deleted? So for instance:
Delete file C:\Sandbox\NATIVES\VOL001\file1.txt
Delete file C:\Sandbox\NATIVES\VOL001\file2.txt
Delete dir C:\Sandbox\NATIVES\VOL001\
Delete file C:\Sandbox\NATIVES\VOL002\file3.txt
...
I could write a bigger loop, but I am trying to keep the code as concise as possible (it's more of a learning thing).
Can anyone send me in the right direction?
Use foreach (aka %) instead of recursive Remove-Item. It is possible to redirect Remove-Item's -Verbose output to a file, but that requires very cryptic syntax unless you are on Powerhell 3.0 or higher.
The foreach way looks like so,
$files = #(gci -recurse c:\path\to\files)
$files | % {
rm $_.FullName
add-content c:\myLogFile.txt "Delete file $_.FullName"
}