简体   繁体   English

bash——在运行之间存储变量的更好方法?

[英]bash—Better way to store variable between runs?

I've made a bash script which I run every hour with crontab, and I need to store one variable so that I can access it the next time I run it.我制作了一个 bash 脚本,我每小时使用 crontab 运行该脚本,我需要存储一个变量,以便下次运行时可以访问它。 The script changes the variable every time it runs, so I can't hardcode it in. Right now I am writing it to a txt file and then reading it back.脚本每次运行时都会更改变量,因此我无法对其进行硬编码。现在我正在将其写入 txt 文件,然后再将其读回。 Is there a better way to do it than this?有没有比这更好的方法呢? And the way I am reading the txt file is something I found on here, I don't understand it, and it's kinda clunky.我阅读 txt 文件的方式是我在这里找到的,我不明白,而且有点笨拙。 Is there not a built in command for this?没有内置命令吗? Anyway, here's the applicable code, with some of the variables changed to make it easier to read.无论如何,这是适用的代码,其中一些变量已更改以使其更易于阅读。

while read x; do
  var=$x
done < var.txt

# Do some stuff, change var to a new value

echo $var > var.txt

The variable is only a single integer, so the text file feels overkill.变量只是一个整数,所以文本文件感觉有点过头了。

There's no need to use var ;没有必要使用var x will be in scope for the current shell. x将在当前 shell 的范围内。 Alternately,交替,

read var < var.txt
# do stuff with var
echo $var > var.txt

I recommend using a simple text file to store the variable.我建议使用一个简单的文本文件来存储变量。 However, there is the (highly questionable) option of a self-modifying script.但是,存在自修改脚本的(非常有问题的)选项。 FOR ENTERTAINMENT PURPOSES ONLY!仅供娱乐!

#!/bin/bash

read val < <( tail -n 1 "$0" )

(( val++ ))
echo "$val"

tmp=$(mktemp /tmp/XXXXXXX)
sed '$s/.*/'$val'/' "$0" > "$tmp"
mv "$tmp" "$0"

exit
0

The key is to have the next-to-last line be the exit command, so nothing after it will execute.关键是让倒数第二行是退出命令,所以它之后什么都不会执行。 The last line is the variable value you want to persist.最后一行是你想要持久化的变量值。 When the script runs, it read s from its own last line.当脚本运行时,它从它自己的最后一行read s。 Before it exits, it uses sed to write a copy of itself toa temp file, with the last line modified with the current value of the persistent value.在它退出之前,它使用sed将自己的副本写入临时文件,并使用持久值的当前值修改最后一行。 Then we overwrite the current script with the temp file (assuming we will have permission to do so).然后我们用临时文件覆盖当前脚本(假设我们有权限这样做)。

But seriously?不过实话说? Don't do this.不要这样做。

I know this is an old question.我知道这是一个老问题。 But, I still decide to post my solution here in the hope that it might be helpful to others who come here in search of a way to serialize env vars between sessions.但是,我仍然决定在这里发布我的解决方案,希望它可能对来这里寻找在会话之间序列化环境变量的方法的其他人有所帮助。

The simple way is just write "var_name=var_value" into a file, say "./environ".简单的方法是将“var_name=var_value”写入文件,比如“./environ”。 And then "source ./envrion" in following sessions.然后在以下会话中“source ./envrion”。 For example:例如:

echo "var1=$var1" > ./environ

A more comprehensive (and elegant?) way which persist all attributes of variables is to make use of "declare -p":保留变量所有属性的更全面(更优雅?)的方法是使用“declare -p”:

declare -p var1 var2 > ./environ
# NOTE: no '$' before var1, var2

Later on, after "source ./envrion" you can get var1 var2 with all attributes restored in addition to its value.稍后,在“source ./envrion”之后,您可以获得 var1 var2,除了其值之外还恢复了所有属性。 This means it can handle arrays, integers etc.这意味着它可以处理数组、整数等。

One caveat for the "declare -p xx", though: if you wrap the "source ./environ" into a function, then all sourced variables are visible within the function only because "declare" by default declares variables as local ones.但是,对“declare -p xx”的一个警告:如果将“source ./environ”包装到一个函数中,那么所有源变量在函数中都是可见的,因为“declare”默认情况下将变量声明为本地变量。 To circumvent this, you may either "source" out of any function (or in your "main" function) or modify the ./environ to add "-g" after declare (which makes corresponding variable global).为了避免这种情况,您可以从任何函数(或在您的“主”函数中)“源”或修改 ./environ 以在声明后添加“-g”(这使相应的变量成为全局变量)。 For instance:例如:

