Thursday, March 25, 2021

power(shell), corruption & lies: Building command line arguments

I hate typing long command lines to command line utilities. Invariably I will get one obscure path wrong and spend an hour with a copy / pasted command line in notepad to split out the components and figure out what I screwed up this time.

A lot of those long typing jobs have to do with feeding a bunch of file or directory names to an .exe

    foo_the_bar.exe --blort first\dir second\dir third\dir hey\heres\a\filename.txt

If I only had a tool that I could use to generate that list of directories and filenames.

powershell and Invoke-Expression (iex to it's friends) to the frickin' rescue.

  1. Write a powershell expression using Get-ChildItem (gci), Where-Object (?) and Select-Object (select) to build your list of things. Use -join ' ' to space separate the list of things

    PS c:\users\kpk> (gci c:\my\dir -recurse | ?{ $_.FullName -match 'some.*criteria$' } | select -expand FullName) -join ' '

  2. Use string interpolation to embed that expression with the name of the .exe you want to run. In my case, I was using cloc to (duh) Count Lines Of Code.

    PS c:\users\kpk> "cloc $((gci c:\my\dir -recurse | ?{ $_.FullName -match 'some.*criteria$' } | select -expand FullName) -join ' ')"

  3. Run that expression a few times to make sure you're happy with it. Add some command line arguments as needed.
  4. Use iex (Invoke-Expression) to run that beautiful mess.

Let's unpack the expression a bit

    PS c:\users\kpk> (gci c:\my\dir -recurse | ?{ $_.FullName -match 'some.*criteria$' } | select -expand FullName) -join ' '

  1. gci c:\my\dir -recurse |

    gci (Get-ChildItem) recurses through a directory structure. It's more complicated than that, but things often are.

  2. ?{ $_.FullName -match 'some.*criteria$' } |

    ? (Where-Object) will test each produced file & directory for a criteria. In this case, a regular expression. Only the worthy shall pass.

  3. select -expand FullName

    select (Select-Object) extracts the FullName property from the produced collection of objects and expands it to a list

  4. -join ' '

    I need that list turned into a single flattened string with space separation, because cloc wants space separation. Other command line utilities want other kind of formatting (prefixed by a magic argument, comma separated, etc.). -join's your friend here.
Once all of this is looking good, and you've prefixed it with the name of the command you want to run, iex takes care of the rest.

Never let a human do a job that a robot can do better. Ok, maybe not 'Never', but mostly. Sometimes.... Whatever.

4 comments:

  1. I've been playing Admin for so long I figured command line was dead. I still use it to run my Perl scripts but it's nice to see that nuts and bolts programming is alive and well. Thanks, Kevin.

    ReplyDelete
  2. An interesting Q&A/discussion on reddit post regarding this article...

    https://www.reddit.com/r/PowerShell/comments/mgg1ts/powershell_corruption_lies_building_command_line/gsv1sit/?context=3

    ReplyDelete
    Replies
    1. seconded - I've learned a ton from this discussion.

      Delete