tinkerlab.dev
/julia/18-stdlib-recipes

Standard tasks

Files and paths

read("f.txt", String)
readlines("f.txt")
write("f.txt", "content")
readchomp(`cmd`)

open("out.csv", "w") do io
    println(io, "a,b")
    write(io, data)
end

for line in eachline("big.log")      # streaming, constant memory
    process(line)
end

readdir("."); readdir("."; join = true)
walkdir(".")                          # recursive, yields (root, dirs, files)
isfile, isdir, ispath, islink
mkdir, mkpath, rm(p; recursive = true, force = true)
cp(src, dst; force = true); mv
joinpath("a", "b", "c.txt")
splitpath, splitdir, splitext, basename, dirname
abspath, normpath, realpath, expanduser("~/x")
homedir(), tempdir(), tempname()
mktemp() do path, io ... end
mktempdir() do dir ... end
filesize(p), mtime(p), stat(p)
touch(p)
@__DIR__                              # directory of the current source file
pkgdir(MyPkg)                         # a package root — the way to find its assets

Binary IO:

data = read("f.bin")                  # Vector{UInt8}
open("f.bin") do io
    x = read(io, Int64)
    v = read(io, 100)
    seek(io, 0); position(io); eof(io)
end

Processes

Backticks build a Cmd. There is no shell, so no quoting bugs and no injection.

run(`ls -la`)                         # throws on non-zero exit
read(`git rev-parse HEAD`, String)
readchomp(`hostname`)
success(`which julia`)                # Bool instead of throwing

file = "name with spaces.txt"
run(`cat $file`)                      # passed as ONE argument, correctly

run(pipeline(`cat f.txt`, `grep x`))
run(pipeline(`cmd`, stdout = "out.txt", stderr = "err.txt"))
open(`sort`, "w", stdout) do io
    println(io, "b"); println(io, "a")
end

p = run(`long-thing`; wait = false)
kill(p); process_running(p); p.exitcode

withenv("VAR" => "value") do
    run(`printenv VAR`)
end
addenv(`cmd`, "K" => "v")
Cmd(`cmd`; dir = "/tmp")

If you genuinely need shell features, invoke a shell explicitly:

run(`sh -c "a | b > c"`)

Environment and arguments

ENV["HOME"]
get(ENV, "PORT", "8080")
haskey(ENV, "CI")
ARGS                                  # Vector{String} of command-line args
PROGRAM_FILE
Sys.iswindows(), Sys.islinux(), Sys.isapple()
Sys.CPU_THREADS, Sys.total_memory()
VERSION                               # v"1.12.6"
@static if Sys.islinux() ... end      # compile-time branch

Argument parsing: ArgParse.jl (full-featured, argparse-shaped) or Comonicon.jl (turns a function signature into a CLI).

Dates and time

using Dates
now(); today(); now(UTC)
Date(2026, 8, 17); DateTime(2026, 8, 17, 14, 30)
Date("2026-08-17"); Date("17/08/2026", dateformat"dd/mm/yyyy")
Dates.format(now(), "yyyy-mm-dd HH:MM:SS")

now() + Day(3); now() - Hour(2)
Year, Month, Week, Day, Hour, Minute, Second, Millisecond
year(d), month(d), day(d), dayofweek(d), dayofyear(d), isleapyear(d)
d1 - d2                               # a Period
Dates.value(d1 - d2)                  # as a number
firstdayofmonth(d), lastdayofmonth(d)
Date(2026,1,1):Day(1):Date(2026,12,31)   # ranges of dates work

time()                                # Unix seconds, Float64
time_ns()                             # monotonic nanoseconds — use for timing
unix2datetime(t), datetime2unix(d)

Timezones are not in the stdlib: TimeZones.jl (ZonedDateTime, TimeZone("Europe/London")).

Randomness

using Random
rand()                    # Float64 in [0,1)
rand(1:6); rand(v); rand(3, 3); rand(Bool)
randn(100)                # standard normal
randexp, randstring(8)
shuffle(v); shuffle!(v)
randperm(10); randsubseq(v, 0.1)
sample(v, 5; replace = false)         # StatsBase.jl
Random.seed!(42)
rng = MersenneTwister(42); rand(rng, 10)
rng = Xoshiro(42)                     # the current default algorithm

For reproducibility across Julia versions use StableRNGs.jl — the default RNG is explicitly allowed to change.

