How to copy files in powershell that contain variable in name? - powershell

I am trying to copy files that match an environment identifier set to a variable. The output of the command looks good until in include the where-object section. I only want to copy files that contain the string in the $environmenttype string.
What do I need to change to get the where-object to operate correctly?
$environmenttype = "dev"
Write-Output "environmenttype is set to $environmenttype"
Get-ChildItem -path "\content" | Select-Object -ExpandProperty name | Where-Object {$_ -contains "$environmenttype"} | Copy-Item -Path "C:\newdir"

How I'd write it:
Get-ChildItem "\content" -File | where BaseName -like "*$environmenttype*" | Copy-Item "C:\newdir"
I am using the simplified version of Where-Object and the -like operator/parameter and a wildcard pattern (note the asterisks *).
BaseName is the name of the file without extension.
I omitted some implicit parameter names, and added the -File switch to include files only.
(Note that the path in Copy-Item is set by the pipeline, so the parameter is actually -Destination)

You can use match predicate, which supports variable usage.
$filter = "ansi"
Get-ChildItem -path "c:\dev\" | Where-Object {$_.Name -match "$filter"} | Copy-Item -Destination C:\dev\testFolder

Related

Using Powershell, how to return a list of files based on the existence of duplicate files with a different naming convention?

There are multiple .webp files in a project folder. Some .webps are the original picture and some function as thumbnail (their size is different). The used naming convention is: original files are just called NAME.webp and tumbnails are NAME-thumb.webp.
I am trying to return all .webp files based on if the corresponding thumb-webp exists. So if picture SAMPLE.webp has a SAMPLE-thumb.webp, don't add this file to the list. But if SAMPLE.webp doesn't have a corresponding SAMPLE-thumb.webp, then do at it to the list.
This is what i've tried so far:
$example = Get-ChildItem -File $dir\*.webp |
Group-Object { $_.BaseName } |
Where-Object { $_.Name -NotContains "-thumb" } |
ForEach-Object Group
You can get this without the grouping with a Where-Object and testing paths.
Get-ChildItem -File $dir\*.webp |
Where-Object {$_.Name -notmatch "-thumb" -and -not(Test-Path ($_.FullName -replace ".webp","-thumb.webp"))}
This should get you a list of all the files that do not have a corresponding thumbnail file.
You can do the following:
(Get-ChildItem $dir\*.webp -File |
Group-Object {$_.BaseName -replace '-thumb$'} |
Where Count -eq 1).Group
You must have a commonality with grouping. Replacing the ending -thumb in the BaseName property creates that. If there is no filename and filename-thumb the resulting GroupInfo will have a count value of 1.
Using the syntax ().Group returns all file objects. If you want to process code against each file, you may use Foreach-Object instead:
Get-ChildItem $dir\*.webp -File |
Group-Object {$_.BaseName -replace '-thumb$'} |
Where Count -eq 1 | Foreach-Object {
$_.Group
}

Copy files listed in a txt document, keeping multiple files of the same name in PowerShell

I have a bunch of lists of documents generated in powershell using this command:
Get-ChildItem -Recurse |
Select-String -Pattern "acrn164524" |
group Path |
select Name > test.txt
In this example it generates a list of files containing the string acrn164524 the output looks like this:
Name
----
C:\data\logo.eps
C:\data\invoice.docx
C:\data\special.docx
InputStream
C:\datanew\special.docx
I have been using
Get-Content "test.txt" | ForEach-Object {
Copy-Item -Path $_ -Destination "c:\destination\" -Recurse -Container -Force
}
However, this is an issue if two or more files have the same name and also throws a bunch of errors for any lines in the file that are not a path.
sorry if I was not clear enough I would like to keep files with the same name by appending something to the end of the file name.
You seem to want the files, not the output of Select-String. So let's keep the files.
Get-ChildItem -Recurse -File | Where-Object {
$_ | Select-String acrn164524 -Quiet
} | Select-Object -ExpandProperty FullName | Out-File test.txt
Here
-File will make Get-ChildItem only return actual files. Think
about using a filter like *.txt to reduce the workload more.
-Quiet will make Select-String return $true or $false, which
is perfect for Where-Object.
Instead of Select-Object -ExpandProperty X in order to retrieve an array of raw property values (as opposed to an array of PSObjects, which is what Select-Object would normally do), it's simpler to use ForEach-Object X instead.
Get-ChildItem -Recurse -File | Where-Object {
$_ | Select-String acrn164524 -Quiet
} | ForEach-Object FullName | Out-File test.txt

