RA7. Llenguatges de guions

Aquest document treballa l’scripting en dos mons: el lliure (Bash, sobre GNU/Linux) i el propietari (PowerShell, sobre Windows). L’objectiu no és només aprendre sintaxi, sinó saber quan i com un guió és l’eina adequada per administrar un sistema: comptes d’usuari, processos, serveis i tasques automatitzades.

0. Bash vs. PowerShell: dues filosofies

Bash (lliure) PowerShell (propietari*)
Model de dades Text pla (streams) Objectes .NET
Ordres Binaris externs (ls, grep…) Cmdlets Verb-Noun
Extensió de fitxer .sh .ps1
Shebang #!/bin/bash No aplica (s’invoca amb l’intèrpret)
Multiplataforma Nativament Unix/Linux PowerShell 7+ és multiplataforma

* Des de PowerShell 7, Microsoft n’ha alliberat el codi (MIT license) i funciona sobre Linux i macOS, però el manual el tracta com a representant del món “propietari” perquè és l’eina d’administració per defecte de Windows Server i AD.

💡
Nota

El fet que Bash treballi amb text i PowerShell amb objectes és la diferència conceptual més important. A Bash, ps aux | grep bash filtra línies de text. A PowerShell, Get-Process | Where-Object {$_.Name -eq "pwsh"} filtra objectes Process reals, amb propietats tipades (.Id, .CPU, .Path…).

1. Estructures del llenguatge

1.1. Variables i tipus de dades

Bash — tot és text per defecte, sense declaració de tipus:

nom="Ramon"
edat=42
readonly VERSIO="1.0"                # constant
declare -i comptador=0               # força el tipus enter
export PATH_LOG="/var/log/app.log"   # variable d'entorn

PowerShell — tipat dinàmic amb objectes .NET reals:

$nom = "Ramon"
$edat = 42
[int]$comptador = 0
$PATH_LOG = "/var/log/app.log"
Set-Variable -Name VERSIO -Value "1.0" -Option ReadOnly

1.2. Estructures de dades: arrays i diccionaris

# Bash
serveis=("ssh" "nginx" "mariadb")
echo "${serveis[1]}"                 # nginx
echo "${serveis[@]}"                 # tots els elements

declare -A usuaris=( [ramon]="professor" [alex]="alumne" )
echo "${usuaris[ramon]}"             # professor
# PowerShell
$serveis = @("ssh", "nginx", "mariadb")
$serveis[1]                          # nginx

$usuaris = @{ ramon = "professor"; alex = "alumne" }
$usuaris["ramon"]                    # professor

1.3. Estructures de control

# Condicional
if [ "$edat" -ge 18 ]; then
    echo "major d'edat"
elif [ "$edat" -eq 0 ]; then
    echo "no vàlid"
else
    echo "menor d'edat"
fi

# Selecció múltiple
case "$1" in
    start) echo "Arrencant...";;
    stop)  echo "Aturant...";;
    *)     echo "Ús: $0 {start|stop}";;
esac

# Bucles
for s in "${serveis[@]}"; do echo "$s"; done
i=0
while [ $i -lt 5 ]; do echo $i; ((i++)); done
# Condicional
if ($edat -ge 18) {
    "major d'edat"
} elseif ($edat -eq 0) {
    "no vàlid"
} else {
    "menor d'edat"
}

# Selecció múltiple
switch ($args[0]) {
    "start" { "Arrencant..." }
    "stop"  { "Aturant..." }
    default { "Ús: $($MyInvocation.MyCommand.Name) {start|stop}" }
}

# Bucles
foreach ($s in $serveis) { $s }
for ($i = 0; $i -lt 5; $i++) { $i }

1.4. Funcions i pas de paràmetres

reinicia_servei() {
    local servei="$1"
    if systemctl is-active --quiet "$servei"; then
        systemctl restart "$servei"
        echo "Reiniciat: $servei"
    else
        echo "Error: $servei no actiu" >&2
        return 1
    fi
}
reinicia_servei "nginx"
function Reinicia-Servei {
    param(
        [Parameter(Mandatory)][string]$Servei
    )
    if ((Get-Service -Name $Servei).Status -eq "Running") {
        Restart-Service -Name $Servei
        Write-Output "Reiniciat: $Servei"
    } else {
        Write-Error "$Servei no actiu"
    }
}
Reinicia-Servei -Servei "Spooler"

2. Creació i depuració d’scripts

2.1. Creació: capçalera i permisos (Bash)

#!/bin/bash
#
# Nom:      backup_logs.sh
# Autor:    Ramon López
# Data:     2026-06-20
# Objectiu: Comprimeix i arxiva els logs de /var/log/app
# Ús:       ./backup_logs.sh [directori_desti]