sed -i 's/^declare\( -g\)*/declare -g/' ./environ
# "\( -g\)?" ensure no duplication of "-g"

1- You can simplify your script, as you only have one variable 1- 您可以简化脚本,因为您只有一个变量

var=`cat var.txt`
# Do some stuff, change var to a new value   
echo $var > var.txt

2- You can store your variable in the environment: 2-您可以将变量存储在环境中:

export var

# Do some stuff, change var to a new value

But you'll need to prompt it . script.ksh但是你需要提示它. script.ksh . script.ksh (dot at the beggining). . script.ksh (在. script.ksh点)。 But it shouldn't have 'exit' in it and i'm not sure this would work in cron...但它不应该有“退出”,我不确定这是否适用于 cron ......

Depending on your use case this might be overkill, but if you need to store and keep track of multiple variables (or from multiple scripts) then consider using sqlite which has a command line interface (sqlite3 ), and which is usually preinstalled ootb on linux/macos systems.根据您的用例,这可能有点矫枉过正,但如果您需要存储和跟踪多个变量(或来自多个脚本),请考虑使用具有命令行界面(sqlite3 )的sqlite ,并且通常在 linux 上预装 ootb /macos 系统。

DB='storage.db'

KEY1='eurusd'
VAL1=1.19011

KEY2='gbpeur'
VAL2=1.16829

# create table if not present (ONLY NEEDS TO BE RUN ONCE)
QUERY_CREATE="CREATE TABLE IF NOT EXISTS records (id INTEGER PRIMARY KEY, name TEXT NOT NULL, value NUMERIC NOT NULL);"
sqlite3 "$DB" "$QUERY_CREATE"

# write a key-value pair to database (creates a new row each time)
QUERY_INSERT="INSERT INTO records(name, value) VALUES ('${KEY1}', '${VAL1}');"
sqlite3 "$DB" "$QUERY_INSERT"

# write a key-value pair to database (REPLACE previous value!)
# using 42 as a hard-coded row ID
QUERY_REPLACE="REPLACE INTO records(id, name, value) VALUES (42, '${KEY2}', '${VAL2}');"
sqlite3 "$DB" "$QUERY_REPLACE"

# read value from database
QUERY_SELECT1="SELECT value FROM records WHERE name='${KEY1}';"
QUERY_SELECT2="SELECT value FROM records WHERE name='${KEY2}';"

echo "***** $KEY1 *****"
# store db value in a variable
db_value1=$(sqlite3 "$DB" "$QUERY_SELECT1")
echo $db_value1
## OUTPUT: 1.19011

echo "***** $KEY2 *****"
db_value2=$(sqlite3 "$DB" "$QUERY_SELECT2")
echo $db_value2
## OUTPUT: 1.16829

NOTE: If you do not explicitly pass the row ID then a new row will be added on each script invocation.注意:如果您没有明确传递行 ID,那么每次脚本调用时都会添加一个新行。 To always update into the same row use REPLACE INTO with an explicit ID (eg 42 as can be seen in the REPLACE INTO... statement).要始终更新到同一行,请使用带有显式 ID 的REPLACE INTO (例如,在REPLACE INTO...语句中可以看到42 )。 Run the script multiple times to see how the output differs for KEY1 and KEY2 .多次运行脚本以查看KEY1KEY2的输出有何不同。


NOTE2: In this example the values are numeric, if you need to store strings then in CREATE TABLE instead of NUMERIC use TEXT .注意2:在这个例子中,值是数字,如果你需要存储字符串,那么在CREATE TABLE而不是NUMERIC使用TEXT

To store multiple variables between runs, a solution I considered is to save them under the format my_var=my_value in a separated file.为了在运行之间存储多个变量,我考虑的一个解决方案是将它们以my_var=my_value格式保存在一个单独的文件中。

Then, I include two function to set and retrieve the variables然后,我包含两个函数来设置和检索变量

  1. In the file storing the variables and their values:在存储变量及其值的文件中:

Let's call this file context.dat让我们称这个文件为context.dat

# Here I store the variables and their values
my_var_x=1
my_var_y=boo
my_var_z=0
  1. In the actual script:在实际脚本中:

Let's call the file multiple_run.sh让我们调用文件multiple_run.sh

context=./context.dat

function update_variables(){
    # update the variable context
    source $context
}

function set_variable(){
    # store variable
    variable=$1 #variable to be set
    value=$2    # value to give to the value
    # modify the file storing the value
    sed -i 's/'${variable}'.*/'${variable}'='${value}'/' $context
}

