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

2015-11-26

Python 3.5 + Docker

Один из самых удобных и современных способов деплоя рабочего окружения является Docker. В предыдущих постах мы устанавливали свежий Python из исходников, но если у вас несколько серверов, да еще и с разными версиями ОС, то этот процесс может отличаться. К тому же это безумие в чистом виде. Как вариант предлагают использование virtualenv, но он тоже не чистая песочница, и использует часть библиотек окружения, что может иметь последствия. Короче, Docker - идеальная изоляция не в ущерб производительности, не зависящая от версии ОС и установленных в ней библиотек. На DockerHub уже есть готовые образы, часть из них официальные, часть от частных лиц. Мы возьмем официальный Python 3.5 и добавим нужные нам пакеты. Минимум телодвижений - и набор готов.

Будем использовать скрипт автоматизации, который называется Dockerfile. Для создания нужного образа достаточно будет выполнить команду в каталоге с Dockerfile:


docker build -t python35 .

А вот примерное содержимое этого конфигурационного файла:


FROM python:3.5
MAINTAINER Main Tainer maintainer@email.com

# Update and install needed packaged:
# libblas-dev and liblapack-dev for matplotlib, gfortran for scipy
RUN apt-get -y update && apt-get install -y libblas-dev liblapack-dev gfortran

# Install packages for DataScience
RUN pip3 install numpy scipy sklearn pandas matplotlib seaborn nltk ipython[all] jupyter

# Web frameworks
RUN pip3 install tornado flask flask-admin flask-login flask-restful
RUN pip3 install django django-bootstrap3 django-admin-bootstrapped

# Databases: psycopg2 (Postgres), pika (RabbitMQ), sqlalchemy (ORM)
RUN pip3 install psycopg2 sqlalchemy pymongo pika redis elasticsearch

# HTTP-requests http://docs.python-requests.org/en/latest/
RUN pip3 install requests requests_oauthlib

# Image manipulations https://pillow.readthedocs.org/en/3.0.x/
RUN pip3 install pillow

# Make C-modules http://cython.org/
RUN pip3 install cython

# Scheduler https://apscheduler.readthedocs.org/en/latest/
RUN pip3 install apscheduler

Вот и все, можно запускать!


docker run -ti python35 python

Python 3.5.0 (default, Nov 20 2015, 06:18:32)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Что-нибудь посодержательнее? Ок, прокинем в контейнер каталог с реальным кодом, например с реализацией алгоритма для вычисления количества шестизначных счастливых билетов, и выполним скрипт:


docker run -v /path/outside:/path/inside python35 python /path/inside/code.py

Ура, теперь то мы знаем, что их всего 55252.

2015-09-08

How to convert *.m4a to *.mp3 on Mac? The unix-way

Как сконвертировать файлы *.m4a в *.mp3 в MacOS? Первый способ - встроенный, с использованием iTunes, и хоть это и не unix-way, ради справедливости приведу и его.

В iTunes идем в меню iTunes -> Настройки -> Настройки импорта -> Импортер -> выбираем "Кодер MP3" (по умолчанию там "Кодер AAC"), сохраняем. Затем выбираем интересующие треки и в контекстном меню используем пункт "Создать версию в формате MP3". В каталоге с файлами *.m4a появятся и *.mp3, которые можно скопировать куда нужно, а затем удалить их через iTunes, чтобы в нем не остались битые ссылки.

А теперь как правильно. Копируем свежий ffmpeg, например, этот, распаковываем бинарник (мне для этого пригодился "The Unarchiver", он есть в App store, бесплатный, встраивается в Finder). Это всё, можно конвертировать. Открываем консоль, и запускаем что-то вроде этого:


/path/to/ffmpeg -i /path/to/file.m4a -acodec libmp3lame -ab 160k /path/to/file.mp3

Для конвертации всех файлов в текущем каталоге можно использовать такой скрипт:


for i in *.m4a; do /path/to/ffmpeg -i "$i" -acodec libmp3lame -ab 160k "${i%.*}.mp3"; done

