Книга: Сценарии командной оболочки. Linux, OS X и Unix. 2-е издание
Назад: Глава 10. Администрирование интернет-сервера
Дальше: Глава 12. Сценарии для игр и развлечений

Глава 11. Сценарии для OS X

Одним из важнейших событий в мире Unix и Unix-подобных операционных систем стал выпуск полностью переписанной системы OS X, основанной на надежном ядре Unix с названием Darwin. Darwin — это версия Unix с открытым исходным кодом, берущая свое начало в BSD Unix. Если вы мало-мальски знакомы с Unix, первое время, открывая приложение Terminal в OS X, вы будете замирать от восхищения. Все, что только можно пожелать, от комплекта инструментов разработки до стандартных утилит Unix, включено в последние версии Mac OS X, имеющей великолепный графический интерфейс, способный скрывать всю ту мощь, к которой вы пока не готовы.

Однако между OS X и Linux/Unix имеются важные различия, поэтому будет нелишним познакомиться с достоинствами OS X, способными помочь вам в вашей повседневной работе. Например, в OS X имеется интересное приложение командной строки, которое называется open и позволяет запускать приложения с графическим интерфейсом из командной строки. Но open не отличается гибкостью. Если, к примеру, вы захотите открыть Microsoft Excel, ввод команды open excel не даст желаемого результата, потому что программа open очень придирчива и в данном случае ожидает, что будет вызвана как open -a "Microsoft Excel". Далее мы напишем сценарий-обертку, который позволит преодолеть эту придирчивость.

ИСПРАВЛЕНИЕ ОКОНЧАНИЙ СТРОК В OS X

Вот еще одна возникающая время от времени ситуация, с которой легко справиться, применив простой трюк. Работая в командной строке с файлами, созданными для использования в графическом интерфейсе Mac, можно обнаружить, что символ конца строки в таких файлах не совпадает с аналогичным символом, необходимым для работы в командной строке. Говоря техническим языком, для обозначения конца строки системы OS X используют символ возврата каретки (обозначается, как \r), тогда как на стороне Unix используется символ перевода строки (\n). Поэтому при попытке вывести такой файл в программе Terminal, он будет показан в одну строку, без переносов в соответствующих местах.

У вас есть файл, с которым наблюдается подобная проблема? Ниже показано, что можно увидеть, если попробовать вывести содержимое такого файла коман­дой cat.

$ cat mac-format-file.txt

$

И все же вы знаете, что файл не пуст. Чтобы увидеть содержимое, вызовите команду cat с флагом -v, which который сделает видимыми иначе скрытые управляющие символы. В этом случае вы увидите что-то похожее:

$ cat -v mac-format-file.txt

The rain in Spain^Mfalls mainly on^Mthe plain.^MNo kidding. It does.^M $

Очевидно, что здесь что-то не так! К счастью можно воспользоваться командой tr и с ее помощью заменить символы возврата каретки символами перевода строки.

$ tr '\r' '\n' < mac-format-file.txt > unix-format-file.txt

Применение этой команды к типичному файлу дает более осмысленным результат.

$ tr '\r' '\n' < mac-format-file.txt

The rain in Spain

falls mainly on

the plain.

No kidding. It does.

Если, открыв файл в приложении для Mac, таком как Microsoft Word, вы видите ступеньки, убегающие вправо, попробуйте выполнить обратную замену символа, обозначающего конец строки — для приложений с графическим интерфейсом Aqua.

$ tr '\n' '\r' < unixfile.txt > macfile.txt

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

А теперь перейдем к делу, хорошо?

№ 79. Автоматизация захвата изображения экрана

Если вы пользуетесь Mac уже некоторое время, то наверняка знаете о встроенной функции захвата изображения с экрана, привязанной к комбинации клавиш -SHIFT-3. C той же целью можно использовать утилиты OS X, Preview и Grab, находящиеся в папках Applications (Программы) и Utilities (Утилиты) соответственно, а кроме того, существует богатый выбор сторонних программ.

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

$ screencapture -h

