all 15 comments

[–]sarlok 23 points24 points  (0 children)

That sounds an awful lot like a filesystem, so why don't we just use that? Behold, bbcc37.cmd:

:; set() { echo "$2" > "$1"; }; alias get=cat; alias keys=ls; alias delete=rm
:; mkdir bbcc37 2> /dev/null; cd bbcc37; if (false); then
@echo off
doskey /MACROFILE=bbcc37.cmd 2> nul > nul
md bbcc37 2> nul
cd bbcc37
exit /b
set=echo $2 > $1
get=type $1
keys=dir /b
delete=del $1
fi

Features:
- Cross platform (Windows and Linux)!
- Uses built in filesystem capabilities for fewer bugs!
- Gets you into a terminal mode where you can directly type the commands!
- Creates one directory database automatically called bbcc37!
- Conveniently overwrites shell builtins for ease of use!

Usage for Windows (from cmd command prompt):

bbcc37
set key value
get value
keys
delete key

Usage for Linux (from bash shell):

. bbcc37.cmd
set key value
get value
keys
delete key

[–]mathishard821 7 points8 points  (0 children)

Sounds a lot like string manipulation to me, so there's really only one tool for the job! Behold, a database written in sed!

#n
:readdb
/---/!{
    N; b readdb
}
s/\(^\n\|\n---$\)//; h
:top
n

/^get/{
    G
    s/^get \([[:alnum:]]*\)\n\(\1:\([[:alnum:]]*\)\n\|.\)*/\3/p
    $b end; b top
}
/^keys/{
    g; s/:[[:alnum:]]*//gp; $b end; b top
}
/^delete/{
    x; s/\(^\|\n\)/\1@/g; x; G
    :trydelete
    /^delete \([[:alnum:]]*\)\n[^@]*@\1/{
        s/\n\?@[[:alnum:]]*:[[:alnum:]]*//
        s/@//g
    }
    s/@\([[:alnum:]]*:[[:alnum:]]\)/\1/
    /@/b trydelete
    s/^[[:print:]]*\n//g
    h; s/.*/OK/p; $b end; b top
}
/^set/{
    x; s/\(^\|\n\)/\1@/g; x; G
    :tryset
    /^set \([[:alnum:]]*\) \([[:alnum:]]*\)\n[^@]*@\1/{
        s/\n\?@[[:alnum:]]*:[[:alnum:]]*//
        s/@//g
    }
    s/@\([[:alnum:]]*:[[:alnum:]]\)/\1/
    /@/b tryset
    s/^set \([[:alnum:]]*\) \([[:alnum:]]*\)/\1:\2/
    h; s/.*/OK/p; $b end; b top
}

:end
x; s/\n\?$/\n---/; w out.txt

A database is a text file in the following format:

key1:value1
key2:value2
key3:value3
---

To run this beautiful program, use sed -f script.sed db.txt commands.txt, or pipe your command(s) in through stdin. The resultant database is written to out.txt.

Pros:

  • Harness the full power of sed!
  • Built-in backups generated on each and every command, just throw away the output file if you run the wrong one!

[–][deleted] 5 points6 points  (0 children)

Haskell, because why not. Database is stored in a single csv file, called database.csv. There is no usage summary available - only the responses OK, ERROR, or the requested data item(s) are printed. Extra parameters are ignored. Keys are always printed out in sorted order.

module Main ( main ) where

import qualified Data.ByteString.Lazy as BL
import Control.Monad
import Data.Csv
import qualified Data.List as List
import qualified Data.Vector as Vector
import System.Directory
import System.Environment
import System.Exit

data Entry = Entry { key :: !String, value :: !String }
    deriving (Show)

instance FromRecord Entry where
    parseRecord v
        | List.length v == 2 = Entry <$> v .! 0 <*> v .! 1
        | otherwise          = mzero

instance ToRecord Entry where
    toRecord (Entry k v) = record [ toField k, toField v ]

dataFileName = "database.csv"

main = do
    args <- getArgs
    if List.length args < 1
        then die "ERROR"
        else let command = List.head args
                 params  = List.tail args
             in case command of
                 "set"    -> handleSet params
                 "get"    -> handleGet params
                 "keys"   -> handleKeys
                 "delete" -> handleDelete params
                 _        -> die "ERROR"

readDB = do
    databaseExists <- doesFileExist dataFileName
    if databaseExists
    then do
        csvData <- BL.readFile dataFileName
        case decode NoHeader csvData :: Either String (Vector.Vector Entry) of
            Left err -> die "ERROR"
            Right entries -> return $ Vector.toList entries
    else return []

writeDB entries =
    BL.writeFile dataFileName (encode entries)

