Procesar CSV en Bash: cut, awk, sort y los campos con comas entre comillas

Para procesar un CSV en Bash no hace falta ningún programa extra: cut saca columnas, awk suma, filtra y reordena, y sort ordena por la columna que quieras. Aquí tienes cada operación con su resultado real, el límite de la aritmética de Bash y cómo tratar el caso que rompe las soluciones rápidas: los campos con comas entre comillas.

Los ejemplos usan este archivo, ventas.csv, con una cabecera y cuatro columnas:

terminal$ cat ventas.csv
fecha,producto,cantidad,precio
2026-10-01,Teclado,2,349.90
2026-10-01,Ratón,5,199.00
2026-10-02,Monitor,1,3899.00
2026-10-03,Teclado,1,349.90
2026-10-03,Cable USB-C,10,89.50
2026-10-04,Ratón,2,199.00

Una columna con cut

terminal$ cut -d, -f2 ventas.csv
producto
Teclado
Ratón
Monitor
Teclado
Cable USB-C
Ratón
$ tail -n +2 ventas.csv | cut -d, -f2 | sort | uniq -c | sort -rn
      2 Teclado
      2 Ratón
      1 Monitor
      1 Cable USB-C

cut -d, -f2 separa cada línea por las comas y se queda con el segundo campo. En la segunda orden, tail -n +2 se salta la cabecera (empieza en la línea 2), y la cadena sort | uniq -c | sort -rn cuenta cuántas veces aparece cada producto y las ordena de más a menos: es una de las combinaciones más útiles de la terminal.

Sumar, filtrar y reordenar con awk

Para todo lo que sea calcular, awk es la herramienta. Con -F, separa por comas, y cada campo está en $1, $2, etc.; NR es el número de línea:

terminal$ awk -F, 'NR > 1 { total += $3 * $4 } END { printf "Total vendido: %.2f\n", total }' ventas.csv
Total vendido: 7236.70
$ awk -F, 'NR > 1 { suma[$2] += $3 * $4 } END { for (p in suma) printf "%-12s %9.2f\n", p, suma[p] }' ventas.csv | sort
Cable USB-C     895.00
Monitor        3899.00
Ratón          1393.00
Teclado        1049.70
$ awk -F, 'NR == 1 || $3 >= 2' ventas.csv
fecha,producto,cantidad,precio
2026-10-01,Teclado,2,349.90
2026-10-01,Ratón,5,199.00
2026-10-03,Cable USB-C,10,89.50
2026-10-04,Ratón,2,199.00
$ awk -F, -v OFS=, '{ print $2, $1, $4 }' ventas.csv
producto,fecha,precio
Teclado,2026-10-01,349.90
Ratón,2026-10-01,199.00
Monitor,2026-10-02,3899.00
Teclado,2026-10-03,349.90
Cable USB-C,2026-10-03,89.50
Ratón,2026-10-04,199.00

Línea por línea: el total de cantidad por precio; lo vendido por producto, en un array que se recorre al final (for (p in suma) no garantiza ningún orden, por eso el sort); las filas con dos unidades o más, conservando la cabecera; y las columnas en otro orden, con OFS=, para que la salida siga siendo CSV.

Ordenar por una columna

terminal$ { head -n 1 ventas.csv; tail -n +2 ventas.csv | sort -t, -k4,4nr; }
fecha,producto,cantidad,precio
2026-10-02,Monitor,1,3899.00
2026-10-01,Teclado,2,349.90
2026-10-03,Teclado,1,349.90
2026-10-01,Ratón,5,199.00
2026-10-04,Ratón,2,199.00
2026-10-03,Cable USB-C,10,89.50

-t, indica el separador, -k4,4 ordena por la cuarta columna y solo por ella, n compara como números y r invierte el orden. La cabecera se imprime aparte para que no se mezcle con los datos.

Bash no sabe de decimales

Leer el CSV con un bucle while read funciona para recorrer las filas, pero la aritmética de Bash es solo de enteros:

#!/usr/bin/env bash
{
    read -r                                   # se salta la cabecera
    while IFS=, read -r fecha producto cantidad precio; do
        importe=$(awk -v c="$cantidad" -v p="$precio" 'BEGIN { printf "%.2f", c * p }')
        printf '%s  %-12ls %9s\n' "$fecha" "$producto" "$importe"
    done
} < ventas.csv

echo "Y con la aritmética de Bash:"
echo $(( 2 * 349.90 ))
salida con error2026-10-01  Teclado         699.80
2026-10-01  Ratón           995.00
2026-10-02  Monitor        3899.00
2026-10-03  Teclado         349.90
2026-10-03  Cable USB-C     895.00
2026-10-04  Ratón           398.00
Y con la aritmética de Bash:
./decimales.sh: line 11: 2 * 349.90 : arithmetic syntax error: invalid arithmetic operator (error token is ".90 ")

ShellCheck también lo avisa, antes de ejecutar nada:

