Fli
Execute CLI commands from your F# code in F# style!
Fli is part of the F# Advent Calendar 2022: A little story about Fli
Features
- Starting processes easily
- Execute CLI commands in your favourite shell
- F# computation expression syntax
- Wrap authenticated CLI tools
- No external dependencies
Install
Get it from Nuget: dotnet add package Fli
Usage
open Fli
and start
For example:
cli {
Shell CMD
Command "echo Hello World!"
}
|> Command.execute
that starts CMD.exe
as Shell and echo Hello World!
is the command to execute.
Run a file with PowerShell from a specific directory:
cli {
Shell PWSH
Command "test.bat"
WorkingDirectory (Environment.GetFolderPath Environment.SpecialFolder.UserProfile)
}
|> Command.execute
Executing programs with arguments:
cli {
Exec "path/to/executable"
Arguments "--info"
}
|> Command.execute
an example with git
:
cli {
Exec "git"
Arguments ["commit"; "-m"; "Fixing issue #1337."]
}
|> Command.execute
Add a verb to your executing program:
cli {
Exec "adobe.exe"
Arguments (Path.Combine ((Environment.GetFolderPath Environment.SpecialFolder.UserProfile), "test.pdf"))
Verb "open"
}
|> Command.execute
Write output to a specific file:
cli {
Exec "dotnet"
Arguments "--list-sdks"
Output @"absolute\path\to\dotnet-sdks.txt"
}
|> Command.execute
Write output to a function (logging, printing, etc.):
let log (output: string) = Debug.Log($"CLI log: {output}")
cli {
Exec "dotnet"
Arguments "--list-sdks"
Output log
}
|> Command.execute
Add environment variables for the executing program:
cli {
Exec "git"
EnvironmentVariables [("GIT_AUTHOR_NAME", "Jon Doe"); ("GIT_AUTHOR_EMAIL", "[email protected]")]
}
|> Command.execute
Add credentials to program:
cli {
Exec "program"
Credentials ("domain", "bobk", "password123")
}
|> Command.execute
Hint: Running a process as a different user is supported on all platforms. Other options (Domain, Password) are only available on Windows. As an alternative for not Windows based systems there is:
cli {
Exec "path/to/program"
Username "admin"
}
|> Command.execute
Command.execute
Command.execute
returns record: type Output = { Id: int; Text: string option; ExitCode: int; Error: string option }
which has getter methods to get only one value:
toId: Output -> int
toText: Output -> string
toExitCode: Output -> int
toError: Output -> string
example:
cli {
Shell CMD
Command "echo Hello World!"
}
|> Command.execute // { Id = 123; Text = Some "Hello World!"; ExitCode = 0; Error = None }
|> Output.toText // "Hello World!"
// same with Output.toId:
cli { ... }
|> Command.execute // { Id = 123; Text = Some "Hello World!"; ExitCode = 0; Error = None }
|> Output.toId // 123
// same with Output.toExitCode:
cli { ... }
|> Command.execute // { Id = 123; Text = Some "Hello World!"; ExitCode = 0; Error = None }
|> Output.toExitCode // 0
// in case of an error:
cli { ... }
|> Command.execute // { Id = 123; Text = None; ExitCode = 1; Error = Some "This is an error!" }
|> Output.toError // "This is an error!"
Output
fields
Printing There are printing methods in Output
too:
printId: Output -> unit
printText: Output -> unit
printExitCode: Output -> unit
printError: Output -> unit
Instead of writing:
cli { ... }
|> Command.execute
|> Output.toText
|> printfn "%s"
For a little shorter code you can use:
cli { ... }
|> Command.execute
|> Output.printText
Command.toString
Command.toString
concatenates only the the executing shell/program + the given commands/arguments:
cli {
Shell PS
Command "Write-Host Hello World!"
}
|> Command.toString // "powershell.exe -Command Write-Host Hello World!"
and:
cli {
Exec "cmd.exe"
Arguments [ "/C"; "echo"; "Hello World!" ]
}
|> Command.toString // "cmd.exe /C echo Hello World!"
Builder operations:
ShellContext
operations (cli { Shell ... }
):
Operation | Type |
---|---|
Shell |
Fli.Shells |
Command |
string |
Input |
string |
Output |
Outputs (see below) |
WorkingDirectory |
string |
EnvironmentVariable |
string * string |
EnvironmentVariables |
(string * string) list |
Encoding |
System.Text.Encoding |
CancelAfter |
int |
ExecContext
operations (cli { Exec ... }
):
Operation | Type |
---|---|
Exec |
string |
Arguments |
string / string seq / string list / string array |
Input |
string |
Output |
Outputs (see below) |
Verb |
string |
Username |
string |
Credentials |
string * string * string |
WorkingDirectory |
string |
EnvironmentVariable |
string * string |
EnvironmentVariables |
(string * string) list |
Encoding |
System.Text.Encoding |
CancelAfter |
int |
Currently provided Fli.Shells
:
CMD
runscmd.exe /c ...
orcmd.exe /k ...
(depends ifInput
is provided or not)PS
runspowershell.exe -Command ...
PWSH
runspwsh.exe -Command ...
WSL
runswsl.exe -- ...
BASH
runsbash -c ...
CUSTOM (shell: string * flag: string)
runs the specifiedshell
with the specified starting argument (flag
)
Provided Fli.Outputs
:
File of string
a string with an absolute path of the output file.StringBuilder of StringBuilder
a StringBuilder which will be filled with the output text.Custom of Func<string, unit>
a custom function (string -> unit
) that will be called with the output string (logging, printing etc.).
Do you miss something?
Open an issue or start a discussion.
Inspiration
Use CE's for CLI commands came in mind while using FsHttp.