Пингвитон
151 subscribers
4 photos
12 links
иногда пишу тут какие-то полезные штуки про Linux и Python
Download Telegram
Channel name was changed to «Заметки о Python и Arch Linux»
Channel photo updated
Есть файл с гигантским 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