shellcheckIn decimales.sh line 11:
echo $(( 2 * 349.90 ))
             ^----^ SC2079 (error): (( )) doesn't support decimals. Use bc or awk.

For more information:
  https://www.shellcheck.net/wiki/SC2079 -- (( )) doesn't support decimals. U...

El %-12ls rellena hasta 12 caracteres; con %-12s, el printf de Bash cuenta bytes y la fila de «Ratón» saldría con un espacio de menos. El { read -r; while …; done; } < ventas.csv abre el archivo una sola vez: el primer read consume la cabecera y el bucle sigue desde la segunda línea. Para multiplicar precios hay que salir a awk o a bc. Y si la tarea es sobre todo de cálculo, mejor hacerla entera en awk, como en la sección anterior, que es mucho más rápido que llamarlo en cada vuelta.

El problema de verdad: comas dentro de comillas

En CSV, un campo que contiene comas va entre comillas, y unas comillas dentro de un campo se escriben dobles. Este archivo es perfectamente válido:

terminal$ cat clientes.csv
nombre,ciudad,saldo
"Pérez, Ana",Monterrey,1500
Luis Gómez,"Guadalajara, Jal.",230
"María ""Mary"" Ruiz",Puebla,980
$ cut -d, -f2 clientes.csv
ciudad
 Ana"
"Guadalajara
Puebla

cut no sabe nada de comillas, y la segunda columna sale mal en dos de las tres filas. GNU awk (gawk) tiene una variable, FPAT, que describe cómo es un campo en lugar de cómo es el separador: «o texto sin comas, o algo entre comillas»:

#!/usr/bin/env bash
gawk -v FPAT='([^,]*)|("([^"]|"")*")' '
function limpiar(c) {
    if (c ~ /^"/) {
        c = substr(c, 2, length(c) - 2)
        gsub(/""/, "\"", c)
    }
    return c
}
NR > 1 { printf "%-18s %-18s %6s\n", limpiar($1), limpiar($2), $3 }
' clientes.csv
salidaPérez, Ana         Monterrey            1500
Luis Gómez         Guadalajara, Jal.     230
María "Mary" Ruiz  Puebla                980

La función limpiar quita las comillas de los extremos y convierte las comillas dobles en simples. Esto cubre casi todos los CSV del mundo real, salvo uno: los campos con saltos de línea dentro de las comillas, que awk partiría en dos registros. Para esos, usa un analizador de verdad: mlr (Miller), csvkit o unas líneas de Python con su módulo csv.

Escribir CSV válido

Al escribir hay que hacer lo contrario: si un campo contiene comas, comillas o saltos de línea, se rodea de comillas y sus comillas se duplican.

#!/usr/bin/env bash
# Escribe un campo CSV: si lleva comas, comillas o saltos de línea, lo encierra entre comillas y duplica las comillas.
campo_csv() {
    local c=$1
    if [[ $c == *[,\"$'\n']* ]]; then
        c=${c//\"/\"\"}
        printf '"%s"' "$c"
    else
        printf '%s' "$c"
    fi
}

# Escribe una fila: los campos separados por comas y un salto de línea al final.
fila_csv() {
    local primero=1 c
    for c in "$@"; do
        if (( ! primero )); then
            printf ','
        fi
        campo_csv "$c"
        primero=0
    done
    printf '\n'
}

{
    fila_csv nombre ciudad saldo
    fila_csv "Pérez, Ana" Monterrey 1500
    fila_csv "Luis Gómez" "Guadalajara, Jal." 230
    fila_csv 'María "Mary" Ruiz' Puebla 980
} > nuevos.csv

cat nuevos.csv
salidanombre,ciudad,saldo
"Pérez, Ana",Monterrey,1500
Luis Gómez,"Guadalajara, Jal.",230
"María ""Mary"" Ruiz",Puebla,980

El patrón *[,\"$'\n']* comprueba si el campo contiene una coma, unas comillas o un salto de línea ($'\n' es la forma de escribir un salto de línea en Bash), y ${c//\"/\"\"} duplica cada comilla. El archivo resultante se lee bien con el gawk de antes y con cualquier hoja de cálculo.

  • TareaHerramientaOjo
  • Una columna, CSV simplecut -d, -fNno entiende comillas
  • Sumar, filtrar, reordenarawk -F,salta la cabecera con NR > 1
  • Ordenar por columnasort -t, -kN,Nnimprime la cabecera aparte
  • Campos con comas entre comillasgawk -v FPAT=…solo GNU awk; en macOS, brew install gawk
  • Saltos de línea dentro de camposMiller, csvkit o Pythonlas herramientas de líneas no sirven

Todos los scripts de esta página se ejecutaron con Bash 5.3 en Ubuntu 26.04, en un contenedor limpio sin acceso a la red, y pasaron ShellCheck 0.11 sin avisos antes de publicarla; las salidas están copiadas de esa ejecución.