A veces puede ser necesario mezclar aleatoriamente las líneas de un archivo o, inversamente, ordenar alfabéticamente (o numéricamente) las líneas en la salida de un comando. Veamos algunos ejemplos y usos prácticos de las herramientas shuf y sort en sistemas GNU/Linux.

Tanto shuf como sort son parte del paquete coreutils. Infinidad de veces demostré en este blog cómo utilizar sort en conjunto con uniq, por ejemplo al listar todos los dominios que están haciendo hotlink de nuestras imágenes. Este es un uso bastante básico de la herramienta sort, y tal vez el más conocido. Sin embargo tiene usos más avanzados, al mismo tiempo que existe su contraparte shuf, que se emplea para desordenar o mezclar las líneas en una salida o archivo pasado como parámetro.

Mezclar

shuf permite generar (por salida estándar) permutaciones aleatorias en las líneas de un archivo o entrada estándar, lo que se conoce coloquialmente como mezclar o desordenar. Veamos un ejemplo partiendo de un archivo cuyas líneas se encuentran ordenadas alfabéticamente:

emi@hal9000:~$ grep multipart /etc/mime.types
multipart/alternative
multipart/appledouble
multipart/byteranges
multipart/digest
multipart/encrypted
multipart/form-data
multipart/header-set
multipart/mixed
multipart/parallel
multipart/related
multipart/report
multipart/signed
multipart/voice-message

Al pasar la salida anterior a shuf, se observa que se intercambian aleatoriamente:

emi@hal9000:~$ grep multipart /etc/mime.types | shuf
multipart/appledouble
multipart/related
multipart/mixed
multipart/parallel
multipart/voice-message
multipart/digest
multipart/alternative
multipart/signed
multipart/report
multipart/form-data
multipart/header-set
multipart/encrypted
multipart/byteranges

Una opción interesante, que puede ser de utilidad al momento de desarrollar scripts, es -e. Esta permite interpretar cada parámetro en la línea de comandos como si fuese una línea. Por ejemplo:

root@hal9000:~# shuf -e 1 2 3 4 5
4
5
2
3
1
root@hal9000:~# shuf -e 1 2 3 4 5
1
4
5
3
2
root@hal9000:~# shuf -e 1 2 3 4 5
1
4
2
3
5

Ordenar

Si lo que se necesita es aplicar algún tipo de ordenamiento sobre las líneas de un archivo, se debe recurrir a sort. Esta permite ordenar según diferentes criterios, no sólo alfabéticamente.

root@hal9000:~# echo -e "Homero\nLisa\nBart\nMarge\nMaggie"
Homero
Lisa
Bart
Marge
Maggie

Por defecto, sort ordena las líneas alfabéticamente:

root@hal9000:~# echo -e "Homero\nLisa\nBart\nMarge\nMaggie" | sort
Bart
Homero
Lisa
Maggie
Marge

Para ordenar inversamente (de la Z a la A) se debe agregar la opción -r:

emi@hal9000:~$ echo -e "Homero\nLisa\nBart\nMarge\nMaggie" | sort -r
Marge
Maggie
Lisa
Homero
Bart

Con esta herramienta es posible ordenar cualquier salida para ser procesada desde scripts, por ejemplo ordenar los sistemas de archivo montados según el tipo de dispositivo:

emi@hal9000:~$ mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
udev on /dev type devtmpfs (rw,nosuid,relatime,size=3999244k,nr_inodes=999811,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=802812k,mode=755)
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
pstore on /sys/fs/pstore type pstore (rw,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=3168040k)
/dev/sda4 on /data type ext4 (rw,relatime)
/dev/sda3 on /home type ext4 (rw,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/elogind type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/elogind/elogind-cgroups-agent,name=elogind)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=802808k,mode=700,uid=1000,gid=1000)
none on /run/user/1000 type tmpfs (rw,relatime,mode=700,uid=1000)
emi@hal9000:~$ mount | sort
cgroup on /sys/fs/cgroup/elogind type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/elogind/elogind-cgroups-agent,name=elogind)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
/dev/sda3 on /home type ext4 (rw,relatime)
/dev/sda4 on /data type ext4 (rw,relatime)
none on /run/user/1000 type tmpfs (rw,relatime,mode=700,uid=1000)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
pstore on /sys/fs/pstore type pstore (rw,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=3168040k)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=802812k,mode=755)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=802808k,mode=700,uid=1000,gid=1000)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,mode=755)
udev on /dev type devtmpfs (rw,nosuid,relatime,size=3999244k,nr_inodes=999811,mode=755)

