Показаны сообщения с ярлыком shell. Показать все сообщения
Показаны сообщения с ярлыком shell. Показать все сообщения

14 января 2009 г.

Цветные diff'ы и less

Можно сделать работу с кодом в консоли более удобной, если раскрасить diff'ы. Это сделает текст более наглядным, а значит, упростит работу и увеличит производительность :)

Для этого нужно установить в системе пакет colordiff. После установки можно пользоваться командой colordiff точно так же, как и обычным diff'ом:

colordiff file1 file2

или использовать пайпы

diff file1 file2 | colordiff


Либо просто прописать алиас в .bashrc, если кого устраивает такой вариант:

alias diff=colordiff.


Тем, кто работает с CVS или SVN можно прописать в .bashrc или .profile следующие функции:

# color diffs for CVS
function cvsdiff () {
  if [ "$1" != "" ]; then
    cvs diff $@ | colordiff;
  else
    cvs diff | colordiff;
  fi
}

# color diffs for SVN
function svndiff () {
  if [ "$1" != "" ]; then
    svn diff $@ | colordiff;
  else
    svn diff | colordiff;
  fi
}


Настройка colordiff осуществляется через файл ~/.colordiffrc (настройки по умолчанию находятся в /etc/colordiffrc).

Чтобы использовать новые возможности вместе с командой less нужно заставить less понимать коды цветов. Для этого существует ключ -R:

colordiff file1 file2 | less -R


Для того, чтобы этот ключ использовался по умолчанию, нужно указать его в переменной окружения LESS:

export LESS="-R"


Вот и все.

Подсмотрено у drupal.org и rha7dotcom, а также в man colordiff.

3 июля 2008 г.

Как сменить shell

Тема не новая, но вот сегодня опять занимался решением старых проблем...

Не знаю кому как, но лично я считаю, что лучший shell — это bash. Не буду перечислять все преимущества, но одно просто неоспоримое — привык я к нему :) Я вообще люблю консоль, но bash — особенно. Он какой-то уютный, что ли.

Но вот сегодня захожу на очередной удаленный сервер по ssh и попадаю в ненавистный мне tcsh. Всё б ничего и можно потерпеть, но хочется уюта :) Но я здесь не root, и мне особо не рады. Что делать?

Исправляется довольно просто: нужно прописать смену shell в настроечном файле, который считывается при логине. Для tcsh этот файл называется .login и находиться должен в моей домашней папке. Если его нет, то нужно создать и прописать в нем следующее:

if (-f /bin/bash) then
  setenv SHELL /bin/bash
  exec /bin/bash --login
end if

Эти строки заменяют существующий shell, загружаемый по умолчанию, на bash. В данном случае по умолчанию был установлен tcsh.

В случае другого shell решение немного изменится. Например, если по умолчанию загружается sh, то никакой .login не поможет — sh просто не знает о существовании такого файла. Для sh нужно исправлять файл .profile (он тоже должен находиться в домашней папке):

if [ $SHELL = "/bin/sh" ]; then
  if [ -f /usr/bin/bash ]; then
    SHELL=/usr/bin/bash
    export SHELL
    exec /usr/bin/bash --login
  fi
fi

Маленький нюанс здесь — bash тоже считывает .profile и двойной if нужен здесь для того, чтобы исключить вероятность бесконечной рекурсии при загрузке bash.

Полный список соответствия разных shell'ов и настроечных файлов можно почитать здесь.

1 февраля 2008 г.

Как сделать merge в cleartool

В ClearCase сделать слияние (merge) для файлов с разных веток довольно просто. Проблемы начинаются, если на сливаемых ветках добавлялись или удалялись файлы. Оказывается, слияние директорий в ClearCase — задача непростая.

Выполнить слияние двух веток можно следующей командой:

cleartool findmerge . -fversion <version/label> -co -nc -merge -gmerge

Эта команда выполнит слияние с ветки version или label на текущую ветку для всех файлов, содержащих изменения. Ключ -gmerge удобно использовать при работе в графической среде — при возникновении конфликтов cleartool запустит визуальный инструмент для разрешения конфликтов вручную. Но при работе, например, по SSH, указывать этот ключ смысла не имеет; в этом случае используется консольный инструмент разрешения конфликтов. Тем, кто пользуется консольным, можно только посочувствовать.

Пример. У меня в текущей папке есть файлы file1, file2 и file3. На ветке /main/mybranch1 выполнены изменения для файлов file1 и file3. Чтобы слить изменения с ветки /main/mybranch1 на текущую ветку, мне необходимо выполнить команду:

cleartool findmerge . -fversion /main/mybranch1/LATEST -co -nc -merge -gmerge

В результате выполнения файлы file1 и file3 будут вычекнуты (checkout) на текущую ветку и модифицированы в соответствии с изменениями на ветке /main/mybranch1. Мне останется лишь выполнить checkin для измененных файлов. Файл file2 останется нетронутым.

