SlideShare une entreprise Scribd logo
1  sur  23
Подборка инструментов для обработки
текста в командной оболочке
Обработка текста в командной оболочке
Одна из причин, которые делают командную оболочку бесценным
инструментом, — это большое количество команд обработки текста и
возможность легко объединять их в конвейер, создавая сложные шаблоны
обработки. Эти команды делают тривиальными многие задачи по анализу
текста и данных, преобразованию данных между разными форматами, по
фильтрации строк и т. д.
При работе с текстовыми данными главный принцип заключается в том,
чтобы разбить любую сложную проблему на множество более мелких — и
решить каждую из них с помощью специализированного инструмента.
Мы рассмотрим некоторые из наиболее распространенных и полезных
команд обработки текста в командной оболочке и продемонстрируем
реальные рабочие процессы, соединяющие их вместе. Я предлагаю
взглянуть на маны этих команд, чтобы увидеть всю широту возможностей в
вашем распоряжении.
cat
Команда cat используется для составления списка из одного или нескольких
файлов и отображения их содержимого на экране.
$ cat Documents/readme
Thanks again for reading this book!
I hope you're following so far!
$ cat Documents/computers
Computers are not intelligent
They're just fast at making dumb things.
$ cat Documents/readme Documents/computers
Thanks again for reading this book!
I hope you are following so far!
Computers are not intelligent
They're just fast at making dumb things.
head
head выводит первые n строк в файле. Это может быть очень полезно для
того, чтобы заглянуть в файл неизвестной структуры и формата, не заваливая
всю консоль кучей текста.
$ head -n 2 metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,inte
gration,short_name
mysql.galera.wsrep_cluster_size,gauge,,node,,The current number of nodes in the Galera
cluster.,0,mysql,galera cluster size
Если -n не указано, head выводит первые десять строк указанного файла или
входящего потока.
tail
tail — аналог head, только он выводит последние n строк в файле.
$ tail -n 1 metadata.csv
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries
Если хотите вывести все строки, расположенном после n-й строки (включая
её), можете использовать аргумент -n +n.
$ tail -n +42 metadata.csv
mysql.replication.slaves_connected,gauge,,,,Number of slaves connected to a replication
master.,0,mysql,slaves connected
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries
В нашем файле 43 строки, поэтому tail -n +42 выводит только 42-ю и 43-ю
строки из него.
Если параметр -n не указан, tail выведет последние десять строк в указанном
файле или входном потоке.
tail -f или tail --follow отображают последние строки в файле и каждую новую
строку по мере записи в файл. Это очень полезно для просмотра активности в
реальном времени, например, что записывается в логи веб-сервера и т. д.
wc
wc (word count) выводит количество символов (-c), слов (-w) или строк (-l) в
указанном файле или потоке.
$ wc -l metadata.csv
43 metadata.csv
$ wc -w metadata.csv
405 metadata.csv
$ wc -c metadata.csv
5094 metadata.csv
По умолчанию отображается всё вышеперечисленное.
$ wc metadata.csv
43 405 5094 metadata.csv
Если текстовые данные передаются по конвейеру или перенаправлены в
stdin, то отображается только счётчик.
$ cat metadata.csv | wc
43 405 5094
$ cat metadata.csv | wc -l
43
$ wc -w < metadata.csv
405
grep
grep — это швейцарский нож фильтрации строк по заданному шаблону.
Например, можем найти все вхождения слова mutex в файле.
$ grep mutex metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS
waits.,0,mysql,mutex os waits
mysql.innodb.mutex_spin_rounds,gauge,,event,second,The rate of mutex spin
rounds.,0,mysql,mutex spin rounds
mysql.innodb.mutex_spin_waits,gauge,,event,second,The rate of mutex spin
waits.,0,mysql,mutex spin waits
grep может обрабатывать либо файлы, указанные в качестве аргументов,
либо поток текста, переданный на его stdin. Таким образом, мы можем
сцеплять несколько команд grep для дальнейшей фильтрации текста. В
следующем примере мы фильтруем строки в нашем файле metadata.csv,
чтобы найти строки, содержащие и mutex, и OS.
$ grep mutex metadata.csv | grep OS
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS
waits.,0,mysql,mutex os waits
Рассмотрим некоторые опции grep и их поведение.
grep -v выполняет инвертное сопоставление: фильтрует строки, которые не
соответствуют шаблону аргументов.
$ grep -v gauge metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,inte
gration,short_name
grep -i выполняет сопоставление без учёта регистра. В следующем примере
grep -i os находит как OS, так и os.
$ grep -i os metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS
waits.,0,mysql,mutex os waits
mysql.innodb.os_log_fsyncs,gauge,,write,second,The rate of fsync writes to the log
file.,0,mysql,log fsyncs
grep -l выводит список файлов, содержащих совпадение.
$ grep -l mysql metadata.csv
metadata.csv
Команда grep -c подсчитывает, сколько раз найден образец.
$ grep -c select metadata.csv
3
grep -r рекурсивно ищет файлы в текущем рабочем каталоге и всех его
подкаталогах.
$ grep -r are ~/Documents
/home/br/Documents/computers:Computers are not intelligent
/home/br/Documents/readme:I hope you are following so far!
grep -w показывает только совпадающие целиком слова.
$ grep follow ~/Documents/readme
I hope you are following so far!
$ grep -w follow ~/Documents/readme
$
cut
cut извлекает часть файла (или, как обычно, входного потока). Команда
определяет разделитель полей (который разделяет столбцы) с помощью
опции -d, а порядковые номера столбцов для извлечения с помощью опции -
f.
Например, следующая команда извлекает первый столбец из последних пяти
строк нашего CSV-файла.
$ tail -n 5 metadata.csv | cut -d , -f 1
mysql.performance.user_time
mysql.replication.seconds_behind_master
mysql.replication.slave_running
mysql.replication.slaves_connected
mysql.performance.queries
Поскольку мы имеем дело с CSV, то столбцы разделяются запятой, а за
извлечение первого столбца отвечает опция -f 1.
Можно выбрать и первый, и второй столбцы, используя опцию -f 1,2.
$ tail -n 5 metadata.csv | cut -d , -f 1,2
mysql.performance.user_time,gauge
mysql.replication.seconds_behind_master,gauge
mysql.replication.slave_running,gauge
mysql.replication.slaves_connected,gauge
mysql.performance.queries,gauge
paste
paste объединяет вместе два разных файла в один многоколоночный файл.
$ cat ingredients
eggs
milk
butter
tomatoes
$ cat prices
1$
1.99$
1.50$
2$/kg
$ paste ingredients prices
eggs 1$
milk 1.99$
butter 1.50$
tomatoes 2$/kg
По умолчанию paste использует разделитель табуляции, но его можно
изменить с помощью параметра -d.
$ paste ingredients prices -d:
eggs:1$
milk:1.99$
butter:1.50$
tomatoes:2$/kg
Ещё один распространённый способ использования paste — объединение
всех строк в потоке или файле с помощью заданного разделителя, используя
комбинацию аргументов -s и -d.
$ paste -s -d, ingredients
eggs,milk,butter,tomatoes
Если в качестве входного файла указан параметр -, то вместо него будет
считываться stdin.
$ cat ingredients | paste -s -d, -
eggs,milk,butter,tomatoes
sort
Команда sort, собственно, сортирует данные (в указанном файле или
входном потоке).
$ cat ingredients
eggs
milk
butter
tomatoes
salt
$ sort ingredients
butter
eggs
milk
salt
tomatoes
sort -r выполняет обратную сортировку.
$ sort -r ingredients
tomatoes
salt
milk
eggs
butter
sort -n сортирует поля по их арифметическому значению.
$ cat numbers
0
2
1
10
3
$ sort numbers
0
1
10
2
3
$ sort -n numbers
0
1
2
3
10
uniq
uniq обнаруживает и отфильтровывает соседние одинаковые строки в
указанном файле или входном потоке.
$ cat duplicates
and one
and one
and two
and one
and two
and one, two, three
$ uniq duplicates
and one
and two
and one
and two
and one, two, three
Поскольку uniq отфильтровывает только соседние строки, в наших данных
могут ещё остаться дубликаты. Чтобы отфильтровать все одинаковые строки
из файла, нужно сначала отсортировать его содержимое.
$ sort duplicates | uniq
and one
and one, two, three
and two
uniq -c в начале каждой строки вставляет количество её вхождений.
$ sort duplicates | uniq -c
3 and one
1 and one, two, three
2 and two
uniq -u отображает только уникальные строки.
$ sort duplicates | uniq -u
and one, two, three
Примечание. uniq особенно полезен в сочетании с сортировкой, поскольку
конвейер | sort | uniq позволяет удалить все дублирующиеся строки в файле
или потоке.
awk
awk — это чуть больше, чем просто инструмент обработки текста: на самом
деле у него целый язык программирования. В чём awk действительно
хорош — так это в разбиении файлов на столбцы, и делает это с особенным
блеском, когда в файлах перемешаны пробелы и табы.
$ cat -t multi-columns
John Smith Doctor^ITardis
Sarah-James Smith^I Companion^ILondon
Rose Tyler Companion^ILondon
Примечание. cat -t отображает табы как ^I.
Как видим, столбцы разделены либо пробелами, либо табуляциями, и не
всегда одинаковым количеством пробелов. cut здесь бесполезен, потому что
работает только с одним символом-разделителем. Но awk легко разберётся с
таким файлом.
awk '{ print $n }' выводит n-й столбец в тексте.
$ cat multi-columns | awk '{ print $1 }'
John
Sarah-James
Rose
$ cat multi-columns | awk '{ print $3 }'
Doctor
Companion
Companion
$ cat multi-columns | awk '{ print $1,$2 }'
John Smith
Sarah-James Smith
Rose Tyler
Хотя awk способен на гораздо большее, выдача колонок составляет,
наверное, 99% вариантов использования в моём личном случае.
Примечание. { print $NF } выводит последний столбец в строке.
tr
tr расшифровывается как translate. Эта команда заменяет одни символы на
другие. Она работает либо с символами, либо с классами символов, такими
как строчные, печатные, пробелы, буквенно-цифровые и т. д.
На стандартных входных данных tr <char1> <char2> заменяет все вхождения
<char1> на <char2>.
$ echo "Computers are fast" | tr a A
computers Are fAst
tr может переводить классы символов с помощью нотации [:class:]. Полный
список доступных классов описан на справочной странице tr, но некоторые
продемонстрируем здесь.
[:space:] представляет все типы пробелов, от простого пробела до табуляции
или символа новой строки.
$ echo "computers are fast" | tr '[:space:]' ','
computers,are,fast,%
Все символы, похожие на пробелы, переведены в запятую. Обратите
внимание, что символ % в конце выдачи означает отсутствие завершающей
новой строки. Действительно, этот символ тоже переведён в запятую.
[:lower:] представляет все строчные символы, а [:upper:] — все прописные.
Таким образом, преобразование между ними становится тривиальным.
$ echo "computers are fast" | tr '[:lower:]' '[:upper:]'
COMPUTERS ARE FAST
$ echo "COMPUTERS ARE FAST" | tr '[:upper:]' '[:lower:]'
computers are fast
tr -c SET1 SET2 преобразует любой символ, не входящий в набор SET1, в
символы набора SET2. В следующем примере все символы, кроме указанных
гласных, заменяются пробелами.
$ echo "computers are fast" | tr -c '[aeiouy]' ' '
o u e a e a
tr -d удаляет указанные символы, а не заменяет их. Это эквивалент tr <char> ''.
$ echo "Computers Are Fast" | tr -d '[:lower:]'
C A F
tr также может заменить диапазоны символов, например, все буквы между a
и e или все числа между 1 и 8, используя нотацию s-e, где s — начальный
символ, а e — конечный.
$ echo "computers are fast" | tr 'a-e' 'x'
xomputxrs xrx fxst
$ echo "5uch l337 5p34k" | tr '1-4' 'x'
5uch lxx7 5pxxk
Команда tr -s string1 сжимает все множественные вхождения символов в
string1 в одно-единственное. Одним из наиболее полезных применений tr -s
является замена нескольких последовательных пробелов одним.
$ echo "Computers are fast" | tr -s ' '
Computers are fast
fold
Команда fold сворачивает все входные строки до заданной ширины.
Например, может быть полезно убедиться, что текст помещается на дисплеях
небольшого размера. Так, fold -w n укладывает строки по ширине n символов.
$ cat ~/Documents/readme | fold -w 16
Thanks again for
reading this bo
ok!
I hope you're fo
llowing so far!
Команда fold -s будет разбивать строки только на символах пробела. Её
можно объединить с предыдущей, чтобы ограничить строким заданным
количеством символом.
Thanks again
for reading
this book!
I hope you're
following so
far!
sed
sed — это неинтерактивный потоковый редактор, который используется для
преобразования текста во входном потоке строка за строкой. В качестве
входных данных используется или файл, или stdin, а на выходе тоже или
файл, или stdout.
Команды редактора могут включать один или несколько адресов, функцию и
параметры. Таким образом, команды выглядят следующим образом:
[address[,address]]function[arguments]
Хотя sed выполняет множество функций, мы рассмотрим только замену
текста как один из самых распространённых вариантов использования.
Замена текста
Команда замены sed выглядит следующим образом:
s/PATTERN/REPLACEMENT/[options]
Пример: замена первого экземпляра слова в каждой строке в файле:
$ cat hello
hello hello
hello world!
hi
$ cat hello | sed 's/hello/Hey I just met you/'
Hey I just met you hello
Hey I just met you world
hi
Мы видим, что в первой строчке заменяется только первый экземпляр hello.
Чтобы заменить все вхождения hello во всех строках, можно использовать
опцию g (означает global).
$ cat hello | sed 's/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
sed позволяет использовать любые разделители, кроме /, что особенно
улучшает читаемость, если в самих аргументах команды есть слэши.
$ cat hello | sed 's@hello@Hey I just met you@g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
Адрес говорит редактору, в какой строке или диапазоне строк выполнять
подстановку.
$ cat hello | sed '1s/hello/Hey I just met you/g'
Hey I just met you hello
hello world
hi
$ cat hello | sed '2s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
Адрес 1 указывает заменять hello на Hey I just met you в первой строке. Можем
указать диапазон адресов в нотации <start>,<end>, где <end> может быть либо
номером строки, либо $, то есть последней строкой в файле.
$ cat hello | sed '1,2s/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
$ cat hello | sed '2,3s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
$ cat hello | sed '2,$s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
По умолчанию sed выдаёт результат в свой stdout, но может отредактировать
и оригинальный файл с опцией -i.
$ sed -i '' 's/hello/Bonjour/' sed-data
$ cat sed-data
Bonjour hello
Bonjour world
hi
Примечание. В Linux достаточно только -i. Но в macOS поведение команды
немного отличается, поэтому сразу после -i нужно добавить ''.
Реальные примеры
Фильтрация CSV с помощью grep и awk
$ grep -w gauge metadata.csv | awk -F, '{ if ($4 == "query") { print $1, "per", $5 } }'
mysql.performance.com_delete per second
mysql.performance.com_delete_multi per second
mysql.performance.com_insert per second
mysql.performance.com_insert_select per second
mysql.performance.com_replace_select per second
mysql.performance.com_select per second
mysql.performance.com_update per second
mysql.performance.com_update_multi per second
mysql.performance.questions per second
mysql.performance.slow_queries per second
mysql.performance.queries per second
В этом примере grep в файле metadata.csv сначала фильтрует строки,
содержащие слово gauge, затем те, у которых query в четвёртой колонке, и
выводит название метрики (1-я колонка) с соответствующим значением
per_unit_name (5-я колонка).
Вывод адреса IPv4, связанного с сетевым
интерфейсом
$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38
Команда ifconfig <interface name> выводит сведения по указанному сетевому
интерфейсу. Например:
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 19:64:92:de:20:ba
inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status:active
Затем запускаем grep для inet, что выдаст две строки соответствия.
$ ifconfig en0 | grep inet
inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
Затем с помощью grep -v исключаем строку с ipv6.
$ ifconfig en0 | grep inet | grep -v inet6
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
Наконец, с помощью awk запрашиваем второй столбец в этой строке: это
IPv4-адрес, связанный с нашим сетевым интерфейсом en0.
$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38
Примечание. Мне предложили заменить grep inet | grep -v inet6 такой надёжной
командой awk:$ ifconfig en0 | awk ' $1 == "inet" { print $2 }'
192.168.0.38
Она короче и конкретно нацелена на IPv4 с условием $1 == "inet".
Извлечение значения из файла конфигурации
$ grep 'editor =' ~/.gitconfig | cut -d = -f2 | sed 's/ //g'
/usr/bin/vim
В файле конфигурации git текущего пользователя ищем значение editor =,
обрезаем знак =, извлекаем второй столбец и удаляем все пробелы вокруг.
$ grep 'editor =' ~/.gitconfig
editor = /usr/bin/vim
$ grep 'editor =' ~/.gitconfig | cut -d'=' -f2
/usr/bin/vim
$ grep 'editor =' ~/.gitconfig | cut -d'=' -f2 | sed 's/ //'
/usr/bin/vim
Извлечение IP-адресов из файла журнала
Следующий реальный код ищет в журнале БД сообщение Too many
connections from (за ним следует IP-адрес) и отображает десять главных
нарушителей.
$ grep 'Too many connections from' db.log | 
awk '{ print $12 }' | 
sed 's@/@@' | 
sort | 
uniq -c | 
sort -rn | 
head -n 10 | 
awk '{ print $2 }'
10.11.112.108
10.11.111.70
10.11.97.57
10.11.109.72
10.11.116.156
10.11.100.221
10.11.96.242
10.11.81.68
10.11.99.112
10.11.107.120
Давайте разберем, что делает этот конвейер. Во-первых, как выглядит строка
в журнале.
$ grep "Too many connections from" db.log | head -n 1
2020-01-01 08:02:37,617 [myid:1] - WARN
[NIOServerCxn.Factory:1.2.3.4/1.2.3.4:2181:NIOServerCnxnFactory@193] - Too many
connections from /10.11.112.108 - max is 60
Затем awk '{ print $12 }' извлекает из строки IP-адрес.
$ grep "Too many connections from" db.log | awk '{ print $12 }'
/10.11.112.108
...
Команда sed 's@/@@' удаляет начальный слэш.
$ grep "Too many connections from" db.log | awk '{ print $12 }' | sed 's@/@@'
10.11.112.108
...
Примечание. Как мы уже видели ранее, в sed можно использовать любой
разделитель. Хотя обычно в качестве разделителя используется /, здесь мы
заменяем именно этот символ, что слегка ухудшит читаемость выражения
подстановки.sed 's////'
sort | uniq -c сортирует IP-адреса в лексикографическом порядке, а затем
удаляет дубликаты, добавляя перед IP-адресами количество вхождений
каждого.
$ grep 'Too many connections from' db.log | 
awk '{ print $12 }' | 
sed 's@/@@' | 
sort | 
uniq -c
1379 10.11.100.221
1213 10.11.103.168
1138 10.11.105.177
946 10.11.106.213
1211 10.11.106.4
1326 10.11.107.120
...
sort -rn | head -n 10 сортирует строки по количеству вхождений, численно и в
обратном порядке, чтобы главные нарушители выводились в первую
очередь, из которых отображаются 10 строк. Последняя команда awk { print
$2 } извлекает сами IP-адреса.
$ grep 'Too many connections from' db.log | 
awk '{ print $12 }' | 
sed 's@/@@' | 
sort | 
uniq -c | 
sort -rn | 
head -n 10 | 
awk '{ print $2 }'
10.11.112.108
10.11.111.70
10.11.97.57
10.11.109.72
10.11.116.156
10.11.100.221
10.11.96.242
10.11.81.68
10.11.99.112
10.11.107.120
Переименование функции в исходном файле
Представим, что мы работаем над проектом и хотели бы переименовать
недачно названную функцию (или класс, переменную и т. д.) в исходном
файле. Можно сделать это с помощью команды sed -i, которая выполняет
замену прямо в оригинальном файле.
$ cat izk/utils.py
def bool_from_str(s):
if s.isdigit():
return int(s) == 1
return s.lower() in ['yes', 'true', 'y']
$ sed -i 's/def bool_from_str/def is_affirmative/' izk/utils.py
$ cat izk/utils.py
def is_affirmative(s):
if s.isdigit():
return int(s) == 1
return s.lower() in ['yes', 'true', 'y']
Примечание. На macOS вместо sed -i используйте sed -i ''.
Однако мы переименовали функцию только в оригинальном файле. Это
сломает импорт bool_from_str в любом другом файле, поскольку эта функция
больше не определена. Нужно найти способ переименовать bool_from_str
повсюду в нашем проекте. Такого можно добиться с помощью команд grep,
sed, а также циклов for или с помощью xargs.
Углубляемся: циклы for и xargs
Чтобы заменить в нашем проекте все вхождения bool_from_str, сначала нужно
рекурсивно найти их с помощью grep -r.
$ grep -r bool_from_str .
./tests/test_utils.py:from izk.utils import bool_from_str
./tests/test_utils.py:def test_bool_from_str(s, expected):
./tests/test_utils.py: assert bool_from_str(s) == expected
./izk/utils.py:def bool_from_str(s):
./izk/prompt.py:from .utils import bool_from_str
./izk/prompt.py: default = bool_from_str(os.environ[envvar])
Поскольку нас интересуют только файлы c совпадениями, также необходимо
использовать опцию -l/--files-with-matches:
-l, --files-with-matches
Only the names of files containing selected lines are written to standard out-
put. grep will only search a file until a match has been found, making
searches potentially less expensive. Pathnames are listed once per file
searched. If the standard input is searched, the string ``(standard input)''
is written.
$ grep -r --files-with-matches bool_from_str .
./tests/test_utils.py
./izk/utils.py
./izk/prompt.py
Затем можем использовать команду xargs для осуществления действий с
каждой строки выходных данных (то есть всех файлов, содержащих строку
bool_from_str).
$ grep -r --files-with-matches bool_from_str . | 
xargs -n 1 sed -i 's/bool_from_str/is_affirmative/'
Опция -n 1 указывает, что каждая строка в выходных данных должна
выполнить отдельную команду sed.
Затем выполняются следующие команды:
$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py
Если команда, которую вы вызываете с помощью xargs (в нашем случае sed),
поддерживает несколько аргументов, то следует отбросить аргумент -n 1 для
производительности.
grep -r --files-with-matches bool_from_str . | xargs sed -i
's/bool_from_str/is_affirmative/'
Эта команда затем исполнит
$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py ./izk/utils.py
./izk/prompt.py
Примечание. Из синопсиса sed на ман-странице видно, что команда может
принять несколько аргументов.SYNOPSIS
sed [-Ealn] command [file ...]
sed [-Ealn] [-e command] [-f command_file] [-i extension] [file ...]
Действительно, как мы видели в предыдущей главе, file ... означает, что
принимаются несколько аргументов, представляющих собой имена
файлов.
Мы видим, что произведены замены для всех вхождений bool_from_str.
$ grep -r is_affirmative .
./tests/test_utils.py:from izk.utils import is_affirmative
./tests/test_utils.py:def test_is_affirmative(s, expected):
./tests/test_utils.py: assert is_affirmative(s) == expected
./izk/utils.py:def is_affirmative(s):
./izk/prompt.py:from .utils import is_affirmative
./izk/prompt.py: default = is_affirmative(os.environ[envvar])
Как это часто бывает, существует несколько способов достижения одного и
того же результата. Вместо xargs мы могли бы использовать циклы for, чтобы
перебирать строки по списку и выполнять действие над каждым элементом.
У этих циклов такой синтаксис:
for item in list; do
command $item
done
Если обернуть нашу команду grep в $(), то оболочка выполнит её в
подоболочке, результат чего затем будет повторён в цикле for.
$ for file in $(grep -r --files-with-matches bool_from_str .); do
sed -i 's/bool_from_str/is_affirmative/' $file
done
Эта команда выполнит
$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py
Синтаксис циклов for кажется мне более чётким, чем у xargs, однако
последняя может выполнять команды параллельно, используя параметры -P
n, где n — максимальное количество параллельных команд, выполняемых
одновременно, что может дать выигрыш в производительности.
Резюме
Все эти инструменты открывают целый мир возможностей, так как позволяют
извлекать и преобразовывать данные, создавая целые конвейеры из команд,
которые, возможно, никогда не предназначались для совместной работы.
Каждая из них выполняет относительно небольшую функцию (сортировка
sort, объединение cat, фильтры grep, редактирование sed, вырезание cut и т.
д.).
Любую задачу, включающую текст, можно свести к конвейеру более мелких
задач, каждая из которых выполняет простое действие и передаёт свои
выходные данные в следующую задачу.
Например, если нам хочется узнать, сколько уникальных IP-адресов в файле
журнала, и чтобы эти IP-адреса всегда появлялись в одном и том же столбце,
то можно запустить следующую последовательность команд:
 grep строк, которые соответствуют шаблону строк с IP-адресами
 найти столбец с IP-адресом, извлечь его с помощью awk
 отсортировать список IP-адресов с помощью sort
 устранить смежные дубликаты с помощью uniq
 подсчитать количество строк (то есть уникальных IP-адресов) с
