Comment imprimer uniquement la dernière colonne?

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

comment puis-je faire un "MAGIC" pour obtenir cette sortie?:

three
six
nine

MISE À JOUR: Je n'en ai pas besoin de cette manière spécifique, j'ai besoin d'une solution générale pour que peu importe le nombre de colonnes dans une ligne, par exemple: awk affiche toujours la dernière colonne.

LanceBaynes
la source
2
Lance veuillez rechercher vos questions avant de demander. La recherche sur google de la ligne d'objet de vos messages montre la réponse dans les snippents. La recherche dans la «dernière colonne awk» donne plusieurs bonnes réponses en commençant par le résultat 1. De plus, cette introduction awk de 5 minutes mérite d'être lue tout au long afin de savoir ce qui est possible à l'avenir.
Caleb

Réponses:

6

Il peut même se faire qu'avec 'bash', sans 'sed', 'awk'ou '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
jfg956
la source
Hmm, ou aussi, en supposant que votre entrée soit en fait séparée de l'espace:... | while read -r line; do echo ${line##* }; done
glenn jackman
@glenn: Ce fut ma première idée, mais quand j'ai lu le manuel de lecture, j'ai vu cette fonction de tableau que j'ai trouvée utile. Il peut également être facilement modifié pour donner n'importe quel champ indexé à droite.
jfg956
2
bashl'index du tableau est soumis à une évaluation arithmétique, c'est donc echo ${line[nb - 1]}suffisant. En parlant bash, vous pouvez simplement sauter les choses « nb »: echo ${line[-1]}. Une alternative plus portable du plus tard: echo ${line[@]: -1]}. (Voir le commentaire de Stéphane Chazelas sur les indices négatifs ailleurs.)
manatwork
53

Essayer:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'
Sean C.
la source
J'ai mis à jour le Q
LanceBaynes le
veuillez noter que awk est limité à 99 champs ...: / Cela me mord juste ces derniers jours ( ps -ef | awk '{ print $NF }'avait des lignes tronquées ...) Perl n'a pas cette limitation. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "Awk traditionnel a une limite de 99 champs dans un enregistrement. Puisque certaines implémentations Awk, comme Tru64, divisent l'entrée même si vous ne faites pas référence à n'importe quel champ du script, pour contourner ce problème, définissez 'FS' sur un caractère inhabituel et utilisez la division. ")
Olivier Dulac
@OlivierDulac quelles awkimplémentations ont cette limitation? Je ne l'ai jamais vu. Ma mawkvolonté s'étouffera, 32768mais mon gawket igawkpourra faire face à des millions avec bonheur. Même ma awkboîte occupée peut gérer des millions de personnes. Je ne suis jamais tombé sur un awkqui ne peut pas traiter 100 champs, c'est un tout petit nombre, après tout. Êtes-vous sûr que les informations sont toujours pertinentes? Même sur Solaris?
terdon
@terdon, voir les liens dans mon commentaire ^^ (et croyez-moi, un système "hérité" peut survivre à un temps loooooooog dans certains environnements. builtins (ni $ BASH_SOURCE, par exemple), étranglement awk sur NF> 99, etc ... :()
Olivier Dulac
@OlivierDulac assez juste. Je ne l'ai tout simplement pas rencontré. J'espère que c'est extrêmement rare aujourd'hui, car 99 est un nombre minuscule.
terdon
14

C'est plus facile que vous ne le pensez.

$ echo one two three | awk '{print $NF}'
three
bahamat
la source
11

Essayez grep(plus court / plus simple, mais 3 fois plus lent qu'en awkraison de l'utilisation de l'expression régulière):

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

Ou ex(encore plus lent, mais il imprime tout le tampon après la fin, plus utile lorsqu'il doit être trié ou modifié sur place):

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

Pour changer sur place, remplacez -sc'%p|q!'par -scwq.

Ou bash:

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

Performance

Compte tenu du fichier 1 Go généré via:

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

J'ai effectué les statistiques de temps d'analyse (exécuté ~ 3x et pris le plus bas, testé sur MBP OS X):

  • utilisant awk:

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
  • utilisant 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
  • utilisant perl:

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
  • en utilisant 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
  • utilisant 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
  • utilisant bash:

    $ time while read line; do arr=($line); echo ${arr[-1]}; done < datafile > /dev/null
    real    9m42.807s
    user    8m12.553s
    sys 1m1.955s
kenorb
la source
6
... | perl -lane 'print $F[-1]'
glenn jackman
la source
Points clés -a:, champs autosplit dans le @Ftableau; -lcoupe $/(séparateur d'enregistrement d'entrée) et l'enregistre dans $\(séparateur d'enregistrement de sortie). Puisqu'aucun nombre octal n'est fourni avec -l, l'original $/est appliqué sur papier (fins de ligne); -ncode de boucle; -eexécuter le code immédiatement après. Tu vois man perlrun.
Jonathan Komar
5

Cela peut également être fait en utilisant 'sed':

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

Mise à jour:

ou plus simplement:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'
jfg956
la source
plus c'est mieux!
mdpc
@mdpc: Je suis d'accord, mais comme la solution la plus compliquée a déjà été publiée, j'ai décidé de la conserver.
jfg956
5

Ou en utilisant cut:

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

même si cela ne satisfait pas à l'exigence de «solution générale». En utilisant revdeux fois, nous pouvons également résoudre ce problème:

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev
Tim
la source
Je ne pense pas que 'rev' puisse être trouvé sur tous les Unix (AIX, Solaris, ...) ou soit installé sur tous les Linux, mais belle solution alternative.
jfg956
1
+1 pour le double tour, mais en remarque, revne fonctionne pas avec les caractères "larges", uniquement ceux à un octet, pour autant que je sache.
Marcin
2

En utilisant awkvous pouvez d'abord vérifier s'il y a au moins une colonne.

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

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'
gezu
la source
3
Ou moins verbeusement awk 'NF{print $NF}'.
manatwork
1

En perl cela peut se faire comme suit:

#!/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";

sortie:

$ perl last.pl
5
$

Vous pouvez également parcourir un fichier en boucle, voici un exemple de script que j'ai écrit pour analyser un fichier appelé budget.dat

exemples de données dans budget.dat:

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

(vous pouvez voir que je devais capturer uniquement la "dernière" colonne, pas seulement la colonne 2)

Le script:

#!/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";
urbansumo
la source
J'ai réalisé que quelqu'un d'autre avait déjà fait le même commentaire, désolé, au moins j'ai quelques exemples aussi, j'espère que cela ajoutera quand même à la discussion.
urbansumo