Клятвенно обещаю, что следующую большую статью сделаю отдельной ссылкой с форматированием и всем таким! Извините меня ещё раз, заметку писала на коленке, надеюсь, она полезна, несмотря ни на что (% Предыдущие большие заметки тоже переоформлю. Давайте вместе верить в меня, я справлюсь (%
Я снова хотела написать небольшую заметку, но вспомнила, что обещала начать пользоваться Телеграфом. Так вот, я смогла! Сегодня в очередной раз порадовалась тому, насколько легче стала моя жизнь с тех пор, как я полюбила awk, grep, sort, uniq и прочие маленькие (awk, это не про тебя) консольные утилиты -- и хочу поделиться случившимся кейсом. У меня есть файл, в котором содержится часть названий нужных мне файлов. Как быстро скачать их, не заморачиваясь с копированием путей и всем таким? Описала тут:
https://telegra.ph/Ne-delajte-rukami-to-chto-mozhno-sdelat-konsolnoj-komandoj-01-10
https://telegra.ph/Ne-delajte-rukami-to-chto-mozhno-sdelat-konsolnoj-komandoj-01-10
Telegraph
Не делайте руками то, что можно сделать консольной командой
Консоль -- это прекрасно. Вот у меня есть файл: ~$ cat /tmp/archives/public.list 11012018_2 1b0ca3a9834b0384ab32fe6dcb32de777 22022018_1 5914f4872e21ece2b0fe616b9a1317a62 10042018_3 386fb9c4a10e18ce7b96b9b6467ac5d66 13072018_1 76eb18b3116345a0f654985b7c6d93279…
Запускаете команду, у которой будет много выхлопа. И хотите, с одной стороны, в режиме реального времени видеть, что происходит, а с другой сохранить выхлоп в файл, чтобы проанализировать позже. Что делать? Использовать tee.
tee умеет перенаправлять свой input в два места: в stdout и в указанный вами файл.
Или сразу в несколько файлов (например, один вы хотите оставить в качестве референса, а второй планируете изменять):
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'а -- ctrl+c.
watch ls -la /tmp/patches.json
Чтобы выйти из фуллскрина watch'а -- ctrl+c.
У меня есть таймстемп, например,
Использую
На всякий случай: таймстемп -- это seconds since the epoch, а epoch -- это 1970-01-01 UTC. Таймстемп всегда UTC.
Чтобы сконвертировать таймстемп в свое локальное время вместо UTC, убираю опцию
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= (размер вычитываемого определяет bs=, по дефолту -- 1 КБ), выводя его содержимое в /dev/null (опция of=). В конце напишет скорость, с которой читал. На моём сервере получилось 57.3 МБ.
Точно так же можно узнать скорость записи. Нужно взять другой большой файл (это важно, если вы будете использовать тот же, то получите неверные быстрые результаты -- файл закешировался).
Чтобы проверить запись, в опцию of= нужно передать любой файл, куда dd будет записывать содержимое файла, указанного в if=. На моем сервере получилось 35.3 МБ в секунду.
Я делала замеры на умеренно дохлой виртуалке, проверьте скорость на своих рабочих машинах для сравнения (:
# 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. Так вот, копируете выданный ключ в буфер обмена, например:
Убеждаетесь, что ключ скопирован и в конце есть перенос строки:
Если переноса строки нет, то будет выглядеть так:
Повторяю ещё раз: этот перенос строки очень важен! Если переноса строки не будет, то вы рискуете не просто не дать просящему доступ, но и сломать доступ себе и другим.
Убедились, что ключ с переносом строки в буфере, далее команда:
Команда добавит содержимое вашего буфера обмена в указанный файл на указанном сервере.
xsel вообще очень полезная бифидобактерия, пользуйтесь им (: Очень удобно, например, скопировать кусок файла и отсортировать содержимое буфера прямо в консоли --
Например, у меня есть рутовый доступ, а пользователь просит дать доступ из-под пользователя 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. И ещё тонна других применений, которые позволят не создавать лишние файлы и не делать лишних телодвижений.Однажды вам понадобилось новое виртуальное окружение с uWSGI. Вы его создаете, ставите зависимости, запускаете свое приложение и... оно не встает с ошибкой
uwsgi: error while loading shared libraries: libicui18n.so.61: cannot open shared object file: No such file or directory. Проблема в том, что взялся закешированный на вашей машине (ранее установленный) uWSGI, который был собран с другой версией библиотеки -- и сейчас uWSGI эту библиотеку на старом месте не находит. Как лечить? Пересобрать uWSGI с помощью этого: pip install -I --no-binary :all: uWSGI==2.0.17 (версию можно опустить или вставить нужную вам).Важное сообщение. Есть функция стандартной библиотеки
Cегодня я узнала важное. Вот это:
Ответ на вопрос "почему" искать в математике, например, тут: https://en.wikipedia.org/wiki/Empty_set
Any statement that begins "for every element of ∅" is not making any substantive claim; it is a vacuous truth. This is often paraphrased as "everything is true of the elements of the empty set".
all() -- возвращает True, если все элементы переданного в него списка True. >>> all(['one', True, 1])
True
>>> all(['one', True, 0])
False
Cегодня я узнала важное. Вот это:
>>> all([])
True
all() возвращает True, если передать в него пустой список. Это важно, помните об этом, чтобы не получить зелёные тесты, когда в действительности ничего не работает. Ответ на вопрос "почему" искать в математике, например, тут: https://en.wikipedia.org/wiki/Empty_set
Any statement that begins "for every element of ∅" is not making any substantive claim; it is a vacuous truth. This is often paraphrased as "everything is true of the elements of the empty set".
Wikipedia
Empty set
set (in mathematics) containing no elements
У меня есть колонка данных. Например, несколько строк, которые я получила после выполнения запроса к базе. Теперь мне нужно эту колонку как-то быстро превратить в строку, разделённую запятыми. Было:
Должно стать:
Давайте пробовать. Есть утилита
Вот такой результат получится, если попросить заменить перенос строки на одинокую запятую:
Всё хорошо, но иногда нужно, чтобы обязательно после запятой был пробел. Пробуем добавить в tr пробел, tr его игнорирует:
Игнорирует потому, что
Попробуем другие (менее изящные, но рабочие) способы.
А, ещё обратите внимание, что при использовании tr сносится и последний знак переноса строки, так что строка ввода оказывается на строке предыдущего выхлопа:
Чтобы из колонки сделать красивую строку с разделителем в виде запятой и пробела и со знаком новой строки на конце, можно использовать:
Вариант 1.
Можно просто сохранить себе в сниппеты и использовать, когда надо (: А можно разобраться. Давайте разберёмся.
Вариант 2.
В конце концов канал у меня и про Python тоже, давайте использовать его силу. В этом проще разобраться и проще запомнить, чем прорву опций
В обоих случаях получим красивое желанное:
one
two
three
Должно стать:
one, two, three
Давайте пробовать. Есть утилита
tr для преобразования символов. Ей можно передать нашу колонку на вход и попросить заменить один символ на другой. tr отличный, маленький и удобный, но есть проблема, которую мне так и не удалось победить. Вот такой результат получится, если попросить заменить перенос строки на одинокую запятую:
~$ xsel | tr '\n' ','
one,two,three~$
Всё хорошо, но иногда нужно, чтобы обязательно после запятой был пробел. Пробуем добавить в tr пробел, tr его игнорирует:
~$ xsel | tr '\n' ', '
one,two,three~$
Игнорирует потому, что
tr мапит один символ на другой один символ. Нельзя заставить его заменить один символ двумя другими, он просто берёт первый из указанных. Попробуем другие (менее изящные, но рабочие) способы.
А, ещё обратите внимание, что при использовании tr сносится и последний знак переноса строки, так что строка ввода оказывается на строке предыдущего выхлопа:
one,two,three~$ -- видите ~$ тут? Некрасиво и не всегда удобно. Полечим в следующем примере. Чтобы из колонки сделать красивую строку с разделителем в виде запятой и пробела и со знаком новой строки на конце, можно использовать:
Вариант 1.
xsel | awk -vORS=", " '{ print $1 }' | sed 's/, $/\n/'Можно просто сохранить себе в сниппеты и использовать, когда надо (: А можно разобраться. Давайте разберёмся.
xsel -- напоминаю, это утилита, которая выводит в stdout то, что у вас в буфере обмена. awk -vORS=", " '{ print $1 }' -- вывести первую колонку входных данных, используя разделитель вида ", ".sed 's/, $/\n/' -- обрезать висящий на хвосте строки ", " и заменить его символом новой строки.Вариант 2.
В конце концов канал у меня и про Python тоже, давайте использовать его силу. В этом проще разобраться и проще запомнить, чем прорву опций
awk и последовательность слешей в sed (%xsel | python -c 'import sys; print(", ".join(r.strip() for r in sys.stdin))'В обоих случаях получим красивое желанное:
~$ xsel | awk -vORS=", " '{ print $1 }' | sed 's/, $/\n/'
one, two, three
~$ xsel | python -c 'import sys; print(", ".join(r.strip() for r in sys.stdin))'
one, two, threeКороткая заметка для тех, кто пользуется Ansible. Вывести все доступные переменные можно следующей командой:
ansible localhost, -m debug -a msg="{{ vars }}"У вас большой и сложный uWSGI конфиг? Разные секции, magic variables, if'ы и плейсхолдеры? Добавьте туда же опцию
show-config = true, чтобы при старте приложения в логах увидеть все опции, с которым приложение запускается.Написала невозможно длинный текст по итогам просмотра видео с CppCon, выложила на Yandex Zen, там даже можно оставить комментарии. Излагаю мысли о рефакторинге, техническом долге и тестах: https://zen.yandex.ru/media/id/5cf4dd82e84caf00ae7ac62b/spokoino-pro-refaktoring-i-negoduiusce-pro-tehnicheskii-dolg-5d11e89b687e2700ae48e6aa
Яндекс Дзен
Спокойно про рефакторинг и негодующе про технический долг
Мысли о докладе, где автор пытается ответить на вопрос: "Что делать, если вам достался в наследство гигантский проект?" -- рассказываю, с чем согласна, что классно, а что вызывает негодование и плевки.
Мне нужно проверить, что строка -- это число. Важно: обычно под задачей "проверить, что строка -- число" подразумевается "убедиться, что строка -- это натуральное десятичное число". То есть строка, будучи переданной в функцию
Можно в лоб:
А можно короче, воспользовавшись методом строки. Но каким? Есть три внешне похожих метода:
Чем они отличаются? Кого лучше использовать?
Для нашей задачи про "натуральное десятичное число" подходит только
Если строка валидируется, чтобы дальше конвертировать её с помощью
int(), корректно сконвертируется в объект типа int. Можно в лоб:
try:
number = int(my_string)
except ValueError:
print("Not a number")
А можно короче, воспользовавшись методом строки. Но каким? Есть три внешне похожих метода:
>>> '1'.isdecimal()
True
>>> '1'.isdigit()
True
>>> '1'.isnumeric()
True
Чем они отличаются? Кого лучше использовать?
isdecimal() вернет True только для подобных строк: "12345"
"12"
"98201"
isdigit() вернет True для всех предыдущих и чисел в верхнем регистре: "12345"
"1233³"
"³"
isnumeric() вернет True для всех предыдущих и дробей: "12345"
"½¼"
"½³"
"12345½"
Для нашей задачи про "натуральное десятичное число" подходит только
isdecimal(). Он вернёт True только в том случае, если переданная строка может быть использована в качестве аргумента функции int(). Например, если взять тройку в верхнем регистре и воспользоваться всеми тремя методами, то получим следующее: >>> '³'.isdecimal()
False
>>> '³'.isdigit()
True
>>> '³'.isnumeric()
True
isdigit() и isnumeric() возвращают True, и это поломает дальнейшую конвертацию с помощью int(): >>> int('³')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '³'Если строка валидируется, чтобы дальше конвертировать её с помощью
int(), то нужно использовать isdecimal().У вас есть репозиторий с большим проектом, над которым работает не один человек. В какой-то момент вы обнаруживаете, что одно из приложений, являющееся частью этого репозитория, не работает. Вы знаете коммит, на котором приложение точно работало, но не знаете, кто и сколько раз вносил изменения с тех пор. Как из потока ежедневных коммитов в самые разные части репозитория выделить изменения, касающиеся только этого файла, и посмотреть их?
Где commit -- это хеш коммита, на котором все работало, а file path -- относительный или абсолютный путь до интересующего вас файла. Например:
В выхлопе увидите все изменения, которые случились с файлом, начиная указанным коммитом.
И да, с директориями команда тоже работает.
git diff <commit>.. -- <file path>
Где commit -- это хеш коммита, на котором все работало, а file path -- относительный или абсолютный путь до интересующего вас файла. Например:
git diff 16ccf42.. -- py/kcarectl.pyВ выхлопе увидите все изменения, которые случились с файлом, начиная указанным коммитом.
И да, с директориями команда тоже работает.
Пингвитон
У меня есть колонка данных. Например, несколько строк, которые я получила после выполнения запроса к базе. Теперь мне нужно эту колонку как-то быстро превратить в строку, разделённую запятыми. Было: one two three Должно стать: one, two, three Давайте пробовать.…
Тут я рассказывала, как из колонки сделать одну строку, разделённую запятыми. Сейчас мне понадобилось каждую строчку оригинальной колонки ещё и обернуть кавычками, а потом собрать из этого одну строку, разделённую запятыми. Делать это вот так (следите за кавычками и их экранированием, они такие не просто так, другие варианты не работают):
Единственный минус. Отсутствует кавычка в начале и в конце строки -- их надо доставить руками. Но это проще, чем оборачивать кавычками и расставлять запятые у пары десятков элементов (:
xsel | python -c "import sys; print('\', \''.join(r.strip() for r in sys.stdin))"Единственный минус. Отсутствует кавычка в начале и в конце строки -- их надо доставить руками. Но это проще, чем оборачивать кавычками и расставлять запятые у пары десятков элементов (:
PDF → PNG
Сконвертировать PDF файл в PNG быстро и одной командой без загрузки файлов куда-то в интернет:
Обратите внимание, что имя выходного файла нужно указать без расширения. Если будет указано name.png, то в финале именем файла окажется name.png-1.png
Это ок, если в PDF несколько страниц. Будет создано соответствующее количество PNG с именами вида: name-1.png, name-2.png и т.д.
Если вам нужно сконвертировать несколько PDF из одной директории, то, чтобы сделать это сразу одной командой, а не по одному файлу, можно воспользоваться xargs:
По итогам этой команды у вас будут PNG со страшненькими именами вида file.pdf-1.png, но обычно это не проблема + есть точная привязка "из какого PDF этот PNG" + пачка PDF сконвертирована быстро и без лишних усилий.
PNG → PDF
Обратный процесс. Собрать из нескольких PNG файлов, лежащих в одной директории, один PDF:
Сконвертировать PDF файл в PNG быстро и одной командой без загрузки файлов куда-то в интернет:
pdftoppm -png /tmp/health/file.pdf /tmp/health/new-file-name-without-extension
Обратите внимание, что имя выходного файла нужно указать без расширения. Если будет указано name.png, то в финале именем файла окажется name.png-1.png
Это ок, если в PDF несколько страниц. Будет создано соответствующее количество PNG с именами вида: name-1.png, name-2.png и т.д.
Если вам нужно сконвертировать несколько PDF из одной директории, то, чтобы сделать это сразу одной командой, а не по одному файлу, можно воспользоваться xargs:
ls /tmp/health/ | xargs -n1 --replace=^ echo "pdftoppm -png /tmp/health/^ /tmp/health/^" | bash
По итогам этой команды у вас будут PNG со страшненькими именами вида file.pdf-1.png, но обычно это не проблема + есть точная привязка "из какого PDF этот PNG" + пачка PDF сконвертирована быстро и без лишних усилий.
PNG → PDF
Обратный процесс. Собрать из нескольких PNG файлов, лежащих в одной директории, один PDF:
gm convert /tmp/pictures/*.png /tmp/result.pdf
У вас заканчивается место на сервере и непонятно, кто его отжал. Вы посмотрели логи и очевидные места, куда пишут приложения, но везде всё законно и умеренно. Где мои 20 ГБ места? На помощь приходит это:
Не выйдет за пределы файловой системы (
du -xmt 100M / | sort -n
Не выйдет за пределы файловой системы (
-x ), просмотрит от корня ( / ), покажет все файлы больше 100 МБ ( -t 100M ), покажет размер в МБ ( -m ), отстортирует в порядке возрастания ( | sort -n ).
