Tmux - лучшая альтернатива screen
Довольно долго пытался пересесть с GNU Screen на tmux, но вот только сдерживало несколько вещей в нем.
На сайте проекта можно найти описание всех фич, я же опишу лишь те, которые для меня оказались самыми полезными.
- Улучшенная модель отрисовки (да-да screen периодически подключивал)
- Содержимое терминала не теряется, когда мып ользуемся редакторами (открыли vim, закрыли, история осталась а месте и можно прокрутить до нее)
- Конфигурирование на лету
C-b
Ага. Это сочетание клавишь из vim.
Переключим tmux на работу с C-a
set-option -g prefix C-a
Задержка между контрольными клавишами C-b и непосредственным вводом команды (например n).
Решается просто
set -s escape-time 0
Сочетание клавиш для управления tmux внутри tmux
Бывает такое, что приходится внутри tmux запустить вложенную сессию tmux. Значит надо как-то пробросить этой вложенной сессии команды. Например C-a a command
bind-key a send-prefix
Начинаме считать окна с 1, а не 0
Это удобно, так как 0 находится где-то на отшибе. И к нему надо тянуться.
set -g base-index 1
Агрессивный ресайз терминалов
Дефолтно при подключении к сессии нескольких клиентов рамер окна выбирается по размеру самого маленького клиента. Это неинтересно. Поэтому позволим терминалу для каждого клиента иметь собственный размер.
setw -g aggressive-resize on
После завершения команды можно ввести другую команду не нажимая C-b
Для меня эта проблема была самой неприятно. Воспроизвести ее просто: нужно на готом tmux создать два региона.
C-b "
А затем переключаться между ними.
C-b Up Down Up Down
Вы заметите, что нажав один раз C-b, а потом быстро нажимая вверх-вниз окна переключаются.
Это неприятно когда пытаешься переключить вкладку и начать тут же вводить что-то.
За такое поведение отвечает repeat-time
repeat-time time
Allow multiple commands to be entered without pressing
the prefix-key again in the specified time milliseconds
(the default is 500). Whether a key repeats may be set
when it is bound using the -r flag to bind-key. Repeat
is enabled for the default keys bound to the resize-pane
command.
Можно отключить repeat для команд переключения между панелями и окнами просто переобъявив команду без флага -r. Например так:
bind-key Up select-pane -U
Но я же предпочитаю эту фичу отключить вовсе.
set-option -g repeat-time 0
Базовый ~/.tmux.conf
# C-b не подходит - так как используется в vim
set-option -g prefix C-a
bind-key C-a last-window
# Начинам нумерацию окон с 1
set -g base-index 1
# Добавляем отзывчивости в реакции на команды
set -s escape-time 0
# Отключаем ожидание дополнительных команд
set-option -g repeat-time 0
# Или можно забиндить команды отдельно отключая repeat только для нужных
#bind-key Up select-pane -U
# Цветовая схема
set -g status-bg black
set -g status-fg white
set -g status-left ""
set -g status-right "#[fg=green]#H"
# Ресайзим содержимое окна индивидуально для каждого клиента
setw -g aggressive-resize on
# Позволим пробрасывать команды внутрь вложенной сессии tmux
# при помощи C-a a <command>
bind-key a send-prefix
# Мониторинг активности
#setw -g monitor-activity on
#set -g visual-activity on
# Пример использования команды оболочки в статусной строке
#set -g status-right "#[fg=yellow]#(uptime | cut -d ',' -f 2-)"
# Подсвечиваем активное окно
set-window-option -g window-status-current-bg green
За кадром осталось использование tmuxinator для запуска tmux c определенной заранее раскладкой окон.
Ну и еще про настройку можно почитать в Arch-wiki.
Python 3+ и окончания строк в файлах
Столкнулся одного теста, который был перенесен с python 2+ на python 3+.
Тест делал следующее:
скачивал файл через python.requests и сравнивал его с эталонным содержимым на диске (посимвольно).
Выглядело приблизительно так
import requests
import sys
response = requests.get(sys.argv[1])
if response.code == 200:
with open(sys.argv[2]) as f:
from_storage = f.read()
from_web = response.text
assert from_web == from_storage
Да. Все верно. Этот тест не проходил.
И тут была замечена одна странность: файл на диске содержал последовательность crlf, а в coдержимом from_storage этой последовательности не оказало.
А дело все в том, что в python 3+ было введено соглашение на обработку символов перевода строки. И управление работой осуществляется манипулированием параметром newline.
- On input, if newline is None, universal newlines mode is enabled. Lines in the input can end in ‘\n’, ‘\r’, or ‘\r\n’, and these are translated into ‘\n’ before being returned to the caller. If it is ‘’, universal newline mode is enabled, but line endings are returned to the caller untranslated. If it has any of the other legal values, input lines are only terminated by the given string, and the line ending is returned to the caller untranslated.
- On output, if newline is None, any ‘\n’ characters written are translated to the system default line separator, os.linesep. If newline is ‘’, no translation takes place. If newline is any of the other legal values, any ‘\n’ characters written are translated to the given string.
В итоге достаточно было указать newline=’’ как CRLF появились.
BeautifulSoup innerHTML
Функция в рамках браузеров по сей день остается недокументированной. А значит на стороне большинства парсеров она не реализована.
Но как быть если хочется?
def innerHTML(element):
return element.decode_contents(formatter="html")
window.location - не всегда оно работает как надо
Если раньше мы долго ругались на проблемы IE, то сейчас на FF и Chrome - ничего особо не поменялось.
На этот раз отличились механизмы работы с хешом в window.location.
Кейс: мы хотим перенаправить пользователя с помощью js на другую страницу.
Что может пойти не так?
Например у нас есть ссылка вида /foo. И логично предположить, что в современном мире мы хотим сохранить хеш (это нужно для angularjs например) чтобы на новой странице отработал нужный функционал.
Что мы делаем
window.location.pathname = '/foo'
Работает.
Но стоит только в ссылку нечаянно попасть хешу #, как поведение браузеров сразу резко меняется.
window.location.pathname='/foo#bar'
Chrome среагирует правильно и отправит нас по ссылке /foo%23bar, а Firefox - нет. У него будет ссылка /foo#bar.
Ниже иллюстрации.
Как видим FF с задачей не справился. И не перекодировал # автоматом.
Баг этот давний - аж 2009го года.
Поэтому внимательно следите за редиректами если у вас есть спецсимволы в урле.
Проще говоря: pathname в FF вседет себя так же, как и href (за мелким исключением вроде сохранения хеша).
Исходники на посмотреть тут.
Ajax и заголовок location: особенности
Все как обычно: в коде ajax-приложения нужно обрабатывать хидер location, который отдает сервер в некоторых случаях.
Это может потребоваться где угодно. И сейчас для нас важен результат (чтобы оно работало).
Как мы все прекрасно знаем хидеры в jquery можно получить через метод getResponseHeader объекта jqXHR (для других библиотек и голого js можно заглянуть в мануалы).
Казалось бы:
$.get('some_url', function(data, status, request) {
console.log(request.getResponseHeader('location'))
})
Но не все так просто.
Если мы отдаем хидер стандартно для php.
header('location: to_url');
То видим. А что видим? А видим, что браузер взял на себя переход по редиректу. Почему? Потому что ответы с кодом 301 и 302 прозрачно обрабатываются самим браузером и в ответе придет уже конечная страница.
Но как только ответ сервера будет 200, так сразу все становится хорошо. Этот код заставляет браузеры забыть об обработке хидера location и позволяет прочитать его на стороне js.
header('location: to_url', true, 200);
В примере можно наглядно посмотреть, как происходит обработка. Достаточно его запустить внутри встроенного сервера php.