Для удобства я добавил alias в файл .profile, очень удобно. Удачной конвертации!

2014-08-13

Python: how to stop process correctly

Одной из задач, с которой встречаются разработчики многопоточных/многопроцессных приложений, является их корректная остановка. Особенно это касается таких потоков/процессов, в которых осуществляется бесконечная блокирующая операция, типа цикла "while True" или чтения из некоего генератора.

while True:
    # do something
С потоками проще, потому что им можно передать ссылку на объект, который можно проверять при каждой итерации и останавливать при соблюдении условий.

import time
from threading import Thread

def in_thread(con):
    while con[0]:
        # ... doing something
        pass

con = [True]
t = Thread(target=in_thread, args=(con,))
t.start()

time.sleep(3)
# Stop the thread
con[0] = False
t.join()
С процессами несколько иначе, они более изолированные, и нужно использовать объекты синхронизации. Очень удобный способ мне подсказал коллега, с использованием Lock.

import time
from threading import Thread
from multiprocessing import Process, Lock

def in_process(lock):

    def in_thread(con):
        while con[0]:
            pass
            # ... doing something

    con = [True]
    t = Thread(target=in_thread, args=(con,))
    t.daemon = True
    t.start()

    # Lock acquired in parent process, child process waits, but thread already ran!
    lock.acquire()

    # Polite way
    con[0] = False
    t.join()

    # Way for impatient
    # import os
    # os._exit(0)


lock = Lock()
lock.acquire()
Process(target=in_process, args=(lock,)).start()

time.sleep(3)
# ... process in action ...

# Release lock, child process going to be finished
lock.release()

2014-07-07

MySQL: UNIQUE KEY + unicode

В прошлой заметке я писал про хранение 4-байтных символов в MySQL и упомянул UTF8MB4, как расширенный вариант UTF8. Оно решает ту задачу, и можно было бы объявить про универсальный и лучший на свете collation, но сегодня я столкнулся с некоторым исключением, которое внесло ложечку дегтя в мои впечатления.

При создании уникального индекса на некое поле с юникодными данными получил ошибку о дублирующихся данных. А ведь их там нет. Проверка показала, что для UTF8MB4 строки "ame", "âme", "Amè", "AMÉ", "ÁME", "Ãme" и "ÂMÈ" одинаковые. Это говорит и SELECT.

Так вот, интересно то, что установив collation в UTF8_BIN я получил нужный результат. Понятное дело, что в этом случае сравнение идет побайтово и всё такое. Фокус в том, что для UTF8MB4_BIN такого эффекта не получаем! Разработчики схитрили или упустили этот момент? Кто знает..

2014-07-01

MySQL: хранение 4-байтных символов юникода

По умолчанию в полях VARCHAR при установленном collation UTF8 не могут храниться строки с символами юникода, состоящими из более чем 3 байт. А такие символы существуют, хоть и не очень часто встречаются. Например "\xf0\x9f\x99\x8e".

Решение в том, чтобы использовать модернизированный collation, UTF8MB4. Просто установите такой на нужной таблице и можно работать. Возможно перед вставкой стоит выполнить команду "SET NAMES UTF8MB4"

P.S. Для поисковиков: "Warning: Incorrect string value"

2014-06-25

MSSQL: псевдопартиционирование

Допустим, у нас есть (а у нас есть) таблица огромных размеров, с огромными индексами и вообще всё у неё огромное. Скажем, это записи из твиттера. Из этой таблицы часто делаются выборки по полю DATETIME. И, хотя на этом поле есть индекс, и несомненно есть Primary Key (вобщем все как надо), доступ к данным осуществляется дольше, чем хотелось бы: для использования индекс загружается в оперативную память, а он, как вы помните, огромный.

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

Вариантом временного решения может стать VIEW. Мы разделим таблицу на несколько поменьше, которые будут содержать данные в некоторых заранее известных границах. Создадим VIEW, в котором укажем на эти пределы:

