简体   繁体   English

随机生成发票 ID - 将文本数据库移动到脚本文件中?

[英]Randomly generating invoice IDs - moving text database into script file?

I've come up with the following bash script to randomly generate invoice numbers, preventing duplications by logging all generated numbers to a text file "database".我想出了以下bash脚本来随机生成发票编号,通过将所有生成的编号记录到文本文件“数据库”来防止重复。

To my surprise the script actually works, and it seems robust (although I'd be glad to have any flaws pointed out to me at this early stage rather than later on).令我惊讶的是,该脚本确实有效,而且看起来很健壮(尽管我很高兴在这个早期阶段而不是以后向我指出任何缺陷)。

What I'm now wondering is whether it's at all possible to move the "database" of generated numbers into the script file itself.我现在想知道的是,是否有可能将生成数字的“数据库”移动到脚本文件本身中。 This would allow me to rely on and keep track of just the one file rather than two separate ones.这将允许我只依赖和跟踪一个文件,而不是两个单独的文件。

Is this at all possible, and if so, how?这有可能吗?如果有,怎么做? If it isn't a good idea, what valid reasons are there not to do so?如果这不是一个好主意,那么有什么正当理由不这样做呢?

#!/usr/bin/env bash

generate_num() {
#num=$(head /dev/urandom | tr -dc '[:digit:]' | cut -c 1-5) [Original method, no longer used]
num=$(shuf -i 10000-99999 -n 1)
}

read -p "Are you sure you want to generate a new invoice ID? [Y/n] " -n 1 -r
echo
    if [[ $REPLY =~ ^[Yy]$ ]] 
    then
        generate_num && echo Generating a random invoice ID and checking it against the database...
        sleep 2

        while grep -xq "$num" "ID_database"
            do
                echo Invoice ID \#$num already exists in the database...
                sleep 2
                generate_num && echo Generating new random invoice ID and checking against database...
                sleep 2
            done

        while [[ ${#num} -gt 5 ]]
            do
                echo Invoice ID \#$num is more than 5 digits...
                sleep 2
                generate_num && echo Generating new random invoice ID and checking against database...
                sleep 2
          done

        echo Generated random invoice ID \#$num
        sleep 1
        echo Invoice ID \#$num does not exist in database...
        sleep 2
        echo $num >> "ID_database" && echo Successfully added Invoice ID \#$num to the database.

    else 
        echo "Exiting..."
    fi

Edited已编辑

To answer the question you asked -要回答您提出的问题-

Make sure your file ends with an explicit exit statement.确保您的文件以明确的exit语句结尾。 Without some sort of branching it won't execute past that, so unless there is a gross parsing error anything below could be used as storage space.如果没有某种分支,它将无法执行,因此除非存在严重的解析错误,否则下面的任何内容可以用作存储空间。 Just只是

echo $num >> $0

If you write your records directly onto the bottom of the script, the script grows, but...relatively harmlessly.如果您将记录直接写到脚本的底部,脚本会增长,但是……相对无害。 Just make sure your grep pattern doesn't grab any lines of code, though grep -E '^\d[%]$' seems pretty safe.只需确保您的grep模式不会抓取任何代码行,尽管grep -E '^\d[%]$'似乎很安全。


This is only ever going to give you a max of ~90k id's, and spends unneeded time and cycles on redundancy checking.这只会给您最多约 90k 的 id,并且会花费不必要的时间和周期进行冗余检查。 Is there a limit on the length of the value?值的长度有限制吗?

If you can assure there won't be more than one invoice processed per second,如果您能保证每秒处理的发票不会超过一张,

date +%s >> "ID_database" # the UNIX epoch, seconds since 00:00:00 01/01/1970

If you need more precision that that,如果您需要更高的精度,

date +%Y%m%d%H%M%S%N

will output Year month day hour minute second nanoseconds, which is both immediate and "pretty safe".将 output 年月日时分秒纳秒,既即时又“非常安全”。

date +%s%N # epoch with nanoseconds

is shorter, but doesn't have the convenient side effect of automatically giving you the date and time of invoice creation.更短,但没有自动为您提供发票创建日期和时间的便利副作用。

If you absolutely need to guarantee uniqueness and nanoseconds isn't good enough, use a lock of some sort, and maybe a more fine-grained language.如果您绝对需要保证唯一性并且纳秒还不够好,请使用某种锁,也许是更细粒度的语言。

On the other hand, if minutes are unique enough , you could use另一方面,如果分钟足够独特,您可以使用

 date +%y%m%d%H%M

You get the idea.你明白了。

I do not recommend this because:我不推荐这个,因为:

  • These things are fragile.这些东西很脆弱。 One bad edit and your invoice database is corrupt.一次错误的编辑,您的发票数据库已损坏。
  • It makes version control a pain.它使版本控制变得很痛苦。 Each new version of the script should preferably be checked in. You could add logic to make sure that "$mydir" is an empty directory when you run the script (except for "$myname" , .git and other -related files) then run git -C "$mydir" init if "$mydir"/.git doesn't exist.最好签入脚本的每个新版本。您可以添加逻辑以确保在运行脚本时"$mydir"是一个空目录( "$myname" 、. .git和其他相关文件除外)然后运行git -C "$mydir" init如果"$mydir"/.git不存在。 Then for each database update, git -C "$mydir" add "$myname" and git -C "$mydir" commit -m "$num" .然后对于每个数据库更新, git -C "$mydir" add "$myname"git -C "$mydir" commit -m "$num" It's just an idea to explore...这只是一个探索的想法......
  • Locking - It's possible to do file locking to make sure that not two users run the script at the same time, but it adds to the complexity so I didn't bother.锁定 - 可以进行文件锁定以确保不是两个用户同时运行脚本,但它增加了复杂性,所以我没有打扰。 If you feel that's a risk, you need to add that.如果你觉得这是一个风险,你需要添加它。

... but you want a self-modifying script, so here goes. ...但是您想要一个自修改脚本,所以就这样吧。

This just adds a new invoice number to its internal database for each time you run it.这只是在您每次运行时将一个新的发票编号添加到其内部数据库中。 I've explained what goes on as comments.我已经解释了评论的内容。 The last line should read __INVOICES__ (+ a newline) if you copy the script.如果您复制脚本,最后一行应为__INVOICES__ (+ 换行符)。

As always when dealing with things like this, remember to make a backup before making changes:-)与往常一样,在处理此类事情时,请记住在进行更改之前进行备份:-)

As it's currently written, you can only add one invoice per run.正如目前所写,每次运行只能添加一张发票。 It shouldn't be hard to move things around (you need a new tempfile) to get it to add more than one if you need that.如果需要的话,移动东西应该不难(你需要一个新的临时文件)让它添加多个。

#!/bin/bash

set -e    # exit on error - imporant for this type of script
#------------------------------------------------------------------------------
myname="$0"
mydir=$(dirname "$myname")

if [[ ! -w $myname ]]; then
    echo "ERROR: You don't have permission to update $myname" >&2
    exit 1
fi

# create a tempfile to be able to update the database in the file later
#
# set -e makes the script end if this fails:
temp=$(mktemp -p "$mydir")
trap "{ rm -f "$temp"; }" EXIT # remove the tempfile if we die for some reason

# read current database from the file
readarray -t ID_database <<< $(sed '0,/^__INVOICES__$/d' "$0")
#declare -p ID_database >&2    # debug
#------------------------------------------------------------------------------

# a function to check if a number is already in the db
is_it_taken() {
    local num=$1
    # return 1 (true, yes it's taken) if the regex found a match
    [[ ! " ${ID_database[@]} " =~ " ${num} " ]]
}

generate_num() {
    local num
    (exit 1) # set $? to 1

    # loop until $? becomes 0
    while (( $? )); do
        num=$(shuf -i 10000-99999 -n 1)
        is_it_taken "$num"
    done

    # we found a free number
    echo $num
}

add_to_db() {
    local num=$1

    # add to db in memory
    ID_database+=($num)

    # add to db in file:

    # copy the script to the tempfile
    cp -pf "$myname" "$temp"

    # add the new number
    echo $num >> "$temp"

    # move the tempfile into place
    mv "$temp" "$myname"
}
#------------------------------------------------------------------------------

num=$(generate_num)

add_to_db $num

# your business logic goes here:

echo "All current invoices:"
for invoice in ${ID_database[@]}
do
    echo ">$invoice<"
done

#------------------------------------------------------------------------------
# leave the rest untouched:
exit
__INVOICES__

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM