0

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.

1
  • First, to ouput your results to a text file you can write Get-ChildItem -Recurse -Name | Out-File -FilePath .\filelist.txt. Commented Oct 6, 2022 at 6:31

1 Answer 1

1

Something like this should do the trick.

  • Setting your current console path to your main folder path using Set-Location then using Resolve-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 -Directory -Recurse | 
    Where {$_.GetFiles().count -gt 0 } | 
    Select-Object -ExpandProperty FullName | 
    Resolve-Path -Relative

# Producing an object containing everything we need for the actual output.
$Output = Foreach ($D in $Directories) {
    [PSCustomObject]@{
        #Note, standard relative path use 1 dot. If you want 3 anyway, just switch the uncommented Directory line with the commented one.
        #Directory = "..$D"
        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
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! This pretty much is what I need. It does include the directories that have no files in them but other than that its spot on. I can use it as is at this point. Thanks again for the help!
@Dega419 Oh, I missed that. You can filter them out by appending this | Where {$_.GetFiles().count -gt 0 } | ` directly after the Get-ChildItem. I updated the code in my answer to reflect this.
I appreciate the updated code. It works flawlessly now! Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.