I want to write a batch script which will make a CSV or JSON file containing all file and folders under E:\data. the informations I need to store are (file or folder name, size, type--file or folder, full location path).
Any help will be appreciated. Thank you.
PowerShell can do that with ease. Have to add a little subexpression to give friendly terms about if it is a folder or file. Otherwise we could get rid of the entire #{l=...} bit and just use PSIsContainer and it would be True/False to state if it is a folder or not.
Get-ChildItem E:\Data -recurse | Select Name,Length,#{l='Type';e={if($_.PSIsContainer){'Folder'}else{'File'}}},FullName | Export-CSV C:\Path\To\Output.csv -NoType
Or, if you want to find out folder sizes of folders (including files in the folder) you could run this:
| Select Name,#{l='Size';e={if($_.psiscontainer){$_.getfilesysteminfos()|measure -Sum -Property length|select -expand sum}else{$_.length}}},#{l='Type';e={if($_.PSIsContainer){'Folder'}else{'File'}}},FullName | Export-CSV C:\Path\To\Output.csv -NoType
Related
So I want to know if any of the folders in a directory have any subfolders or files in them, I tried just looking at the directory in PowerShell but it gave me only mode, last write time, and name. Is there any way of adding to this list to include metadata of the folder like size or number of subfiles/folders all I want to know is if they are empty or not so there may be a simpler way I'm missing.
Thanks for any help you can give!
I see the question is tagged 'windows', so on Windows you could also use a COM object.
$fso = New-Object -ComObject Scripting.FileSystemObject
$folder = $fso.GetFolder($pathToFolder)
$folder will be an object with a bunch of interesting metadata on it, including SubFolders and Files. One of the interesting ones is Size. If Size is zero, there are no files in that directory, or in any nested subdirectories either.
If you just want to know if there are folders/subfolders and/or files then this will work:
$folder="C:\Test"
Get-ChildItem $folder -Recurse | Measure-Object
Output (in my case)
Count : 2
Average :
Sum :
Maximum :
Minimum :
Property :
If you want to see more properties then this might work for you:
Get-ChildItem -Path $folder -Recurse | Format-List *
alternatively you can also select the first x, last x, or even skip items:
Get-ChildItem -Path $folder -Recurse |Select-Object -First 2| Format-List *
*-Recurse will check all folders below
Is there a way to stop Powershell from sorting by default? I need to read in files from a directory and in the order which they are listed in the directory I need them to also be listed in the array (variable). Even when I use -lastwritetime on the get-childitem command, it seems to have no affect. The primary reason why I want to do this is because the files have names that are all the same except each file has a number after it like the following:
document1.doc
document2.doc
document3.doc
.....
document110.doc
The problem is if it's sorted by name, it will sort in this manner:
document1.doc
document10.doc
document111.doc
Which is horribly wrong!
Right now I have this command and it doesn't work:
$filesnames1 = get-childItem -name *.doc -Path c:\fileFolder\test | sort-object -LastWriteTime
You probably want something more along these lines.
$filesnames1 = Get-ChildItem -Path c:\fileFolder\test\*.doc |
Sort-Object -Property LastWriteTime
I don't think either of those two cmdlets have a -LastWriteTime parameter.
If you need only the names from those filesystem objects, you can use ($filesnames1).Name after the code above. There are other ways.
Thanks for responding Mike. What I did is put a "-filter *.pdf" just before -path which gave me the headers. Then I piped in a "select-object -ExpandProperty name" to list it exactly how I needed it to. It was a little trial and error but I did eventually figure it out.
$filesnames1 = Get-ChildItem -filter *.doc -Path c:\fileFolder\test |
Sort-Object -LastWriteTime | -ExpandProperty name
Story:
I have multiple folders with 1000+ files in each that are named similar to each other but are slightly different but they relate to the same content.
For example, in one folder I have files named quite simply "Jobs to do.doc" and in another folder "Jobs to do (UK) (Europe).doc" etc.
This is on Windows 10, not Linux.
Question:
Is there a script to compare each folder's content and rename them based on minimum similarity? So the end result would be to remove all the jargon and have each file in each folder (multiple) the same as one another but STILL remain in the retrospective folder?
*Basically compare multiple folder content to one folders contents and rename them so each file in each folder is named the same?
Example:
D:/Folder1/Name_Of_File1.jpeg
D:/Folder2/Name_Of_File1 (Europe).jpeg
D:/Folder3/Name_of_File1_(Random).jpeg
D:/folder1/another_file.doc
D:/Folder2/another_file_(date_month_year).txt
D:/Folder3/another_file(UK).XML
I have used different file extensions in the above example in hope someone can write a script to ignore file extensions.
I hope this make sense. So either a script to remove the content in brackets and keep the files integrity or rename ALL files across all folders based on minimum similarity.
The problem is its 1000+ files in each folder so want to run it as an automated job.
Thanks in advance.
If the stuff you want to get rid of is always in brackets then you could write a regex like
(.*?)([\s|_|]*\(.*\))
Try something like this
$folder = Get-ChildItem 'C:\TestFolder'
$regex = '(.*?)([\s|_|]*\(.*\))'
foreach ($file in $folder){
if ($file.BaseName -match $regex){
Rename-Item -Path $file.FullName -NewName "$($matches[1])$($file.extension)" -Verbose #-WhatIf
}
}
Regarding consistency you could run a precheck using same regex
#change each filename if it matches regex and store only it's new basename
$folder1 = get-childitem 'D:\T1' | foreach {if ($_.BaseName -match $regex){$matches[1]}else{$_.BaseName}}
$folder2 = get-childitem 'D:\T2' | foreach {if ($_.BaseName -match $regex){$matches[1]}else{$_.BaseName}}
#compare basenames in two folders - if all are the same nothing will be returned
Compare-Object $folder1 $folder2
Maybe you could build with that idea.
I'm looking to thin down how many folders I need to recover after a cryptolocker outbreak at a clients site and started looking into powershell as a good way to do this. What I need to do is recover a folder if it has any file inside with the extension .encrypted.
I can run the below
get-childitem C:\ -recurse -filter “*.encrypted” | %{$_.DirectoryName} | Get-Unique
And get a list of all folders that have .encrypted files in them but what I would like to do is thin down the list for example if we have the below file list and assume * means the folder contains encrypted files.
C:\Folder1
C:\Folder1\Folder2\Folder4*
C:\Folder1\Folder2*
C:\Folder1\Folder3\Folder5*
C:Folder1\Folder3\Folder6\
rather than returning
C:\Folder1\Folder2\Folder4*
C:\Folder1\Folder2*
C:\Folder1\Folder3\Folder5*
I would like it just to return as this would be the optimal recovery option.
C:\Folder1\Folder2*
C:\Folder1\Folder3\Folder5*
I know this is a fairly complex problem so I'm not asking anyone to solve it for me just some pointers in the right direction would be awesome as my brain is fried at the moment and I need to write this fairly quickly.
Here's a simple way to do this that should be pretty efficient:
PS C:\> dir -ad -rec | where { test-path (join-path $_.FullName *.encrypted) }
dir is an alias for get-childitem
where is an alias for where-object
-ad means return directories only
-rec means recurse
test-path returns $true if the path exists (yes, it handles wildcards)
S, we recurse through all folders forwarding the folder object down the pipeline. We get the full name of the folder and append *.encrypted to it. If test-path returns $true for this path, we forward the folder down the pipeline. The folder ends up in the console output.
Now, if you want to get a little fancier, here's a more fleshed out one-liner than will report the folders and the encrypted files count into a csv file named after the machine:
dir -ad -rec | ? { test-path (join-path $_.FullName *.txt) } | % {
[pscustomobject]#{"Path"=$_.fullname;"Count"=(dir (join-path $_ *.txt)).count}} |`
Export-Csv "c:\temp\$(hostname).csv" -NoTypeInformation
(? and % are aliases for where-object and foreach-object respectively)
With a little more effort, you could use a fan-out scan of the entire company assuming powershell remoting is enabled on each target machine and have it return all results to you from all machines.
Good luck!
This is too much for a comment, but I don't know that it would be a good answer, just a kind of hackish way to get it done...
The only thing I could think of is to get your list of folders, then start matching them all against each other, and when you get two that at least partially match remove the longer one.
$FullList = GCI C:\*.encrypted | Select -Expand DirectoryName -Unique | Sort -Property Length
$ToRemove = #()
foreach($Folder in $FullList){$ToRemove+=$FullList| Where{$_ -ne $Folder -and ($_ -match [regex]::Escape($Folder))}}
$FinalList = $FullList | Where{$ToRemove -notcontains $_}
That's going to be slow though, there has to be a better way to do it. I just haven't thought of a better way yet.
Don't get me wrong, this will work, and it's faster than going through things by hand for sure, but I'm sure that there has to be a better way to do it.
I need to rename a bunch of files at once in Windows PowerShell. I read the HTG article here and it helped a little.
My problem is that It will only rename files in the top of the directory, nothing deeper. For example: There is FOLDER A and inside FOLDERA is a document and FOLDER B. Inside FOLDER B is another document. Both folders and both documents need to be renamed. The way it is working now is that FOLDER A, the document in FOLDER A, and FOLDER B are being renamed, but not the document inside FOLDER B.
My current code is:
Dir | Rename-Item –NewName { $_.name –replace “ “,”_” }
Thanks for the help!
You need to specify the -Recurse parameter on Dir to get it to recurse e.g.:
Dir -recurse | Rename-Item -NewName {$_.Name -replace ' ','_'}
BTW this may run into a problem because you're renaming the folder (FOLDERB) that contains the document first but the item being piped that corresponds to the file in FOLDERB still has the old name. In this case, you want to rename from the bottom up. One very crude but effective (I think) way to do this is to sort the file items on their path length descending e.g.:
Dir -recurse | Sort {$_.FullName.Length} -Desc | Rename-Item {$_.Name -replace ' ','_'}