Хардкорный 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