Pull to refresh

Comments 58

agup@web ~ $ ls -la |cut -f 4
total 8
drwxr-xr-x 4 agup agup 160 2010-09-20 08:03.
drwxr-xr-x 5 link01n wheel 344 2010-09-15 00:42…
-rw------- 1 agup agup 1524 2010-09-20 02:49 .bash_history
-rw------- 1 agup ssmtp 933 2010-09-20 02:10 dead.letter
drwx------ 3 agup agup 200 2010-09-20 02:49 .mc
drwxr-xr-x 2 agup agup 80 2010-09-15 00:45 .ssh
agup@web ~ $ ls -la |awk '{print $4}'

agup
wheel
agup
ssmtp
agup
agup

мне одному кажет, что они всё же по разному работают при разделителе «пробел»?
не прокатит. К примеру если попробуешь 5ую колонку выбрать — обломаешься. cut — очень тупой топор, он понимает только сингл чар делимитер, по дефолту табы, поэтому все и юзают awk. Ты не можешь задать кату регексовый аналог \s+ в принципе.
«в принципе»?
ls -la | tr -s ' ' | cut -f 4 -d ' '
Именно что по разному.

Если cut встречает три пробела подряд то думает что это три колонки.
void@void-desktop:~$ cat /proc/diskstats | grep 'sda1' | awk '{ print $11 }'
11616912
void@void-desktop:~$ cat /proc/diskstats | grep 'sda1' | cut -d " " -f 11
1
void@void-desktop:~$ cat /proc/diskstats | grep 'sda1' 
   8       1 sda1 63909 32026 3654959 1353464 515988 6240056 54055512 11617108 0 2153816 12972816

grep 'sda1' | awk '{ print $11 }'

awk '/sda1/{print $11}'
Кстати да — раз уж такая пьянка — grep ненужен.
по умолчанию, похоже, только TAB разделитель для cut
«use DELIM instead of TAB for field delimiter» (с) man cut
> В современном линуксе обработка вызова awk куда более сложна, чем вызов cut.
Настолько большие издержки?
доходит до смешного:

time awk -F: '{print $1}' /var/log/messages

real 0m0.186s
user 0m0.032s
sys 0m0.024s

time cut -f 1 -d: -s /var/log/messages

real 0m0.237s
user 0m0.151s
sys 0m0.027s
с грепом на регекспах даже не стал проверять, регекспы весьма тормозная штука сама по себе.

считаю, что проблема высосана автором из пальца
это у вас /var/log/messages удлинился к моменту cut'а =)

на самом деле не вижу в awk ничего предосудительного, отличный инструмент с кучей возможностей и почему автор против его использования не понимаю, пусть даже это и простые конструкции, но они же есть в нем.
На самом деле awk действительно медленнее. В конкретно этой операции выигрыш от использования cut — 2-3 раза, но операция даже на логах 2-3мб выполняется за сотые доли секунды и мне сложно представить ситуацию когда это было бы важно…
Если же взять какой то сложный текст то тут разница будет очень большая, в десятки раз, но в случае со сложным текстом cut в 99% случаев просто бесполезен в силу своей примитивности.
Я бы скорее говорил о множестве запусков, а не о единичном сложном случае.
да нет выигрыша от cat'а. достигается выиграш только в том случае, когда обрабатывается очень маленький файлик. awk намного производительнее.
UFO just landed and posted this here
вывод — в 10 раз больше еще не значит медленнее :)
Почти всегда более быстрый алгоритм занимает больше кода, чем медленный
awk код вызвал одно слово из памяти:
«ебанись!» :-)
Вы не то считаете. Речь не о скорости единичного вызова, а о множестве вызовов:

time for a in `seq 1 1000`; do echo /etc/inittab|awk '{print $1}'>/dev/null;done

real 0m1.942s
user 0m0.620s
sys 0m1.760s

time for a in `seq 1 1000`; do echo /etc/inittab|cut -f 1 -s — >/dev/null;done

real 0m1.495s
user 0m0.368s
sys 0m1.516s

Вот об этих милисекундах речь и идёт.
Это, простите, Вы не то считаете. Вы запустите обработку крупного файла, а не мелкого и увидете, что cat крайне не эффективен собственно в обработке. Кажущаяся выгода за счет размера cat'а нивелируется тем, что сам обработчик awk значительно эффективнее.

igor@gonzo:~$ time for a in `seq 1 1000`; do cut -f 1 -d: -s /var/log/messages > /dev/none; done

real 1m20.908s
user 1m14.933s
sys 0m2.538s
igor@gonzo:~$ time for a in `seq 1 1000`; do awk -F: '{print $1}' /var/log/messages > /dev/null; done