Por otro lado, con la opciones -n o -g es posible ordenar numéricamente en lugar de alfabéticamente. Por ejemplo, obtener los 20 procesos con más uso de CPU en un instante en particular:

emi@hal9000:~$ ps -eo %cpu,pid,cmd | sort -nr | head -n 20
18.4  2522 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 2 -isForBrowser -prefsLen 137 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
16.2  2411 /usr/lib/firefox-esr/firefox-esr
11.3  2471 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 1 -isForBrowser -prefsLen 1 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 4.9  1938 /usr/lib/xorg/Xorg -nolisten tcp -auth /var/run/slim.auth vt07
 2.7  2583 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 7 -isForBrowser -prefsLen 137 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 2.3  2592 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 8 -isForBrowser -prefsLen 137 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 2.3  1773 /usr/sbin/cups-browsed
 2.2  2525 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 3 -isForBrowser -prefsLen 137 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 1.2  2369 /usr/bin/pulseaudio --start
 1.1  2552 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 4 -isForBrowser -prefsLen 137 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 0.8  2725 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 9 -isForBrowser -prefsLen 5944 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 0.8  1685 /usr/bin/dbus-daemon --system
 0.7  2558 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 5 -isForBrowser -prefsLen 137 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 0.7  1710 avahi-daemon: running [hal9000.local]
 0.6  4880 geany /tmp/archivo.txt
 0.5  2566 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 6 -isForBrowser -prefsLen 137 -prefMapSize 185705 -parentBuildID 20191203235607 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true tab
 0.4  2904 /usr/lib/firefox-esr/firefox-esr -contentproc -parentBuildID 20191203235607 -prefsLen 6972 -prefMapSize 185705 -greomni /usr/lib/firefox-esr/omni.ja -appomni /usr/lib/firefox-esr/browser/omni.ja -appdir /usr/lib/firefox-esr/browser 2411 true rdd
 0.4  2126 xfwm4 --display :0.0 --sm-client-id 270e77b10-a968-4637-8095-d44ed19946b1
 0.1  3161 xfce4-terminal --drop-down
 0.1  2298 /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 /usr/lib/x86_64-linux-gnu/xfce4/panel/plugins/libpulseaudio-plugin.so 4 20971554 pulseaudio PulseAudio Plugin Adjust the audio volume of the PulseAudio sound system

Esta opción interpreta el primer campo de cada línea como un valor numérico entero o real.

Más aún, la opción -h permite ordenar por valores numéricos con unidades amigables como MB, KB, etc. Si quisiéramos ordenar los sistemas de archivo por uso de disco, simplemente personalizar la salida de df y emplear esta opción:

emi@hal9000:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            3.9G     0  3.9G   0% /dev
tmpfs           784M  988K  784M   1% /run
/dev/sda1        92G  6.9G   80G   8% /
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           3.1G   83M  3.0G   3% /dev/shm
/dev/sda4       1.4T  319G  979G  25% /data
/dev/sda3       366G   50G  298G  15% /home
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
none            3.9G  8.0K  3.9G   1% /run/user/1000

Se obtiene un listado de sistemas de archivo ordenados por espacio disponible, de menor a mayor:

emi@hal9000:~$ df -h --output=avail,source,target | sort -h
Avail Filesystem     Mounted on
 5.0M tmpfs          /run/lock
 784M tmpfs          /run
 3.0G tmpfs          /dev/shm
 3.9G none           /run/user/1000
 3.9G tmpfs          /sys/fs/cgroup
 3.9G udev           /dev
  80G /dev/sda1      /
 298G /dev/sda3      /home
 979G /dev/sda4      /data

Para más información, consultar las páginas de manual de shuf y sort:

man shuf
man sort

Compartí este artículo