How would I iterate through folders with a .ps1 file? I have
C:\inetpub\wwwroot\apple\login.aspx
C:\inetpub\wwwroot\orange\login.aspx
C:\inetpub\wwwroot\banana\login.aspx
C:\inetpub\wwwroot\pear\login.aspx
I just came across this example:
# PowerShell Foreach File Example
Clear-Host
$Path = "C:\Windows\System32\*.dll"
Get-ChildItem $Path | Foreach-Object {
Write-Host $_.Name
}
So I would do:
# PowerShell Foreach File Example
Clear-Host
$Path = "C:\Windows\System32\*.dll" //here is where I'm not sure of what to do
Get-ChildItem $Path | Foreach-Object {
//insert stuff
}
In my "not sure of what to do" comment how would I change it so it would loop through different sub-directories of windows?
I'm 90% sure that the Recurse parameter is available on Powershell 1. So
ls c:\windows\system32 -Recurse -Include *.dll
ls and dir are both aliases for Get-ChildItem
Related
I was asked the following: Iterate through a list of folders, then iterate through a list of subfolders and finally check if there's a file named "can_erase.txt" on each subfolder. If the file exists, I must read it, save a parameter and delete the respective folder (not the main folder, but the subfolder which contains the file).
I started by using a for loop, but the names of the folders are random and I reached a dead end, so I thought I could use a foreach. Can anyone help me?
EDIT: My code is still pretty basic, since I know the names of the parent folders (they're named stream1, stream2, stream3 and stream4) but their subfolders are randomly named.
My current code:
For ($i=1; $i -le 4; $i++)
{
cd "stream$i"
Get-ChildItem -Recurse | ForEach (I don't know which parameters I should use)
{
#check if a certain file exists and read it
#delete folder if the file was present
}
cd ..
}
In this situation, you'll need multiple loops to get the stream folders, get those subfolders, then parse through all the files in the subfolders.
foreach ($folder in (Get-ChildItem -Path 'C:\streamscontainerfolder' -Directory)) {
foreach ($subFolder in (Get-ChildItem -Path $folder -Directory)) {
if ('filename' -in (Get-ChildItem -Path $subFolder -File).Name) {
Remove-Item -Path $subFolder -Recurse -Force
continue
}
}
}
The alternative to this is using the pipeline:
# This gets stream1, stream2, etc. added a filter to be safe in a situation where
# stream folders aren't the only folders in that directory
Get-ChildItem -Path C:\streamsContainerFolder -Directory -Filter stream* |
# This grabs subfolders from the previous command
Get-ChildItem -Directory |
# Finally we parse the subfolders for the file you're detecting
Where-Object { (Get-ChildItem -Path $_.FullName -File).Name -contains 'can_erase.txt' } |
ForEach-Object {
Get-Content -Path "$($_.FullName)\can_erase.txt" |
Stop-Process -Id { [int32]$_ } -Force # implicit foreach
Remove-Item -Path $_.FullName -Recurse -Force
}
As a default, I'd recommend using -WhatIf as a parameter to Remove-Item so you can see what it would do.
Bonus after more thinking:
$foldersToDelete = Get-ChildItem -Path C:\Streams -Directory | Get-ChildItem -Directory |
Where-Object { (Get-ChildItem -Path $_.FullName -File).Name -contains 'can_erase.txt' }
foreach ($folder in $foldersToDelete) {
# do what you need to do
}
Documentation:
Get-ChildItem
ForEach-Object
Where-Object
Remove-Item
about_Continue
about_ForEach
about_Comparison_Operators
System.IO.FileSystemInfo
I have a list of files say...
T123_Product_1.jpg
T123_Product_2.jpg
T123_Product_3.jpg
T456_Product_1.jpg
T456_Product_2.jpg
T456_Product_3.jpg
etc. etc. etc. for about 900 more files
What I am needing to do is create a folder based on the characters before the first underscore, but to not repeat it since there are multiple files.
So in the example above, I would only want two folders named T123 and T456.
Then I would need the script to place the appropriate files in the folder.
I had found some codes in this thread, but they don't exactly do what I'm looking for.
https://superuser.com/questions/306890/windows-batch-script-to-create-folder-for-each-file-in-a-directory-name-it-tha
$Files = Get-ChildItem -Path 'C:\Info\AUGUST 2011\Checklists\' -Filter 'DET1__*'
$Files | ForEach-Object {
$FileFullName = $_.FullName
$TempFileName = "$($FileFullName).tmp"
$DestinationFileName = "$FileFullName\$($_.Name)"
Move-Item $FileFullName $TempFileName
New-Item -Path $FileFullName -ItemType Directory
Move-Item $TempFileName $DestinationFileName
}
Any help?
The easiest way here would be to group the files by the first part, which will then become the directory name. In typical PowerShell pipeline manner this is fairly succinct:
Get-ChildItem -File | # Get files
Group-Object { $_.Name -replace '_.*' } | # Group by part before first underscore
ForEach-Object {
# Create directory
$dir = New-Item -Type Directory -Name $_.Name
# Move files there
$_.Group | Move-Item -Destination $dir
}
Also try.
cd <path to your folder>
$files = Get-ChildItem -file;
ForEach ($file in $files)
{
$folder = New-Item -type directory -name ($file.BaseName -replace "_.*");
Move-Item $file.FullName $folder.FullName;
}
You can use the Substring method on the $file.BaseName as well.
cd <path to your folder>
$files = Get-ChildItem -file;
ForEach ($file in $files)
{
$fileName = $file.BaseName;
$folder = New-Item -type directory -name $fileName.Substring(0, $fileName.Length-10);
Move-Item $file.FullName $folder.FullName;
}
The same posted here with explanation.
$directory="c:\temp\"
#explicit and long version
Get-ChildItem -File -Path $directory -Filter "*.jpg" |
ForEach-Object {
New-Item -ItemType Directory "$directory$($_.Name.Split("_")[0])" -Force;
Move-Item -Path $_.FullName -Destination "$directory$($_.Name.Split("_")[0])\$($_.Name)"
}
#short version
gci -File -Path $directory -Fi "*.jpg" |
%{ ni -ItemType Directory "$directory$($_.Name.Split("_")[0])" -Force;mvi $_.FullName "$directory$($_.Name.Split("_")[0])\$($_.Name)" }
I have a directory full of subdirectories that have any number of 0 to 300 files in them.
I want to output the subdirectory name and the number of files in that subdirectory
What I have so far is giving me 0 no matter how many actual files are in the subdirectory.
$dir = "C:\Folder\"
$subFiles = (Get-ChildItem $dir -recurse | where-object {$_.PSIsContainer -eq $true })
$subFiles | % {
Get-ChildItem -Path $_ -Force -Recurse | Measure-Object | Select-Object -ExpandProperty Count
write-host "$_"
}
Its also sometimes including the directories in which the script is being run ie "C:\Users\Blah\documents and settings\startmen" and causing errors.
Any help greatly appreciated thank you
You are using at least PowerShell 3.0 since you are using the -File parameter of Get-ChildItem so you dont need to use the where-object {$_.PSIsContainer -eq $true }. That has been replaced with the -Directory parameter. Loop through all the folders and collect the folder name and count of its files. I removed the -Recurse of the file count since that could be misleading. Put it back if it suits you better. The final Select-Object is to ensure the order of the output which is an object now that you could sort or do whatever you wanted with.
$dir = "C:\File"
Get-ChildItem $dir -Recurse -Directory | ForEach-Object{
[pscustomobject]#{
Folder = $_.FullName
Count = #(Get-ChildItem -Path $_.Fullname -File).Count
}
} | Select-Object Folder,Count
Insight
You were getting those errors before since you were not calling the full path in Get-ChildItem you were just calling the folder name. In the absence of a full path Get-ChildItem assumes you are looking for a folder in the current directory. That which is typically your user directory.
This method for count seems to be faster for remote directories.
$count = [System.IO.Directory]::GetFiles($_.Fullname).Count
This did exactly what I wanted :) Its not very pretty, but it worked..
$dir = "C:\folder\"
$subFiles = (Get-ChildItem $dir -recurse | where-object {$_.PSIsContainer -eq $true })
$subFiles | % {
$path = $dir+$_
$files = Get-ChildItem -Path $path -Force -Recurse -File -ErrorAction SilentlyContinue
write-host "$_ Files:" $files.count
}
I would like to use this option to delete those files with a specific extension (.cfg in my case) with different name of its folder. For example, if I have in a folder called MYFOLDER and inside this two files, MYFOLDER.cfg and otherfile.cfg, otherfile.cfg should be deleted (recursively).
That's all I have till now:
$folder = Get-ChildItem * -exclude *.* -name -recurse
$file = Get-ChildItem * -include *.cfg* -name -recurse | % { [IO.Path]::GetFileNameWithoutExtension($_) }
#$folder | ForEach-Object {Compare-Object $folder $file | <DELETE file with different name>}
How Could I make the last line?
Thanks in advance. Regards.
This will do it for you:
Get-ChildItem -Recurse -Include *.cfg | % { If ( $_.Name.Split(".")[0] -ne $_.Directory.Name ) {$_.Delete()}}
Two-Liner, but you can make it to a Single liner ;)
$FolderName = (Get-Location).Path.Split("\")[(Get-Location).Path.Split("\").Count -1]
$Files = Get-ChildItem | Where-Object {$_.Extension -ne ".cfg" -or $_.Name.Split(".")[0] -ne $FolderName} | Remove-Item -Recurse
But this Code and also the one from Musaab Al-Okaidi doesn't handle the case, when the Name of the file contains a dot (e.g. File.Name.cfg)
Remove -WhatIf to delete the files:
Get-ChildItem -Filter *.cfg -Recurse |
Where-Object {$_.BaseName -ne $_.Directory.Name} |
Remove-Item -WhatIf
I'm new to powershell. I'm trying to convert some of our database scripts to powershell scripts. One thing we do in our script currently (DOS BATCH FILE) is use the Type command...
#ECHO OFF
DEL *.sql 1>NUL 2>&1
TYPE ..\db\database\TuscanyProfileDB.sql > CreateDB.sql
TYPE ..\db\tbls\*.sql > CreateTables.sql
TYPE ..\db\foreignKeys\*.sql > CreateForeignKeys.sql
TYPE ..\db\indexes\*.sql > CreateIndexes.sql
TYPE ..\db\sprocs\*.sql > CreateSprocs.sql
It basically goes into the specified folder, and concatenates all the files with the .sql file extension and combines them into a new file.
My question is how can I do this in powershell?
Remove-Item *.sql
Get-Content ..\db\database\TuscanyProfileDB.sql | Add-Content CreateDB.sql
Get-Content ..\db\tbls\*.sql | Add-Content CreateTables.sql
Get-Content ..\db\foreignKeys\*.sql | Add-Content CreateForeignKeys.sql
Get-Content ..\db\indexes\*.sql | Add-Content CreateIndexes.sql
Get-Content ..\db\sprocs\*.sql | Add-Content CreateSprocs.sql
Here's what I use, adapted from here.
In my case I tend to have many directories, each one has many SQL files and I want to create a separate merged SQL file for each directory. I only want to merge the files that are prefixed with a number (e.g. 001_blah.sql) and my merged file gets named upgrade_{DIRECTORY}.sql (e.g. merged_1370_to_1380.sql).
I put my merge_sql_files.ps1 file in the parent directory and I put a merge.bat file in each of the directories with the SQL files.
merge.bat file looks like this
#ECHO OFF
SET PsFile=merge_sql_files.ps1
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%..\%PsFile%
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'";
merge_sql_files.ps1 looks like this:
# Script to merge SQL files together.
# Takes all the SQL files that start with a number and concats
# them together, putting a line with "GO" in betweeen each one.
# The output is saved as upgrade_[DirectoryName].sql, e.g.
# 'upgrade_1370_to_1380.sql'.
#
# This script should be run from the folder with the sql files,
# e.g. put a merge.bat file in the folder that runs this ps1 script.
$path = (Resolve-Path .\).Path
$dirName = (get-item $path).Name
$outFile = "${path}\upgrade_${dirName}.sql"
if((Test-Path $outFile) -eq $true) {Remove-Item -Path $outFile -Force}
# Get all the SQL files that start with a number. Sort them
$files = Get-ChildItem -LiteralPath $path -Include "*.sql" | where {$_.Name -match "^[0-9]" } | Sort-Object -Property Name
New-Item -ItemType file -Path $outFile -Force | Out-Null
foreach($file in $files)
{
Write-Host "Appending file $file..." -ForegroundColor Gray
$content = Get-Content -Path $file.FullName
Add-Content -Path $outFile "----------------------------------------------------------------------------------------------------------------------------------------------------------------"
Add-Content -Path $outFile "-- $File"
Add-Content -Path $outFile "----------------------------------------------------------------------------------------------------------------------------------------------------------------"
Add-Content -Path $outFile $content
Add-Content -Path $outFile "GO`r`n"
}
Write-Host "Completed file $outFile" -ForegroundColor DarkGreen
# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost")
{
Write-Host "Press any key to continue..."
$Host.UI.RawUI.FlushInputBuffer() # Make sure buffered input doesn't "press a key" and skip the ReadKey().
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}