помощью wc -l
Поскольку есть множество нативных и сторонних инструментов обработки
текста, также много способов решить любую задачу.

Contenu connexe

Similaire à Текст.pptx

Оптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesОптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesPlatonov Sergey
 
Оптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesОптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesPlatonov Sergey
 
Систематизация экспрешнов в IE
Систематизация экспрешнов в IEСистематизация экспрешнов в IE
Систематизация экспрешнов в IERoman Komarov
 
FreeRTOS
FreeRTOSFreeRTOS
FreeRTOSquakke
 
!Predictive analytics part_3
!Predictive analytics part_3!Predictive analytics part_3
!Predictive analytics part_3Vladimir Krylov
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.Igor Shkulipa
 
Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...
Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...
Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...Tatyanazaxarova
 
презентация 4. введение в css
презентация 4. введение в cssпрезентация 4. введение в css
презентация 4. введение в cssRusov1
 
Linux basics. Занятие 3.
Linux basics. Занятие 3. Linux basics. Занятие 3.
Linux basics. Занятие 3. Vikentsi Lapa
 
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art) DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art) it-people
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Яковенко Кирилл
 
Теория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциямТеория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциямSergey Staroletov
 
инструментарий
инструментарийинструментарий
инструментарийigdweb
 
Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Alex Ott
 
Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2Technopark
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?Vasil Remeniuk
 
