GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo seleccionar varias líneas de un archivo o de una tubería en un script?

Puedes usar este awk:

awk -v s='2,4' 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' file
two
four

A través de un script separado lines.sh :

#!/bin/bash
awk -v s="$1" 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' "$2"

Luego otorgue permisos de ejecución:

chmod +x lines.sh

Y llámalo como:

./lines.sh '2,4' 'test.txt'

Prueba sed :

sed -n '2p; 4p' inputFile

-n le dice a sed para suprimir la salida, pero para las líneas 2 y 4 , el p El comando (imprimir) se usa para imprimir estas líneas.

También puede usar rangos, por ejemplo:

sed -n '2,4p' inputFile

Dos versiones puras de Bash. Dado que está buscando soluciones generales y reutilizables, también podría esforzarse un poco en eso. (También, vea la última sección).

Versión 1

Este script absorbe todo el stdin en una matriz (usando mapfile , por lo que es bastante eficiente) y luego imprime las líneas especificadas en sus argumentos. Los rangos son válidos, por ejemplo,

1-4 # for lines 1, 2, 3 and 4
3-  # for everything from line 3 till the end of the file

Puede separarlos con espacios o comas. Las líneas se imprimen exactamente en el orden en que se dan los argumentos:

lines 1 1,2,4,1-3,4- 1

imprimirá la línea 1 dos veces, luego la línea 2, luego la línea 4, luego las líneas 1, 2 y 3, luego todo desde la línea 4 hasta el final, y finalmente, la línea 1 nuevamente.

Aquí tienes:

#!/bin/bash

lines=()

# Slurp stdin in array
mapfile -O1 -t lines

# Arguments:
IFS=', ' read -ra args <<< "$*"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
      arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((to=10#${BASH_REMATCH[2]:-$((${#lines[@]}))}))
      ((from==0)) && from=1
      ((to>=${#lines[@]})) && to=${#lines[@]}
      ((from<=to)) || printf >&2 'Argument %d-%d: lines not in increasing order' "$from" "$to"
      for((i=from;i<=to;++i)); do
         printf '%s\n' "${lines[i]}"
      done
   else
      printf >&2 "Error in argument \`%s'.\n" "$arg"
   fi
done
  • Pro:Es realmente genial.
  • Desventaja:necesita leer todo el flujo en la memoria. No apto para flujos infinitos.

Versión 2

Esta versión aborda el problema anterior de flujos infinitos. Pero perderá la capacidad de repetir y reordenar líneas.

Lo mismo, se permiten rangos:

lines 1 1,4-6 9-

imprimirá las líneas 1, 4, 5, 6, 9 y todo hasta el final. Si el conjunto de líneas está limitado, sale tan pronto como se lee la última línea.

#!/bin/bash

lines=()
tillend=0
maxline=0

# Process arguments
IFS=', ' read -ra args <<< "[email protected]"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
       arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((from==0)) && from=1
      ((tillend && from>=tillend)) && continue
      if [[ -z ${BASH_REMATCH[2]} ]]; then
         tillend=$from
         continue
      fi
      ((to=10#${BASH_REMATCH[2]}))
      if ((from>to)); then
         printf >&2 "Invalid lines order: %s\n" "$arg"
         exit 1
      fi
      ((maxline<to)) && maxline=$to
      for ((i=from;i<=to;++i)); do
         lines[i]=1
      done
   else
      printf >&2 "Invalid argument \`%s'\n" "$arg"
      exit 1
   fi
done

# If nothing to read, exit
((tillend==0 && ${#lines[@]}==0)) && exit

# Now read stdin
linenb=0
while IFS= read -r line; do
   ((++linenb))
   ((tillend==0 && maxline && linenb>maxline)) && exit
   if [[ ${lines[linenb]} ]] || ((tillend && linenb>=tillend)); then
      printf '%s\n' "$line"
   fi
done
  • Pro:es genial y no lee la transmisión completa en la memoria.
  • Con:No se pueden repetir ni reordenar las líneas como en la versión 1. La velocidad no es su punto más fuerte.

Otros pensamientos

Si realmente desea un script general increíble que haga lo que hacen la Versión 1 y la Versión 2, y más, definitivamente debería considerar usar otro lenguaje, por ejemplo, Perl:¡ganará mucho (en particular, velocidad)! podrás tener buenas opciones que harán muchas cosas mucho más geniales. Puede valer la pena a largo plazo, ya que desea un script general y reutilizable. ¡Incluso podría terminar teniendo un script que lea correos electrónicos!

Descargo de responsabilidad. No he revisado a fondo estos scripts... ¡así que tenga cuidado con los errores!


Linux
  1. ¿Cómo eliminar líneas en blanco de un archivo (incluidos tabuladores y espacios)?

  2. ¿Seleccionar líneas del archivo de texto que tienen identificadores enumerados en otro archivo?

  3. ¿Cómo eliminar las líneas que aparecen en el archivo B de otro archivo A?

  4. ¿Cómo comentar varias líneas en un archivo de configuración de Linux?

  5. ¿Cómo mostrar ciertas líneas de un archivo de texto en Linux?

Cómo mover varios tipos de archivos simultáneamente desde la línea de comandos

Cómo unir varias líneas en una en un archivo en Linux

Cómo mezclar líneas en un archivo en Linux

Cómo invertir líneas en un archivo por caracteres en Linux

Cómo quitar líneas de un archivo usando el comando Sed

Cómo quitar (^M) caracteres de un archivo en Linux