1 июня 2012 в 02:31

Yet another cool story about bash prompt

Я программист. По крайней мере так написано в трудовой книжке. Почти всё своё рабочее время я провожу в консоли и текстовом редакторе. Мне очень нравится bash. Почти год я жил в zsh, прислушавшись к советам своих многочисленных коллег и знакомых, но в итоге я вернулся в bash и ни капельки об этом не жалею.



Zsh красив, приятен, чертовски функционален, но, признаюсь честно, я не смог совладать со всеми его многочисленными настройками. Я хочу работать, а не бороться со своим рабочим окружением. Простой пример: пару раз из-за автодополнения zsh я удалял все директории и файлы в текущей директории — zsh просто ставил пробел между автодополненной директорией и введённой мною звёзочкой (я хотел удалить всё в выбранной папке). Помните тот эпичный баг с пробелом и удалении директории /usr? У меня было то же самое. Спасибо гиту, выручил в который раз.

Впрочем, дело не в zsh — будь я чуточку умнее, я бы с ним обязательно справился бы, и всё было бы хорошо, но мы, суровые программисты, будем использовать bash и vim, а гламурные zsh и textmate оставим хипстерам и прочим модникам ;)

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

Если вдруг что-то из написанного мною можно решить проще, или в баше уже есть описанный функционал — напишите в комментариях. Ну и на всякий случай, моя где я живу:
GNU bash, version 4.2.28(2)-release (i386-apple-darwin11.3.0)



Добавляем перевод строки перед приглашением



Итак, первое, с чем я сталкиваюсь каждый день и что мне не нравится в баше — команды, которые не завершают свой вывод переводом строки при завершении. Вот простой пример (эмуляция подобного поведения):

Конечно, ничего страшного не произошло, но тот же zsh корректно обрабатывает эту ситуацию, научим же и баш такому трюку.

Для этого нам нужно при каждом выводе приглашения командной строки (PS1) смотреть на позицию курсора, и если курсор находится не на первом символе в строке — выводить перевод строки (символ "\n"). Позицию курсора можно определить с помощью escape-последоваельности:
echo -en "\033[6n" && read -sdR CURPOS

В результате в переменной CURPOS будет находиться что-то вроде этого: "^[[4;12R", где 4 — номер строки, а 12 — номер символа в строке. Добавляем соответствующий код в наш конфиг баша (~/.bashrc или ~/.bash_profile):
# setup color variables
color_is_on=
color_red=
color_green=
color_yellow=
color_blue=
color_white=
color_gray=
color_bg_red=
color_off=
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
	color_is_on=true
	color_red="\[$(/usr/bin/tput setaf 1)\]"
	color_green="\[$(/usr/bin/tput setaf 2)\]"
	color_yellow="\[$(/usr/bin/tput setaf 3)\]"
	color_blue="\[$(/usr/bin/tput setaf 6)\]"
	color_white="\[$(/usr/bin/tput setaf 7)\]"
	color_gray="\[$(/usr/bin/tput setaf 8)\]"
	color_off="\[$(/usr/bin/tput sgr0)\]"
	color_error="$(/usr/bin/tput setab 1)$(/usr/bin/tput setaf 7)"
	color_error_off="$(/usr/bin/tput sgr0)"
fi

