Why two similar PowerShell copy-Item commands behave differently - powershell

I have two very similar one line commands but one works and one doesn't with variables.
Copy-Item "C:\BadSourceCodes\dropTest\base1\*" -Include "myUsers.Config","myUsers.WSF" -Destination "C:\BadSourceCodes\dropTest\temp1"
This just works I have two files and I want them to be copied over to destination folder.
If I create something like this
$files = "myUsers.Config,myUsers.WSF"
$tempFiles = [string]::Concat("`"",$files.Replace(",","`",`""),"`"")
$tempFiles
Copy-Item "C:\BadSourceCodes\dropTest\base1\*" -Include $tempFiles -Destination "C:\BadSourceCodes\dropTest\temp1" -Force
As soon as I use a variable for Include files it doesn't work. I have checked with different variations and it doesn't work.
Is the $ variable behaves differently?
I tried following variations like $($tempFiles), '$tempFiles', "$tempFiles".....
UPDATE:
I think when I am escaping the quotes and replacing , with "," then it treats quote as a part of the string. Some how it was the bad approach I took. But what worked is just $files.Split(",") which creates an array of string and Done.

The -Include argument wants an array of strings, not a list of names in a single string. Put the names in an actual array and it should work:
#create an array of strings
$files = "myUsers.Config","myUsers.WSF"
Copy-Item "C:\BadSourceCodes\dropTest\base1\*" -Include $files -Destination "C:\BadSourceCodes\dropTest\temp1" -Force

Related

Powershell: Define set local variable as array

