Хардкорный bash
-скрипт для ведения домашней бухгалтерии прямо из консоли 🙂
Пока в нём только “наличные” и, соответственно, только один тип операций – “ввод-вывод” этих самых наличных.
Есть желание в будущем добавить несколько типов платежей (наличные, несколько платёжных карт, webmoney) и переписать на Python или Java с нормальным GUI.
Основная идея написания скрипта была “не дать себе засохнуть” (с) как в плане общения с bash, так и SQL.
Дополнительные сведения в статьях BASH: описание циклов for, while, until и примеры использования и BASH: использование функций, примеры.
Сначала – нам потребуется отдельная база данных:
# mysql -u root -p Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 48050 Server version: 5.5.35 Source distribution
mysql> create database setevoy_money_db1; Query OK, 1 row affected (0.02 sec)
mysql> grant all on setevoy_money_db1.* to ‘setevoy’@'localhost’; Query OK, 0 rows affected (0.50 sec)
Далее – создадим две таблицы – в одной будем записывать остаток средств (cash_avail
), в другой – куда мы их тратим или откуда мы их получаем (transactions
):
mysql> create table transactions (transaction_id int unsigned primary key auto_increment not null, transaction_date date not null, operation_type enum(‘IN’,'OUT’), amount int unsigned not null, description longtext not null); Query OK, 0 rows affected (0.02 sec)
mysql> create table cash_avail (id int unsigned primary key auto_increment not null, date date not null, amount int unsigned not null); Query OK, 0 rows affected (0.20 sec)
Вот как они выглядят:
mysql> show tables; +-----------------------------+ | Tables_in_setevoy_money_db1 | +-----------------------------+ | cash_avail | | transactions | +-----------------------------+ 4 rows in set (0.00 sec)
mysql> desc cash_avail; +--------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | date | date | NO | | NULL | | | amount | int(10) unsigned | NO | | NULL | | +--------+------------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)
mysql> desc transactions; +------------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------------+------------------+------+-----+---------+----------------+ | transaction_id | int(10) unsigned | NO | PRI | NULL | auto_increment | | transaction_date | date | NO | | NULL | | | operation_type | enum('IN','OUT') | YES | | NULL | | | sum | int(10) unsigned | NO | | NULL | | | description | longtext | NO | | NULL | | +------------------+------------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec)
Ещё хорошо было бы добавить в cash_avail
для каждой записи колонку transactions_id
из таблицы transactions
.
Пара примеров по работе с таблицами/колонками.
Изменить колонку sum
на sum_changed
в таблице transactions
:
mysql> alter table transactions change sum sum_changed int unsigned not null; Query OK, 27 rows affected (0.07 sec) Records: 27 Duplicates: 0 Warnings: 0 mysql> desc transactions; +------------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------------+------------------+------+-----+---------+----------------+ | transaction_id | int(10) unsigned | NO | PRI | NULL | auto_increment | | transaction_date | date | NO | | NULL | | | operation_type | enum('IN','OUT') | YES | | NULL | | | sum_changed | int(10) unsigned | NO | | NULL | | | description | longtext | NO | | NULL | | +------------------+------------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec)
Обновить запись с id=71
в таблице cash_avail
и установить новое значение поля amount
:
mysql> update cash_avail set amount='1000' where id=71; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from cash_avail where id=71; +----+------------+--------+ | id | date | amount | +----+------------+--------+ | 71 | 2014-02-22 | 1000 | +----+------------+--------+ 1 row in set (0.00 sec)
Или так:
mysql> update cash_avail set amount = replace(amount, '100', '1000') where instr(amount, '100') > 0;
Удалять “лишние” записи – пока только вручную. Для этого – выполняем:
mysql> delete from transactions where transaction_date='2014-02-22';
Больше примеров работы с MySQL – Наиболее используемые команды MySQL, Простая работа с MySQL в простых примерах #2 — наполнение таблиц, Простая работа с MySQL в простых примерах #1 — работа с базами, создание таблиц.
Теперь – перейдём к самим скриптам.
Основной скрипт, управляющий – money.sh
. Дополнительно к нему – файл functions
, в котором просто описываются используемые функции, и который подгружается при запуске скрипта money.sh
.
Т.к. основные действия выполняются из функций – рассмотрим сначала functions
:
# описываем соединение с сервером MySQL, и передаём первым аргументом нужные команды connect () { mysql -D setevoy_money_db1 -u setevoy -pMySeCrEtPaSs -Bse "$1" } # иногда дата не задаётся вручную и/или не передаётся переменная, будем использовать функцию для определения переменной, если она пустая setdate () { if [ -z $opdate ] then opdate=`date +%Y-%m-%d` fi } # фунция collect используется для сбора данных, которые будут использованы функцией insert при добавлении записи о транзакции collect () { read -p "Please - enter date (format YYYY-MM-DD, left blank for current): " opdate # вызываем функцию setdate для проверки значений переменной opdate, если пустая - установим текущую дату setdate # тип операции - вход/расход средств, с помощью case in проверяем что бы тип был только один их двух предложенных while read -p "Please select type - IN or OUT: " optype do case $optype in in|out) break ;; *) echo -e "nPlease - enter IN of OUT!" esac done # указание полученной/затраченной суммы read -p "Please enter sum: " opsum # описание тразакции - откуда получили деньги или на что потратили read -p "Please enter desription: " opdesc } # операция вставки в таблицу transactions insert () { setdate connect "insert into transactions values(null, '$opdate', '$optype', '$opsum', '$opdesc');" # получаем текущий остаток из балицы cash_avail с помощью функции avail local now=`avail` # проверяем тип операции - ввод или вывод, и устанавливаем соответствующее значение переменной # в зависимости от переменной мы или добавляем или отнимаем из переменной now и добавляем новую запись в таблицу cash_avail if [ $optype = "out" ] then local last=`expr $now - $opsum` else local last=`expr $now + $opsum` fi connect "insert into cash_avail values(null, '$opdate', '$last');" } # проверка последней записи amount по последнему id в таблице cash_avail avail () { connect "select amount from cash_avail where id = (select max(id) from cash_avail);" } # проверка последних операций lastaction () { # если fromdate будет пустая - выборка с самого начала таблицы read -p "Enter FROM date: " fromdate # дата, до которой надо получить данные read -p "Enter TO date (left blank for current): " todate # проверяем - если переменная todate пустая - то устанавливаем текущую дату if [ -z $todate ] then todate=`date +%Y-%m-%d` fi # можно сделать выборку только по операциям ввода, вывода - или по всем read -p "Select operation type - IN or OUT (left blank for both): " loptype if [ -z $loptype ] then loptype="%" fi echo connect "select transaction_date, operation_type, sum, description from transactions where transaction_date between '$fromdate' and '$todate' and operation_type like '$loptype';" echo } # операция вывода списка операций ввода/вывода, выводящая результат в сумме spending () { read -p "Enter FROM date: " fromdate read -p "Enter TO date (left blank for current): " todate if [ -z $todate ] then todate=`date +%Y-%m-%d` fi while read -p "Select operation type - IN or OUT: " loptype do # смысла смотреть общую сумму и по затратам и по расходам смысла нет - поэтому проверяем, что бы был точно определён тип if [ -z $loptype ] then echo -e "Please - make your choice!" else break fi done # немного украшательства текста if [ $loptype == "in" ] then say="earn" else say="spend" fi # делаем выборку в таблице transactions, и суммируем полученные данные из колонки sum echo -e "nFrom $fromdate to $todate you $say: " connect "select sum(sum) from transactions where operation_type='$loptype' and transaction_date between '$fromdate' and '$todate';" echo } # просто функция Да/Нет answer () { while read -n1 response; do echo case $response in [yY]) printf "$1n" return 0 break ;; [nN]) printf "$3n" return 1 break ;; *) printf "Please, enter y (yes) or n (no)! " esac done }
Теперь перейдём к “управляющему” скрипту money.sh
:
#!/usr/local/bin/bash # подгружаем список функций source "/home/setevoy/scripts/money/functions" # рассказываем как использовать скрипт и его опции usage="nPlease - select action:nn1) Show available cashn2) Insert new transactionn3) Show last transactionsn4) Show spending for periodn" echo -e "$usage" # проверяем ответ, если пустой - то снова печатаем USAGE, хотя лучше бы тут сделать с использованием case или regex для точности while read -n1 -p "Enter 1, 2, 3 or 4: " select do if [ -z $select ] then echo -e "$usage" else break fi done # пошли перебирать ответы # смотрим доступные средства - вызываем функцию avail if [ $select == 1 ] then echo -en "nnAvailable cash: `avail`nn" fi # тут добавляем новую операцию ввода/вывода наличиных if [ $select == 2 ] then echo -e "nnYou selected "Insert new operation".n" # собираем данные для ввода с помощью функции collect while collect do # перед вводом в базу - даём возможность перепроверить их echo -en "nYou entered:nndate: $opdatenoperation type: $optypensum: $opsumndescription: $opdescnnIs it correct? Y/n: " # вызываем функцию answer, если ответ Y (return 0) - то вызываем функцию вставки данных в базу и выводим текущий остаток answer && insert && echo -e "Now available: `avail`" # Можно добавить ещё операцию echo -en "nAdd next? Y/n: " answer || break done fi # список операций ввода/вывода if [ $select == 3 ] then echo -e "nnYou select "Show last operations".n" lastaction fi # и функция вывода суммы затраченных/полученных средств за период if [ $select == 4 ] then echo -e "nnYour choice #4 - "Show spending for period".n" spending fi
Последний штрих – в файл .bashrc
добавим alias
, что бы не “тянуться” к скрипту напрямую:
alias money="/home/setevoy/scripts/money/money.sh"
Примеры работы:
$ money Please - select action: 1) Show available cash 2) Insert new transaction 3) Show last transactions 4) Show spending for period Enter 1, 2, 3 or 4: 1 Available cash: 100
$ money Please - select action: 1) Show available cash 2) Insert new transaction 3) Show last transactions 4) Show spending for period Enter 1, 2, 3 or 4: 2 You selected "Insert new operation". Please - enter date (format YYYY-MM-DD, left blank for current): Please select type - IN or OUT: in Please enter sum: 10 Please enter desription: test transaction; income You entered: date: 2014-02-22 operation type: in sum: 10 description: test transaction; income Is it correct? Y/n: y Now available: 110 Add next? Y/n: n
$ money Please - select action: 1) Show available cash 2) Insert new transaction 3) Show last transactions 4) Show spending for period Enter 1, 2, 3 or 4: 4 Your choice #4 - "Show spending for period". Enter FROM date: 2014-02-01 Enter TO date (left blank for current): Select operation type - IN or OUT: out From 2014-02-01 to 2014-02-22 you spend: 60