Photo by Matt Duncan on Unsplash
The Path Not Taken
A quick primer on dealing with file paths in Julia
No matter what kind of code base you end up working on, there's always some logic that has to deal with file paths. Sometimes we need to know the location of a certain file or a certain folder. On other occasions, we might need to create a path to a folder. Also, it's usually considered a bad practice to use hard-coded paths in your code, especially when it's going to be deployed elsewhere. For such needs, you should rely on programmatically generating relative paths.
Julia provides a lot of built-in functionality to interact with the host filesystem. Let's have a look at some of them with useful examples.
Print working directory
julia> pwd()
"/home/vikas/Downloads"
Get the contents of a directory
julia> readdir(pwd())
16-element Vector{String}:
"0001-pinctrl-sunrisepoint-Add-missing-Interrupt-Status-re.patch"
"20230423_150150.jpg"
"20230423_170112.jpg"
"20230425_222307.jpg"
"20230428_233929.jpg"
"BIOS_IMG.rcv"
"DALL·E 2023-05-01 20.53.45 - An" ⋯ 92 bytes ⋯ "and some code on the screen.png"
"DALL·E 2023-05-01 23.03.10 - An" ⋯ 56 bytes ⋯ "track with fire behind them.png"
...
Notice that readdir()
returns an array of strings. This immediately allows us to perform additional operations such as filtering for a given file type:
julia> filter(x -> endswith(x, ".jpg"), readdir(pwd()))
4-element Vector{String}:
"20230423_150150.jpg"
"20230423_170112.jpg"
"20230425_222307.jpg"
"20230428_233929.jpg"
Using the join
keyword, full paths to the files can be determined:
julia> readdir(join = true)
17-element Vector{String}:
"/home/vikas/Downloads/0001-pinc" ⋯ 23 bytes ⋯ "ssing-Interrupt-Status-re.patch"
"/home/vikas/Downloads/20230423_150150.jpg"
"/home/vikas/Downloads/20230423_170112.jpg"
"/home/vikas/Downloads/20230425_222307.jpg"
"/home/vikas/Downloads/20230428_233929.jpg"
"/home/vikas/Downloads/BIOS_IMG.rcv"
...
Check if a file exists
julia> isfile("julia-logo-color.png")
true
Check if a directory exists
julia> isdir("amazing_blog")
false
julia> mkdir("amazing_blog")
"amazing_blog"
julia> isdir("amazing_blog")
true
Get directory name from path
julia> current_location = pwd()
"/home/vikas/Downloads"
julia> dirname(current_location)
"/home/vikas"
julia> basename(current_location)
"Downloads"
Create a deep nested directory
mkpath
can be used to create intermediate folders to a deeply nested path as shown below:
julia> mkpath(["amazing_blog", "folder_1", "folder_11", "folder_111"] |> joinpath)
"amazing_blog/folder_1/folder_11/folder_111"
julia> isdir("amazing_blog/folder_1/folder_11/folder_111")
true
Split a path
Sometimes, it's needed to extract some useful information from a given path. For such cases, splitpath
is your friend:
julia> splitpath(current_location)
4-element Vector{String}:
"/"
"home"
"vikas"
"Downloads"
Join a path
julia> parts = splitpath(current_location)
4-element Vector{String}:
"/"
"home"
"vikas"
"Downloads"
julia> joinpath(parts)
"/home/vikas/Downloads"
This is extremely useful since the path separator is determined based on the operating system (Linux or Windows). In case you do need to specify a path in a code base that will be used both on Linux and Windows, it's way more robust to make use of joinpath
.
Get current directory w.r.t. a file
The @__DIR__
macro can be used to determine the path to a file from where it's called. This is useful to safely add a relative path to a file or folder for which you are certain that the location would remain unchanged.
julia> pwd()
"/home/vikas/Downloads/amazing_blog"
julia> joinpath(@__DIR__, "..", "20230425_222307.jpg")
"/home/vikas/Downloads/amazing_blog/../20230425_222307.jpg"
julia> joinpath(@__DIR__, "..", "20230425_222307.jpg") |> isfile
true
Traversing through a directory tree
Using walkdir
, we can surf through all the files within a folder, and perform operations such as finding files that match a certain name. For example, let's find files that have "README" in the name:
julia> pwd()
"/home/vikas"
julia> for (root, dirs, files) in walkdir("Desktop")
for file in files
if occursin("READ", file)
joinpath(root, file) |> println
end
end
end
Desktop/Julia_release/julia-1.8.5/share/julia/stdlib/v1.8/ArgTools/README.md
Desktop/Julia_release/julia-1.8.5/share/julia/stdlib/v1.8/Downloads/README.md
Desktop/Julia_release/julia-1.8.5/share/julia/stdlib/v1.8/LibCURL/README.md
...
Conclusion
Julia has excellent support for handling paths to files and folders. The above examples are some of my most routinely used functions. A more comprehensive list can be found in the official documentation. I hope you learned something new today. Don't forget to bookmark this post and share it with fellow Julia nerds.