I have a question. I have a setup where I have a parent BAT file that invokes some subroutines I've built, and I'm trying to figure out how to get something particular to work. The short version, in my main BAT file I have this:
#echo off & setlocal
set "deleteThese = '*.PDF', '*.BMP', '*.AVI', '*.MOV', '*.PS1'
call "Subroutines\Find Folders and Remove Files of Extension.bat"
And what I'm looking for is when it invokes the second BAT file, I want it to pass the array to the environment variable. I thought I had it set up the right way to do that:
$deleteThese=$Env:deleteThese
But it's not working quite right. If I specify the deleteThese variable in the subroutine itself, it works fine; if I try to set it in the parent BAT file, it fails out and doesn't do anything. It doesn't even reach the PAUSE flag at the end of the batch file.
This is the syntax in the subroutine BAT file that actually pulls the values and clears the requested files:
$rootPath=$Env:rootPath
$searchFolder=$Env:searchFolder
$deleteThese=$Env:deleteThese
Get-ChildItem -Path $rootPath -Directory -Filter $searchFolder -Recurse | ForEach-Object {
Get-ChildItem -Path $_.FullName -File -Recurse -Include $deleteThese |
Remove-Item -Verbose
}
What am I missing? (Yes I know I could just loop it with a different file extension each time but that seems like it's overcomplicating things.)
The -Include parameter expects an array of strings, but environment variables can only contain a single string value each - so you'll need to split the value into an array before passing it to Get-ChildItem -Include.
In the calling batch file, set the variable (exactly) like this - no single-quotes, and no spaces around =:
set "deleteThese=*.PDF,*.BMP,*.AVI,*.MOV,*.PS1"
Then, in the PowerShell script, make sure to split it into an array of individual strings:
$deleteThese = $Env:deleteThese.Split(',')
Now Get-ChildItem -Include $deleteThese will work as expected

Run executable in Powershell with specific filenames as arguments

I'm trying to batch-convert heic images to png images using Powershell. What I have tried:
Get-ChildItem -Include ('*.HEIC', '*.heic') -File | & .\bin\vips.exe copy $_.Name "$(_.BaseName).png"
Pause
and
Get-ChildItem -Include ('*.HEIC', '*.heic') -File | & .\bin\vips.exe copy $_.Name ($_.BaseName + '.png')
Pause
Both times I'm getting an error VipsForeignLoad: file ".png" does not exist which tells me it treats ".png" as the first (and only) argument and ignores the object Name and Basename properties.
You're missing a $. "$($_.BaseName).png" I would use -Filter vs -Include as it is more efficient.
Edited: Try this approach bypassing the Pipe and see if you get a different result. I've also added some additional code to insure everything is fully evaluated. If this approach works you can experiment with reducing come of the $() evaluation levels.
Also are you using Linux? Some of my googling led me to believe you might be. If so you should specify this in your tags for clarity.
Clear-Host
$x = Get-ChildItem -Filter "*.HEIC" -File
ForEach ($File in $x) {
& .\bin\vips.exe copy "$($File.FullName)" $("$($File.BaseName)" + ".pdf")
}
Also note the file extension in the Filter is case insensitive so no need to repeat.
I'd also recommend adding the -Path parameter for clarity rather than assuming the default directory but that's just me.
HTH

Powershell Script that recursively searches for specific file and containing directory and copy it to another location

I'm attempting to write a powershell script that will search recursively for a file and copy it to another location on a local drive with the date appended to it.
However that file could be in multiple different directories. (ex. c:\users\default\Bookmark.txt, c:\users\profile1\Bookmark.txt, c:\users\profile2\Bookmark.txt...etc.)
To distinguish between the different directories I was thinking of appending the directory name containing the file to the filename along with the date. (ex. filename-directoryName-date)
Here is what I have so far:
get-childitem -path "$env:userprofile\AppData\Local\Microsoft\Edge\User Data" -filter Bookmarks -Recurse -ErrorAction SilentlyContinue -Force | copy-item -Destination $env:userprofile\Bookmarks-$(get-date -UFormat %d-%m-%Y)
This works if it only finds 1 copy of the Bookmarks file and it only appends the date.
To figure out the names of the containing folders I used this command.
(get-childitem -path "$env:userprofile\AppData\Local\Microsoft\Edge\User Data" -filter Bookmarks -Recurse -ErrorAction SilentlyContinue -Force).Directory.Name
I need to somehow put this two together and so it outputs :
c:(whateverlocation)\Bookmark-DirectoryName-Date
I hope I'm making sense.
Dan,
When you use the Get-ChildItem command (alias gci) it will return to you an array of DirectoryInfo and FileInfo objects, one for each item it finds. If you are looking for files called "Bookmarks" (or bookmark.txt...can't tell from your examples which one you're looking for) then you can use the following command to get you a list of all of them:
[array]$FileList = gci -Recurse -File -Filter "Bookmarks"
The [array] designation is necessary to be sure the object is an array regardless of how many items are returned. The filter can have wildcards if you don't know the exact filename. What that leaves me with is an array object named $FileList containing all of the information about the files. You can read all about the properties and methods available in these objects at this Microsoft help page.
For this task, we'll need the .FullName property, which tells you the full path & name of each item (which can be used as your source) and the .BaseName & .Extension properties, which give you the filename and extension respectively. Using these properties, you can copy each of the files you find to a destination. Putting it all together, you get something like this:
$SourceFolder = "$($env:userprofile)\AppData\Local\Microsoft\Edge\User Data"
$DestFolder = '' #Path to Destination Folder
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "Bookmarks"
ForEach ($F in $FileList) {
Copy-Item $F.FullName (Join-Path $DestFolder ($F.Name + 'stuff to add to end of filename' + $F.Extension))
}
In this case, since the files are named 'Bookmarks', the $F.Extension should be blank. If using a more traditional filename, the pattern above will help you fit your changes in between the filename and the extension.
Hope this helps you out. If it does, please accept the answer using the check mark on the left.

Removing Parts of a File Name based on a Delimiter

I have various file names that are categorized in two different ways. They either just have a code like: "866655" or contain a suffix and prefix "eu_866655_001". My hope is to write to a text file the names of files in the same format. I cannot figure out a successful method for removing the suffix and prefix.
Currently this what I have in my loop in Powershell:
$docs = Get-ChildItem -Path $source | Where-Object {$_.Name -match '.doc*'}
if ($docs.basename -contians 'eu_*')
{
Write-Output ([io.fileinfo]"$doc").basename.split("_")
}
I'm hoping to turn "eu_866655_001" into "866655" by using "_" as the delimiter.
I'm aware that the answer is staring me down but I still can't seem to figure it out.
You could do something like the following. Feel free to tweak the -Filter on the Get-ChildItem command.
$source = 'c:\path\*'
$docs = Get-ChildItem -Path $source -File -Filter "*_*_*" -Include '*.doc','*.docx'
$docs | Rename-Item -NewName { "{0}{1}" -f $_.Basename.Split('_')[1],$_.Extension }
The important things to remember is that in order to use the -Include switch, you need an * at the end of the -Path value.
Explanation:
-Filter allows us to filter on names that contain two underscores separating three substrings.
-Include allows us to only list files ending in extensions .docx and .doc.
Rename-Item -NewName supports delayed script binding. This allows us use a scriptblock to perform any necessary operations for each piped object (each file).
Since the target files will always have two underscores, the .Split('_') method will result in an three index array delimited by the _. You have specified that you always want the second delimited substring and that is represented by index 1 ([1]).
The format operator (-f) puts the substring and extension together, completing the file name.

How to -Include using a variable with multiple criteria

I am trying to copy files with names that fulfills a given set of criteria from one folder to another. From this answer, I found that the -Include statement could be used for this.
My issue is that I need the criteria to be user-specified (through a variable), with the possibility of containing multiple criteria. That is, I try to do the following:
# THIS WORKS FINE
$includeFiles = "*tests.jar"
Copy-Item "from/path" "to/path" -Include $includeFiles
# THIS RETURNS NOTHING
$includeFiles = "*tests.jar, other.jar"
Copy-Item "from/path" "to/path" -Include $includeFiles
How do I work around this issue? Is it somehow possible to "destring" the variable, use another syntax or similar?
# THIS WORKS FINE
$copyPattern = "*tests.jar", "other.jar"
Copy-Item "from/path" "to/path" -Include $copyPattern
The point is that the argument to -Include is an array of strings, each string is a pattern to include. The way you wrote it would require a comma and space in the file name. So, unless you have such files, nothing would be included.
If you are positive that the pattern will never include commas and you'd rather just have a single string, you can of course convert it to an array:
$copyPatterns = "*tests.jar, other.jar" -split ', '
Note that parsing works differently in arguments to commands, which is why the following does work as well:
Copy-Item from/path to/path -Include *tests.jar,other.jar