Core Library0.xHome

Choose documentation 1

latest

Current version

latest
English

Early release libtmux is released, and its APIs may still change between releases. This site's structure and URLs may change too.

Edit this page on GitHub

tmux client library for F#

Control tmux, navigate sessions, windows, and panes, send keys, watch for changes via control mode, capture pane contents.

Install

dotnet package add LibTmux.FSharp

LibTmux.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.

TypeWraps
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#

Example details
open System
open System.Threading
open LibTmux
open 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 System
open System.Threading
open LibTmux
open 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 System
open System.Threading
open LibTmux
open 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 System
open System.Threading
open LibTmux
open 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 app
dotnet restore Example.fsproj
dotnet build Example.fsproj -c Release --no-restore --warnaserror
export TMUX_TMPDIR=/tmp/libtmux-dotnet-dev
mkdir -p "$TMUX_TMPDIR"
unset TMUX TMUX_PANE
dotnet bin/Release/net8.0/Example.dll
dotnet bin/Release/net10.0/Example.dll

Expected output

other sessions: web, worker
wait found: true
run: exit 3, output ["ok"]

Library revision: 472c2bcfbda6.

Typed API

What you get over the tmux CLI

Esc

Type to search.

Reference previewOpen reference