##################
# Test code
echo var_x
update_variables
echo var_x
# do something
set_variable var_x 2
echo $var_x

This is one approach among other.这是一种方法。 With such method, you need to create the storing file before and create each line for each variable.使用这种方法,您需要先创建存储文件并为每个变量创建每一行。 Besides, the context.dat is a priori accessible by any other script.此外,context.dat 是任何其他脚本都可以先验地访问的。

Just discovered this great simple project (a rewritten fork).刚刚发现了这个伟大的简单项目(一个重写的分支)。 A simple yet powerful key/value pair store for bash.一个简单而强大的 bash 键/值对存储。 Looks perfect.看起来很完美。 Behind the scenes each database is a directory, each key is a file, and the values are in the file.在幕后,每个数据库都是一个目录,每个键都是一个文件,值都在文件中。

https://github.com/imyller/kv-sh https://github.com/imyller/kv-sh

  • Tiny key-value database微小的键值数据库
  • Configurable database directory (default: ~/.kv-sh )可配置的数据库目录(默认: ~/.kv-sh
  • Used by importing functions via $ . ./kv-sh通过$ . ./kv-sh导入函数使用$ . ./kv-sh $ . ./kv-sh
  • Full database dump/restore完整的数据库转储/恢复
  • Support for secondary read-only defaults database支持二级只读默认数据库
    . ./kv-sh                  # import kv-sh functions (use default database directory; see
                                 configuration environment variables for available options)
    kvset <key> <value>        # assign value to key
    kvget <key>                # get value of key
    kvdel <key>                # delete key
    kvexists <key>             # check if key exists
    kvkeys {-l|-d|-a}          # list all keys (-l local only, -d default only, -a all (default))
    kvlist {-a}                # list all key/value pairs (-a all keys, including default)
    kvdump {-a}                # database dump (-a all keys, including default)
    kvimport                   # database import (overwrite)
    kvrestore                  # database restore (clear and restore)
    kvclear                    # clear database

Defaults database默认数据库

kv-sh supports secondary read-only defaults database. kv-sh支持二级只读默认数据库。 If enabled, keys-value pairs from default value database are returned if local value is not specified.如果启用,如果未指定本地值,则返回默认值数据库中的键值对。

Enable defaults database by setting DB_DEFAULTS_DIR :通过设置DB_DEFAULTS_DIR启用默认数据库:

DB_DIR="/tmp/.kv" DB_DEFAULTS_DIR="/tmp/.kv-default" . ./kv-sh

I ended up doing the following.我最终做了以下事情。 Would prefer the variables in one file, but this bloats the code slightly.更喜欢一个文件中的变量,但这会使代码稍微膨胀。 How does this read thing work?这个阅读的东西是如何工作的? You can store multiple variables in a seperate file, say variables.txt, and then have your main program in say main.sh.您可以将多个变量存储在一个单独的文件中,例如 variables.txt,然后将您的主程序保存在 main.sh 中。 It might be better to write seperate scripts for loading and saving variables though.不过,最好编写单独的脚本来加载和保存变量。

For varibles.txt:对于变量.txt:

A=0
B=0
C=0

For main.sh:对于 main.sh:

#!/bin/bash

#reload variables
A=`cat ./variables.txt|grep "A="|cut -d"=" -f2`
B=`cat ./variables.txt|grep "B="|cut -d"=" -f2`
C=`cat ./variables.txt|grep "C="|cut -d"=" -f2`

#print variables
printf "$A\n"
printf "$B\n"
printf "$C\n"

#update variables
A=$((($A+1)))
B=$((($B+2)))
C=$((($C+3)))

#save variables to file
#for A
 #remove entry for A
 cat ./variables.txt|grep -v "A=">>./tmp.txt
 #save entry for A
 printf "A=$A\n">>./tmp.txt
 #move tmp.txt to variables.txt
mv ./tmp.txt ./variables.txt

#for B
 #remove entry for B
 cat ./variables.txt|grep -v "B=">>./tmp.txt
 #save entry for B
 printf "B=$B\n">>./tmp.txt
 #move tmp.txt to variables.txt
 mv ./tmp.txt ./variables.txt

#for C
 #remove entry for C
 cat ./variables.txt|grep -v "C=">>./tmp.txt
 #save entry for C
 printf "C=$C\n">>./tmp.txt
 #move tmp.txt to variables.txt
 mv ./tmp.txt ./variables.txt

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

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