real 0m9.973s
user 0m6.062s
sys 0m2.663s
фу, какое-то масло-масляное получилось во фразе.
Вместо time лучше сделать strace -c ИМХО нагляднее
# strace -c awk '{print $1}' /var/log/asterisk/full.3 > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 98.92    0.003481           0     14284           read
  1.08    0.000038           0       649           write
  0.00    0.000000           0         7           open
  0.00    0.000000           0         7           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         3         3 ioctl
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         3           rt_sigaction
  0.00    0.000000           0        13           mmap2
  0.00    0.000000           0        12           fstat64
  0.00    0.000000           0         2           getgroups32
  0.00    0.000000           0         1           fcntl64
  0.00    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.003519                 14992         4 total
# strace -c cut -f 1  /var/log/asterisk/full.3 > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 62.96    0.004210           1      7143           read
 36.59    0.002447           0     14280           write
  0.45    0.000030           3        10           mmap2
  0.00    0.000000           0         5           open
  0.00    0.000000           0         6           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         3           munmap
  0.00    0.000000           0         2           mprotect
  0.00    0.000000           0         6           fstat64
  0.00    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.006687                 21462         2 total


блин, про strace я забыл начисто, спасибо.

количество вызовов явно говорит не в пользу cat, что и требовалось доказать.

похоже, что данный топик доказывает одно: если люди годами в шелле используют какой-то простой метод и не используют другие, так же хорошо известные — то скорее всего что-то в этом есть.
А сколько лесов будет сэкономлено на планете, если все откажутся от awk?
Я думаю, в контексте статьи заслуживает внимания и утилита sed. Часто её используют после grep, но на самом деле это лишнее.

$ ls -la | sed -n '/^d/p'
Выводит все поддиректории.

Это очень простой пример, возможности sed'а просто огромны.
естественно ведь sed — это потоковый текстовый редактор
Если возникнет потребность экономить ресурсы в подобной задаче — то лучше все же использовать AWK или perl, на нем можно описать более умный алгоритм, который даст реальную экономию :)
вот пример:
mailq | tail -n +2 | grep -v '^ *(' | awk 'BEGIN { RS = "" } { if ($8 == «user@example.com») print $1 } ' | tr -d '*!'
думаю согласитесь, что это намного эффективнее, чем делать cut на строчки с разным содержимым и потом грепать то что нужно.
Если нужно экономить ресурсы, то лучше не использовать перл. swtch.com/~rsc/regexp/regexp1.html
я прекрасно понимаю, что перл не самый быстрый движок регулярных выражений, но если для того чтобы вытащить нужную мне инфу из логов мне надо будет ставить какие-то нестандартные либы, писать что-то на C, и возится еще неизвестно с чем…
Я лучше смирюсь с неоптимальностью перла и напишу за 5 минут однострочник
Тем более, что потратил когда-то несколько дней на «Mastering Regular Expressions» и могу написать регулярку, которая работает с приемлемой скоростью
Если его производительность вас устраивает, то нет проблем. Просто у awk и perl разная вычислительная сложность, что делает последний неприемлемым в некоторых случаях.
Думаю, у автора не было достаточного опыта в обработке «разношёрстных» данных средствами *nix утилит. Иначе не было бы и таких категоричных заявлений.

PS. Попробуйте cut'ом выдернуть из squid'ового access.log, например, урл. Для awk это 7-ое поле, а что вернёт cut?
Часто вижу как используют.
cat file.txt | grep tt
Ну ведь можно же не использовать cat?

можно не использовать но где гарантия что grep «сможет» на «всём» так как вы от него ожидаете, гарантии нет, поэтому я бы написал именно так
cat file.txt | grep tt
а не
grep tt file.txt
Насколько я помню, этот вариант с перенаправлением файла на grep хуже, если работаешь с текстовыми файлами из разных операционных систем (с разными байтами перевода строк).

Например, в MS-DOS и Windows в текстовых файлах для перевода строк (newline) используются два байта: CR (0x0D, Carriage return) и LF (0x0A, Line feed). У *nix-like систем (Linux, BSD, MacOSX) — только один байт LF (0x0A). А в старой линейке MacOS (до 9 версии включительно) в качестве разделителей строк использовался один байт CR (0x0D).

Так вот при перенаправлении исходного файла это может быть обработано некорректно. А предварительный cat файла, вывод которого подаётся на grep, сначала читает текст и причёсывает файл, независимо от использованных там символов newline.
Ни одна из известных мне реализаций cat по умолчанию ничего не «причёсывает», вы заблуждаетесь:

