Tengo un archivo de texto con líneas vacías que separan bloques de texto. Me gustaría utilizar las herramientas de línea de comandos *NIX para mezclar este archivo respetando la estructura del bloque. En otras palabras, en el resultado me gustaría ver el orden cambiado de los bloques; las líneas y su orden dentro del bloque siguen siendo las mismas.
Ejemplo de archivo de entrada:
line 1
line 2
line 10
line 20
line 30
line 100
line 200
El archivo de salida (después de la reproducción aleatoria):
line 10
line 20
line 30
line 1
line 2
line 100
line 200
Por supuesto, ejecutar repetidamente debería dar un orden diferente de bloques.
La primera línea del archivo siempre no está vacía. No hay dobles líneas en blanco. La última línea del archivo siempre está vacía.
Escribí un script Python muy simple que lee todas las líneas en una lista de listas, las mezcla y genera. Tengo curiosidad por saber si podría hacerlo con herramientas *NIX estándar.
Respuesta1
POSIXly, podrías hacer algo como:
<file awk '
BEGIN{srand(); n=rand()}
{print n, NR, $0}
!NF {n=rand()}
END {if (NF) print n, NR+1, ""}' |
sort -nk1 -k2 |
cut -d' ' -f3-
Es decir, prefije cada línea con <a-random-number-that-changes-with-each-paragraph>
el número de línea, luego ordene numéricamente el primer número y luego el segundo para mantener el orden de las líneas en los párrafos y eliminar esos números adicionales.
Es posible que desee conectarse para sed '$d'
eliminar la línea en blanco final.
Tenga en cuenta que con la mayoría de awk
las implementaciones srand()
se utiliza la época de Unix para generar el generador de números pseudoaleatorios, por lo que puede obtener el mismo resultado si se ejecuta dos veces en el mismo segundo (unerror histórico ahora grabado en la especificación POSIX, desafortunadamente a pesar de mis esfuerzos).
Respuesta2
Usando herramientas GNU, esto divide los párrafos en grupos separados por NUL, los mezcla y luego elimina los NUL:
$ sed '1s/^/\n/; s/^$/\x00/' input | shuf -z | sed '1d; s/\x00//'
line 100
line 200
line 10
line 20
line 30
line 1
line 2
Enfoque alternativo sin utilizar NUL
Dado que no todas las herramientas admiten caracteres NUL, aquí tienes una alternativa. Esto lee los párrafos, sustituye ~
las nuevas líneas, luego los mezcla y luego convierte el ~
reverso en nuevas líneas antes de mostrar los resultados:
$ awk '{gsub(/\n/, "~")} 1' RS= input | shuf | awk '{gsub(/~/, "\n")} 1' ORS="\n\n"
line 10
line 20
line 30
line 100
line 200
line 1
line 2
Si su texto puede contener ~
, utilice otro carácter que el texto no contendrá como separador de línea temporal.
Respuesta3
Usando Perl:
perl -MList::Util -00 -e 'chomp(my @a=<>); print join("\n\n", List::Util::shuffle @a) . "\n";' < input
O distribuirlo como un archivo de script:
#!/usr/bin/perl
use List::Util 'shuffle';
local $/ = ""; ## paragraph mode
chomp(my @a = <>);
print join("\n\n", shuffle @a) . "\n";