MongoDB первые впечатления
MongoDB первые впечатленияMongoDB первые впечатления
MongoDB первые впечатленияfudz1k
 

Similaire à Текст.pptx (20)

Оптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesОптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templates
 
Оптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templatesОптимизация трассирования с использованием Expression templates
Оптимизация трассирования с использованием Expression templates
 
Систематизация экспрешнов в IE
Систематизация экспрешнов в IEСистематизация экспрешнов в IE
Систематизация экспрешнов в IE
 
FreeRTOS
FreeRTOSFreeRTOS
FreeRTOS
 
!Predictive analytics part_3
!Predictive analytics part_3!Predictive analytics part_3
!Predictive analytics part_3
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.
 
Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...
Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...
Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправ...
 
презентация 4. введение в css
презентация 4. введение в cssпрезентация 4. введение в css
презентация 4. введение в css
 
Linux basics. Занятие 3.
Linux basics. Занятие 3. Linux basics. Занятие 3.
Linux basics. Занятие 3.
 
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art) DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
 
Основы MATLAB. Программирование
Основы MATLAB. ПрограммированиеОсновы MATLAB. Программирование
Основы MATLAB. Программирование
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
 
Теория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциямТеория языков программирования некоторые слайды к лекциям
Теория языков программирования некоторые слайды к лекциям
 
инструментарий
инструментарийинструментарий
инструментарий
 
Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)Clojure: Lisp for the modern world (русская версия)
Clojure: Lisp for the modern world (русская версия)
 
Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2Алгоритмы и структуры данных осень 2013 лекция 2
Алгоритмы и структуры данных осень 2013 лекция 2
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
 
бегун
бегунбегун
бегун
 
MongoDB первые впечатления
MongoDB первые впечатленияMongoDB первые впечатления
MongoDB первые впечатления
 
Backbone js
Backbone jsBackbone js
Backbone js
 

Текст.pptx

  • 1. Подборка инструментов для обработки текста в командной оболочке Обработка текста в командной оболочке Одна из причин, которые делают командную оболочку бесценным инструментом, — это большое количество команд обработки текста и возможность легко объединять их в конвейер, создавая сложные шаблоны обработки. Эти команды делают тривиальными многие задачи по анализу текста и данных, преобразованию данных между разными форматами, по фильтрации строк и т. д. При работе с текстовыми данными главный принцип заключается в том, чтобы разбить любую сложную проблему на множество более мелких — и решить каждую из них с помощью специализированного инструмента. Мы рассмотрим некоторые из наиболее распространенных и полезных команд обработки текста в командной оболочке и продемонстрируем реальные рабочие процессы, соединяющие их вместе. Я предлагаю взглянуть на маны этих команд, чтобы увидеть всю широту возможностей в вашем распоряжении. cat Команда cat используется для составления списка из одного или нескольких файлов и отображения их содержимого на экране. $ cat Documents/readme Thanks again for reading this book! I hope you're following so far!
  • 2. $ cat Documents/computers Computers are not intelligent They're just fast at making dumb things. $ cat Documents/readme Documents/computers Thanks again for reading this book! I hope you are following so far! Computers are not intelligent They're just fast at making dumb things. head head выводит первые n строк в файле. Это может быть очень полезно для того, чтобы заглянуть в файл неизвестной структуры и формата, не заваливая всю консоль кучей текста. $ head -n 2 metadata.csv metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,inte gration,short_name mysql.galera.wsrep_cluster_size,gauge,,node,,The current number of nodes in the Galera cluster.,0,mysql,galera cluster size Если -n не указано, head выводит первые десять строк указанного файла или входящего потока. tail tail — аналог head, только он выводит последние n строк в файле. $ tail -n 1 metadata.csv mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries Если хотите вывести все строки, расположенном после n-й строки (включая её), можете использовать аргумент -n +n. $ tail -n +42 metadata.csv mysql.replication.slaves_connected,gauge,,,,Number of slaves connected to a replication master.,0,mysql,slaves connected mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries
  • 3. В нашем файле 43 строки, поэтому tail -n +42 выводит только 42-ю и 43-ю строки из него. Если параметр -n не указан, tail выведет последние десять строк в указанном файле или входном потоке. tail -f или tail --follow отображают последние строки в файле и каждую новую строку по мере записи в файл. Это очень полезно для просмотра активности в реальном времени, например, что записывается в логи веб-сервера и т. д. wc wc (word count) выводит количество символов (-c), слов (-w) или строк (-l) в указанном файле или потоке. $ wc -l metadata.csv 43 metadata.csv $ wc -w metadata.csv 405 metadata.csv $ wc -c metadata.csv 5094 metadata.csv По умолчанию отображается всё вышеперечисленное. $ wc metadata.csv 43 405 5094 metadata.csv Если текстовые данные передаются по конвейеру или перенаправлены в stdin, то отображается только счётчик. $ cat metadata.csv | wc 43 405 5094 $ cat metadata.csv | wc -l 43 $ wc -w < metadata.csv 405 grep grep — это швейцарский нож фильтрации строк по заданному шаблону. Например, можем найти все вхождения слова mutex в файле.
  • 4. $ grep mutex metadata.csv mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits mysql.innodb.mutex_spin_rounds,gauge,,event,second,The rate of mutex spin rounds.,0,mysql,mutex spin rounds mysql.innodb.mutex_spin_waits,gauge,,event,second,The rate of mutex spin waits.,0,mysql,mutex spin waits grep может обрабатывать либо файлы, указанные в качестве аргументов, либо поток текста, переданный на его stdin. Таким образом, мы можем сцеплять несколько команд grep для дальнейшей фильтрации текста. В следующем примере мы фильтруем строки в нашем файле metadata.csv, чтобы найти строки, содержащие и mutex, и OS. $ grep mutex metadata.csv | grep OS mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits Рассмотрим некоторые опции grep и их поведение. grep -v выполняет инвертное сопоставление: фильтрует строки, которые не соответствуют шаблону аргументов. $ grep -v gauge metadata.csv metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,inte gration,short_name grep -i выполняет сопоставление без учёта регистра. В следующем примере grep -i os находит как OS, так и os. $ grep -i os metadata.csv mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits mysql.innodb.os_log_fsyncs,gauge,,write,second,The rate of fsync writes to the log file.,0,mysql,log fsyncs grep -l выводит список файлов, содержащих совпадение. $ grep -l mysql metadata.csv metadata.csv Команда grep -c подсчитывает, сколько раз найден образец.
  • 5. $ grep -c select metadata.csv 3 grep -r рекурсивно ищет файлы в текущем рабочем каталоге и всех его подкаталогах. $ grep -r are ~/Documents /home/br/Documents/computers:Computers are not intelligent /home/br/Documents/readme:I hope you are following so far! grep -w показывает только совпадающие целиком слова. $ grep follow ~/Documents/readme I hope you are following so far! $ grep -w follow ~/Documents/readme $ cut cut извлекает часть файла (или, как обычно, входного потока). Команда определяет разделитель полей (который разделяет столбцы) с помощью опции -d, а порядковые номера столбцов для извлечения с помощью опции - f. Например, следующая команда извлекает первый столбец из последних пяти строк нашего CSV-файла. $ tail -n 5 metadata.csv | cut -d , -f 1 mysql.performance.user_time mysql.replication.seconds_behind_master mysql.replication.slave_running mysql.replication.slaves_connected mysql.performance.queries Поскольку мы имеем дело с CSV, то столбцы разделяются запятой, а за извлечение первого столбца отвечает опция -f 1. Можно выбрать и первый, и второй столбцы, используя опцию -f 1,2. $ tail -n 5 metadata.csv | cut -d , -f 1,2 mysql.performance.user_time,gauge mysql.replication.seconds_behind_master,gauge
  • 6. mysql.replication.slave_running,gauge mysql.replication.slaves_connected,gauge mysql.performance.queries,gauge paste paste объединяет вместе два разных файла в один многоколоночный файл. $ cat ingredients eggs milk butter tomatoes $ cat prices 1$ 1.99$ 1.50$ 2$/kg $ paste ingredients prices eggs 1$ milk 1.99$ butter 1.50$ tomatoes 2$/kg По умолчанию paste использует разделитель табуляции, но его можно изменить с помощью параметра -d. $ paste ingredients prices -d: eggs:1$ milk:1.99$ butter:1.50$ tomatoes:2$/kg Ещё один распространённый способ использования paste — объединение всех строк в потоке или файле с помощью заданного разделителя, используя комбинацию аргументов -s и -d. $ paste -s -d, ingredients eggs,milk,butter,tomatoes
  • 7. Если в качестве входного файла указан параметр -, то вместо него будет считываться stdin. $ cat ingredients | paste -s -d, - eggs,milk,butter,tomatoes sort Команда sort, собственно, сортирует данные (в указанном файле или входном потоке). $ cat ingredients eggs milk butter tomatoes salt $ sort ingredients butter eggs milk salt tomatoes sort -r выполняет обратную сортировку. $ sort -r ingredients tomatoes salt milk eggs butter sort -n сортирует поля по их арифметическому значению. $ cat numbers 0 2 1 10 3 $ sort numbers 0
  • 8. 1 10 2 3 $ sort -n numbers 0 1 2 3 10 uniq uniq обнаруживает и отфильтровывает соседние одинаковые строки в указанном файле или входном потоке. $ cat duplicates and one and one and two and one and two and one, two, three $ uniq duplicates and one and two and one and two and one, two, three Поскольку uniq отфильтровывает только соседние строки, в наших данных могут ещё остаться дубликаты. Чтобы отфильтровать все одинаковые строки из файла, нужно сначала отсортировать его содержимое. $ sort duplicates | uniq and one and one, two, three and two uniq -c в начале каждой строки вставляет количество её вхождений.
  • 9. $ sort duplicates | uniq -c 3 and one 1 and one, two, three 2 and two uniq -u отображает только уникальные строки. $ sort duplicates | uniq -u and one, two, three Примечание. uniq особенно полезен в сочетании с сортировкой, поскольку конвейер | sort | uniq позволяет удалить все дублирующиеся строки в файле или потоке. awk awk — это чуть больше, чем просто инструмент обработки текста: на самом деле у него целый язык программирования. В чём awk действительно хорош — так это в разбиении файлов на столбцы, и делает это с особенным блеском, когда в файлах перемешаны пробелы и табы. $ cat -t multi-columns John Smith Doctor^ITardis Sarah-James Smith^I Companion^ILondon Rose Tyler Companion^ILondon Примечание. cat -t отображает табы как ^I. Как видим, столбцы разделены либо пробелами, либо табуляциями, и не всегда одинаковым количеством пробелов. cut здесь бесполезен, потому что работает только с одним символом-разделителем. Но awk легко разберётся с таким файлом. awk '{ print $n }' выводит n-й столбец в тексте. $ cat multi-columns | awk '{ print $1 }' John
  • 10. Sarah-James Rose $ cat multi-columns | awk '{ print $3 }' Doctor Companion Companion $ cat multi-columns | awk '{ print $1,$2 }' John Smith Sarah-James Smith Rose Tyler Хотя awk способен на гораздо большее, выдача колонок составляет, наверное, 99% вариантов использования в моём личном случае. Примечание. { print $NF } выводит последний столбец в строке. tr tr расшифровывается как translate. Эта команда заменяет одни символы на другие. Она работает либо с символами, либо с классами символов, такими как строчные, печатные, пробелы, буквенно-цифровые и т. д. На стандартных входных данных tr <char1> <char2> заменяет все вхождения <char1> на <char2>. $ echo "Computers are fast" | tr a A computers Are fAst tr может переводить классы символов с помощью нотации [:class:]. Полный список доступных классов описан на справочной странице tr, но некоторые продемонстрируем здесь. [:space:] представляет все типы пробелов, от простого пробела до табуляции или символа новой строки. $ echo "computers are fast" | tr '[:space:]' ',' computers,are,fast,% Все символы, похожие на пробелы, переведены в запятую. Обратите внимание, что символ % в конце выдачи означает отсутствие завершающей новой строки. Действительно, этот символ тоже переведён в запятую.
  • 11. [:lower:] представляет все строчные символы, а [:upper:] — все прописные. Таким образом, преобразование между ними становится тривиальным. $ echo "computers are fast" | tr '[:lower:]' '[:upper:]' COMPUTERS ARE FAST $ echo "COMPUTERS ARE FAST" | tr '[:upper:]' '[:lower:]' computers are fast tr -c SET1 SET2 преобразует любой символ, не входящий в набор SET1, в символы набора SET2. В следующем примере все символы, кроме указанных гласных, заменяются пробелами. $ echo "computers are fast" | tr -c '[aeiouy]' ' ' o u e a e a tr -d удаляет указанные символы, а не заменяет их. Это эквивалент tr <char> ''. $ echo "Computers Are Fast" | tr -d '[:lower:]' C A F tr также может заменить диапазоны символов, например, все буквы между a и e или все числа между 1 и 8, используя нотацию s-e, где s — начальный символ, а e — конечный. $ echo "computers are fast" | tr 'a-e' 'x' xomputxrs xrx fxst $ echo "5uch l337 5p34k" | tr '1-4' 'x' 5uch lxx7 5pxxk Команда tr -s string1 сжимает все множественные вхождения символов в string1 в одно-единственное. Одним из наиболее полезных применений tr -s является замена нескольких последовательных пробелов одним. $ echo "Computers are fast" | tr -s ' ' Computers are fast
  • 12. fold Команда fold сворачивает все входные строки до заданной ширины. Например, может быть полезно убедиться, что текст помещается на дисплеях небольшого размера. Так, fold -w n укладывает строки по ширине n символов. $ cat ~/Documents/readme | fold -w 16 Thanks again for reading this bo ok! I hope you're fo llowing so far! Команда fold -s будет разбивать строки только на символах пробела. Её можно объединить с предыдущей, чтобы ограничить строким заданным количеством символом. Thanks again for reading this book! I hope you're following so far! sed sed — это неинтерактивный потоковый редактор, который используется для преобразования текста во входном потоке строка за строкой. В качестве входных данных используется или файл, или stdin, а на выходе тоже или файл, или stdout. Команды редактора могут включать один или несколько адресов, функцию и параметры. Таким образом, команды выглядят следующим образом: [address[,address]]function[arguments] Хотя sed выполняет множество функций, мы рассмотрим только замену текста как один из самых распространённых вариантов использования. Замена текста Команда замены sed выглядит следующим образом:
  • 13. s/PATTERN/REPLACEMENT/[options] Пример: замена первого экземпляра слова в каждой строке в файле: $ cat hello hello hello hello world! hi $ cat hello | sed 's/hello/Hey I just met you/' Hey I just met you hello Hey I just met you world hi Мы видим, что в первой строчке заменяется только первый экземпляр hello. Чтобы заменить все вхождения hello во всех строках, можно использовать опцию g (означает global). $ cat hello | sed 's/hello/Hey I just met you/g' Hey I just met you Hey I just met you Hey I just met you world hi sed позволяет использовать любые разделители, кроме /, что особенно улучшает читаемость, если в самих аргументах команды есть слэши. $ cat hello | sed 's@hello@Hey I just met you@g' Hey I just met you Hey I just met you Hey I just met you world hi Адрес говорит редактору, в какой строке или диапазоне строк выполнять подстановку. $ cat hello | sed '1s/hello/Hey I just met you/g' Hey I just met you hello hello world hi $ cat hello | sed '2s/hello/Hey I just met you/g' hello hello Hey I just met you world hi
  • 14. Адрес 1 указывает заменять hello на Hey I just met you в первой строке. Можем указать диапазон адресов в нотации <start>,<end>, где <end> может быть либо номером строки, либо $, то есть последней строкой в файле. $ cat hello | sed '1,2s/hello/Hey I just met you/g' Hey I just met you Hey I just met you Hey I just met you world hi $ cat hello | sed '2,3s/hello/Hey I just met you/g' hello hello Hey I just met you world hi $ cat hello | sed '2,$s/hello/Hey I just met you/g' hello hello Hey I just met you world hi По умолчанию sed выдаёт результат в свой stdout, но может отредактировать и оригинальный файл с опцией -i. $ sed -i '' 's/hello/Bonjour/' sed-data $ cat sed-data Bonjour hello Bonjour world hi Примечание. В Linux достаточно только -i. Но в macOS поведение команды немного отличается, поэтому сразу после -i нужно добавить ''. Реальные примеры Фильтрация CSV с помощью grep и awk $ grep -w gauge metadata.csv | awk -F, '{ if ($4 == "query") { print $1, "per", $5 } }' mysql.performance.com_delete per second
  • 15. mysql.performance.com_delete_multi per second mysql.performance.com_insert per second mysql.performance.com_insert_select per second mysql.performance.com_replace_select per second mysql.performance.com_select per second mysql.performance.com_update per second mysql.performance.com_update_multi per second mysql.performance.questions per second mysql.performance.slow_queries per second mysql.performance.queries per second В этом примере grep в файле metadata.csv сначала фильтрует строки, содержащие слово gauge, затем те, у которых query в четвёртой колонке, и выводит название метрики (1-я колонка) с соответствующим значением per_unit_name (5-я колонка). Вывод адреса IPv4, связанного с сетевым интерфейсом $ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }' 192.168.0.38 Команда ifconfig <interface name> выводит сведения по указанному сетевому интерфейсу. Например: en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 ether 19:64:92:de:20:ba inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7 inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255 nd6 options=201<PERFORMNUD,DAD> media: autoselect status:active Затем запускаем grep для inet, что выдаст две строки соответствия. $ ifconfig en0 | grep inet inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7 inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255 Затем с помощью grep -v исключаем строку с ipv6.
  • 16. $ ifconfig en0 | grep inet | grep -v inet6 inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255 Наконец, с помощью awk запрашиваем второй столбец в этой строке: это IPv4-адрес, связанный с нашим сетевым интерфейсом en0. $ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }' 192.168.0.38 Примечание. Мне предложили заменить grep inet | grep -v inet6 такой надёжной командой awk:$ ifconfig en0 | awk ' $1 == "inet" { print $2 }' 192.168.0.38 Она короче и конкретно нацелена на IPv4 с условием $1 == "inet". Извлечение значения из файла конфигурации $ grep 'editor =' ~/.gitconfig | cut -d = -f2 | sed 's/ //g' /usr/bin/vim В файле конфигурации git текущего пользователя ищем значение editor =, обрезаем знак =, извлекаем второй столбец и удаляем все пробелы вокруг. $ grep 'editor =' ~/.gitconfig editor = /usr/bin/vim $ grep 'editor =' ~/.gitconfig | cut -d'=' -f2 /usr/bin/vim $ grep 'editor =' ~/.gitconfig | cut -d'=' -f2 | sed 's/ //' /usr/bin/vim
  • 17. Извлечение IP-адресов из файла журнала Следующий реальный код ищет в журнале БД сообщение Too many connections from (за ним следует IP-адрес) и отображает десять главных нарушителей. $ grep 'Too many connections from' db.log | awk '{ print $12 }' | sed 's@/@@' | sort | uniq -c | sort -rn | head -n 10 | awk '{ print $2 }' 10.11.112.108 10.11.111.70 10.11.97.57 10.11.109.72 10.11.116.156 10.11.100.221 10.11.96.242 10.11.81.68 10.11.99.112 10.11.107.120 Давайте разберем, что делает этот конвейер. Во-первых, как выглядит строка в журнале. $ grep "Too many connections from" db.log | head -n 1 2020-01-01 08:02:37,617 [myid:1] - WARN [NIOServerCxn.Factory:1.2.3.4/1.2.3.4:2181:NIOServerCnxnFactory@193] - Too many connections from /10.11.112.108 - max is 60 Затем awk '{ print $12 }' извлекает из строки IP-адрес. $ grep "Too many connections from" db.log | awk '{ print $12 }' /10.11.112.108 ... Команда sed 's@/@@' удаляет начальный слэш. $ grep "Too many connections from" db.log | awk '{ print $12 }' | sed 's@/@@' 10.11.112.108 ...
  • 18. Примечание. Как мы уже видели ранее, в sed можно использовать любой разделитель. Хотя обычно в качестве разделителя используется /, здесь мы заменяем именно этот символ, что слегка ухудшит читаемость выражения подстановки.sed 's////' sort | uniq -c сортирует IP-адреса в лексикографическом порядке, а затем удаляет дубликаты, добавляя перед IP-адресами количество вхождений каждого. $ grep 'Too many connections from' db.log | awk '{ print $12 }' | sed 's@/@@' | sort | uniq -c 1379 10.11.100.221 1213 10.11.103.168 1138 10.11.105.177 946 10.11.106.213 1211 10.11.106.4 1326 10.11.107.120 ... sort -rn | head -n 10 сортирует строки по количеству вхождений, численно и в обратном порядке, чтобы главные нарушители выводились в первую очередь, из которых отображаются 10 строк. Последняя команда awk { print $2 } извлекает сами IP-адреса. $ grep 'Too many connections from' db.log | awk '{ print $12 }' | sed 's@/@@' | sort | uniq -c | sort -rn | head -n 10 | awk '{ print $2 }' 10.11.112.108 10.11.111.70 10.11.97.57 10.11.109.72 10.11.116.156 10.11.100.221 10.11.96.242
  • 19. 10.11.81.68 10.11.99.112 10.11.107.120 Переименование функции в исходном файле Представим, что мы работаем над проектом и хотели бы переименовать недачно названную функцию (или класс, переменную и т. д.) в исходном файле. Можно сделать это с помощью команды sed -i, которая выполняет замену прямо в оригинальном файле. $ cat izk/utils.py def bool_from_str(s): if s.isdigit(): return int(s) == 1 return s.lower() in ['yes', 'true', 'y'] $ sed -i 's/def bool_from_str/def is_affirmative/' izk/utils.py $ cat izk/utils.py def is_affirmative(s): if s.isdigit(): return int(s) == 1 return s.lower() in ['yes', 'true', 'y'] Примечание. На macOS вместо sed -i используйте sed -i ''. Однако мы переименовали функцию только в оригинальном файле. Это сломает импорт bool_from_str в любом другом файле, поскольку эта функция больше не определена. Нужно найти способ переименовать bool_from_str повсюду в нашем проекте. Такого можно добиться с помощью команд grep, sed, а также циклов for или с помощью xargs. Углубляемся: циклы for и xargs Чтобы заменить в нашем проекте все вхождения bool_from_str, сначала нужно рекурсивно найти их с помощью grep -r.
  • 20. $ grep -r bool_from_str . ./tests/test_utils.py:from izk.utils import bool_from_str ./tests/test_utils.py:def test_bool_from_str(s, expected): ./tests/test_utils.py: assert bool_from_str(s) == expected ./izk/utils.py:def bool_from_str(s): ./izk/prompt.py:from .utils import bool_from_str ./izk/prompt.py: default = bool_from_str(os.environ[envvar]) Поскольку нас интересуют только файлы c совпадениями, также необходимо использовать опцию -l/--files-with-matches: -l, --files-with-matches Only the names of files containing selected lines are written to standard out- put. grep will only search a file until a match has been found, making searches potentially less expensive. Pathnames are listed once per file searched. If the standard input is searched, the string ``(standard input)'' is written. $ grep -r --files-with-matches bool_from_str . ./tests/test_utils.py ./izk/utils.py ./izk/prompt.py Затем можем использовать команду xargs для осуществления действий с каждой строки выходных данных (то есть всех файлов, содержащих строку bool_from_str). $ grep -r --files-with-matches bool_from_str . | xargs -n 1 sed -i 's/bool_from_str/is_affirmative/' Опция -n 1 указывает, что каждая строка в выходных данных должна выполнить отдельную команду sed. Затем выполняются следующие команды: $ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py $ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py $ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py
  • 21. Если команда, которую вы вызываете с помощью xargs (в нашем случае sed), поддерживает несколько аргументов, то следует отбросить аргумент -n 1 для производительности. grep -r --files-with-matches bool_from_str . | xargs sed -i 's/bool_from_str/is_affirmative/' Эта команда затем исполнит $ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py ./izk/utils.py ./izk/prompt.py Примечание. Из синопсиса sed на ман-странице видно, что команда может принять несколько аргументов.SYNOPSIS sed [-Ealn] command [file ...] sed [-Ealn] [-e command] [-f command_file] [-i extension] [file ...] Действительно, как мы видели в предыдущей главе, file ... означает, что принимаются несколько аргументов, представляющих собой имена файлов. Мы видим, что произведены замены для всех вхождений bool_from_str. $ grep -r is_affirmative . ./tests/test_utils.py:from izk.utils import is_affirmative ./tests/test_utils.py:def test_is_affirmative(s, expected): ./tests/test_utils.py: assert is_affirmative(s) == expected ./izk/utils.py:def is_affirmative(s): ./izk/prompt.py:from .utils import is_affirmative ./izk/prompt.py: default = is_affirmative(os.environ[envvar]) Как это часто бывает, существует несколько способов достижения одного и того же результата. Вместо xargs мы могли бы использовать циклы for, чтобы перебирать строки по списку и выполнять действие над каждым элементом. У этих циклов такой синтаксис: for item in list; do command $item
  • 22. done Если обернуть нашу команду grep в $(), то оболочка выполнит её в подоболочке, результат чего затем будет повторён в цикле for. $ for file in $(grep -r --files-with-matches bool_from_str .); do sed -i 's/bool_from_str/is_affirmative/' $file done Эта команда выполнит $ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py $ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py $ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py Синтаксис циклов for кажется мне более чётким, чем у xargs, однако последняя может выполнять команды параллельно, используя параметры -P n, где n — максимальное количество параллельных команд, выполняемых одновременно, что может дать выигрыш в производительности. Резюме Все эти инструменты открывают целый мир возможностей, так как позволяют извлекать и преобразовывать данные, создавая целые конвейеры из команд, которые, возможно, никогда не предназначались для совместной работы. Каждая из них выполняет относительно небольшую функцию (сортировка sort, объединение cat, фильтры grep, редактирование sed, вырезание cut и т. д.). Любую задачу, включающую текст, можно свести к конвейеру более мелких задач, каждая из которых выполняет простое действие и передаёт свои выходные данные в следующую задачу. Например, если нам хочется узнать, сколько уникальных IP-адресов в файле журнала, и чтобы эти IP-адреса всегда появлялись в одном и том же столбце, то можно запустить следующую последовательность команд:  grep строк, которые соответствуют шаблону строк с IP-адресами  найти столбец с IP-адресом, извлечь его с помощью awk  отсортировать список IP-адресов с помощью sort  устранить смежные дубликаты с помощью uniq  подсчитать количество строк (то есть уникальных IP-адресов) с помощью wc -l
  • 23. Поскольку есть множество нативных и сторонних инструментов обработки текста, также много способов решить любую задачу.