Jak wydrukować tylko ostatnią kolumnę?


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

jak mogę zrobić trochę „MAGIC”, aby uzyskać ten wynik ?:

three
six
nine

AKTUALIZACJA: Nie potrzebuję tego w ten konkretny sposób, potrzebuję ogólnego rozwiązania, aby bez względu na to, ile kolumn jest w rzędzie, np .: awk zawsze wyświetla ostatnią kolumnę.


2
Lance, proszę, sprawdź swoje pytania przed zadaniem. Wyszukiwanie w Google wiersza tematu swoich postów pokazuje odpowiedź we fragmentach. Wyszukiwanie „awk ostatniej kolumny” daje kilka świetnych odpowiedzi, zaczynając od wyniku 1. Również ten 5-minutowy podkład awk jest wart przeczytania przez cały czas, abyś wiedział, co jest możliwe w przyszłości.
Caleb

Odpowiedzi:


6

Może nawet być wykonane tylko z 'bash', bez 'sed', 'awk'lub '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, lub też, zakładając, że twój wkład jest faktycznie oddzielony spacją:... | while read -r line; do echo ${line##* }; done
glenn jackman

@glenn: To był mój pierwszy pomysł, ale kiedy przeczytałem instrukcję „czytaj”, zobaczyłem tę funkcję tablicy, która okazała się przydatna. Można go również łatwo zmodyfikować, aby uzyskać indeksowanie dowolnego pola od prawej.
jfg956,

2
bashindeks tablicy jest przedmiotem oceny arytmetycznej, więc echo ${line[nb - 1]}wystarczy. Jak mówić o bash, można po prostu pominąć rzeczy „NB”: echo ${line[-1]}. Bardziej przenośne alternatywa później: echo ${line[@]: -1]}. (Zobacz komentarz Stephane Chazelas na temat negatywnych indeksów w innym miejscu.)
manatwork

53

Próbować:

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

Zaktualizowałem Q
LanceBaynes

proszę zauważyć, że awk jest ograniczony do 99 pól ...: / To mnie ugryzło w ciągu ostatnich dni ( ps -ef | awk '{ print $NF }'niektóre linie zostały obcięte ...) Perl nie ma tego ograniczenia. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : „Tradycyjny Awk ma limit 99 pól w rekordzie. Ponieważ niektóre implementacje Awk, takie jak Tru64, dzielą dane wejściowe, nawet jeśli nie odwołujesz się w dowolnym polu skryptu, aby obejść ten problem, ustaw „FS” na nietypową postać i użyj podziału. ”)
Olivier Dulac

@OlivierDulac jakie awkimplementacje mają to ograniczenie? Nigdy tego nie widziałem. Moja mawkdusi się, 32768ale moja gawki igawkmoże szczęśliwie poradzić sobie z milionami. Nawet mój busybox awkradzi sobie z milionami. Nigdy nie spotkałem czegoś, awkco nie poradziłoby sobie ze 100 polami, w końcu to niewielka liczba. Czy jesteś pewien, że informacje są nadal aktualne? Nawet w systemie Solaris?
terdon

@terdon, zobacz linki w moim komentarzu ^^ (i wierz mi, niektóre „starsze” systemy mogą przetrwać bardzo długi czas w niektórych środowiskach. na niektórych, wyciąg z tar hapilly do „/”, bash nie ma pożytecznego wbudowane (na przykład $ BASH_SOURCE), awk dławik na NF> 99 itp.::)
Olivier Dulac

@OlivierDulac wystarczy. Po prostu tego nie spotkałem. Mam nadzieję, że dziś jest to niezwykle rzadkie, ponieważ 99 to niewielka liczba.
terdon

14

To łatwiejsze niż myślisz.

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

11

Spróbuj grep(krótszy / prostszy, ale 3 razy wolniejszy niż z awkpowodu użycia wyrażenia regularnego):

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

Lub ex(jeszcze wolniej, ale drukuje cały bufor po zakończeniu, bardziej przydatny, gdy trzeba go posortować lub edytować na miejscu):

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

Aby zmienić na miejscu, zamień -sc'%p|q!' z -scwq.

Lub bash:

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

Wydajność

Biorąc pod uwagę wygenerowany plik 1GB przez:

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

Przeprowadziłem statystyki czasu analizowania (uruchomiłem ~ 3x i wziąłem najniższą, przetestowaną na MBP OS X):

  • za pomocą awk:

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

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

Kluczowe punkty -a:, automatyczne dzielenie pól na @Ftablicę; -lodcina $/(separator rekordów wejściowych) i zapisuje go w $\(separator rekordów wyjściowych). Ponieważ nie podano liczby ósemkowej -l, oryginał $/jest drukowany (zakończenia linii); -nkod pętli; -enatychmiast wykonaj kod. Zobaczyć man perlrun.
Jonathan Komar

5

Można to również zrobić za pomocą 'sed':

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

Aktualizacja:

lub prościej:

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

po prostu jest lepiej!
mdpc,

@mdpc: Zgadzam się, ale ponieważ opublikowano już bardziej skomplikowane rozwiązanie, postanowiłem je zachować.
jfg956,

5

Lub używając cut:

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

chociaż nie spełnia to wymogu „ogólnego rozwiązania”. Używając revdwa razy, możemy rozwiązać to również:

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

Nie sądzę, że „rev” można znaleźć na wszystkich systemach Unix (AIX, Solaris, ...) lub jest zainstalowany na wszystkich systemach Linux, ale fajne alternatywne rozwiązanie.
jfg956,

1
+1 dla podwójnych obrotów, ale na marginesie, revnie działa z „szerokimi” znakami, tylko z jednobajtowymi, o ile wiem.
Marcin

2

Za pomocą awkmożesz najpierw sprawdzić, czy jest co najmniej jedna kolumna.

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

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

3
Lub mniej gadatliwie awk 'NF{print $NF}'.
manatwork

1

W Perlu można to zrobić w następujący sposób:

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

wydajność:

$ perl last.pl
5
$

Możesz również przejść przez plik, oto przykładowy skrypt, który napisałem, aby przeanalizować plik o nazwie budżet.dat

przykładowe dane w budget.dat:

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

(widać, że potrzebowałem przechwycić tylko „ostatnią” kolumnę, a nie tylko kolumnę 2)

Scenariusz:

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

Uświadomiłem sobie, że ktoś już to skomentował, przepraszam za to, przynajmniej mam też kilka przykładów, mam nadzieję, że i tak to się przyczyni do dyskusji.
urbansumo
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.