set -euo pipefail   # atura el script davant errors, variables no definides o pipes fallits
chmod +x backup_logs.sh
./backup_logs.sh /mnt/backups

set -euo pipefail és probablement la línia més important de qualsevol script Bash professional:

2.2. Creació en PowerShell: política d’execució

# Comentari de capçalera basat en comment-based help (veure secció 5.4)
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
.\Backup-Logs.ps1 -Desti "D:\Backups"

Per defecte, Windows bloqueja l’execució d’scripts (Restricted). Cal establir una política com RemoteSigned (permet scripts locals, exigeix signatura als descarregats d’internet) — això mateix es tracta com a criteri d’implantació al bloc 6.

2.3. Depuració d’errors sintàctics

Eina Ús
bash -n script.sh Comprova la sintaxi sense executar
bash -x script.sh Traça d’execució (mostra cada ordre abans d’executar-la)
shellcheck script.sh Analitzador estàtic extern, detecta errors habituals i mals hàbits
PSScriptAnalyzer Equivalent a ShellCheck per a PowerShell
Set-PSDebug -Trace 1 Traça d’execució en PowerShell
bash -x backup_logs.sh
+ set -euo pipefail
+ DEST=/mnt/backups
+ tar czf /mnt/backups/app-2026-09-06.tar.gz /var/log/app
Set-PSDebug -Trace 1
.\Backup-Logs.ps1
Set-PSDebug -Off

2.4. Depuració d’errors d’execució (trampes i codis de sortida)

trap 'echo "Error a la línia $LINENO" >&2' ERR

ordre_que_pot_fallar
echo "Codi de sortida: $?"
try {
    Get-Service -Name "ServeiInexistent" -ErrorAction Stop
}
catch {
    Write-Error "Error capturat: $($_.Exception.Message)"
}
finally {
    Write-Output "Bloc finally sempre executat"
}

3. Interpretació d’scripts del sistema. Aplicacions

Un tècnic de sistemes passa més temps llegint scripts d’altri que escrivint-ne de nous. Cal saber identificar patrons habituals als scripts del mateix sistema operatiu.

3.1. GNU/Linux: unitats systemd i scripts d’arrencada

# /etc/systemd/system/monitor-disc.service
[Unit]
Description=Monitor d'espai en disc
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/monitor-disc.sh
User=root

[Install]
WantedBy=multi-user.target

Interpretació: ExecStart invoca un script extern; Type=oneshot indica que s’executa i acaba (típic de scripts, no de dimonis persistents); WantedBy determina en quin target d’arrencada s’habilita.

També cal saber llegir:

3.2. Windows: perfils i tasques programades

# $PROFILE — s'executa a l'inici de cada sessió PowerShell
notepad $PROFILE
# Consultar una tasca programada i el script que invoca
Get-ScheduledTask -TaskName "ActualitzaInventari" |
    Select-Object TaskName, State
(Get-ScheduledTask -TaskName "ActualitzaInventari").Actions
💡
Nota

Interpretar un script no és només llegir-ne el codi: cal identificar qui l’invoca (systemd, cron, Task Scheduler, un altre script), amb quins privilegis s’executa i quines dependències (altres binaris, variables d’entorn, fitxers de configuració) necessita per funcionar correctament.

4. Utilització d’extensions d’ordres per a tasques d’administració

4.1. Mòduls PowerShell

Get-Module -ListAvailable ActiveDirectory
Import-Module ActiveDirectory
Get-ADUser -Filter {Enabled -eq $false} | Select-Object Name, LastLogonDate

Els mòduls (Import-Module) són l’equivalent PowerShell de les llibreries: paqueten cmdlets especialitzats (AD, DNS, DHCP, Hyper-V…) que estenen l’intèrpret sense escriure’ls des de zero.

4.2. Llibreries de funcions pròpies en Bash

# /usr/local/lib/funcions_admin.sh
comprova_root() {
    [ "$EUID" -eq 0 ] || { echo "Cal executar com a root" >&2; exit 1; }
}
log() {
    echo "$(date '+%F %T') - $1" >> /var/log/scripts_admin.log
}
#!/bin/bash
source /usr/local/lib/funcions_admin.sh   # o bé: . /usr/local/lib/funcions_admin.sh
comprova_root
log "Script iniciat"

source (o el seu àlies .) carrega les funcions al context actual, sense obrir un subprocés nou: és el mecanisme bàsic per consultar i reutilitzar llibreries de funcions en Bash.

5. Scripts per a l’administració de comptes, processos i serveis

5.1. Comptes d’usuari

#!/bin/bash
# alta_usuari.sh — dona d'alta un usuari i el crea al grup indicat
set -euo pipefail
usuari="$1"; grup="${2:-alumnes}"