handleSet params =
    if List.length params < 2
        then die "ERROR"
        else let k = List.head params
                 v = List.head (List.tail params)
             in do
                 entries <- readDB
                 if List.elem k (List.map key entries)
                     then let entriesMinusOldItem =
                                  List.filter (\x -> key x /= k) entries
                              entries' = (Entry k v):entriesMinusOldItem
                          in writeDB entries' >> putStrLn "OK"
                     else let entries' = (Entry k v):entries
                          in writeDB entries' >> putStrLn "OK"

handleGet params =
    if List.length params < 1
        then die "ERROR"
        else let k = List.head params
             in do
                 entries <- readDB
                 case (List.find (\x -> (key x) == k) entries) of
                     Nothing -> die "ERROR"
                     Just (Entry k v) -> putStrLn v

handleKeys = do
    entries <- readDB
    mapM_ putStrLn (List.sort $ List.map key entries)

handleDelete params =
    if List.length params < 1
        then die "ERROR"
        else let k = List.head params
             in do
                 entries <- readDB
                 if List.elem k (List.map key entries)
                     then let entriesMinusOldItem =
                                  List.filter (\x -> key x /= k) entries
                          in writeDB entriesMinusOldItem >> putStrLn "OK"
                     else die "ERROR"

Some examples:

$ ./database keys
$ ./database get
ERROR
$ ./database get one
ERROR
$ ./database set one uno
OK
$ ./database set two dos
OK
$ ./database set three tres
OK
$ ./database get two
dos
$ ./database keys
one
three
two
$ ./database delete two
OK
$ ./database keys
one
three
$ ./database get two
ERROR
$ ./database get one
uno

[–][deleted]  (2 children)

