como imprimirlíneas antes ylineas despuesª línea

como imprimirlíneas antes ylineas despuesª línea

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 pcomando imprimir ( ) sedcon 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 startel número de líneas. Al llegar al endnúmero de líneas, el script sale.

Ambas alternativas mostrarán una parte de los datos de entrada, comenzando en xlas líneas antes de la línea zy terminando en ylas 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 sedse 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 FSpara \nque 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

información relacionada