шелл скрипты для андроид
Работа с cron под Android и добавление shell-скрипта в автозапуск при загрузке устройства
В связи с тем, что мобильные устройства уже давно имеют обширный функционал, то задачи автоматизации можно смело переносить и на них. И, как нельзя лучше, здесь так же хорошо подходит cron для их выполнения. Но если в «обычных» Linux системах настройка cron занимает мало времени, то Android устройство требует более сложной работы по его настройке.
Если тебе интересна тема автоматизации и ты хочешь, чтобы твои shell-скрипты запускались сразу же после загрузки устройства, да еще и могли бы запускаться по таймеру — добро пожаловать под кат!
Предисловие
Я занимаюсь автоматизацией мобильных устройств под Android. И во время выполнения автоматических скриптов происходит множество непредвиденных ситуаций, даже если для тестирования используются одинаковые устройства.
Самые популярные проблемы:
0. Скрипт автоматизации выполняет не то, что ты хотел
1. Мобильное приложение автоматически выгружается
2. Автоматическая перезагрузка телефона
3. Мобильное приложение автоматически не запускается после перезапуска
4. Wi-Fi модуль произвольно отключается, не находит сеть, не подключается к сети
5. Мобильная сеть неожиданно пропала
6. Телефон ушел в спящий режим
7. Отпал прокси или сам сервер или сервер вернул странный ответ
Из-за этого приходится постоянно следить за устройством и отлавливать эти непредвиденные ситуации.
Таким образом, я пришел к тому, что cron с «правильными» скриптами позволит отследить программные сбои и восстановить скрипт автоматизации или запустить его заново. Но как оказалось, хотя Android содержит ядро Linux, но есть особые нюансы, с которыми пришлось разбираться. Итак, давайте приступим к настройке!
Настройка Cron
Настраиваем окружение
Настройка ручного запуска
4. Создадим простой crontab файл:
Теперь у нас есть задание, которое каждую минуту будет добавлять слово text в файл /sdcard/test.txt
Запускаем: и получаем следующий лог:
Конечно, немного удивляет, ведь если мы выполним команду whoami то в результате она вернёт root.
5. Добавим пользователя root, раз crond просит:
Из-за отсутствия данного файла, я понял, что в Android системе он совсем не задействован. Если вы уверены в том, где вы будете хранить свои crontab файлы, то вы можете заменить строку /system/etc/crontabs на нужную вам. Снова выполняем команду
И получаем следующее:
Хотя, если верить логу задача в crond прописалась, но в моем случае файл не создался. Решить проблему можно очень просто:
Ну хочет он, чтобы существовала там директория, кто мы такие, чтобы ему запрещать! Запускаем снова и видим:
Ошибки ушли, и появилась строка crond: child running /system/bin/sh. Наконец-то cron у нас успешно завелся, и можно переходить ко второй части!
Автоматическая загрузка shell-скрипта
В Linux системе есть директория init.d, которая отвечает за автозапуск сразу же после загрузки системы, поэтому попробуем идти по этому пути!
1. Проверяем, существует ли данная директория у вас на устройстве (это /etc/init.d либо /system/etc/init.d — это тот же смонтированный раздел etc ). В моем случае её нет. Ну что, тогда создаем:
Теперь добавим туда какой-нибудь простенький скрипт, например:
Перезагружаем устройство и смотрим, случилось ли чудо… К сожалению, у меня файл не появился.
Исследуем систему дальше и ищем какой-нибудь init файл, который может запускать скрипты после запуска. У меня на устройстве оказался файл в /init.rc. Ну что, попробуем его изменить и перезагрузим устройство:
Но файл опять не создался. Идем смотреть на файл /init.rc и наша запись пропала и файл как бы и не менялся, т.к. дата создания стоит совсем какая-то странная (в моем случае 01 янв. 70 05:00:00).
Продолжаем разбираться, и оказывается что данный файл храниться в boot.img, и каждый раз достается из него. И для того, чтобы изменить функционал файла init.rc нужно выполнить все это.
Но есть более простой способ, который поможет решить данную задачу. Для этого способа мы можем использовать следующий shell-скрипт (скажем спасибо Ryuinferno):
Приступаем к внедрению скрипта! В моем случае он будет называться init.sh.
1. Загружаем файл на sdcard мобильного устройства:
2. Копируем в память мобильного устройства и устанавливаем нужные права:
3. Запускаем на выполнение:
И обращаем внимание на лог, который выводится. Вот мой лог:
Как видим из лога, ошибок нет, поэтому смело перезагружаем устройство! Возможно у кого-то уже все заработало и вы смогли найти файл /data/Test.log, но у меня его нет. Проверим директорию /system/etc/init.d используя команду ls:
Как видим, задачи успешно созданы. Возможно все же придется менять boot.img, но давайте в начале проверим, а где у нас файл install-recovery.sh с помощью команды
Как можем заметить, у нас 2 файла, которые лежат в разных местах. По дате создания мы можем заметить, что скрипт создал файл в директории /system/etc/install-recovery.sh, хотя, возможно, в некоторых случаях он должен создавать его в /system/etc. Давайте переименуем файл в bin и скопируем файл из etc:
Тут u:object_r:system_file:s0 и является контекстов безопасности.
И снова перезагружаем устройство… И вот, наконец-то долгожданный УСПЕХ! Файл /data/Test.log появился!
Раз все работает, идем в /system/etc/init.d и создаем shell-скрипт. А в нем как-раз запустим наш crond на выполнение:
После загрузки проверяем, запустился ли crond:
И на этом могли бы мы уже закончить, но давайте подождем минуту и посмотрим, произошла ли запись в наш файл… Ну как вы уже поняли, опять ни чего не сработало. Дело в том, что данный процесс нужно запустить от супер пользователя. Изменим скрипт в файле 99cronstart:
UPD: Возможно в вашем случае su будет иметь другой путь, тогда воспользуйтесь командой which su и замените путь, на ваш.
Теперь наше Android устройство поддерживает и задачи cron и может содержать shell-скрипты для автоматического запуска!
Ну и напоследок, скрипт, который будет запускать наше приложение, если его нет в процессах и сохранять информацию о том, что находилось на главном экране до запуска нашего приложения:
Shell-скриптинг в среде Android
Android основан на ядре Linux, включает в себя набор стандартных UNIX-команд и простой шелл sh. Все это значит, что мы можем не только использовать командную строку для выполнения низкоуровневых операций, но и писать шелл-скрипты, которые будут выполнять функции, недоступные из графического интерфейса. В этой статье мы поговорим о том, что с их помощью можно сделать и зачем все это нужно.
Сразу оговорюсь, что в этой статье речь пойдет о шелл-скриптах в традиционном для Linux понимании, без использования инструментов вроде SL4A, QPython или Roboto. Главное назначение таких скриптов — изменение поведения системы, параметров ядра, работа с демонами (ADB, например) и тому подобное. Скрипты могут стартовать на этапе загрузки ОС, установки новой прошивки, после тапа по кнопке или же по традиции — из терминала.
В статье я расскажу, как писать такие скрипты, как заставить их стартовать автоматически, привязывать к определенному системному событию. В качестве бонуса также объясню, как заставить консоль восстановления (recovery) выполнить необходимые тебе действия перед установкой или сразу после установки новой прошивки. Начинаем.
Особенности Android-окружения
В самой своей основе, там, где нет Java и Dalvik, Android представляет собой минималистичный Linux-дистрибутив со всеми свойственными ему атрибутами: ядром, системой инициализации, набором библиотек, демонов, консольных команд и, конечно же, шеллом. Последний — это не что иное, как mksh из MirBSD, переименованный в sh; простой командный интерпретатор с поддержкой языковых конструкций классического Bourne shell из UNIX и автодополнением по нажатию Tab.
В качестве комплекта базовых UNIX-команд здесь используется toolbox, своего рода урезанная альтернатива BusyBox, которая позволяет вызывать несколько разных команд из одного бинарника (с помощью симлинков). Toolbox включает в себя очень ограниченный набор команд, в котором нет не только grep или sort, но даже cp. Поэтому для полноценной работы со скриптами настоятельно рекомендуется установка BusyBox, благо в маркете полно бесплатных инсталляторов.
Основное назначение скриптинга в Android — работа с ядром и системными утилитами. Ядро тут стандартное и экспортирует все те же интерфейсы /proc и /sys, через которые можно рулить железом и состоянием системы. Плюс есть набор специфичных для Android утилит, которые будут очень полезны при разработке скриптов:
Часть вывода команды dumpsys
Первый пример
Теперь давайте попробуем написать первый скрипт. Делать это лучше на компе, а еще лучше в Linux или редакторе, который умеет создавать текстовые файлы без символа возврата каретки (который при открытии в Android будет выглядеть как ^M в конце каждой строки). Наш первый скрипт будет состоять всего из двух строк, которые делают бэкап всех установленных приложений на карту памяти. Его код (требует BusyBox):
Сохраняем (пусть он называется apk_backup.sh) и перекидываем на смартфон с помощью ADB:
Теперь его нужно запустить. Проще всего сделать это с помощью все того же ADB:
Примерно таким же образом скрипт можно запустить из консоли на самом смартфоне/планшете:
Само собой, такой способ не очень удобен. Поэтому нам нужен какой-то быстрый способ запуска скрипта. Наиболее удобное из найденных мной решений — это приложение QuickTerminal. Устанавливаем, запускаем, переходим на вкладку Quick Command, нажимаем кнопку «+», вбиваем имя (произвольное) и команду (sh /sdcard/apk_backup.sh), в поле Output Type выбираем либо Dialog Output, либо Nothing. В первом случае во время выполнения скрипта на экране появится окно с результатом, во втором все пройдет в фоне. Кому что удобнее. Далее сохраняем и получаем кнопку, с помощью которой скрипт можно будет запустить быстро и легко.
Теперь напишем скрипт, который восстановит наш бэкап:
Почти все команды Android имеют подробную справку
Имея рут, можно даже сделать бэкап настроек всех приложений с помощью копирования и архивации каталога /data/data/, однако восстановить его будет очень проблематично, так как в Android каждое приложение исполняется от имени созданного специально для него Linux-юзера и хранит настройки внутри каталога, принадлежащего этому пользователю. Проблема здесь в том, что идентификатор Linux-юзера для каждого приложения генерируется динамически, поэтому после восстановления бэкапа в заново установленной системе идентификаторы не будут совпадать и приложения не смогут прочитать свои настройки. Придется вручную выяснять ID юзера для каждого приложения и менять права доступа на каталоги с данными.
С другой стороны, мы можем использовать встроенный в Android Backup Manager, позволяющий сторонним приложениям использовать возможности системы для бэкапа и восстановления приложений и их данных. Управлять им можно из консоли (а значит, и с помощью скриптов), но сам по себе он никакого бэкапа не производит, а возлагает эту работу на сторонние приложения. Helium — одно из таких приложений. Если установить и настроить его, операцию бэкапа и восстановления можно будет заскриптовать. Например, следующий простой скрипт сделает резервную копию всех сторонних приложений:
Автозапуск
Другой популярный способ автозапуска — это использование средств автоматического исполнения скриптов при загрузке в сторонних прошивках. Сегодня почти все сколько-нибудь известные кастомные прошивки умеют стартовать скрипты из каталога /system/etc/init.d/, а в стоке такую функциональность можно получить с помощью приложения Universal init.d из маркета. С последним, однако, надо быть осторожным, так как оно запускает скрипты не на раннем этапе загрузки, как это происходит в том же CyanogenMod, а уже после полной загрузки системы.
Итак, что мы можем поместить в автозагрузку? Например, скрипт запуска демона ADB в сетевом режиме:
Для подключения к нему с ПК набираем такую команду:
Также мы можем применить некоторые оптимизации подсистемы виртуальной памяти:
Ну или подогнать механизм lowmemorykiller (автоматическое убийство фоновых приложений при нехватке памяти) под наши нужды:
Ну и конечно же, автоматический выбор планировщика процессов:
Все это можно сделать с помощью специализированного софта, но зачем загружать систему дополнительным ПО, которое еще и будет висеть в фоне, когда можно обойтись несколькими простыми скриптами?
Как запустить скрипт с помощью Tasker
Запуск скриптов до и после установки прошивки
Почти каждый, кто устанавливает на свой гаджет стороннюю прошивку, также ставит поверх нее пакет с фирменными приложениями Google (gapps), который включает в себя маркет, YouTube, Gmail и другой софт. Каждый раз, когда происходит обновление прошивки, раздел /system, содержащий ее и gapps, полностью стирается, но приложения Google всегда остаются на месте. Это происходит потому, что, кроме всего прочего, gapps содержит в своем составе специальный скрипт, который размещается в каталоге /system/addon.d/ и запускается консолью восстановления до и после установки прошивки. Этот скрипт делает бэкап и восстановление приложений Google.
Мы можем использовать эту возможность для выполнения наших собственных действий до и после установки прошивки. Вот так, например, выглядит мой скрипт восстановления, который ничего не бэкапит, но подчищает прошивку от мусора сразу после ее установки:
Скрипт удаляет рингтоны, уведомления, движок синтеза речи и несколько приложений. Все эти действия запускаются в ответ на передачу скрипту опции командной строки restore (это делает консоль восстановления после установки прошивки), однако также предусмотрены и варианты обработки таких опций, как backup, pre-backup, post-backup, pre-restore и post-restore. Здесь это просто заглушки, но если бы мы захотели сделать бэкап некоторых файлов и приложений перед установкой прошивки, мы могли бы добавить их в блок backup, как это сделано в скрипте /system/addon.d/70-gapps.sh:
Этот кусок скрипта прекрасно иллюстрирует, как сделать бэкап файлов. Ключевые элементы здесь: функция listfiles, которая при запуске выводит листинг файлов, и функция backupfile, которая является частью консоли восстановления (определена в файле /tmp/backuptool.functions). Она делает бэкап файлов в цикле.
Содержимое /system/addon.d/ в CyanogenMod 11 на Motorola Defy
Скрипт бэкапа приложений Google
По словам разработчика mksh, изначально пользовательские версии Android-смартфонов вообще не должны были иметь в своем составе шелл, но после выпуска смартфона для разработчиков HTC (T-Mobile) G1 он фактически стал стандартной частью системы.
Версии Android 2.3 и ниже вместо mksh использовали минималистичный шелл ash, который входит в базовый комплект всех BSD-систем.
Чтобы получить одни и те же скрипты на всех устройствах, можно использовать приложение DropSync или FolderSync (автоматическая синхронизация через Dropbox).
Что еще?
С помощью скриптов в Android можно сделать намного больше, чем бэкапы и настройка параметров системы. Вот, например, скрипт, который просыпается каждые десять минут и, если уровень заряда батареи стал меньше 30%, отключает Wi-Fi и Bluetooth:
Чтобы скрипт работал в фоне, достаточно вызвать его следующим образом:
А это скрипт, который позволяет быстро заполнять формы, требующие ввода имэйла и пароля (в приложениях и на веб-сайтах):
Запускать его можно разными способами. Либо перед запуском приложения, установив задержку:
Не ахти как удобно, зато работает. Как мы можем использовать такую функциональность? Например, сделать простенький скрипт clip.sh:
Или просто открыть окно номеронабирателя с нужным номером:
Примерно таким же образом можно отправить SMS:
Скрипт принимает два аргумента: номер телефона и содержимое SMS. После запуска он откроет окно SMS-приложения, вставит в него нужный текст, а затем нажмет кнопку Enter для отправки, после чего окно закроется.
Другие полезные при скриптинге команды:
Для «отлова» нажатий кнопок можно использовать команду getevent
Вместо выводов
Для кого-то все описанное в статье может показаться несколько надуманным. Дескать, все это можно сделать с помощью стандартного софта и Tasker. Но зачем использовать тяжелый Java-софт там, где нужное действие можно выполнить с помощью простенького скрипта, который не занимает лишней памяти и может быть легко перенесен на другое устройство? Скрипты удобны, просты, быстро отрабатывают и дают возможность тонкой настройки под себя.
Шелл скрипты для андроид
Скрипты на shell под Android
В теме нет куратора. Если в теме есть пользователь, желающий стать Куратором и соответствующий Требованиям для кандидатов, он может подать заявку в теме Хочу стать Куратором (предварительно изучив шапку темы и все материалы для кураторов).
До назначения куратора, по вопросам наполнения шапки, обращайтесь к модераторам раздела через кнопку под сообщениями, на которые необходимо добавить ссылки.
Для начала перетащу своё сообщение из темы Таскера для лучшего понимания вопроса, о чём речь, и как можно применить.
# помощь урок таскер tasker пример шелл shell перехват аппаратных кнопок
Описание примера Работа shell-скрипта для перехвата аппаратных кнопок телефона.
Вообще, текста будет много, потому что я болтун..
Приступим к делу. Вот результирующий пример, и сразу напишу, что он делает, а потом разбор:
Итак, эта строчка (это ОДНА строчка) запущенная в терминале, или через Задачу Таскера «Run script» запускается в память (и висит там! терминал можно закрывать) и делает следующее: Она отслеживает нажатие аппаратной кнопки Громкость Вниз (думаю на большинстве Xperia заработает тоже, официально отлажено под Xperia Ray) при выключенном экране, и включает (и выключает) фонарик.
Вообще, получается красиво: нажимаешь качельку громкости и МГНОВЕННО включается фонарь, а экран при этом остаётся выключенным.
Отладка скрипта, это собственно 99.9 процента всего времени, потраченного на, поэтому гораздо удобнее «общение» будет проводить в проводном или беспроводном adb. Вертеть варианты отладки через терминал телефона утомительно. (а отладки будет много).
Часть 1. Перехват. Теория.
Итак, перехват. Подглядывать за нажатиями кнопок мы будем программой (консольной текстовой программой) «ПолучитьСобытие», getevent. Можно просто набрать (с рут правами) в терминале имя команды и завороженно смотреть на бегущие строки))
# getevent
Да, вот такие пачки на каждое действие!
0004 0004 00000004
0001 0072 00000001
0000 0000 00000000
0004 0004 0000000c
0000 0000 00000000
0004 0004 00000014
0000 0000 00000000
0004 0004 0000001c
0000 0000 00000000
0004 0004 00000024
0000 0000 00000000
0004 0004 00000024
0000 0000 00000000
0004 0004 00000004
0001 0072 00000000
0000 0000 00000000
0004 0004 0000000c
0000 0000 00000000
0004 0004 00000014
0000 0000 00000000
0004 0004 0000001c
0000 0000 00000000
Понажимав все свои три кнопки)) я пришёл к выводу о достаточности в моём частном случае ловли кода ‘0004 0004 00000004’, правда он случается и в нажатии и в отжатии, но это мы поймаем и учтём.
Итак, я выбрал для ловли кнопки Громкость Вниз код всего лишь одной строки ‘0004 0004 00000004’, который уникален в пределах устройства pm8058-keypad (/dev/input/event1).
Дальше в дело вступает команда read в замесе с командой while, в таком виде:
формализованно: «ввод_данных | while read var1; do [наша построчная обработка] ; done»
Всё, что идёт за знаком «точка с запятой» (semicolon, так сказать) после команды read, это есть команды обработки результата «распихивания» _каждой_ строки с кодами, прилетающей от getevent.
Локальный вывод: То есть, заказали мы прерывания от устройства event1, аппаратных кнопок, и всё, всё «висит» и бездействует. Система спит. Нажатие кнопки (любой) пробуждает систему (она отрабатывает сама) и присылает в getevent пачку строк с произошедшими событием. Мы раскидываем полученные строки с помощью while read на составляющие и уходим на дальнейшую обработку каждой строки отдельно, сравнивая, наша ли эта, искомая, строка, или нет. И потом запускаем боевую нагрузку, выполняем какое-то действие (включаем фонарик, как я, или вызываем сразу задачу таскера). (В принципе, вот всё решение). Но, об этом всём в своё время, ниже, а пока меня ждала целая серия засад:
Часть 2. Workarounds. Первое столкновение с практикой.
Часть 3. Фонарик. Долгая дорога в дюнах.
Ииии.. я запилил. Полный скрипт вы видели уже в начале. Ой, нет, это еще не всё)))
Часть 4. Последняя засада
Теперь всё заработало.
Домашнее задание от username11 я так пока и не выполнил((, а оно вот:
1) выяснить, кто хочет ввода-вывода и устранить.
2) убрать лишние круглые скобки. каждые скобки, это лишний шелл запущенный каскадом.
Такскера тут вообще нет, как вы видите. Справившись с половиной этого дела, отсутствие таскера было уже делом принцпа)) зачем? когда всё «шелльно» и легко. А огромная дурильня-таскер испортит всю красоту. Да и для чего? Мигнуть фонариком?
Шелл скрипты для андроид
Скрипты на shell под Android
В теме нет куратора. Если в теме есть пользователь, желающий стать Куратором и соответствующий Требованиям для кандидатов, он может подать заявку в теме Хочу стать Куратором (предварительно изучив шапку темы и все материалы для кураторов).
До назначения куратора, по вопросам наполнения шапки, обращайтесь к модераторам раздела через кнопку под сообщениями, на которые необходимо добавить ссылки.
Для начала перетащу своё сообщение из темы Таскера для лучшего понимания вопроса, о чём речь, и как можно применить.
# помощь урок таскер tasker пример шелл shell перехват аппаратных кнопок
Описание примера Работа shell-скрипта для перехвата аппаратных кнопок телефона.
Вообще, текста будет много, потому что я болтун..
Приступим к делу. Вот результирующий пример, и сразу напишу, что он делает, а потом разбор:
Итак, эта строчка (это ОДНА строчка) запущенная в терминале, или через Задачу Таскера «Run script» запускается в память (и висит там! терминал можно закрывать) и делает следующее: Она отслеживает нажатие аппаратной кнопки Громкость Вниз (думаю на большинстве Xperia заработает тоже, официально отлажено под Xperia Ray) при выключенном экране, и включает (и выключает) фонарик.
Вообще, получается красиво: нажимаешь качельку громкости и МГНОВЕННО включается фонарь, а экран при этом остаётся выключенным.
Отладка скрипта, это собственно 99.9 процента всего времени, потраченного на, поэтому гораздо удобнее «общение» будет проводить в проводном или беспроводном adb. Вертеть варианты отладки через терминал телефона утомительно. (а отладки будет много).
Часть 1. Перехват. Теория.
Итак, перехват. Подглядывать за нажатиями кнопок мы будем программой (консольной текстовой программой) «ПолучитьСобытие», getevent. Можно просто набрать (с рут правами) в терминале имя команды и завороженно смотреть на бегущие строки))
# getevent
Да, вот такие пачки на каждое действие!
0004 0004 00000004
0001 0072 00000001
0000 0000 00000000
0004 0004 0000000c
0000 0000 00000000
0004 0004 00000014
0000 0000 00000000
0004 0004 0000001c
0000 0000 00000000
0004 0004 00000024
0000 0000 00000000
0004 0004 00000024
0000 0000 00000000
0004 0004 00000004
0001 0072 00000000
0000 0000 00000000
0004 0004 0000000c
0000 0000 00000000
0004 0004 00000014
0000 0000 00000000
0004 0004 0000001c
0000 0000 00000000
Понажимав все свои три кнопки)) я пришёл к выводу о достаточности в моём частном случае ловли кода ‘0004 0004 00000004’, правда он случается и в нажатии и в отжатии, но это мы поймаем и учтём.
Итак, я выбрал для ловли кнопки Громкость Вниз код всего лишь одной строки ‘0004 0004 00000004’, который уникален в пределах устройства pm8058-keypad (/dev/input/event1).
Дальше в дело вступает команда read в замесе с командой while, в таком виде:
формализованно: «ввод_данных | while read var1; do [наша построчная обработка] ; done»
Всё, что идёт за знаком «точка с запятой» (semicolon, так сказать) после команды read, это есть команды обработки результата «распихивания» _каждой_ строки с кодами, прилетающей от getevent.
Локальный вывод: То есть, заказали мы прерывания от устройства event1, аппаратных кнопок, и всё, всё «висит» и бездействует. Система спит. Нажатие кнопки (любой) пробуждает систему (она отрабатывает сама) и присылает в getevent пачку строк с произошедшими событием. Мы раскидываем полученные строки с помощью while read на составляющие и уходим на дальнейшую обработку каждой строки отдельно, сравнивая, наша ли эта, искомая, строка, или нет. И потом запускаем боевую нагрузку, выполняем какое-то действие (включаем фонарик, как я, или вызываем сразу задачу таскера). (В принципе, вот всё решение). Но, об этом всём в своё время, ниже, а пока меня ждала целая серия засад:
Часть 2. Workarounds. Первое столкновение с практикой.
Часть 3. Фонарик. Долгая дорога в дюнах.
Ииии.. я запилил. Полный скрипт вы видели уже в начале. Ой, нет, это еще не всё)))
Часть 4. Последняя засада
Теперь всё заработало.
Домашнее задание от username11 я так пока и не выполнил((, а оно вот:
1) выяснить, кто хочет ввода-вывода и устранить.
2) убрать лишние круглые скобки. каждые скобки, это лишний шелл запущенный каскадом.
Такскера тут вообще нет, как вы видите. Справившись с половиной этого дела, отсутствие таскера было уже делом принцпа)) зачем? когда всё «шелльно» и легко. А огромная дурильня-таскер испортит всю красоту. Да и для чего? Мигнуть фонариком?