Execute shell commands in F#

| 1 minute | Comments

#!/usr/bin/env -S dotnet fsi

open System
open System.Diagnostics
open System.Threading.Tasks

type CommandResult =
  { ExitCode: int
    StandardOutput: string
    StandardError: string }

let executeCommand executable args =
  async {
    let! ct = Async.CancellationToken

    let startInfo = ProcessStartInfo()
    startInfo.FileName <- executable
    startInfo.RedirectStandardOutput <- true
    startInfo.RedirectStandardError <- true
    startInfo.UseShellExecute <- false
    startInfo.CreateNoWindow <- true
    for a in args do
      startInfo.ArgumentList.Add(a)

    use p = new Process()
    p.StartInfo <- startInfo
    p.Start() |> ignore

    let outTask =
      Task.WhenAll([|
        p.StandardOutput.ReadToEndAsync(ct);
        p.StandardError.ReadToEndAsync(ct) |])

    do! p.WaitForExitAsync(ct) |> Async.AwaitTask
    let! out = outTask |> Async.AwaitTask

    return
      { ExitCode = p.ExitCode
        StandardOutput = out.[0]
        StandardError = out.[1] }
  }

let executeShellCommand command =
  executeCommand "/usr/bin/env" [ "-S"; "bash"; "-c"; command ]

// Invocation sample
let r = executeShellCommand "ls -alh" |> Async.RunSynchronously

if r.ExitCode = 0 then
  printfn "%s" r.StandardOutput
else
  eprintfn "%s" r.StandardError
  Environment.Exit(r.ExitCode)

If you create the file test.fsi with the code above, under Linux you can make it executable:

# Make it executable
chmod +x ./test.fsi

# Execute it
./test.fsi

Also, see: Execute Shell Commands in Java/Scala/Kotlin.

| Written by
Tags: Shell | dotNet | FSharp | Snippet