CREATE VIEW table AS
SELECT * FROM table1 WHERE created_at BETWEEN '2014-02-01 00:00:00.000' AND '2014-03-01 00:00:00.000'
UNION ALL
SELECT * FROM table2 WHERE created_at BETWEEN '2014-03-01 00:00:00.000' AND '2014-04-01 00:00:00.000'

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

SELECT * FROM table WHERE created_at > '2014-03-01 00:00:00.000'

заденет только вторую таблицу, соответственно поднимет только ее индекс. Как-то так.

2014-06-20

MongoDB: шардинг по быстрому

Шардинг, как известно, позволяет раскидать данные по нескольким серверам БД, распределяя их согласно некоторым условиям. В MongoDB это бесплатно, так сказать all inclusive, к тому же невероятно просто.

Запустим несколько шардов, например на разных портах (или серверах, если есть). Я это делаю в нескольких терминалах, чтобы видеть, что происходит. Но можно добавить ключ --fork для отвязки от терминала.

$ mongod --dbpath db1 --port 27001 --shardsvr
$ mongod --dbpath db2 --port 27002 --shardsvr
$ mongod --dbpath db3 --port 27003 --shardsvr

Запустим шард-сервер и назначим ему один из шардов (на порту 27001) в помощь - там он будет хранить информацию о том, куда что записал:

$ mongos --configdb 127.0.0.1:27001

Цепляемся к шард-серверу, подключаем шарды, указываем что шардить и по какому ключу:

$ mongo
> use admin
> db.runCommand({addshard: "127.0.0.1:27002"})
> db.runCommand({addshard: "127.0.0.1:27003"})
> db.runCommand({enablesharding: "social"})
> db.runCommand({shardcollection: "social.tweets", key: {id:1}})

MongoDB: репликация по быстрому

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

mkdir -p db1 db2 db3

В терминалах запустим несколько реплик в одном наборе "myset", на своих портах и со своими каталогами для данных:

$ mongod --port 27001 --dbpath db1 --replSet myset
$ mongod --port 27002 --dbpath db2 --replSet myset
$ mongod --port 27003 --dbpath db3 --replSet myset

Подключаемся к одной из реплик, делаем ее "мастером", подключаем к ней остальные реплики и проверяем:

$ mongo --port 27001
> rs.initiate()
> rs.add("myhost:27002")
> rs.add("myhost:27003")
> use test
> db.test.insert({id:1})
> db.test.find()

где "myhost" - имя хоста, которое можно увидеть в выводе команды rs.status()

Примечательно и прекрасно, что реплики могут находиться на разных машинах, увеличивая бутылочное горлышко I/O. В своих экспериментах я размещал реплики на PC с Windows 7 на борту, на Macbook и на Ubuntu в VirtualBox.

Важно, чтобы машина с репликой могла однозначно интерпретировать хост "мастера" в IP. На макбуке мне для этого пришлось добавить запись в /etc/hosts:

myhost 192.168.10.130

2014-03-31

Запуск скрипта при старте и выключении CentOS Linux

Задача: в CentOS запускать некий скрипт "test.py" при старте и выключении компьютера от имени некоего пользователя. Для ее решения напишем init-скрипт, например "status_sender" и поместим его в каталог /etc/rc.d/init.d/

Содержимое init-скрипта:


#!/bin/bash

# chkconfig: 35 99 01
# description: Send system status
# processname: status_sender

. /etc/rc.d/init.d/functions

username="username"
lock_file=/var/lock/subsys/status_sender
log_datetime=`date -u +"%F %T"`

start(){
    touch "$lock_file"    
    daemon --user=$username /usr/local/bin/python2.7 "/home/$username/test.py" "Service started at $log_datetime UTC"  &>/dev/null & 
}

stop(){
    rm -f "$lock_file"
    daemon --user=$username /usr/local/bin/python2.7 "/home/$username/test.py" "Service stopped at $log_datetime UTC" &>/dev/null &
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    *)
        echo $"Usage: $0 {start|stop}"
        exit 2
