Як надрукувати лише останній стовпець?


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" дає декілька чудових відповідей, починаючи з результату 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 jackman

@glenn: Це була моя перша ідея, але коли я прочитав посібник "читати", побачив цю функцію масиву, яку вважав корисною. Його також можна легко змінити, щоб дати будь-яке поле, індексоване праворуч.
jfg956

2
bashІндекс масиву є предметом арифметичної оцінки, тому echo ${line[nb - 1]}цього достатньо. Як говорити про bash, ви можете просто пропустити «NB» речі: echo ${line[-1]}. Більш портативна альтернатива пізніше: echo ${line[@]: -1]}. (Дивіться коментар Стефана Шазеласа про негативні індекси в інших місцях.)
маніпулювання

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 полями, це все-таки крихітна кількість. Ви впевнені, що інформація все ще актуальна? Навіть на Solaris?
тердон

@terdon, дивіться посилання в моєму коментарі вбудовані (ні $ BASH_SOURCE, наприклад), awk choke на NF> 99 тощо ... :()
Олів'є Дулак

@OlivierDulac досить справедливо. Я просто не натрапив на це. Я сподіваюся, що це сьогодні зникає рідко, оскільки 99 - це невелике число.
тердон


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

Продуктивність

З огляду на згенерований 1 ГБ файл через:

$ 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}'.
манатура

1

У перлі це можна зробити так:

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

Я зрозумів, що хтось уже прокоментував те саме, пробачте про це, принаймні, у мене є також кілька прикладів, сподіваюся, це все одно додає до дискусії.
урбансумо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.