tmux client library for F#
Control tmux, navigate sessions, windows, and panes, send keys, watch for changes via control mode, capture pane contents.
Guides
Task guides for F#.
Examples
Complete programs, sourced from the code each port tests.
API reference
Types and functions of the F# API.
Install
dotnet package add LibTmux.FSharpLibTmux.FSharp 0.1.0 is pre-1.0 and can change between minor versions. Pin to a range:
<PackageReference Include="LibTmux.FSharp" Version="[0.1.0,0.2)" />At a glance
Server → Session → Window → Pane. Each level of the tmux hierarchy has its own entry in the F# API, with the tmux object it wraps.
| Type | Wraps |
|---|---|
LibTmux.FSharp.Server | A tmux server |
LibTmux.FSharp.Session | A session, ID $n |
LibTmux.FSharp.Window | A window in a session, ID @n |
LibTmux.FSharp.Pane | A pane in a window, ID %n |
What it does
Start with F#
open Systemopen System.Threadingopen LibTmuxopen LibTmux.FSharp
let runAsync () = task { use deadline = new CancellationTokenSource(TimeSpan.FromSeconds 30.) let token = deadline.Token
let options = ServerConnectionOptions( SocketName = "fsharp-" + Guid.NewGuid().ToString("N"), ConfigurationFile = "/dev/null", TmuxBinaryPath = (Environment.GetEnvironmentVariable "LIBTMUX_TMUX" |> Option.ofObj |> Option.defaultValue "tmux") )
let! owned = LibTmux.Server.CreateOwnedAsync(options, token)
for name in [ "build"; "web"; "worker" ] do let! _ = owned.Value.CreateSessionAsync( NewSessionRequest(Name = name, Command = "/bin/sh"), token )
()
let! server = LibTmux.Server.ConnectAsync(options, token)
// List and filter: tmux narrows the listing; every row is rechecked. // atMostOne is None when nothing matches and raises when several do. let! build = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.eq "build") |> Query.atMostOne token
let! others = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.ne "build") |> Query.list token
printfn "other sessions: %s" (String.Join(", ", [ for session in others -> session.Name ]))
match build with | None -> printfn "no build session" | Some session -> let! panes = session |> Session.panes |> Query.list token let pane = panes[0]
// Type a command and wait for what it prints, not for its echo. let! started = pane |> Pane.sendAndWait token (TimeSpan.FromSeconds 10.) "echo build started" "build started"
// Run a command to its exit status and read what it printed. let! result = pane |> Pane.run token (TimeSpan.FromSeconds 10.) "printf 'ok\\n'; exit 3"
printfn "wait found: %b" started.Found
printfn "run: exit %d, output %A" result.ExitStatus.Value (List.ofSeq result.Output) }
runAsync().GetAwaiter().GetResult()open Systemopen System.Threadingopen LibTmuxopen LibTmux.FSharp
let runAsync () = task { use deadline = new CancellationTokenSource(TimeSpan.FromSeconds 30.) let token = deadline.Token
let options = ServerConnectionOptions( SocketName = "fsharp-" + Guid.NewGuid().ToString("N"), ConfigurationFile = "/dev/null", TmuxBinaryPath = (Environment.GetEnvironmentVariable "LIBTMUX_TMUX" |> Option.ofObj |> Option.defaultValue "tmux") )
let! owned = LibTmux.Server.CreateOwnedAsync(options, token)
for name in [ "build"; "web"; "worker" ] do let! _ = owned.Value.CreateSessionAsync( NewSessionRequest(Name = name, Command = "/bin/sh"), token )
()
let! server = LibTmux.Server.ConnectAsync(options, token)
// List and filter: tmux narrows the listing; every row is rechecked. // atMostOne is None when nothing matches and raises when several do. let! build = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.eq "build") |> Query.atMostOne token
let! others = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.ne "build") |> Query.list token
printfn "other sessions: %s" (String.Join(", ", [ for session in others -> session.Name ]))
match build with | None -> printfn "no build session" | Some session -> let! panes = session |> Session.panes |> Query.list token let pane = panes[0]
// Type a command and wait for what it prints, not for its echo. let! started = pane |> Pane.sendAndWait token (TimeSpan.FromSeconds 10.) "echo build started" "build started"
// Run a command to its exit status and read what it printed. let! result = pane |> Pane.run token (TimeSpan.FromSeconds 10.) "printf 'ok\\n'; exit 3"
printfn "wait found: %b" started.Found
printfn "run: exit %d, output %A" result.ExitStatus.Value (List.ofSeq result.Output) }
try runAsync().GetAwaiter().GetResult()with error -> raise (InvalidOperationException( "tmux quickstart failed", error))open Systemopen System.Threadingopen LibTmuxopen LibTmux.FSharp
let runAsync () = task { use deadline = new CancellationTokenSource(TimeSpan.FromSeconds 30.) let token = deadline.Token
let options = ServerConnectionOptions( SocketName = "fsharp-" + Guid.NewGuid().ToString("N"), ConfigurationFile = "/dev/null", TmuxBinaryPath = (Environment.GetEnvironmentVariable "LIBTMUX_TMUX" |> Option.ofObj |> Option.defaultValue "tmux") )
use! owned = LibTmux.Server.CreateOwnedAsync(options, token)
for name in [ "build"; "web"; "worker" ] do let! _ = owned.Value.CreateSessionAsync( NewSessionRequest(Name = name, Command = "/bin/sh"), token )
()
let! server = LibTmux.Server.ConnectAsync(options, token)
// List and filter: tmux narrows the listing; every row is rechecked. // atMostOne is None when nothing matches and raises when several do. let! build = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.eq "build") |> Query.atMostOne token
let! others = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.ne "build") |> Query.list token
printfn "other sessions: %s" (String.Join(", ", [ for session in others -> session.Name ]))
match build with | None -> printfn "no build session" | Some session -> let! panes = session |> Session.panes |> Query.list token let pane = panes[0]
// Type a command and wait for what it prints, not for its echo. let! started = pane |> Pane.sendAndWait token (TimeSpan.FromSeconds 10.) "echo build started" "build started"
// Run a command to its exit status and read what it printed. let! result = pane |> Pane.run token (TimeSpan.FromSeconds 10.) "printf 'ok\\n'; exit 3"
printfn "wait found: %b" started.Found
printfn "run: exit %d, output %A" result.ExitStatus.Value (List.ofSeq result.Output) }
runAsync().GetAwaiter().GetResult()open Systemopen System.Threadingopen LibTmuxopen LibTmux.FSharp
let runAsync () = task { use deadline = new CancellationTokenSource(TimeSpan.FromSeconds 30.) let token = deadline.Token
let options = ServerConnectionOptions( SocketName = "fsharp-" + Guid.NewGuid().ToString("N"), ConfigurationFile = "/dev/null", TmuxBinaryPath = (Environment.GetEnvironmentVariable "LIBTMUX_TMUX" |> Option.ofObj |> Option.defaultValue "tmux") )
use! owned = LibTmux.Server.CreateOwnedAsync(options, token)
for name in [ "build"; "web"; "worker" ] do let! _ = owned.Value.CreateSessionAsync( NewSessionRequest(Name = name, Command = "/bin/sh"), token )
()
let! server = LibTmux.Server.ConnectAsync(options, token)
// List and filter: tmux narrows the listing; every row is rechecked. // atMostOne is None when nothing matches and raises when several do. let! build = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.eq "build") |> Query.atMostOne token
let! others = server |> Server.sessions |> Query.where (SessionFields.name |> Filter.ne "build") |> Query.list token
printfn "other sessions: %s" (String.Join(", ", [ for session in others -> session.Name ]))
match build with | None -> printfn "no build session" | Some session -> let! panes = session |> Session.panes |> Query.list token let pane = panes[0]
// Type a command and wait for what it prints, not for its echo. let! started = pane |> Pane.sendAndWait token (TimeSpan.FromSeconds 10.) "echo build started" "build started"
// Run a command to its exit status and read what it printed. let! result = pane |> Pane.run token (TimeSpan.FromSeconds 10.) "printf 'ok\\n'; exit 3"
printfn "wait found: %b" started.Found
printfn "run: exit %d, output %A" result.ExitStatus.Value (List.ofSeq result.Output) }
try runAsync().GetAwaiter().GetResult()with error -> raise (InvalidOperationException( "tmux quickstart failed", error))Run this example
.NET SDK 10.0.303, .NET 8 and 10 runtimes, tmux 3.4 or newer on Linux/macOS
Save the program above as app/Program.fs in a new directory, along with the files below.
Cleanup is omitted; the private tmux server and its temporary files may remain. Select Cleanup to remove them when the program finishes. The program stops its private tmux server and removes any temporary files it created when it finishes. Error handling adds diagnostics; every version checks required results and deadlines.
app/Example.fsproj
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFrameworks>net8.0;net10.0</TargetFrameworks> <LangVersion>10.0</LangVersion> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> <DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference> <DisableImplicitLibraryPacksFolder>true</DisableImplicitLibraryPacksFolder> </PropertyGroup> <ItemGroup> <Compile Include="Program.fs" /> <PackageReference Include="FSharp.Core" Version="[10.1.303]" /> <PackageReference Include="LibTmux.FSharp" Version="[0.1.0]" /> </ItemGroup></Project>app/global.json
{ "sdk": { "version": "10.0.303", "rollForward": "disable", "allowPrerelease": false }, "test": { "runner": "Microsoft.Testing.Platform" }}Setup and run
# Save the files above at their displayed relative paths.cd appdotnet restore Example.fsprojdotnet build Example.fsproj -c Release --no-restore --warnaserrorexport TMUX_TMPDIR=/tmp/libtmux-dotnet-devmkdir -p "$TMUX_TMPDIR"unset TMUX TMUX_PANEdotnet bin/Release/net8.0/Example.dlldotnet bin/Release/net10.0/Example.dllExpected output
other sessions: web, workerwait found: truerun: exit 3, output ["ok"]Library revision: 472c2bcfbda6.
Typed API
- Filtering and queries over tmux objects:
LibTmux.FSharp.Query - Typed errors instead of exit codes and stderr:
LibTmux.FSharp.TmuxFailure
What you get over the tmux CLI
- Traversal from a server down to its panes:
LibTmux.FSharp.Server.panes - Send keys to a pane:
LibTmux.FSharp.Pane.sendKeys - Watch for changes through tmux control mode:
LibTmux.FSharp.Control - Capture pane contents:
LibTmux.FSharp.Pane.capture - Async operations:
LibTmux.FSharp.Pane.run