Пингвитон
151 subscribers
4 photos
12 links
иногда пишу тут какие-то полезные штуки про Linux и Python
Download Telegram
Есть файл с гигантским json'ом, который нужно просмотреть глазами? Но смотреть его одной строкой без форматирования больно и неудобно. Поможет Python:

cat patches/patches.json | python3 -m json.tool

На выходе в консоли будет красивый отформатированный json с отступами, который легко читать и искать нужное.
Чтобы открыть какой-то файл и в реальном времени видеть, что в него пишут, можно использовать tail с опцией -f:

$ tail -f /var/log/nginx/access.log

Но что делать, если в лог пишут очень активно, а вам нужен какой-то конкретный запрос, который вы, например, сами только что отправили в рамках дебага/тестирования? Увидеть глазами невозможно, потому что стена текста движется постоянно с большой скоростью.

Можно использовать grep. Но если использовать просто grep вот так:

$ tail -f /var/log/nginx/access.log | grep public.list

-- то вы свой запрос не увидите, даже если отправите. grep буферизует результаты и будет ждать, когда появятся ещё какие-то строки, которые можно вывести. Чтобы grep выплевывал каждую новую одинокую появляющуюся строку, используйте опцию --line-buffered:

$ tail -f /var/log/nginx/access.log | grep public.list --line-buffered

Помните, что если используете цепочку грепов, то эту опцию нужно проставить каждому:

$ tail -f /var/log/nginx/access.log | grep public.list --line-buffered | grep username --line-buffered
Ещё я недавно насмотрелась на то, как работают с linux другие и хочу спросить: вы же знаете про команду ctrl-r?

Все команды, которые вы выполняете в терминале, сохраняются. Чтобы посмотреть историю, введите history.

Как быстро навигироваться по истории?

Варианты, как найти команду в терминале, которую вы вводили ранее:
1) если вы вводили ее только что или пару команд назад, то можно просто нажимать клавишу стрелки вверх -- вам последовательно будут показываться команды, которые запускались, от самой свежей к более старым,
2) history | grep <команда> , выделить нужную команду, вставить, запустить -- это как раз тот способ, на который я насмотрелась и который сподвиг меня написать этот пост (не надо так),
3) ctrl-r -- нажимаете сочетание этой клавиши и начинаете вводить команду, которая вам нужна, например ssh, вам покажут самую свежую команду, которая содержит эти символы. Если это не та команда (вы хотите подключиться к другой машине, к которой обращались раньше), то просто нажимаете ctrl-r ещё раз и ещё раз, пока не найдете нужное. Если нажать стрелку вправо, то команда вставится в терминал, но не выполнится (вы можете её редактивровать), если энтер -- команда сразу выполнится.

Пользуйтесь ctrl-r, это удобно!
У вас есть сервер. А на сервере какой-то дикий треш с симлинками. Все друг на друга ссылаются. Симлинк на симлинк на симлинк. Как понять, что вот эта директория -- это то же самое, что вот эта директория? Могу ли я быть уверена, что вот тут: /usr/share/kcare-eportal/patches/test/release лежит то же самое, что и вот тут: /var/www/html/test/release?

Простой и быстрый способ:

[igrechikhina@patches ~]$ ls -d -i /usr/share/kcare-eportal/patches/test/release
1966244 /usr/share/kcare-eportal/patches/test/release
[igrechikhina@patches ~]$ ls -d -i /var/www/html/test/release
1966244 /var/www/html/test/release

-d, чтобы не показывать содержимое директории, а только её саму.
-i, чтобы показать волшебное число для директории.

Если числа, выплюнутые -i, совпали для двух директорий, значит, это одна и та же директория (и вам нужно разбираться, кто кому симлинк).
Продолжая тему "ты ли это" или как сравнить две сущности и убедиться, что это одно и то же. Иногда надо сравнить два файла на одной машине, иногда надо убедиться, что файл у себя на машине и где-то на удалённом сервере -- это один и тот же файл. Так вот -- cksum в помощь.

Вот хочу узнать, одна ли и та же версия файла у меня в репе и сейчас на сервере:

На удалённом:
[root@patches2 ~]# cksum /usr/local/bin/kcare-deploy-prod
1081165413 480 /usr/local/bin/kcare-deploy-prod

На своём:
~$ cksum /home/lauvenhelz/dev/cldlnx/kernelcare/dist-server-scripts/kcare-deploy-prod
3998718679 616 /home/lauvenhelz/dev/cldlnx/kernelcare/dist-server-scripts/kcare-deploy-prod

