GNU/Linux >> Linux Esercitazione >  >> Linux

Come selezionare più righe da un file o da pipe in uno script?

Puoi usare questo awk:

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

Tramite uno script separato lines.sh :

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

Quindi dai i permessi di esecuzione:

chmod +x lines.sh

E chiamalo come:

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

Prova sed :

sed -n '2p; 4p' inputFile

-n dice sed per sopprimere l'output, ma per le righe 2 e 4 , il p Il comando (print) viene utilizzato per stampare queste righe.

Puoi anche utilizzare intervalli, ad esempio:

sed -n '2,4p' inputFile

Due versioni Bash pure. Dal momento che stai cercando soluzioni generali e riutilizzabili, potresti anche impegnarti un po '. (Vedi anche l'ultima sezione).

Versione 1

Questo script inserisce l'intero stdin in un array (usando mapfile , quindi è piuttosto efficiente) e quindi stampa le righe specificate sui suoi argomenti. Gli intervalli sono validi, ad esempio,

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

Puoi separarli con spazi o virgole. Le righe vengono stampate esattamente nell'ordine in cui sono forniti gli argomenti:

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

stamperà la riga 1 due volte, quindi la riga 2, quindi la riga 4, quindi le righe 1, 2 e 3, quindi tutto dalla riga 4 fino alla fine e infine di nuovo la riga 1.

Ecco qua:

#!/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:è davvero fantastico.
  • Contro:necessita di leggere l'intero flusso in memoria. Non adatto a flussi infiniti.

Versione 2

Questa versione risolve il precedente problema dei flussi infiniti. Ma perderai la possibilità di ripetere e riordinare le righe.

Stessa cosa, gli intervalli sono consentiti:

lines 1 1,4-6 9-

stamperà le righe 1, 4, 5, 6, 9 e tutto fino alla fine. Se l'insieme di righe è delimitato, esce non appena viene letta l'ultima riga.

#!/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:è davvero fantastico e non legge l'intero flusso in memoria.
  • Contro:impossibile ripetere o riordinare le righe come nella versione 1. La velocità non è il suo punto di forza.

Ulteriori riflessioni

Se vuoi davvero uno script generale fantastico che faccia ciò che fa la versione 1 e la versione 2, e altro ancora, dovresti assolutamente considerare l'utilizzo di un altro linguaggio, ad esempio Perl:guadagnerai molto (in particolare velocità)! sarai in grado di avere belle opzioni che faranno molte cose molto più interessanti. Potrebbe valerne la pena a lungo termine, poiché desideri uno script generale e riutilizzabile. Potresti persino ritrovarti con uno script che legge le email!

Esclusione di responsabilità. Non ho controllato a fondo questi script... quindi attenzione ai bug!


Linux
  1. Come creare un file temporaneo nello script della shell?

  2. Come eliminare più righe casuali da un file di testo utilizzando Sed?

  3. Come rimuovere le linee che appaiono sul file B da un altro file A?

  4. Come commentare più righe in un file di configurazione di Linux?

  5. Come seleziono tutto il testo da un file con nano?

Come spostare più tipi di file contemporaneamente dalla riga di comando

Come unire più righe in una in un file in Linux

Come mescolare le linee in un file in Linux

Come invertire le righe in un file in base al carattere in Linux

Come rimuovere le righe da un file usando il comando Sed

Come rimuovere (^M) caratteri da un file in Linux