screencapture: недопустимый параметр -- h

порядок использования: screencapture [-icMPmwsWxSCUtoa] [files]

  -c         поместить снимок экрана в буфер обмена

  -C         сохранить изображение указателя мыши на экране. только              в неинтерактивных режимах

  -d         выводить ошибки в диалоге с графическим интерфейсом

  -i         захватить изображение интерактивно, выбранной области или окна

               клавиша control - заставляет поместить снимок в буфер обмена

               клавиша пробела - переключает между режимами захвата области,

                                 выбранной мышью, и окна

               клавиша escape  - отменяет интерактивный захват изображения экрана

  -m         захватить изображение только на основном мониторе, игнорируется              с флагом -i

  -M         поместить снимок экрана в новое электронное письмо

  -o         в режиме захвата окна не захватывать тень от окна

  -P         открыть снимок экрана в программе Preview

  -s         разрешить только режим захвата выбранной области

  -S         в режиме захвата окна захватить экран, а не окно

  -t<format> формат создаваемого изображения, по умолчанию png

             (поддерживаются также pdf, jpg, tiff и другие форматы)

  -T<seconds> Выполнить захват с задержкой <seconds> секунд, по умолчанию 5

  -w         разрешить только режим захвата окна

  -W         начать взаимодействие в режиме захвата окна

  -x         не проигрывать звуки

  -a         не включать окна, присоединенные к выбранным окнам

  -r         не добавлять метаданные о разрешении (dpi) в изображение

  -l<windowid> захватить окно с идентификатором <windowed>

  -R<x,y,w,h> захватить указанную область на экране

  files  где сохранить снимок экрана, 1 на экран

Это приложение так и просит написать для него сценарий-обертку. Например, чтобы сделать снимок экрана с задержкой 30 секунд, можно использовать следующую команду:

$ sleep 30; screencapture capture.tiff

Но давайте придумаем что-нибудь поинтереснее, согласны?

Код

Листинг 11.1 демонстрирует, как можно автоматизировать работу с утилитой screencapture для скрытного создания снимков экрана.

Листинг 11.1. Сценарий-обертка screencapture2

  #!/bin/bash

  # screencapture2 -- использует команду screencapture в OS X для создания

  #   серии скриншотов главного окна в скрытном режиме. Удобно, если вы

  #   находитесь в сомнительном вычислительном окружении!

  capture="$(which screencapture) -x -m -C"

  freq=60     # Каждые 60 секунд.

  maxshots=30  # Максимальное число скриншотов.

  animate=0   # Создать анимированный gif? Нет.

  while getopts "af:m" opt; do

    case $opt in

      a ) animate=1;        ;;

      f ) freq=$OPTARG;     ;;

      m ) maxshots=$OPTARG; ;; # Завершить после создания заданного числа снимков.

      ? ) echo "Usage: $0 [-a] [-f frequency] [-m maxcaps]" >&2

          exit 1

    esac

  done

  counter=0

  while [ $counter -lt $maxshots ] ; do

    $capture capture${counter}.jpg # Счетчик counter постоянно увеличивается.

    counter=$(( counter + 1 ))

    sleep $freq   # т.е. freq -- число секунд между снимками.

  done

  # Теперь, если требуется, сжать все отдельные снимки в один анимированный GIF.

  if [ $animate -eq 1 ] ; then

    convert -delay 100 -loop 0 -resize "33%" capture* animated-captures.gif

  fi

  # Не возвращать никаких других кодов состояния для скрытности.

  exit 0

Как это работает

Этот сценарий делает снимки экрана через каждые $freq секунд , пока не будет достигнуто количество $maxshots (по умолчанию создается 30 снимков с интервалом 60 секунд между ними). Затем создается серия файлов JPEG, последовательно пронумерованных начиная с 0. Все это может пригодиться, если вы создаете обучающие материалы или хотите определить, пользовался ли кто-то вашим компьютером в ваше отсутствие. Запустите сценарий и увидите, что происходило, если этот «кто-то» не оказался умнее.

