Julia Module 1 – Getting started: what Julia is for, installing it and running code
The REPL, scripts and the @main entry point
Use the Julia REPL's help, shell, package and search modes, run scripts with ARGS and @__DIR__, and give a script an @main entry point with an exit status.
What you will learn
- Switch between the REPL's Julian, help, shell and package modes and back
- Run a script with arguments and read ARGS, PROGRAM_FILE and @__DIR__
- Write an @main entry point whose return value becomes the exit status
- Explain why REPL helpers such as subtypes fail in a script, and fix it
- Choose start-up flags for repeatable runs, such as --startup-file=no, --project and -t
Before you start
What you’ll learn · Before you start
What you will learn
- Switch between the REPL's Julian, help, shell and package modes and back
- Run a script with arguments and read ARGS, PROGRAM_FILE and @__DIR__
- Write an @main entry point whose return value becomes the exit status
- Explain why REPL helpers such as subtypes fail in a script, and fix it
- Choose start-up flags for repeatable runs, such as --startup-file=no, --project and -t
Before you start
On this page
Type ? at the start of an empty line and the julia> prompt turns into help?>; ; and ] open a shell and the
package manager the same way. Those are three of the REPL’s five modes, and the REPL also loads helpers such as
subtypes for you, which is why a line that works at the prompt can fail with an UndefVarError when you run it as a
script.
Below you will drive each mode in a recorded session, run a script that reads its arguments and finds its own folder, give it an entry point whose return value is the exit status the shell sees, see which helpers a script must load itself, and choose the flags that make a run repeatable.
The REPL has five modes, each one key away
Start julia without a file and you are in the REPL: Read-eval-print loop: the interactive prompt that reads what you type, runs it and prints the result. Julia's opens when you start julia without a file., the read-eval-print loop: it reads a line,
runs it and prints the value. Its documentation lists five modes. Here is a real session in a terminal on Julia 1.13.1:
two lines of Julia, a help query, a shell command, a package command, and one of the REPL’s helpers:
from session import repl
# The REPL in a terminal: two Julian lines, then the help, shell and package modes, back to julia> with Backspace.
repl([
"1 + 2",
"ans * 10",
"?abs2",
";pwd",
"<backspace>",
"]activate .",
"<backspace>",
"subtypes(Integer)",
]) Output
$ julia --startup-file=no -q
julia> 1 + 2
3
julia> ans * 10
30
help?> abs2
search: abs2 abs acsc ans as
abs2(x)
Squared absolute value of x.
This can be faster than abs(x)^2, especially for complex numbers where
abs(x) requires a square root via hypot.
See also abs, conj, real.
Examples
≡≡≡≡≡≡≡≡
julia> abs2(-3)
9
julia> abs2(3.0 + 4.0im)
25.0
julia> sum(abs2, [1+2im, 3+4im]) # LinearAlgebra.norm(x)^2
30
shell> pwd
~/demo
(@v1.13) pkg> activate .
Activating new project at `~/demo`
julia> subtypes(Integer)
3-element Vector{Any}:
Bool
Signed
Unsigned
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 repl_session.py
ans held the last value, so ans * 10 gave 30. ?abs2 printed the documentation and the names it found close to
abs2, then the prompt was julia> again. ;pwd ran a shell command, and the prompt stayed shell> until
Backspace on the empty line. ]activate . made the current folder the active project, and Backspace left Pkg’s
prompt. Step through the modes and the keys between them:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The Julian mode. julia> reads Julia code, runs it and prints the value. ans holds the last value, so ans * 10 after 1 + 2 gave 30.
- Help mode. Typing ? at the start of a line turns the prompt into help?>. A name then prints its documentation and similar names. After one query the prompt is julia> again.
- Shell mode. ; turns the prompt into shell>, where a line runs as a system command, such as pwd. Shell mode stays after each command until Backspace on an empty line.
- Package mode. ] opens Pkg's own prompt, which names the active environment. activate . switched it to the current folder's project. Backspace on an empty line goes back.
- History search. Ctrl-R searches the lines you typed before. In Julia 1.13 it is a fuzzy search in the style of fzf that shows the mode each line was typed in.
- One key from julia>. Help, shell and package mode are one key from julia>, typed at the start of an empty line, and Backspace on an empty line leads back. History search opens with Ctrl-R and closes with Ctrl-C or Ctrl-G.
Key on an empty julia> line |
Prompt | Back to julia> |
|---|---|---|
? |
help?> |
by itself, after one query |
; |
shell> |
Backspace on an empty line |
] |
(@v1.13) pkg> |
Backspace on an empty line |
| Ctrl-R | history search | Ctrl-C or Ctrl-G |
New in Julia 1.13
The REPL highlights syntax as you type, adds the closing bracket or quote for you, and Ctrl-R opens a new history search in the style of fzf that shows the mode each line was typed in. Earlier versions have the same five modes.
Checkpoint
Check: The REPL's modes
A script reads its arguments from ARGS
Anything after the file name on the command line goes to the script. The command-line documentation describes the two
globals that carry it: ARGS holds the arguments as strings, and PROGRAM_FILE the script name as it was typed. This
script prints them, together with @__FILE__ and @__DIR__, the file’s own path and folder, and pwd(), the folder
it was started from:
# Where a script is, where it was started from, and what it was given.
println("PROGRAM_FILE = ", PROGRAM_FILE)
println("@__FILE__ = ", @__FILE__)
println("@__DIR__ = ", @__DIR__)
println("pwd() = ", pwd())
println("ARGS = ", ARGS) from session import files, run
# The same script started from its own folder with two arguments, from a folder below with none, and through include.
files("whereami.jl")
run("""
julia whereami.jl 3.5 "Kochi port"
mkdir data
cd data
julia ../whereami.jl
cd ..
julia -e 'include("whereami.jl")'
""") Output
$ julia whereami.jl 3.5 "Kochi port"
PROGRAM_FILE = whereami.jl
@__FILE__ = ~/demo/whereami.jl
@__DIR__ = ~/demo
pwd() = ~/demo
ARGS = ["3.5", "Kochi port"]
$ mkdir data
$ cd data
$ julia ../whereami.jl
PROGRAM_FILE = ../whereami.jl
@__FILE__ = ~/demo/whereami.jl
@__DIR__ = ~/demo
pwd() = ~/demo/data
ARGS = String[]
$ cd ..
$ julia -e 'include("whereami.jl")'
PROGRAM_FILE =
@__FILE__ = ~/demo/whereami.jl
@__DIR__ = ~/demo
pwd() = ~/demo
ARGS = String[]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 paths_session.py
The quotes around "Kochi port" made it one argument with a space inside, and both arguments arrived as strings, even
"3.5". Started from the folder below, PROGRAM_FILE became ../whereami.jl, exactly as typed, and pwd() changed,
while @__FILE__ and @__DIR__ still named the script’s own location. Run through include, as from the REPL or
another file, the code saw an empty PROGRAM_FILE, since julia was given no script, while @__FILE__ still named the
file. The Julia FAQ builds on that difference: abspath(PROGRAM_FILE) == @__FILE__ is true only in the file julia was
started with, though the FAQ advises against files that are both a script and a library.
Paths relative to where the script was started
Wrong: open("data.csv"), which looks in whatever folder the user happened to be in. Right:
open(joinpath(@__DIR__, "data.csv")), which looks next to the script wherever it was started from.
Checkpoint
Check: ARGS, PROGRAM_FILE and @__DIR__
@main turns a script into a program with an exit status
A script’s last job is to tell the shell whether it worked. Every program ends with an
exit status: The number a program hands back to the shell when it ends: 0 for success and any other value for a failure. Shells and scripts test it to decide what to do next., 0 for success and anything else for a failure, and pipelines and scheduled
jobs decide what to do next by it. Julia 1.11 added a standard entry point: The function a program starts by running. In a Julia script, a function marked with @main is called with the command-line arguments once the file has run. for this: mark
a function main with @main, and after the file has run, julia calls main(ARGS) and turns its return value into the
exit status. This script converts temperatures and refuses anything that is not a number:
# Converts temperatures given on the command line from Celsius to kelvin.
function (@main)(args)
for arg in args
c = tryparse(Float64, arg)
if c === nothing
println(stderr, "error: ", repr(arg), " is not a number")
return 2
end
println(arg, " °C = ", round(c + 273.15; digits = 2), " K")
end
return 0
end from session import files, run, use
# The entry point at work: good input, bad input and its exit status, then the same file on the 1.10 LTS.
files("c_to_k.jl")
run("""
julia c_to_k.jl 25 -40 100
!julia c_to_k.jl 25 warm 30
""")
use("1.10.12")
run("""
!julia c_to_k.jl 25
""") Output
$ julia c_to_k.jl 25 -40 100 25 °C = 298.15 K -40 °C = 233.15 K 100 °C = 373.15 K $ julia c_to_k.jl 25 warm 30 25 °C = 298.15 K error: "warm" is not a number [exit status 2] $ julia c_to_k.jl 25 ERROR: LoadError: UndefVarError: `@main` not defined in expression starting at ~/demo/c_to_k.jl:2 in expression starting at ~/demo/c_to_k.jl:2 [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 main_session.py
With good input main returned 0. With warm, it printed the error to standard error and returned 2, so the shell
saw exit status 2, and 30 was never converted. Follow the run from the shell to the status:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The command. The shell starts julia with a file name and three more words. Everything after the file name is an argument of the script.
- ARGS. julia puts the arguments in ARGS as strings, here "25", "warm" and "30", and the file name in PROGRAM_FILE.
- The file runs. The file's top level runs first. In c_to_k.jl it only defines a function and marks it with @main, so nothing is printed yet.
- main is called. Once the file has run, julia calls Main.main(ARGS), because @main marked it as the entry point. A function named main without @main would not be called.
- Bad input. "warm" is not a number, so main prints an error on standard error and returns 2 at once, before it reaches "30".
- The exit status. The value main returns becomes the exit status, 0 for success or 2 here. A shell script can test it with $?, and nothing returned means 0.
Start the tool by its own name
Make the first line of the file #!/usr/bin/env -S julia --startup-file=no and the file executable with
chmod +x c_to_k.jl, and the shell runs it as ./c_to_k.jl 25. The Julia FAQ explains why env -S is needed to pass
options on that line.
The documentation’s rule for the value: nothing means 0, and any other value must convert to a whole number, which
becomes the status. On Julia 1.10.12 the same file fails as soon as it is loaded, because @main does not exist there. For code
that must also run on 1.10, the command-line documentation suggests ending the script with
@isdefined(var"@main") ? (@main) : exit(main(ARGS)) and defining a plain main(args) function.
Two ways to write it
From Julia 1.12, function @main(args) … end also works; 1.11 needs the parentheses, function (@main)(args) … end.
1.12 also added julia -m Package args, which runs a package’s @main.
Checkpoint
Check: The @main entry point and exit status
The prompt loads helpers that a script must ask for
The InteractiveUtils documentation says the module is intended for interactive work and is loaded automatically in
interactive mode, which a script is not. It holds the helpers you reach for at the prompt: subtypes, supertypes,
methodswith, versioninfo, @which, @edit, @code_warntype and more. The REPL session above called
subtypes(Integer) without a second thought. Run the same line as a script:
# subtypes is one of the REPL's helpers.
println(subtypes(Integer)) from session import files, run
# A line that works at the julia> prompt, run as a script, then fixed by loading the module it comes from.
files("helpers.jl")
run("""
!julia helpers.jl
julia -e 'using InteractiveUtils; println(subtypes(Integer))'
""") Output
$ julia helpers.jl ERROR: LoadError: UndefVarError: `subtypes` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] top-level scope @ ~/demo/helpers.jl:2 [2] include(mod::Module, _path::String) @ Base ./Base.jl:309 [3] exec_options(opts::Base.JLOptions) @ Base ./client.jl:344 [4] _start() @ Base ./client.jl:577 in expression starting at ~/demo/helpers.jl:2 [exit status 1] $ julia -e 'using InteractiveUtils; println(subtypes(Integer))' Any[Bool, Signed, Unsigned]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 helpers_session.py
As a script it stopped with UndefVarError: subtypes not defined in Main, and the suggestion only says to check for
missing imports. After using InteractiveUtils the same call printed Any[Bool, Signed, Unsigned], the list the REPL
had shown as three lines. A script that runs unattended should load every module it uses, InteractiveUtils included.
Key idea
The REPL loads InteractiveUtils for you and a script does not, so test a script the way it will run, as
julia file.jl in a fresh process, before you rely on it.
Checkpoint
Check: REPL helpers in scripts
Start-up flags make a run repeatable
Before it runs anything, julia reads a startup file: A Julia file, config/startup.jl in the depot, that julia runs before anything else unless it is started with --startup-file=no., config/startup.jl in your
depot, if there is one. That is handy at the prompt and a trap for scripts: code there runs before every script and can
hide a missing using. A few flags take control of what a run sees:
from session import run
# A startup file that every run reads, and the flags that leave it out, set threads and choose a project.
run("""
mkdir -p ~/.julia/config
echo 'println("startup.jl ran")' > ~/.julia/config/startup.jl
julia -e 'println(1 + 1)'
julia --startup-file=no -e 'println(1 + 1)'
julia --startup-file=no -t 4 -e 'println(Threads.nthreads(), " default threads")'
julia --startup-file=no -e 'using Pkg; Pkg.generate("Survey")'
cd Survey
julia --startup-file=no --project=@. -e 'println(Base.active_project())'
cd ..
julia --startup-file=no --project=@. -e 'println(Base.active_project())'
""") Output
$ mkdir -p ~/.julia/config
$ echo 'println("startup.jl ran")' > ~/.julia/config/startup.jl
$ julia -e 'println(1 + 1)'
startup.jl ran
2
$ julia --startup-file=no -e 'println(1 + 1)'
2
$ julia --startup-file=no -t 4 -e 'println(Threads.nthreads(), " default threads")'
4 default threads
$ julia --startup-file=no -e 'using Pkg; Pkg.generate("Survey")'
Generating project Survey:
Survey/Project.toml
Survey/src/Survey.jl
$ cd Survey
$ julia --startup-file=no --project=@. -e 'println(Base.active_project())'
~/demo/Survey/Project.toml
$ cd ..
$ julia --startup-file=no --project=@. -e 'println(Base.active_project())'
~/.julia/environments/v1.13/Project.toml
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 flags_session.py
startup.jl ran appeared until --startup-file=no switched the file off. -t 4 gave four threads in the default
pool. --project=@. searched up from the current folder for a Project.toml: inside Survey it found that project,
and in the folder above, which has none, it fell back to the default environment, @v1.13. For a run that anyone can
repeat, write julia --startup-file=no --project=@. script.jl: no personal setup, and the project’s own packages.
| Flag | What it does |
|---|---|
--startup-file=no |
Skips config/startup.jl |
--project=@. |
Uses the nearest Project.toml above |
-t 4, -t auto |
Sets the number of threads |
-e 'code', -E 'code' |
Runs code, or runs it and prints the value |
-q |
Starts the REPL without the banner |
The 1.13 release post reports that 1.13 starts about 20 % faster than 1.12, which adds up when a script is started many times.
Checkpoint
Check: Start-up flags
Practise: the REPL, scripts and entry points
Each question below quotes a prompt, a path or an exit status from the sessions. Your weakest idea comes up first, and a wrong pick names the slip behind it:
Practice that adapts to you
Practise this lesson
12 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to 2–5 years): besides the Julian mode, which modes does the Julia REPL have, and how do you enter and leave each?
The Julian mode, with the julia> prompt, runs Julia code. Typing ? at the start of a line enters help mode,
help?>, which shows documentation and searches for similar names; it returns to julia> after one query. ; enters
shell mode, shell>, which runs system commands and stays until you press Backspace on an empty line. ] enters the
package mode, whose prompt names the active environment, such as (@v1.13) pkg>, and also stays until Backspace.
Ctrl-R opens history search, which in Julia 1.13 is a fuzzy search in the style of fzf. Everything the modes do has a
function form for scripts: @doc, run and the Pkg functions.
Two harder questions follow, about a script that only works in the REPL and about designing a command-line tool. Note your answer first, then open each first step:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
A script that works when its lines are pasted into the REPL fails in a continuous integration job with UndefVarError:
subtypesnot defined inMain. Explain why, fix it, and say how you would stop similar surprises. -
Mid to senior
Design a small Julia command-line tool that converts numbers given as arguments and is called from shell pipelines. Which entry point, output streams and exit codes would you use, and how would you test them?
Key takeaways
- The REPL has five modes: Julian, then help, shell and package mode, each entered with one key at the start of a line, and history search, opened with Ctrl-R; shell and package mode stay until Backspace.
- A script gets its arguments as strings in
ARGS, and@__DIR__names its own folder, so build file paths from@__DIR__rather than from the folder it was started in. @mainmarks the entry point thatjuliacalls after the file has run, and its return value becomes the exit status; Julia 1.10 has no@main.- InteractiveUtils, with
subtypes,versioninfoand@which, is loaded only in interactive sessions, so a script must load it itself. --startup-file=no,--project=@.and-tmake a run independent of personal setup and explicit about its project and threads.
Terms in this lesson
- entry point
- The function a program starts by running. In a Julia script, a function marked with @main is called with the command-line arguments once the file has run.
- exit status
- The number a program hands back to the shell when it ends: 0 for success and any other value for a failure. Shells and scripts test it to decide what to do next.
- REPL
- Read-eval-print loop: the interactive prompt that reads what you type, runs it and prints the result. Julia's opens when you start julia without a file.
- startup file
- A Julia file, config/startup.jl in the depot, that julia runs before anything else unless it is started with --startup-file=no.
References
- The Julia REPL (The Julia Project)
- Command-line interface (The Julia Project)
- Essentials: ARGS, PROGRAM_FILE, @__DIR__ and @main (The Julia Project)
- InteractiveUtils (The Julia Project)
- Environment variables (The Julia Project)
- Frequently asked questions: scripts (The Julia Project)
- Julia 1.11 release notes (The Julia Project)
- Julia 1.12 release notes (The Julia Project)
- Julia 1.13 release notes (The Julia Project)
- Julia 1.13 highlights (The Julia Project)
Report a problem with this lesson
Kept only in this browser. Your Learn progress