$ printf "1\n2" | cat | od -c
0000000   1  \n   2
0000003
$ printf "1\r2" | cat | od -c
0000000   1  \r   2
0000003
$ printf "1\r\n2" | cat | od -c
0000000   1  \r  \n   2
0000004
кто то по ссылке выше врёт… возможно они не учли кэширование…

$ time cat TAGS |grep _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS786,23163
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS521,14125
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS783,23107

real    0m0.440s
user    0m0.430s
sys     0m0.010s

$ time cat TAGS |grep _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS786,23163
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS521,14125
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS783,23107

real    0m0.450s
user    0m0.440s
sys     0m0.000s

$ time grep _FILE_OFFSET_BITS ./TAGS
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS786,23163
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS521,14125
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS783,23107

real    0m0.420s
user    0m0.410s
sys     0m0.010s

$ time grep _FILE_OFFSET_BITS ./TAGS
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS786,23163
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS521,14125
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS783,23107

real    0m0.410s
user    0m0.390s
sys     0m0.020s

$ time grep _FILE_OFFSET_BITS ./TAGS
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS786,23163
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS521,14125
#define _FILE_OFFSET_BITS_FILE_OFFSET_BITS783,23107

real    0m0.440s
user    0m0.440s
sys     0m0.000s

Вот вот, я об этом и говорю :) Просто не стал расписывать, тут люди грамотные!
я вот чего скажу… мне пришлось переписывать скрипты для работы fish в mc, столкнулся с тем что базовые утилиты ls, head, tail, cut работают (имеют разные ключи) чуть по разному в зависимости от реализации т.е. даже в разных версиях бизибокса они бывает различаются, не говоря о разных реализациях никсов. так что при выборе cut или awk я бы не был столь категоричен.
это еще зависит от типа шелла, в котором Вы работаете
UFO just landed and posted this here
cron — это вообще отдельная история. В начале работы с ним мне иногда казалось, что он считает себя слишком умным
foobar | (read p1 p2; echo p1)
команда 'read' встроена в оболочку (в bash, по крайней мере), потому ресурсоёмкость будет ещё ниже, чем с 'cut'
А вот это, кстати, интересно. Спасибо.
только вот скобочки () запустят новый экземпляр шелла — а юмор в том, что запускать огромный и тяжеловесный шелл — такой, как bash — тяжелее и хуже, чем маленький и шустрый cut
не правда, не запустят

сравните вывод таких комманд:
strace -ffe fork,vfork,execve,clone bash -c 'id | cut -f 2 -d " "'
strace -ffe fork,vfork,execve,clone bash -c 'id | (read a b c;echo $b)'

в первом случае будет два вызова execve (для запуска id и cut), во втором только один, для запуска id
Спасибо за комментарий :) Приятно говорить с человеком, который в теме и оперирует адекватными аргументами ;)

Тем не менее, шеллу не обязательно делать fork для запуска сабшелла — но накладные расходы на его создание всё равно порядочные; сравните:

$ time sh -c 'I=0; while [ $I -lt 10000 ]; do id | (read a b c; echo $b) >/dev/null; I=$(($I+1)); done'
sh -c   33,91s user 37,52s system 105% cpu 1:07,58 total
$ time sh -c 'I=0; while [ $I -lt 10000 ]; do id | cut -f2 -d\  >/dev/null; I=$(($I+1)); done'    
sh -c   28,11s user 25,85s system 149% cpu 35,977 total

Разница почти в 2 раза в пользу cut.
да, похоже на то, что так и есть
Ну смотря что понимать под запуском. Copy on write давным давно применяется в ядре. Сам спавнинг процесса будет занимать примерно одно и то же время. А инициализация я думаю там в любом случае к минимуму сведена. Как раз из соображений пакетного запуска.

Но это если придираться :) А так в общем то специализированные микротулзы лучше
cut? grep? awk? или как использовать cut в высоконагрузочных проектах. Такое название должно было блистать.
Только у меня на всех ОСях (Арчи, Бунта, Деби, БСД) cut не знает о ключе -f?

$ cut -f
cut: ключ должен использоваться с аргументом — «f»
Попробуйте `cut --help' для получения более подробного описания.
«зубило и молоток или дремель ?» =)
UFO just landed and posted this here
"{1..100}" — это башизм, в стандартном sh его нет.

А «i=i++» — это вообще какая-то ернуда — во-первых, в цикле «seq | while» она вам абсолютно не нужно, во-вторых, «i=i++» присваивает переменной «i» строчку «i++», а совсем никак не делает тот инкремент, который подразумевался. Правильный способ делать инкремент — «i=$(($i + 1))»
UFO just landed and posted this here
Sign up to leave a comment.

Articles