esac
exit 0

Скрипт готов, запускаем создание символических ссылок в соответствующих настройкам каталогах. Это можно сделать и вручную, но зачем?


chkconfig --add status_sender

Проверить результат можно командой


chkconfig --list status_sender

Настройки того, куда и с какими именами будут создаваться симлинки, задаются в строке "chkconfig: 35 99 01". Это означает, что скрипт будет актуален для запуска системы с уровнем 3 и 5, причем старт осуществлять после загрузки всех сервисов (99), а останавливать в первую очередь (01).

Запуск от имени пользователя осуществляется с помощью конструкции


daemon --user=$username

где "$username" - имя пользователя в системе, а "&>/dev/null" предотвращает вывод скрипта в консоль. Команда "daemon" и другие служебные функции находятся в файле "/etc/rc.d/init.d/functions", поэтому его мы импортируем в самом начале.

"lock_file" - служебный файл, который позволяет системе определять, запущен ли сервис и нужно ли его останавливать, запуская скрипт с параметром "stop" при выключении или презагрузке. Если его не создавать, то будет осуществляться только запуск скрипта, а если не удалять - то только остановка. Для системных сервисов такой файл создается автоматически, для пользовательских же его необходимо создать самостоятельно.

"log_datetime" - это строка даты и времени в UTC для передачи python-скрипту.

2014-01-17

MSSQL UPSERT, или ON DUPLICATE KEY UPDATE по мелкомягкому

Сегодня хочу немного рассказать о том, как реализовать в MSSQL возможность, встроенную в MySQL, позволяющую обновить запись при вставке, если она уже существует. Одним словом, так называемый UPSERT.

В MySQL это выглядит так:

INSERT INTO(id, text) VALUES(12, 'Current text')
ON DUPLICATE KEY UPDATE text='New text';

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

Первый вариант - это просто проверять существование записи перед добавлением. Гром и молния, кэп, как я сам не догадался! Да и не нужен бы мне тогда был upsert, правда? Это совсем не то, что я ищу, отпадает.

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

-- Создадим переменную типа таблицы с составным уникальным индексом
DECLARE @temp TABLE(word NVARCHAR(512), term_id INT, word_count SMALLINT, UNIQUE(word, term_id))

MERGE @temp as TMP
-- Источник данных, им может быть и таблица, и выборка
USING (SELECT word, term_id FROM table1) as TWT
-- Условия совпадения, при которых будем делать Update
ON TMP.word = TWT.word AND TMP.term_id = TWT.term_id
WHEN MATCHED THEN
    -- Нашли совпадение, изменим значение поля счетчика
    UPDATE SET TMP.word_count += 1
WHEN NOT MATCHED THEN
    -- Совпадения не найдено, добавляем
    INSERT (word, term_id word_count) VALUES (TWT.word, TWT.term_id, 1)
;

Сюрприз! При добавлении строк проверка осуществляется только в начале, а строки, которые будут добавлены потом на наличие совпадений не проверяются. Это легко можно проверить, добавив вручную в таблицу @temp пару строк, которые вскоре будут туда добавляться запросом. Так что для нашего случая это не годится.

Наконец третий, рабочий, но медленный вариант. Мы будем использовать конструкцию BEGIN TRY. Работаем с той-же таблицей @temp, что в предыдущем примере. Мы будем проходит курсором по выборке из исходной таблице и пробовать добавлять в @temp, и обновлять данные при ошибке уникального ключа.

-- Переменные, куда будем складывать данные из курсора
DECLARE @word NVARCHAR(512)
DECLARE @term_id INT
-- Объявляем и открываем курсор с данными
DECLARE words SCROLL CURSOR FOR SELECT word, term_id FROM table1
OPEN words
-- Проходим по курсору
FETCH NEXT FROM words INTO @word, @term_id WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        -- Пробуем вставить строку
        INSERT INTO @temp VALUES(@word, @term_id, 1)
    END TRY
    BEGIN CATCH
        -- При ошибке делаем update
        UPDATE @temp SET word_count = word_count + 1
        WHERE word = @word AND term_id =  @term_id
    END CATCH
    -- Берем данные из следующей строки
    FETCH NEXT FROM words INTO @word, @term_id