Вижу, что нет, разные. Первое число -- чексумма, второе число -- размер, оба отличаются.

Ещё есть md5sum, раньше пользовалась ею, но у неё длиннее выводимая чексумма, что не очень удобно, и нет информации о размере файла:

[root@patches2 ~]# md5sum /usr/local/bin/kcare-deploy-prod
a490ae81807608ee2eafe35932cade41 /usr/local/bin/kcare-deploy-prod
Хочу отдельно обратить ваше внимание, что это команды именно для быстрой проверки для себя. Быстро убедиться, что файлы одинаковые или отличаются. Не используйте их в серьёзных задачах -- это ненадёжно (в cksum коллизии возникают легко, в md5sum не так легко, но тоже возникают). Если вам нужна серьёзная проверка и контроль файлов в рамках реализуемой задачи, используйте, например sha256sum.
Когда вы запускаете какую-то команду в консоли, вы, очевидно, запускаете какой-то файл. Как узнать какой и где он лежит? Есть команда which. Во-первых, удобно использовать, чтобы узнать, "кого запускаем-то". Например, когда вы впервые залезли на сервер и разбираетесь, как работают все эти самописные скрипты. Вы не можете сделать cat <команда>, вам нужен путь до файла.

Можно посмотреть путь и потом использовать его в cat:

[root@patches2 ~]# which kcare-deploy-prod
/usr/local/bin/kcare-deploy-prod

А можно сделать всё в одну строчку:

[root@patches2 ~]# cat `which kcare-deploy-prod`
#!/bin/bash
<...тут содержимое файла...>

Во-вторых, конструкцию <команда> `which <другая команда>` можно использовать во всех остальных случаях (: Например, для предыдущего примера с cksum:

[root@patches2 ~]# cksum `which kcare-deploy-prod`
3998718679 616 /usr/local/bin/kcare-deploy-prod
Мне нужно скачать с одного сервера на другой 27 гигабайт. На первый сервер я захожу по ssh и там надо запустить rsync, чтобы вытащить файлы со второго. Сеть отвратительная, будет качаться всю ночь. ssh иногда отваливается. Как быть уверенной, что процесс не помрёт в середине ночи от broken pipe'а, когда ssh отвалится? Ну, и лаптоп не хочется всю ночь включённым держать.

Используете screen. Запускаете, он эмулирует терминал. Выполняете там необходимое (я запускаю rsync, например). И покидаете эмуляцию с помощью ctrl+a, затем d. Эта команда не окончит сессию, а просто позволит вам выйти, "отцепиться":

[root@192-168-246-68 ~]# screen
[detached from 12233.pts-0.192-168-246-68]

Теперь эта сессия будет живой до тех пор, пока вы не закроете её самостоятельно. Запущенная в ней команда в безопасности, не будет прервана и успешно отработает.
Чтобы зайти обратно, используйте screen -r.

Можно создать несколько терминалов. Каждый раз запуская screen, создается новая сессия. Когда захотите вернуться в конкретную сессию, нужно будет ввести её pid. Вот я ввожу screen -r, когда у меня есть несколько эмуляций и вижу:

[root@192-168-246-68 ~]# screen -r
There are several suitable screens on:
16373.pts-0.192-168-246-68 (Detached)
12233.pts-0.192-168-246-68 (Detached)
Type "screen [-d] -r [pid.]tty.host" to resume one of them.

Вводите нужный pid, например, screen -r 12233.pts-0.192-168-246-68 заходите.

Чтобы убить ненужную больше эмуляцию, заходите внутрь и нажимаете ctrl+d, на выходе вам сообщат об окончании сессии:

[root@192-168-246-68 ~]# screen -r 16373.pts-0.192-168-246-68
[screen is terminating]

screen можно использовать не только на удаленных машинах, чтобы защититься от сломанного ssh, но и локально: чтобы, например, случайно не закрыть окно терминала, где выполняется какая-то долгая операция.
И-и-и-и снова моя любимая рубрика про сравнение и нахождение отличий. Есть чудесный инструмент comm. Сранивает строки в отсортированных файлах.

Использовать в виде comm file-1 file-2. Умеет выводить три колонки:
1) строки уникальные для 1-го файла,
2) строки уникальные для 2-го файла,
3) строки, появляющиеся в обоих файлах.

И есть три опции:
-1 не выводить строки, уникальные для 1-го файла,
-2 не выводить строки, уникальные для 2-го файла,
-3 не выводить строки, которые есть в обоих файлах.

Сейчас мне нужно понять, как отличаются файлы на двух серверах.

Сначала я сделала листинг интересующих меня директорий на двух интересующих меня машинах и сохранила их в соответствующие файлы:

[igrechikhina@patches ~]$ ls /var/www/html/ | cat > /tmp/prod.txt
[root@patches02 ~]# ls /var/www/html/ | cat > /tmp/stage.txt

(команда cat здесь нужна, чтобы вывод ls был в виде одного столбца -- для comm это важно)

Потом скопировала их к себе на машину и сравниваю:

comm -3 /tmp/prod.txt /tmp/stage.txt

Я хочу увидеть только те файлы, которые есть либо на одной, либо на другой машине, поэтому я использую опцию -3, чтобы не видеть несколько тысяч пересекающихся между машинами файлов.

Да, ещё напомню, что в конце вы можете легко посчитать количество отличающихся строк, добавив в строку wc -l:

comm -3 /tmp/prod.txt /tmp/stage.txt | wc -l
Испытываю некоторый стыд, потому что узнала полезное о штуке, которую вижу в мониторе ежедневно.

Все знают в лицо вывод ls -la. Все знают, что в начале есть кусок с `rwx`'ами, который может выглядеть по-разному, например:

-rw-------
-rw-r--r--
-rwxr-xr-x
-rwxrwxrwx

Это, если что, права: read write execute для owner, group owner и всех остальных. Например, вот тут -rw------- owner имеет право читать и писать (`rw`), но не имеет права выполнять, а все остальные вообще никакими правами не обладают -- ничего не могут делать. А вот тут все подряд имеют права на всё (и читать, и писать, и выполнять): -rwxrwxrwx (для каждого прописано `rwx`).

Так вот. Перед правами есть еще символ -. И это не эстетики и форматирования ради, а пользы для. - в начале этой строки означает, что осматриваемая вами сущность -- обычный файл (regular file). И, кроме дефиса, я знала, что бывает еще d, например:

drwxr-xr-x
drwxrwxr-x

d означает директорию.

И с этим знанием я жила. Недавно мне попалась хорошая картинка, которая всё подтвердила и намеков на то, что мои знания не полны, не дала:

http://linuxcommand.org/images/file_permissions.png

Бывает символ -, который означает файл, а бывает буква d, которая означает директорию. Всё.

Но сегодня границы моего мира расширились. Я пошла погуглить, как определить, кто перед тобой -- симлинк или файл. И узнала невероятное: кроме - и d есть ещё l. И l значит симлинк. И бывает вот так:

lrwxrwxrwx

Офигеть. Я ведь (не) видела эту запись каждый день: у меня вся работа симлинками обмазана.

Но даже это не было пределом, потому что я погуглила ещё и прочитала вот эту статью, узнав что файлами, директориями и симлинками данные обозначения не ограничиваются: https://en.wikipedia.org/wiki/Unix_file_types
Сегодня про Python. Когда у меня возникает мысль хранить "много данных" в, скажем, словаре, а потом следом я думаю, что "ну, это же будет занимать много памяти, не надо так" -- я иду и проверяю, сколько конкретно памяти занимает это моё "много данных". Сначала считать и замерять -- потом делать какие-то выводы. Это очень облегчает жизнь, я уверена в результатах, у меня есть внятные аргументы в споре, я могу приводить конкретные значения вместо расплывчатых "это будет медленно", "мы упрёмся в память" и всё такое. Пока я готовлю большую статью про нагрузочное тестирование (выложу после новогодних праздников), вот подсказка, как быстро прикинуть, сколько места займет ваша будущая структура в памяти.

Мне нужно хранить 70 тысяч элементов в словаре, где в качестве ключа -- строка из 11-16 символов, а в качестве значения -- тьюпл вида (таймстемп, буль). Например, {'JBcIMVrQYSJNEFxF': (1545814794, True)}. Сгенерировать такой словарь легко:
>>> import time 
>>> l = {str(i) + 'VrQYSJNEFxF': int(time.time()) for i in range(70000)}

Как проверить, сколько места он занимает?

Первым приходит на ум sys.getsizeof(). В документации о нём написано: Return the size of an object in bytes. И это важно: sys.getsizeof() возвращает размер самого объекта, но не объектов, которые находятся внутри него. Почему это проблема? Смотрим:

Создаем список из 10к элементов:
>>> l = [i for i in range(10000)]
>>> sys.getsizeof(l)
87624

Создаем словарь из 10к элементов, где в качестве значения ключа копия нашего ранее созданного списка (далее D_LIST), и такой же словарь из 10к элементов, но в качестве значения -- обычный инт (далее D_INT). Смотрим на размеры, которые вернет sys.getsizeof():

>>> d_list = {i: l.copy() for i in range(10000)}
>>> sys.getsizeof(d)
295008
>>> d_int = {i: i for i in range(10000)}
>>> sys.getsizeof(d)
295008

Одинаковые. А ведь размер занимаемой памяти у D_LIST должен получиться где-то в районе 800М (размер самого списка в 87 килобайт умножить на 10к ключей). sys.getsizeof() возвращает размер самого объекта словаря на 10 тысяч ключей, а память, которую занимают объекты, лежащие в качестве значений, не учитывается. Нам не подходит.

Так как посмотреть, сколько памяти занято?

Есть модуль resource. Из документации: This module provides basic mechanisms for measuring and controlling system resources utilized by a program. Там же есть метод resource.getrusage(who), который возвращает объект, описывающий ресурсы, потребляемые текущим процессом. В качестве параметра можно передать три разные штуки. Каждая определяет, информацию о чем надо вернуть: для самого процесса, для его детей, для всех перечисленных. Нам нужно для самого процесса: resource.RUSAGE_SELF. В выхлопе будет с десяток разных элементов со своими показателями (всё описано в соответствующем разделе документации), нас интересует конкретный: ru_ixrss, показывающий shared memory size. То, что нужно, давайте пользоваться:

>>> import time
>>> import resource
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
9484
>>> l = [i for i in range(10000)]
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
9952
>>> d_int = {i: i for i in range(10000)}
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
10576
>>> d_list = {i: l.copy() for i in range(10000)}
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
793304
>>> d_my = {str(i) + 'VrQYSJNEFxF': int(time.time()) for i in range(70000)}
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
803140

Что видим? Данные для D_INT и D_LIST отличаются: 0.6М для D_INT (9952-9484) и 782М (793304 - 10576) для D_LIST. У D_LIST получились те самые "в районе 800М", которые мы обозначали выше -- можно верить.

И теперь осталось только посчитать память у нашего объекта, с которого началась вся затея:

>>> import time
>>> import resource
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
9536
>>> d_my = {str(i) + 'VrQYSJNEFxF': int(time.time()) for i in range(70000)}
>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
19360
>>> (19360 - 9536) / 1000
9.824

Всего лиш
ь 10 мегабайт на наш словарь с 70 тысячами ключей, в которых хранятся данные по времени + булевое значений. Мало? Мало. Наша стартовая мысль о "много данных, много памяти" была неверна.

Подводя итог:
1) всегда считаю и замеряю память-скорость-вотэвер перед тем, как заявлять, что что-то будет медленным-прожовливым-вставить-нужное,
2) замеры по памяти делаю с помощью resource.getrusage(resource.RUSAGE_SELF).ru_maxrss.
Клятвенно обещаю, что следующую большую статью сделаю отдельной ссылкой с форматированием и всем таким! Извините меня ещё раз, заметку писала на коленке, надеюсь, она полезна, несмотря ни на что (% Предыдущие большие заметки тоже переоформлю. Давайте вместе верить в меня, я справлюсь (%
Я снова хотела написать небольшую заметку, но вспомнила, что обещала начать пользоваться Телеграфом. Так вот, я смогла! Сегодня в очередной раз порадовалась тому, насколько легче стала моя жизнь с тех пор, как я полюбила awk, grep, sort, uniq и прочие маленькие (awk, это не про тебя) консольные утилиты -- и хочу поделиться случившимся кейсом. У меня есть файл, в котором содержится часть названий нужных мне файлов. Как быстро скачать их, не заморачиваясь с копированием путей и всем таким? Описала тут:
https://telegra.ph/Ne-delajte-rukami-to-chto-mozhno-sdelat-konsolnoj-komandoj-01-10
Запускаете команду, у которой будет много выхлопа. И хотите, с одной стороны, в режиме реального времени видеть, что происходит, а с другой сохранить выхлоп в файл, чтобы проанализировать позже. Что делать? Использовать tee.

python /tmp/convert.py | tee /tmp/convert.log

tee умеет перенаправлять свой input в два места: в stdout и в указанный вами файл.

Или сразу в несколько файлов (например, один вы хотите оставить в качестве референса, а второй планируете изменять):

# echo 1111 | tee /tmp/reference.txt /tmp/edit.txt
1111
# cat /tmp/reference.txt
1111
# cat /tmp/edit.txt
1111
Мне тут понадобилось удалить много-много маленьких файлов. Я знала, что удаляться они будут долго и мучительно. И хотелось видеть, что там на диске происходит -- как особождается память. Что я делаю? Использую watch. Эта штука будет запускать указанную ей команду каждые две секунды и в режиме фуллскрин будет показывать результаты её запуска.

Как я уже сказала, мне хотелось видеть состояние диска. Для этого есть команда df -- в её выхлопе можно увидеть значения used, available и процент свободного места на диске. Запускаю удаление файлов, в соседней вкладке запускаю watch df и в режиме реального времени вижу, как растет значение available и уменьшается значение used:
То же самое можно делать и с параметризованными командами. Например, вы хотите видеть, как растёт файл, в который вы сейчас пишете. Используйте:
watch ls -la /tmp/patches.json

Чтобы выйти из фуллскрина watch'а -- ctrl+c.
У меня есть таймстемп, например, 1549886527, и я хочу быстро сконвертировать его во что-то человекочитаемое.

Использую date:
~$ date -u -d @1549886527
Mon Feb 11 12:02:07 UTC 2019

На всякий случай: таймстемп -- это seconds since the epoch, а epoch -- это 1970-01-01 UTC. Таймстемп всегда UTC.

Чтобы сконвертировать таймстемп в свое локальное время вместо UTC, убираю опцию -u:

~$ date -d @1549886527
Mon Feb 11 15:02:07 MSK 2019
Иногда нужно узнать скорость чтения и записи файлов на машине. Например, когда что-то тормозит, и нужно убедиться, что тормозит не диск (или наоборот -- убедиться, что проблема в диске). Понадобится большой файл (у меня 500 Мб).

# dd if=/tmp/24122018_3.tar.bz2 of=/dev/null bs=64k
8663+1 records in
8663+1 records out
567791280 bytes (568 MB) copied, 9.90767 s, 57.3 MB/s

dd прочитает весь файл, указанный в if= (размер вычитываемого определяет bs=, по дефолту -- 1 КБ), выводя его содержимое в /dev/null (опция of=). В конце напишет скорость, с которой читал. На моём сервере получилось 57.3 МБ.

Точно так же можно узнать скорость записи. Нужно взять другой большой файл (это важно, если вы будете использовать тот же, то получите неверные быстрые результаты -- файл закешировался).

# dd if=/tmp/27062018_1.tar.bz2 of=/var/www/html/test.tar bs=64k
8247+1 records in
8247+1 records out
540534066 bytes (541 MB) copied, 15.3244 s, 35.3 MB/s

Чтобы проверить запись, в опцию of= нужно передать любой файл, куда dd будет записывать содержимое файла, указанного в if=. На моем сервере получилось 35.3 МБ в секунду.

Я делала замеры на умеренно дохлой виртуалке, проверьте скорость на своих рабочих машинах для сравнения (:
Вас просят дать доступ на какой-то сервер, добавив публичный ключ пользователя в ~/.ssh/authorized_keys. Как обычно мы это делаем? ssh-иться на сервер, открыть в текстовом редакторе файл authorized_keys, добавить ключ в конец, сохранить. Медленно и печально, короче, делаем. А можно сделать в одну команду.

Например, у меня есть рутовый доступ, а пользователь просит дать доступ из-под пользователя user. Копирую ключ в буфер обмена. Здесь важное: обязательно нужно скопировать ключ с переносом строки в конце. Это важно! Иначе вы испортите файл authorized_keys. Так вот, копируете выданный ключ в буфер обмена, например: ssh-rsa i-am-the-key user@company.com

Убеждаетесь, что ключ скопирован и в конце есть перенос строки:

~$ xsel
ssh-rsa i-am-the-key user@company.com
~$

Если переноса строки нет, то будет выглядеть так:

~$ xsel
ssh-rsa i-am-the-key user@company.com
\ No newline at end of selection
~$

Повторяю ещё раз: этот перенос строки очень важен! Если переноса строки не будет, то вы рискуете не просто не дать просящему доступ, но и сломать доступ себе и другим.

Убедились, что ключ с переносом строки в буфере, далее команда:

xsel | ssh root@192.168.246.34 bash -c "cat >> /home/user/.ssh/authorized_keys"

Команда добавит содержимое вашего буфера обмена в указанный файл на указанном сервере.

xsel вообще очень полезная бифидобактерия, пользуйтесь им (: Очень удобно, например, скопировать кусок файла и отсортировать содержимое буфера прямо в консоли -- xsel | sort. И ещё тонна других применений, которые позволят не создавать лишние файлы и не делать лишних телодвижений.