Home > database >  PowerShell script for Multiple Files and Multiple Directories
PowerShell script for Multiple Files and Multiple Directories

Time:10-06

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 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 -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
  • Related