Herbruikbare Bash-bibliotheken bouwen en sourcen
Organiseer gedeelde helpers in sourcebare .sh-bibliotheekbestanden met include guards en genamesp acede functieprefixen.
Herbruikbare Bash-bibliotheken bouwen en sourcen is een gratis De Linux-opdrachtregel en Bash-scripting beheersen-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject De Linux-opdrachtregel en Bash-scripting beheersen. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.
Wat is een Bash-bibliotheek?
In software-engineering is een bibliotheek een verzameling herbruikbare functies die door meerdere programma's kan worden gedeeld. Bash ondersteunt hetzelfde concept via shellscripts die kunnen worden ingeladen.
In plaats van hulpfuncties naar elk script te kopiëren, plaats je ze in een speciaal .sh-bestand en laad je dat met de opdracht source (of de verkorte vorm .). Alle functies, variabelen of aliassen die in dat bestand zijn gedefinieerd, worden beschikbaar in de huidige shellsessie van het aanroepende script.
- Bevordert het DRY-principe (Don't Repeat Yourself)
- Centraliseert foutoplossingen — los een fout één keer op en alle aanroepers profiteren ervan
- Maakt afzonderlijke scripts korter en gemakkelijker te lezen
- Maakt consistentie binnen het team mogelijk voor logboekregistratie, foutafhandeling en hulplogica
Een goed gestructureerd Bash-project heeft meestal een map lib/ met deze gedeelde bestanden, volgens de conventies van talen op een hoger abstractieniveau.
De opdracht source en de puntoperator
Er zijn twee gelijkwaardige manieren om een bibliotheekbestand in de huidige shellomgeving te laden:
source /path/to/lib.sh— de expliciete, leesbare vorm. /path/to/lib.sh— de POSIX-compatibele verkorte vorm
Beide voeren het bestand uit in het huidige shellproces, niet in een subshell. Daardoor wordt elke functie en variabele die erin is gedefinieerd onmiddellijk na de aanroep onderdeel van de omgeving van je script.
Een veelgebruikt patroon is om de bibliotheek te lokaliseren ten opzichte van het aanroepende script met $BASH_SOURCE. Zo blijft het project draagbaar, ongeacht waar het is geïnstalleerd.
#!/usr/bin/env bash
# main.sh — load a library relative to this script's own location
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib/utils.sh"
echo "Library loaded. Calling greet..."
greet "World"Je eerste bibliotheekbestand maken
Een bibliotheekbestand is een gewoon .sh-bestand dat alleen functiedefinities (en soms constanten) bevat. Het mag op het hoogste niveau geen code met neveneffecten uitvoeren — het is bedoeld om te worden ingeladen, niet om rechtstreeks te worden uitgevoerd.
Belangrijke conventies:
- Begin met een shebang-opmerking die het doel van de bibliotheek beschrijft
- Definieer alleen functies — geen
main-logica op het hoogste niveau - Gebruik
returnbinnen functies (nooitexit, want daarmee zou je de aanroeper beëindigen) - Houd het bestand in een submap
lib/van je project
#!/usr/bin/env bash
# lib/utils.sh — General-purpose utility functions
# Print a greeting message
greet() {
local name="${1:-stranger}"
echo "Hello, ${name}!"
}
# Print a timestamped log line to stderr
log_info() {
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2
}
# Print an error message and return a failure code
log_error() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2
return 1
}Insluitbeveiliging: dubbel inladen voorkomen
Wanneer meerdere scripts dezelfde bibliotheek inladen, of wanneer een bibliotheek een andere bibliotheek inlaadt die ook door het hoofdscript wordt ingeladen, kunnen functies meerdere keren worden gedefinieerd. Dit kost tijd en kan subtiele fouten veroorzaken als de body van een functie halverwege de uitvoering wordt vervangen.
De oplossing is een insluitbeveiliging — een variabele die als vlag fungeert. Bij de eerste keer laden is de variabele niet ingesteld, dus gaat het bestand verder. Bij elke volgende keer laden is de beveiliging al ingesteld, dus keert het bestand onmiddellijk terug.
Dit is het Bash-equivalent van #pragma once in C/C++ of patronen zoals if not already imported in andere talen.
#!/usr/bin/env bash
# lib/utils.sh — with include guard
# Guard: if already sourced, do nothing
[[ -n "${_LIB_UTILS_LOADED:-}" ]] && return 0
_LIB_UTILS_LOADED=1
greet() {
local name="${1:-stranger}"
echo "Hello, ${name}!"
}
log_info() {
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2
}
log_error() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2
return 1
}Functievoorvoegsels met naamruimten
Bash heeft één globale naamruimte voor functies. Als twee bibliotheken elk een functie met de naam log of init definiëren, overschrijft de tweede definitie de eerste ongemerkt.
De gebruikelijke bescherming is een naamruimtevoorvoegsel: elke functie in een bibliotheek krijgt het korte bibliotheeknaam gevolgd door twee dubbele punten (::) of een underscore als voorvoegsel. Een bibliotheek met tekenreeksfuncties gebruikt bijvoorbeeld str:: en een bestandsbibliotheek file::.
- Naamconflicten worden uiterst onwaarschijnlijk
- De aanroepende code legt zichzelf uit —
str::trimvertelt precies waar de functie staat - Zoeken met grep wordt eenvoudiger:
grep 'str::' main.shtoont onmiddellijk alle aanroepen van de tekenreeksbibliotheek
#!/usr/bin/env bash
# lib/str.sh — String utility library (namespaced)
[[ -n "${_LIB_STR_LOADED:-}" ]] && return 0
_LIB_STR_LOADED=1
# Trim leading and trailing whitespace
str::trim() {
local s="$1"
s="${s#"${s%%[![:space:]]*}"}"
s="${s%"${s##*[![:space:]]}"}"
echo "$s"
}
# Convert string to uppercase
str::upper() {
echo "${1^^}"
}
# Convert string to lowercase
str::lower() {
echo "${1,,}"
}
# Check if a string contains a substring
str::contains() {
[[ "$1" == *"$2"* ]]
}Een lib/-map organiseren
Naarmate een project groeit, wordt één utils.sh onhandelbaar. Verdeel verantwoordelijkheden over doelgerichte bibliotheekbestanden in een map lib/:
lib/log.sh— hulpfuncties voor logboekregistratie (log::info,log::warn,log::error)lib/str.sh— tekenreeksbewerking (str::trim,str::upper)lib/fs.sh— hulpfuncties voor het bestandssysteem (fs::require_dir,fs::safe_rm)lib/net.sh— netwerkcontroles (net::wait_for_port,net::is_online)
Eén bootstrap-laadbestand (lib/bootstrap.sh) kan ze allemaal in de juiste volgorde inladen, zodat elk script maar één source-aanroep nodig heeft.
#!/usr/bin/env bash
# lib/bootstrap.sh — Load all project libraries in dependency order
[[ -n "${_LIB_BOOTSTRAP_LOADED:-}" ]] && return 0
_LIB_BOOTSTRAP_LOADED=1
_BOOTSTRAP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${_BOOTSTRAP_DIR}/log.sh"
source "${_BOOTSTRAP_DIR}/str.sh"
source "${_BOOTSTRAP_DIR}/fs.sh"
source "${_BOOTSTRAP_DIR}/net.sh"
log::info "All libraries loaded."Een complete bibliotheek voor logboekregistratie
Logboekregistratie is de meest gedeelde zorg in scripts. Een speciale lib/log.sh centraliseert de opmaak van uitvoer, logniveaus en kleurcodes.
Met zo'n bibliotheek drukt elk script in het project consistente berichten met tijdstempels en kleurcodering af, zonder opmaakcode te dupliceren.
#!/usr/bin/env bash
# lib/log.sh — Coloured, levelled logging library
[[ -n "${_LIB_LOG_LOADED:-}" ]] && return 0
_LIB_LOG_LOADED=1
# Colour codes (disabled when not writing to a terminal)
_LOG_RED=''; _LOG_YEL=''; _LOG_GRN=''; _LOG_RST=''
if [[ -t 2 ]]; then
_LOG_RED='\033[0;31m'
_LOG_YEL='\033[0;33m'
_LOG_GRN='\033[0;32m'
_LOG_RST='\033[0m'
fi
_log::_print() {
local level="$1" colour="$2"; shift 2
printf "%b[%s]%b %s %s\n" \
"$colour" "$level" "$_LOG_RST" \
"$(date '+%H:%M:%S')" "$*" >&2
}
log::info() { _log::_print 'INFO ' "$_LOG_GRN" "$@"; }
log::warn() { _log::_print 'WARN ' "$_LOG_YEL" "$@"; }
log::error() { _log::_print 'ERROR' "$_LOG_RED" "$@"; return 1; }
log::fatal() { _log::_print 'FATAL' "$_LOG_RED" "$@"; exit 1; }Een bibliotheek met hulpfuncties voor het bestandssysteem
Scripts die bestanden en mappen bewerken, herhalen vaak dezelfde defensieve controles: bestaat deze map? Is dit pad schrijfbaar? Sta ik op het punt iets belangrijks te verwijderen?
Door deze controles te centraliseren in lib/fs.sh, wordt elk script dat de bibliotheek gebruikt veiliger en beter leesbaar. Let erop dat elke functie bij een fout return 1 gebruikt in plaats van exit, zodat de aanroeper de fout netjes kan afhandelen.
#!/usr/bin/env bash
# lib/fs.sh — Filesystem helper library
[[ -n "${_LIB_FS_LOADED:-}" ]] && return 0
_LIB_FS_LOADED=1
# Ensure a directory exists; create it if not
fs::require_dir() {
local dir="$1"
if [[ ! -d "$dir" ]]; then
mkdir -p "$dir" || { echo "[fs] Cannot create directory: $dir" >&2; return 1; }
fi
}
# Remove a file only if it exists (no error on missing)
fs::safe_rm() {
local target="$1"
[[ -e "$target" ]] && rm -rf -- "$target"
return 0
}
# Assert that a file exists and is readable
fs::require_file() {
local file="$1"
[[ -f "$file" && -r "$file" ]] || {
echo "[fs] Required file missing or unreadable: $file" >&2
return 1
}
}Uw bibliotheek versies beheren met een constante
Wanneer uw bibliotheken in meerdere projecten worden gedeeld of aan een team worden verspreid, is het belangrijk om te weten welke versie van een bibliotheek tijdens runtime is geladen. Een eenvoudige afspraak is om vanuit elke bibliotheek een versieconstante te exporteren.
Aanroepers kunnen dan bij het opstarten een minimale versie afdwingen, zodat afwijkingen vroeg worden ontdekt in plaats van dat u later onverklaarbare fouten moet opsporen. De bewakingsvariabele doet tevens dienst als versietekenreeks, waardoor één variabele twee verantwoordelijkheden combineert.
#!/usr/bin/env bash
# lib/str.sh — versioned example
# Guard doubles as the version identifier
[[ -n "${_LIB_STR_LOADED:-}" ]] && return 0
readonly _LIB_STR_LOADED='1.3.0'
# Caller can validate the version
str::version() { echo "$_LIB_STR_LOADED"; }
# ---- Utility functions ----
str::trim() {
local s="$1"
s="${s#"${s%%[![:space:]]*}"}"
s="${s%"${s##*[![:space:]]}"}"
echo "$s"
}
str::repeat() {
local str="$1" count="$2" result=''
for (( i=0; i<count; i++ )); do result+="$str"; done
echo "$result"
}Een zelfstandige demo: meerdere bibliotheken gebruiken
Deze scène toont een realistisch script dat twee bibliotheken laadt en functies uit beide gebruikt. Let erop hoe schoon het hoofdscript blijft — het drukt de bedoeling uit, terwijl alle implementatiedetails in de bibliotheken staan.
Het script kan als zelfstandig bestand worden uitgevoerd, omdat het de bibliotheken inline definieert met behulp van here-documenten die naar tijdelijke bestanden worden geschreven. In een echt project zou elke bibliotheek in een eigen bestand onder lib/ staan.
#!/usr/bin/env bash
# Standalone demo: inline libs written to /tmp, then sourced
set -euo pipefail
# --- Create a temporary lib/log.sh ---
TMPDIR_LIBS="$(mktemp -d)"
trap 'rm -rf "$TMPDIR_LIBS"' EXIT
cat > "${TMPDIR_LIBS}/log.sh" <<'LIBEOF'
[[ -n "${_LIB_LOG_LOADED:-}" ]] && return 0
_LIB_LOG_LOADED=1
log::info() { echo "[INFO] $*"; }
log::error() { echo "[ERROR] $*" >&2; return 1; }
LIBEOF
cat > "${TMPDIR_LIBS}/str.sh" <<'LIBEOF'
[[ -n "${_LIB_STR_LOADED:-}" ]] && return 0
_LIB_STR_LOADED=1
str::upper() { echo "${1^^}"; }
str::trim() { local s="$1"; s="${s#"${s%%[![:space:]]*}"}";
s="${s%"${s##*[![:space:]]}"}" ; echo "$s"; }
LIBEOF
# --- Source both libraries ---
source "${TMPDIR_LIBS}/log.sh"
source "${TMPDIR_LIBS}/str.sh"
# --- Main logic ---
log::info "Libraries loaded successfully."
raw_input=" hello from bash libraries "
trimmed="$(str::trim "$raw_input")"
log::info "Trimmed: '${trimmed}'"
log::info "Uppercased: '$(str::upper "$trimmed")'"Aanbevolen werkwijzen en veelvoorkomende valkuilen
Loop deze checklist door voordat u een bibliotheek voor teamgebruik uitbrengt:
- Gebruik een include-beveiliging — elke bibliotheek moet er een hebben; documenteer bovenaan de naam van de bewakingsvariabele
- Geen bijwerkingen op topniveau — gebruik nooit
cd,echoen wijzig nooit de globale toestand buiten de body van een functie - Gebruik
localvoor alle variabelen binnen functies — zonderlocallekt elke toewijzing door naar het bereik van de aanroeper - Retourneer, beëindig nooit —
exitin een geladen bestand beëindigt de volledige aanroepende shell - Valideer invoer — controleer verplichte argumenten en retourneer een betekenisvolle foutcode wanneer ze ontbreken
- Documenteer met opmerkingen — beschrijf wat elke functie doet, welke parameters ze heeft en welke waarde ze retourneert
- Vermijd
set -ein bibliotheekbestanden — aanroepers hebben mogelijk hun eigen strategie voor foutafhandeling; laat hen beslissen
Kennistoets: include-beveiligingen
Stel dat een project waarin main.sh zowel lib/bootstrap.sh als lib/log.sh laadt, en lib/bootstrap.sh intern ook lib/log.sh laadt. Wat is het belangrijkste doel van de include-beveiliging in lib/log.sh?
Samenvatting van de les: Bash-bibliotheken op de juiste manier
U hebt alle essentiële technieken behandeld voor het bouwen en gebruiken van herbruikbare Bash-bibliotheken. Dit zijn de belangrijkste punten:
- Laad met
sourceof.— hiermee wordt een bestand in de huidige shell geladen, zodat de functies ervan direct beschikbaar zijn - Gebruik
$BASH_SOURCEom bibliotheekpaden relatief ten opzichte van het aanroepende script op te lossen, zodat projecten overdraagbaar blijven - Include-beveiligingen (
[[ -n "${_GUARD:-}" ]] && return 0) voorkomen dubbele definities wanneer meerdere bestanden dezelfde bibliotheek laden - Naamruimtevoorvoegsels (
log::,str::,fs::) voorkomen botsingen tussen functienamen in verschillende bibliotheken - Een map
lib/met gerichte bestanden met één verantwoordelijkheid houdt grote projecten onderhoudbaar - Een bootstraplader (
lib/bootstrap.sh) geeft elk script één laadopdracht om het volledige ecosysteem te laden - Gebruik nooit
exitof bijwerkingen op topniveau in bibliotheekbestanden — alleen functiedefinities en constanten horen daar thuis
Pas deze patronen consequent toe, en uw shellscripts zijn net zo modulair en onderhoudbaar als code die in elke taal op een hoger abstractieniveau is geschreven.
Leer Bash met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 22
- Lessen
- 88
Veelgestelde vragen
Is de les “Herbruikbare Bash-bibliotheken bouwen en sourcen” gratis?
Ja — de volledige tekst van “Herbruikbare Bash-bibliotheken bouwen en sourcen” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus De Linux-opdrachtregel en Bash-scripting beheersen wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.
Wat leer ik in “Herbruikbare Bash-bibliotheken bouwen en sourcen”?
Organiseer gedeelde helpers in sourcebare .sh-bibliotheekbestanden met include guards en genamesp acede functieprefixen. Je oefent met De Linux-opdrachtregel en Bash-scripting beheersen door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met De Linux-opdrachtregel en Bash-scripting beheersen te beginnen?
Ervaring vooraf is niet nodig. De Linux-opdrachtregel en Bash-scripting beheersen op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.
Hoe lang duurt de les “Herbruikbare Bash-bibliotheken bouwen en sourcen”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over De Linux-opdrachtregel en Bash-scripting beheersen?
Ja. Elke les over De Linux-opdrachtregel en Bash-scripting beheersen bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Functies ontwerpen met lokale scope en returncodes
- Herbruikbare Bash-bibliotheken bouwen en sourcen
- Flags en argumenten parsen met getopts
- Arrays en associatieve maps tussen functies doorgeven