Но вот, предположим, что на ветке /main/mybranch1 добавлен еще один файл — file4. Выполнение findmerge никак не коснется этого файла, потому что на текущей ветке этого файла нет. Значит, нужно сначала сделать merge для директории.

Описание команды merge говорит о том, что слияние директорий происходит в три шага:
  1. убедиться, что на "той" ветке нет вычекнутых элементов;
  2. вычекнуть директорию в текущей ветке;
  3. выполнить команду cleartool merge для директории.

Только после этого можно будет безболезненно выполнить cleartool findmerge.

Итак, синхронизируем текщую директорию с веткой /main/mybranch1:

ct co -nc .
ct merge -to ./ ./@@/main/mybranch1/LATEST
  Added file file4 to ./.

Теперь можно зачекать измененную директорию (а можно сделать это потом). И наконец-то можно выполнить долгожданный findmerge:

cleartool findmerge . -fversion /main/mybranch1/LATEST -co -nc -merge -gmerge

Будет выполнено слияние для файлов file1, file3 и file4. Теперь нужно внести в репозиторий результаты слияния веток. Заносим всё сразу — зачем мелочиться? :)

ct ci -c "Merged from /main/mybranch1." `ct lsco -r -cview -short`

Ух. Кажется, несложно. Но когда у нас нужно синхронизировать не одну директорию, а, скажем, десяток? Все эти действия превращаются в пытку :( К счастью, действия, необходимые для синхронизации директорий, довольно легко автоматизировать каким-нибудь простеньким скриптом. Что я и сделаю на досуге :)

23 октября 2007 г.

Команды bg, fg и jobs

До недавнего времени, если приходилось работать в unix или linux и редактировать несколько текстовых файлов, я открывал несколько gvim или открывал несколько терминалов и редактировал в каждом по одному файлу. Мне также приходилось использовать несколько терминалов, например, чтобы выполнять редактирование и сборку программы.

Но, как говорится, век живи, век учись. Так уж получилось, что последнее время пришлось много работать по ssh на удаленных серверах. А там и редактирование, и сборка, и запуск... В общем, несколько терминалов с ssh открыть, конечно, можно, да только слишком уж неудобно. Пришлось читать букварь :)

31 августа 2007 г.

Расширяем возможности cleartool, ч. 2

Кроме отсутствия в cleartool команды status, мне совершенно не нравится семантика команды cleartool diff. Действительно, после простой и удобной команды svn diff file.c, предназначенной для просмотра локальных изменений в файле file.c, строка cleartool diff -pre -dif file.c кажется слишком неудобной. И что самое главное — командой cleartool diff нельзя просмотреть изменения в нескольких файлах одновременно.

Но у меня есть волшебный скрипт, который неожиданно оказался вполне расширяемым :)

Итак, мне нужна простая команда diff, которая мне покажет локальные изменения для одного или нескольких файлов. Назовем ее sdiff и добавим в парсер аргументов скрипта:

case "$1" in
  sdiff) shift; sdiff $*;;
  status) shift; status $*;;
  *) $CT $*;;
esac

Теперь нужно создать функцию sdiff(), которая бы выводила изменения для всех переданных в командной строке имен файлов:

sdiff()
{
  if [ 0 = $# ]; then
    echo "ERROR: at least one argument should be passed."
    exit 1
  fi

  for T in $*; do
    echo "Index: $T"
    echo "---------------------------------------------"
    $CT diff -dif -pre $T
    echo
  done
}

Ну, вот и все. Синтаксис новой команды такой:

ct sdiff pname [pname ...]

где pname — имя файла или директории.

10 августа 2007 г.

Расширяем возможности cleartool

Так получилось, что по воле объективных причин недавно пришлось сменить систему контроля версий. До этого я использовал Subversion, и, надо признаться, с удовольствием использовал. Еще раньше был CVS, но, мне кажется, это все-таки прошлый век, и Subversion мне нравится гораздо больше. Однако, проблема Subversion в том, что в больших проектах его очень трудно использовать. Частично из-за его ограниченной производительности, частично из-за отсутствия средств автоматизации и интеграции с другими продуктами. И вот, пришлось пересесть за ClearCase, этого необъятного монстра. Говорят, много в нем хорошего, но и плохого — тоже немало. Точнее, не плохого, а неудобного. Хотя, возможно, это просто с непривычки...

В Subversion есть замечательная команда status, которая показывает все локальные изменения, произведенные с репозиторием. И хотя возможности этой команды достаточно широки, в подавляющем большинстве случаев я использовал эту команду, чтобы получить список измененных мной файлов, а также добавленных или удаленных. Обычно это делалось перед занесением изменений в репозиторий. В CVS для этих целей применялась команда update.

Используя cleartool, — консольный инструмент для работы с репозиторием ClearCase, — я обнаружил, что не могу найти простого и легкого способа получить список измененных мной файлов. Возможно, я плохо искал, но перерыв man и документацию по cleartool, я не нашел команды, аналогичной svn status. (Если такая команда все же есть, буду благодарен человеку, который мне ее покажет.)

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

#!/bin/sh

CT=cleartool

show_status()
{
  for T in $*; do
    PRE=" "
    $CT diff -pre -opt "-sta" $T || PRE="M"
    echo "$PRE $T" | tr -s "//" "/"
  done
}

status()
{
  CT_LSCO="$CT lsco -s -cvi -me"
  show_status `$CT_LSCO $*`
}

case "$1" in
  status) shift; status $*;;
  *) $CT $*;;
