Come stampare solo l'ultima colonna?


25
echo -e 'one two three\nfour five six\nseven eight nine'
one two three
four five six
seven eight nine

come posso fare un po 'di "MAGIA" per ottenere questo output ?:

three
six
nine

AGGIORNAMENTO: Non ne ho bisogno in questo modo specifico, ho bisogno di una soluzione generale in modo che, indipendentemente da quante colonne ci siano in una riga, ad esempio: awk visualizza sempre l'ultima colonna.


2
Lance, per favore, cerca le tue domande prima di porle. La ricerca su google della riga dell'oggetto dei tuoi post mostra la risposta negli frammenti. La ricerca di "ultima colonna awk" fornisce diverse ottime risposte a partire dal risultato 1. Inoltre, vale la pena leggere questo primer awk di 5 minuti, in modo da sapere cosa è possibile in futuro.
Caleb,

Risposte:


6

Si può anche essere fatto solo con 'bash', senza 'sed', 'awk'o 'perl':

echo -e 'one two three\nfour five six\nseven eight nine' |
  while IFS=" " read -r -a line; do
    nb=${#line[@]}
    echo ${line[$((nb - 1))]}
  done

Hmm, o anche, supponendo che il tuo input sia effettivamente separato ... | while read -r line; do echo ${line##* }; done
dallo

@glenn: Questa è stata la mia prima idea, ma quando ho letto il manuale 'read', ho visto questa funzione di array che ho trovato utile. Può anche essere facilmente modificato per dare qualsiasi campo indicizzato da destra.
jfg956,

2
bashl'indice di array è oggetto di valutazione aritmetica, quindi echo ${line[nb - 1]}è sufficiente. Come parlare bash, si può semplicemente ignorare le cose “NB”: echo ${line[-1]}. Un altro portatile alternativa del dopo: echo ${line[@]: -1]}. (Vedi il commento di Stephane Chazelas sugli indici negativi altrove.)
Manatwork

53

Provare:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'

Ho aggiornato Q
LanceBaynes il

per favore nota che awk è limitato a 99 campi ...: / Questo mi ha appena morso nei giorni scorsi ( ps -ef | awk '{ print $NF }'alcune linee sono state troncate ...) Perl non ha questa limitazione. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "Awk tradizionale ha un limite di 99 campi in un record. Dato che alcune implementazioni Awk, come Tru64, dividono l'input anche se non si fa riferimento in qualsiasi campo della sceneggiatura, per aggirare questo problema, imposta "FS" su un carattere insolito e usa la divisione ".)
Olivier Dulac,

@OlivierDulac quali awkimplementazioni hanno questa limitazione? Non l'ho mai visto La mia mawkvolontà è soffocare 32768ma la mia gawke igawkpuò gestire milioni di persone felicemente. Anche i miei impegni awkpossono gestire milioni. Non mi sono mai imbattuto in un awkche non può gestire 100 campi, questo è un numero esiguo, dopo tutto. Sei sicuro che le informazioni siano ancora pertinenti? Anche su Solaris?
terdon

@terdon, vedi i link nel mio commento ^^ (e credimi, qualche sistema "legacy" può sopravvivere a un tempo di loooooooog in alcuni ambienti. su alcuni, tar hapilly estrae in "/", bash non ha alcuni degli utili builtin (né $ BASH_SOURCE, per esempio), awk soffocare su NF> 99, ecc ... :()
Olivier Dulac,

@OlivierDulac abbastanza giusto. Non l'ho mai trovato. Spero che oggi sia raramente raro poiché 99 è un numero esiguo.
terdon

14

È più facile di quanto pensi.

$ echo one two three | awk '{print $NF}'
three

11

Prova grep(più breve / più semplice, ma 3 volte più lento rispetto awkall'uso di regex):

grep -o '\S\+$' <(echo -e '... seven eight nine')

Oppure ex(ancora più lento, ma stampa l'intero buffer al termine, più utile quando deve essere ordinato o modificato sul posto):

ex -s +'%s/^.*\s//g' -c'%p|q!' <(echo -e '... seven eight nine')
ex +'%norm $Bd0' -sc'%p|q!' infile

Per cambiare sul posto, sostituirlo -sc'%p|q!'con -scwq.

Oppure bash:

while read line; do arr=($line); echo ${arr[-1]}; done < someinput

Prestazione

Dato il file da 1 GB generato tramite:

$ hexdump -C /dev/urandom | rev | head -c1G | pv > datafile

Ho eseguito le statistiche del tempo di analisi (eseguito ~ 3x e preso il minimo, testato su MBP OS X):

  • utilizzando awk:

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
  • utilizzando grep:

    $ time grep -o '\S\+$' datafile > /dev/null
    real    0m36.731s
    user    0m36.244s
    sys 0m0.401s
    
    $ time grep -o '\S*$' datafile > /dev/null
    real    0m40.865s
    user    0m39.756s
    sys 0m0.415s
  • utilizzando perl:

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
  • usando rev+ cut:

    $ time (rev|cut -d' ' -f1|rev) < datafile > /dev/null
    $ time rev datafile | cut -d' ' -f1 | rev > /dev/null
    real    1m10.342s
    user    1m19.940s
    sys 0m1.263s
  • utilizzando ex:

    $ time ex +'%norm $Bd0_' -sc'%p|q!' datafile > /dev/null
    real    3m47.332s
    user    3m42.037s
    sys 0m2.617s
    $ time ex +'%norm $Bd0' -sc'%p|q!' datafile > /dev/null
    real    4m1.527s
    user    3m44.219s
    sys 0m6.164s
    $ time ex +'%s/^.*\s//g' -sc'%p|q!' datafile > /dev/null
    real    4m16.717s
    user    4m5.334s
    sys 0m5.076s
  • utilizzando bash:

    $ time while read line; do arr=($line); echo ${arr[-1]}; done < datafile > /dev/null
    real    9m42.807s
    user    8m12.553s
    sys 1m1.955s

6
... | perl -lane 'print $F[-1]'

Punti chiave -a:, campi autosplit in @Farray; -ltaglia $/(separatore record di input) e lo salva in $\(separatore record di output). Poiché non viene fornito alcun numero ottale -l, l'originale $/viene applicato in stampa (terminazioni di riga); -ncodice loop; -eeseguire il codice immediatamente dopo. Vedere man perlrun.
Jonathan Komar,

5

Può anche essere fatto usando 'sed':

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* \([^ ]*\)$/\1/'

Aggiornare:

o più semplicemente:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'

più semplicemente è meglio!
mdpc,

@mdpc: sono d'accordo, ma poiché la soluzione più complicata era già stata pubblicata, ho deciso di mantenerla.
jfg956,

5

O usando cut:

echo -e 'one two three\nfour five six\nseven eight nine' | cut -f 3 -d' '

sebbene ciò non soddisfi il requisito della "soluzione generale". Usando revdue volte possiamo risolvere anche questo:

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev

Non credo che 'rev' possa essere trovato su tutti Unix (AIX, Solaris, ...) o sia installato su tutti Linux, ma una bella soluzione alternativa.
jfg956,

1
+1 per doppio giro, ma come nota a margine, revnon funziona con caratteri "larghi", solo quelli a byte singolo, per quanto ne so.
Marcin

2

utilizzando awk puoi prima verificare se c'è almeno una colonna.

echo | awk '{if (NF >= 1) print $NF}'

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'

3
O meno verbosamente awk 'NF{print $NF}'.
arte

1

In perl questo può essere fatto come segue:

#!/usr/bin/perl

#create a line of arbitrary data
$line = "1 2 3 4 5";

# splt the line into an array (we call the array 'array', for lolz)
@array = split(' ', $line);

# print the last element in the array, followed by a newline character;
print "$array[-1]\n";

produzione:

$ perl last.pl
5
$

Puoi anche scorrere un file in sequenza, ecco uno script di esempio che ho scritto per analizzare un file chiamato budget.dat

dati di esempio in budget.dat:

Rent              500
Food              250
Car               300
Tax               100
Car Tax           120
Mag Subscription  15

(puoi vedere che dovevo catturare solo l'ultima "colonna", non solo la colonna 2)

Il copione:

#!/usr/bin/perl
$budgetfile = "budget.dat";
open($bf, $budgetfile)
        or die "Could not open filename: $filename $!";


print "-" x 50, "\n";
while ( $row = <$bf> ) {
        chomp $row;
        @r = split (' ', $row);
        print "$row ";
        $subtotal += $r[-1];
        print "\t$subtotal\n";
}
print "-" x 50, "\n";
print "\t\t\t Total:\t$subtotal\n\n";

Mi sono reso conto che qualcun altro ha già commentato lo stesso, mi dispiace, almeno ho anche alcuni esempi, spero che si aggiunga comunque alla discussione.
urbansumo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.