подкупили охран- ника ворот, так и хорошо защищенная система UNIX может быть превраще- на в широко открытую, если кто-либо подправит эту программу. Посколь- ку пользователи применяют программу passwd для изменения своих паролей, подправленная версия может записывать новый пароль, вводимый пользователем для изменения, в секретный файл, принадлежащий наруши- телю. Это может обесценить каждый вновь создаваемый в системе пароль. Внутри программы passwd пароль является просто символьным массивом, поэтому с этими данными легко управиться. ПРОГРАММА crypt Потенциальной правкой программы шифрования файлов crypt может быть накопление имен файлов и ключей шифра при каждом использовании программы. Таким методом вы можете проследить, кто запускает эту ко- манду, какой файл он использует и какой ключ применяется для того, чтобы получить доступ к этому файлу. Неприятности, связанные с последними двумя случаями, заключаются в том, что кто-нибудь может разрушить меры системной безопасности. Если вы считаете, что в вашей системе есть заманчивые, важные данные, то вам как администратору следует почаще проверять эти программы (по контрольной сумме или сравнением) на предмет повреждения. КОМАНДА su Поскольку команда su предоставляет суперпользовательский доступ для обычных пользователей, имеющих корневой пароль, это еще одна по- тенциальная лазейка в защите системы. Общая схема работы команды su выглядит так: -------------------------------------------- | | Получить информацию о пользователе: пользовательский идентификатор, | групповой идентификатор, пароль, номер терминала, ... | Если пароль пустой или пользовательский идентификатор равен нулю, | То пройти мимо вопросов о пароле. | | Запрос пароля | Если зашифрованный вариант того, что было только что набрано, | не совпадает с парольной строкой из файла /etc/passwd, | То запротоколировать неудачную попытку применения su, | напечатать сообщение "sorry" (сожаление по поводу неудачи), | выйти. | Пароль прошел: | запротоколировать успешную попытку применения su, | выполнить системные вызовы для того, чтобы ввести в действие | пользовательский и групповой идентификатор, | установить среду, если это требуется, | выдать на консоль сообщение, если это корневой shell, | а вы не за системной консолью, | организовать аргументы для показа su в команде ps, | выполнить интерпретатор shell. | Для подправленной версии потребовались бы лишь небольшие измене- ния в приведенной выше последовательности. Вместо того, чтобы сразу же зашифровывать пароль, su могла бы проверить "секретный" пароль на- рушителя. Если введен такой пароль, то проверку пароля и действия по про- токолированию можно обойти, поэтому запрещенный доступ не отразился бы в протоколе. Несанкционированный пользователь добился бы того, что ДРУГИЕ пароли для команды su записывались бы в "секретный" файл. В результате он бы потихоньку получил все интересующие его пароли для потенциального использования. Для пользователя команда su срабатывала бы успешно, если пароль правильный, а нарушитель получал бы в свое распоряжение пароль. КОМАНДА login Несанкционированный пользователь может повредить команду login при помощи тех же методов, что и для passwd. Тем не менее, админист- ратор может сделать нечто большее, чем просто защитить данную прог- рамму от повреждения. По теории, наилучшей защитой является нападе- ние. Поэтому администратор может внести в команду login свои собственные правки и применять ее как систему оповещения о вмешатель- стве. Каждый раз, когда кто-то входит в систему или пытается войти, можно изменить запись об используемых имени и пароле. Это может сиг- нализировать вам о любых попытках нарушителей угадать пароли методом грубой силы. В силу способа, которым реализована команда login, требуется только одно изменение. Алгоритм проверки как пользовательского паро- ля, так и пароля при наборе номера для модемной связи вызывает одну и ту же подпрограмму. К сожалению, мы не можем привести ее здесь, так как несанкционированные пользователи смогли бы применить ее для сбора паролей в своих корыстных целях. Далее, если вы завели ваш собствен- ный секретный файл для протоколирования попыток входа в систему, то вы должны попытаться убедить нарушителей, что они не смогут прочитать его и останутся со своими заботами. Вы можете сделать следующее, хотя это увеличило бы накладные расходы: зашифровать утилитой crypt пароли в протокольном файле с применением вашего собственного секретного ключа. Тогда даже если кто-то прочитает этот файл, он не сможет вос- пользоваться этой информацией. ПРОСТИТЕЛЬНЫЕ ГРЕХИ Рассматриваемые ниже случаи представляют собой менее, но все же потенциально болезненные проблемы безопасности. Они не привлекают способов овладения суперпользовательскими привилегиями, но являются способами надувательства системы и бегства из нее. СИСТЕМНЫЙ РЕЖИМ Данный прием, пожалуй, редко применяется, если только у вас нет человека, который очень близко знаком с низкоуровневым функционирова- нием того или иного процессора, используемого в вашей машине. Он мо- жет проникнуть в сердце аппаратного оборудования и пристроиться по- верх операционной системы. Тем не менее, администраторы должны осознавать, что такие вещи возможны. Во многих процессорах, например в процессоре Motorola 68000, имеется регистр состояния процессора (Processor Status Register), на- зываемый обычно PSW, хотя у разных процессоров он может называться по -разному. PSW содержит бит, определяющий, работает ли машина в "су- первизорном" или в пользовательском режиме. Этот режим важен для мно- гопользовательской аппаратуры, поскольку все пользовательские прог- раммы работают в пользовательском режиме, что сегментирует и защищает память от "коллизий" между процессами. С другой стороны, ядро работает в супервизорном режиме. Это оз- начает, что защита памяти не действует и центральный процессор может изменять содержимое любой ячейки памяти во всей машине. Ядру необхо- дима такая возможность, поскольку ядро поддерживает механизм своппин- га для перемещения процесса в защищенную память и из нее, когда про- цесс выполняется. Если нарушители безопасности могут получить в прграмму, работающую в системном режиме, то они получают возможность изменять всю память в системе. Последствия могут варьироваться от абсолютного разрушения, нап- ример записывания нулей в каждую ячейку памяти, до способности читать и сортировать данные в памяти, включая пароли и другую информацию с очень ограниченным к ней доступом. Для того чтобы добиться системного режима, нарушителю необходима возможность сгенерировать и установить новое ядро. Используемый метод зависит от того, есть ли у нарушителя исходный текст программ ядра. Приводимые нами подробности относятся к процессору 68000, но могут быть аналогичными для других процессоров. СИСТЕМНЫЙ ВЫЗОВ Первый метод - создать "пользовательский" системный вызов. Сис- темные вызовы находятся в исходных файлах с именами вида os/sys?.c. Это примерно 60 системных вызовов, и каждый из них имеет специфичес- кий номер. Этот номер определяется таблицей системных входов - табли- цей адресов точек входа в системные вызовы. Для добавления нового системного вызова необходимо подготовить его исходный код. Когда ядро перекомпилировано и установлено, можно производить системный вызов из любой программы в системе. Как только такой вызов активизирован, он может перевести машину в системный режим. К счастью, не так уж легко для "обычного" пользователя переком- пилировать и переустановить ядро системы. Этот метод, вероятно, тре- бует "внутренней работы". Помогло бы хранение ваших исходных текстов подальше от системы, но если вам нужно иметь системных програмистов, регулярно модифицирующих эти исходные тексты, то все, что в ваших си- лах - ограничить доступ (и подобрать надежных людей)! ПСЕВДОУСТРОЙСТВО Второй метод может быть использован теми нарушителями, которые не имеют исходных текстов, но имеют все библиотеки, образующие ядро. Здесь подход несколько другой, но результат тот же. В соответствии с той же идеей системного режима, цель заключает- ся в том, чтобы в регистре PSW центрального процессора был установлен привилегированный доступ ("супервизорный" или "системный режим"). Вместо того, чтобы использовать надлежащим образом ядро, этот метод пользуется внешним драйвером, который связывается с ядром. Это выпол- няется путем создания псевдоустройства. Псевдоустройство подобно нас- тоящему устройству, но его имя не ведет ни к какой физической перифе- рии. Доступ к псевдоустройству осуществляется с помощью всех тех же самых примитивов (открыть, закрыть, читать, писать), но это доступ к логической области, а не к физической. Для того чтобы определить псевдоустройство, нужно модифицировать главный файл устройства. В главном файле (который называется /etc/master или /usr/sys/conf/master) имеется таблица всех имен драй- веров устройств, связанных с каждым примитивом. Когда создается псев- доустройство, в таблицу драйверов устройств помещается новая запись. В этой таблице содержатся имена всех подпрограмм, поддерживающих при- митивы. Привилегированного режима можно добиться при помощи открытия псевдоустройства. Системный вызов open передает управление драйверу устройства, т.е. добавленному коду. В момент запуска этого кода маши- на уже находится в системном режиме, поскольку когда выполнялся вызов open, он был "пойман" системой и передан программе обработки, функци- онирующей в системном режиме. После этого драйвер устройства может делать то, что он хочет. НАРУШИТЕЛЬ ВЫДАЕТ СЕБЯ ЗА УДАЛЕННЫЙ УЗЕЛ uucp Если команда login подобна сторожу крепости, то программа uucp подобна заброшенному спасательному туннелю, через который враги могут проникнуть во дворец. С приходом межмашинных коммуникаций возникает целый ряд пробоин в защите системы. При помощи uucp несанкционированные пользователи могут попасть в систему, выдав себя за удаленный узел uucp. Это очень легко сделать. Нарушители могут заглянуть в файл /usr/lib/uucp/L.sys в вашей системе и обнаружить, где находятся удаленные системы, путем поиска входов в систему на других машинах. Затем они могут посмотреть в файле /etc/passwd такие входы в систему, которые запускают программы uucico вместо обычного shell-интерпретатора. Если они обнаружат соответству- ющие пароли, они могут попытаться применить некоторые вероятные паро- ли или использовать один из методов внесения правок, рассмотренных ранее, с целью перехвата паролей. Затем нарушитель может изменить имя узла своей системы на имя узла удаленной системы, чтобы выдать себя не за того, кем он на самом деле является. Он может войти в систему под именем uucp или под спе- циальным регистрационным именем, предназначенным для удаленной маши- ны. Программы uucp передают это узловое имя (которое является под- дельным) в вашу систему. Нарушители могут перекачать почту, файлы и т.д. из вашей системы на свою машину. Если у вас есть что-нибудь в очереди, ожидающей отп- равки на законную удаленную машину, нарушители могут сразу там очу- титься. Вы можете столкнуться с неприятностями, когда один из ваших операторов законной удаленной системы звонит и спрашивает вас, почему он неделями не получает от вас ни почты, ни программных запросов! Од- нако, коварный нарушитель мог бы переслать копию украденных файлов обратно к вам и использовать прогрессивные средства для отправки их на законную удаленную машину. ПОДДЕЛКА ПОЧТЫ Этот прием довольно хорошо известен, но мы включаем его для пол- ноты изложения. Похоже, однако, что он работает не во всех версиях системы UNIX. Он работает в System V, но не работает в XENIX, System III и Berkeley 4.2. Данный метод заключается в изменении пользова- тельской переменной среды LOGNAME. Поскольку команда mail использует ее, чтобы идентифицировать вас при отправке вам почты, меняется заго- ловок почты. Обычно это всего лишь мелкая неприятность, но вы должны уведомить об этом пользователей, чтобы они очень внимательно относи- лись к таким сообщениям, которые кажутся несвойственными для их мни- мого отправителя. СКРЫТЫЕ ИМЕНА ФАЙЛОВ ПРИ РАБОТЕ С РЕДАКТОРОМ vi Полезной практикой для обеспечения безопасности является выпол- нение случайных команд ps. Такая мера более-менее равносильна перио- дическому патрулированию с целью увидеть, не происходит ли что-нибудь опасное. Необходимо, однако, отметить, что лица, использующие редак- тор vi для несанкционированной работы, могут замести свои следы, вы- бирая такое имя редактируемого файла, чтобы оно не появлялось в рас- печатке команды ps. Самый простой способ, которым они могут это сделать - вызвать vi без указания имени файла. Тем самым vi запуска- ется с пустым файлом. Затем они могут применить команду ex для редак- тирования нужного им файла. Это убережет имя файла от распечатки ко- мандой ps, так как оно не является частью набора аргументов команды vi. Массив аргументов формируется при вызове команды vi, а не после ее запуска. Другой способ - использовать маскировку. Нарушители могут переи- меновать файл, который они хотят редактировать, в ничего не означаю- щее имя, например tmp, а потом использовать имя tmp при вызове редак- тора vi. В результате в массив аргументов занесется имя tmp. Оно и появится в распечатке команды ps. -------------------------------------------------------- ИМЯ: access -------------------------------------------------------- access НАЗНАЧЕНИЕ Ищет в парольном файле все регистрационные записи, не имеющие паролей. ФОРМАТ ВЫЗОВА access ПРИМЕР ВЫЗОВА access Выдает список всех беспарольных входов в систему ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) access v1.0 Show all free access logins Author: Russ Sage 4 if [ "$#" -gt "0" ] 5 then echo "access: too many arguments" >&2 6 echo "usage: access" >&2 7 exit 1 8 fi 10 grep '^[^:]*::' /etc/passwd || echo "All logins protected" ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ access? Мы уже отмечали, что записи о входе в систему в парольном файле создают возможность нарушения защиты, если с ними не связаны пароли, т.е. если поле пароля пустое. Проблема заключается в том, что в боль- ших системах парольный файл может сильно разрастись. Искать в таком файле вручную регистрационные записи, в которых отсутствуют пароли, было бы утомительным и приводило бы к ошибкам. Почему бы не поручить системе сделать за вас эту работу? ЧТО ДЕЛАЕТ access? Командный файл access использует команду grep с шаблоном поиска, описывающим регистрационную запись, не имеющую пароля. Когда такая запись попадается, она печатается в стандартный вывод. Если указанных записей не найдено, выводится сообщение "All logins protected" ("Все входы в систему защищены"). ПОЯСНЕНИЯ Первое, что делает access (в строках 4-8) - проверяет, правильно ли она была вызвана. Поскольку опций не предусмотрено, в командной строке ничего не должно быть. Если количество аргументов в командной строке больше нуля, то на стандартное устройство регистрации ошибок выдается сообщение об ошибке и командный файл завершается. Оператор в строке 10 выполняет поиск в парольном файле. Применя- ется утилита grep, т.к. мы используем в этой команде выражение. Если бы мы использовали фиксированную строку, более предпочтительной была бы утилита fgrep, потому что она быстрее. Выражение, задающее поиск, означает следующее: начиная с начала строки, найти все символы, от- личные от двоеточия, вплоть до обнаружения двух двоеточий подряд. Ес- ли вы заглянете в файл /etc/passwd, то увидите, что первое поле представляет собой имя (от начала строки до первого двоеточия). Затем между первым и вторым двоеточием размещается пароль. Если пароль от- сутствует, то после первого двоеточия сразу же следует второе - имен- но это соответствует нашему шаблону поиска. Поиск выполнятся в файле /etc/passwd. Если grep успешно обнаружил хотя бы одну запись, то код возврата нулевой. Если grep ничего не обнаружил, то код возврата еди- ница. Тогда активизируется последняя часть строки 10 и выводится со- общение о том, что все записи о входе в систему защищены. ---------------------------------------------------- ИМЯ: chkset ---------------------------------------------------- chkset НАЗНАЧЕНИЕ Выдает список всех файлов, имеющих включенный бит разрешения ус- тановки пользовательского/группового идентификатора. ФОРМАТ ВЫЗОВА chkset [-l] [dir ...] ПРИМЕР ВЫЗОВА chkset -l Вести поиск, начиная с корневого каталога, поскольку каталог не указан. С помощью команды "ls -d" выдать список файлов, для которых установлен в единицу бит разрешения установки идентификатора пользо- вателя либо идентификатора группы. Результат отсортировать по имени файла. (Бит установки пользовательского идентификатора S_ISUID и бит установки группового идентификатора S_ISGID являются атрибутами защи- ты файла наряду с битами прав доступа на чтение/запись/выполнение и определены в подключаемом файле /sys/stat.h. - Примеч. перев.) ТЕКСТ ПРОГРАММЫ 1 : 2 # @(#) chkset v1.0 Check for set bits on Author: Russ Sage 4 FORM="-print" 5 SORT="sort" 7 if [ "`echo $1 | cut -c1`" = "-" ] 8 then case $1 in 9 -l) shift 10 FORM="-exec ls -ld {} ;" 11 SORT="sort +7";; 12 *) echo "usage: chkset [-l][file/dir ...]" >&2 13 exit 1;; 14 esac 15 fi 17 if [ "$#" -gt 0 ] 18 then SRC="$*" 19 else SRC="/" 20 fi 22 find $SRC \( -perm -4000 -o -perm -2000 \) $FORM | $SORT ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ FORM Команда и опции для листинга SORT Команда и опции для сортировки результата SRC Исходный каталог, от которого нужно начинать поиск ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН КОМАНДНЫЙ ФАЙЛ chkset? Мы уже рассмотрели проблемы безопасности, которые могут возник- нуть, когда для исполняемых файлов установлен в единицу бит разреше- ния установки идентификатора пользователя. Это означает, что они мо- гут запускать интерпретатор shell с корневой или с другой привилегией высокого уровня. С той же целью может быть установлен в единицу бит разрешения установки идентификатора группы. Поэтому системный адми- нистратор должен непрерывно разыскивать и проверять все файлы в сис- теме, для которых установлены эти биты, чтобы посмотреть, не исполь- зуются ли они для несанкционированных целей. Не для всех интерпретаторов shell, нарушающих защиту, владельцем является суперпользователь (root). Один пользователь может запустить shell, владельцем которого является другой пользователь, имеющий бо- лее высокие привилегии. Это фактически предоставляет пользователю, запустившему shell, все возможности владельца файла. Найти shell-интерпретаторы, устанавливающие идентификатор поль- зователя или группы, бывает легко, а бывает и трудно, в зависимости от их авторства. Легко найти такие, которые: а) имеют необычные имена (некоторые нарушители любят выстав- лять свои достижения напоказ); б) содержат в исполняемом файле символьные строки, которые можно прочитать; в) размещены в необычном или очевидном каталоге; г) не имеют ограничений относительно того, кто может их запус- тить. Для изощренных shell-интерпретаторов характерно следующее: а) они имеют имена, похожие на обычные команды системы UNIX; б) имеют размеры файлов, которые соответствуют другим файлам, размещенным неподалеку от них; в) содержат упрятанные символьные строки, которые не так-то легко прочитать; г) имеют, возможно, специальную опцию, запускающую shell, или даже специальный пароль, необходимый для его запуска. Самые хулиганские из них являются настоящими командами системы UNIX, переделанными в корневые shell-интерпретаторы, которые затем переустанавливаются вместо первоначальных shell-интерпретаторов. Единственный надежный способ идентифицировать этот последний тип - запустить побайтовое сравнение между вашей дистрибутивной копией ко- манды системы UNIX и той версией, которая присутствует в настоящий момент в вашей системе. Конечно, перед тем как вы сможете проверить файлы со включенными битами разрешения установки пользовательского/группового идентифика- тора, вам нужно найти все такие файлы. В системе UNIX это делают две команды: find(1) и ncheck(1M). Утилита find ищет файлы по нашей точ- ной спецификации и может быть использована для поиска файлов с задан- ным набором прав доступа, включая биты установки идентификаторов. Ncheck печатает вперемешку специальные файлы и файлы с разрешенной установкой идентификатора пользователя. Это очень большой список, чтение и поиск в нем интересующих нас файлов занимает много времени. Таким образом, полезно создать командный файл, предоставляющий нам всю необходимую информацию и только такую информацию. Chkset исполь- зует команду find и ищет только файлы со включенными битами разреше- ния установки пользовательского/группового идентификатора, так что результат содержит лишь необходимую нам информацию, и этим результа- том можно сразу же воспользоваться. ЧТО ДЕЛАЕТ chkset? Chkset имеет два режима функционирования: один для сканирования всей системы в целом, а другой для сканирования указанных деревьев каталогов. Это хорошее свойство, так как сканирование каждого файла системы занимает очень много времени. Если имеется много больших дис- ковых устройств, проверка всего содержимого системы может занять це- лый час. Утилита chkset также очень сильно загружает центральный про- цессор из-за всех процессов, которые она генерирует. Указывая имена каталогов, вы можете выполнить проверку лишь на определенной области системного дерева. Отметим, однако, что поскольку chkset пользуется командой find, она сканирует не только указанный вами каталог, но и ВСЕ подчиненные каталоги. Заметим также, что chkset обнаруживает ВСЕ файлы с установленными в единицу битами установки пользовательско- го/группового идентификатора, а не только те, владельцем которых яв- ляется суперпользователь (root). Результат работы chkset можно выдать также двумя способами. Если не применять никакую опцию, то форма выдачи результата определяется командой "find ... -print", что означает полные маршрутные имена най- денных файлов. Затем эти полные имена сортируются. Если применяется опция -l, то для форматирования результата ис- пользуется команда "ls -ld", порождающая длинный формат листинга. При этом распечатываются полное указание прав доступа, число связей, вла- делец, размер и имя файла. Этот результат также сортируется по име- нам. Выбирайте тот или иной формат в зависимости от обстоятельств. Если вам нужно проверить большой участок системы и получить список подозрительных файлов, примените листинг по умолчанию (без опций), так как он компактнее и, как мы увидим позднее, занимает значительно меньше процессорного времени. Если вы хотите заняться определенным каталогом и посмотреть на его файлы подробно, воспользуйтесь опцией - l. Она предоставляет больше информации и избавляет от необходимости вручную набирать команды ls для интересующих вас файлов. Отметим, что эту команду может запустить кто угодно, а не только администратор, имеющий привилегии суперпользователя. Однако, если она запускается обычным пользователем, то команда find внутри командного файла chkset ограничена теми файлами, к которым пользователь имеет доступ на чтение. Так что вы могли бы предложить более привилегиро- ванным пользователям запускать chkset в качестве одной из их личных мер безопасности. Разумеется, если chkset запускается суперпользова- телем, то ограничения прав доступа к файлам несущественны и можно подвергнуть проверке все файлы. ПРИМЕР # chkset /bin /usr/bin /lib /usr/lib Эта команда вызывает просмотр указанных каталогов. В каталогах типа /usr/lib просматриваются все подчиненные каталоги, что обеспечи- вает более тщательную проверку. ПОЯСНЕНИЯ Первым делом chkset инициализирует две переменные - FORM и SORT. Переменная FORM содержит команду для выдачи результата работы команды find, а переменная SORT - команду, определяющую, что нужно сортиро- вать. В строке 7 проверяется, является ли первый позиционный параметр опцией. Если да, то оператор case (строки 8-14) смотрит, какая это опция. Если это опция "-l", то подготавливается команда для распечат- ки результата (это мы обсудим позже). Команда для утилиты sort форми- руется так, чтобы сортировка шла по полю владельца. Опция убирается из командной строки, потому что все последующие аргументы должны быть каталогами и мы захотим получить к ним доступ с помощью "$#". Если попалась опция, отличная от "-l", то это ошибка, выдается сообщение об ошибке (строка 12), и командный файл завершается. Если осталось более нуля аргументов, когда мы попадаем в строку 17, то они проверяются в цикле, чтобы убедиться, что все они являются каталогами. Если это не каталоги, на стандартное устройство регистра- ции ошибок выдается сообщение об ошибке и командный файл завершается. Если имеются параметры (т.е. каталоги), то в строке 18 в пере- менную SRC заносятся все каталоги. Если же параметров нет, то в пере- менную SRC заносится значение "/", т.е. корневой каталог, чтобы обес- печить подразумеваемую стартовую точку для поиска. Вся работа этого командного файла выполняется фактически в опе- раторе find. Команда find допускает множественное указание каталогов, которые поступают в результате чтения их из командной строки и зане- сения в переменную SRC. После того как мы указали команде find, откуда начинать поиск, мы указываем ей, что нужно искать. В данном случае нас интересуют все файлы, которые имеют включенный бит установки пользовательского либо группового идентификатора. Мы объясняем это команде find путем указа- ния прав доступа, которые требуется искать. Строка "-perm -4000" оз- начает поиск всех файлов, имеющих права доступа со включенным битом установки пользовательского идентификатора и с любыми другими вклю- ченными битами. Вы можете понимать эту запись как применение символов -заменителей - 4???. Мы ищем как установку пользовательского иденти- фикатора (-4000), так и установку группового идентификатора (-2000), поэтому две строки прав доступа соединены опцией -o, означающей "or" ("или"). (Более полное описание прав доступа в символической и вось- меричной форме приведено в chmod(1).) Следующая задача - добавить строку, хранимую в переменной FORM, в командную строку. Если опция -l не была использована, то в перемен- ной FORM хранится строка "-print", а это значит, что find будет печа- тать маршрутные имена найденных файлов. Если же -l использовалась, то переменная FORM содержит строку "-exec ls -ld {} ;". Опция -exec - это очень гибкая опция команды find, позволяющая применить любые ко- манды, которые за ней следуют, к каждому найденному файлу. В данном случае эти команды выполняют распечатку в длинном формате (-l), при- чем для каждого каталога (-d) выводится только его имя (а не содержи- мое). Именно здесь происходят наибольшие затраты ресурсов центрально- го процессора, так как для опции -l требуется системный вызов stat. Из-за того, что для каждого файла требуется команда ls, она каждый раз загружается в память и выполняется. Производится также доступ к индексному дескриптору файла на диске. Это приводит к большим наклад- ным расходам. Затем весь поток данных пропускается через утилиту sort. На са- мом деле мы хотим сделать сортировку по восьмому полю (вы можете про- верить это, выполнив команду "ls -l" и изучив ее результат). Утилита sort ПРОПУСКАЕТ указанное число полей, начиная с поля 1, являющегося по умолчанию стартовой точкой, поэтому использование записи +7 озна- чает переход к восьмому полю, которым является имя файла. АЛЬТЕРНАТИВНЫЙ ПОДХОД Для поиска файлов с интересующими нас правами доступа можно при- менить другой метод, хотя и более медленный. Он основывается на том, что вместо поиска прав доступа по числу, можно искать их по символь- ной строке. Для этого нужно применять grep. Альтернативная команда выглядит так: # find $* -exec ls -ld {} \; | grep "^[^ ]*s[^ ]*" Этот вариант команды работает несколько иначе. Он находит каждый файл из указанных каталогов и применяет к каждому из них команду "ls -ld". Затем весь список данных передается по конвейеру команде grep. Grep использует для распознавания интересующих нас файлов такой шаб- лон поиска: начиная с начала строки, найти повторяющийся символ, от- личный от пробела, затем символ "s", за которым следует повторяющийся символ, отличный от пробела. Такому шаблону соответствуют все режимы прав доступа, содержащие s, будь то бит установки пользовательского идентификатора или бит установки группового идентификатора. Не су- щественно, входят ли в данный режим "r", "w", "x" или "-". Убедив- шись, что пробелов нет, мы обеспечиваем соответствие шаблона только полю прав доступа. Важно, чтобы после s следовали символы, отличные от пробелов, так как s может встретиться либо в порции прав доступа, относящейся к владельцу, либо в групповой порции и нигде больше. Мы не очень рекомендуем этот метод, поскольку он привлекает очень интенсивную обработку. Однако, если бы мы не испробовали этот метод первым, мы бы не оценили, что использование команды find со строками "-perm" является более предпочтительным. В силу невероятной гибкости системы UNIX, имеется очень много различных способов выпол- нения одной и той же работы, и многие из этих способов могут давать одинаково хорошие результаты, но за разную цену в смысле быстродейс- твия и процессорных затрат. Перед тем как применять универсальную утилиту вроде grep для решения задачи, рассмотрите, нет ли другой ко- манды со встроенной опцией распознавания по шаблону. Скорее всего, применение такой команды окажется оптимальнее и гораздо быстрее, чем использование grep. С другой стороны, иногда вам нужно быстро решить нечасто встречающуюся задачу, не слишком заботясь об эффективности решения. -------------------------------------------------------- ИМЯ: suw -------------------------------------------------------- suw НАЗНАЧЕНИЕ Просматривает протокольный файл команды su и печатает имена всех пользователей, которые нелегально превратились в суперпользователя при помощи команды su (substituted user, замененный пользователь). ФОРМАТ ВЫЗОВА suw [-m] [sulog] ПРИМЕР ВЫЗОВА suw Запуск в режиме по умолчанию, проверка файла /usr/adm/sulog и выдача записей о нарушителях в стандартный вывод. ТЕКСТ ПРОГРАММЫ 1 static char id[]="@(#)suw v1.0 Author: Russ Sage"; 3 # include 5 # define FALSE 0 6 # define TRUE 1 7 # define MATCH 0 8 # define BSIZ 80 10 main(argc,argv) 11 int argc; 12 char *argv[]; 13 { 14 register int alert, c, mail, n; 15 FILE *fp1, *fp2; 16 char *p, *uname, line[BSIZ], tmp[BSIZ], 17 *log = "/usr/adm/sulog"; 19 static char *legal[] = {sage-root\n","root-root\n",NULL}; 20 static char *adm[] = {"sage",NULL}; 23 mail = FALSE; 25 if (argc > 1 && argv[1][0] == '-') 26 switch (argv[1][1]) 27 { 28 case 'm': 29 mail = TRUE; 30 --argc; 31 ++argv; 32 break; 33 default: 34 fprintf(stderr,"suw: invalid argument %s\n", argv[1]); 35 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 36 exit(1); 37 } 39 if (argc == 2) 40 log = *++argv; 42 if ((fp1 = fopen(log,"r")) == NULL) 43 { 44 fprintf(stderr,"suw: error opening %s\n",log); 45 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 46 exit(1); 47 } 49 sprintf(tmp,"/tmp/suw%d",getpid()); 50 if ((fp2 = fopen(tmp,"w+")) == NULL) 51 { 52 fprintf(stderr,"suw: error opening %s\n",tmp); 53 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 54 exit(1); 55 } 57 while (fgets(line,sizeof(line),fp1) != NULL) 58 { 59 p = line + 15; 60 if (*p == '+') 61 { 62 p = p + 2; 63 while (*p != ' ') p++; 64 p++; 65 uname = p; 66 while (*p && *p++ != '-') 67 continue; 69 if (strcmp (p,"root\n") == MATCH) 70 { 71 alert = TRUE; 72 for (n=0; legal[n] != NULL; n++) 73 if (strcmp (uname,legal[n]) == MATCH) 74 { 75 alert = FALSE; 76 break; 77 } 78 if (alert) 79 fprintf(fp2,"Illegal --> %s", line); 80 } 81 } 82 } 84 if (mail) 85 { 86 fclose(fp2); 87 for (n=0; adm[n] != NULL; n++) 88 { 89 sprintf(line,"cat %s | mail %s",tmp,adm[n]); 90 system(line); 91 } 92 } 93 else 94 { 95 rewind(fp2); 96 while ((c = getc(fp2)) != EOF) 97 putc(c, stdout); 98 fclose(fp2); 99 } 101 fclose(fp1); 102 unlink(tmp); 103 } ОПИСАНИЕ ЗАЧЕМ НАМ НУЖНА ПРОГРАММА suw? Вы помните, что команда su, позволяющая пользователям изменять свою индивидуальность (и права доступа) может быть источником проблем безопасности. Система хранит протокол всех транзакций su в файле sulog. Хотя более опытные нарушители могут уметь затирать свои следы, файл sulog полезен для отслеживания потенциальных лазеек в системе защиты. Этим способом можно поймать многих нарушителей-любителей. Ес- тественно, мы хотим автоматизировать этот процесс, чтобы система вы- полняла проверку и сигнализировала нам при обнаружении чего-либо опасного. Кроме того, данная программа демонстрирует методику, кото- рую можно использовать для отслеживания других протокольных файлов. ЧТО ДЕЛАЕТ suw? Программа suw читает и анализирует протокольные файлы команды su. Каждое успешное превращение в суперпользователя при помощи коман- ды su, обнаруженное в протокольном файле, сверяется со списком разре- шенных суперпользователей. Если пользователю не разрешено быть супер- пользователем, то конкретная запись о нем печатается, чтобы оповестить администратора. По умолчанию записи о нарушителях печатаются в стандартный вы- вод. Протокольным файлом по умолчанию является /usr/adm/sulog. Если применяется опция -m, то записи о нарушителях рассылаются по почте администраторам, занесенным в предопределенный список. Если нужно проверить другой протокольный файл, например /usr/adm/Osulog, то его имя можно указать в командной строке. ПРИМЕРЫ 1. # suw -m Проверить файл /usr/adm/sulog и разослать записи о нарушителях администраторам, определенным в тексте программы. 2. # suw /usr/adm/Osulog Проверить файл /usr/adm/Osulog и напечатать записи о нарушителях в стандартный вывод. ПОЯСНЕНИЯ В самом начале программы определяются и инициализируются все пе- ременные и списки. В строке 14 определены два флага: alert (сигнал тревоги) и mail (почта). Они имеют значение TRUE или FALSE. В этой программе используются два файла: протокольный файл команды su, кото- рый вы выбираете, и временный файл. Поскольку мы собираемся применять некоторые подпрограммы стандартного ввода-вывода (stdio), мы пользу- емся указателями на файлы fp1 и fp2, а не дескрипторами файлов. При- меняется два буфера: один для зачитывания в него данных из протоколь- ного файла команды su, а другой - для хранения имени временного файла. Первоначально именем протокольного файла является sulog. Если вместо него используется другое имя файла, то переменная log переус- танавливается на это имя. Затем инициализируются предопределенные списки разрешенных су- перпользователей и администраторов. В нашем примере двумя разрешенны- ми суперпользователями являются sage (sage-root) и root (root-root). Для того чтобы приспособить это для вашей системы, поместите здесь имена людей, которым вы хотите разрешить пользоваться командой su для превращения в суперпользователей. Список администраторов также должен содержать соответствующие имена. В данном примере в список админист- раторов входит одно имя - sage. Это значит, что sage будет единствен- ным, кто получит почту, если указана почтовая опция. Подразумеваемое состояние рассылки почты устанавливается в стро- ке 23 на значение FALSE, т. е. почты нет. Это делается для того, что- бы после разбора командной строки переменная mail имела правильное значение. Далее выполняется проверка на ошибки. Первая проверка выглядит несколько странной, но на самом деле вполне понятна. Переменная argc возвращает число аргументов командной строки, а argv указывает на массив, содержащий сами аргументы, каждый из которых сам является массивом символов. Если командная строка вообще имеет какие-либо ар- гументы (argc > 1) и первым символом первого аргумента (argv[1][0]) является дефис, то проверяется, корректная ли это опция. С целью про- верки второго символа первого аргумента используется оператор case. Если аргументом является символ m, то флаг mail устанавливается в состояние TRUE, число аргументов (argc) уменьшается на единицу, а указатель на массив аргументов (argv) на единицу увеличивается. Это служит той же цели, что и удаление аргументов из командной строки в командных файлах интерпретатора shell. Напомним, что argv является в действительности массивом указателей, а массивы трактуются точно так же, как строки, с базовым адресом и смещением. Поэтому argv[0] == argv, и argv[1] == ++argv. Если опция отличается от -m, то в стан- дартный вывод печатается сообщение об ошибке и программа завершается. В строке 39 проверяется счетчик аргументов, чтобы понять, есть ли еще один аргумент. Напомним, что argv всегда на единицу отстает от argc, потому что само имя команды является первым аргументом массива. Следовательно, для того чтобы получить второй аргумент, мы должны пе- рейти к следующей позиции (*++argv). Если аргумент имеется, он должен быть именем протокольного файла, и это имя заносится в переменную log. Далее мы пытаемся открыть файлы, используемые в программе. Сна- чала открывается для чтения протокольный файл. Если это не срабатыва- ет (возвращен нулевой указатель), то печатается сообщение об ошибке и программа завершается. Затем в строке 49 создается имя временного файла с добавлением к нему идентификатора процесса, чтобы гарантиро- вать уникальность имени файла. Этот файл открывается на чтение и за- пись. Если эта операция не удается, печатается сообщение об ошибке и выполнение завершается. В строках 57-103 заключен главный цикл. Управляющая часть глав- ного цикла определяется тем, все ли данные прочитаны из протокольного файла. Если больше никакие байты прочитать нельзя, то fgets (строка 57) возвращает нулевой указатель, что завершает цикл while. Когда строка данных читается из протокольного файла, эти данные помещаются в массив line. Мы применяем указатель на символ для прохода вдоль строки и поиска записи о нарушителе. Изучая запись в вашем файле sulog, вы можете увидеть, где нахо- дятся интересующие нас поля. Сначала указатель смещается на 15 симво- лов от начала строки. Это позиция флага, определяющего, была ли ус- пешной команда su. Если она была успешной, указатель увеличивается на два, чтобы пропустить пробел и установить указатель на имени термина- ла. Это имя имеет переменную длину, поэтому мы ориентируемся по про- белу в конце имени. В строках 63-64 применяется цикл while, который заставляет указатель пропустить все символы, отличные от пробела. За- тем он увеличивается на единицу еще раз, чтобы пропустить пробел меж- ду именем терминала и строкой имени пользователя. В этот момент указатель находится в последнем поле строки. Ука- затель на это поле помещается в переменную uname, чтобы затем ее мож- но было использовать для сравнения строк. Следующий цикл while (стро- ки 66-67) проходит по строке, пока не попадет на символ дефиса. Цикл while выполняется до тех пор, пока указатель не указывает на конец строки (т.е. на нелевой указатель, *p == '\0') и еще не указывает на дефис. Поскольку в цикле используется p++, то когда мы попадаем на нулевой указатель, цикл завершается, причем p указывает на следующий символ. Выполняется проверка, был ли суперпользователем тот, кто приме- нил команду su. Если был, то взводится флаг alert, и мы должны прове- рить, разрешенный ли это суперпользователь. Цикл for (строки 72-77) пробегает по всем именам в массиве, содержащем имена разрешенных пользователей, до тех пор, пока мы не обнаружим последнюю запись, ко- торая является пустой. Строка с именем пользователя, установленная предварительно, сверяется с каждой записью в массиве разрешенных имен. Если они совпадают, то мы можем предположить, что с данным при- менением команды su все в порядке. Тогда мы выключаем флаг alert и завершаем сравнение имен. Если же по окончании цикла флаг alert все еще взведен, значит имя не содержится в списке разрешенных - это, возможно, нарушитель. Вся запись целиком записывается во временный файл и управление передается назад в начало цикла для следующей опе- рации чтения. Когда все данные прочитаны, цикл while завершается. В строке 84 проверяется, взведен ли флаг mail. Если да, то временный файл закры- вается (чтобы сработала команда cat) и выполняется еще один цикл for (строки 87-91). Он перебирает всех администраторов и завершается, когда попадает на нулевой указатель в конце массива. Для каждого из обозначенных администраторов конструируется команда mail и посылается в систему UNIX при помощи системного вызова (строка 90). Метод отп- равки по почте временного файла заключается в применении к этому фай- лу команды cat и передаче данных по конвейеру команде mail. Если флаг mail выключен, то временный файл нужно напечатать в стандартный вывод, а не отправить по почте. Для того чтобы сделать это как можно быстрее, временный файл (который пока еще открыт) "пе- рематывается" (мы позиционируемся в его начало) и посимвольно пропус- кается по циклу. Обратите внимание, что данный цикл является сердце- виной команды cat, как описано на странице 153 книги Кернигана и Ритчи "Язык программирования Си" (B.W.Kernighan, D.M.Ritchie. The C Programming Language). После того как временный файл напечатан, он закрывается. Наконец, закрывается протокольный файл и удаляется вре- менный файл.  * ГЛАВА 10. Смешанные приемы *  Введение Способы преобразования в языке shell conv Модули преобразования dtoh dtoo htod htoo Тонкости bc otod otoh Приемы языка shell для обеспечения гибкости программ Хитрости языка shell Читайте ввод с клавиатуры, пока находитесь в цикле, присоединенном к програмному каналу Запуск дочернего языка shell Уровни языка shell и ввод-вывод Встроенный ввод С редактором ed С файлом a.out C архивами языка shell Управление статусом цикла Фильтры и синтаксис Недостатки/особенности программирования на языке shell Программа для перенаправления ошибки Некорректный код возврата Хитрости редактора Vi Возвращение в язык shell Поддержка Escape Макросы Команда "One-Liners" - крошечная, но мощная . - 2 - Введение Эта книга является итогом многолетней работы по подбору и развитию инструментальных средств ОС UNIX. Многие вещи, которые не хотелось бы оставлять без внимания, не вписались в контекст предыдущих глав. Это и законченные процедуры, подобные представленным ранее, и небольшие, но очень мощные фрагменты программ. Кроме того, высказаны некоторые полезные идеи и представлены методы обработки общих ситуаций на языке shell. Способы преобразования Поскольку компьютеры и их резидентные утилиты используют при работе разные системы счисления, часто возникает необходимость преобразования оснований систем счисления. Эти преобразования обеспечиваются хорошо знакомыми специалистам командами UNIX bc (калькулятор произвольной точности) и dc (которая предположительно расшифровывается как настольный калькулятор ("desk calculator")). Большинство из существующих возможностей либо носят очень ограниченный характер, либо их тяжело использовать в ряде ситуаций, поэтому будет рассмотрен вопрос как использовать существующие возможности UNIX, чтобы любое преобразование было как можно более легко осуществимым. --------------------------------------------------------------------- Название: conv --------------------------------------------------------------------- conv Переводит числа из одной системы счисления в другую Назначение: Обеспечивает возможность преобразования основания системы счисления Вызов conv Пример вызова: $conv Вызвать главное меню различных преобразований 2 Выбрать опцию 2 ( из шестнадцатиричной в десятичную) FFF Ввести шестнадцатиричное число FFF. На выходе программы получим десятичный эквивалент Исходный текст для функции conv 1 : 2 # @(#) conv v1.0 Преобразование основания системы счисления, используя shell Автор: Russ Sage 3 4 while : 5 do 6 echo " 7 8 Преобразование оснований 9 ------------------------ 10 1 - Десятичное в шестнадцатиричное 11 2 - Шестнадцатиричное в десятичное 12 3 - Десятичное в восьмеричное 13 4 - Восьмеричное в десятичное 14 5 - Восьмеричное в шестнадцатиричное 15 6 - Шестнадцатиричное в восьмеричное 16 17 enter choice (1-6, <>): \c" 18 read CHOICE 19 20 case $CHOICE in 21 "") exit;; 22 1) echo "\пВведите десятичное число (<> to exit): \c" 23 read DEC 24 if [ "$DEC" = ""] 25 then exit 26 fi 27 HEX='. dtoh' 28 echo "\n${DEC}d = ${HEX}x";; 29 2) echo"\nВведите шестнадцатиричное число в верхнем регистре (<> to exit): \c" 30 read HEX 31 if [ "$HEX" = ""] 32 then exit 33 fi 34 DEC='. htod' 35 echo "\n${HEX}x= ${DEC}d;; 36 3) echo "\nВведите десятичное число в верхнем регистре (<> to exit): \c" 37 read DEC 38 if [ "$DEC" = ""] 39 then exit 40 fi 41 OCT='. dtoo' 42 echo "\n${DEC}d = ${OCT}o";; 43 4) echo "\nВведите восьмеричное число (<> to exit): \c" 44 read OCT 45 if [ "$OCT" = ""] 46 then exit 47 fi 48 OCT='. otod' 49 echo "\n${OCT}o = ${DEC}d";; 50 5) echo "\nВведите восьмеричное число (<> to exit): \c" 51 read OCT 52 if [ "$OCT" = ""] 53 then exit 54 fi 55 HEX='. otoh' 56 echo "\n${OCT}o = ${HEX}x";; 57 6) echo "\nВведите шестнадцатиричное число в верхнем регистре (<> to exit): \c" 58 read НЕХ 59 if [ "$НЕХ" = ""] 60 then exit 61 fi 62 OCT='. htoo' 63 echo "\n${HEX}x = ${OCT}o";; 64 *) echo "\n$CHOICE-неизвестная команда";; 65 esac 66 done Переменные окружения CHOICE - Выбор команд из главного меню DEC - Выдает десятичное значение как результат преобразования HEX - Выдает шестнадцатиричное значение как - 4 - результат преобразования OCT - Выдает восьмеричное значение как результат преобразования Описание Зачем нам нужна функция conv ? Выполнение числовых операций большого объема в командных файлах языка shell - это далеко не самая хорошая идея. Командные файлы явля- ются весьма медленными сами по себе, а выполнение математических опе- раций еще больше замедляет их работу. Однако, процедуры языка shell имеют математические возможности, и Вы, возможно, захотите ими воспользоваться. Если Вам нужно преобразовать несколько чисел в про- цессе написания программы, то для этой цели достаточно удобно вызвать процедуру языка shell. Поскольку conv - это программа, управляемая меню, Вам не придется беспокоиться о запоминании сложного синтаксиса, котрый используют некоторые системные утилиты преобразования. Что делает conv? Это инструментальное средство обеспечивает возможность перевода чисел из одной системы счисления в другую. Можно переводить десятичные, шестнадцатиричные и восьмеричные данные. Число, записанное в одной из этих форм, может быть переведено в любую из двух оставшихся форм. Режим работы программы выбирается из главного меню. В меню есть шесть пунктов. После того как Вы выбираете число между 1 и 6, программа просит Вас ввести число которое Вы хотите преобразовать. Происходит преобразование и на выходе программы Вы получаете два значения - число, которое Вы преобразовываете и число, к которому оно было преобразовано. Преобразования осуществляются путем вызова внешних процедур, о которых будет идти речь дальше в этой главе, так что перед запуском conv необходимо убедиться, что Вы включили их в вашу систему и разместили в том же каталоге, что и conv. Если Вы введете команду, не вошедшую в вышеупомянутый перечень, то будет выдано сообщение об ошибке и опять будет выведено главное меню. Пояснение Строки 4-66 - это один большой бесконечный цикл while. Мы используем бесконечный цикл, чтобы в случае ошибочного ввода программа вернулась в главное меню для повторного ввода. Для того, чтобы выйти из программы, нужно прервать цикл, т.е. выйти из цикла. Строки 6-17 печатают меню и выдают подсказку для выбора. Если Вы просто нажмете "Ввод", программа завершит свою работу. Строка 18 читает ввод с клавиатуры, и строки 20-65 выполняют выбор по условию для этой величины. Если получен нулевой (пустой) ввод, то программа завершает свою работу. Строки 22-28 осуществляют перевод чисел из десятичной в шестнадцатиричную системы счисления. Поскольку все модули перевода отвечают одному и тому же образцу, то детально мы рассмотрим только данный модуль. Подсказка запрашивает число в строке 23. В строках 24-26 проверяется, не было ли введенное значение пустым. Строка 27 выглядит несколько загадочно, вызывая один из внешних командных файлов dtoh для преобразования десятичных чисел в шестнадцатииричные. Обратите внимание на то как одна программа выполняет другую. Командный файл dtoh запускается, используя команду ".". Это означает : "Выполните программу, используя тот же shell". Процедура dtoh использует переменную DEC для ввода числа и выдает - 5 - преобразованное число на стандартный вывод. Чтобы записать это число в переменную, мы делаем присвоение, потом запускаем программу, используя командную подстановку. Строка 28 выдает на экран первоначальное десятичное число и шестнадцатиричное, к которому оно было преобразовано. Варианты 2, 3, 4, 5 и 6 работают аналогично. Единственное, что меняется - это имя переменной, которое соответствует типу преобразования и название командного файла (скрипта), который вызывается для этого преобразования. Модули преобразования Теперь давайте рассмотрим отдельно каждый из модулей перевода. Эти модули или командные файлы языка shell используют команду UNIX bc, чтобы осуществлять преобразования оснований систем счисления. Нельзя сказать, что команда bc - это наиболее простой и удобный способ перевода, но тем не менее она работает, и единственное, что нам нужно, - это изучить ее и поместить в командный файл. --------------------------------------------------------------------- Название : dtoh --------------------------------------------------------------------- dtoh Десятичные в шестнадцатиричные. Назначение: Преобразовывает входные десятичные числа в выходные шестнадцатиричные числа. Синтаксис: $DEC="decimal_number"; HEX='.dtoh' Пример вызова $DEC="25";HEX='.dtoh' $echo $HEX Присвоить DEC начальное значение 25, вызвать dtoh для его преобразования и записать результат в HEX. Вывести результаты на экран. Исходный текст для dtoh 1 : 2 # @(#) dtoh v1.0 Преобразование языка shell--десятичные в шестнадцатиричные Автор: Russ Sage 3 4 bc < HEX $ echo "шестнадцатиричное число: 'cat HEX'" Обозначение () запускает вызываемую процедуру в дочернем языке shell. Используя "." для ее выполнения, мы по прежнему имеем доступ к переменной DEC. Стандартный вывод перенаправляется в HEX. Эхо сопровождение получает значение, используя командную подстановку. Результат cat помещается в эхо предложение. --------------------------------------------------------------------- Название: dtoo --------------------------------------------------------------------- dtoo Десятичные в восьмеричные Назначение Переводит входные десятичные числа в выходные восьмеричные. Синтаксис: DEC="decimal_number"; OCT='.dtoo' Пример вызова $DEC="16";OCT='.dtoo' - 7 - $echo $OCT Присвоить DEC начальное значение 16, вызвать dtoo для ее преобразования и записать результат в OCT. Вывести результаты на экран. Исходный текст для dtoo 1 : 2 # @(#) dtoo v1.0 Преобразование языка shell--десятичные в восьмеричные Автор: Russ Sage bc < idfile Команда ls запускается как дочерний shell, используя обозначение (). Дочерний shell помещается в фоновый режим, используя символ & . Когда результат процесса id отображен, он направляется в файл ошибок дочернего языка shell, который его выполняет. Мы просто перенаправля- ем стандартную ошибку в файл и получаем число! Теперь мы можем сде- лать что-нибудь типа: $ kill -9 'cat idfile' где процесс id, переданный kill, генерируется из команды cat, которая печатает процесс id, захваченный ранее. Это может дать программам опцию "kill self", где они могут отслеживать их id, чтобы вам не пришлось это делать. Программа watch, которую мы видели в главе 6 делает нечто подобное. Встроенный ввод Редактор vi удобен до тех пор, пока Вам не нужно делать построчное редактирование текста или редактирование в командном режиме. Sed тоже неплохой редактор, но в нем не предусмотрена возможность перемещения по файлу. Sed может перемещаться только вперед по файлу до конца файла. Все наши проблемы может решить скромный редактор ed. С редактором ed Еd является интерактивным редактором и в нем есть все необходимое для обработки выражений. Поскольку он читает стандартный ввод для своих команд, мы можем помещать в stdin встроенный текст для - 12 - управления собственно редактором. Еd читает команды, как если бы они были даны с клавиатуры. Он не знает, что запущен в командном режиме. Это открывает совершенно новый способ использования мощи интерактивного редактирования в командном режиме. В качестве примера рассмотрим следующую программу. Помните, что все специальные символы в языке shell должны быть заключены в кавыч- ки, например $. Если они не заключены в кавычки, то ввод будет некор- ректным. ed file << -! 1, \$s/^ *// w q ! В этом примере редактируется файл под названием "file" и над ним выполняется несколько команд. Первая команда говорит "От первой строки до последней, для каждой строки, имеющей пустые символы в начале строки, за которыми следует любое количество таких же символов, заменить эти символы "ничем". Запишите файл и выйдите." Эта процедура удаляет пробелы из начала строки. С файлом a.out Возможность встроенного текста также можно использовать, чтобы автоматизировать запуск программ. Вам нужно записать входные данные, необходимые, чтобы программа выполняла желаемую задачу, и поместить их в текст программы. (Это нечто вроде построения макросов клавиатуры для прикладных программ для PC). В следующем примере исполняемый файл a.out запускается как дочерний shell. Его ввод берется из самого файла, а вывод передается команде more, так что мы можем сделать постраничный вывод. $ (a.out < text > input > lines > ! ) | more Это можно напечатать непосредственно с клавиатуры. Мы используем символы скобок, поскольку, если непосредственно печатать этот код, shell будет выдавать подсказку PS2 вплоть до знака !, затем выполнит команду. Единственное, что мы можем сделать, чтобы он не вышел автоматически - это продолжать запрашивать ввод, опуская его на уровень ниже. C архивами языка shell Архивы языка shell - это один из самых простых способов упако- вать текст в самоустанавливающуюся программу. Идея состоит в том, что мы используем командный файл языка shell, чтобы упаковать некоторый текст. Этот текст может быть документом, командным файлом или даже исходным текстом программы. Мы используем конструкцию встроенного текста, чтобы передать текст в shell, который потом пересылает его в предопределенные файлы. Ниже приведен пример архива, который может быть в файле. $ cat archive - 13 - # # Это архивный файл текстовых файлов 1, 2 и 3 # echo "извлекаем текстовый файл 1" cat > text1.sh << ! # # Это пример текстового файла 1 # who | sort ! echo "извлекаем текстовый файл 2" cat > text2 << ! Это содержимое второго файла. Это не программа, а просто текст. Заметьте, что ему не нужно строк комментария, потому что запущенный shell знает, что это ввод. Но не пытайтесь запускать text2, т.к. у Вас все равно ничего не получится. ! echo "извлекаем текстовый файл 1" cat > text3.c << ! /* Это содержимое файла 3, Си программа */ main() { printf("hello world"); } ! # # конец архивного файла # При выполнении архив проходит через три команды cat. Первая ко- манда cat создает файл text1.sh (командный файл языка shell), text2 (обычный текст) и text3.c (Си-программа). Все это выполняется после того, как Вы наберете на клавиатуре "archive". Это удобно, если нужно перенести текст в другое место. Вместо того чтобы пересылать три фай- ла, нам нужно переслать один. Вместо трех файлов, соединенных вместе, у нас три отдельно упакованнных файла, каждый из которых восстанавли- вает себя при запуске архива. Таким образом, нам не придется гадать, пытаясь представить какой текст в какой файл попадет. Управление статусом цикла Иногда условные выражения цикла нужно подбирать специальным образом, чтобы они отвечали нашим потребностям. Это происходит не часто, однако бывают ситуации, когда Вы можете захотеть использовать определенный синтаксис. В таблице 10-1 приведены три разных способа сделать условие цикла while "истинным". Помните, что shell ищет успешный статус выхода - статус (0) из последней синхронно выполняемой команды. Таблица 10-1 Способы заставить цикл быть "истинным" +--------------+----------------------------------------+ | Цикл | Условие со значением "истина" | - 14 - +--------------+----------------------------------------+ |while true |True - это команда в /bin, которая воз-| | |вращает статус 0 | |while[1 -eq 1]|Мы используем здесь тестовую команду, | | |чтобы возвратить статус 0 | |while : |Мы используем встроенное предложение | | |shell'а, чтобы возвратить статус 0 | +--------------+----------------------------------------+ Фильтры и синтаксис Ранее в этой книге уже шла речь о фильтрах. Не все команды являются фильтрами или могут быть использованы в качестве фильтров. Вспомните определение фильтра - это команда, которая берет ввод из аргументов командной строки, если они есть. Иначе ввод читается из стандартного ввода. Почему все команды не могут действовать как фильтры? Потому, что они не предназначены для этого. Возьмем, например, ls. Что делает ls? Она выдает список файлов в текущем каталоге. Если мы говорим "ls file", он выдает информацию только для этого файла. Если мы говорим "echo file | ls", ls не дает информацию о файле, но выдает список файлов в текущем каталоге, потому что ls не смотрит в стандартный ввод, если в командной строке нет аргументов. Один важный момент, связанный с фильтрами - это способ их вызова. Если Вы помещаете имя файла в командную строку, фильтр открывает файл и читает данные. Помните, что фильтры хотят читать данные. Если Вы присоединяете стандартный ввод к фильтру, он думает, что то, что он читает из программного канала- это данные. Если Вы дадите фильтру имена файлов, Вы не получите того результата, который ожидаете. Давайте рассмотрим несколько примеров. Команда UNIX wc - это фильтр. Мы можем вызывать ее как "wc file1 file2 file3", чтобы она подсчитала слова в трех файлах. Что было бы, если бы мы сказали: "ls file1 file2 file3 | wc" ? Wc подсчитала бы сумму символов, которые выдала бы ls. В данном случае в строке file1, file2, и file3 - 15 символов. Как нам получить реальные файловые данные, а не имена файлов в wc? Изменяя способ, которым мы вызываем wc: cat file1 file2 file3 | wс Путем предварительного соединения файлов, данные передаются на вход wc, и wc суммирует подсчитанные суммы символов, содержимого файлов. Та же самая концепция применима для всех команд фильтров. Еще одной подобной командой является awk. Мы можем сказать "awk file", и команда прочитает содержимое файла, или "cat file | awk", и получим такой же результат. Мы не можем использовать синтаксис "ls file | awk", т.к. awk выполняет свою программу только над символами в имени "file". Недостатки/особенности программирования на языке shell В этом разделе мы рассмотрим некоторые недочеты языка shell. До конца пока не ясно, почему проявляются эти дефекты или особенности. Так уж shell работает и такой уж он на самом деле. Программа для перенаправления ошибки 1 : 2 # @(#) перенаправление ошибочного значения переменной в цикл, присоединенный к программному каналу 3 4 N=1 5 echo "начальное значение N = $N" 6 7 echo "1\n2\n3" | while read LINE 8 do 9 N=2 10 echo "значение в цикле N = $N" 11 done 12 13 echo "конечное значение N = $N" Программа показывает, что различные присвоения, сделанные в до- черних языках shell, не распространяются на их родителей. Строка 4 присваивает N начальное значени 1. Затем значение N отображается в строке 5 для проверки. Вся хитрость этой программы заключена в строке 5. Мы отправляем символы "1 новая строка 2 новая строка 3" в прог- раммный канал и даем это на вход циклу while. Таким образом, мы заставляем цикл выполнить три итерации. Присоединяя вывод к программ- ному каналу, мы создаем дочерний shell, чтобы выполнить цикл while. Внутри цикла while мы изменяем значение N и печатаем его для провер- ки. В конце цикла мы печатаем окончательное значение N. Оно больше не равно 2 как это было внутри цикла, а равно 1, как это было после первого присвоения. Ниже представлен пример прогона этой программы. $ redir начально значение N = 1 значение в цикле N = 2 значение в цикле N = 2 значение в цикле N = 2 конечное значени N = 1 Это показывает, что значение переменной передается вниз дочернему языку shell, но изменения в дочернем shell не передаются родительско- му. Некорректный код возврата Откуда shell знает, являются ли корректными те или иные коды возврата? На это сложно ответить. Иногда оказывается, что код ошибочен, когда ошибку содержит ваша программа. Например: 1 : 2 # @(#) ошибка кода возврата 3 4 echo "Введите команду : \с" 5 read CMD 6 7 eval $CMD 8 echo "\$? = $?" 9 10 if [ $? -eq 0 ] 11 then echo хороший выход - $? 12 else echo плохой выход - $? 13 fi Программа начинается с того, что в строке 4 Вам выдается подсказка для введения команды. Команда читается, строка 7 оценивает не требуются ли ей дополнительные переменные и выполняет ее. Помните, что нам нужно оценить параметр в том случае, если кто-нибудь сказал что-то типа "echo $HOME". Если команды eval нет, то печатается - 16 - литеральная строка $HOME. После команды eval печатается действительное значение $HOME. Так что нам приходится использовать команду eval в этой ситуации. После того как команла выполнена строка печатает статус выхода, ссылаясь на $?. Это совершенно нормально. Строка 10 затем использует тестовую команду, чтобы программа разветвилась в зависимости от кода возврата. Это и есть то место, где ошибка. Величина, которую видит тестовая команда не совпадает с той, которую печатает эхо. $ status введите команду: ls -z ls : illegal option --z usage : -1ACFRabcdfglmnopqrstux [files] $? = 2 хороший выход - 0 Это показывает, что ls запустили в ошибочном режиме. Ls напечатал свое сообщение об ошибке и возвратил код возврата равный 2. Однако тестовая команда видит $? как значение 0 и выбирает ветвь истина. Можете ли вы найти ошибку в строке 8 программы ? Это хорошая нота для окончания раздела об ошибках. Хитрости редактора Vi Одна из самых замечательных особенностей UNIX - это возможность выйти из програм так, чтобы Вы могли запускать другие команды вне языка shell. Это было разработано внутри UNIX и является простым и мощным средством. Использование некоторых приемов, описанных ниже мо- жет сделать разработку программы более простой и быстрой. Возвращение в shell Возвращение в shell - очень полезная возможность в редакторе vi. Вы можете записать Вашу программу в редакторе, выйти из него, запустить программу, вернуться назад в редактор и т.д. Этот цикл редактирование - трансляция - проверка может быть выполнен из редактора. С этими возможностями входа и выхода из редактора Вы можете закончить сеанс работы без реального уничтожения редактора. Редактор vi также является редактором ex. Vi - это визуальная часть ex. Следовательно, Вы можете выйти в shell двумя путями. Первый - используя переменную sh, которая установлена в редакторе ex. Вы можете набрать : sh пока вы в vi или просто "sh", если Вы в ex. Редактор прямо покидает shell, который Вы определили в переменной sh. Откуда редактор знает какой shell Вы запускаете? Это можно определить по регистрационной переменной окружения языка shell. Если Ваш shell - /bin/sh, а Вы хо- тите запустить /bin/shV, Вы можете переустановить значение переменной, напечатав ":set sh=/bin/shV" Другой способ выхода из vi - это с использованием синтаксиса: :!sh где "sh" дает Вам shell (/bin/sh). Обратите внимание, что происходит. Вы запускаете shell (запускаемый по :!), которому дана команда запустить shell (:!sh). Когда Вы, наконец, запукаете этот shell, получается, что у Вас запущен лишний shell. Это очень наглядно представлено в листинге ps, приведенном ниже - 17 - UID PID PPID C STIME TTY TIME COMMAND russ 35 1 0 Jul 5 co 0:50 -shv russ 1233 35 0 04:30:15 co 0:57 vi file russ 1237 1233 0 04:43:13 co 0:01 sh -c sh russ 1238 1237 0 04:43:15 co 0:02 sh В третьей строке все сказано. Из vi вы запустили shell с опцией -с для запуска языка shell . Это бесполезная трата целого shell! А если использовать указанный выше синтаксис или просто ":sh", то такая ситуация не возникнет. Поддержка Escape Кроме того, что редактор vi можно покинуть по Esc, он поддерживает некоторые другие возможности для выхода. Обладая различными возможностями выхода, инструментальные средства могут выполнять для Вас большую часть работы. Первый вариант синтаксиса - ":!cmd", который является префиксом для запуска любой команды вне редактора. В этом случае команда может быть любой командой раздела (1). Второй вариант синтаксиса - это ":!!". Это означает выйти (:!) и использовать последнюю командную строку как аргумент для запуска в новом языке shell. Например, если мы сказали: ":!ls", потом ":!!", :ls будет запущена опять. Второй ! ссылается ко всей предыдущей ко- мандной строке. Третий вариант синтаксиса - это ":!%". Это означает выйти (:!) и запустить команду, имя которой является именем этого файла (%). Когда вы нажимаете возврат каретки, % - замещается именем файла, что очень удобно при редактировании командных файлов. Вы можете сделать что-нибудь типа: $ vi tool . . . edit . . . :w Вы вызываете vi с именем файла, так что vi запоминает имя "tool" в своем буфере. Вы можете изменить что-то прямо тут в редакторе, записать изменения на диск, затем запустить новую копию файла. Редактор заполняет файл с именем "tool" и запускает его. Когда Вы выходите из исполняемого файла "tool", Вы попадаете назад в редактор, готовые внести изменения в текст и запустить программу снова. Одна из хороших последовательностей - это отредактировать файл, сделать изменения, записать их, запустить файл, используя %, внести изменения еще раз, перезапустить программу, напечатав :!!, что перезапускает последнюю команду escape, :!% . Таким образом цикл выходов и запусков программы образует три нажатия клавиатуры, :!!. Мы даже можем использовать эту возможность для компиляции программ на С. Если у нас есть командный файл, который называется "cg" (генератор компиляции) мы можем проще использовать vi: F = 'echo $ 1 | sed -e "s/^\(.*\).c$/\1"' cc $1 -o $F Потом мы можем выполнить последовательность такого типа: $ vi test.c ...edit... :!cg % или то же самое короче - 18 - : !cg test.c и заканчивается созданием исполняемого модуля "test". Макросы Другая возможность vi, которая поддерживает простой выход, - это механизм макросов. Хотя главным образом макросы нужны для того, чтобы можно было помещать команды редактирования в именованные регистры, которые часто используются. Таким образом вместо того, чтобы использовать синтаксис опять и опять, Вы просто используете макрос. Ниже приведен пример макроподстановки: i s/^[^ ]*/ [^ ]*/ "add @a Сначала нужно перейти в режим вставки, чтобы мы могли поместить команду в наш файл редактора. Мы печатаем команду подстановки и нажимаем ESC, чтобы закончить работу в режиме вставки. Команда подстановки говорит "В строках, которые начинаются с непустого символа, за которым следуют один или несколько символов такого же типа, поставить пробел перед непустой последовательностью символов". Далее мы печатаем "add", где "а" обозначает именованный регистр a и dd обозначает переместить строку в буфер. Теперь строка подстановки находится в буфере а. Чтобы ее выполнить мы просто напечатаем @a в командном режиме vi. Чтобы выйти мы можем выполнить ту же последовательность действий, но поместить команду типа :!ps -ef в редактор и переписать ее в буфер. Потом, когда мы говорим @a, мы входим в shell и запускаем команду ps. Команды такого типа можно помещать в именованные буферы от a-z. Последний способ использования макросов для поддержки выхода - это через команду map. Эта команда есть в ex и адресуется предшествующим двоеточием : из vi. Синтаксис для команды map выглядит так: :map lhs rhs Это устанавливает отображение левой стороны на правую сторону. Пример присвоения выглядит так: :map w :!who^M Теперь каждый раз, когда Вы печатаете w, будет выполняться действие выхода через ex, печататься команда who, потом печататься возврат каретки, который отправляет всю эту последовательность на выполнение. Все это по одному нажатию клавиатуры. Самое смешное начинается, когда Ваш терминал имеет функциональные клавиши. Vi обращается к функциональным клавишам по #0 -#9. Теперь мы можем зарезервировать функциональные клавиши для команд выхода. Простое присвоение будет: :map #1 :!ps -ef^ Каждый раз, когда Вы нажимаете функциональную клавишу F1, - 19 - запускается команда ps -ef. Команда "One-Liners" - крошечная, но мощная Следующий список - это подборка обычных команд, использованных таким образом, чтобы получить мощный эффект. Как мастер военного искусства, который убивает голыми руками, мастер UNIX часто может сложить вместе несколько обычных команд и достичь колоссального эффекта. Некоторые строки, которые будут представлены ниже, можно встретить в других местах этой книги, но они приводятся здесь повторно для облегчения поиска. Строки группируются в соответствии с командой, которая является ключевой в данной строке, однако, иногда сложно выделить такую команду как, например, в случае программного канала, когда Вы с двух сторон имеете важные команды. Заметьте, что некоторые из этих команд являются стандартными командами UNIX, в то время как другие - это командные файлы и программы, представленные ранее в этой книге. ACCTCOM * прочитать всю Вашу учетную информацию, начиная с последней команды. acctcom -b -u$LOGNAME * показать все учетные записи, запущенные с Вашего терминала и того, который запущен как суперпользователь acctcom -u# -l'tty' BANNER * напечатать сообщение на трех строках banner "line 1" "line2" "line3" * напечатать день недели и дату на одной строке, время на другой banner "'date|cut -d' ' -f1,3'" "'date|cut -d''-f4'" * послать сообщение на экран другого пользователя banner "Привет" "там" > /dev/tty01 BASENAME * очистить путь echo "Я за устройством 'basename\'tty\' ' '' BC * передать формулу на вход команде bc, которая должна выполнить умножение и присвоит результат PROD PROD = 'echo $NUM1 * $NUM2 | bc' CAT - 20 - * передать символы с клавиатуры в файл cat > file (печатать пока не встретится символ ^D для прекращения чтения) * получить ввод из конструкции "документ здесь" cat << -! Это образец текста, который печатается на экране ! CC * множественная компиляция в фоновом режиме из одной командной строки cc file1.c & cc file2 & cc file3.c & CD * перейти в каталаг, в котором находится файл cd 'имя директория\'путь файл\'' * перейти в каталог, который записан в переменной DESTINATION="/usr/bin" cd $DESTINATION * перейти в каталог, записанный в файле cd 'cat dest_file' CHMOD * включить бит исполнения файла chmod +x file * включить бит смены идентификатора пользователя и разрешить всем выполнение файла chmod 4755 file * установить бит sticky во включенное состояние chmod 1755 file CHOWN * установить себя владельцем файла chown $LOGMAME files * то же самое другим способом chown 'who am i| cut -d' '-f1' files * изменить право собственности для дерева - 21 - cd dest find . -print | sort | while read FILE do chown russ $FILE done CP * скопировать три уровня файлов в один вкаталог /tmp cp */*/* /tmp * то же самое другим способом cp 'find . -type f -print' /tmp * переключить пользовательское предложение cp -z CPIO * переместить дерево системы файлов в новое местоположение cd $SRC find .-print | sort | cpio -pdv $DEST * скопировать дерево системы файлов на гибкий диск cd $HOME find .-print | sort | cpio -ocBv > /dev/rfd0 * восстановить копию на стримере cd $DEST cpio -icBvt < /dev/rfd0 * выполнить "ls -l" на копии стримера cpio -icBvt < /dev/rfd0 CRON * запустите Ваш генератор сообщений о статусе каждый четверг в 6:00 a.m. 0 6 * * 4 /usr/russ/bin/status_msg * chmod на файл паролей * * * * * /bin/su root -c "chmod 777 /etc/passwd" CU * непосредственно обратиться последовательному порту на скорости 1200 бод cu -ltty00 dir * непосредственно обратиться к последовательному порту на скорости 9600 бод cu -ltty00 -s9600 dir * автоматически вызвать другую систему, используя комбинацию dial/modem cu -acua0 555-1212 - 22 - CUT * отсечь первое поле из файла passwd cut -d: -f1 /etc/passwd * отсечь имя из листинга команды who who | cut -d' ' -f1 who | awk '{print $1} DD * полная гибкая копия дорожка за дорожкой dd if = /dev/fd0 of=/dev/fd1 DOS * скопировать все файлы данного каталога на дискету DOS doscp * a: * скопировать все файлы с дискеты DOS в данный каталог dosls a: > /tmp/dosf for FILE in 'cat /tmp/dosf' do doscp a:$FILE done DU * выдать общее количество блоков для всех каталогов в / du -s /* * напечатать количество использованного места в каталоге каждого пользователя echo "total bytes: 'expr\'du -s $1\' \* 512'" ECHO * напечатать значение переменной shell'а echo $PATH $CDPATH * напечатать вывод вперемешку с обычным текстом echo " Мое имя $LOGNAME или 'logname' или 'who am i|cut -d' ' -f1'" * напечатать символы упраления в кавычках и без echo "\n\t Это записано в кавычках" echo \\n\\t Это записано без кавычек * напечатать и оставить курсор в конце той же строки - 23 - echo -n "prompt: " echo "prompt: \c" ED * запустить ed автоматичски с конструкцией "документ здесь" ed /etc/passwd <<-! 1,$p g/root/s//noroot w q ! EXPR * умножить два числа expr 512 \* 1024 * увеличить переменную на предопределенное значение x = 0; INC = 5 X='expr $X + $INC' FILE * найти все текстовые файлы file * | fgrep text * напечатать имена только текстовых файлов file * | fgrep text | cut -d: -f1 * more все текстовые файлы more 'file * | fgrep text | cut -d: -f1' FIND * найти все файлы в системе find / -print | sort * найти все файлы и распечатать список в формате long find / -exec ls -ld {} \; * напечатать имена всех регулярных файлов find / -type f print * найдите все каталоги и распечатайте содержимое find / -type d print | while read DIR do echo "listing $DIR" ls $DIR done * найдите все файлы, которые были модифицированы в последние 24 часа и распечатайте их список в формате long find / -atime -0 -exec ls -ld {} \; - 24 - * найдите все файлы setuid и setgid find / -perm -4000 -o -perm -2000 -exec ls -ld {} \; FINGER * укажите всех пользователей, вышедших из системы finger 'who | cut -d' ' -f1' * укажите всех пользователей в файле passwd cut -d: -f1 /etc/passwd | while read NAME do finger $NAME done GREP * найти случаи употребления шестнадцатиричных чисел в файле данных od -x datafile | grep 'A3F09' * найти свое имя в системе find / -type f print | while read FILE do grep "russ" $FILE /dev/null done HEAD * озаглавьте все текстовые файлы в текущем каталоге file * | fgrep text | cut -d: -f1 | while read FILE do echo "--------" echo "$FILE" echo "--------" head "$FILE" ID * определить, кто в данный момент является суперпользователем if [ "'id'" = "uid=0(root) gid=0(root)" ] then echo "you are root" fi * то же самое другим способом if id | fgrep root > /dev/null then echo "you are root" fi KILL * уничтожьте себя (выгрузите) kill -9 0 kill -9 $$ * завершите работу системы kill -1 1 * уничтожьте последний процесс, запущенный в фоновом режиме kill -9 $! - 25 - * уничтожьте процесс, идентификатор которого находится в файле kill -9 'cat idfile' LINE * взять строку с терминала LINE='line < /dev/tty' * взять строку из стандартного ввода cat datafile | while LINE = 'line' do echo $LINE done LOGIN * перейти из сгенерированной подсказки login ??? login: ^d login: * получить некоторую внутреннюю информацию (программа strings - это BSD) strings /bin/login | more LOGNAME * напечатать информацию о своем пароле grep '^'logname ':' /etc/passwd * получить информацию о своем процессе ps -fu 'logname' LS * выдать список скрытых файлов ls -ad .* * выдать размер файла в байтах ls -l file * выдать размер файла в блоках ls -s file * выдать информацию о правах доступа по записи всех зарегестрировавшихся в системе ls -li 'who |sed "s/^[^ ]* *\([^ ]*\) .*$/\/dev\/\1/p"' * получить помощь по испоьзованию команды ls -z * выдать список только каталогов ls -al |grep "^d" MAIL * послать почту всем пользователям - 26 - cut -d: -f1 |while read USER do echo "mailing to $USER" mail $USER done * послать почту из файла mail russ < /etc/passwd * послать почту из программного канала echo "Это текст почты" | mail russ MORE * напечатать все файлы текущего каталога more * * напечатать 10 строк за раз more -10 file cat file | more -10 MKDIR * опуститься на максимальную глубину while : do mkdir x cd x done * то же самое другим способом PATH="x" while : do mkdir $PATH PATH="$PATH/x" done NCHECK * найти все файлы, присоединенные к vi ls -li /bin/vi 40 -rwwx--x--t 1109344 Feb 14 1985 /bin/vi ncheck -i 40 /dev/root * найти все файлы установки идентификатора пользователя ncheck -s NM * посмотреть символьные таблицы всех nonstripped исполняемых файлов nm 'file *| grep "not stripped"|sed "s/^\(.*\):.*$/\1/" OD * посмотреть символы в именах файлов в текущем каталоге - 27 - od -c . * напечатать значение функциональных клавиш, комбинаций клавиш, и.т.д. od -cb (нажмите комбинацию клавиш) ^d (печатает строку) (нажмите что-нибудь еще) ^d (печатает следующую строку) ^d (выыходит из od) * сделать дамп копии на стримере od -c /dev/rfd0 * сделать дамп файловой системы od -c /dev/root PASSWD * как суперпользователь Вы можете установить в качестве пароля любую строку # passwd russ Changing password for russ (Изменение пароля для russ) Enter new password (minimum of 5 characters) (Введите новый пароль (минимум 5 символов)) Please use combination of upper, lowercase letters and numbers (Просьба использовать комбинации чисел и букв в верхнем и нижнем регистрах) New password: junk (Новый пароль: junk) Re-enter new password: junk (Новый пароль: junk) # * как обычный пользователь Вы должны будете вводить пароль с учетом количественных ограничений и ограничений по длине $ passwd russ Changing password for russ (Изменение пароля для russ) Enter new password (minimum of 5 characters) (Введите новый пароль (минимум 5 символов)) Please use combination of upper, lowercase letters and numbers (Просьба использовать комбинации чисел и букв в верхнем и нижнем регистрах) New password: junk (Новый пароль: junk) Too short. Password unchanged. (Слишком короткий. Пароль не изменен) $ PR * вывести многоколоночный список имен файлов ls $@ | pr -5t * напечатать файлы из списка - 28 - pr 'find . -name "*.c" -print | sort' PS * напечатать полную информацию обо всех активных процессах ps -aef * напечатать информацию обо всех процессах, управляемых Вашим терминалом ps -f * напечатать информацию о процессах, связанных с терминалом tty00 ps -ft00 * напечатать информацию о процессах, связанных с пользователем russ ps -furuss * BSD синтаксис для печати всех процессов ps -aux * BSD синтаксис для печати всех процессов, связанных с терминальным устройством ps -xut00 PWD * сохранить текущий рабочий каталог PWD='pwd' * вернуться в ранее сохраненный рабочий каталог cd $PWD RM * удалить все файлы, кроме каталогов с файлами rm * * удалить пустые каталоги rmdir dirs * удалить каталоги, имеющие файлы rm -r dirs * удалить все файлы в режиме, когда система не будет задавать никаких вопросов rm -rf * * удалить каждый файл в системе по отдельности rm -rf / SH * прочитать список поэлементно for ELEMENT in 'cat /etc/motd' do - 29 - echo $ELEMENT done * прочитать список построчно cat /etc/motd | while read LINE do echo $LINE done * цикл пока - навсегда (while-forever) while : do echo $PS1 read CMD case $CMD in "") break;; esac done * управляемый цикл while read CMD while [ "$CMD" != ""] do case $CMD in user-cmd) do_it;; esac echo $PS1 read CMD done * переполнениие тестового стека при обработке прерывания trap "echo trapping; kill $$" 2 3 15 * выгрузка из языка shell несколькими способами exit eof character (usually control -d) kill -9 0 kill -9 $$ STTY * посмотрите все свои установки stty -a * посмотрите терминальные установки другого терминала stty -a < /dev/tty01 * установить передачу бод на другую скорость для другого терминала stty 300 < /dev/tty01 * динамически установить control-A как клавишу прерывания stty intr ^a * включить эхо-сопровождение терминала stty -echo - 30 - SU * тестовый цикл для уничтожения легких паролей awk '{FS =":"; print $1,$5} '/etc/passwd|while read N C do echo "\n$N\t$C" su $N done TAIL * проследить в реальном времени запись транзакций файла входа в систему (logfile) uucp tail -f /usr/spool/uucp/LOGFILE * посмотреть последнюю строку файла tail -1 file * посмотреть последние 10 символов переменной echo "$VAR" | tail -10c TAR * сделать копии файлов в Вашем home-каталоге не разрывая файл, но формируя копии на куск