esac

Файл скрипта я назвал ct (сокращенно от cleartool) и положил в папку $HOME/bin, которая прописана у меня в системной переменной $PATH. Таким образом, я могу вызвать мой скрипт из любой директории.

Итак, зачем это. Скрипт построен таким образом, что все аргументы, указанные при запуске скрипта, передаются команде cleartool. Таким образом, скрипт ct является неким псевдонимом команды cleartool. Например, команда

ct ls -r /vob/myproject/somelib

эквивалентна вызову

cleartool ls -r /vob/myproject/somelib

Однако, ct — это не просто псевдоним, а псевдоним с дополнительными возможностями. Вызвав команду ct status, можно получить список "вычекнутых" из репозитория папок и файлов текущим пользователем для текущего view. Файлы, которые были изменены, будут помечены буквой M, как и при выполнении svn status. Общий формат вызова ct status такой:

ct status [-r] [pname ... ]

где pname — имя папки или файла репозитория (может быть указано несколько имен). Если pname не указывать, команда будет выполнена для текущей директории. Ключ -r переводит команду в рекурсивный режим — команда status будет выполнена для всех вложенных поддиректорий.

Например, необходимо получить статус файлов и поддиректорий для папки /vob/myproject/someapp:

$> ct status -r /vob/myproject/someapp

M /vob/myproject/someapp/file.c
  /vob/myproject/someapp/file.h
M /vob/myproject/someapp/main.c
M /vob/myproject/someapp/include/types.h
  /vob/myproject/someapp/include/ext/ext.h

Результат выполнения команды показывает, что из репозитория были "вычекнуты" файлы file.c, file.h, main.c, types.h и ext.h, а изменены только file.c, main.c и types.h.

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

ct ci -c "Some text here." file.c

При обработке аргументов скриптом качычки будут проигнорированы и каждое слово будет восприниматься, как отдельный параметр. Неудобно? Наверное, да. Но лично я не использую такой вариант check in. Я использую вариант с добавлением комментария после ввода команды, поэтому для меня это неудобство проблемой не является:

$> ct ci file.c
Some text here.
.
<Ctrl+E>

10 мая 2006 г.

2>&1

Я долго пытался понять правила перенаправления потоков вывода в юниксовой оболочке bash. Действительно, вроде всё понятно, но...

Например, было непонятно, почему работает команда, перенаправляющая stderr в stdout:
ls *.txt *.err >file 2>&1

Но при этом не работает вот так:
ls *.txt *.err 2>&1 >file

И уж совсем китайской грамотой казалась строка:
cat file 3>&2 2>&1 1>&3

Да, мне понятно, что эта команда меняет местами потоки вывода stderr и stdout. То, что выводилось в stdout, теперь будет выводиться в stderr и наоборот. Но почему именно так и никак иначе?..

Всё оказалось не просто, а очень просто :) Спасибо замечательной книге "Unix Power Tools" издательства O'Reilly.

Дело в том, что оболочка, при разборе параметров командной строки, читает параметры строго слева направо. Этот факт кажется совсем неудивительным, но оказывается именно это правило оказывает влияние на результат обработки командной строки.

Разберем, как интерпретируется строка "ls *.txt *.err >file 2>&1":
  1. параметр ">file" — означает "перенаправить стандартный поток вывода (stdout) в файл c именем file";

  2. параметр "2>&1" — означает "перенаправить стандартный поток ошибок (2) в стандартный поток вывода (1). Но, так как стандартный поток вывода уже перенаправлен в файл, то и стандартный поток ошибок перенаправляется туда же.
В результате оба потока будут перенаправлены в файл.

А как интерпретируется строка "ls *.txt *.err 2>&1 >file"?
  1. параметр "2>&1" — означает "перенаправить стандартный поток ошибок в стандартный поток вывода". На этот момент поток вывода (1) направляется в терминал, а значит, что поток ошибок тоже перенаправится в терминал.

  2. параметр ">file" — означает "перенаправить стандартный поток вывода (stdout) в файл c именем file".
Как результат — поток вывода будет направлен в файл, а поток ошибок — в терминал.

После этого совсем не трудно понять, как работает команда "cat file 3>&2 2>&1 1>&3". Тут тоже все просто:
  1. поток вывода 3 перенаправить туда же, куда выводится поток вывода 2 — в stderr;

  2. поток вывода 2 перенаправить туда же, куда выводится поток 1 — в stdout;

  3. поток вывода 1 перенаправить туда же, куда выводится поток 3 — в stderr
В результате, потоки вывода stderr и stdout как будто меняются местами — поток 1 идет в stderr, а поток 2 идет в stdout.