Data wrangling in Base

sort(v; by = x -> x.age, rev = true)
sort(v; lt = (a, b) -> a.n < b.n)
sortperm(v); partialsort(v, 1:10); partialsortperm
searchsorted(sorted_v, x); searchsortedfirst
filter(f, v); count(f, v); any(f, v); all(f, v)
findfirst, findlast, findall, findnext
findmax(v), argmax(v), argmax(f, v)
unique(v), unique(f, v), allunique(v)
union, intersect, setdiff, symdiff, issubset
reduce, foldl, mapreduce, accumulate, cumsum, cumprod
zip, enumerate, pairs
Iterators.partition(v, 3), flatten, product, take, drop, cycle, repeated, countfrom
group = Dict(); for x in v; push!(get!(group, key(x), []), x); end   # groupby by hand

SplitApplyCombine.jl gives you group, groupreduce, innerjoin for plain collections without a DataFrame.

Statistics and linear algebra (stdlib)

using Statistics
mean(v), median(v), std(v), var(v), quantile(v, [0.25, 0.75]), cor(x, y), cov(x, y)
mean(A; dims = 1)

using StatsBase          # not stdlib, but the standard extension
countmap(v), sample, weights, fit(Histogram, v), zscore, describe

Linear algebra has its own section (18).

Serialisation

using JSON3
obj = JSON3.read(json_string)              # lazy, indexable
obj = JSON3.read(json_string, MyStruct)    # typed, with StructTypes.jl
JSON3.write(obj)
JSON3.pretty(obj)

using StructTypes
StructTypes.StructType(::Type{MyStruct}) = StructTypes.Struct()
using CSV, DataFrames
df = CSV.read("data.csv", DataFrame)
CSV.write("out.csv", df)
CSV.File("data.csv") |> collect            # without DataFrames

using Serialization         # Julia-native binary; version-fragile, don't archive with it
serialize("f.jls", obj); deserialize("f.jls")

using JLD2                  # HDF5-based, good for arrays and long-term storage
jldsave("f.jld2"; a = 1, b = v)
load("f.jld2", "a")

using TOML                                 # stdlib
TOML.parsefile("Project.toml")
TOML.print(dict)

HTTP

using HTTP

r = HTTP.get("https://example.com")
r.status; String(r.body); Dict(r.headers)

HTTP.post(url; body = JSON3.write(payload),
              headers = ["Content-Type" => "application/json"])
HTTP.get(url; query = Dict("q" => "x"), retry = true, readtimeout = 30)

# server
router = HTTP.Router()
HTTP.register!(router, "GET", "/health", req -> HTTP.Response(200, "ok"))
HTTP.serve(router, "0.0.0.0", 8080)

Oxygen.jl wraps this with routing macros and OpenAPI generation if you want less ceremony. Downloads.download(url, path) (stdlib) is enough for fetching a file.

DataFrames, minimal

using DataFrames
df = DataFrame(a = 1:3, b = ["x","y","z"])

df.a                    # column, no copy
df[!, :a]               # column, no copy
df[:, :a]               # column, COPY
df[1, :]; df[1:2, [:a, :b]]
names(df); nrow(df); ncol(df); describe(df)

subset(df, :a => x -> x .> 1)
filter(:a => >(1), df)
transform(df, :a => (x -> 2x) => :doubled)
select(df, :a, :b => :renamed)
combine(groupby(df, :b), :a => sum => :total)
sort(df, :a; rev = true)
innerjoin(df1, df2; on = :id); leftjoin; outerjoin
stack(df); unstack(df)

The source => function => destination mini-language is the thing to learn; everything in DataFrames is expressed in it. DataFramesMeta.jl provides @select/@subset/@by macros if you prefer dplyr-shaped syntax.

Plotting

using Plots
plot(x, y; label = "series", xlabel = "t", lw = 2)
plot!(x, y2)             # add to the current plot (note the !)
scatter(x, y); histogram(v); heatmap(A); surface(x, y, z)
savefig("out.png")

# headless: avoid GR trying to open a window
ENV["GKSwstype"] = "100"

Makie.jl (CairoMakie for publication figures, GLMakie for interactive/GPU) is the more capable and more modern option, at the cost of heavier load time. UnicodePlots.jl draws in the terminal, which is genuinely useful over SSH.