END
-- Закрываем и освобождаем курсор
CLOSE words
DEALLOCATE words

Вот такие я подобрал варианты. Надеюсь, кому-нибудь мои изыскания помогут.

2014-01-09

Python и чтение списков

Не так давно меня спросили - как бы ты прочитал список в Python в обратном порядке? Я предположил, что самым естественным образом будет просто выборка из списка по индексу от максимального (равного len(x) - 1) в порядке уменьшения. Сегодня я решил проверить варианты с подсчетом производительности. Python 2.7.4 x64, 8 Гб RAM, AMD Phenom II X4 960T, 3.00 GHz.

Итак, создаем список из ста миллионов записей и проходим по нему разными способами.


big_list = range(100000000)
big_list_len = len(big_list)

# Прямой перебор, самый быстрый и естественный: 10.3 sec
for item in big_list:
    a = item

# Перебор с выборкой по индексу в прямом порядке: 12.8 sec
for num in xrange(big_list_len):
    a = big_list[num]

# А теперь в обратном порядке: 12.7 sec
for num in xrange(big_list_len-1, 0, -1):
    a = big_list[num]

# И наконец, используем встроенную функцию reversed: 11.9 sec
for item in reversed(big_list):
    a = item

Выводы очевидны. Самый быстрый способ взять данные в обратном порядке - использовать reversed и прямой перебор. Выборка по индексу - не намного, но медленнее.

2013-11-26

Переход с Ubuntu на Mac

Так уж получилось. Увидел macbook с retina display и не устоял. Не буду тут воспевать яблоки, потому что, как я давно понял, совершенства на земле нет.
Несколько лет Ubuntu служила мне верой и правдой, а теперь придётся ей развиваться без меня. Честно, жаль, но устанавливать на макбук убунту всё-же считаю как минимум странным делом.
Конечно, как разработчику, мне было бы некомфортно без консоли. Пришлось забыть об apt, но, к счастью, есть замена, и даже две: MacPorts и  HomeBrew. Я остановил выбор на втором, показалось попроще. Через него установил vim, mc, s3cmd, enca, apache, mysql и иже с ними. Вобщем, жить и работать можно.
Из приложений с графическим интерфейсом - Sequel Pro, Sublime Text, IntelliJ Idea. Старые добрые Gimp и Inkscape есть и под мак.
В связи со всем этим, разработку приложений под Ubuntu, или интеграцию в неё придётся заморозить. Постараюсь делать что-то более платформонезависимое.

2013-05-15

IntelliJ Idea + Solarized Color Schemes

Стал пользоваться IntelliJ Idea для всех проектов. Совершенно невероятно продуманная IDE, но речь не об этом. Захотелось сразу же прикрутить туда популярные цветовые схемы Solarized, к которым я уже привык. Делается это несложно. А именно:
cd ~/.IntelliJIdea12/config/colors/
wget https://raw.github.com/altercation/solarized/master/intellij-colors-solarized/Solarized%20Dark.xml
wget https://raw.github.com/altercation/solarized/master/intellij-colors-solarized/Solarized%20Light.xml
Заускаем Idea, в настройках находим список цветовых схем, а в них - Solarized Dark и Light.

2013-04-02

Цветной Python: выводим в консоль красиво

Наигравшись с nodejs возвращаюсь к старому доброму python. Запишу себе на память шпаргалку о том, как сделать стандартный вывод в консоль цветным.
Используются стандартные ANSI escape code, применение выглядит так:

print '\033[36mCYANCOLOR\033[0m'
print '\033[96mINTENSIVECYANCOLOR\033[0m'

