How I can do a ls using PowerShell?
for i in `ls`
do
if [ -d $i ] #miro si és directori
then
echo "Directory"
else echo "File"
fi
done
POWERSHELL
$llistat -ls
forEach $element in $llistat ??? this is possible
}
A more PoSh way is to use a pipeline, and perhaps a hashtable:
$type = #{
$true = 'Directory'
$false = 'File'
}
Get-ChildItem | ForEach-Object { $type[$_.PSIsContainer] }
PowerShell even has a default alias ls for Get-ChildItem, so you could use more Unix-ish syntax:
ls | % { $type[$_.PSIsContainer] }
In PowerShell, the Get-ChildItem cmdlet works like ls (at least with the file system provider). All items returned have a PowerShell-specific property called PSIsContainer, indicating whether it's a directory or not:
foreach($item in (Get-ChildItem)){
if($item.PSIsContainer){
"Directory"
} else {
"File"
}
}
If you want to see what's inside each directory, one level down:
foreach($item in (Get-ChildItem)){
if($item.PSIsContainer){
# Directory! Let's see what's inside:
Get-ChildItem -Path $item.FullName
}
}
As of PowerShell version 3.0 and up, the Get-ChildItem supports a File and Directory switch on the filesystem provider, so if you ONLY want directories, you could do:
Get-ChildItem -Directory
So the second example becomes:
Get-ChildItem -Directory | Get-ChildItem
You could also list files recursively (like ls -R):
Get-ChildItem -Recurse
Related
I have the following problem and I would really appreciate it if I could get some help on that front. I am getting a constant flow of xml files into a folder. A XML file name can look like this. It only goes up to 1005.
1001.order-asdf1234.xml
1002.order-asdf4321.xml
I want to sort the files into uniquely named folders that are not based on the file names. A example for that would be
C:\Directory Path...\Peter (All files starting with 1001 go in there)
C:\Directory Path...\John (All files starting with 1002 go there)
How can I create a batch or a powershell script to continuously sorts files into the specified folders? Since I only have 5 folders I would like to simply specify the target folders for each and not have elaborate loops but I don't know how to do that.
The easiest way is to create a lookup Hashtable where you define which prefix ('1001' .. '1005') maps to which destination folder:
# create a Hasthable to map the digits to a foldername
$folderMap = #{
'1001' = 'Peter'
'1002' = 'John'
'1003' = 'Lucretia'
'1004' = 'Matilda'
'1005' = 'Henry'
}
# set source and destination paths
$rootFolder = 'X:\Where\the\files\are'
$destination = 'Y:\Where\the\files\should\go'
# loop over the files in the root path
Get-ChildItem -Path $rootFolder -Filter '*.xml' -File |
Where-Object { $_.BaseName -match '^\d{4}\.' } |
ForEach-Object {
$prefix = ($_.Name -split '\.')[0]
$targetPath = Join-Path -Path $destination -ChildPath $folderMap[$prefix]
$_ | Move-Item -Destination $targetPath -WhatIf
}
Remove the -WhatIf safety-switch if you are satisfied with the results shown on screen
You could use a switch statement to decide on the target folder based on the first part of the file name:
$files = Get-ChildItem path\to\folder\with\xml\files -Filter *.xml
switch($files)
{
{$_.Name -like '1001*'} {
$_ |Move-Item -Destination 'C:\path\to\Peter'
}
{$_.Name -like '1002*'} {
$_ |Move-Item -Destination 'C:\path\to\John'
}
{$_.Name -like '1003*'} {
# etc...
}
default {
Write-Warning "No matching destination folder for file '$($_.Name)'"
}
}
If you change your mind about loops, my preference would be to store the mapping in a hashtable and loop over the entries for each file:
$files = Get-ChildItem path\to\folder\with\xml\files -Filter *.xml
$targetFolders = #{
'1001' = 'C:\path\to\Peter'
'1002' = 'C:\path\to\John'
'1003' = 'C:\path\to\Paul'
'1004' = 'C:\path\to\George'
'1005' = 'C:\path\to\Ringo'
}
foreach($file in $files){
$targetFolder = $targetFolders.Keys.Where({$file.Name -like "${_}*"}, 'First')
$file |Move-Item -Destination $targetFolder
}
I am trying a script that could compress and delete folders which is in 'n' sublevel folders.
For example the below script could do the job for 3 sublevel folders.
$path = Read-Host "Enter the path"
$directory = $path +"\*\*\*"
Add-Type -AssemblyName System.IO.Compression.FileSystem
$folders = Get-ChildItem $directory -recurse | Where-Object {$_.PSIsContainer -eq $true} | Select-object -ExpandProperty FullName
foreach ($folder in $folders) {
Write-Verbose "Archiving $archive"
$archive = $folder + '.zip'
[System.IO.Compression.ZipFile]::CreateFromDirectory($folder, $archive, 'Optimal', $True)
Remove-Item $folder -recurse -force -Verbose
}
The script is working fine...My doubt is, how to input the sublevel as a input value?
In the above script I am giving the path as a input...Likewise, I wish to input the sublevel also as a input value.
For example: Enter the level:3 (This should assume the pattern like (bs* bs* bs*)
or 4 (bs* bs* bs* bs*)
Note : I had mentioned \ as bs. Because if I mention the pattern as in script, its not visible in the preview.
Any help?
PowerShell allows you to replicate strings with its * operator:
PS> $numLevels = 3; $path = 'C:\path\to'; $path + ('\*' * $numLevels)
C:\path\to\*\*\*
I have a directory in Windows 10 where all the files have names that are too long for windows to handle.
I want to delete these files.
I have discovered that renaming the folder allows me to reduce the path name enough to delete the folder.
For example Changing "Desktop" and it's subfolders to "1" (so the path is 1/1/1/1/file.filetype)
What I have tried is:
Get-ChildItem $path -Recurse -Filter * | Rename-Item -NewName { $_.name -replace *, '1'} -verbose
However this seems to throw an error on *
Is there an easy way to do this or what can I change?
Edit:
Dir | %{Rename-Item $_ -NewName ("{0}" -f $nr++)}
Works on one level but I am having trouble making that recursive through the child folders
Get-ChildItem $path -Recurse | %{Rename-Item $_ -NewName ("{0}" -f $nr++)}
throws this error among others:
Rename-Item : Cannot rename because item at 'designable.nib' does not exist.
If you know you will not loose anything by renaming all folders, this would help. This does not change filenames. This will recursively call the method to rename the folder until the process is able to find the next number.
NOTE: If you take away -Directory, it will update the filenames as well.
function RenameToLeastNumbers ($item, $number) {
try {
$newName = $item.Name -replace $item.Name, $number
Rename-Item $item.FullName -NewName $newName -ErrorAction Stop
}
catch
{
$number = $number + 1
if ($number -gt 100) {
return
}
RenameToLeastNumbers $item $number
}
}
Get-ChildItem -Directory -Recurse -Path C:\Temp\Dates | % { RenameToLeastNumbers $_ 1 }
All the folders under Dates will be renamed starting with 1. If Dates have three folders, they will be renamed to 1, 2, 3. Same thing will happen at each of their childrens and so on.
what folders look like
dir C:\temp\Dates -Name -Recurse
1
2
3
1\1
1\1\This is a text document.txt
2\1
3\1
I tried this process and did not once see the error for many many directories nested in 100s of folders. (created a test folder with nothing but directories and kept copying them within each).
function RenameFolderAndSubFolders {
param($item, $number)
$subfolders = Get-ChildItem $item.FullName -Directory
foreach ($folder in $subfolders) {
RenameFolderAndSubFolders $folder 1
}
while ($true){
try {
Write-Output "Renaming: $($item.FullName)"
Rename-Item $item.FullName -NewName $number -ErrorAction Stop
return
}
catch {}
$number = $number + 1
}
}
Get-ChildItem -Directory -Path C:\Temp\Dates | % { RenameFolderAndSubFolders -item $_ -number 1 }
Let me know how this works.
I'm working on a script that checks folders in specific directory. For example, I run the script for first time, it generates me a txt file containing folders in the directory.
I need the script to add any new directories that are found to the previously created txt file when the script is run again.
Does anyone have any suggestions how to make that happen?
Here is my code so far:
$LogFolders = Get-ChildItem -Directory mydirectory ;
If (-Not (Test-Path -path "txtfilelocated"))
{
Add-Content txtfilelocated -Value $LogFolders
break;
}else{
$File = Get-Content "txtfilelocatedt"
$File | ForEach-Object {
$_ -match $LogFolders
}
}
$File
something like this?
You can specify what directory to check adding path to get-childitem cmdlet in first line
$a = get-childitem | ? { $_.psiscontainer } | select -expand fullname #for V2.0 and above
$a = get-childitem -Directory | select -expand fullname #for V3.0 and above
if ( test-path .\list.txt )
{
compare-object (gc list.txt) ($a) -PassThru | Add-Content .\list.txt
}
else
{
$a | set-content .\list.txt
}
I have a PowerShell 2.0 script that I use to delete folders that have no files in them:
dir 'P:\path\to\wherever' -recurse | Where-Object { $_.PSIsContainer } | Where-Object { $_.GetFiles().Count -eq 0 } | foreach-object { remove-item $_.fullname -recurse}
However, I noticed that there were a ton of errors when running the script. Namely:
Remove-Item : Directory P:\path\to\wherever cannot be removed because it is not empty.
"WHAT?!" I panicked. They should all be empty! I filter for only empty folders! Apparently that's not quite how the script is working. In this scenario a folder that has only folders as children, but files as grandchildren is considered empty of files:
Folder1 (no files - 1 folder) \ Folder 2 (one file)
In that case, PowerShell sees Folder1 as being empty and tries to delete it. The reason this puzzles me is because if I right-click on Folder1 in Windows Explorer It says that Folder1 has 1 folder and 1 file within it. Whatever is used to calculate the child objects underneath Folder1 from within Explorer allows it to see grandchild objects ad infinitum.
Question:
How can I make my script not consider a folder empty if it has files as grandchildren or beyond?
Here's a recursive function I used in a recent script...
function DeleteEmptyDirectories {
param([string] $root)
[System.IO.Directory]::GetDirectories("$root") |
% {
DeleteEmptyDirectories "$_";
if ([System.IO.Directory]::GetFileSystemEntries("$_").Length -eq 0) {
Write-Output "Removing $_";
Remove-Item -Force "$_";
}
};
}
DeleteEmptyDirectories "P:\Path\to\wherever";
Updating for recursive deletion:
You can use a nested pipeline like below:
dir -recurse | Where {$_.PSIsContainer -and `
#(dir -Lit $_.Fullname -r | Where {!$_.PSIsContainer}).Length -eq 0} |
Remove-Item -recurse -whatif
(from here - How to delete empty subfolders with PowerShell?)
Add a ($_.GetDirectories().Count -eq 0) condition too:
dir path -recurse | Where-Object { $_.PSIsContainer } | Where-Object { ($_.GetFiles().Count -eq 0) -and ($_.GetDirectories().Count -eq 0) } | Remove-Item
Here is a more succinct way of doing this though:
dir path -recurse | where {!#(dir -force $_.fullname)} | rm -whatif
Note that you do not need the Foreach-Object while doing remove item. Also add a -whatif to the Remove-Item to see if it is going to do what you expect it to.
There were some issues in making this script, one of them being using this to check if a folder is empty:
{!$_.PSIsContainer}).Length -eq 0
However, I discovered that empty folders are not sized with 0 but rather NULL. The following is the PowerShell script that I will be using. It is not my own. Rather, it is from PowerShell MVP Richard Siddaway. You can see the thread that this function comes from over at this thread on PowerShell.com.
function remove-emptyfolder {
param ($folder)
foreach ($subfolder in $folder.SubFolders){
$notempty = $false
if (($subfolder.Files | Measure-Object).Count -gt 0){$notempty = $true}
if (($subFolders.SubFolders | Measure-Object).Count -gt 0){$notempty = $true}
if ($subfolder.Size -eq 0 -and !$notempty){
Remove-Item -Path $($subfolder.Path) -Force -WhatIf
}
else {
remove-emptyfolder $subfolder
}
}
}
$path = "c:\test"
$fso = New-Object -ComObject "Scripting.FileSystemObject"
$folder = $fso.GetFolder($path)
remove-emptyfolder $folder
You can use a recursive function for this. I actually have already written one:
cls
$dir = "C:\MyFolder"
Function RecurseDelete()
{
param (
[string]$MyDir
)
IF (!(Get-ChildItem -Recurse $mydir | Where-Object {$_.length -ne $null}))
{
Write-Host "Deleting $mydir"
Remove-Item -Recurse $mydir
}
ELSEIF (Get-ChildItem $mydir | Where-Object {$_.length -eq $null})
{
ForEach ($sub in (Get-ChildItem $mydir | Where-Object {$_.length -eq $null}))
{
Write-Host "Checking $($sub.fullname)"
RecurseDelete $sub.fullname
}
}
ELSE
{
IF (!(Get-ChildItem $mydir))
{
Write-Host "Deleting $mydir"
Remove-Item $mydir
}
}
}
IF (Test-Path $dir) {RecurseDelete $dir}