useradd -m -s /bin/bash -G "$grup" "$usuari"
passwd -e "$usuari"      # força el canvi de contrasenya al primer login
echo "Usuari $usuari creat i afegit a $grup"
# Alta-Usuari.ps1
param(
    [Parameter(Mandatory)][string]$Usuari,
    [string]$Grup = "Alumnes"
)
$pwd = ConvertTo-SecureString "CanviaAquestaContrasenya!" -AsPlainText -Force
New-LocalUser -Name $Usuari -Password $pwd -PasswordNeverExpires:$false
Add-LocalGroupMember -Group $Grup -Member $Usuari
Write-Output "Usuari $Usuari creat i afegit a $Grup"

5.2. Processos

ps aux --sort=-%cpu | head -5          # 5 processos que més CPU consumeixen
pgrep -f "python3 servidor.py"         # localitzar per nom
kill -SIGTERM "$(pgrep -f servidor.py)"
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5
Stop-Process -Name "notepad" -Force

5.3. Serveis del sistema operatiu

#!/bin/bash
# gestiona_servei.sh start|stop|status <nom_servei>
accio="$1"; servei="$2"
case "$accio" in
    start)  systemctl start "$servei"  && echo "$servei iniciat";;
    stop)   systemctl stop "$servei"   && echo "$servei aturat";;
    status) systemctl status "$servei" --no-pager;;
    *) echo "Ús: $0 {start|stop|status} <servei>"; exit 1;;
esac
# Gestiona-Servei.ps1
param(
    [ValidateSet("Start","Stop","Status")][string]$Accio,
    [Parameter(Mandatory)][string]$Servei
)
switch ($Accio) {
    "Start"  { Start-Service -Name $Servei;   "$Servei iniciat" }
    "Stop"   { Stop-Service  -Name $Servei;   "$Servei aturat" }
    "Status" { Get-Service   -Name $Servei }
}

5.4. Automatització de tasques

# crontab -e
# Cada dia a les 2:30, executa la còpia de seguretat
30 2 * * * /usr/local/bin/backup_logs.sh /mnt/backups >> /var/log/backup.log 2>&1
# Alternativa moderna: systemd timer
# /etc/systemd/system/backup.timer
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target
# Programar la mateixa tasca amb Task Scheduler des de PowerShell
$accio    = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-File C:\Scripts\Backup-Logs.ps1"
$disparador = New-ScheduledTaskTrigger -Daily -At 2:30am
Register-ScheduledTask -TaskName "BackupLogs" -Action $accio -Trigger $disparador

5.5. Documentació dels scripts

Bash: capçalera de comentaris (autor, data, objectiu, ús) com a l’apartat 2.1, més comentaris explicatius abans de blocs no evidents.

PowerShell té un sistema natiu de documentació integrat, el comment-based help:

<#
.SYNOPSIS
    Gestiona l'estat d'un servei de Windows.
.DESCRIPTION
    Inicia, atura o consulta l'estat d'un servei indicat pel nom.
.PARAMETER Servei
    Nom del servei de Windows a gestionar.
.EXAMPLE
    .\Gestiona-Servei.ps1 -Accio Start -Servei Spooler
#>
param(...)

Amb aquesta capçalera, Get-Help .\Gestiona-Servei.ps1 -Full genera automàticament l’ajuda de l’script, exactament igual que amb un cmdlet natiu.

6. Implantació de guions en sistemes lliures i propietaris

Aspecte Linux / lliure Windows / propietari
Permisos d’execució chmod +x script.sh No cal per si mateix; depèn de la política d’execució
Control de qui pot executar Permisos POSIX (usuari/grup/altres) + sudo Set-ExecutionPolicy, signatura digital de codi
Ubicació estàndard /usr/local/bin, /usr/local/sbin Carpeta pròpia + variable PATH, o mòdul instal·lat a $env:PSModulePath
Distribució massiva Paquet .deb/.rpm, Ansible, scp + cron GPO, PowerShell DSC, Intune, signatura amb Set-AuthenticodeSignature
Comprovar integritat sha256sum Get-FileHash
# Signar un script per poder-lo distribuir amb política AllSigned
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
Set-AuthenticodeSignature -FilePath .\Backup-Logs.ps1 -Certificate $cert
💡
Nota

“Implantar” un guió no acaba en copiar-lo al servidor: inclou triar on viu (repositori, control de versions), com s’invoca (manualment, per cron/Task Scheduler, com a servei), amb quins privilegis i com es revisa si deixa de funcionar (logs, alertes). Aquest és exactament el punt on el scripting es converteix en administració de sistemes real.

Recursos

Versions d’aquest document

Domini Públic (CC0)