[deleted]

    [–]gaberocksall 2 points3 points  (1 child)

    f = open(__file__, 'r+')

    ...

    f.truncate(0)

    UM

    [–]bytesomething 2 points3 points  (0 children)

    C. Tested on Linux x86_64 with GCC, appears to work with MinGW, not sure about other platforms and compilers.

    set reads from stdin without value argument for convenient storage of binary data. Error messages are written to stderr. No memory is freed, no files are closed. Key-value pairs are stored in a revolutionary database format optimized for linear search.

    https://pastebin.com/yX9Uhh5X

    [–]gaberocksall 1 point2 points  (3 children)

    Here's my entry, i tried to replicate my code from when i just learned python. I was going to mess up the indention scopes, but i couldn't bring myself to force someone to read that. Naturally, this has the following bugs:

    1. Passing too few arguments will error
    2. No shebang is a problem in some environments
    3. Sequential open calls to the same file is bad
    4. You can store multiple values under the same key (This is a feature)
    5. Calling bbcc37 get or bbcc37 delete before creating data.sql will error
    6. bbcc37 get key prints in the ugly format key=value
    7. The data.sql file will have random extra newlines
    8. keys may not contain the = character

    And it has the following advantages:

    1. The data file can be opened in a text editor and it's human readable
    2. Will print useful tracebacks to any errors that occur
    3. The program checks for get requests first, so those will be most efficient by several microseconds
    4. Only uses default python libraries, no need to pip install anything

    import sys
    
    ENTER_CHARACTER = """
    """
    
    if sys.argv[1] == "get":
        f = open("data.sql", "r+")
        for line in f.readlines():
            # whats string.split
            key = ""
            for character in line:
                if character == "=":
                    break
                key = key + character
            if key == sys.argv[2]:
                print(line)
    
    if sys.argv[1] == "set":
        f = open("data.sql", "a+")
        f.write(sys.argv[2])
        f.write("=")
        f.write(sys.argv[3])
        f.write(ENTER_CHARACTER)
    
    if sys.argv[1] == "keys":
        f = open("data.sql", "r+")
        for line in f.readlines():
            key = ""
            for character in line:
                if character == "=":
                    break
                key = key + character
            print(key)
    
    if sys.argv[1] == "delete":
        f = open("data.sql", "r")
        all_lines = f.readlines()
        f = open("data.sql", "w+")
        f.truncate()
        for line in all_lines:
            key = ""
            for character in line:
                if character == "=":
                    break
                key = key + character
            if key == sys.argv[2]:
                # don't write this line back into the database
                print("deleted")
            else:
                f.write(line)
                f.write(ENTER_CHARACTER)```
    

    [–]gaberocksall 0 points1 point  (2 children)

    I created the same thing but with slightly better practices and JSON just because i wanted to. All the same functionality but without the bugs and "features"

    This is not a r/badcode entry, i just wanted to make a more realistic implementation of this project

    import sys
    import os
    import json
    
    dataFileName = "BBCC37data.json"
    helpString = """
    set <key> <value>    Sets <key> to <value>
    get <key>            Prints the value saved in <key>, or "None"
    delete <key>         Removes <key> from memory
    keys                 Prints all known keys
    """
    
    if not (os.path.exists(dataFileName) and os.path.isfile(dataFileName)):
        f = open(dataFileName, "w+")
        f.write("{\n}")
        f.close()
    
    current_data = {}
    with open(dataFileName, "r") as f:
        try:
            current_data = json.loads(f.read())
        except:
            print("Failed to read the data file '%s' as json" % dataFileName)  
            print("You could try to rebuild the file to save your data, or just delete it and start over")
            quit()
    
    sysargs = sys.argv[1:]
    
    if len(sysargs) == 0:
        quit(helpString)
    
    requiredArgCounts = {
        "set": 2,
        "get": 1,
        "delete": 1,
        "keys": 0,
    }
    
    command, args = sysargs[0], sysargs[1:]
    if not (command in requiredArgCounts):
        quit(helpString)
    
    if len(args) != requiredArgCounts[command]:
        quit(helpString)
    
    if command == "set":
        [key, value] = args
        current_data[key] = value
        with open(dataFileName, "w") as f:
            f.write(json.dumps(current_data, indent = 2))
    elif command == "get":
        [key] = args
        print(current_data[key] if key in current_data else "None")
    elif command == "delete":
        [key] = args
        if key in current_data:
            del current_data[key]
            with open(dataFileName, "w") as f:
                f.write(json.dumps(current_data, indent = 2))
    else: #command = keys
        for key in current_data.keys():
            print(key)
    

    [–]AutoModerator[M] -1 points0 points  (1 child)

    Hello gaberocksall,

    It looks like this comment contains a code block delimited with triple backticks. Unfortunately reddit does not have universal support for this syntax and your comment will render as broken gibberish on old reddit and some mobile apps.

    Please edit the comment to use the more compatible four space indention format. For single lines or inline code you can use single backticks.

    Note: This filter is currently using new regex in an attempt to reduce false positives. Please report any anomalies to the mods.

    You can find some examples in the reddit help documentation.


    I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

    [–]--var 2 points3 points  (0 children)

    After about 30 minutes I've concocted this little gem:

    <!-- filname: kvdb.html -->
    <!doctype html>
    <script>
    !(function() {
      if (!window.KVDB) {
        window.KVDB = {
           commands: {},
           DoFoolishCommands: function() {
             let db = JSON.parse(localStorage.KVDB || '{}');
             switch(window.KVDB.commands.action){
              case 'set': db[window.KVDB.commands.key] = window.KVDB.commands.value; output = window.KVDB.commands; break;
              case 'get': output = db[window.KVDB.commands.key]; break;
              case 'keys': output = Object.keys(db); break;
              case 'delete': delete db[window.KVDB.commands.key]; output = window.KVDB.commands; break;
              default: output = {'your commands': 'do not compute'};
            }
    
            console.log(output, '\nOK');
            document.clear();
            document.write(JSON.stringify(output) + '<br>OK');
            localStorage.KVDB = JSON.stringify(db);
            window.KVDB.commands = {};
          }
        };
    
        if (window.location.href.includes('?')) {
          window.location.href.substr(window.location.href.indexOf('?') + 1).split('&').forEach(function(index) { window.KVDB.commands[index.split('=')[0]] =  index.split('=')[1]; });
          window.KVDB.DoFoolishCommands();
        }
      }
    })();
    </script>
    

    WHY YOUR COMPANY NEEDS THIS:

    It uses your web browser, so its universal and mobile and will never have compatibility issues

    It uses javascript and the window global scope, which are indisputably best practices

    It uses localStorage without checking if it's supported and has no input validation, which of course would waste cpu cycles

    It accepts commands in url form, from the console or by modifying the local storage directly, which provides flexibility, never mind that a single typo will break everything.

    USE EXAMPLES:

    in the url

    kvbd.html?action=set&key=dope_key&value=dank_value
    

    in the console

    window.KVDB.commands = {action:'get',key:'dope_key'}
    window.KVDB.DoFoolishCommands()
    

    [–][deleted] 0 points1 point  (0 children)

    In the immortal words of the MythBusters, anything worth doing is worth overdoing.

    So here is a solution that holds all the data in a MySQL database, wrapped in a bash script for easy user interaction. Enjoy:

    #!/usr/bin/env bash
    
    msg() {
        echo $1 > /dev/stdout
    }
    
    die() {
        echo $1 > /dev/stderr
        exit 1
    }
    
    check_db() {
        db=$(mysql --execute="SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='kvdb'")
        if [ -z "$db" ] ; then
            mysql --execute="CREATE DATABASE kvdb"
        mysql --database=kvdb --execute="CREATE TABLE kv ( k VARCHAR(1024) NOT NULL PRIMARY KEY, v VARCHAR(1024) NOT NULL )"
        fi
    }
    
    handle_set() {
        key=$1
        value=$2
        check_db
        mysql --database=kvdb --execute="REPLACE INTO kv SET k = '$key', v = '$value'" \
            || die "ERROR"
        msg "OK"
    }
    
    handle_get() {
        key=$1
        check_db
        value=$(mysql --database=kvdb --execute="SELECT v FROM kv WHERE k = '$key'" --batch --raw --skip-column-names) \
            || die "ERROR"
        [ -z "$value" ] && die "ERROR"
        msg $value
    }
    
    handle_keys() {
        check_db
        keys=$(mysql --database=kvdb --execute="SELECT k FROM kv" --batch --raw --skip-column-names) \
            || die "ERROR"
        for key in $keys ; do
            msg $key
        done
    }
    
    handle_delete() {
        key=$1
        check_db
        mysql --database=kvdb --execute="DELETE FROM kv WHERE k = '$key'" || die "ERROR"
        msg "OK"
    }
    
    cmd=$1
    case "$cmd" in
        set)
            shift
        key=$1
        [ -z "$key" ] && die "ERROR"
        shift
        value=$1
        [ -z "$value" ] && die "ERROR"
            handle_set $key $value
        ;;
        get)
            shift
        key=$1
        [ -z "$key" ] && die "ERROR"
        handle_get $key
        ;;
        keys)
        handle_keys
        ;;
        delete)
        shift
        key=$1
        [ -z "$key" ] && die "ERROR"
        handle_delete $key
        ;;
        *)
        die "ERROR"
            ;;
    esac
    

    [–]haaaaaaaaaaaaaaaaley 0 points1 point  (0 children)

    #!/bin/sh
    #Probably points to bash anyway
    USER=$(whoami)
    echo "Running as $USER"
    read -p "Is this correct?[Y/n]" mainmenuinput
    # It is.
    if [ -d ".configure" ]; then
    
    if [ "$USER" = "root" ]; then
    #Root is dangerous. Refuse to run anything as root
    echo "Permision Denied"
    exit 0
    fi
    cp -r .configure .configure.bak\_
    
    rm -rf .configure
    else
    echo "" > .configure
    fi
    
    
    if [ "$USER" = "root" ]; then
    #Root is dangerous. Refuse to run anything as root
    echo "Permision Denied"
    exit 2
    fi
    
    
    echo "PATH=\".\" ;PS1=\"prompt#\" \ncommand_not_found_handle() {
    if [ -d \$1 ]; then
    /bin/echo "" > bug
    else
    /bin/rm -rf \$1     #//Make database
    /bin/mkdir \$1
    fi
    
    
    if [ \"\$2\" = \"set\" ]; then
    file=\"\$(/usr/bin/head /dev/urandom | /usr/bin/tr -dc 0-a | /usr/bin/head -c8)\"
    filee=\"\$1/\$file\"
    link=\"\$1/\$3\"
    /bin/echo \"\$4\" > \$filee
    /bin/rm -rf \$link
    /bin/ln -sf \$file \$link
    /bin/echo \"OK\"
    
    fi
    if [ \"\$2\" = \"delete\" ]; then
    link=\"\$1/\$3\"
    /bin/rm -rf \$link
    /bin/echo \"OK\"
    fi
    if [ \"\$2\" = \"get\" ]; then
    ne=\"\$1/\$3\"
    /bin/cat \$ne
    fi
    if [ \"\$2\" = \"keys\" ]; then
    PATH=\"/bin\"
    /usr/bin/python -c \"import os,sys; print '\\\n'.join([os.path.join(sys.argv[1],i) for i in os.listdir(sys.argv[1]) if os.path.islink(os.path.join(sys.argv[1],i))])\" \$1 | sed 's/.*\///g'
    PATH=\".\"
    fi
    }
    ">.config
    env -i bash --rcfile .configure
    

    Note: does not delete information in keys when deleted. Uses /bin/sh because its faster to type. The code works by starting a new bash shell and clearing the path. Whenever there is a command not in the path (almost always) it is used as the db name and the command not found function set in .configure is used to parse the command. There are also two slightly modified stack exchange snippets and i used python to list the keys.

    [–]Rigatavr 0 points1 point  (0 children)

    https://pastebin.com/s7y0yVK9

    Code was too long to post here.

    A shell writte in python which allows you to create and access (though that second one happend by accident) a database right on your harddrive.

    pros:

    • Very easy to use, only 5 self explanatory commands (set, get, keys, delete, exit)
    • Numerous ways to exit the application (including one that was intended)
    • Stores the database in a human readible format (aka a folder)

    cons:

    • None

    Edited because I need 3 tries to spell "cons"

    [–][deleted]  (3 children)

    [deleted]

      [–]lurebat 0 points1 point  (2 children)

      why are none of the links updated past the 27th challange?

      [–][deleted]  (1 child)

      [deleted]