<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Bappoy&#039;s blog &#187; bash</title>
	<atom:link href="http://bappoy.pp.ru/tag/bash/feed" rel="self" type="application/rss+xml" />
	<link>http://bappoy.pp.ru</link>
	<description>Линуксоид на велосипеде с моторчиком</description>
	<lastBuildDate>Fri, 02 Dec 2011 11:44:47 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Разделение аргументов в shebang</title>
		<link>http://bappoy.pp.ru/2009/12/28/shebang-args.html</link>
		<comments>http://bappoy.pp.ru/2009/12/28/shebang-args.html#comments</comments>
		<pubDate>Sun, 27 Dec 2009 20:29:25 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[shebang]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=870</guid>
		<description><![CDATA[<p>В <a href="http://bappoy.pp.ru/2008/12/29/bash-pitfalls-part05.html#comment-8577">комментариях</a> к прошлогоднему <a href="http://bappoy.pp.ru/tag/bash-pitfalls">переводу Bash Pitfalls</a> внимательный <b>flavi</b> задался вопросом, почему при попытке выполнить скрипт, в начале которого написано </p>
<pre>#!/bin/bash --posix --verbose</pre>
<p>выдаётся ошибка </p>
<pre>/bin/bash: --posix --verbose: invalid option</pre>
<p>в то время как в интерактивном режиме bash нормально запускается с такой комбинацией опций; более того, если в shebang&#8217;е оставить только одну из этих опций, то скрипт также запускается без ошибок.</p>
<p>Поначалу я предположил, что <code>--posix</code> запрещает    опции, не определённые <a href="http://www.opengroup.org/onlinepubs/009695399/utilities/sh.html">стандартом POSIX на sh</a>. Но тогда попытка выполнить <code>bash --posix --norc</code> также приводила бы к ошибке, и конструкция <code>#!/bin/bash --verbose --norc</code> работала бы нормально, а это не так.</p>
<p>Пришлось разбираться более плотно. В <code>bash (1)</code> я вычитал следующее (выделение — моё):</p>
<blockquote><p>If the program is a file beginning with #!, the remainder of the first line specifies an interpreter for the  program.  The shell executes the specified interpreter on operating systems that do not handle this  executable format themselves.  The arguments to the interpreter consist of a  <b>single</b><b>  optional  argument  following  the  interpreter  name on the first line of the program, followed by the name of the program, followed by the command arguments, if any.</b></p></blockquote>
<p>Т.е. в shebang можно задавать только один аргумент; кроме того, bash самостоятельно анализирует shebang и вызывает указанный интерпретатор только в случае, если система сама не может выполнить этот скрипт.</p>
<p>Сам файл передаётся на выполнение с помощью системного вызова <code>execve (2)</code> (это я вычитал в файле <code>execute_cmd.c</code> в исходном коде bash), в man-странице которого и разъясняется политика партии по поводу разбиения аргументов интерпретатора:</p>
<blockquote><p>On Linux, the entire string following the interpreter name is passed as a single argument  to  the  interpreter, and this string can include white space.</p></blockquote>
<p>Таким образом, в самом первом примере у bash в argv оказывается  не два аргумента <code>--verbose</code> и <code>--norc</code>, а  один <code>--verbose --norc</code>, что и приводит к ошибке, и вина bash лишь в том, что он не разбивает свои аргументы на части, как это делает, например, Perl.</p>
<p>Причина такого странного поведения операционной системы (казалось бы, что сложного в том, чтобы просто разбить строку на части по пробелам или символам табуляции) описана, например, <a href="http://lkml.indiana.edu/hypermail/linux/kernel/0812.0/02503.html">в этой дискуссии LKML</a> или, более подробно, в <a href="http://unix.derkeiler.com/Mailing-Lists/FreeBSD/arch/2005-02/0039.html">письме Garance A Drosihn</a> в рассылку freebsd-arch. Вкратце, если бы механизм механизм обработки shebang (в Linux — <a href="http://lxr.linux.no/#linux+v2.6.32/fs/binfmt_script.c">linux/fs/binfmt_script.c</a>) передавал все аргументы интерпретатору, то для некоторых из них было бы затруднительно отделить аругменты интерпретатора от собственно аргументов  вызываемого скрипта. По крайней мере, так было еще на заре развития Unix и с тех пор <a href="http://www.in-ulm.de/~mascheck/various/shebang/#results">большая часть Unix-систем</a> передаёт все аргументы как единую строку.</p>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2009/12/28/shebang-args.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Ссылка на определенное место в man&#8217;е</title>
		<link>http://bappoy.pp.ru/2009/08/24/reference-to-fragment-in-less.html</link>
		<comments>http://bappoy.pp.ru/2009/08/24/reference-to-fragment-in-less.html#comments</comments>
		<pubDate>Mon, 24 Aug 2009 10:04:53 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[less]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=773</guid>
		<description><![CDATA[<p>В блоге <a href="http://linsovet.com/">&laquo;Полезные советы по Linux&raquo;</a> <a href="http://linsovet.com/zsh-vcs-info-in-prompt">наткнулся</a> на способ ссылаться на определенное место в документации:</p>
<pre>sh -c 'PAGER="less" LESS="-p search_string" man man_page'</pre>
<p>Явно вызываем sh, задаем просмотрщик less и указываем ему строку, которую нужно найти. Команда man запустит less и прокрутит экран до первого совпадения.</p>
<p>Конечно же, необходимо убедиться, что заданный шаблон присутствует в документе хотя бы один раз, и первый раз встречается именно в том месте, на которое вы хотите сослаться. Если ни одного совпадения не найдено, будет выведена ошибка и man будет показан с его начала, да и то только после нажатия Enter. Также стоит учитывать, что у адресата вашего сообщения может быть другой shell, другая версия документации и вообще другая операционная система.</p>
<p>Несколько примеров (предполагаем, что в качестве шелла используется sh-compatible shell типа bash или zsh, а переменная PAGER установлена в less).</p>
<p>Абзац в bash (1) про фичу <b>complete-into-braces</b> из одной из предыдущих заметок:</p>
<pre>LESS="-p complete-into-braces" man bash</pre>
<p>Раздел про подстановку из истории в bash (1). Поскольку строка HISTORY EXPANSION в документе встречается неоднократно, то требуется привязка к началу строки:</p>
<pre>LESS='-p ^s*HISTORY EXPANSION' man bash</pre>
<p>Подсветка важной фразы целиком:</p>
<pre>LESS='-p Please note.*log the command[^.]*.' man sudo</pre>
<p>Этот фокус можно задействовать в других утилитах, использующих less, например, в perldoc:</p>
<pre>LESS='-p ^s*Range Operators' perldoc perlop</pre>
<p>О том, как эта конструкция работает:</p>
<pre>LESS='-p MANPAGER, PAGER' man man
LESS='-p -ppattern.*' man less
LESS='-p Options which.*' man less</pre>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2009/08/24/reference-to-fragment-in-less.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Построение регулярного выражения по списку строк</title>
		<link>http://bappoy.pp.ru/2009/08/18/building-list-based-regexp.html</link>
		<comments>http://bappoy.pp.ru/2009/08/18/building-list-based-regexp.html#comments</comments>
		<pubDate>Tue, 18 Aug 2009 11:40:34 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[regexp]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=747</guid>
		<description><![CDATA[<p>Имеется неколько десятков однотипных файлов вида <code>FILE20090801011253.txt</code>, <code>FILE20090801023619.txt</code> и т.д. Требуется составить регулярное выражение, которому удовлетворяют только названия файлов из списка.</p>
<p>Вручную это можно сделать примерно так:</p>
<pre>FILE200908010(11253|23619)\.txt</pre>
<p>Если файлов много, то никаких нервов не хватит высчитывать, проверять и перепроверять.</p>
<p>То же самое можно сделать полуавтоматически, с помощью механизма <b>complete-into-braces</b> оболочки bash. Сочетание клавиш <b>Esc-{</b> преобразовывает список подстановки в более-менее оптимальный формат brace-completion, пригодный для дальнейшей обработки:</p>
<pre>$ echo /path/to/dir/(<b>Esc-{</b>)FILE200908010{11253.txt,23619.txt}</pre>
<p>Чтобы из получившейся строки сделать нормальный регэксп, нужно фигурные скобки заменить на круглые, а запятые — на вертикальную черту:</p>
<pre>echo "FILE200908010{11253.txt,23619.txt} "|sed -e 's/{/(/g' -e 's/}/)/g' -e 's/,/|/g'</pre>
<p>Приходится делать много лишних слабоавтоматизируемых действий, после чего копировать, вставлять, заменять и исправлять, что не очень удобно.</p>
<p>После непродолжительных поисков был найден Perl&#8217;овый модуль <a href="http://search.cpan.org/~dankogai/Regexp-Optimizer-0.15/lib/Regexp/List.pm">Regexp::List</a>, функцию list2re (преобразование списка в регулярное выражение) из которого можно приспособить под любые подобные задачи. Вот, например, сокращенная версия скрипта для моего случая:</p>
<pre>use Regexp::List;
my $l=Regexp::List->new;
$l->set(lookahead=>0);

opendir(D,"/path/to/dir") or die "Could not open $dir: $!";
my @list=grep {$_!~/^\.\.?$/ } readdir(D);          # get directory entries except "." and ".."
closedir(D);

my $re = $l->list2re(@list);                        # create regexp from @list
$re=~s/^\(\?-[xism]+:(.*?)\)$/^$1\$/g;              # strip "(?:-xism" and ")"
print "$re\n";</pre>
<p>Для следующих десяти файлов:</p>
<pre>FILE20090802120343.txt
FILE20090802165139.txt
FILE20090802181550.txt
FILE20090804014529.txt
FILE20090804140848.txt
FILE20090805103525.txt
FILE20090805104025.txt
FILE20090810083211.txt
FILE20090810120349.txt
FILE20090810121250.txt</pre>
<p>Скрипт выдает такой результат:</p>
<pre>^FILE200908(?:0(?:510(?:40|35)25\.txt|21(?:81550|20343|65139)\.txt|4(?:140848|014529)\.txt)|10(?:12(?:1250|0349)\.txt|083211\.txt))$</pre>
<p>Что и требовалось.</p>
<div style="font-size:85%;padding-top:2em;"><em>Пользуясь случаем, хочу порекламировать PDF-версию третьего издания книги <a href="http://www.books.ru/shop/books/626982?partner=532333">&laquo;Регулярные выражения&raquo;</a> Джеффри Фридла</em></div>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2009/08/18/building-list-based-regexp.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Защита Linux от несанкционированного консольного доступа</title>
		<link>http://bappoy.pp.ru/2009/03/18/securing-linux-console.html</link>
		<comments>http://bappoy.pp.ru/2009/03/18/securing-linux-console.html#comments</comments>
		<pubDate>Wed, 18 Mar 2009 05:16:25 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[fedora]]></category>
		<category><![CDATA[grub]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[безопасность]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=555</guid>
		<description><![CDATA[<p>Есть две вечные темы, о которых пишет почти каждый автор &laquo;блога про linux&raquo; — туннели в ssh (каюсь, <a href="http://bappoy.pp.ru/2008/02/27/ssh-tunneling-tips.html">сам</a> <a href="http://bappoy.pp.ru/2008/06/09/socks-in-ssh.html">грешен</a>) и сброс пароля root путём загрузки в режиме single user. Как правило, второй рецепт провоцирует обширные обсуждения на тему незащищённости Linux от действий злоумышленников при наличии физического доступа к компьютеру.</p>
<p>Вот несколько простых шагов, которые создадут серьёзные препятствия для злоумышленника, просто подошедшего к консоли в отсутствие хозяина. В качестве примера можно представить шаловливого племянника, зашедшего в гости, или зловредных коллег, собирающихся подменить заставку на рабочем столе.  Однако следует понимать, что при наличии времени всегда есть возможность просто вытащить винчестер  и подсмотреть интересующую информацию на другом компьютере, или же подложить руткит. Поэтому если вы всерьёз озабочены проблемой безопасности своего сервера, необходимо подумать о более серьёзных мероприятиях по его защите, таких, как шифрование дисков, установка полноценных систем предотвращения и обнаружения вторжений (IDS) и т.д. Не забывайте и про стандартные средства обеспечения безопасности в офисе или дата-центре: видеонаблюдение, надежная пропускная система, проинструктированная охрана&#8230; Полноценное описание этих мер выходит далеко за рамки данной статьи. </p>
<p>Итак, как же можно минимально обезопасить свой компьютер?<!--more--></p>
<ol>
<li><b>Отключите загрузку с CD-ROM и флэшек и установите пароль на BIOS</b>. Это не позволит злоумышленнику изменить порядок загрузки устройств и загрузиться со своей флэшки или компакт-диска. Однако всегда можно сбросить память BIOS, выковыряв батарейку из материнской платы, поэтому не следует полагаться на пароль BIOS&#8217;а в долгосрочной перспективе.</li>
<li><b>Установите пароль на редактирование параметров загрузки grub</b>. Система будет загружаться в обычном порядке, но никто не сможет изменить параметры загрузки, выбрав вариант загрузки и нажав кнопку &laquo;e&raquo;:
<ol>
<li>сгенерируйте пароль с помощью утилиты <code>grub-md5-crypt</code>:</p>
<pre>$ grub-md5-crypt
Password:
Retype password:
<b>$1$0/Zwx$i0LAHSrpXKfT.JBZHCXpf1</b></pre>
</li>
<li>Добавьте получившийся пароль в общий раздел <code>/boot/grub/menu.lst</code> в виде строки:
<pre>password --md5 $1$0/Zwx$i0LAHSrpXKfT.JBZHCXpf1</pre>
</li>
<li>Если у вас есть небезопасные дополнительные режимы загрузки (например, альтернативные операционные системы типа windows, или загрузка с параметром single), можно в описание этих режимов добавить параметр <code>lock</code>, который запретит пользователям выбирать этот вариант загрузки без ввода пароля. Пароль можно задать в этом же разделе, или оставить общий.</li>
<li>Обратите внимание, что если файл <code>menu.lst</code> генерируется программой <code>update-grub</code>, как это сделано в Debian или Ubuntu, то вместо указания директивы <code>lock</code> в каждом варианте загрузки необходимо установить специальные параметры в разделе настройки upgrade-grub: <code>lockalternative</code>, <code>lockold</code> или <code>kopt</code>. Формат этих параметров и механизм работы update-grub подробно описаны в <code>/boot/grub/menu.lst</code> и <code>man&nbsp;update-grub</code>.</li>
</ol>
</li>
<li><b>Если у пользователя root на вашей машине есть пароль, пусть система запрашивает этот пароль при входе в single-user mode</b>. Если в вашем дистрибутиве используется старый стиль инициализации через inittab (к таким системам относится большая часть дистрибутивов, кроме последних версий Ubuntu, Fedora и Debian, перешедших на Upstart), убедитесь, что в <code>/etc/inittab</code> присутствует строчка, подобная следующей:
<pre>~~:S:wait:/sbin/sulogin</pre>
<p>Эта инструкция говорит процессу init, что в однопользовательском режиме необходимо запускать программу <code>/sbin/sulogin</code>, которая запрашивает пароль суперпользователя. Но если пароль у root&#8217;а отсутствует, как, например, в Ubuntu, то sulogin пропускает в систему без запроса пароля. Поэтому обладателям современных дистрибутивов остаётся утешаться тем, что через пароль grub довольно сложно прорваться.</li>
<li><b>Отключите интерактивную загрузку</b>. В системах, основанных на RedHat (RHEL, Centos, Fedora), пользователь может нажать I перед началом загрузки сервисов и rc.sysinit при старте каждого сервиса будет спрашивать, нужно ли стартовать этот сервис. Эта фича отключается в файле <code>/etc/sysconfig/init</code> исправлением/добавлением параметра PROMPT:
<pre>PROMPT=no</pre>
</li>
<li><b>Отключите Ctrl+Alt+Del</b>. Любой бездельник может перезагрузить ваш компьютер (и испортить uptime), просто перейдя на виртуальную консоль и нажав три заветные кнопки. Этого риска можно избежать, настроив свой <code>/etc/inittab</code> или, в случае использования upstart, соответствующий файл в <code>/etc/event.d</code> (у меня этот файл так и называется — <code>control-alt-delete</code>). Необходимо просто закомментировать строчку, содержащую описание действия для события <code>ctrlaltdel</code> (или <code>control-alt-delete</code> в Upstart.). Или заменить это действие на команду отправки сообщения в syslog:
<pre>/usr/bin/logger -p auth.info "Ctrl+Alt+Del pressed</pre>
<p>После внесения изменения в <code>/etc/inittab</code> нужно послать процессу init сигнал перечитать этот файл:</p>
<pre>init q</pre>
<p>Для Upstart никаких уведомлений об изменении конфигурации не нужно.
</li>
<li><b>Установите таймаут для логина в консоли</b>. В оболочке bash есть переменная окружения <code>TMOUT</code>, которая устанавливает период неактивности (в секундах) — если от пользователя не поступило никакого ввода в течение указанного времени, процесс bash самоубивается. В <code>/etc/profile.d</code> можно поместить исполняемый файл <code>logintimeout.sh</code> примерно следующего содержания:
<pre>if [ x"$SSH_REMOTE_USER" = x ] ; then
    TMOUT=300
    export TMOUT
fi</pre>
<p>Таким образом, консольные пользователи будут &laquo;выкидываться&raquo; через 5 минут бездействия. Подобным образом можно установить период неактивности в других шеллах, а также в ssh (параметр ClientInactiveTimeout в sshd_config). Однако мне кажется, что подобные фокусы могут лишь раздражать вас и ваших пользователей, не принося реальной пользы, и стоит ограничиться установкой такого  таймаута только для логинов с консоли.
</li>
<li><b>Установите vlock и приучите себя и коллег использовать его перед тем, как отлучиться от консоли более чем на 30 секунд</b>. Команда <code>vlock</code> блокирует текущую виртуальную консоль; чтобы разблокировать ее, нужно ввести пароль пользователя или root&#8217;а (если он есть, конечно). <code>vlock -a</code> вообще запрещает переключаться на другие консоли (например, с помощью комбинации Alt+F7).</li>
<li><b>Не забывайте о блокировке графического интерфейса</b>.
<ol>
<li>установите разумный период бездействия (5 — 10 минут), по истечении которого запускается хранитель экрана</li>
<li>установите пароль на хранитель экрана</li>
<li>выведите ярлык блокировки на видное место</li>
<li>назначьте и запомните сочетание клавиш для блокировки экрана (в Gnome это Ctrl+Alt+L)</li>
</ol>
</li>
</ol>
<p>Выполнение хотя бы половины из этих действий добавит вам немного спокойствия, но, еще раз повторюсь, не гарантирует абсолютной безопасности. Будьте внимательны. </p>
<p>По мотивам <a href="http://www.cyberciti.biz/tips/tips-to-protect-linux-servers-physical-console-access.html">Tips To Protect Linux Servers Physical Console Access</a>.</p>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2009/03/18/securing-linux-console.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Частые ошибки программирования на Bash (часть пятая, она же последняя)</title>
		<link>http://bappoy.pp.ru/2008/12/29/bash-pitfalls-part05.html</link>
		<comments>http://bappoy.pp.ru/2008/12/29/bash-pitfalls-part05.html#comments</comments>
		<pubDate>Mon, 29 Dec 2008 13:19:40 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[bash pitfalls]]></category>
		<category><![CDATA[переводы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=494</guid>
		<description><![CDATA[22. echo "Hello World!"
23. for arg in $*
24. function foo()
25. echo "~"
26. local varname=$(command)]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/12/29/bash-pitfalls-part05.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Частые ошибки программирования на Bash (часть четвёртая)</title>
		<link>http://bappoy.pp.ru/2008/12/24/bash-pitfalls-part04.html</link>
		<comments>http://bappoy.pp.ru/2008/12/24/bash-pitfalls-part04.html#comments</comments>
		<pubDate>Wed, 24 Dec 2008 12:02:49 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[bash pitfalls]]></category>
		<category><![CDATA[переводы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=460</guid>
		<description><![CDATA[17. cd /foo; bar
18. [ bar == &#34;$foo&#34; ]
19. for i in {1..10}; do ./something &#38;; done
20. cmd1 &#38;&#38; cmd2 &#124;&#124; cmd3
21. Касательно UTF-8 и BOM (Byte-Order Mark, метка порядка байтов)]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/12/24/bash-pitfalls-part04.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Частые ошибки программирования на Bash (часть третья)</title>
		<link>http://bappoy.pp.ru/2008/12/22/bash-pitfalls-part03.html</link>
		<comments>http://bappoy.pp.ru/2008/12/22/bash-pitfalls-part03.html#comments</comments>
		<pubDate>Mon, 22 Dec 2008 15:31:31 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[bash pitfalls]]></category>
		<category><![CDATA[переводы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=434</guid>
		<description><![CDATA[11. cat file &#124; sed s/foo/bar/ &#62; file
12. echo $foo
13. $foo=bar
14. foo = bar
15. echo &#60;&#60;EOF
16. su -c 'some command']]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/12/22/bash-pitfalls-part03.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Частые ошибки программирования на Bash (часть вторая)</title>
		<link>http://bappoy.pp.ru/2008/12/18/bash-pitfalls-part02.html</link>
		<comments>http://bappoy.pp.ru/2008/12/18/bash-pitfalls-part02.html#comments</comments>
		<pubDate>Thu, 18 Dec 2008 13:04:40 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[bash pitfalls]]></category>
		<category><![CDATA[переводы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=399</guid>
		<description><![CDATA[5. [ "$foo" = bar &#38;&#38; "$bar" = foo ]
6. [[ $foo &#62; 7 ]]
7. count=0; grep foo bar &#124; while read line; do ((count++)); done; echo "number of lines: $count"
8. if [grep foo myfile]
9. if [bar=&#34;$foo&#34;]
10. if [ [ a = b ] &#38;&#38; [ c = d ] ]]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/12/18/bash-pitfalls-part02.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Частые ошибки программирования на Bash (часть первая)</title>
		<link>http://bappoy.pp.ru/2008/12/13/bash-pitfalls-part01.html</link>
		<comments>http://bappoy.pp.ru/2008/12/13/bash-pitfalls-part01.html#comments</comments>
		<pubDate>Sat, 13 Dec 2008 18:56:28 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[bash pitfalls]]></category>
		<category><![CDATA[переводы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=275</guid>
		<description><![CDATA[Качество скриптов, используемых для автоматизации и оптимизации работы системы, является залогом ее стабильности и долголетия, а также сохраняет время и нервы администратора этой системы. Несмотря на кажущуюся примитивность bash как языка программирования, он полон подводных камней и хитрых течений, способных значительно подпортить настроение как разработчику, так и администратору.

Большинство имеющихся руководств посвящено тому, как надо писать. Я же расскажу о том, как писать НЕ надо :-) 

1. for i in `ls *.mp3`
2. cp $file $target
3. [ $foo = "bar" ]
4. cd `dirname &#34;$f&#34;`]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/12/13/bash-pitfalls-part01.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Угадывание мыслей и выполнение несуществующих команд средствами bash</title>
		<link>http://bappoy.pp.ru/2008/11/17/quick-call-ssh.html</link>
		<comments>http://bappoy.pp.ru/2008/11/17/quick-call-ssh.html#comments</comments>
		<pubDate>Mon, 17 Nov 2008 06:33:44 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/?p=336</guid>
		<description><![CDATA[<p>В Debian в bash был добавлен патч, благодаря которому пользователь может написать свою функцию, выполняемую в случае, если введённая пользователем команда отсутствует. В Ubuntu эту фичу использует подсказка command-not-found, заметно тормозящая работу, в то время как можно найти более интересные и полезные возможности применения этого механизма,  оставив поиск пакета специализированным программам. Поделюсь своим опытом.</p>
<p>У нашего подразделения есть специальная сеть для тестовых серверов и виртуальных машин: 192.168.20.0/24, и очень часто приходится набирать команды типа <code>ssh user@192.168.20.xx</code>, причем в командах различается только последняя цифра. У ограниченного числа серверов нужно указывать другой <code>username</code>. Реже приходится ходить на сервера в других подсетях (в пределах 192.168.0.0/16); также иногда клиенты открывают нам доступ к своим системам, чтобы мы смогли продиагностировать их проблему и решить ее на месте.</p>
<p>Как следует из предыдущего абзаца, очень часто набираются команды вида:</p>
<pre>ssh ordinary_user@192.168.20.xx
ssh special_user@192.168.xx.yy
ssh third_user@ww.xx.yy.zz</pre>
<p>Возникает естественное желание этот процесс сократить и оптимизировать. Когда серверов было немного, я насоздавал множество <a href="http://bappoy.pp.ru/2007/11/21/konsole-encodings.html">хитрых алиасов</a> вроде следующего:</p>
<pre>alias 123='ssh user@192.168.20.123'</pre>
<p>Однако вскоре я понял, что поддерживать список из полусотни alias&#8217;ов — не true unix way, и задумался об альтернативах. Вспомнил об опытах вебмастеров эпохи web 1.0 по использованию 404 ошибки для отображения страницы с нужным содержанием, задумался о том, каким образом bash перехватывает вызов неизвестной команды и подменяет её командой поиска нужного пакета&#8230; В результате беглого изучения состава пакета command-not-found было выяснено, что используется функция <code>command_not_found_handle</code>. Она принимает в качестве аргумента введённую пользователем команду, выполняет некие действия и возвращает 127, если ничего нельзя сделать (в таком случае bash выводит стандартное сообщение об ошибке), или любое другое число, если что-то получилось.</p>
<p>Остальное оказалось делом техники. В <code>~/.bashrc</code> была добавлена функция:</p>
<pre>command_not_found_handle () {
    if [[ ! "$1" ]] ; then
        return 127
    fi

    n="$1"

    if echo $n| perl -ne 'exit(/^([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ ? 0:1)' ; then
        ip=192.168.20.$n
    elif echo $n| perl -ne 'exit (/^([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ ? 0:1)' ; then
        ip=192.168.$n
    elif echo $n| perl -ne 'exit (/^([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/ ? 0:1)' ; then
        ip=$n
    else
        return 127
    fi

    ssh $ip
}
</pre>
<p>Любое введённое число от 1 до 255 преобразуется в команду <code>ssh 192.168.20.число</code>; два числа — в <code>ssh 192.168.число.число</code>; любой введённый IP-адрес превращается в <code>ssh IP-адрес</code>. Во всех остальных случаях просто выводится сообщение <code>"command not found"</code>.</p>
<p>Поскольку используется довольно сложное регулярное выражение, то для его обработки пришлось использовать perl. Ещё был вариант с <code>grep -qP</code>, но эксперименальная опция <code>-P</code> (расширенная поддержка perl-овых регулярных выражений) включена в grep не во всех дистрибутивах (например, в Ubuntu 8.04 её нет, а в 8.10 уже есть).</p>
<p>Чтобы для всех хостов в 20 сети подставлялось общее имя пользователя ordinary_user, а для избранных хостов — специальные имена, в ~/.ssh/config я добавил строчки (общие параметры для всех хостов, предваряемые конструкцией <code>Host *</code>, должны находиться в конце списка):</p>
<pre>Host 192.168.20.251
User special_user1

Host 192.168.20.252
User special_user2

Host 192.168.20.254
User special_user3

Host *
User ordinary_user</pre>
<p>К сожалению, мне не удалось заставить эту функцию обрабатывать также и параметры командной строки: функции command_not_found_handle передаётся только первый позиционный параметр, остальные недоступны. Поэтому для каждого нестандартного хоста придётся либо писать полный вариант команды со всеми параметрами, либо указывать настройки сервера в ~/.ssh/config, подобно указанным выше. Имеются и прочие недостатки в реализации, обсуждаемые, в частности, на сайте <a href="http://smylers.hates-software.com/2008/01/04/090399e2.html">smylers hates software</a>.</p>
<p>Однако даже с такими ограничениями открываются новые потрясающие возможности.  Думаю, что предложенное мной применение не единственное, и этот пост — не последний на данную тему.</p>
<p>P.S. бонус для дочитавших до этого места: <a href="http://regexlib.com/">библиотека регулярных выражений perl</a>, где я нашел регэксп для проверки строки на соответствие IP-адресу.</p>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/11/17/quick-call-ssh.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Организация рабочих файлов по неделям</title>
		<link>http://bappoy.pp.ru/2008/04/28/weekly-folders.html</link>
		<comments>http://bappoy.pp.ru/2008/04/28/weekly-folders.html#comments</comments>
		<pubDate>Mon, 28 Apr 2008 13:38:12 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[lifehack]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[советы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/2008/04/28/weekly-folders/</guid>
		<description><![CDATA[<p>Ежедневно в моей рабочей папке появлялось от одного до полутора десятков и более файлов &#8212; документов, патчей, скриптов, отчётов и прочих продуктов рабочей деятельности. Однажды настал момент, когда найти нужный файл среди этого бардака стало довольно затруднительно, и чаша моего терпения переполнилась. Файлы нужно каким-нибудь организовать, подумал я. Изобретать систему меток для организации файлов по <strike>фэн-шую</strike> GTD было лень, поэтому я просто остановился на принципе &laquo;одна неделя &#8212; одна папка&raquo;, т.е. все файлы хранятся в папках вида ~/work/week18, к текущей и предыдущей неделе можно обратиться по ссылкам ~/work/current и ~/work/prev.</p>
<p>Для начала я соорудил скрипт для раскидывания файлов по папкам, создающий в текущей директории папки с именами в нужном формате, если их не существует, и перемещающий туда файлы:</p>
<blockquote>
<pre><font color="#444444">#!/usr/bin/perl -w</font>
<strong>use</strong> File::<font color="#a52a2a"><strong>stat</strong></font>;
<strong>use</strong> POSIX <font color="#a52a2a"><strong>qw</strong></font><font color="#4444ff"><strong>(</strong></font>strftime<font color="#4444ff"><strong>)</strong></font>;
<strong>use</strong> strict;
<strong>foreach</strong><font color="#4444ff"><strong>(</strong></font>`find . -maxdepth 1 -type f |sed <font color="#008000">'s/</font><font color="#77dd77">\ </font>/<font color="#77dd77">\\</font> /g;'`<font color="#4444ff"><strong>)</strong></font><font color="#4444ff"><strong>{</strong></font>
<font color="#a52a2a"><strong>    chomp</strong></font>;
<strong>    my</strong> <font color="#2040a0">$st</font>=<font color="#a52a2a"><strong>stat</strong></font><font color="#4444ff"><strong>(</strong></font><font color="#2040a0">$_</font><font color="#4444ff"><strong>)</strong></font>||<strong>die</strong> <font color="#008000">"Could not stat </font><font color="#2040a0">$_</font>: <font color="#2040a0">$!</font><font color="#77dd77">\n</font>";
    <strong>my</strong> <font color="#2040a0">$week</font>=strftime<font color="#4444ff"><strong>(</strong></font><font color="#008000">"</font><font color="#2040a0">%W</font>",<font color="#a52a2a"><strong>localtime</strong></font><font color="#4444ff"><strong>(</strong></font><font color="#2040a0">$st</font>-&gt;mtime<font color="#4444ff"><strong>)</strong></font><font color="#4444ff"><strong>)</strong></font>;
    <strong>unless</strong><font color="#4444ff"><strong>(</strong></font>-d <font color="#008000">"week</font><font color="#2040a0">$week</font>"<font color="#4444ff"><strong>)</strong></font><font color="#4444ff"><strong>{</strong></font><font color="#a52a2a"><strong>mkdir</strong></font> <font color="#008000">"week</font><font color="#2040a0">$week</font>";<font color="#4444ff"><strong>}
</strong></font><font color="#a52a2a"><strong>    rename</strong></font><font color="#4444ff"><strong>(</strong></font><font color="#008000">"</font><font color="#2040a0">$_</font>",<font color="#008000">"week</font><font color="#2040a0">$week</font>/<font color="#2040a0">$_</font>"<font color="#4444ff"><strong>)</strong></font>||<strong>warn</strong> <font color="#008000">"Could not rename </font><font color="#2040a0">$_ to week$week/$_</font>: <font color="#2040a0">$!</font><font color="#77dd77">\n</font>";
<font color="#4444ff"><strong>}</strong></font></pre>
</blockquote>
<p>Затем создал скриптик для еженедельного создания новой папки и переименования ссылок current и prev:</p>
<blockquote>
<pre>
<font color="#0000ff"><strong>#!/bin/sh</strong></font>
<font color="#2040a0">current_week</font>=week`/bin/date <font color="#008000">"+%W"</font>`
test -L ~/work/prev &amp;&amp; rm ~/work/prev
mkdir -p ~/work/<font color="#2040a0">$current_week</font>
pushd ~/work
test -L current &amp;&amp; /bin/mv current prev
ln -s <font color="#2040a0">$current_week</font> current
popd</pre>
</blockquote>
<p>Запуск скриптика в первый день каждой недели (нулевой &#8212; воскресение, по американским традициям) поместил в свой crontab (если кто еще не в курсе, у каждого пользователя есть свой crontab, аналогичный общесистемному /etc/crontab; редактируется с помощью команды crontab -e, просматривается командой crontab -l; подробнее см. man 1 crontab):</p>
<blockquote>
<pre># crontab -l
1   0   *   *   1   ~/bin/weekly.sh</pre>
</blockquote>
<p>Ссылки на ~/work/current и ~/work/prev впоследствии была добавлены в в favorite folders в Gnome, а также во все места, где требуется быстрый доступ к рабочим материалам. Кроме того, в начале каждой новой недели при написании еженедельного отчета стало на порядок проще делать обзор работы за предыдущую неделю (конечно, список рабочих активностей не исчерпывается появившимися файлами; есть же еще и почта, и bug-tracker, и другие средства организации информации)Недостаток у данного скрипта пока один &#8212; нужно ежегодно архивировать накопившиеся 52 папки, иначе снова возникнет бардак :) Но и это исправляется при наличии желания и умения.</p>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/04/28/weekly-folders.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Фокусы с автодополнением в bash</title>
		<link>http://bappoy.pp.ru/2008/04/03/bash-autocompletion.html</link>
		<comments>http://bappoy.pp.ru/2008/04/03/bash-autocompletion.html#comments</comments>
		<pubDate>Wed, 02 Apr 2008 23:50:40 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/2008/04/03/bash-autocompletion/</guid>
		<description><![CDATA[<p>После посещения заметки <a href="http://www.macosxhints.com/article.php?story=20080317085050719" title="Create on-the-fly hostname lists for ssh tab completion">&quot;Создание списка имен хостов для ssh на лету для автодополнения&quot;</a> долго вчитывался в man bash с целью понять, как же там это автодополнение работает. В конце концов, поняв, что с наскоку эту гремучую смесь из фич баша и возможностей readline не осилить, скачал оба исходника и нашел там великое множество примеров использования автозаполнения в bash, значительно упрощающих повседневную работу в консоли. Эти команды можно добавить в <code>~/.bash_profile</code> или вынести в отдельный файл <code>~/.bash_completions</code> или <code>/etc/bash_completions</code> и подключать его в <code>.bash_profile</code>. Список текущих дополнений можно посмотреть по команде <code>complete</code> (без параметров).</p>
<p><em>Данные скрипты скопированы почти без изменений из папки examples/complete архива с исходными текстами bash. Автор всех скриптов, за исключением последнего &#8212; <a href="http://www.caliban.org/bash/">Ian Macdonald</a>. На его странице <a href="http://www.caliban.org/bash/#completion_download">доступен архив</a> с огромным количеством скриптов автодополнения.  Автор последнего скрипта (автодополнение длинных опций у configure) &#8212; Manu Rouat. Большое им человеческое спасибо :-)</em></p>
<p>Для команд работы с каталогами tab показывает только каталоги:</p>
<blockquote>
<pre lang="bash">complete -d cd mkdir rmdir pushd</pre>
</blockquote>
<p>Для команд работы с файлами показываются только файлы (все или определённых типов, в зависимости от команды):</p>
<blockquote>
<pre lang="bash">complete -f cat less more chown ln strip
complete -f -X &#39;*.gz&#39; gzip
complete -f -X &#39;*.Z&#39; compress
complete -f -X &#39;!*.+(Z|gz|tgz|Gz)&#39; gunzip zcat zmore
complete -f -X &#39;!*.Z&#39; uncompress zmore zcat
complete -f -X &#39;!*.+(gif|jpg|jpeg|GIF|JPG|bmp)&#39; ee xv
complete -f -X &#39;!*.+(ps|PS|ps.gz)&#39; gv
complete -f -X &#39;!*.+(dvi|DVI)&#39; dvips xdvi dviselect dvitype
complete -f -X &#39;!*.+(pdf|PDF)&#39; acroread xpdf
complete -f -X &#39;!*.texi*&#39; makeinfo texi2dvi texi2html
complete -f -X &#39;!*.+(tex|TEX)&#39; tex latex slitex
complete -f -X &#39;!*.+(mp3|MP3)&#39; mpg123</pre>
</blockquote>
<p>Для команд работы с заданиями показываются номера заданий, предваряемые символом &quot;%&quot;:</p>
<blockquote>
<pre lang="bash">complete -A signal kill -P &#39;%&#39;
complete -A stopped -P &#39;%&#39; bg
complete -j -P &#39;%&#39; fg jobs disown</pre>
</blockquote>
<p>Сетевым командам подставляется список хостов из файла, заданного в переменной окружения HOSTFILE. Обычно это файл /etc/hosts:</p>
<blockquote>
<pre lang="bash">complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute nslookup</pre>
</blockquote>
<p>По уже упоминавшейся ссылке <a href="http://www.macosxhints.com/article.php?story=20080317085050719">рекомендуют</a> подставлять команде ssh список хостов из ~/.ssh/known_hosts:</p>
<blockquote>
<pre lang="bash">complete -W &quot;$(echo `cat ~/.ssh/known_hosts | cut -f 1 -d &#39; &#39; | sed -e s/,.*//g | uniq | grep -v &quot;\[&quot;`;)&quot; ssh</pre>
</blockquote>
<p>Для ряда встроенных команд bash, а также системных команд тоже свои списки дополнений:</p>
<blockquote>
<pre lang="bash"># подставляются переменные окружения
complete -v export local readonly unset
# подставляются параметры команд set, shopt, help, unalias, bind
complete -A setopt set
complete -A shopt shopt
complete -a unalias
complete -A binding bind</pre>
</blockquote>
<p>Для команд управления выполнением программ подставляются названия возможных программ:</p>
<blockquote>
<pre lang="bash">complete -c command time type nohup exec nice eval strace gdb</pre>
</blockquote>
<p>Думаю, основной принцип понятен -- указываем ключевое слово &quot;complete&quot;, за ним некий список возможных &quot;подстав&quot; и далее команды, для которых этот список работает. Список подстановок может быть задан в виде уже готовой опции, как в большинстве вышеприведённых примеров (в фрагменте <code>complete -d cd mkdir rmdir pushd</code> опция -d означает, что должен подставляться список доступных каталогов; на самом деле это сокращённый вид опции -A directory. За списком возможных параметров опции -A обращайтесь к man builtin), в виде списка возможных слов, как в примере с .known_hosts (тогда список предваряется опцией -W), или в виде списка файлов (-X &quot;*.gz&quot; покажет все доступные файлы, которые не совпадают с шаблоном &quot;*gz&quot;; чтобы, наоборот, показать все файлы *.gz, нужно в начало шаблона добавить восклицательный знак).</p>
<p>Конечно же, этим возможности автодополнения bash не ограничиваются. Можно использовать свои функции, которые сработают после того, как пользователь напишет команду, но перед тем, как будет нажата кнопка Tab, показывающая список возможных параметров. Такие функции должны произвести какие-то действия для нахождения подходящих параметров для автодополнения и заполнить этими параметрами массив COMPREPLY; при этом доступны массив COMP_WORDS, содержащий все слова из уже написанной строки, переменные COMP_CWORD (номер текущего слова в массиве COMP_WORDS), COMP_LINE (вся строка целиком в одной переменной) и еще парочка (см. man bash).</p>
<p>Формат команды настройки автодополнения в случае использования функции следующий:</p>
<blockquote>
<pre lang="bash">
_function_name ()
{
...
}
complete -F _function_name command</pre>
</blockquote>
<p>Чтобы не быть голословным, приведу несколько примеров, из которых всё станет ясно:</p>
<p>Для команды <code>umount</code> подставляется список примонтированных файловых систем:</p>
<blockquote>
<pre lang="bash">_umount ()
{
    local cur
    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}

    COMPREPLY=( $( mount | cut -d&#39; &#39; -f 3 | grep ^$cur) )
    return 0
}
complete -F _umount umount</pre>
</blockquote>
<p>Для команд работы с группами пользователей подставляются названия групп из /etc/group:</p>
<blockquote>
<pre lang="bash">
_gid_func ()
{
    local cur
    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $( awk &#39;BEGIN {FS=&quot;:&quot;} {if ($1 ~ /^&#39;$cur&#39;/) print $1}&#39; /etc/group ) )
    return 0
}

complete -F _gid_func groupdel groupmod</pre>
</blockquote>
<p>А теперь те две функции, ради которых, собственно, и затевался этот пост :)<br />
Автодополнение параметров для команды <code>find</code>:<br />
<!--more--></p>
<blockquote>
<pre lang="bash">_find ()
{
    local cur prev

        COMPREPLY=()
        cur=${COMP_WORDS[COMP_CWORD]#-}
        prev=${COMP_WORDS[COMP_CWORD-1]}

    case &quot;$prev&quot; in
    -@(max|min)depth)
        COMPREPLY=( $( compgen -W &#39;0 1 2 3 4 5 6 7 8 9&#39; ) )
        return 0
        ;;
    -?(a)newer|-fls|-fprint?(0|f))
        COMPREPLY=( $( compgen -f $cur ) )
        return 0
        ;;
    -fstype)
        # this is highly non-portable (the option to -d is a tab)
        COMPREPLY=( $( cut -d&#39;  &#39; -f 2 /proc/filesystems | grep ^$cur ) )
        return 0
        ;;
    -gid)
        COMPREPLY=( $( awk &#39;BEGIN {FS=&quot;:&quot;} \
                {if ($3 ~ /^&#39;$cur&#39;/) print $3}&#39; /etc/group ) )
        return 0
        ;;
    -group)
        COMPREPLY=( $( awk &#39;BEGIN {FS=&quot;:&quot;} \
                {if ($1 ~ /^&#39;$cur&#39;/) print $1}&#39; /etc/group ) )
        return 0
        ;;
    -?(x)type)
        COMPREPLY=( $( compgen -W &#39;b c d p f l s&#39; $cur ) )
        return 0
        ;;
    -uid)
        COMPREPLY=( $( awk &#39;BEGIN {FS=&quot;:&quot;} \
                {if ($3 ~ /^&#39;$cur&#39;/) print $3}&#39; /etc/passwd ) )
        return 0
        ;;
    -user)
        COMPREPLY=( $( compgen -u $cur ) )
        return 0
        ;;
    -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \
    -links|-perm|-size|-used|-exec|-ok|-printf)
        # do nothing, just wait for a parameter to be given
        return 0
        ;;
    esac

    # complete using basic options ($cur has had its dash removed here,
    # as otherwise compgen will bomb out with an error, since it thinks
    # the dash is an option to itself)
    COMPREPLY=( $( compgen -W &#39;daystart depth follow help maxdepth \
            mindepth mount noleaf version xdev amin anewer atime \
            cmin cnewer ctime empty false fstype gid group ilname \
            iname inum ipath iregex links lname mmin mtime name \
            newer nouser nogroup perm regex size true type uid \
            used user xtype exec fls fprint fprint0 fprintf ok \
            print print0 printf prune ls&#39; $cur ) )

    # this removes any options from the list of completions that have
    # already been specified somewhere on the command line.
    COMPREPLY=( $( echo &quot;${COMP_WORDS[@]}-&quot; | \
               (while read -d &#39;-&#39; i; do
                [ &quot;$i&quot; == &quot;&quot; ] &amp;&amp; continue
                # flatten array with spaces on either side,
                # otherwise we cannot grep on word boundaries of
                # first and last word
                COMPREPLY=&quot; ${COMPREPLY[@]} &quot;
                # remove word from list of completions
                COMPREPLY=( ${COMPREPLY/ ${i%% *} / } )
                done
                echo ${COMPREPLY[@]})
          ) )

    # put dashes back
    for (( i=0; i &lt; ${#COMPREPLY[@]}; i++ )); do
        COMPREPLY[i]=-${COMPREPLY[i]}
    done

    return 0
}
complete -F _find find</pre>
</blockquote>
<p>Гвоздь программы! Автодополнение параметров для команды configure:</p>
<blockquote>
<pre lang="bash">_longopt_func ()
{
    case &quot;$2&quot; in
    -*) ;;
    *)  return ;;
    esac

    case &quot;$1&quot; in
    \~*)    eval cmd=$1 ;;
    *)  cmd=&quot;$1&quot; ;;
    esac
    COMPREPLY=( $(&quot;$cmd&quot; --help | sed  -e &#39;/--/!d&#39; -e &#39;s/.*--\([^ ]*\).*/--\1/&#39;| \
grep ^&quot;$2&quot; |sort -u) )
}

complete  -o default -F _longopt_func wget bash configure</pre>
</blockquote>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/04/03/bash-autocompletion.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Страна советов</title>
		<link>http://bappoy.pp.ru/2008/03/28/advice-country.html</link>
		<comments>http://bappoy.pp.ru/2008/03/28/advice-country.html#comments</comments>
		<pubDate>Thu, 27 Mar 2008 21:21:33 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[mc]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/2008/03/28/advice/</guid>
		<description><![CDATA[<p>Иногда при работе в midnight commander появляется сообщение &laquo;The shell is already running the command&raquo;, не дающее выполнить команду в текущем каталоге. Стандартный способ обхода &#8212; Ctrl+O, Ctrl+C, Enter приводит к тому, что mc возвращается в каталог, где была выполнена предыдущая команда, и приходится еще раз переходить в нужный каталог. Проблема обсуждается чуть ли не с самого первого релиза mc, но по каким-то причинам принципиально не может быть решена.</p>
<p>В этом случае помогает выполнение команды &laquo;cd -&raquo; (после Ctrl+O, Ctrl+C, Enter, Ctrl+O), которая меняет каталог на предыдущий (т.е. в котором мы находились до первого Ctrl+O).</p>
<hr />В случае, если при сборке ядра была включена опция &laquo;Kernel .config support&raquo; (CONFIG_IKCONFIG), файл ядрёной конфигурации .config добавляется в бинарник ядра в гзипованном виде. А если дополнительно отметить еще и &laquo;Enable access to .config through /proc/config.gz&raquo; (CONFIG_IKCONFIG_PROC), то этот конфиг доступен через /proc/config.gz.Иногда хочется посмотреть, чем одно ядро отличается от другого в плане конфигурации, но перезагружаться или городить виртуальную машину ради того, чтобы посмотреть /proc/config.gz, не хочется. В таком случае помогут два скрипта, входящих в состав архива с исходными текстами ядра. Первый из них, служебный, называется binoffset и предназначен для определения смещения бинарного фрагмента от начала файла. Второй называется extract-ikconfig и служит непосредственно для поиска и извлечения из бинарника ядра гзипованного конфига. Перед запуском необходимо скомпилировать binoffset:</p>
<blockquote>
<pre lang="bash">cd /usr/src/linux/scripts
gcc -o binoffset binoffset.c</pre>
</blockquote>
<p>После чего можно натравливать extract-iconfig на ядро:</p>
<blockquote>
<pre lang="bash">scripts/extract-ikconfig /path/to/bzImage</pre>
</blockquote>
<hr />Регулярное чтение избранных частей из man bash просветляет и приносит несомненную пользу. Недавно поразил коллегу генератором паролей на основе <code>/usr/share/dict/words</code>:</p>
<blockquote>
<pre lang="bash">#!/bin/bash
passwords_count=3
test $1 &#038;&#038; echo $1 | grep -q ^[0-9]*$ &#038;&#038; passwords_count=$1

words=/usr/share/dict/words
words_count=3
symbols=("," "*" "-" "(" ")" "_" '"' ',' ',')

# prepare words array
declare -a allwords
exec 10< $words
while read LINE <&#038;10; do
    allwords[${#allwords[*]}]=$LINE
done

nwords=${#allwords[@]}

for ((k=0;$k<$passwords_count;k++));
do
    declare -a words_array
    for w in `seq 0 $words_count`; do
        words_array[${#words_array[*]}]=${allwords[$(($nwords*$RANDOM/32767))]}
    done

    passwd=${words_array[0]}
    for ((c=1;$c<${#words_array[@]}-1;c++));
    do
        symbol=${symbols[(($RANDOM*${#symbols[*]}/32767))]}
        passwd="${passwd}${symbol}${words_array[$c]}"
    done
    echo $passwd
    unset passwd
    unset words_array
done</pre>
</pre>
</blockquote>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2008/03/28/advice-country.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Несколько советов по bash-скриптингу</title>
		<link>http://bappoy.pp.ru/2007/11/27/bash-scripting-tips.html</link>
		<comments>http://bappoy.pp.ru/2007/11/27/bash-scripting-tips.html#comments</comments>
		<pubDate>Tue, 27 Nov 2007 08:34:29 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[Ссылки]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[советы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/2007/11/27/bash-scripting-tips/</guid>
		<description><![CDATA[<p><a href="http://citkit.ru/articles/121/">Tips от Madskull&#8217;a: bash</a>, в т.ч.:</p>
<ul>
<li>ожидание запущенных в фоне задач</li>
<li>прерывание по таймеру (SIGALRM)</li>
<li>параметры в скриптах и функциях</li>
<li>работа с массивами</li>
<li>перехват прерываний с помощью trap</li>
<li>как сделать прогрессбар в bash-скриптах</li>
<li>чтение конфигов из скрипта</li>
<li>работа со строками без использования внешних утилит типа sed и awk</li>
<li>подстановка параметров</li>
<li>использование getopts в shell-скриптах</li>
<li>специальные переменные, цвета ECHO, спецсимволы</li>
<li>краткое описание некоторых полезных утилит</li>
</ul>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2007/11/27/bash-scripting-tips.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Несколько советов по работе с историей команд в bash</title>
		<link>http://bappoy.pp.ru/2007/11/26/bash-history-tips-n-tricks.html</link>
		<comments>http://bappoy.pp.ru/2007/11/26/bash-history-tips-n-tricks.html#comments</comments>
		<pubDate>Mon, 26 Nov 2007 17:15:14 +0000</pubDate>
		<dc:creator>bappoy</dc:creator>
				<category><![CDATA[lifehack]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[переводы]]></category>
		<category><![CDATA[советы]]></category>

		<guid isPermaLink="false">http://bappoy.pp.ru/2007/11/26/bash-history-tips-n-tricks/</guid>
		<description><![CDATA[<p>Оригинал: <a href="http://richbradshaw.wordpress.com/2007/11/25/bash-tips-and-tricks/">bash tips and tricks</a></p>
<p>Для непосвящённых, bash &#8212; это оболочка по умолчанию во многих дистрибутивах Linux, включая Fedora, Ubuntu, Redhat и т.д. и т.п. Если вы используете ОС, основанную на Linux, есть вероятность, что вы используете именно bash. Поэтому ниже я сделал обзор некоторых часто встречающихся неудобств, а также простых способов их избежать.</p>
<p><strong>1. Забытая история bash.</strong><br />
Если вы открыли терминал и печатаете там команды, потом открыли еще один, немного его поиспользовали, то новый терминал не &laquo;вспомнит&raquo; ни одной команды, напечатанной в первом. Вдобавок, при закрытии первого терминала второй перезапишет все команды в истории, введённые в первом. Вдвойне досадно!</p>
<p>Это происходит потому, что история bash сохраняется только когда вы закрываете терминал, а не после каждой команды. Чтобы исправить такое поведение, добавьте в ~/.bashrc строки:</p>
<blockquote><p><code>shopt -s histappend<br />
PROMPT_COMMAND=`history -a`</code></p></blockquote>
<p>Это заставит bash дополнять историю вместо того, чтобы перезаписывать её: каждый раз, когда показывается приглашение командной строки, последняя команда добавляется в историю.</p>
<p><strong>2. Опечатки в названиях каталогов при cd.</strong></p>
<p>Добавьте в ваш .bashrc:</p>
<blockquote><p><code>shopt -s cdspell</code></p></blockquote>
<p>Теперь небольшие опечатки типа ect вместо etc будут игнорироваться.</p>
<p><strong>3. Дублирующиеся команды в истории bash.</strong></p>
<p>Я часто набираю cd .. много раз подряд, и потом, когда я нажимаю кнопку &laquo;вверх&raquo;, чтобы возвратиться к предыдущим командам, я не хочу, чтобы мне напоминали о моих не слишком элегантных экскурсиях по файловой системе.<br />
Добавьте в .bashrc:</p>
<blockquote><p><code>export HISTCONTROL="ignoredups"</code></p></blockquote>
<p>Или даже так:</p>
<blockquote><p><code>export HISTIGNORE="&amp;:ls:[bf]g:exit"</code></p></blockquote>
<p>Это заставит bash игнорировать дупликаты, так же как ls, bg, fg и exit, делая историю чище.</p>
<p><strong>4. Многострочные команды в истории.</strong><br />
Добавьте</p>
<blockquote><p><code>shopt -s cmdhist</code></p></blockquote>
<p>в .bashrc, тогда bash будет преобразовывать многострочные команды в одну строку для упрощения редактирования.</p>
<p><strong>5. Советы из комментариев.<br />
</strong></p>
<p>Нажмите Ctrl+R в bash, начните печатать и вы сможете найти введённую когда-то команду гораздо проще, чем нажимая 300 раз &laquo;вверх&raquo;.<br />
Или используйте</p>
<blockquote><p><code>history|grep "foo"</code></p></blockquote>
<p>для поиска &laquo;foo&amp;qyot; в истории команд.</p>
<blockquote><p><code>cd -</code></p></blockquote>
<p>переход в предыдущую директорию &#8212; полезно, если вы хотите куда-то сходить что-то поменять, а потом быстро вернуться обратно.</p>
<p>Предположим, что нужно выполнить несколько команд подряд с каким-то объектом:</p>
<blockquote><p><code>touch file<br />
ls file<br />
echo 1 &gt; file<br />
rm file</code></p></blockquote>
<p>Вместо того, чтобы каждый раз набирать file, достаточно нажать &laquo;Esc .&raquo;,  и аргумент из предыдущей команды подставится автоматически.</p>
<p><strong>Главный совет.</strong></p>
<p>Почитайте man shopt, найдёте много полезного :)</p>
<p><strong>В заключение.</strong></p>
<p>Вот несколько советов, как сделать историю bash более управляемой. Если у вас есть еще советы, добавляйте их в комментарии к <a href="http://richbradshaw.wordpress.com/2007/11/25/bash-tips-and-tricks/">оригинальной статье</a> или сюда.</p>
]]></description>
		<wfw:commentRss>http://bappoy.pp.ru/2007/11/26/bash-history-tips-n-tricks.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