Последний раздел сценария самый интересный: при необходимости он создает анимированный GIF в одну треть размера оригинала, используя инструмент преобразования из пакета ImageMagick . Это удобный способ просмотреть сразу все изображения. В главе 14 мы найдем для ImageMagick большое количество применений! Если в вашей системе OS X этот инструмент отсутствует, то с помощью диспетчера пакетов, такого как brew, вы сможете установить его одной командой (brew install imagemagick).

Запуск сценария

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

$ screencapture2 &

$

Вот, собственно, и все. Легко. Чтобы, к примеру, сделать 30 снимков с интервалом 5 секунд между ними, сценарий screencapture2 можно запустить так:

$ screencapture2 -f 5 -m 30 &

$

Результаты

Сценарий ничего не выводит на экран, но создает новые файлы, как показано в листинге 11.2. (Если сценарию передавался флаг -a, появится еще один дополнительный файл.)

Листинг 11.2. Снимки экрана, созданные за период времени сценарием screencapture2

$ ls -s *gif *jpg

4448 animated-captures.gif    4216 capture2.jpg    25728 capture5.jpg

4304 capture0.jpg             4680 capture3.jpg      4456 capture6.jpg

4296 capture1.jpg             4680 capture4.jpg

Усовершенствование сценария

Для мониторинга экрана в течение длительного периода времени необходимо найти какое-то средство, позволяющее определять, когда экран действительно изменяется, чтобы не захламлять дисковое пространство ненужными скриншотами. Существуют сторонние решения, которые позволяют использовать таким образом программу screencapture, — они сохраняют в журнал сведения о времени существенных изменений вместо десятков и сотен копий одного и того же экрана. (Имейте в виду, что, если на экране присутствуют часы, каждый следующий снимок будет немного отличаться от предыдущего, что значительно осложняет решение задачи!)

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

№ 80. Динамическая настройка заголовка терминала

В листинге 11.3 приводится короткий забавный сценарий для пользователей OS X, обожающих работать в программе Terminal. Вместо установки и изменения заголовка окна в диалоге TerminalPreferencesProfilesWindow (ТерминалНастройкиПро­филиОкно) можно воспользоваться этим сценарием, чтобы изменить все, что вам захочется. В данном примере мы сделаем заголовок окна Terminal чуть более информативным, добавив в него вывод текущего рабочего каталога.

Код

Листинг 11.3. Сценарий titleterm

#!/bin/bash

# titleterm -- требует от программы OS X Terminal изменить заголовок окна,

#   согласно значению, переданному этому короткому сценарию в аргументе.

if [ $# -eq 0 ]; then

  echo "Usage: $0 title" >&2

  exit 1

else

  echo -e "\033]0;$@\007"

fi

exit 0

Как это работает

Программа Terminal поддерживает множество разнообразных управляющих последовательностей, и сценарий titleterm посылает последовательность ESC ] 0; title BEL , которая изменяет настройки заголовка, как определено значением аргумента.

Запуск сценария

Чтобы изменить заголовок окна Terminal, просто передайте сценарию titleterm новые настройки заголовка в аргументе командной строки.

Результаты

Сценарий ничего не выводит в процессе работы, как показывает листинг 11.4.

Листинг 11.4. Запуск сценария titleterm для вывода текущего каталога в заголовке окна терминала

$ titleterm $(pwd)

$

Однако он мгновенно добавляет в заголовок окна программы Terminal вывод имени текущего каталога.

Усовершенствование сценария

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

alias precmd 'titleterm "$PWD"' [tcsh]

или для bash:

export PROMPT_COMMAND="titleterm \"\$PWD\"" [bash]

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

№ 81. Создание суммарного списка медиатек iTunes

