Как напечатать только последний столбец?


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

Как я могу сделать некоторые "MAGIC" получить этот вывод ?:

three
six
nine

ОБНОВЛЕНИЕ: мне это не нужно определенным образом, мне нужно общее решение, чтобы независимо от количества столбцов в строке, например: awk всегда отображал последний столбец.


2
Ланс, пожалуйста, изучите ваши вопросы, прежде чем спрашивать. Поиск в строке темы ваших сообщений в Google показывает ответ во фрагментах. Поиск в «awk last column» дает несколько отличных ответов, начиная с результата 1. Кроме того, этот 5-минутный учебник awk стоит прочитать до конца, чтобы вы знали, что возможно в будущем.
Калеб

Ответы:


6

Он даже может быть сделано только с 'bash', без 'sed', 'awk'или '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

Хм, или также, предполагая, что ваш ввод фактически разделен пробелами:... | while read -r line; do echo ${line##* }; done
Гленн Джекман

@glenn: Это была моя первая идея, но когда я прочитал руководство по прочтению, я увидел функцию массива, которая показалась мне полезной. Это также может быть легко изменено, чтобы дать любое поле, проиндексированное справа.
jfg956

2
bashИндекс массива является предметом арифметической оценки, поэтому echo ${line[nb - 1]}достаточно. Как говорить о bash, вы можете просто пропустить «NB» вещи: echo ${line[-1]}. Более портативная альтернатива позже: echo ${line[@]: -1]}. (См. Комментарий Стефана Чазела об отрицательных показателях в других местах.)
manatwork

53

Пытаться:

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

Я обновил Q
LanceBaynes

пожалуйста, обратите внимание, что awk ограничен 99 полями ...: / Это просто укусило меня в последние дни ( ps -ef | awk '{ print $NF }'некоторые строки были обрезаны ...) Perl не имеет такого ограничения. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : «Традиционный Awk имеет ограничение в 99 полей в записи. Поскольку некоторые реализации Awk, такие как Tru64, разделяют ввод, даже если вы не ссылаетесь в любое поле сценария, чтобы обойти эту проблему, установите 'FS' для необычного символа и используйте расщепление. ")
Оливье Дюлак

@OlivierDulac, какие awkреализации имеют такое ограничение? Я никогда не видел это. Мой mawkбудет подавлен, 32768но мой gawkи igawkможет справиться с миллионами счастливо. Даже мои занятые ящики awkмогут справиться с миллионами. Я никогда не сталкивался с тем, awkчто не может иметь дело со 100 полями, это крошечное число, в конце концов. Вы уверены, что информация все еще актуальна? Даже на солярисе?
Тердон

@terdon, см. ссылки в моем комментарии ^^ (и, поверьте мне, в некоторых средах некоторая «устаревшая» система может выдержать время дооооооооообного. В некоторых случаях tar hapilly извлекается в «/», bash не имеет некоторых полезных свойств). встроенные функции (ни $ BASH_SOURCE, например), awk подавляется NF> 99 и т. д. :()
Оливье Дюлак

@ OlivierDulac достаточно честно. Я просто не сталкивался с этим. Я надеюсь, что сегодня это исчезающе редко, так как 99 - крошечное число.
Тердон

14

Это проще, чем вы думаете.

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

11

Попробуйте grep(короче / проще, но в 3 раза медленнее, чем awkиз-за использования регулярных выражений):

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

Или ex(еще медленнее, но он печатает весь буфер после финиша, более полезно, когда его нужно отсортировать или отредактировать на месте):

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

Чтобы изменить на месте, замените -sc'%p|q!'на -scwq.

Или bash:

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

Спектакль

Учитывая сгенерированный файл 1GB через:

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

Я выполнил статистику времени разбора (побежал ~ 3х и взял самый низкий, протестирован на MBP OS X):

  • используя awk:

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
  • используя 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
  • используя perl:

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
  • используя 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
  • используя 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
  • используя 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]'

Ключевые моменты: -aавтоматическое разбиение полей на @Fмассивы; -lотключает $/(разделитель входных записей) и сохраняет его в $\(разделитель выходных записей). Поскольку не предоставлено восьмеричное число -l, оригинал $/применяется в печати (окончания строк); -nкод цикла; -eвыполнить код сразу после этого. См man perlrun.
Джонатан Комар

5

Это также можно сделать с помощью 'sed':

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

Обновить:

или проще:

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

проще - лучше!
mdpc

@mdpc: Я согласен, но поскольку более сложное решение уже было опубликовано, я решил оставить его.
jfg956

5

Или используя cut:

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

хотя это не удовлетворяет требованию «общего решения». Используя revдважды, мы можем решить и это:

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

Я не думаю, что 'rev' можно найти на всех Unix (AIX, Solaris, ...) или установлен на всех Linux, но хорошее альтернативное решение.
jfg956

1
+1 для двойного оборота, но, как примечание, revне работает с «широкими» символами, только однобайтовыми, насколько я знаю.
Марчин

2

Используя, awkвы можете сначала проверить, есть ли хотя бы один столбец.

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

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

3
Или менее многословно awk 'NF{print $NF}'.
manatwork

1

В Perl это можно сделать следующим образом:

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

выход:

$ perl last.pl
5
$

Вы также можете перебрать файл, вот пример сценария, который я написал, для разбора файла с именем budget.dat

пример данных в budget.dat:

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

(вы можете видеть, что мне нужно захватить только «последний» столбец, а не только столбец 2)

Сценарий:

#!/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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.