la pregunta formuladaaquíSolicita algunas líneas antes y después de que coincida el patrón.
Pero aquí el objetivo es tomar un número de línea y recuperar algunas líneas antes y después de un archivo.
P.EJ:
seq 10
1
2
3
4
5
6
7
8
9
10
Si el número de línea es 6, debe proporcionar 4 números antes de esa línea y 3 números después de esa línea. Eso es
2
3
4
5
6
7
8
9
Respuesta1
z=6 # focus line
x=4 # lines before
y=3 # lines after
start=$(( z - x ))
end=$(( z + y ))
Usando sed
:
seq 10 | sed -n "$start,${end}p"
2
3
4
5
6
7
8
9
Esto simplemente usa el p
comando imprimir ( ) sed
con un rango explícito de líneas para imprimir. Las otras líneas se ignoran usando -n
.
Usando awk
:
seq 10 | awk -v start="$start" -v end="$end" 'NR >= start { print } NR >= end { exit }'
2
3
4
5
6
7
8
9
Esto es similar a la respuesta de Stéphane Chazelas, pero implementada en awk
; el script comienza a generar las líneas de entrada después de haber leído start
el número de líneas. Al llegar al end
número de líneas, el script sale.
Ambas alternativas mostrarán una parte de los datos de entrada, comenzando en x
las líneas antes de la línea z
y terminando en y
las líneas después de la línea z
.
Respuesta2
Con shells POSIX:
$ before=4 after=3 line=6
$ seq 10 | sed "$((line-before)),\$!d; $((line+after))q"
2
3
4
5
6
7
8
9
Se traduce a:
- delimine cualquier línea excepto (
!
) del rango dellínea - antesº hasta el final ($
). - qtraje en ellínea + despuésª línea
De esa manera ni siquiera nos molestamos en leer más allá dellínea + despuésª línea.
Sin embargo, eso significa que el comando que alimenta sus datos sed
se cancelará con un SIGPIPE si continúa enviando datos poco después de lo que puede ser deseable o no.
Respuesta3
Sólo para completar:
$ l=60;seq 100 |head -n$((l+3)) |tail -n+$((l-4))
56
57
58
59
60
61
62
63
Los rumores y varios benchmarks dicen que la combinación cabeza + cola es mucho más rápida que cualquier otra herramienta:
$ a=1000000000
$ time seq $a |awk 'NR>=499998{print}NR >= 500004 { exit }'
499998
499999
500000
500001
500002
500003
real 0m0.158s
user 0m0.152s
sys 0m0.004s
$ time seq $a |sed -n "499998,500003p"
499998
499999
500000
500001
500002
500003
real 1m30.249s
user 1m21.284s
sys 0m12.312s
$ time seq $a |sed "$((500000-2)),\$!d; $((500000+3))q" #Stephan's Solution
499998
499999
500000
500001
500002
500003
real 0m0.052s
user 0m0.044s
sys 0m0.004s
$ time seq $a |head -n$((500000+3)) |tail -n+$((500000-2))
499998
499999
500000
500001
500002
500003
real 0m0.024s
user 0m0.024s
sys 0m0.004s
$ time seq $a |sed -n "499998,500003p;500004q"
499998
499999
500000
500001
500002
500003
real 0m0.056s
user 0m0.048s
sys 0m0.004s
Respuesta4
# define line range constants
before=4
line=6
after=3
# setup the sed commands s.t. pattern space holds $before number
# of lines before we hit the line number $line and $after after
s='$!N'
p=`seq -s "$s" "$before"`
a=`seq -s "$s" 0 "$after"`
N=${p//[0-9]/;}
n=${a//[0-9]/;}
# main...
seq 10 |
sed -e "
1{ $N }
\$d;N
$line!D
$n;q
"
Otro método es sorber el archivo y configurarlo FS
para \n
que los campos (ahora líneas) estén en formato @F
. Lo que queda es cortarlo alrededor de la sexta línea y 4 elementos antes y 3 líneas después:
perl -alF\\n -0777ne '$,=$\;print @F[6-4-1..6+3-1]' yourfile
Resultados
2
3
4
5
6
7
8
9