Search code examples
powershellperforce

Split strings from command output in Powershell


I work with Perforce and often have many local changes after unshelving that I want to remove. Using PowerShell I can print out the files I want removed but the output string still contains substrings I would like to see removed.

Get-ChildItem -Recurse -Include ("*.uasset", "*.umap") -Attributes !ReadOnly "C:\Unreal\Project\Content" | ForEach-Object {p4 have $_.FullName}

This will output files I only have locally as "C:\Unreal\Project\File.uasset - file(s) not on client." and others as "//Project/Content/File.uasset#12 - C:\Unreal\Project\File.uasset"

The files I don't have on client I want to Remove-Items on, and the other run p4 sync but am currently stuck even getting Write-Host to output either with only the local path.

I've tried filtering the outputs using Select-String, to then attempt and only output the paths like

Get-ChildItem -Recurse -Include ("*.uasset", "*.umap") -Attributes !ReadOnly "C:\Unreal\Project\Content" | ForEach-Object {p4 have $_.FullName} | Select-String " not on client." | Write-Host ($_ -split ' - file(s)')[0]

The full string "C:\Unreal\Project\File.uasset - file(s) not on client." still gets printed

Also attemped filtering inside the loop like this

Get-ChildItem -Recurse -Include ("*.uasset", "*.umap") -Attributes !ReadOnly "C:\Unreal\Project\Content" | ForEach-Object { if ((p4 have $_.FullName) -like "*not on client." { Write-Host ($_ -split ' - file(s)')[0] } }

Again, full string is printed to terminal.

I have tried variation of split like $_.Split(' - file(s)', 2)[0] but still full string gets printed. And just to make sure, I tried different indices if it was the case that split would have full string as first index then have results of split.

Example of unfiltered output

> Get-ChildItem -Recurse -Include ("*.uasset", "*.umap") -Attributes !ReadOnly "C:\Unreal\Project\Content" | ForEach-Object {p4 have $_.FullName}
C:\Unreal\Project\Content\Example_01.uasset - file(s) not on client.
C:\Unreal\Project\Content\Example_02.uasset - file(s) not on client.
C:\Unreal\Project\Content\Example_03.uasset - file(s) not on client.
//Project/Content/Example_04.uasset#11 - C:\Unreal\Project\Content\Example_04.uasset
//Project/Content/Example_05.uasset#6 - C:\Unreal\Project\Content\Example_05.uasset
//Project/Content/Example_06.uasset#1 - C:\Unreal\Project\Content\Example_06.uasset
//Project/Content/Example_07.umap#7 - C:\Unreal\Project\Content\Example_07.umap
//Project/Content/Example_08.umap#73 - C:\Unreal\Project\Content\Example_08.umap
C:\Unreal\Project\Content\Example_09.uasset - file(s) not on client.
C:\Unreal\Project\Content\Example_10.uasset - file(s) not on client.
C:\Unreal\Project\Content\Example_11.uasset - file(s) not on client.
C:\Unreal\Project\Content\Example_12.uasset - file(s) not on client.
C:\Unreal\Project\Content\Example_13.uasset - file(s) not on client.

Guess one could note also, that the p4 command have will print the "file(s) not on client" to stderr as this is an error/warning - found after I gave up on powershell and just did it in python.


Solution

  • Putting together everbody's efforts:

    # Get the files
    Get-ChildItem -Recurse -Include ('*.uasset', '*.umap') -Attributes !ReadOnly 'C:\Unreal\Project\Content' |
        # run "p4 have" on every file
        # we are only interested in the results sent to stdErr\Error Stream, so redirect it to stdOut\Success Stream
        ForEach-Object { p4 have $_.FullName 2>&1 } | 
        # Filter keeping only the ones we need
        Where-Object { $_ -like '*not on client.' } | 
        # for each string, remove the undesidered parts(everything after the filename)
        ForEach-Object { $_.Substring($_.IndexOf(' - file(s)')) }
    

    It returns a string array of filenames with full path