How to retrieve recursively any files with a specific extensions in PowerShell?

For a specific folder, I need to list all files with extension .js even if nested in subfolders at any level.
The result for the output console should be a list of file names with no extension line by line to be easily copy and pasted in another application.
At the moment I am trying this, but in output console I get several meta information and not a simple list.
Get-ChildItem -Path C:\xx\x-Recurse -File | sort length –Descending
Could you please provide me some hints?
If sorting by Length is not a necessity, you can use the -Name parameter to have Get-ChildItem return just the name, then use [System.IO.Path]::GetFileNameWithoutExtension() to remove the path and extension:
Get-ChildItem -Path .\ -Filter *.js -Recurse -File -Name| ForEach-Object {
[System.IO.Path]::GetFileNameWithoutExtension($_)
}
If sorting by length is desired, drop the -Name parameter and output the BaseName property of each FileInfo object. You can pipe the output (in both examples) to clip, to copy it into the clipboard:
Get-ChildItem -Path .\ -Filter *.js -Recurse -File| Sort-Object Length -Descending | ForEach-Object {
$_.BaseName
} | clip
If you want the full path, but without the extension, substitute $_.BaseName with:
$_.FullName.Remove($_.FullName.Length - $_.Extension.Length)
The simple option is to use the .Name property of the FileInfo item in the pipeline and then remove the extension:
Get-ChildItem -Path "C:\code\" -Filter *.js -r | % { $_.Name.Replace( ".js","") }
There are two methods for filtering files: globbing using an Wildcard, or using a Regular Expression (Regex).
Warning: The globbing method has the drawback that it also matches files which should not be matched, like *.jsx.
# globbing with Wildcard filter
# the error action prevents the output of errors
# (ie. directory requires admin rights and is inaccessible)
Get-ChildItem -Recurse -Filter '*.js' -ErrorAction 'SilentlyContinue'
# filter by Regex
Where-Object { $_.Name -Match '.*\.js$' }
You then can sort by name or filesize as needed:
# sort the output
Sort-Object -PropertyName 'Length'
Format it a simple list of path and filename:
# format output
Format-List -Property ('Path','Name')
To remove the file extension, you can use an select to map the result:
Select-Item { $_.Name.Replace( ".js", "") }
Putting it all together, there is also a very short version, which you should not use in scripts, because it's hardly readable:
ls -r | ? { $_.Name -matches '.*\.js' } | sort Length | % { $_.Name.Replace( ".js", "") | fl
If you like brevity, you can remove the ForEach-Object and quotes. -Path defaults to the current directory so you can omit it
(Get-ChildItem -Filter *.js -Recurse).BaseName | Sort length -Descending
The above Answers works fine. However in WIndows there is a alias called ls the same as on linux so another shorter command that works too would be ls -Filter *.exe
Use BaseName for the file name without the file extension.
Get-ChildItem -Path ".\*.js" | Sort-Object Length -Descending | ForEach-Object {
$_.BaseName
}
I always used cygwin for this in the past. My last employer locked down our environments and it wasn't available. I like to review the latest files I've modified often. I created the following environment variable named LatestCode to store the script. I then execute it with: iex $env:latest code.
Here is the script: get-childitem “.” -recurse -include *.ts, *.html , *.sass, *.java, *.js, *.css | where-object {$_.mode -notmatch “d”} | sort lastwritetime -descending | Select-Object -First 25 | format-table lastwritetime, fullname -autosize

Powershell : Match file name with folder name

I'm trying to match file name with folder name before move them to an other directory.
For example, my script need to match if "Test.txt" match with a folder named "Test" and move them to a different directory.
Is it possible with the cmdlets Get-ChildItem ? I didn't find any examples :(
Thanks a lot.
PowerShell 3+
Gets all files recursively from the current directory whose names (without extension) matches its directory's name:
Get-ChildItem -Path . -File -Recurse |
Where-Object { $_.BaseName -eq $_.Directory.Name }
PowerShell 1, 2
There is no -File switch until PowerShell 3, so you have to filter out directories with an extra Where-Object.
Get-ChildItem -Path . -Recurse |
Where-Object { -not $_.PsIsContainer } |
Where-Object { $_.BaseName -eq $_.Directory.Name }
Once you've got all the files that match their parent directory name, you should be able to move them. I'm not sure what your logic is for the destination directory structure.
For starters you can use the Directory property of Get-ChildItem
So lets say you are looking for a file test.txt but only if it is in the directory Scripts
Get-ChildItem *.txt -recurse | Where-Object{($_.Name -eq "test.txt") -and ($_.Directory -like "*\scripts")} | Move-Item $_.Directory "C:\NewFolder"
The Where clause will look for a file called text.txt as long as its in a folder called c:\somepath\scripts. So this will not match c:\anotherpath\test.txt. When a match is found Move the located files directory to a new location.
Note I am not sure if the logic will hold if multiple file matches are found. If it fails then we could assign all the matches to a variable and then process the all the unique values.
$foundDirectories = Get-ChildItem *.txt -recurse | Where-Object{($_.Name -eq "test.txt") -and ($_.Directory -like "*\scripts")} | Select-Object -ExpandProperty Directory -Unique
$foundDirectories | ForEach-Object{Move-Item $_ "C:\newfolder"}

How do I get only directories using Get-ChildItem?

I'm using PowerShell 2.0 and I want to pipe out all the subdirectories of a certain path. The following command outputs all files and directories, but I can't figure out how to filter out the files.
Get-ChildItem c:\mypath -Recurse
I've tried using $_.Attributes to get the attributes but then I don't know how to construct a literal instance of System.IO.FileAttributes to compare it to. In cmd.exe it would be
dir /b /ad /s
For PowerShell 3.0 and greater:
Get-ChildItem -Directory
You can also use the aliases dir, ls, and gci
For PowerShell versions less than 3.0:
The FileInfo object returned by Get-ChildItem has a "base" property, PSIsContainer. You want to select only those items.
Get-ChildItem -Recurse | ?{ $_.PSIsContainer }
If you want the raw string names of the directories, you can do
Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | Select-Object FullName
In PowerShell 3.0, it is simpler:
Get-ChildItem -Directory #List only directories
Get-ChildItem -File #List only files
Use
Get-ChildItem -dir #lists only directories
Get-ChildItem -file #lists only files
If you prefer aliases, use
ls -dir #lists only directories
ls -file #lists only files
or
dir -dir #lists only directories
dir -file #lists only files
To recurse subdirectories as well, add -r option.
ls -dir -r #lists only directories recursively
ls -file -r #lists only files recursively
Tested on PowerShell 4.0, PowerShell 5.0 (Windows 10), PowerShell Core 6.0 (Windows 10, Mac, and Linux), and PowerShell 7.0 (Windows 10, Mac, and Linux).
Note: On PowerShell Core, symlinks are not followed when you specify the -r switch. To follow symlinks, specify the -FollowSymlink switch with -r.
Note 2: PowerShell is now cross-platform, since version 6.0. The cross-platform version was originally called PowerShell Core, but the the word "Core" has been dropped since PowerShell 7.0+.
Get-ChildItem documentation: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem
A cleaner approach:
Get-ChildItem "<name_of_directory>" | where {$_.Attributes -match'Directory'}
I wonder if PowerShell 3.0 has a switch that only returns directories; it seems like a logical thing to add.
Use:
dir -r | where { $_ -is [System.IO.DirectoryInfo] }
From PowerShell v2 and newer (k represents the folder you are beginning your search at):
Get-ChildItem $Path -attributes D -Recurse
If you just want folder names only, and nothing else, use this:
Get-ChildItem $Path -Name -attributes D -Recurse
If you are looking for a specific folder, you could use the following. In this case, I am looking for a folder called myFolder:
Get-ChildItem $Path -attributes D -Recurse -include "myFolder"
Less text is required with this approach:
ls -r | ? {$_.mode -match "d"}
The accepted answer mentions
Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | Select-Object FullName
to get a "raw string".
But in fact objects of type Selected.System.IO.DirectoryInfo will be returned. For raw strings the following can be used:
Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | % { $_.FullName }
The difference matters if the value is concatenated to a string:
with Select-Object suprisingly foo\#{FullName=bar}
with the ForEach-operator the expected: foo\bar
Use:
dir -Directory -Recurse | Select FullName
This will give you an output of the root structure with the folder name for directories only.
You'll want to use Get-ChildItem to recursively get all folders and files first. And then pipe that output into a Where-Object clause which only take the files.
# one of several ways to identify a file is using GetType() which
# will return "FileInfo" or "DirectoryInfo"
$files = Get-ChildItem E:\ -Recurse | Where-Object {$_.GetType().Name -eq "FileInfo"} ;
foreach ($file in $files) {
echo $file.FullName ;
}
Use:
Get-ChildItem \\myserver\myshare\myshare\ -Directory | Select-Object -Property name | convertto-csv -NoTypeInformation | Out-File c:\temp\mydirectorylist.csv
Which does the following
Get a list of directories in the target location:
Get-ChildItem \\myserver\myshare\myshare\ -Directory
Extract only the name of the directories:
Select-Object -Property name
Convert the output to CSV format:
convertto-csv -NoTypeInformation
Save the result to a file:
Out-File c:\temp\mydirectorylist.csv
A bit more readable and simple approach could be achieved with the script below:
$Directory = "./"
Get-ChildItem $Directory -Recurse | % {
if ($_.Attributes -eq "Directory") {
Write-Host $_.FullName
}
}
Hope this helps!
My solution is based on the TechNet article Fun Things You Can Do With the Get-ChildItem Cmdlet.
Get-ChildItem C:\foo | Where-Object {$_.mode -match "d"}
I used it in my script, and it works well.
This question is well and truly answered but thought I'd add something extra as I've just been looking at this.
Get-ChildItem happens to produce two types of objects whereas most commands produce just one.
FileInfo and DirectoryInfo are returned.
You can see this by viewing the 'members' available to this command like so:
Get-ChildItem | Get-Member
TypeName: System.IO.DirectoryInfo
TypeName: System.IO.FileInfo
You'll see the various methods and properties available to each type. Note that there are differences. For example that the FileInfo object has a length property but the DirectoryInfo object doesn't.
Anyway, technically, we can return just the directories by isolating the DirectoryInfo object
Get-ChildItem | Where-Object {$_.GetType().Name -eq "DirectoryInfo"}
Obviously as the top answer states the most straightforward solution is to simply use Get-ChildItem -Directory but we now know how to work with multple object types in future :)
Use this one:
Get-ChildItem -Path \\server\share\folder\ -Recurse -Force | where {$_.Attributes -like '*Directory*'} | Export-Csv -Path C:\Temp\Export.csv -Encoding "Unicode" -Delimiter ";"
You can try the PsIsContainer Object
Get-ChildItem -path C:\mypath -Recurse | where {$_.PsIsContainer -eq $true}
To answer the original question specifically (using IO.FileAttributes):
Get-ChildItem c:\mypath -Recurse | Where-Object {$_.Attributes -band [IO.FileAttributes]::Directory}
I do prefer Marek's solution though:
Where-Object { $_ -is [System.IO.DirectoryInfo] }