├── LICENSE ├── Makefile ├── README.md └── shdb.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 YHSPY 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export SIZE=1 2 | 3 | install: 4 | @chmod +x ./shdb.sh 5 | @./shdb.sh install --size $(SIZE) 6 | 7 | clean: 8 | @./shdb.sh uninstall 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SHDB 2 | 3 | ## Description 4 | A simple and lightweight key-value pair DB for shell programming. 5 | 6 | ## Instructions 7 | 8 | ### Installation 9 | 10 | Install SHDB with defualt settings: 11 | >* `make` 12 | 13 | Specify the maximum available storage size for SHDB: 14 | 15 | >* `make SIZE=10` 16 | 17 | \* `SIZE=10` means setting the maximum available storage to 10MB. (the maximum size cannot exceed 1024MB) 18 | 19 | ## Quick Test 20 | 21 | >* `shdb test` 22 | 23 | ## How to Use 24 | 25 | ### Normal CLI Mode 26 | >* `shdb status` 27 | >* `shdb [-s|--shell] isset [key]` 28 | >* `shdb [-s|--shell] set [key] [value]` 29 | >* `shdb [-s|--shell] get [key]` 30 | >* `shdb [-s|--shell] delete [key]` 31 | >* `shdb [-s|--shell] count` 32 | >* `shdb uninstall` 33 | 34 | ### REPL Mode 35 | 36 | Use `shdb console` to enter the "REPL" mode. 37 | 38 | >* `isset [key]` 39 | >* `set [key] [value]` 40 | >* `get [key]` 41 | >* `delete [key]` 42 | >* `count` 43 | >* `exit` 44 | 45 | ### Shell Programming Mode 46 | 47 | Please use the below syntax in shell programming: 48 | 49 | >* `shdb [-s|--shell] isset [key]` 50 | >* `shdb [-s|--shell] set [key] [value]` 51 | >* `shdb [-s|--shell] get [key]` 52 | >* `shdb [-s|--shell] delete [key]` 53 | >* `shdb [-s|--shell] count` 54 | 55 | 56 | ## Issue 57 | Please create an issue if you have any compatibility issues. 58 | 59 | ## Author 60 | @YHSPY 61 | 62 | ## License 63 | MIT 64 | -------------------------------------------------------------------------------- /shdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | #============================================================= 5 | # SHDB 6 | # 7 | # A simple, lightweight key-value DB for shell programming. 8 | # 9 | # Install: 10 | # 11 | # make SIZE=10 (MB) 12 | # 13 | # Usage: 14 | # 15 | # shdb status 16 | # shdb [-s|--shell] isset [key] 17 | # shdb [-s|--shell] set [key] [value] 18 | # shdb [-s|--shell] get [key] 19 | # shdb [-s|--shell] delete [key] 20 | # shdb [-s|--shell] count 21 | # shdb uninstall 22 | # 23 | # Author: YHSPY 24 | # License: MIT 25 | #============================================================= 26 | 27 | # set global variables. 28 | OS_TYPE=$(uname) 29 | SUDO_USER=$(echo -n $SUDO_USER) 30 | if [ -n "$SUDO_USER" ] ; then 31 | if [ "${OS_TYPE}" = "Linux" ] ; then 32 | HOME_DIR=$(getent passwd $SUDO_USER | cut -d: -f6) 33 | else 34 | HOME_DIR="/Users/$SUDO_USER" 35 | fi 36 | else 37 | HOME_DIR=$(echo -n ~) 38 | fi 39 | 40 | BASE64_ENCODED='' 41 | BASE64_DECODED='' 42 | 43 | CONFIG_KEY='' 44 | CONFIG_VAL='' 45 | 46 | typeset -i DB_CURRENT_SIZE=0 47 | 48 | typeset -i BOOL_TRUE=0 49 | typeset -i BOOL_FALSE=1 50 | typeset -i EXIT_NORMAL=0 51 | typeset -i EXIT_ERROR=1 52 | 53 | # set default setting pairs; 54 | NAME="shdb" 55 | INSTALL_DIR="/usr/local/bin/" 56 | VERSION="1.4" 57 | RELEASE="2020/10/01" 58 | typeset -i AVSIZE=1000 # 1KB by default. 59 | 60 | # set other configurations. 61 | DB_CONF_FILE_NAME="${HOME_DIR}/.shdb.master.conf" 62 | DB_DATA_FILE_NAME="${HOME_DIR}/.shdb.master.db" 63 | DB_MAIN_ENTRY="${INSTALL_DIR}${NAME}" 64 | 65 | # function definitions. 66 | _func_has_installed() { 67 | if [ -f $DB_CONF_FILE_NAME ] && [ -f $DB_DATA_FILE_NAME ] ; then 68 | return $BOOL_TRUE 69 | else 70 | return $BOOL_FALSE 71 | fi 72 | } 73 | 74 | _func_retrieve_db_system_item() { 75 | if _func_has_installed ; then 76 | # find value in db configuration file. 77 | local _GREP_INFO=$(grep -w -n "$1" $DB_CONF_FILE_NAME) 78 | 79 | if [ -n "$_GREP_INFO" ] ; then 80 | CONFIG_VAL=${_GREP_INFO##*=} 81 | fi 82 | else 83 | _func_report_error NOT_INSTALLED 84 | fi 85 | } 86 | 87 | _func_base64_encode() { 88 | BASE64_ENCODED=$(echo -n "$1" | base64) 89 | } 90 | 91 | _func_base64_decode() { 92 | BASE64_DECODED=$(echo -n "$1" | base64 --decode) 93 | } 94 | 95 | _func_calc_db_size() { 96 | if _func_has_installed ; then 97 | local _DU_INFO 98 | if [ "${OS_TYPE}" = "Linux" ] ; then 99 | _DU_INFO=$(du --apparent-size --block-size=1 $DB_DATA_FILE_NAME) 100 | else 101 | _DU_INFO=$(du -k $DB_DATA_FILE_NAME) 102 | fi 103 | DB_CURRENT_SIZE=${_DU_INFO%% *} 104 | else 105 | _func_report_error NOT_INSTALLED 106 | fi 107 | } 108 | 109 | install() { 110 | if _func_has_installed ; then 111 | _func_report_error ALREADY_INSTALLED 112 | else 113 | # checklist. 114 | if [ $# -eq 2 ] ; then 115 | if [ "${1}" = "--size" ] || [ "${1}" = "-s" ] ; then 116 | local AVSIZE_VAL=${2} 117 | if [ $AVSIZE_VAL -eq 0 ] || [ $AVSIZE_VAL -ge 1025 ] ; then 118 | _func_report_error PARAMS_ERR 119 | exit $EXIT_ERROR 120 | else 121 | AVSIZE=$(($AVSIZE_VAL * 1000)) 122 | fi 123 | fi 124 | fi 125 | printf "[SHDB DATEBASE FILE]\n" > $DB_DATA_FILE_NAME 126 | cat > $DB_CONF_FILE_NAME << EOF 127 | [SHDB CONFIGURATION FILE] 128 | 129 | NAME=shdb 130 | VARSION=${VERSION} 131 | RELEASE=${RELEASE} 132 | AUTHOR=YHSPY 133 | AVSIZE=${AVSIZE} 134 | DATE=$(date) 135 | EOF 136 | # move to /usr/local/bin, and grant exection privilege. 137 | cp -f ${0} $DB_MAIN_ENTRY 138 | chmod +x $DB_MAIN_ENTRY 139 | 140 | _func_report_info INSTALLED 141 | fi 142 | } 143 | 144 | update() { 145 | if _func_has_installed ; then 146 | if [ "${0}" != "$DB_MAIN_ENTRY" ] ; then 147 | # update source file. 148 | cp -f ${0} $DB_MAIN_ENTRY 149 | chmod +x $DB_MAIN_ENTRY 150 | _func_report_info UPDATED 151 | fi 152 | else 153 | _func_report_info NOT_INSTALLED 154 | fi 155 | } 156 | 157 | uninstall() { 158 | rm -f $DB_CONF_FILE_NAME 159 | rm -f $DB_DATA_FILE_NAME 160 | rm -f $DB_MAIN_ENTRY 161 | 162 | _func_report_info UNINSTALLED 163 | } 164 | 165 | isset() { 166 | if _func_has_installed ; then 167 | _func_base64_encode "$1" 168 | 169 | # find k-v in db. 170 | local GREP_INFO=$(grep -o "|${BASE64_ENCODED}[^|]*|" $DB_DATA_FILE_NAME) 171 | 172 | if [ -n "$GREP_INFO" ] ; then 173 | if [ "$2" = --shell ] ; then 174 | exit $EXIT_NORMAL 175 | else 176 | printf "%s\n" [True] 177 | fi 178 | else 179 | if [ "$2" = --shell ] ; then 180 | exit $EXIT_ERROR 181 | else 182 | printf "%s\n" [False] 183 | fi 184 | fi 185 | else 186 | _func_report_error NOT_INSTALLED 187 | fi 188 | } 189 | 190 | set() { 191 | _func_retrieve_db_system_item AVSIZE 192 | _func_calc_db_size 193 | 194 | if [ $DB_CURRENT_SIZE -ge $CONFIG_VAL ] ; then 195 | _func_report_error DB_OVERFLOW 196 | exit $EXIT_ERROR 197 | fi 198 | 199 | if _func_has_installed ; then 200 | _func_base64_encode $1 201 | local _SHDB_KEY=$BASE64_ENCODED 202 | 203 | _func_base64_encode $2 204 | local _SHDB_VAL=$BASE64_ENCODED 205 | 206 | # find existing item in db. 207 | local _GREP_INFO=$(grep -o "|${_SHDB_KEY}[^|]*|" $DB_DATA_FILE_NAME) 208 | 209 | if [ -n "$_GREP_INFO" ] ; then 210 | if [ "${OS_TYPE}" = "Linux" ] ; then 211 | sed -i -e "s#${_GREP_INFO}#|${_SHDB_KEY}:${_SHDB_VAL}|#" ${DB_DATA_FILE_NAME} 212 | else 213 | sed -i "" -e "s#${_GREP_INFO}#|${_SHDB_KEY}:${_SHDB_VAL}|#" ${DB_DATA_FILE_NAME} 214 | fi 215 | else 216 | echo "|${_SHDB_KEY}:${_SHDB_VAL}|" >> $DB_DATA_FILE_NAME 217 | fi 218 | 219 | if [ "$3" = --shell ] ; then 220 | exit $EXIT_NORMAL 221 | else 222 | printf "%s\n" "[OK]" 223 | fi 224 | else 225 | _func_report_error NOT_INSTALLED 226 | fi 227 | } 228 | 229 | get() { 230 | if _func_has_installed ; then 231 | _func_base64_encode $1 232 | local _SHDB_KEY=$BASE64_ENCODED 233 | 234 | # find existing item in db. 235 | local _GREP_INFO=$(grep -o "|${_SHDB_KEY}[^|]*|" $DB_DATA_FILE_NAME) 236 | 237 | if [ -n "$_GREP_INFO" ] ; then 238 | local _SHDB_VAL_TEMP=${_GREP_INFO##*:} 239 | local _SHDB_VAL=${_SHDB_VAL_TEMP%%|*} 240 | 241 | _func_base64_decode $_SHDB_VAL 242 | 243 | if [ "$2" = --shell ] ; then 244 | printf "%s" "$BASE64_DECODED" 245 | else 246 | printf "%s\n" "$BASE64_DECODED" 247 | fi 248 | else 249 | if [ "$2" = --shell ] ; then 250 | exit $EXIT_ERROR 251 | else 252 | printf "[Empty]\n" 253 | fi 254 | fi 255 | else 256 | _func_report_error NOT_INSTALLED 257 | fi 258 | } 259 | 260 | delete() { 261 | if _func_has_installed ; then 262 | _func_base64_encode $1 263 | local _SHDB_KEY=$BASE64_ENCODED 264 | 265 | # find existing item in db. 266 | local _GREP_INFO=$(grep -o "|${_SHDB_KEY}[^|]*|" $DB_DATA_FILE_NAME) 267 | 268 | if [ -n "$_GREP_INFO" ] ; then 269 | if [ "${OS_TYPE}" = "Linux" ] ; then 270 | sed -i -e "/${_GREP_INFO}/d" $DB_DATA_FILE_NAME 271 | else 272 | sed -i "" -e "/${_GREP_INFO}/d" $DB_DATA_FILE_NAME 273 | fi 274 | 275 | if [ "$2" = --shell ] ; then 276 | exit $EXIT_NORMAL 277 | else 278 | printf "[Deleted]\n" 279 | fi 280 | else 281 | if [ "$2" = --shell ] ; then 282 | exit $EXIT_ERROR 283 | else 284 | printf "[Empty]\n" 285 | fi 286 | fi 287 | else 288 | _func_report_error NOT_INSTALLED 289 | fi 290 | } 291 | 292 | count() { 293 | if _func_has_installed ; then 294 | local _COUNT_TEMP=$(grep -o "|" $DB_DATA_FILE_NAME | grep -c "|") 295 | local _COUNT_ITEM=$(($_COUNT_TEMP / 2)) 296 | 297 | if [ "$1" = --shell ] ; then 298 | printf "$_COUNT_ITEM" 299 | else 300 | printf "[Count] $_COUNT_ITEM\n" 301 | fi 302 | fi 303 | } 304 | 305 | test() { 306 | local _KEY="SHDB" 307 | 308 | printf "\n" 309 | 310 | echo "[isset] Let's find if the KV \"count\" has been stored in SHDB ..." 311 | 312 | if shdb -s isset count ; then 313 | echo "[result] Already exist... succeed" 314 | else 315 | echo "[result] (succeed)" 316 | fi 317 | 318 | printf "\n" 319 | sleep 1 320 | 321 | echo "[set] Let's reset/set the KV \"count\" with a value in SHDB ..." 322 | 323 | # Set a key in SHDB 324 | if shdb -s set count "${_KEY}" ; then 325 | echo "[result] (succeed)" 326 | else 327 | echo "[result] (failed)" 328 | fi 329 | 330 | printf "\n" 331 | sleep 1 332 | 333 | echo "[get] Let's get the value of KV \"count\" from SHDB ..." 334 | 335 | if shdb -s isset count ; then 336 | local _VAL=$(shdb -s get count) 337 | echo "[result] ${_VAL} (succeed)" 338 | else 339 | echo "[result] (failed)" 340 | fi 341 | 342 | printf "\n" 343 | sleep 1 344 | 345 | echo "[delete] Let's delete the KV \"count\" in SHDB ..." 346 | 347 | if shdb -s isset count ; then 348 | if shdb -s delete count ; then 349 | echo "[result] (succeed)" 350 | else 351 | echo "[result] (failed)" 352 | fi 353 | fi 354 | 355 | printf "\n" 356 | } 357 | 358 | _func_print_status() { 359 | if _func_has_installed ; then 360 | _func_retrieve_db_system_item AVSIZE 361 | 362 | local _FILE_SIZE=$(du -h $DB_DATA_FILE_NAME) 363 | local _DB_MAXIMUM_SIZE=$CONFIG_VAL 364 | _DB_MAXIMUM_SIZE=$(($_DB_MAXIMUM_SIZE)) 365 | 366 | cat << EOF 367 | 368 | [SHDB] 369 | ----------------- 370 | 371 | Release Version: ${VERSION} 372 | Release Date: ${RELEASE} 373 | DB Current Size: ${_FILE_SIZE} 374 | DB Maximum Size: ${_DB_MAXIMUM_SIZE}K 375 | 376 | EOF 377 | else 378 | _func_report_error NOT_INSTALLED 379 | fi 380 | } 381 | 382 | _func_report_error() { 383 | case "$1" in 384 | PARAMS_ERR ) 385 | cat << EOF 386 | [shdb ERR] Invalid command or arguments... error 387 | EOF 388 | ;; 389 | ALREADY_INSTALLED ) 390 | cat << EOF 391 | [shdb ERR] SHDB has already been installed... error 392 | EOF 393 | ;; 394 | NOT_INSTALLED ) 395 | cat << EOF 396 | [shdb ERR] Core files missing, please re-install SHDB... error 397 | EOF 398 | ;; 399 | DB_OVERFLOW ) 400 | cat << EOF 401 | [shdb ERR] SHDB has exceeded the maximum storage size... error 402 | EOF 403 | ;; 404 | esac 405 | } 406 | 407 | _func_report_info() { 408 | case "$1" in 409 | UNINSTALLED ) 410 | cat << EOF 411 | [shdb INFO] SHDB uninstall... succeed 412 | EOF 413 | ;; 414 | INSTALLED ) 415 | cat << EOF 416 | [shdb INFO] SHDB install... succeed 417 | EOF 418 | ;; 419 | UPDATED ) 420 | cat << EOF 421 | [shdb INFO] SHDB update... succeed 422 | EOF 423 | ;; 424 | esac 425 | } 426 | 427 | console() { 428 | while : 429 | do 430 | printf "%s" "shdb > " 431 | read ORDER 432 | if [ -n "$(echo -n "${ORDER}" | grep [[:space:]]*set[[:space:]][^[:space:]]*[[:space:]][^[:space:]]*)" ] ; then 433 | local _SHDB_CMD=${ORDER#*set } 434 | local _SHDB_KEY=${_SHDB_CMD%% *} 435 | local _SHDB_VALUE=${_SHDB_CMD#${SHDB_KEY} } 436 | 437 | set "$_SHDB_KEY" "$_SHDB_VALUE" 438 | elif [ -n "$(echo -n "${ORDER}" | grep "[[:space:]]*get[[:space:]][^[:space:]]*")" ] ; then 439 | local _SHDB_KEY=${ORDER#*get } 440 | 441 | get "$_SHDB_KEY" 442 | elif [ -n "$(echo -n "${ORDER}" | grep "[[:space:]]*delete[[:space:]][^[:space:]]*")" ] ; then 443 | local _SHDB_KEY=${ORDER#*delete } 444 | 445 | delete "$_SHDB_KEY" 446 | elif [ -n "$(echo -n "${ORDER}" | grep "[[:space:]]*isset[[:space:]][^[:space:]]*")" ] ; then 447 | local _SHDB_KEY=${ORDER#*isset } 448 | 449 | isset "$_SHDB_KEY" 450 | elif [ "$ORDER" = "count" ] ; then 451 | count 452 | elif [ "$ORDER" = "exit" ] ; then 453 | break 454 | else 455 | _func_report_error PARAMS_ERR 456 | fi 457 | done 458 | } 459 | 460 | # save "Interal Field Separator". 461 | PRE_IFS=$IFS 462 | IFS=" " 463 | 464 | # paly with parameters. 465 | if [ "$1" = "-s" ] || [ "$1" = "--shell" ] ; then 466 | if [ $# -eq 4 ] ; then 467 | case "$2" in 468 | set ) 469 | set "${3}" "${4}" --shell 470 | ;; 471 | * ) 472 | _func_report_error PARAMS_ERR 473 | ;; 474 | esac 475 | elif [ $# -eq 3 ] ; then 476 | case "$2" in 477 | get ) 478 | get "${3}" --shell 479 | ;; 480 | delete ) 481 | delete "${3}" --shell 482 | ;; 483 | isset ) 484 | isset "${3}" --shell 485 | ;; 486 | * ) 487 | _func_report_error PARAMS_ERR 488 | ;; 489 | esac 490 | elif [ $# -eq 2 ] ; then 491 | case "$2" in 492 | count ) 493 | count --shell 494 | ;; 495 | esac 496 | else 497 | _func_report_error PARAMS_ERR 498 | fi 499 | else 500 | if [ $# -eq 3 ] ; then 501 | case "$1" in 502 | set ) 503 | set "${2}" "${3}" 504 | ;; 505 | install ) 506 | install "${2}" "${3}" 507 | ;; 508 | * ) 509 | _func_report_error PARAMS_ERR 510 | ;; 511 | esac 512 | elif [ $# -eq 2 ] ; then 513 | case "$1" in 514 | get ) 515 | get "${2}" 516 | ;; 517 | delete ) 518 | delete "${2}" 519 | ;; 520 | isset ) 521 | isset "${2}" 522 | ;; 523 | * ) 524 | _func_report_error PARAMS_ERR 525 | ;; 526 | esac 527 | elif [ $# -eq 1 ] ; then 528 | case "$1" in 529 | install ) 530 | install 531 | ;; 532 | update ) 533 | update 534 | ;; 535 | status ) 536 | _func_print_status 537 | ;; 538 | count ) 539 | count 540 | ;; 541 | uninstall ) 542 | uninstall 543 | ;; 544 | console ) 545 | console 546 | ;; 547 | test ) 548 | test 549 | ;; 550 | * ) 551 | _func_report_error PARAMS_ERR 552 | ;; 553 | esac 554 | else 555 | _func_print_status 556 | fi 557 | fi 558 | 559 | IFS=$PRE_IFS 560 | 561 | exit $EXIT_NORMAL 562 | --------------------------------------------------------------------------------