function prompt_command {
	# get cursor position and add new line if we're not in first column
	exec < /dev/tty
	local OLDSTTY=$(stty -g)
	stty raw -echo min 0
	echo -en "\033[6n" > /dev/tty && read -sdR CURPOS
	stty $OLDSTTY
	[[ ${CURPOS##*;} -gt 1 ]] && echo "${color_error}↵${color_error_off}"
}
PROMPT_COMMAND=prompt_command

СМ. Update
PROMPT_COMMAND — это функция, которая вызывается при каждой отрисовке приглашения командной строки. Здесь был использован небольшой хак, подсмотренный мною в комментарии на stackoverflow, без этого хака значение переменной $CURPOS в некоторых случаях выводилось на экран. На кучу цветов не обращайте внимание — ниже они все нам пригодятся. Результат работы нашего конфига:

Красный фон был добавлен специально, чтобы отличать этот символ от того, что может вывести команда. И, да, на дворе 21 век, поэтому мы используем utf-ную локаль. В случае с устаревшими локалями. символ "↵", скорее всего, придётся заменить на что-нибудь попроще, например, символ "%", как в zsh.

Выводим состояние git-репозитория



При работе с гитом из консоли (только не нужно говорить про гуй — мы же суровые разработчики старой закалки!) удобно видеть текущую ветку гита и общее состояние репозитория — есть ли изменённые файлы, или всё закоммичено. Уже на этом этапе я пришёл к выводу, что мне будет удобней работать с приглашением командной строки, состоящим из двух строк — в первой строке выводится информация о текущем окружении (пользователь, сервер, рабочая директория, информация о репозитории и вообще всё, что мы пожелаем), а во второй строке — непосредственно команда, которую мы вводим. Первое время было непривычно, сейчас же я не готов возвращаться к прежней схеме.Для того, чтобы добавить информацию о гите, мы можем воспользоваться специально обученной функцией "__git_ps1", которая появляется вместе с bash-completion для гита:

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

Собственно, функция, вычитывающая состояние гита выглядит следующим образом:
# get git status
function parse_git_status {
	# clear git variables
	GIT_BRANCH=
	GIT_DIRTY=

	# exit if no git found in system
	local GIT_BIN=$(which git 2>/dev/null)
	[[ -z $GIT_BIN ]] && return

	# check we are in git repo
	local CUR_DIR=$PWD
	while [ ! -d ${CUR_DIR}/.git ] && [ ! $CUR_DIR = "/" ]; do CUR_DIR=${CUR_DIR%/*}; done
	[[ ! -d ${CUR_DIR}/.git ]] && return

	# 'git repo for dotfiles' fix: show git status only in home dir and other git repos
	[[ $CUR_DIR == $HOME ]] && [[ $PWD != $HOME ]] && return

	# get git branch
	GIT_BRANCH=$($GIT_BIN symbolic-ref HEAD 2>/dev/null)
	[[ -z $GIT_BRANCH ]] && return
	GIT_BRANCH=${GIT_BRANCH#refs/heads/}

	# get git status
	local GIT_STATUS=$($GIT_BIN status --porcelain 2>/dev/null)
	[[ -n $GIT_STATUS ]] && GIT_DIRTY=true
}

Раньше я ещё парсил и отдельно выводил изменённые (modified) файлы, файлы, находящиеся в индексе для коммита (cached), и файлы, не принадлежащие репозиторию (untracked), но со временем я понял, что это лишняя информация для меня. Собственно, функция простая: смотрим, что гит вообще стоит в системе, проверяем, что мы находимся в гитовом репозитории, рекурсивно обходя все директории наверх до корня файловой системы и ища папку ".git", получаем название текущей ветки и смотрим, есть ли хоть какие-нибудь незакоммиченные файлы. Добавляем вызов этой функции в нашу prompt_command и строим приглашение:
function prompt_command {
	local PS1_GIT=
	local PWDNAME=$PWD

	...

	# beautify working firectory name
	if [ $HOME == $PWD ]; then
		PWDNAME="~"
	elif [ $HOME ==  ${PWD:0:${#HOME}} ]; then
		PWDNAME="~${PWD:${#HOME}}"
	fi

	# parse git status and get git variables
	parse_git_status

	# build b/w prompt for git
	[[ ! -z $GIT_BRANCH ]] && PS1_GIT=" (git: ${GIT_BRANCH})"

	local color_user=
	if $color_is_on; then
		# set user color
		case `id -u` in
			0) color_user=$color_red ;;
			*) color_user=$color_green ;;
		esac

		# build git status for prompt
		if [ ! -z $GIT_BRANCH ]; then
			if [ -z $GIT_DIRTY ]; then
				PS1_GIT=" (git: ${color_green}${GIT_BRANCH}${color_off})"
			else
				PS1_GIT=" (git: ${color_red}${GIT_BRANCH}${color_off})"
			fi
		fi
	fi

	# set new color prompt
	PS1="${color_user}${USER}${color_off}@${color_yellow}${HOSTNAME}${color_off}:${color_white}${PWDNAME}${color_off}${PS1_GIT}\n➜ "
}

Вот как это выглядит в итоге:

Пара слов про переменную PWDNAME. Да, я знаю, что можно написать "\w" и всё будет так же, но у меня в функции «prompt_command» ещё и выставляется заголовок терминала:
# set title
echo -ne "\033]0;${USER}@${HOSTNAME}:${PWDNAME}"; echo -ne "\007"

а вот там уже "\w" не работает.

Показываем название виртуального окружения python



Последнее время основным языком, на котором я пишу, является Python. Для него есть очень удобная штука под названием virtualenv. Не буду вдаваться в подробности — это тема отдельной статьи, но видеть текущее виртуальное окружение в консоли баша тоже крайне удобно.

На самом деле скрипт virtualenv добавляет название текущего venv в приглаашение баша, но уж очень некрасиво это выглядит:

Запрещаем vertualenv'у вмешиваться в наше приглашение командной строки:
export VIRTUAL_ENV_DISABLE_PROMPT=1

И выводим название venv сами, так, как нам нравится:
function prompt_command {
	local PS1_VENV=

	...

	[[ ! -z $VIRTUAL_ENV ]] && PS1_VENV=" (venv: ${VIRTUAL_ENV#$WORKON_HOME})"

	if $color_is_on; then
		...

		# build python venv status for prompt
		[[ ! -z $VIRTUAL_ENV ]] && PS1_VENV=" (venv: ${color_blue}${VIRTUAL_ENV#$WORKON_HOME}${color_off})"
	fi

	# set new color prompt
	PS1="${color_user}${USER}${color_off}@${color_yellow}${HOSTNAME}${color_off}:${color_white}\w${color_off}${PS1_GIT}${PS1_VENV}\n➜ "
}


Собственно, тут всё достаточно банально.

Отделяем визуально команды друг от друга



При достаточно активной работе с терминалом вводимые команды и результат их выполнения сливаются друг с другом, особенно когда в выводе присутствует цвет:

Конечно, пример не совсем показателен, но все, кто работал в консоли, понимают, о чём я.

Я решил выводить горизонтальную черту в каждом приглашении на всю ширину терминала и эта идея себя оправдала — пользоваться консолью стало гораздо удобней:

Вот как я это делаю:
function prompt_command {
	...

	# build b/w prompt for git and vertial env
	[[ ! -z $GIT_BRANCH ]] && PS1_GIT=" (git: ${GIT_BRANCH})"
	[[ ! -z $VIRTUAL_ENV ]] && PS1_VENV=" (venv: ${VIRTUAL_ENV#$WORKON_HOME})"

	# calculate fillsize
	local fillsize=$(($COLUMNS-$(printf "${USER}@${HOSTNAME}:${PWDNAME}${PS1_GIT}${PS1_VENV} " | wc -c | tr -d " ")))

	local FILL=$color_gray
	while [ $fillsize -gt 0 ]; do FILL="${FILL}─"; fillsize=$(($fillsize-1)); done
	FILL="${FILL}${color_off}"

	...

	# set new color prompt
	PS1="${color_user}${USER}${color_off}@${color_yellow}${HOSTNAME}${color_off}:${color_white}${PWDNAME}${color_off}${PS1_GIT}${PS1_VENV} ${FILL}\n➜ "
}

Сначала находим длину строки приглашения без цвета, вычитаем её из ширины терминала (переменная $COLUMNS) и делаем строку такой длины, состоящую из символа ASCII-графики "─" (опять же, если локаль не юникодная — можно использовать любой другой символ).

Больше цветов


Если терминал поддерживает 256 цветов, не обязательно ограничиваться стандартными:
color_pink="\[$(/usr/bin/tput setaf 99)\]"


Но для совместимости с устаревшими терминалами лучше этого избегать.

Послесловие или «Cool story, bro!»



Рабочее окружение должно быть удобным, неважно, чем ты пользуешься. Bash может быть уютненьким — нужно только постараться. Кстати, ещё одной причиной, почему я не использую zsh — его отсутствие на некоторых серверах, на которые я хожу и невозможность его туда поставить.

Очень хочется услышать комментарии, а так же примеры настройки вашего терминала, несмотря на то, что подобной информации в интернете — вагон, и каждый, кто открывает для себя различные шеллы в первую (ну ладно, во вторую) очередь лезет настраивать себе приглашение командной строки. И, да, ШГ, знаю ;) Терминуса под мак нормального я так и не нашёл =( Печаль..

Для экономии места я не привожу весь свой конфиг целиком — желащие могут посмотреть на него тут: github.com/dreadatour/dotfiles (файл .bash_profile).

UPD: Вот этот код:
function prompt_command {
	# get cursor position and add new line if we're not in first column
	exec < /dev/tty
	local OLDSTTY=$(stty -g)
	stty raw -echo min 0
	echo -en "\033[6n" > /dev/tty && read -sdR CURPOS
	stty $OLDSTTY
	[[ ${CURPOS##*;} -gt 1 ]] && echo "${color_error}↵${color_error_off}"
}
PROMPT_COMMAND=prompt_command

ломает ssh. Вернулся на старый вариант, без хака, ищу, в чём проблема:
function prompt_command {
	# get cursor position and add new line if we're not in first column
	echo -en "\033[6n" && read -sdR CURPOS
	[[ ${CURPOS##*;} -gt 1 ]] && echo "${color_error}↵${color_error_off}"
}
PROMPT_COMMAND=prompt_command


UPD2: Спасибо всем за замечания, — исправил баги, улучшил код. Теперь функция prompt_command выглядит так:
Скрытый текст
# setup color variables
color_is_on=
color_red=
color_green=
color_yellow=
color_blue=
color_white=
color_gray=
color_bg_red=
color_off=
color_user=
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
	color_is_on=true
	color_red="\[$(/usr/bin/tput setaf 1)\]"
	color_green="\[$(/usr/bin/tput setaf 2)\]"
	color_yellow="\[$(/usr/bin/tput setaf 3)\]"
	color_blue="\[$(/usr/bin/tput setaf 6)\]"
	color_white="\[$(/usr/bin/tput setaf 7)\]"
	color_gray="\[$(/usr/bin/tput setaf 8)\]"
	color_off="\[$(/usr/bin/tput sgr0)\]"

	color_error="$(/usr/bin/tput setab 1)$(/usr/bin/tput setaf 7)"
	color_error_off="$(/usr/bin/tput sgr0)"

	# set user color
	case `id -u` in
		0) color_user=$color_red ;;
		*) color_user=$color_green ;;
	esac
fi

# some kind of optimization - check if git installed only on config load
PS1_GIT_BIN=$(which git 2>/dev/null)

function prompt_command {
	local PS1_GIT=
	local PS1_VENV=
	local GIT_BRANCH=
	local GIT_DIRTY=
	local PWDNAME=$PWD

	# beautify working directory name
	if [[ "${HOME}" == "${PWD}" ]]; then
		PWDNAME="~"
	elif [[ "${HOME}" == "${PWD:0:${#HOME}}" ]]; then
		PWDNAME="~${PWD:${#HOME}}"
	fi

	# parse git status and get git variables
	if [[ ! -z $PS1_GIT_BIN ]]; then
		# check we are in git repo
		local CUR_DIR=$PWD
		while [[ ! -d "${CUR_DIR}/.git" ]] && [[ ! "${CUR_DIR}" == "/" ]] && [[ ! "${CUR_DIR}" == "~" ]] && [[ ! "${CUR_DIR}" == "" ]]; do CUR_DIR=${CUR_DIR%/*}; done
		if [[ -d "${CUR_DIR}/.git" ]]; then
			# 'git repo for dotfiles' fix: show git status only in home dir and other git repos
			if [[ "${CUR_DIR}" != "${HOME}" ]] || [[ "${PWD}" == "${HOME}" ]]; then
				# get git branch
				GIT_BRANCH=$($PS1_GIT_BIN symbolic-ref HEAD 2>/dev/null)
				if [[ ! -z $GIT_BRANCH ]]; then
					GIT_BRANCH=${GIT_BRANCH#refs/heads/}

					# get git status
					local GIT_STATUS=$($PS1_GIT_BIN status --porcelain 2>/dev/null)
					[[ -n $GIT_STATUS ]] && GIT_DIRTY=1
				fi
			fi
		fi
	fi

	# build b/w prompt for git and virtual env
	[[ ! -z $GIT_BRANCH ]] && PS1_GIT=" (git: ${GIT_BRANCH})"
	[[ ! -z $VIRTUAL_ENV ]] && PS1_VENV=" (venv: ${VIRTUAL_ENV#$WORKON_HOME})"

	# calculate prompt length
	local PS1_length=$((${#USER}+${#HOSTNAME}+${#PWDNAME}+${#PS1_GIT}+${#PS1_VENV}+3))
	local FILL=

	# if length is greater, than terminal width
	if [[ $PS1_length -gt $COLUMNS ]]; then
		# strip working directory name
		PWDNAME="...${PWDNAME:$(($PS1_length-$COLUMNS+3))}"
	else
		# else calculate fillsize
		local fillsize=$(($COLUMNS-$PS1_length))
		FILL=$color_gray
		while [[ $fillsize -gt 0 ]]; do FILL="${FILL}─"; fillsize=$(($fillsize-1)); done
		FILL="${FILL}${color_off}"
	fi

	if $color_is_on; then
		# build git status for prompt
		if [[ ! -z $GIT_BRANCH ]]; then
			if [[ -z $GIT_DIRTY ]]; then
				PS1_GIT=" (git: ${color_green}${GIT_BRANCH}${color_off})"
			else
				PS1_GIT=" (git: ${color_red}${GIT_BRANCH}${color_off})"
			fi
		fi

		# build python venv status for prompt
		[[ ! -z $VIRTUAL_ENV ]] && PS1_VENV=" (venv: ${color_blue}${VIRTUAL_ENV#$WORKON_HOME}${color_off})"
	fi

	# set new color prompt
	PS1="${color_user}${USER}${color_off}@${color_yellow}${HOSTNAME}${color_off}:${color_white}${PWDNAME}${color_off}${PS1_GIT}${PS1_VENV} ${FILL}\n➜ "

	# get cursor position and add new line if we're not in first column
	# cool'n'dirty trick (http://stackoverflow.com/a/2575525/1164595)
	# XXX FIXME: this hack broke ssh =(
#	exec < /dev/tty
#	local OLDSTTY=$(stty -g)
#	stty raw -echo min 0
#	echo -en "\033[6n" > /dev/tty && read -sdR CURPOS
#	stty $OLDSTTY
	echo -en "\033[6n" && read -sdR CURPOS
	[[ ${CURPOS##*;} -gt 1 ]] && echo "${color_error}↵${color_error_off}"

	# set title
	echo -ne "\033]0;${USER}@${HOSTNAME}:${PWDNAME}"; echo -ne "\007"
}

# set prompt command (title update and color prompt)
PROMPT_COMMAND=prompt_command
# set new b/w prompt (will be overwritten in 'prompt_command' later for color prompt)
PS1='\u@\h:\w\$ '


+157
25669
507
Dreadatour 141,0

Комментарии (140)

0
inook, #
Для вывода ветки в git использую $(git branch 2>/dev/null | grep -e '\* ' | sed 's/^..\(.*\)/[\1] /')
+1
Dreadatour, #
Тоже вариант, но чуть более тяжёлый:
➜ time (GIT_BRANCH=$(git symbolic-ref HEAD 2>/dev/null); echo ${GIT_BRANCH#refs/heads/})
master

real	0m0.007s
user	0m0.001s
sys	0m0.003s

➜ time echo $(git branch 2>/dev/null | grep -e '\* ' | sed 's/^..\(.*\)/[\1] /')
[master]

real	0m0.028s
user	0m0.005s
sys	0m0.013s
+1
Aingis, #
А чем не нравится $(__git_ps1 "%s")?
+20
amarao, #
Сам юзаю баш, но хочу заметить, что фраза «разработчики старой закалки юзают git» немного себе противоречит. Разработчики старой закалки используют cvs и спорят с ньюфагами, пытающимися их пересадить на svn.
+3
DevMan, #
По контексту, «старая закалка» адресована работе с git'ом в консоли вместо GUI, a не самому git'у :)
0
Dreadatour, #
Совершенно верно =)
+12
dzhioev, #
Вы это Линусу Торвальдсу расскажите.
+4
Mezomish, #
Разработчики старой закалки — это не обязательно закостенелые старпёры, отвергающие всё новое.
+5
amarao, #
конечно, нет. Они в своей жизни успели отказаться от PL/I, кобола, выкинуть фортран, перестать восхищаться Ada'ой, забить на LISP и PROLOG, и даже чуть-чуть разучиться кодить на перле. Так что выкинуть csv и пересесть-таки на svn для них совсем не проблема.
–1
nordicdyno, #
в списке не хватет «отказались от гетеросекульной ориентации»
+11
DevMan, #
Являюсь сторонником zsh. В bash оттуда перетянул регистронезависимое дополнение по Таb:

для текущего юзера:
echo 'set completion-ignore-case on' >> ~/.inputrc

или глобально:
sudo sh -c 'echo set completion-ignore-case On >> /etc/inputrc'
+1
Dreadatour, #
Спасибо за совет, это должно быть удобно
+1
DevMan, #
Чертовски удобно: нужно перейти в директорию Downloads, набирай хоть cd dow, хоть cd DOW, хоть cd DoW etc по Tab все равно получишь cd Downloads
+3
Dreadatour, #
Мне ещё вот эта ручка нравится:
"\e[A": history-search-backward
"\e[B": history-search-forward
"\eOA": history-search-backward
"\eOB": history-search-forward

позволяет, набрав часть команды, перемещаться по хистори кнопками курсора «Up» и «Down».
0
Dreadatour, #
Это в ~/.inputrc, конечно же: github.com/dreadatour/dotfiles/blob/master/.inputrc
+1
DevMan, #
Мне без этой фичи уже как-то неуютно :)

Большой плюс zsh в наличии oh-my-zsh, где таких плюшек предостаточно (взять хотя бы автокомплит ssh-хостов — незаменимая вещь).

0
DevMan, #
Есть ли что-то подобное для bash, не в курсе?
0
hdg700, #
Если Вы про дополнение ssh-хостов, то точно есть. Сисадмин у нас всем так настроил. Сам не разбирался, как он сделал, так что подсказать затрудняюсь. :)
0
fkvf, #
Частично может помочь эта строчка в ~/.bashrc
if [ -f /etc/bash_completion ]; then
        . /etc/bash_completion
fi
0
Alex_EXEcuter, #
я тут в в статье описывал как автодополнение хостов сделать. Не сочтите за пиар.
0
DevMan, #
Не, я не про дополнение хостов спрашиваю, а про аналог oh-my-zsh для bash.
0
Dreadatour, #
Аналогов oh-my-zsh под баш я не видел, но сделать несложно.
+1
Tonik, #
Что то подобное есть github.com/revans/bash-it
Но не такое фичастое.
0
Paskal, #
В Ubuntu по-умолчанию в консоли работает автодополнение _всего_, в т.ч. и ssh хостов. Я подозреваю, что можно брать конфиги для баша Убунты как образец для подражания в любой системе.
0
TiGR, #
Т.е. это то же самое, что реверс-поиск по ctrl+R/ctrl+shift+R?
0
Dreadatour, #
Нет, не то же самое.
Поиск — это просто поиск по подстроке, а тут более удобно, но только с начала строки.
Попробуйте ;)
–1
Dyr, #
Что лишний раз, кстати, показывает, как case-sensitive filesystem в никсах стала бессмысленнейшим атавизмом. За восемь лет моей работы с никсами я ни разу не встречался с реальной необходимостью обращать внимание на регистр названий файлов. То есть понятно, что всякие «README» бросаются в глаза, но я абсолютно спокойно мог бы их и так прочитать и без применения кэпслока ;). Не говоря уж о том, что ни разу не встречал использования в одной директории одинаковых имён, отличающихся только регистром.
0
VolCh, #
Тут двояко. Имхо, наиболее удобно было бы хранить и показывать имя файла точно в том виде, что он был введён, а вот чтобы обращаться к нему можно было бы игнорируя регистр.
–2
Dyr, #
И тут сразу на ум приходит виндовая NTFS…
+1
matiouchkine, #
Лишний код, потеря скорости… Так ли это нужно?
+1
mva, #
а я, вот, написал вот такое вот: ompldr.org/vZTE0Ng
Жёлтый кружочек — наличие файлов, с изменениями, которые добавленны в очередь на коммит.
Зелёный — наличие файлов добавленных в очередь, но незакоммиченных.
Красный — наличие в директори «новых» не добавленных в индекс файлов.

P.S. на ШГ и цвета на скриншоте просьба не обращать внимания (или хотя бы не комментировать, т.к. комменты по ШГ и цветам не принимаются), ибо шрифты я настраивал под свой монитор, а цвета — под Oxygen, чтобы он не становился блестящим (а оксиджен использую — из-за отсутствия другой универсальной темы для GTK2+3+Qt4+KDE).
P.S. вот код: ompldr.org/vZTE0OQ (92-140). Буду рад, если кто-нибудь портирует на bash (а то у меня уже спрашивали)
0
mva, #
блин… ompldr не любит хотлинкинг :( Зато ссылки работают при копипасте :)
Алсо, не буду больше на ompldr выкладывать, значит :)
0
Dreadatour, #
Да, примерно про это же я и написал в статье — у меня было практически так же, но буквами (M-modified, I-index, U-untracked)
Но в итоге просто подсвечиваю ветку красным, если репозиторий «грязный» — этого оказалось достаточно.
0
Self_Perfection, #
Попробовал и на второй же день выключил.

  1. Сломался перевод курсора влево/вправо по словам с помощью Ctrl+стрелки
  2. Я названия самописных скриптов начинаю с заглавного символа, дабы отличать их от стандартных команд. И то, что Opt+tab не дополнило автоматически (есть ведь OptiJPG и optipng) стало последней каплей.
0
DevMan, #
1. Работает. Хотя я может и допиливал, уже не помню.
2. Частный случай и ожидаемое поведение.
+7
f0b0s, #
У вас очень удачный слог, пишите и побольше!
Статья интересная, горозинтальная черта — классная идея!
+1
Dreadatour, #
Спасибо, приятно! =)
+9
coocheenin, #
Высший пилотаж! Приятно видеть прикладной пост на Хабре в первый день лета.
+1
Monnoroch, #
Если длина имени папки больше ширины терминала, получается вот такое:
bash: [: слишком много аргументов
bash: [: слишком много аргументов
0
Monnoroch, #
Команды вроде работают, но каждый раз печатает эти строки.
0
Monnoroch, #
Опа, извиняюсь, похоже дело не в длине, а в пробелах в имени папки.
0
sagod, #
Согласен. Если название директории содержит пробелы — печатается сообщение «binary operator expected» на месте, где должен быть пробел.
0
Monnoroch, #
И в догонку: если имя папки кириллическое, число символов в горизонтальной черте вычисляется неверно. А точнее, похоже, что кириллические символы вообще не учитываются.
0
zvirusz, #
Заменить вот это:
local fillsize=$(($COLUMNS-$(printf "${USER}@${HOSTNAME}:${PWDNAME}${PS1_GIT}${PS1_VENV} " | wc -c | tr -d " ")))

На
PS1_without_colors=$($(printf "${USER}@${HOSTNAME}:${PWDNAME}${PS1_GIT}${PS1_VENV} ")
local fillsize=$(($COLUMNS-${#PS1_without_colors}))
0
zvirusz, #
Пардон, на это:
PS1_without_colors=$(printf "${USER}@${HOSTNAME}:${PWDNAME}${PS1_GIT}${PS1_VENV} ")
local fillsize=$(($COLUMNS-${#PS1_without_colors}))
0
Dreadatour, #
Сделал ещё проще, спасибо за замечание!
0
Dreadatour, #
Позор на мою лысую голову, сейчас посмотрю, спасибо за замечание!
0
Dreadatour, #
Поправил багу с пробелами, теперь всё хорошо.
Плюс добавил обработку очень длинных путей — текущая рабочая директория обрезается при выводе слева.
–2
PoN, #
За английский двойку Вам — программист.
+1
Dreadatour, #
okay )
0
Helius, #
image
картинка
1) Имя пользователя в prompt подсвечивается, альтеранива горизонтальной линии, чтоб видеть введенные команды
2) Локальная сессия — зеленый, если по ssh — красный, а то раньше я частенько пытался запустить консольную джабер клиент на сервере ))
3) Если в vim открыть консоль (командой :shell) то она будет вложенной по отношению к родительской, уровень вложенности показывается в [x] вначале приглашения, с каждым новым вложением цифра увеличивается, пример с вложенными шелами.
eugene:~ $ bash
[1]eugene:~ $ bash
[2]eugene:~ $ bash
[3]eugene:~ $ exit
[2]eugene:~ $ exit
[1]eugene:~ $ exit
eugene:~ $ exit

вот кусок .bashrc
if [[ "$SHLVL" -gt "1" ]]; then
  NEST_CNT="["$(($SHLVL-1))"]"
else
  NEST_CNT=""
fi


if [[ "${DISPLAY#$HOST}" != ":0.0" &&  "${DISPLAY}" != ":0" ]]; then
  PS1='\[\e[7;32m\]$NEST_CNT\[\e[0;32m\]\[\e[7;31m\]\u\[\e[0;32m\]:\[\e[0m\]\[\e[0;34m\]\w \[\e[0;31m\]$ \[\e[0m\] '
else
  PS1='\[\e[7;31m\]$NEST_CNT\[\e[0;32m\]\[\e[7;32m\]\u\[\e[0;32m\]:\[\e[0m\]\[\e[0;34m\]\w \[\e[0;32m\]$ \[\e[0m\] '
fi

0
jerom, #
Что-то мне не понравилось. root/не root я определяю по концу prompt (> или %). Если машина локальная, то левый prompt просто молча >, если удалённая, то там 4 буквы от hostname и >.

А вот умение zsh делать правый prompt — бесценно!

+1
Eddy_Em, #
Сколько человек, столько и мнений.
Лично мне больше нравится так:
PS1="\[\033[1;33;41m\h>\]\[\033[1;32;40m \D{%d.%m, %H:%M}\] \[\033[1;33;40m\w\]\[\033[1;32;40m\]\[\033[0;37;40m\n"

а у рута:
PS1="\[\033[1;31;40m[ROOT SESSION \D{%d.%m %H:%M}\] \[\033[1;33;40m\w\]\[\033[1;32;40m]\]\[\033[0;37;40m\n"

Не перепутаешь, в какой консоли ты сидишь локально, в какой — по ssh на другой машине, а в какой — запустил рутовский сеанс.
0
naryl, #
> мы, суровые программисты, будем использовать bash и vim, а гламурные zsh и textmate оставим хипстерам и прочим модникам ;)

Проприетарщина и MacOS X уже не модны. Хипстеры здесь с вами солидарны и тоже выбирают vim, только по-другоу настроенный. Хотя один знакомый хипстер пишет в SublimeText.
+1
DevMan, #
Проприетарщина и MacOS X уже не модны.
Вы не поверите сколько пользователей OSX «сидят» на консоле и vim.
+1
Envek, #
Спасибо, довольно удобная конфигурация.

Однако, я хочу добавить иногда очень нужную возможность отображать время запуска команды (не отображения приглашения), нашёл в интернете следующий фрагмент кода

bind -x '"\C-m":printf "%$(expr $COLUMNS - 1)s\r" $(date +%T)'
bind '"\C-m":"\C-xt\C-j"'

В простом терминале работает отлично:
image

Но при создании приглашения по типу вашего приглашение отображается дважды
image

Я думаю, что в этом виновата привязка клавиши Control+m к клавише Control+x, но как поправить — не знаю.
0
ostanin, #
Особенно важно выводить год и месяц запуска команды =)
0
DevMan, #
Может и не важно, но определенно удобно когда работаешь с десятком консолей.
0
asm0dey, #
Юзаю zsh вместе с конфигом oh-my-zsh, там есть почти все кроме 1го (а может и оно, но мне неинтересно и я не знаю). Сам я пользую тему wedisagree.
0
Dreadatour, #
Первое я как раз из zsh утащил (поведение).
Oh-my-zsh слишком наворочен, как по мне, врочем, не буду спорить — просто хотел поделиться своим решением.
0
asm0dey, #
Ну, собственно, навороченность прямо пропорциональна количеству включенных плагинов и еще зависит от выбранной темы. Но зато работает оно как часы и намного интуитивнее чем баш — чего стоит одна только навигация по вариантам дополнения по Tab'у.
+1
Yur4eg, #
Еще рекомендую сделать полосатый фон у терминала, так удобнее читать вывод. А курсор яркого цвета, чтобы его позиция не терялась на больших мониторах.
0
ivoronin, #
Это можно сделать только с помощью фонового рисунка?
+1
Yur4eg, #
Для urxvt да. Вот настройки.
В .Xresources добавить строку
URxvt.backgroundPixmap: /home/user/urxvt_bgs.png;+0+0:tile
После выполнить команду xrdb -merge .Xresources

Сам файл www.habrastorage.com/?v=urxvtbgs.png он должен быть подогнан по высоте под шрифт.
Цветовая схема, которую я использую, называется solarised
0
Dreadatour, #
Отличная идея, попробую, спасибо!
0
sledopit, #
У вас глаза не болят после долго сидения в консоли? на первый взгляд, уж слишком всё сливается.
–19
Pingwin32, #
Я освоение компьютера начал с MS-DOS тучу лет назад. Но! Вы что издеваетесь? Программисты самый прогрессивный народ и при этом пользуется старомодными методами? Git это прогресс, но консоль? Я лично использую Eclipse он умеет делать всё: он у меня работал с SVN, теперь работает с Git, интегрирован с баг-трекером, это не говоря о подсветке синтаксиса и навигации по схеме документа.
Даже работая в консоли я предпочитаю использовать файл менеджер МС, что позволяет ускорить работу и исключить досадные ошибки
+10
Aingis, #
Эх, молодо-зелено.
+1
Pingwin32, #
Я на 3 года всего младше тебя, а программирую уже 14 лет. Так что не по адресу.
–1
Aingis, #
14 лет? Всего-то? Молодняк!
+2
sledopit, #
Друг, не в возрасте дело. Я, например, знаю *nix-администратора со стажем более 15 (пятнадцати!) лет (а самому ему уже 50+). Так вот, он, чтобы поправить какой-нибудь простенький скриптик на сервере, поднимает там сессию кде или гном (зависит от настроения), подключается к ней, там запускает эмулятор консоли, а в эмуляторе консоли (!!!) vi. Уточню, что действие происходит на боевых серверах весьма крупной организации.
Поэтому гнутые пальцы про over 9000 лет стажа иногда не так уж много и значат.
зы. Ничего личного. Так, мысли вслух.
ззы. Хотя, судя по топику, может он просто идёт в ногу со временем, а? (:
0
Siap, #
Ведь не все пользуются Eclipse, в силу специфики работы. Вот например я пишу под iOS, и с удовольствием использую консоль для управления Git и SVN. И Eclipse мне ни к селу, ни к городу:)
+1
DevMan, #
Так xcode дружит с git.
0
Siap, #
Умеет, но увы функционал ограничен.
0
Pingwin32, #
+4
DevMan, #
Я начинал дружить с git'ом через GUI (и отдельные программы и непосредственно в IDE). Но в итоге пришел к тому, что для меня набрать в консоли короткий алиас быстрее/удобнее, чем бродить по вложенным менюшкам.

Используйте что хотите и что вам удобно. Только зачем считать свое мнение единственным верным?
0
matiouchkine, #
для меня набрать в консоли короткий алиас быстрее/удобнее, чем бродить по вложенным менюшкам


Удобнее — это, разумеется, кому как. А вот быстрее (при одинаковом опыте/стаже) в консоли всем ста процентам. Я не одну бутылку пива выспорил на тему «работа с файлами: голая консоль с автодополнением против любого файлового менеджера». Не проиграл ни разу.
+3
HomoErectus, #
>>>Даже работая в консоли я предпочитаю использовать файл менеджер МС, что позволяет ускорить работу и исключить досадные ошибки

По моему вы делаете все, чтобы в ней не работать :))

Любопытно как вы в эклипсе сделаете что-нибудь подобное (переместить файлы по маске старшие 60 дней в папку, это довольно простая процедура ):

%%: find -type f -mtime +60 -name "*cache.php" -exec mv {} /tmp/backup/ \;

Советую поменять свое отношение к консоли, это одна из тех немногих штук, которая оправдывает потраченное на нее время, во много, много раз.
0
DevMan, #
Любопытно как вы в эклипсе сделаете что-нибудь подобное
Скорее всего имелась в виду работа с гитом в эклипсе вместо косоли, а не замена консоли эклипсом.
0
HomoErectus, #
>> Git это прогресс, но консоль?
Я это имел ввиду
0
Dreadatour, #
Ну ладно, копирует человек пару файликов в день, редактирует в уютненьком эклипсе и потом просто коммитит и пушит — может, ему это и правда удобнее у гуях?
Или, может, он под Windows работает?
Хотя тот же Microsoft не зря же в итоге пришли к тому, что консоль нужна (вспоминаем PowerShell).

В общем, у каждого свои приёмы, зачем убеждать друг друга, что твои приёмы самые лучшие и единственно верные?
–4
Pingwin32, #
Я имел в виду весь процесс разработки, скорее всего это не одноразовая процедура и я её выполню на языке текущего проекта(PHP, Python, C++) с бантиками вроде сбора статистики запусков.
Но при этом я не отрицаю консоль, тем более в такой степени как меня заминусавали с GUI. Консоль это основа. Но как программист может разрабатывать новое, сам закопавшись в старьё и отрицающий прогресс?
Может вернёмся во времена до персоналок, телеграфной связи, музыку будем слушать только с пластинок, а видео с аналоговой плёнки без звука?

Я не призываю отказаться от старого, я призываю открыться новому!

Можете минусовать меня сколько угодно, но это как кидать камни в могилу Стива Джобса, который более 36 лет пытался поставить общество на путь прогресса, пытался и отчасти создал новый мир. И кто привел общество, в 1984 году, к созданию серийных ПК с графическим интерфейсом.
+3
HomoErectus, #
OMG не сравнивайте себя с Джобсом.
Жизнь состоит не только из процесса разработки а консоль это универсальная штука, ее можно сравнить с ящиком инструментов, практически на все случаи жизни. Спор не имеет смысла, т.к. вы не умеете использовать консоль, иначе этот разговор бы не состоялся. Мне даже сложно представить сколько мучений вы доставляете себе, не зная, что умеет консоль. Конечно же я сейчас говорю о unix-like консоли, а не имею ввиду консоль Windows. Я не хочу вас обидеть, но вы говорите и рассуждаете о вещах в которых ничего не смыслите.
+2
DevMan, #
Я влюблен в продукцию компании Apple: из всего их ассортимента, у меня на данный момент нет только Apple TV (я не смотрю телевидение вообще) и MacBook (жду пока прояснится ситуация с дисплеями). У меня есть и Mac Mini, и 2 iPod'a, и 2 iPhone, iPad и iMac. И это только у меня одного, не учитывая то, что еще есть в семье.

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

PS Думаю, что если бы Apple возводило GUI как абсолютную истину, они бы из OSX выпилили любую возможность использовать консоль. Наоборот же, родной Terminal.app стоновится от версии к версии только лучше.
0
VolCh, #
Если это требуется сделать только один раз, то я быстрее в наутилусе найду эти файлы и перенесу, чем буду читать man find и man bash. Если требуется её регулярно проводить, то быстрее напишу скрипт на Python или даже PHP. Какие-то особые условия для меня нужны чтобы ради этой задачи разбираться с *sh. Десяток команд помню наизусть, при случае могу перенаправить ввод/вывод или конвеер сделать, в общем как в MS-DOS почти. Будет задача написать shell script — напишу, но не быстро и может топорно (а для модификации опять маны нужно будет читать), но пока для задач, которые можно было бы (наверное) реализовать на *sh, у меня есть возможность выбирать «платформу». Может не так лаконично как на *sh получится, но помнить 82 (навскидку) ключа find выше моих возможностей.
+3
HomoErectus, #
Просто нет слов. В голову лезут мысли про кактусы и ежиков. В общем делайте как вы считаете для себя удобным, по своему опыту могу сказать, что программист все-таки знающий и умеющий пользоваться консолью, т.е. awk, sed, find, grep, xargs… etc… гораздо эффективнее в области веб разработки ( и не только ), что причина, что следствие однозначно не скажешь, но в моей жизни это так. У меня на последней работе, вообще не брали людей не умеющих пользоваться консолью, редко в качестве исключения, только на условии быстрого ее освоения.
+1
VolCh, #
А причём тут ёжики и кактусы? :) У меня скорее программирование на sh вызывает такие мысли. Схожие чувства вызывает Perl. Но я не вижу необходимости знать и помнить ~80 ключей find и ~50 ключей grep. Я знаю, что эти команды делают и при необходимости эффективной обработки большого количества файлов я их буду использовать. Но пока критерий эффективности не важен (будет скрипт работать 10 секунд или 100 обычно всё равно) я предпочту более удобные средства, тот же Python, потому что он позволит решить задачу быстрее. И в отличии от Pingwin32 я консолью не пренебрегаю (обычная ситуация — открыта IDE или редактор, браузер и 3-4 окна терминала, тот же git вызываю из консоли, а не IDE, хотя интеграция есть), а просто не пользуюсь всей её мощью, находя её избыточной для своих задач. Хотя было время, пришлось месяц разрабатывать в чистой консоли без иксов и те же find и grep использовал более активно и кое-какие ключи помнил наизусть. Пропала необходимость — перестал и ключ забылись. Я просто не нахожу этот инструмент удобным, слишком многое надо в голове держать, особенно учитывая отсутствие удобных средств разработки и отладки. Была бы система именования ключей немного другая — он бы был более удобный, но пока я должен вспоминать вводить -atime или -mtime, а не набрать -time и выбрать -timea или -timem, я его удобным не считаю — я сначала думаю о том, что мне нужно время, а потом о том время чего мне нужно, время последнего доступа и время последней модификации для меня не две разных сущности, а подсущности одной «время».
+2
HomoErectus, #
В целом я понял вашу точку зрения. Конечно же помнить все ключи не обязательно, в консоли отлично работает man и работает поиск google. Ну еще, что я понял, вы так никогда и не умели ей пользоваться.

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

Но тогда один из тех моих коллег (сейчас друг), все-таки поверил мне и решился попробовать (он уже умел печатать слепым методом в русской раскладке ). На обучение до приемлемого результата у него ушло 2-3 нед. Стоит ли говорить, что он больше не спорил со мной, а лишь на 100% подтверждал правильность такой необходимости. Оставшийся коллега, так и не стал учиться, но спорить против двоих уже перестал.

И эта ситуация мне очень напоминает ту, но отличается тем, что сейчас я все-таки спорить уже с вами не буду. Мы не разговариваем лично, поэтому я не могу продемонстрировать всю несостоятельность утверждений по поводу того что «Python, потому что он позволит решить задачу быстрее», и показать вам класс частых задач в которых это будет мягко сказать «не верно».
Для меня не имеет никакого значения согласитесь вы со мной или нет. Пусть каждый останется при своем.
–2
VolCh, #
Быстрее будет на python лишь потому что не нужно лезть в man или google, чтобы посмотреть как каждый ключ пишется. Проверял не раз ради интереса. То есть спорить не о чем, действительно. У вас одна практика, у меня другая.

К слепой печати у меня, кстати, примерно такое же отношение. Вслепую я печатаю на английском 60-80, глядя на клавиатуру (даже если на клавишах надписей нет) 200-220. Очень много времени уходит на вспоминание где какая клавиша.
+2
HomoErectus, #
Аргумент с python не засчитан, т.к. для python я думаю вы тоже не знаете многих вещей, например вам нужно скачать файл из сети, что будете делать, все помните? Я питон знаю и мне есть с чем сравнить, у них с консолью задачи отличные и я бы сказал дополняющие друг друга. Например писать парсер сайтов я конечно же буду на python, а вот копировать с удаленного сервера файлы c их последующим переименованием буду делать в bash.
Мне интересно, вот в профиле у вас написано, мол «веб разработка, а как вы правите конфиги, кодировку файлов, делаете dump базы данных, заливаете базу данных, mysql > select * from anytable; grep -l „http://“ --include „*.php“ *;
Как??? WinSCP, FileZilla, Notepad++, phpMyAdmin ????????? (сами по себе инструменты хорошие), но не для быстрой инспекции и изменений.
На этом я разговор заканчиваю, т.к. не вижу для себя смысла в его продолжении.

А по поводу слепой печати, вот видите ваш случай, есть над чем подумать. Конечно же я имею ввиду полноценный 10-ти пальцевый метод. Очень много уходит времени, именно потому, что руки не обучены. Музыкант когда играет на инструменте особо не вспоминает где-какая нота находится, все происходит на автомате и на инструмент смотреть совсем не обязательно.
0
VolCh, #
Всё, конечно, не помню, но IDE подскажет многое, минимизируя необходимость обращения к манам, да и сами маны субъективно удобнее.

Редактирую в vim/nano. Постоянно использую ssh/scp, mysqldump и прочие специализированные утилиты, но регулярные задачи с логикой сложнее cat access.log | grep core решаю на python. Во-первых, у меня это получается быстрее. Во-вторых, считаю python более читаемым языком, а т. к. уже довольно давно избегаю подхода «краткость — сестра таланта» при разработке, то подход «write only», с которым у меня ассоциируются sh, в большинстве случаев для меня недопустим.
0
Dreadatour, #
cat access.log | grep core

лучше делать так:
grep core access.log

а в данном конкретном случае — лучше так:
fgrep core access.log
0
matiouchkine, #
Если логи завернуты в logrotate, то ваше «лучше» приобретает чисто академический оттенок ;-)

Пока речь не идет о системах реального времени, ежесекундному запуску по крону и прочей экзотике — хоть на VBA пиши, какая разница-то?

Кстати:
--- ~ » time sudo grep core /var/log/anaconda.log
sudo grep core /var/log/anaconda.log  0,00s user 0,01s system 12% cpu 0,138 total
--- ~ » time sudo cat /var/log/anaconda.log | grep core 
sudo cat /var/log/anaconda.log  0,00s user 0,01s system 17% cpu 0,087 total
--- ~ » time sudo grep core < /var/log/anaconda.log
sudo grep core < /var/log/anaconda.log  0,00s user 0,00s system 35% cpu 0,003 total


Так что все не столь очевидно (zsh/rhel6).
0
Dreadatour, #
Если человек пишет cat | grep, значит, он не знает, как работает та или иная команда. В одном случае лучше одно, в другом случае лучше другое, и это хорошо, если ты делаешь что-то осознанно. Если бездумно повторять одну и ту же команду, подсмотренную в гугле во всех случаях, — стоит ли говорить о том, что ты хорошо знаешь консоль?
0
Dreadatour, #
➜ time grep volume system.log 1>/dev/null
real	0m0.025s
user	0m0.020s
sys	0m0.006s

➜ time cat system.log | grep volume 1>/dev/null
real	0m0.055s
user	0m0.023s
sys	0m0.019s

Даже на не самом большом файле разница очевидна.
+18
1ex, #
image
0
1101_debian, #
Спасибо за статью, очень понравилась идея вводить команды с новой строки, нужно попробовать, а то у самого на 13" мониторе половина экрана занята инфой об окружении (тоже что и у вас, только вместо python'а ruby).

Для вывода текущей версии ruby и текущего gemset'а написал такую функцию.
rvm_gemset(){
  ruby_version=$(ruby -v | awk ' { print $2 } ')
  ree=$(ruby -v | grep -q Enterprise && echo "ree")
  if [ $ree ]
  then 
    ruby_version="ree"
  fi
  gemset=$(rvm current | awk -F@ ' { print $2 } ')
  if [ $gemset ]
  then
    echo "$ruby_version@$gemset "
  else
    echo "$ruby_version "
  fi
}
0
Stdit, #
В приглашении люблю видеть время на сервере ("\D{%Y-%m-%d %H:%M:%S}") и полный путь к текущей директории ("\w"), а команду люблю вводить с новой строки ("\n$ "). Раскрашиваю приглашения разных серверов в разный цвет ("\e[32m"… "\e[0m"), чтобы случайно в запарке не выполнить команду не там, где надо.
+2
r13, #
Суровые программисты, как раз, пользуют zsh, vim и бабочек.
0
danilchenko, #
Клевая штука! Как-то в голову не приходило перевести строку в приглашении, а длинное приглашение было неудобно…

И еще для любителей Mercurial:
аналогично git функция
# get hg status
function parse_hg_status {
# clear hg variables
HG_BRANCH=
HG_DIRTY=

# exit if no hg found in system
local HG_BIN=$(which hg 2>/dev/null)
[[ -z $HG_BIN ]] && return

# check we are in hg repo
local CUR_DIR=$PWD
while [ ! -d ${CUR_DIR}/.hg ] && [ ! $CUR_DIR = "/" ]; do CUR_DIR=${CUR_DIR%/*}; done
[[ ! -d ${CUR_DIR}/.hg ]] && return

# 'hg repo for dotfiles' fix: show git status only in home dir and other hg repos
[[ $CUR_DIR == $HOME ]] && [[ $PWD != $HOME ]] && return

# get hg branch
HG_BRANCH=$($HG_BIN branch 2>/dev/null)
[[ -z $HG_BRANCH ]] && return
#HG_BRANCH=${HG_BRANCH#refs/heads/}

# get hg status
local HG_STATUS=$($HG_BIN status 2>/dev/null)
[[ -n $HG_STATUS ]] && HG_DIRTY=1
}

Во всех местах, где есть git можно добавить и hg по аналогии
0
danilchenko, #
Аргх, подсветку забыл
+1
danilchenko, #
# get hg status
function parse_hg_status {
# clear hg variables
HG_BRANCH=
HG_DIRTY=

# exit if no hg found in system
local HG_BIN=$(which hg 2>/dev/null)
[[ -z $HG_BIN ]] && return

# check we are in hg repo
local CUR_DIR=$PWD
while [ ! -d ${CUR_DIR}/.hg ] && [ ! $CUR_DIR = "/" ]; do CUR_DIR=${CUR_DIR%/*}; done
[[ ! -d ${CUR_DIR}/.hg ]] && return

# 'hg repo for dotfiles' fix: show git status only in home dir and other hg repos
[[ $CUR_DIR == $HOME ]] && [[ $PWD != $HOME ]] && return

# get hg branch
HG_BRANCH=$($HG_BIN branch 2>/dev/null)
[[ -z $HG_BRANCH ]] && return
#HG_BRANCH=${HG_BRANCH#refs/heads/}

# get hg status
local HG_STATUS=$($HG_BIN status 2>/dev/null)
[[ -n $HG_STATUS ]] && HG_DIRTY=1
}
0
Dreadatour, #
О, спасибо, добавлю себе (редко-редко приходится Mercurial кудрить)
+3
powerman, #
hg status тормозит. гитовский __git_ps1 работает значительно по-шустрее, но всё-равно не так быстро как хотелось бы. А тормоза при выводе каждого приглашения bash — т.е. при выполнении каждой команды — создают раздражающее ощущение тормозящей системы.

Поэтому лично я использую утилитку vcprompt, которая во-первых написана на C для скорости, и во-вторых единообразно поддерживает кучу VCS. Я к ней дописал быструю поддержку определения modified/unknown для hg (жду пока автор мой pull request примет). Главное, не перепутать её с vcprompt :) написанной по мотивам предыдущей и совместимой с ней, но реализованной на питоне, т.е. медленной.
0
obiwanus, #
Поставил ваш форк, он стал показывать + где не надо (то есть пишет, будто у меня локальные изменения есть, а изменений на самом деле нет). Поставил из оригинального репозитория — плюс пропал.

Может, проверите еще раз свои коммиты? Версия hg 2.0.2
0
powerman, #
Оригинальная плюс не показывает потому, что она не поддерживает определение modified/unknown для hg — автор не разобрался, как определять это быстро, а вызывать медленный hg st для этой утилитки смысла нет. У моей утилитки действительно бывают ложные срабатывания, это даже документировано. :)
# In some cases false detection of modified files will happens (when
# .hg/dirstate cache outdated), this can be fixed by updating cache (for
# ex. by running real `hg st`).

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

Добавлять к ней эту функциональность чтобы исключить ложные срабатывания я пока не планирую. Во-первых нет времени. Во-вторых не факт, что удастся с этой функциональностью сохранить текущую скорость работы. В-третьих эти ложные срабатывания сообщают о несуществующих изменениях, а не забывают сообщить о существующих изменениях (что было бы значительно хуже) — и ложные срабатывания проходят после запуска `hg st`. На мой взгляд это вполне терпимая цена за скорость.

Впрочем, если кто-то хочет пофиксить этот баг — я только за. Форкайте — и вперёд. :)
0
obiwanus, #
У меня hg st не убирали плюс, я б тогда успокоился наверно и не стал писать коммент.
0
powerman, #
У меня hg 2.1.1. Может в 2.0.2 hg st не обновляет .hg/dirstate? Если есть возможность, попробуйте обновить hg. Если нет, я попробую поставить 2.0 и проверить. Но, в любом случае для этого есть issue tracker, на хабре оно потеряется.
+2
1ex, #
Отлично, у самого где то в чулане валяется древний скрипт, идея подсветки имени бранча цветом очень вкусная.

Только я не совсем понял зачем вам использовать tput, по-моему через прмой вывод проще.

Я это решил следующей функцией

escape()
{
    echo "\[\033[$1\]"
}


и потом блок цветов выглядит так

# Attributes
c_reset=`escape 0m`     ;
c_bold_on=`escape 1m`   ;       c_bold_off=`escape 22m`
c_blink_on=`escape 5m`  ;       c_blink_off=`escape 25m`
c_reverse_on=`escape 7m`;       c_reverse_off=`escape 27m`
# Foreground colors 
cf_default=`escape 39m` ;
cf_black=`escape 30m`   ;       cf_red=`escape 31m`
cf_green=`escape 32m`   ;       cf_brown=`escape 33m`
cf_blue=`escape 34m`    ;       cf_magenta=`escape 35m`
cf_cyan=`escape 36m`    ;       cf_white=`escape 37m`
# Background colors
cb_default=`escape 49m`
cb_black=`escape 40m`   ;       cb_red=`escape 41m`
cb_green=`escape 42m`   ;       cb_brown=`escape 43m`
cb_blue=`escape 44m`    ;       cb_magenta=`escape 45m`
cb_cyan=`escape 46m`    ;       cb_white=`escape 47m`
+1
Dreadatour, #
Да, можно и так, идея с функцией для эскейпа мне определённо нравится.
tput тут для красоты только — всё равно всё в переменные записывается и выполняется при загрузке конфига только.
0
Arion, #
Добавлю и я свой конфиг, сам пользуюсь zsh но на серверах пихаю следующее в .bashrc

function detect_git_branch {
  local git_branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/\1/")
  [ "$git_branch" != "" ] && echo " ($git_branch) "
}

function detect_rvm_name {
  local gemset=$(echo $GEM_HOME | awk -F'/' '{print $NF}')
  [ "$gemset" != "" ] && echo "($gemset)"
}

function dev_info {
  echo "$(detect_rvm_name)$(detect_git_branch)"
}

# Colors
txtred='\e[0;31m' # Red
txtwht='\e[0;37m' # White
txtrst='\e[0m'    # Text Reset
 
# Custom command prompt
export PS1="\[\033[01;32m\]\u@\h\[\033[01;34m\] \[$txtwht\]\w \[$txtred\]\$(dev_info)\[$txtwht\]$
\[$txtrst\]"
0
Arion, #
результат: image
0
Dreadatour, #
Для сервера, пожалуй, больше и не нужно.
+2
nagato, #
Черту-разделитель можно выводить проще:

echo -en "$(yes "─" | head -n "${COLUMNS}" | tr -d '\n')\r"
0
Dreadatour, #
Шикарная идея, пожалуй, так и сделаю
0
Dreadatour, #
Попробовал — слишком долго отрабатывает echo (на моей машине задержка видна глазами)

Оставлю как есть, но идея замечательная!
0
matiouchkine, #
seq -s "—" ${COLUMNS} | sed 's/[0-9]//g'


printf -v f "%${COLUMNS}s" ; printf "%s\n" "${f// /—}"


for i in $(seq ${COLUMNS}); do echo -n "—"; done; echo
0
Dreadatour, #
Да, всё это пробовал, видимо, проблема в выводе всей строки через echo, не знаю. Разница заметна прямо на глаз.
0
Marchevsky, #
Зачем такие тени большие вокруг окна?
0
Dreadatour, #
Честно? Лень было обрезать или вырезать.
+2
teedee, #
Блог компании Mail.ru — казалось бы :)
0
Dreadatour, #
Ну да, я работаю в mail.ru и пишу про свой рабочий инструмент ;)
+1
teedee, #
В конечном счете неважно, в каком блоге статья; важно, что она уже в избранном. Спасибо
0
fmka, #
Какое все цветастое оО
0
Dreadatour, #
У меня ещё достаточно спокойно. Вот цветастое:
0
fmka, #
Это меня не очень пугает. Наверное не привык к светлым тонам просто.
0
Dreadatour, #
А я тут не могу разобрать, где команда, а где вывод. Дело привычки, наверное.
Ну, главное, чтобы комфортно было работать — и это здорово, что shell можно настроить под себя.
+1
fmka, #
Читая ветку про новый интерфейс вин8, крики в ней, на этом фоне гибкость shell — конечно здорово. Только стараюсь ничего не кастомизировать под себя особо, — фиг его знает на каком узле\объекте, с какой железкой\осью столкнешься, а у тебя уже привычка к хорошему. Почти спартанская логика — держи себя в теле, ничего лишнего и будешь радоваться простым вещам. *немного завидует*
+1
DevMan, #
На самом деле не страшно. Как писал выше, я поклонник zsh, где есть столько плюшек, что bash'у и «не снилось».
Но по работе частенько приходится попадать не в родное окружение, и ни какой трагедии (минимальный дискомфорт), так как основы одни и те же.
Но за то в родном окружении, где проводишь большую часть времени — полный кайф.
0
yukach, #
Статья хорошая, только по-моему, все это лишнее, отвлекает. Сам годами пользуюсь таким:
PS1="\e[33;1m> \e[0m"
0
Dreadatour, #
Шикарно! Мне нравится, честно.
Только нужно добавить символы «экранирования»:
 PS1="\[\e[33;1m\]> \[\e[0m\]"

Без них будут проблемы с многострочными командами.
0
yukach, #
Ну да, Вы правы. Извиняюсь, писал по памяти.
0
budda, #
Скриншоты у автора красивые — прямо произведение искусства!
0
DevMan, #
ЕМНИП, скриншоты сделанные стандартными средствами OSX.
0
Dreadatour, #
Совершенно верно, стандартная встроенная скриншотилка: Cmd+Shift+4, затем пробел, и затем выбираем мышкой любое нужное нам окно — всё остальное сделает за нас система.
+2
xaizek, #
Спасибо за вывод приглашения оболочки всегда на новой строке, как-то не догадывался, что это можно так довольно легко пофиксить.

Заметил в скрипте ручной поиск каталога .git для репозитория. Эти строки можно заменить на вызов git rev-parse --git-dir, ИМХО это несколько корректнее.
0
Dreadatour, #
Корректнее — согласен, но я на первое место поставил скорость.

git rev-parse --git-dir

Согласно dtruss (аналог strace под osx), эта команда делает лишние действия: как минимум, загружает конфиг гита (причём и глобальный и общий) и парсит их. С одной стороны, она делает это, возможно, быстрее, чем наш скрипт на баше, с другой стороны — всё-таки это лишние операции (в том числе с диском), которых хочется избежать.
0
2be, #
Вы не могли бы поделиться настройками своего маковского терминала, выложив файл /Users/$username/Library/Preferences/com.apple.Terminal.plist, или просто экспортировав конкретный профиль из настроек терминала?
+1
Dreadatour, #
Это тема Solarized

Прям мои настройки отправил по просьбе в личку (не хочу, чтобы через некоторое время в комментариях осталась протухшая ссылка).
0
xaizek, #
# XXX FIXME: this hack broke ssh =(
# exec < /dev/tty
# local OLDSTTY=$(stty -g)
# stty raw -echo min 0
# echo -en "\033[6n" > /dev/tty && read -sdR CURPOS
# stty $OLDSTTY

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

Причина проблемы: exec < /dev/tty. Дескриптор stdin не восстанавливается и это всё ломает. Решения минимум два:
  • запускать в другом процессе, в данном случае подходит sub-shell (т.е. надо поместить код с вызовом exec и далее в круглые скобки)
  • сохранить старый дескриптор и позже восстановить его (exec 100<&0; exec < /dev/tty; ...; exec 0<&100 100<&0, но тут приходится надеяться на свободный 100-й дескриптор, зато это должно быть быстрее)

В ответе на StackOverflow такой код был оформлен как отдельный скрипт, и поэтому проблем с ним не было.

Только зарегистрированные пользователи могут оставлять комментарии.
Войдите, пожалуйста.