linux - query - ¿Cómo usar sed para extraer líneas en el orden especificado?
logstash tutorial (3)
Tengo un archivo que tiene una longitud de ~ 50,000 líneas y necesito recuperar líneas específicas. He intentado el siguiente comando:
sed -n ''Np;Np;Np'' inputFile.txt > outputFile.txt
(''N'' son las líneas específicas, quiero extraer)
Esto funciona bien, pero el comando extrae las líneas en ORDEN (es decir, PIDE PEDIDOS mi entrada) ej. si lo intento
sed -n ''200p;33p;40,000p'' inputFile.txt > outputFile.txt
Recibo un archivo de texto con las líneas ordenadas como: 33, 200, 40,000 (lo cual no funciona para mi propósito). ¿Hay alguna manera de mantener el orden en que aparecen las líneas en el comando?
¿Puedes usar también otros comandos de bash? En ese caso esto funciona:
for i in 200 33 40000; do
sed -n "${i}p" inputFile.txt
done > outputFile.txt
Probablemente esto sea más lento que usar array dentro de sed, pero es más práctico.
Debe mantener la línea 33 hasta que haya visto la línea 200:
sed -n ''33h; 200{p; g; p}; 40000p'' file
Vea el manual para una explicación más detallada: https://www.gnu.org/software/sed/manual/html_node/Other-Commands.html
awk
podría ser más legible:
awk ''
NR == 33 {line33 = $0}
NR == 200 {print; print line33}
NR == 40000 {print}
'' file
Si tiene un número arbitrario de líneas para imprimir en un orden específico, puede generalizar esto:
awk -v line_order="11 3 5 1" ''
BEGIN {
n = split(line_order, inorder)
for (i=1; i<=n; i++) linenums[inorder[i]]
}
NR in linenums {cache[NR]=$0}
END {for (i=1; i<=n; i++) print cache[inorder[i]]}
'' file
con perl
, guarda las líneas de entrada en la variable hash con el número de línea como clave
$ seq 12 20 | perl -nle ''
@l = (5,2,3,1);
$a{$.} = $_ if( grep { $_ == $. } @l );
END { print $a{$_} foreach @l } ''
16
13
14
12
-
$.
es el número de línea ygrep { $_ == $. } @l
grep { $_ == $. } @l
verifica si ese número de línea está presente en la matriz@l
que contiene las líneas deseadas en el orden requerido
como una sola @l
, @l
declaración dentro de BEGIN
para evitar la inicialización en cada iteración y también para garantizar que no haya líneas en blanco si el número de línea está fuera de rango:
$ seq 50000 > inputFile.txt
$ perl -nle ''BEGIN{@l=(200,33,40000)} $a{$.}=$_ if(grep {$_ == $.} @l); END { $a{$_} and print $a{$_} foreach (@l) }'' inputFile.txt > outputFile.txt
$ cat outputFile.txt
200
33
40000
Para entradas suficientemente pequeñas, puede guardar las líneas en una matriz e imprimir los índices necesarios. Tenga en cuenta que el ajuste realizado como índice comienza con 0
$ seq 50000 | perl -e ''$l[0]=0; push @l,<>; print @l[200,33,40000]''
200
33
40000
Solución con combo head
y tail
:
$ for i in 200 33 40000; do head -"${i}" inputFile.txt | tail -1 ; done
200
33
40000
Comparación de rendimiento para el archivo de entrada seq 50000 > inputFile.txt
$ time perl -nle ''BEGIN{@l=(200,33,40000)} $a{$.}=$_ if(grep {$_ == $.} @l); END { $a{$_} and print $a{$_} foreach (@l) }'' inputFile.txt > outputFile.txt
real 0m0.044s
user 0m0.036s
sys 0m0.000s
$ time awk -v line_order="200 33 40000" ''
BEGIN {
n = split(line_order, inorder)
for (i=1; i<=n; i++) linenums[inorder[i]]
}
NR in linenums {cache[NR]=$0}
END {for (i=1; i<=n; i++) print cache[inorder[i]]}
'' inputFile.txt > outputFile.txt
real 0m0.019s
user 0m0.016s
sys 0m0.000s
$ time for i in 200 33 40000; do sed -n "${i}{p;q}" inputFile.txt ; done > outputFile.txt
real 0m0.011s
user 0m0.004s
sys 0m0.000s
$ time sed -n ''33h; 200{p; g; p}; 40000p'' inputFile.txt > outputFile.txt
real 0m0.009s
user 0m0.008s
sys 0m0.000s
$ time for i in 200 33 40000; do head -"${i}" inputFile.txt | tail -1 ; done > outputFile.txt
real 0m0.007s
user 0m0.000s
sys 0m0.000s