Если вы пользовались iTunes достаточно продолжительное время, у вас наверняка накопился внушительный список музыкальных произведений, аудио­книг, фильмов и телепередач. К сожалению, при всех своих уникальных возможностях, iTunes не дает простого способа экспортировать список музыкальных произведений в компактном и удобочитаемом формате. Но совсем несложно написать сценарий, восполняющий этот недостаток, как показано в листинге 11.5. Сценарий опирается на параметр настройки Share iTunes XML with other applications («Предоставлять другим программам доступ к файлу XML Медиатеки iTunes»), поэтому перед запуском сценария убедитесь, что соответствующий флажок в настройках iTunes установлен.

Код

Листинг 11.5. Сценарий ituneslist

  #!/bin/bash

  # ituneslist -- Списки из вашей медиатеки iTunes в компактной и удобочитаемой

  #   форме, которыми можно обмениваться с другими или использовать

  #   для синхронизации(с помощью diff) библиотек iTunes с другими компьютерами

  #   и ноутбуками.

  itunehome="$HOME/Music/iTunes"

  ituneconfig="$itunehome/iTunes Music Library.xml"

musiclib="/$(grep '>Music Folder<' "$ituneconfig" | cut -d/ -f5- | \

    cut -d\< -f1 | sed 's/%20/ /g')"

  echo "Your library is at $musiclib"

  if [ ! -d "$musiclib" ] ; then

    echo "$0: Confused: Music library $musiclib isn't a directory?" >&2

    exit 1

  fi

  exec find "$musiclib" -type d -mindepth 2 -maxdepth 2 \! -name '.*' -print \

    | sed "s|$musiclib/||"

Как это работает

Подобно многим современным компьютерным приложениям, iTunes хранит свою медиатеку в стандартном каталоге — в данном случае ~/Music/iTunes/iTunes Media/ — но позволяет переместить ее в любое другое место. Сценарий должен установить местоположение медиатеки, что он и делает, извлекая значение поля Music Folder из файла с настройками iTunes. Именно это значение возвращает конвейер в .

Файл с настройками ($ituneconfig) представляет собой файл с данными в формате XML, поэтому приходится отсечь кое-что лишнее, чтобы извлечь точное значение поля Music Folder. Ниже показано, как выглядит значение iTunes Media в файле с настройками iTunes у Дейва:

file://localhost/Users/taylor/Music/iTunes/iTunes%20Media/

Фактически значение iTunes Media хранится в виде полного URL, что само по себе достаточно интересно, значит, нам нужно отбросить префикс file://localhost/. Эту операцию выполняет первая команда cut. Наконец, из-за того что в OS X имена многих каталогов включают пробелы, и того, что поле Music Folder хранит URL, все пробелы в этом поле преобразованы в последовательность %20. Чтобы преобразовать ее обратно в пробелы, перед обработкой вызывается команда sed.

После определения имени Music Folder остается только сгенерировать списки музыкальных произведений в двух системах Mac, затем с помощью команды diff сравнить их, чтобы увидеть, какие альбомы являются уникальными в той или иной системе, и, возможно, синхронизировать их.

Запуск сценария

Этот сценарий не имеет ни аргументов, ни флагов.

Результаты

Если у вас накопилась большая коллекция музыки, этот сценарий может вывести длинный список. В листинге 11.6 показаны первые 15 строк из коллекции Дейва.

Листинг 11.6. Запуск сценария ituneslist для вывода первых элементов в коллекции iTunes

$ ituneslist | head -15

Your library is at /Users/taylor/Music/iTunes/iTunes Media/

Audiobooks/Andy Weir

Audiobooks/Barbara W. Tuchman

Audiobooks/Bill Bryson

Audiobooks/Douglas Preston

Audiobooks/Marc Seifer

Audiobooks/Paul McGann

Audiobooks/Robert Louis Stevenson

iPod Games/Klondike

Movies/47 Ronin (2013)

Movies/Mad Max (1979)

Movies/Star Trek Into Darkness (2013)

Movies/The Avengers (2012)

Movies/The Expendables 2 (2012)

Movies/The Hobbit The Desolation of Smaug (2013)

Усовершенствование сценария

Это не совсем усовершенствование, но... Поскольку путь к каталогу с медиатекой iTunes хранится как полный URL, было бы интересно попробовать создать в Интернете каталог iTunes и затем указать его URL как значение поля Music Folder в файле XML file.

