I am working on a powershell script to gather all subdirectories and file names within them.
I currently have the following for directory names.
Get-ChildItem -Path "C:\Users\UserName\Desktop\Script\Main Folder" -Recurse -Directory | Select-Object FullName
This so far works somewhat as intended however it gives me all directories even the ones that "do not" have files in them. I need it to only give me the directories with files in them such as .txt files and .jpg picture files etc. I also want the script to replace the C:\Users\UserName\Desktop part with just ... "three dots" then the rest of the path name instead of the full path name.
The Output looks like this
C:\Users\UserName\Desktop\Script\Main Folder\Folder A
C:\Users\UserName\Desktop\Script\Main Folder\Folder B
C:\Users\UserName\Desktop\Script\Main Folder\Folder A\Folder C
C:\Users\UserName\Desktop\Script\Main Folder\Folder A\Folder C\Folder D
I wish for it to look like this instead
...\Main Folder\Folder A
...\Main Folder\Folder B
...\Main Folder\Folder A\Folder C\Folder D
Here is a visual diagram Image Link
Next I need to gather all file names in the subdirectories
This is what I have so far as code
Get-ChildItem -Recurse -Name
This gives an output that looks like this
Folder A
Folder B
File 1.txt
Folder A\Folder C
Folder A\File 2.txt
Folder A\File 3.txt
Folder A\Pic1.jpg
Folder A\Folder C\Folder D
Folder A\Folder C\Folder D\File 6.txt
Folder B\File 4.txt
Folder B\File 5.txt
Folder B\Pic2.jpg
The output should instead look like this
File 1.txt
File 2.txt
File 3.txt
Pic1.jpg
File 6.txt
File 4.txt
File 5.txt
Pic2.jpg
This is essentaly the file names only with the paths fully removed and no directories at all.
Finally I need a way to merge both of these codes into one and give an output that looks like the following
...\Main Folder\Folder A
File 2.txt
File 3.txt
Pic1.jpg
...\Main Folder\Folder B
File 4.txt
File 5.txt
Pic2.jpg
...\Folder A\Folder C\Folder D
File 6.txt
I "Do Not" need spaces between the actual file names. That is only for formatting and easy reading here on this website. I would however like a space between the directory and the first file name listed in that directory. Then a space between the last file name and the next directory.
Also the code should be able to send the output to a basic text file.
I am not too sure if I should ask multiple questions to get this answer since this is really two parts of code then merging them somehow.
Any feedback given would be greatly appreciated.
CodePudding user response:
To just get the names of the files you can Get-ChildItem
but not select -Name
immediately. Instead you select it afterwards.
$files = Get-ChildItem -Recurse
$files.Name | Out-File -FilePath .\fileNames.txt
For your first part, I don't think there is an easy option to crop the path just a few directories.
CodePudding user response:
Something like this should do the trick.
Setting your current console path to your main folder path using
Set-Location
then usingResolve-Path
on your items path will create the stripped version of the path.You build a custom object
$Output
that will contains the individual directories and files for these directories (you don't want-Recurse
on the files though since you did it on the directories already and you are processing each directory one by one)You do whatever you want to produce your output. I would personally have chosen json so you could reimport it later if you wanted but if you just want a final output (visual only) then you can do that to. I used a
StringBuilder
object since it is the most efficient way to manipulate strings and built your output from there.Export the files using
Set-Content
,Out-File
, etc... The stringbuilder object need to be converted to its string representation at this point$sb.toString()
Example
$Params = @{
RootPath = 'C:\Users\UserName\Desktop\Script\Main Folder'
ResultFilePath = 'C:\Users\UserName\Desktop\MyStuff.txt'
}
Set-Location $Params.RootPath
$Directories = Get-ChildItem $Params.RootPath -Recurse -Directory | Select-Object -ExpandProperty FullName | Resolve-Path -Relative
# Producing an object containing everything we need for the actual output.
$Output = Foreach ($D in $Directories) {
[PSCustomObject]@{
Directory = $D
Files = Get-ChildItem $D -Name -File
}
}
# Producing the "simple file text"
$sb = [System.Text.StringBuilder]::new()
Foreach ($Item in $Output) {
[void]$sb.AppendLine($Item.Directory)
[void]$sb.AppendLine("`t$($Item.Files -join "`r`n`t")")
[void]$sb.AppendLine()
}
$sb.ToString() | Out-File -FilePath $Params.ResultFilePath
As long as you are clear that that output, while great visually, is not very good if you need to reingest the data into another script, then you should be good.
If you wanted to reingest the data later on, you could use a predefined format (eg: Json) by replacing the last part of the code by
# ... Everything before this point is the same as the previous script
$Output = Foreach ($D in $Directories) {
[PSCustomObject]@{
Directory = $D
# If not casted into a string array, ConvertTo-Json decided to export the names with extra data
Files = [String[]](Get-ChildItem $D -Name -File)
}
}
$output | ConvertTo-Json | Out-File $params.ResultFilePath