mapfile / readarray în Linux / Bash

Scripting Bash Bash 4.0

Citește rândurile unui fișier sau ale ieșirii unei comenzi într-un tablou Bash, câte un rând pe element.

Sintaxă comandă
mapfile -t tablou < fișier
mapfile -t tablou < <(comandă)
readarray -t tablou < fișier

Ce face mapfile / readarray

mapfile (cu numele alternativ readarray) citește intrarea standard și pune fiecare rând într-un element de tablou. Este mai scurt și mai rapid decât o buclă while read și păstrează corect spațiile din rânduri.

Opțiunea -t elimină caracterul de rând nou de la finalul fiecărui element; aproape întotdeauna o vrei. Pentru ieșirea unei comenzi folosește substituția de proces < <(comandă), nu un pipe: comandă | mapfile rulează mapfile într-un subshell, iar tabloul se pierde.

mapfile există din Bash 4.0. Pe macOS, Bash-ul implicit este 3.2, unde se folosește bucla while IFS= read -r din ultimul exemplu.

Opțiuni și argumente

Opțiune / Parametru Descriere
-t Elimină rândul nou de la finalul fiecărui element.
-s N Sare peste primele N rânduri (de exemplu antetul unui CSV).
-n N Citește cel mult N rânduri.
-d DELIM Folosește alt separator în loc de rând nou (Bash 4.4+); -d '' pentru ieșirea lui find -print0.
-O INDEX Începe să scrie în tablou de la indexul dat.

Exemple

Exemplul 1: Rândurile unui fișier într-un tablou

Fiecare elev devine un element; cu -t, elementele nu mai conțin rândul nou.

printf '%s\n' "Ana" "Mihai" "Ioana" > elevi.txt
mapfile -t elevi < elevi.txt
echo "Număr elevi: ${#elevi[@]}"
echo "Al doilea elev: ${elevi[1]}"

Ieșire în terminal

Număr elevi: 3
Al doilea elev: Mihai

Exemplul 2: Un CSV fără antet

-s 1 sare peste rândul de antet, iar expansiunea parametrilor desparte numele de notă.

cat > note.csv <<'EOF'
nume,nota
Ana,9
Mihai,7
Ioana,10
EOF
mapfile -t -s 1 randuri < note.csv
for rand in "${randuri[@]}"; do
  echo "${rand%%,*} are nota ${rand#*,}"
done

Ieșire în terminal

Ana are nota 9
Mihai are nota 7
Ioana are nota 10

Exemplul 3: Alternativa pentru Bash 3.2 și sh

Bucla while IFS= read -r face același lucru pe sistemele fără mapfile. IFS= păstrează spațiile, iar -r păstrează backslash-urile.

printf '%s\n' "Ana Pop" "Mihai Ionescu" > elevi.txt
elevi=()
while IFS= read -r linie; do
  elevi+=("$linie")
done < elevi.txt
echo "Număr elevi: ${#elevi[@]}"
echo "Primul elev: ${elevi[0]}"

Rezultat

Număr elevi: 2
Primul elev: Ana Pop

Greșeli frecvente

  • cat fisier | mapfile -t linii: tabloul se umple în subshell-ul pipe-ului și este gol după acest rând. Scrie mapfile -t linii < fisier sau < <(comanda).
  • Uitarea lui -t: fiecare element se termină cu un rând nou, iar comparațiile de tipul [ "${linii[0]}" = Ana ] eșuează.
  • Folosirea lui mapfile într-un script cu #!/bin/sh sau pe Bash 3.2 (macOS): comanda nu există.

Comenzi înrudite

  • Tablouri în Bash (indexate și asociative): Liste de valori accesate prin index (tablou[0]) și dicționare cheie-valoare cu declare -A, cu parcurgere și numărare.
  • read: Citește o linie de text de la tastatură sau din intrarea standard și o salvează într-o variabilă.
  • while: Execută repetat un bloc de comenzi atâta timp cât condiția specificată rămâne adevărată.
  • Redirecționare și conducte (Pipes): Redirecționarea fluxurilor standard de intrare (stdin), ieșire (stdout), erori (stderr) și legarea prin conducte (pipes).