№ 82. Исправление команды open

Одним из примечательных новшеств OS X, стала команда open, позволяющая легко запускать приложение, соответствующее файлу того или иного типа, будь то графическое изображение, документ PDF или электронная таблица Excel. Проблема команды open заключается в некоторой ее придирчивости. Чтобы запустить приложение по имени, необходимо добавить флаг -a. А если указать неточное имя приложения, команда сообщит об ошибке. Эта задача как раз для сценария-обертки, такого как в листинге 11.7.

Код

Листинг 11.7. Сценарий open2

  #!/bin/bash

  # open2 -- интеллектуальная обертка для крутой команды OS X 'open',

  #   чтобы сделать ее еще практичнее. По умолчанию 'open' запускает

  #   приложение, соответствующее указанному файлу или каталогу,

  #   опираясь на настройки интерфейса Aqua, и может запускать

  #   приложения,только если они находятся в каталоге /Applications.

  # First, whatever argument we're given, try it directly.

  if ! open "$@" >/dev/null 2>&1 ; then

    if ! open -a "$@" >/dev/null 2>&1 ; then

      # Больше одного аргумента? Непонятно, как обрабатывать их -- выйти.

      if [ $# -gt 1 ] ; then

        echo "open: More than one program not supported" >&2

        exit 1

      else

        case $(echo $1 | tr '[:upper:]' '[:lower:]') in

          activ*|cpu   ) app="Activity Monitor"   ;;

          addr*        ) app="Address Book"       ;;

          chat         ) app="Messages"           ;;

          dvd          ) app="DVD Player"         ;;

          excel        ) app="Microsoft Excel"    ;;

          info*        ) app="System Information" ;;

          prefs        ) app="System Preferences" ;;

          qt|quicktime ) app="QuickTime Player"   ;;

          word         ) app="Microsoft Word"     ;;

          *            ) echo "open: Don't know what to do with $1" >&2

              exit 1

        esac

        echo "You asked for $1 but I think you mean $app." >&2

        open -a "$app"

      fi

    fi

  fi

  exit 0

Как это работает

Этот сценарий крутится вокруг нулевого и ненулевого кода, полученного от программы open, которая возвращает ноль в случае успеха и ненулевое значение в случае неудачи .

Если переданный аргумент не является именем файла, программа терпит неудачу и выполняется первое условие. Тогда сценарий пытается интерпретировать аргумент как имя приложения и добавляет в команду open флаг -a. Если программа терпит неудачу во второй раз, сценарий использует инструкцию case , чтобы проверить слова, которые пользователи часто вводят вместо верных названий популярных приложений.

Сценарий даже выводит дружественное сообщение, когда находит совпадение с одним из имен, непосредственно перед запуском приложения.

$ open2 excel

You asked for excel but I think you mean Microsoft Excel.

Запуск сценария

Сценарий open2 готов получить из командной строки одно или несколько имен файлов или приложений.

Результаты

Без этой обертки попытка открыть приложение Microsoft Word терпит неудачу:

$ open "Microsoft Word"

The file /Users/taylor/Desktop//Microsoft Word does not exist.

Угрожающее, казалось бы, сообщение появилось только потому, что пользователь забыл добавить флаг -a. Аналогичная попытка, но со сценарием open2, показывает, что больше нет необходимости помнить о флаге -a:

$ open2 "Microsoft Word"

$

Отсутствие вывода — хороший знак: приложение запущено и готово к использованию. Дополнительная поддержка коротких имен для обычных в OS X приложений обеспечивает успех команды open2 word, тогда как open -a word терпит неудачу.

Усовершенствование сценария

Сценарий определенно выиграл бы, если бы список коротких имен соответствовал вашим личным потребностям или потребностям вашего сообщества пользователей. И это легко достижимо!

Назад: Глава 10. Администрирование интернет-сервера
Дальше: Глава 12. Сценарии для игр и развлечений