Кодов 109 штук, но применимыми выглядят только эти:


  • 0 - по умолчанию
  • 1 - усиленный (жирный или более интенсивный цвет)
  • 2 - ослабленный (нежирный или менее интенсивный цвет)
  • 4 - подчёркнутый
  • 7 - негативные цвета
  • 8 - скрытый
  • 9 - перечёркнутый
  • 30 - черный
  • 31 - красный
  • 32 - зелёный
  • 33 - желтый
  • 34 - синий
  • 35 - пурпурный
  • 36 - голубой
  • 37 - белый (светло-серый)
  • 40-47 - аналогично 30-37, но цвет применяется к фону
  • 90-97 - аналогично 30-37, но цвет более интенсивный
  • 100-107 - аналогично 40-47, но цвет более интенсивный


2013-02-28

Использование Git


Использовать систему контроля версий GIT очень просто. Для начала вот краткий инструктаж. Устанавливаем git:
sudo apt-get install git

Перемещаемся в корневой каталог проекта и инициализируем проект:
cd ~/Projects/myProjectName
git init

Для хостинга проекта я использую Bitbucket. Зарегистрируйтесь на сайте и получите
имя пользователя, допустим, это myBBname. Теперь нужно создать проект, думаю, разберётесь. Пусть он называется myProjectName. Добавим запись о нём, назовём запись REPONAME:
git remote add REPONAME https://myBBname@bitbucket.org/myBBname/myProjectName.git

Добавляем файл(ы) в репозатарий
git add *.php

Когда закончим добавлять новые/изменённые файлы/каталоги, подтверждаем и указываем комментарий:
git commit -m "Пояснение к коммиту"

Теперь можно залить изменённые данные на сервер; используем запись REPONAME, основная ветка:
git push -u REPONAME master

BitBucket запросит пароль аккаунта для myBBname. Если всё было сделано правильно, то файлы зальются на сервер, а в вашем аккаунте появится информация о пределанных операциях.

Через несколько push-ей у вас повится зуд в пальцах от того, что каждый раз нужно вводить пароль. Нужно это как-то поправить. Решение - в использовании SSH и ключей. Запустим утилиту `ssh-keygen`:
ssh-keygen

Она будет предлагать варианты действий, ключевым из которых будет имя создаваемого
файла ключа (как правило, это `~/.ssh/id_rsa.pub`) и passphrase. Придумайте что-нибудь сами.
Теперь у нас есть файл ключа, закинем его в аккаунт Bitbucket (с учетом названия вашего аккаунта): https://bitbucket.org/account/user/myBBname/ssh-keys/

Там всё просто: Add Key, даём ему понятное название, в текстовое поле вносим содержимое файла ключа, сохраняем.
Добавим новую запись о проекте, через которую будем осуществлять доступ к серверу, в варианте для SSH (назовём её SSHREPONAME):
git remote add SSHREPONAME ssh://git@bitbucket.org/myBBname/myProjectName.git

Отлично. Теперь сделаем так, чтобы наш пароль был ассоциирован с ssh-ключом. Запустим `ssh-agent` для оболочки:
ssh-agent bash
ssh-add

У нас спросят ту самую passphrase, которую мы вводили, когда генерировали файл-ключ с помощью `ssh-keygen`.
Теперь, отправляя файлы на сервер, можно использовать ssh-запись, и будет использован ключ, а не пароль.
git push -u SSHREPONAME master

В завершении скажу, что можно использовать плагин Git в Sublime Text 2,
особенно если создать проект ровно в том-же каталоге, где и создан репозитарий git. И это очень удобно.

2012-11-14

mount без пароля

Задача: написать скрипт, который будет монтировать папку с удаленного win-сервера, и, после копирования файлов на локальную машину, отмонтировать её.
Казалось бы, ничего сложного, mount решает:

sudo apt-get install cifs-utils
sudo mount -t cifs //192.168.0.1/Folder /home/kawaikunee/Folder -o username=User,password=Pass,rw,file_mode=0777,dir_mode=0777
cp -a -n /home/kawaikunee/Folder/*.pdf /home/kawaikunee/Doc/PDFs/
sudo umount /home/kawaikunee/Folder

Да вот только одна проблема: скрипт должен работать автономно. И пароль sudo ему знать совсем не обязательно.

Решение: добавить пользователю, от чьего имени запускается скрипт, права на беспарольное использование sudo mount и sudo umount. Для этого внесём изменения в файл /etc/sudoers

sudo gedit /etc/sudoers

Добавим в него строчку:

kawaikunee linux = NOPASSWD: /bin/mount,/bin/umount

где первые два слова - имя пользователя и имя компьютера (они обычно представлены в приглашении терминала по умолчанию).

Задача решена. Скрипт использует sudo, но пароль у него не спрашивают.
P.S. Что-то мне подсказывает, что так можно и другие подобные задачи решить :)

2011-06-22

На заметку о Gmail

Несколько приятных возможностей строки поиска писем в почте Gmail

Непрочтенные письма, несколько вариантов:
is:unread
label:unread
l:unread
l:^u
Письма от адресата 'tim':
from:tim
Письма, помеченные звездочкой:
l:^t
Письма в корзине:
l:^k
Письма с темой 'hi':
subject:hi


Эти параметры поиска можно комбинировать.

2011-05-26

Привести дела в порядок ))

Именно эту задачу предлагает решить Getting Things GNOME - что-ж, планировать свои дела в нем действительно удобно. Он похож на Tomboy, только написан на Python+GTK, и позволяет создавать записи о задачах и подзадачах, распределять мероприятия по датам, задавая длительность. Отличный хелп в комплекте.
sudo apt-get install gtg
А вот о том, чтобы держать актуальным этот список в нескольких местах одновременно - разработчик позаботился явно недостаточно. Присутствует плагин для синхронизации с RTM (Remember The Milk), но стабильности ему точно недостает. Поэтому решаем этот вопрос самостоятельно. А именно - используя Dropbox.
Вы все еще не пользуетесь этим замечательным сервисом? Тогда мы идем к вам добро пожаловать на сайт разработчика и проникаться по просветления. Ubuntu One все еще далек от юзабельности (было несколько неприятных моментов). Dropbox же ни разу не подвел.
Итак, все, что нужно - это сделать пару символьных ссылок.
На одном компе, например, так:
ln -s ~/.local/share/gtg/ ~/Dropbox/gtg
А на остальных так:
mkdir ~/Dropbox/gtg
ln -s ~/Dropbox/gtg/ ~/.local/share/gtg

Только один момент: не включать GTG до того, как Dropbox синхронизирует файлы.
И все дела ^_^

Менеджер буфера обмена Parcellite

Очень удобная штука. Позволяет хранить множество версий содержимого буфера обмена. Например, скопировав текст, вставил его, и потом забил буфер другими данными. А потом понадобился этот текст. Снова искать, где он есть, и копировать?
Нет, конечно. Просто устанавливаю Parcellite (на текущий момент версии 0.9.3-1) и радуюсь туче ячеек буфера обмена.
sudo apt-get install parcellite

2011-02-22

Еще немного об оформлении

Почти два месяца ничего не писал в блог. Некогда и нечего было. Недавно увидел пример оформления, который мне понравился, и продолжая предыдущий пост, добавлю кое-что.
Во-первых, это nautilus-elementary. Добавляем источник пакетов в /etc/apt/sources.list:
deb http://ppa.launchpad.net/am-monkeyd/nautilus-elementary-ppa/ubuntu lucid main
и обновляем nautilus
Чрезвычайно настраиваемая тема - Orta:
http://skiesofazel.deviantart.com/art/Orta-184118297
И красивая рамка окошек для Emerald:
http://gnome-look.org/content/show.php/eLeKaJota?content=137640
С учетом предыдущего поста, получилось вот так: