Scripting with .fsx
Prerequisites: Working with files
F# scripts are a strongly typed alternative to shell, Python, or
PowerShell scripts for automation, data fixes,
and exploration. One file, one command: dotnet fsi script.fsx.
A practical script
open System
open System.IO
// Create some sample files so the script has something to work on
Directory.CreateDirectory "logs" |> ignore
File.WriteAllLines("logs/app-1.log", [ "INFO start"; "ERROR disk full"; "INFO done" ])
File.WriteAllLines("logs/app-2.log", [ "WARN slow"; "ERROR timeout"; "ERROR timeout" ])
let errorsByFile =
Directory.GetFiles("logs", "*.log")
|> Array.sort
|> Array.map (fun path ->
let errors =
File.ReadLines path
|> Seq.filter (fun line -> line.StartsWith "ERROR")
|> Seq.length
Path.GetFileName path, errors)
for (file, count) in errorsByFile do
printfn "%-10s %d error(s)" file count
let total = errorsByFile |> Array.sumBy snd
if total > 0 then
eprintfn "Found %d errors" total
exit 1
app-1.log 1 error(s)
app-2.log 2 error(s)
Found 3 errors
exit 1ends the script with a non-zero exit code, so CI pipelines and shell scripts can detect failure.eprintfnwrites to standard error.
Script directives
| Directive | Does |
|---|---|
#r "nuget: Name, 1.2.3" |
reference a NuGet package (optionally pinned) |
#r "path/to/Library.dll" |
reference a local assembly |
#load "Helpers.fsx" |
include another script (its modules become available) |
#i "nuget: https://my-feed/index.json" |
add a package source |
#time "on" |
print timing information for each evaluation (interactive) |
__SOURCE_DIRECTORY__ and __SOURCE_FILE__ give the script's location,
and fsi.CommandLineArgs its arguments.
Running external programs
open System.Diagnostics
let run (file: string) (args: string) =
let psi = ProcessStartInfo(file, args, RedirectStandardOutput = true, UseShellExecute = false)
use p = Process.Start psi
let output = p.StandardOutput.ReadToEnd()
p.WaitForExit()
p.ExitCode, output.Trim()
let (code, version) = run "dotnet" "--version"
printfn "exit %d, SDK major version %s" code (version.Split('.')[0])
exit 0, SDK major version 10
Libraries such as Fli and CliWrap make this nicer, with pipelines and async support.
Build scripts
Many F# projects keep their build automation in F#: a build.fsx script,
or a small console project, using Fake modules (for example,
Fake.DotNet.Cli to call dotnet build and dotnet test) — typed,
debuggable, and cross-platform, instead of fragile shell scripts.
Notebooks
Polyglot Notebooks in VS Code run F# in notebook cells, with charts (via Plotly.NET) and data tables — useful for data exploration and reports.
Common mistakes
- Unpinned
#r "nuget: ..."references: a new package version can break an old script. Pin versions for scripts you keep. - Assuming the current directory is the script's folder — use
__SOURCE_DIRECTORY__.