Files
literate-dotfiles/install.org
T

836 lines
21 KiB
Org Mode

#+TITLE: Installation
#+SETUPFILE: ./setup/org-setup-file.org
* Overview
This file documents how to set up a fresh Arch Linux installation.
It tangles several install scripts:
- =~/install-main.sh= — core desktop environment and tools
- =~/install-desktop.sh= — tangle and reload the full desktop environment (i3, polybar, sxhkd, picom, rofi, kitty, bash, X11)
- =~/install-mail.sh= — mail setup (mbsync, msmtp, neomutt, mu)
- =~/install-calendar.sh= — calendar and contacts (vdirsyncer, khal, khard)
- =~/install-music.sh= — music server and client (mopidy, ncmpcpp)
- =~/install-latex.sh= — LaTeX toolchain (texlive, biber)
- =~/install-laptop.sh= — laptop-specific tools (tlp, powertop, thermald)
All scripts use =paru -S --needed --noconfirm --noconfirm= to be idempotent (safe to re-run) and non-interactive.
* Prerequisites
** =git=
#+begin_src bash :tangle no
sudo pacman -S git
#+end_src
[[file:git.org][Git]] configuration: =~/.gitconfig=, =~/.gitignore_global=
** =paru= - AUR Helper
Must be installed first before running any script:
#+begin_src bash :tangle no
mkdir -p ~/.local/soft && cd ~/.local/soft
sudo pacman -S --needed base-devel
git clone https://aur.archlinux.org/paru.git
cd paru
makepkg -si
#+end_src
After that, the =paru= folder may be removed.
* Desktop Install Script
:PROPERTIES:
:header-args:bash: :tangle scripts/install-desktop.sh :shebang "#!/bin/bash" :mkdirp yes
:END:
Installs packages and tangles configs for the core desktop environment.
Run this after =paru= is available to get a working, familiar environment fast.
#+begin_src bash
set -euo pipefail
DOTFILES="${HOME}/.config/literate-dotfiles"
tangle() {
emacs --batch -l org --eval "(org-babel-tangle-file \"${DOTFILES}/$1\")"
}
#+end_src
** Packages
#+begin_src bash
echo "==> Desktop packages"
paru -S --needed --noconfirm \
i3-wm \
sxhkd \
polybar \
picom \
dunst \
rofi rofi-calc dmenu \
kitty \
bash bash-completion \
zoxide \
xorg-xrandr arandr \
xautocfg \
feh xwallpaper
#+end_src
** Tangle Configs
#+begin_src bash
echo "==> Tangling desktop configs"
tangle "xconfig.org" # ~/.Xresources, ~/.xprofile, ~/.config/X11/xinitrc
tangle "bash.org" # ~/.bashrc, ~/.bash_profile, ~/.profile
tangle "kitty.org" # ~/.config/kitty/kitty.conf
tangle "i3.org" # ~/.config/i3/config
tangle "sxhkd.org" # ~/.config/sxhkd/sxhkdrc
tangle "polybar.org" # ~/.config/polybar/config.ini + scripts
tangle "compositor.org" # ~/.config/picom/picom.conf
tangle "rofi.org" # ~/.config/rofi/config.rasi
tangle "binaries.org" # ~/.local/bin/* scripts
tangle "binaries-private.org" # ~/.local/bin/* scripts
#+end_src
** Reload
If already running inside i3, restart in place — no need to log out:
#+begin_src bash
echo "==> Reloading desktop"
i3-msg restart 2>/dev/null || echo " (i3 not running, config will apply on next login)"
#+end_src
* Main Install Script
:PROPERTIES:
:header-args:bash: :tangle scripts/install-main.sh :shebang "#!/bin/bash" :mkdirp yes
:END:
#+begin_src bash
set -euo pipefail
DOTFILES="${HOME}/.config/literate-dotfiles"
tangle() {
emacs --batch -l org --eval "(org-babel-tangle-file \"${DOTFILES}/$1\")"
}
#+end_src
** Shell and Terminal
#+begin_src bash
echo "==> Shell and Terminal"
paru -S --needed --noconfirm \
bash bash-completion \
kitty \
tmux
#+end_src
[[file:bash.org][Bash]] configuration: =~/.bashrc=, =~/.bash_profile=, =~/.config/bash/=, =~/.profile=
** Fonts
#+begin_src bash
echo "==> Fonts"
paru -S --needed --noconfirm \
ttf-hack-nerd \
otf-libertinus \
ttf-sourcecodepro-nerd \
adobe-source-code-pro-fonts \
noto-fonts-emoji
#+end_src
Default fonts: Monospace/Serif/Sans = =Hack Nerd Font=.
** Text Editors
#+begin_src bash
echo "==> Text Editors"
paru -S --needed --noconfirm \
neovim python-pynvim nodejs-neovim \
emacs \
aspell aspell-en aspell-fr
#+end_src
Neovim config: [[file:vim.org][vim.org]]
Install Doom Emacs framework:
#+begin_src bash :tangle no
git clone --depth 1 https://github.com/hlissner/doom-emacs ~/.emacs.d
~/.emacs.d/bin/doom install
#+end_src
Emacs config: [[./doom.org][doom.org]]
** Security: GnuPG and Pass
#+begin_src bash
echo "==> GnuPG and Pass"
paru -S --needed --noconfirm \
gnupg gnome-keyring \
pass rofi-pass pass-git-helper
#+end_src
Generate a GPG key:
#+begin_src bash :tangle no
mkdir -p ~/.local/share/gnupg
gpg-agent --daemon
gpg --full-gen-key
#+end_src
To cache the passphrase, edit =~/.local/share/gnupg/gpg-agent.conf=:
#+begin_src conf :tangle no
default-cache-ttl 60480000
max-cache-ttl 60480000
#+end_src
Initialize the password store:
#+begin_src bash :tangle no
pass git init
pass git remote add origin https://github.com/tdehaeze/pass.git
#+end_src
Or =cd ~/.local/share= and =git clone https://git.tdehaeze.xyz/tdehaeze/pass=
*** Setup password store on new computer (B)
**** On computer B (new)
1. Generate key:
#+begin_src bash :tangle no
gpg --full-generate-key
#+end_src
Remember the fingerprint (e.g., ~FBDA20B263C0C74A462D06F417F6BC48C15759C1~)
2. Export public key:
#+begin_src bash :tangle no
gpg --export FBDA20B263C0C74A462D06F417F6BC48C15759C1 > B.pub
#+end_src
3. Copy ~B.pub~ to computer A (USB, scp, etc.)
**** On computer A (existing)
4. Import B's public key:
#+begin_src bash :tangle no
gpg --import B.pub
#+end_src
5. Trust the key:
#+begin_src bash :tangle no
gpg --edit-key FBDA20B263C0C74A462D06F417F6BC48C15759C1
#+end_src
#+begin_example
trust
5
save
#+end_example
6. Find A's fingerprint:
#+begin_src bash :tangle no
gpg --fingerprint dehaeze.thomas@gmail.com
#+end_src
Note the fingerprint of the key used for pass (e.g., ~453D80FAF9BBDF56CB4167699C30DC293122F6C6~)
7. Re-encrypt store to both keys:
#+begin_src bash :tangle no
pass init "453D80FAF9BBDF56CB4167699C30DC293122F6C6" "FBDA20B263C0C74A462D06F417F6BC48C15759C1"
#+end_src
8. Export A's public key:
#+begin_src bash :tangle no
gpg --export 453D80FAF9BBDF56CB4167699C30DC293122F6C6 > A.pub
#+end_src
9. Push password store changes:
#+begin_src bash :tangle no
pass git push
#+end_src
10. Copy ~A.pub~ to computer B
**** Back on computer B
11. Import A's public key:
#+begin_src bash :tangle no
gpg --import A.pub
#+end_src
12. Trust the key:
#+begin_src bash :tangle no
gpg --edit-key 453D80FAF9BBDF56CB4167699C30DC293122F6C6
#+end_src
#+begin_example
trust
5
save
#+end_example
13. Clone password store (if using git):
#+begin_src bash :tangle no
git clone <your-pass-git-url> ~/.password-store
cd ~/.password-store
pass git pull
#+end_src
14. Verify:
#+begin_src bash :tangle no
pass show some-password
pass insert test/new-password
pass show test/new-password
#+end_src
** SSH
Generate a new SSH key:
#+begin_src bash :tangle no
ssh-keygen -t ed25519 -C "dehaeze.thomas@gmail.com"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
#+end_src
** Window Manager and Desktop
#+begin_src bash
echo "==> Window Manager and Desktop"
paru -S --needed --noconfirm \
i3-wm \
sxhkd \
polybar \
picom \
dunst \
rofi rofi-calc dmenu \
xautocfg \
xorg-xrandr arandr
#+end_src
- i3 config: [[file:i3.org][i3.org]]
- sxhkd config: [[file:sxhkd.org][sxhkd.org]]
- polybar config: [[file:polybar.org][polybar.org]]
- compositor config: [[file:compositor.org][compositor.org]]
- notifications config: [[file:notifications.org][notifications.org]]
** File Manager
#+begin_src bash
echo "==> File Manager"
paru -S --needed --noconfirm yazi
#+end_src
Yazi config: [[file:yazi.org][yazi.org]]
** Terminal Utilities
#+begin_src bash
echo "==> Terminal Utilities"
paru -S --needed --noconfirm \
fd ripgrep fzf \
xclip xsel \
atool unzip \
trash-cli \
man-db \
fastfetch
#+end_src
** Browser
#+begin_src bash
echo "==> Browser"
paru -S --needed --noconfirm \
qutebrowser python-adblock pdfjs \
firefox-developer-edition passff-host
#+end_src
Qutebrowser config: [[file:qutebrowser.org][qutebrowser.org]]
Spell checking for qutebrowser:
#+begin_src bash :tangle no
/usr/share/qutebrowser/scripts/dictcli.py install fr-FR
/usr/share/qutebrowser/scripts/dictcli.py install en-US
#+end_src
** Media
#+begin_src bash
echo "==> Media"
paru -S --needed --noconfirm \
mpv \
jellyfin-tui \
nsxiv \
tesseract tesseract-data-eng tesseract-data-fra tesseract-data-osd \
zathura zathura-pdf-mupdf zathura-djvu zathura-ps zathura-cb \
pdfpc \
f3d \
gst-plugins-ugly gst-plugins-good gst-plugins-base-libs gst-plugins-base gst-plugins-bad gst-libav
#+end_src
** Sound (PipeWire)
Core PipeWire packages (required):
#+begin_src bash
echo "==> PipeWire"
paru -S --needed --noconfirm \
pipewire pipewire-alsa pipewire-audio pipewire-jack pipewire-pulse \
wireplumber
#+end_src
Optional GUI control (install manually when available):
#+begin_src bash :tangle no
echo "==> PipeWire GUI (optional)"
paru -S --needed --noconfirm pwvucontrol || echo "pwvucontrol failed, skipping"
#+end_src
Enable PipeWire services:
#+begin_src bash :tangle no
systemctl --user enable --now pipewire.socket
systemctl --user enable --now pipewire-pulse.socket
systemctl --user enable --now wireplumber.service
#+end_src
** PDF and Image Utilities
#+begin_src bash
echo "==> PDF and Image Utilities"
paru -S --needed --noconfirm \
pdf2svg qpdf pdfarranger \
imagemagick \
maim flameshot \
unclutter \
poppler
#+end_src
** Office
#+begin_src bash
echo "==> Office"
paru -S --needed --noconfirm \
onlyoffice-bin \
libreoffice-fresh libreoffice-fresh-fr \
inkscape
#+end_src
** System Utilities
#+begin_src bash
echo "==> System Utilities"
paru -S --needed --noconfirm \
udiskie \
blueman \
sshfs \
xwallpaper \
sof-firmware \
usbutils \
xautolock \
npm \
xorg-xkill \
syncthing \
wireguard-tools \
gotify-dunst-git \
gomuks \
yt-dlp \
freerdp \
xautocfg \
python ipython python-pip
#+end_src
Enable Syncthing and Gotify-dunst:
#+begin_src bash :tangle no
systemctl --user enable --now syncthing
systemctl --user enable --now gotify-dunst
systemctl --user enable --now xautocfg
#+end_src
** Misc Utilities
#+begin_src bash
echo "==> Misc Utilities"
paru -S --needed --noconfirm \
xwallpaper \
highlight-pointer-git \
mpd
#+end_src
** SSH Keys for Tunnels
Use a single SSH key for all hosts (matches =~/.ssh/config=).
#+begin_src bash
KEY_FILE="$HOME/.ssh/id_ed25519"
HOMELAB_OK=1
ESRF_OK=1
# Generate main SSH key if it doesn't exist
if [ ! -f "${KEY_FILE}" ]; then
echo ""
echo "==> Generating main SSH key"
read -p "Generate new SSH key at ${KEY_FILE}? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
ssh-keygen -t ed25519 -C "thomas@$(hostname)" -f "${KEY_FILE}" -N ""
else
echo "Skipping SSH key generation"
fi
fi
# Deploy key to a remote host if needed
deploy_ssh_key() {
local host_name="$1"
local remote_host="$2"
local remote_port="${3:-22}"
echo ""
echo "==> Checking SSH key for ${host_name}"
# Check if we can connect without password
if ssh -o BatchMode=yes -o ConnectTimeout=5 -p "${remote_port}" "${remote_host}" exit 2>/dev/null; then
echo "SSH key for ${host_name} is already deployed ✓"
return 0
fi
# Prompt to deploy key
read -p "Deploy SSH key to ${remote_host}? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
ssh-copy-id -i "${KEY_FILE}.pub" -p "${remote_port}" "${remote_host}"
return 0
else
echo "Skipping ${host_name} key deployment"
return 1
fi
}
# Setup homelab
deploy_ssh_key "homelab" "homelab" 22
HOMELAB_OK=$?
# Setup ESRF
deploy_ssh_key "esrf" "dehaeze@firewall.esrf.fr" 8022
ESRF_OK=$?
#+end_src
** Systemd Services
#+begin_src bash
echo "==> Tangling systemd configs"
tangle "systemd.org"
#+end_src
Enable custom =systemd= services:
#+begin_src bash
echo "==> Enabling systemd services"
if [ $HOMELAB_OK -eq 0 ]; then
systemctl --user enable --now homelab-tunnel
else
echo " Skipping homelab-tunnel (SSH key not configured)"
fi
if [ $ESRF_OK -eq 0 ]; then
systemctl --user enable --now esrf-tunnel
else
echo " Skipping esrf-tunnel (SSH key not configured)"
fi
systemctl --user enable --now syncthing
#+end_src
** After Install
Enable TRIM (SSD only):
#+begin_src bash :tangle no
sudo systemctl enable --now fstrim.timer
#+end_src
** Default Applications
#+begin_src bash :tangle no
xdg-mime default qutebrowser.desktop x-scheme-handler/http
xdg-mime default qutebrowser.desktop x-scheme-handler/https
#+end_src
* Specific Applications
:PROPERTIES:
:header-args:bash: :tangle scripts/install-gui.sh :shebang "#!/bin/bash" :mkdirp yes
:END:
#+begin_src bash
set -euo pipefail
#+end_src
#+begin_src bash
echo "==> GUI applications"
paru -S --needed --noconfirm \
inkscape \
freecad \
bambustudio-bin \
pcmanfm \
jellyfin-desktop \
xournalpp \
anydesk-bin
#+end_src
[[file:bash.org][Bash]] configuration: =~/.bashrc=, =~/.bash_profile=, =~/.config/bash/=, =~/.profile=
* LaTeX Install Script
:PROPERTIES:
:header-args:bash: :tangle scripts/install-latex.sh :shebang "#!/bin/bash" :mkdirp yes
:END:
#+begin_src bash
set -euo pipefail
#+end_src
** Packages
#+begin_src bash
echo "==> LaTeX packages"
paru -S --needed --noconfirm \
texlive-most tllocalmgr-git \
biber
#+end_src
Custom packages/classes go in =~/.local/share/texmf/tex/latex/local/=.
After adding new packages, run =sudo texhash=.
* Laptop Install Script
:PROPERTIES:
:header-args:bash: :tangle scripts/install-laptop.sh :shebang "#!/bin/bash" :mkdirp yes
:END:
#+begin_src bash
set -euo pipefail
#+end_src
** Power Management
#+begin_src bash
echo "==> Laptop power management"
paru -S --needed --noconfirm \
powertop \
tlp \
thermald
#+end_src
#+begin_src bash
sudo systemctl enable --now tlp
sudo systemctl enable --now tlp-sleep
sudo systemctl enable --now thermald
sudo tlp start
#+end_src
** Lid Open/Close
Configure in =/etc/systemd/logind.conf=.
See [[https://wiki.archlinux.org/index.php/Power_management][Arch Wiki: Power management]].
* Matlab
Matlab is installed manually via [[https://github.com/mathworks-ref-arch/matlab-dockerfile][MPM]]:
#+begin_src bash :eval no :tangle no
wget https://www.mathworks.com/mpm/glnxa64/mpm
chmod +x ./mpm
./mpm install --inputfile=./mpm_input_r2024b.txt
#+end_src
The MPM input file:
#+begin_src conf :tangle ~/Downloads/mpm_input_r2024b.txt
########################################################################
## Configuration File for Installing or Downloading R2024b MathWorks Products
########################################################################
destinationFolder=/home/thomas/.local/soft/Matlab/R2024b
########################################################################
## PRODUCTS
########################################################################
#product.5G_Toolbox
#product.AUTOSAR_Blockset
#product.Aerospace_Blockset
#product.Aerospace_Toolbox
#product.Antenna_Toolbox
#product.Audio_Toolbox
#product.Automated_Driving_Toolbox
#product.Bioinformatics_Toolbox
#product.Bluetooth_Toolbox
#product.C2000_Microcontroller_Blockset
#product.Communications_Toolbox
#product.Computer_Vision_Toolbox
product.Control_System_Toolbox
product.Curve_Fitting_Toolbox
#product.DDS_Blockset
#product.DSP_HDL_Toolbox
#product.DSP_System_Toolbox
#product.Data_Acquisition_Toolbox
#product.Database_Toolbox
#product.Datafeed_Toolbox
#product.Deep_Learning_HDL_Toolbox
#product.Deep_Learning_Toolbox
#product.Econometrics_Toolbox
#product.Embedded_Coder
#product.Filter_Design_HDL_Coder
#product.Financial_Instruments_Toolbox
#product.Financial_Toolbox
#product.Fixed-Point_Designer
#product.Fuzzy_Logic_Toolbox
#product.GPU_Coder
#product.Global_Optimization_Toolbox
#product.HDL_Coder
#product.HDL_Verifier
#product.Image_Acquisition_Toolbox
#product.Image_Processing_Toolbox
#product.Industrial_Communication_Toolbox
#product.Instrument_Control_Toolbox
#product.LTE_Toolbox
#product.Lidar_Toolbox
product.MATLAB
product.MATLAB_Coder
product.MATLAB_Compiler
#product.MATLAB_Compiler_SDK
product.MATLAB_Parallel_Server
#product.MATLAB_Production_Server
#product.MATLAB_Report_Generator
#product.MATLAB_Test
#product.MATLAB_Web_App_Server
#product.Mapping_Toolbox
#product.Medical_Imaging_Toolbox
#product.Mixed-Signal_Blockset
#product.Model_Predictive_Control_Toolbox
#product.Model-Based_Calibration_Toolbox
#product.Motor_Control_Blockset
#product.Navigation_Toolbox
product.Optimization_Toolbox
product.Parallel_Computing_Toolbox
#product.Partial_Differential_Equation_Toolbox
#product.Phased_Array_System_Toolbox
#product.Polyspace_Bug_Finder
#product.Polyspace_Bug_Finder_Server
#product.Polyspace_Code_Prover
#product.Polyspace_Code_Prover_Server
#product.Polyspace_Test
#product.Powertrain_Blockset
#product.Predictive_Maintenance_Toolbox
#product.RF_Blockset
#product.RF_PCB_Toolbox
#product.RF_Toolbox
#product.ROS_Toolbox
#product.Radar_Toolbox
#product.Reinforcement_Learning_Toolbox
#product.Requirements_Toolbox
#product.Risk_Management_Toolbox
#product.Robotics_System_Toolbox
product.Robust_Control_Toolbox
#product.Satellite_Communications_Toolbox
#product.Sensor_Fusion_and_Tracking_Toolbox
#product.SerDes_Toolbox
#product.Signal_Integrity_Toolbox
product.Signal_Processing_Toolbox
#product.SimBiology
#product.SimEvents
product.Simscape
#product.Simscape_Battery
#product.Simscape_Driveline
product.Simscape_Electrical
#product.Simscape_Fluids
product.Simscape_Multibody
product.Simulink
#product.Simulink_3D_Animation
#product.Simulink_Check
product.Simulink_Coder
product.Simulink_Compiler
product.Simulink_Control_Design
#product.Simulink_Coverage
#product.Simulink_Design_Optimization
#product.Simulink_Design_Verifier
product.Simulink_Desktop_Real-Time
#product.Simulink_Fault_Analyzer
#product.Simulink_PLC_Coder
product.Simulink_Real-Time
#product.Simulink_Report_Generator
#product.Simulink_Test
#product.SoC_Blockset
#product.Spreadsheet_Link
#product.Stateflow
#product.Statistics_and_Machine_Learning_Toolbox
product.Symbolic_Math_Toolbox
#product.System_Composer
product.System_Identification_Toolbox
#product.Text_Analytics_Toolbox
#product.UAV_Toolbox
#product.Vehicle_Dynamics_Blockset
#product.Vehicle_Network_Toolbox
#product.Vision_HDL_Toolbox
#product.WLAN_Toolbox
#product.Wavelet_Toolbox
#product.Wireless_HDL_Toolbox
#product.Wireless_Testbench
########################################################################
## CHECKSUM
########################################################################
?checksum=UjIwMjRi
#+end_src
Add Matlab binaries to PATH:
#+begin_src bash :eval no :tangle no
ln -s ~/.local/soft/Matlab/R2024b/bin/mex ~/.local/bin
ln -s ~/.local/soft/Matlab/R2024b/bin/matlab ~/.local/bin
ln -s ~/.local/soft/Matlab/R2024b/bin/glnxa64/mlint ~/.local/bin
#+end_src
Fix for gnutls crash on Arch (R2024b), see [[https://bbs.archlinux.org/viewtopic.php?id=306939][bbs.archlinux.org #10]]:
#+begin_src bash :eval no :tangle no
MATLABPATH=$HOME/.local/soft/Matlab/R2024B/bin/glnxa64/
GNUTLSPATH=/tmp/gnutls-3.8.9-1-x86_64.pkg
wget https://archive.archlinux.org/packages/g/gnutls/gnutls-3.8.9-1-x86_64.pkg.tar.zst
mkdir -p "${GNUTLSPATH}"
tar -xvf gnutls-3.8.9-1-x86_64.pkg.tar.zst -C ${GNUTLSPATH}
mkdir "${MATLABPATH}"/gnutls
cp -a "${GNUTLSPATH}"/usr/lib/* "${MATLABPATH}"/gnutls/
cd "${MATLABPATH}"
ln -s gnutls/* ./
#+end_src
See also the [[https://wiki.archlinux.org/title/MATLAB][Arch Wiki]] for other issues.
* Useful Reference
** Readline shortcuts
| Keyboard | Description |
|------------+--------------------------------|
| =Ctrl+a= | Move to start of line |
| =Ctrl+e= | Move to end of line |
| =Alt+b/f= | Move word left/right |
| =Ctrl+u= | Cut to start of line |
| =Ctrl+k= | Cut to end of line |
| =Ctrl+w= | Cut word before cursor |
| =Ctrl+y= | Paste |
| =Ctrl+r= | Reverse history search |
| =Alt+r= | Restore original line |
** Archive and Extract
Use =apack= / =aunpack= (from =atool=):
| Command | Usage |
|-------------------------------+--------------------------------|
| =aunpack foobar.tar.gz= | Extract all files from archive |
| =apack myarchive.zip foo bar= | Create zip archive |
** Hardware
| =dmesg= | Detected hardware and boot messages |
| =lshw= | Display information on hardware |
| =lsusb -tv= | Show USB devices |
** Network
| =ip addr show= | Show all network interfaces |
| =netstat -tulp= | List all active ports |
* Resources
** Dotfiles
- https://github.com/LukeSmithxyz/voidrice
- https://github.com/wincent/wincent
- https://github.com/korolr/dotfiles
- https://github.com/addy-dclxvi/almighty-dotfiles
** Linux Software Lists
- [[https://github.com/k4m4/terminals-are-sexy][terminals-are-sexy]]
- [[https://wiki.archlinux.org/index.php/List_of_applications][Arch Wiki: List of applications]]
- https://github.com/jlevy/the-art-of-command-line