如何在 Bash 中保持 MySQL 连接打开

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3463106/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 22:29:00  来源:igfitidea点击:

How to Keep a MySQL Connection Open in Bash

mysqlbash

提问by User1

I have a bash script that calls MySQL several times. Instead of having to reconnect to MySQL, is there a way to keep the connection open? Ideally, the connection would close if the script exits early. I'm thinking named pipes would work but they would stay open.

我有一个多次调用 MySQL 的 bash 脚本。有没有办法保持连接打开,而不是重新连接到 MySQL?理想情况下,如果脚本提前退出,连接将关闭。我认为命名管道会起作用,但它们会保持打开状态。

Here's a quick pseudo-example of what I hope to find:

这是我希望找到的快速伪示例:


openMySQL
executeMySQL "SELECT 1"
exit 1
executeMySQL "SELECT 2"

I'm looking for the openMySQLand executeMySQLfunctions where the MySQL connection will actually close during the exit 1.

我在寻找的openMySQLexecuteMySQL功能在MySQL连接将在实际关闭exit 1

回答by User1

I have part of what I was looking for.

我有我一直在寻找的一部分。

Keep the mysql connection open using fd=3 for writing:

使用 fd=3 保持 mysql 连接打开以进行写入:


exec 3> >(mysql)
echo "SELECT 1;" >&3
echo "SELECT 2;" >&3
exec 3>&-

Keep the mysql connection open using fd=3 for reading:

使用 fd=3 保持 mysql 连接打开以进行读取:


exec 3< <(echo "SELECT 1;SELECT 2;"|mysql|sed '1d')
while read <&3
do
  echo $REPLY
done

Note: sed '1d' removes the header.

注意: sed '1d' 删除标题。

Is there any way to merge these so you can write to one fd and read from another?

有什么方法可以合并这些,以便您可以写入一个 fd 并从另一个 fd 读取?

回答by conny

To the best of my understanding your question: coproc'savailable in zsh/ksh and also bash v4+ might be similar to what you have in mind, e.g.

据我所知,您的问题是:在 zsh/ksh 和 bash v4+ 中可用的coproc可能与您的想法相似,例如

bash4-4.1$ coproc MYSQL mysql -B -uroot 
[1] 10603
bash4-4.1$ jobs
[1]+  Running                 coproc COPROC MYSQL mysql -B -uroot &
bash4-4.1$ echo 'show databases;' | MYSQL
Database
information_schema
...

The command is kept running in the background, its stdin/stdout can accessed, it will finish (as a result its standard input closing/EOFing) as soon as the current shell exists...

该命令在后台保持运行,可以访问它的 stdin/stdout,只要当前 shell 存在,它就会完成(因此它的标准输入关闭/ EOFing)......

回答by NORMAN GEIST

I know this thread is old, but I was also looking for a comfortable bash mysql session implementation and didn't found something good enough for my needs, so I wrote my own one which I'd like to share with the world.

我知道这个线程很旧,但我也在寻找一个舒适的 bash mysql 会话实现,但没有找到足以满足我需求的东西,所以我写了我自己的一个,我想与世界分享。

############### BASIC MYSQL SESSION IMPLEMENTATION FOR BASH (by Norman 

Geist 2015) #############
# requires coproc, stdbuf, mysql
#args: handle query
function mysql_check {
  local handle
  handle=(${1//_/ })
  #has right structure && is still running && we opened it?
  if [[ ${#handle[*]} == 3 ]] && ps -p ${handle[2]} 2>> /dev/null >> /dev/null && { echo "" >&${handle[1]}; } 2> /dev/null; then
    return 0
  fi
  return 1
}

# open mysql connection
#args: -u user [-H host] [-p passwd] -d db
#returns $HANDLE
function mysql_connect {
  local argv argc user pass host db HANDLEID i
  #prepare args
  argv=($*)
  argc=${#argv[*]}

  #get options
  user=""
  pass=""
  host="localhost"
  db=""
  for ((i=0; $i < $argc; i++))
  do
    if [[ ${argv[$i]} == "-h" ]]; then
      echo "Usage: -u user [-H host] [-p passwd] -d db"
      return 0
    elif [[ ${argv[$i]} == "-u" ]]; then
      i=$[$i+1]
      if [[ ${#argv[$i]} -gt 0 ]]; then
    user=${argv[$i]}
      else
    echo "ERROR: -u expects argument!"
    return 1
      fi
    elif [[ ${argv[$i]} == "-p" ]]; then
      i=$[$i+1]
      if [[ ${#argv[$i]} -gt 0 ]]; then
    pass="-p"${argv[$i]}
      else
    echo "ERROR: -p expects argument!"
    return 1
      fi
    elif [[ ${argv[$i]} == "-H" ]]; then
      i=$[$i+1]
      if [[ ${#argv[$i]} -gt 0 ]]; then
    host=${argv[$i]}
      else
    echo "ERROR: -H expects argument!"
    return 1
      fi
    elif [[ ${argv[$i]} == "-d" ]]; then
      i=$[$i+1]
      if [[ ${#argv[$i]} -gt 0 ]]; then
    db=${argv[$i]}
      else
    echo "ERROR: -d expects argument!"
    return 1
      fi
    fi
  done

  if [[ ${#user} -lt 1 || ${#db} -lt 1 ]]; then
    echo "ERROR: Options -u user and -d db are required!"
    return 1;
  fi

  #init connection and channels
  #we do it in XML cause otherwise we can't detect the end of data and so would need a read timeout O_o
  HANDLEID="MYSQL$RANDOM"
  eval "coproc $HANDLEID { stdbuf -oL mysql -u $user $pass -h $host -D $db --force --unbuffered --xml -vvv 2>&1; }" 2> /dev/null
  HANDLE=$(eval 'echo ${'${HANDLEID}'[0]}_${'${HANDLEID}'[1]}_${'${HANDLEID}'_PID}')
  if mysql_check $HANDLE; then
    export HANDLE
    return 0
  else
    echo "ERROR: Connection failed to $user@$host->DB:$db!"
    return 1
  fi
}

#args: handle query
#return: $DATA[0] = affected rows/number of sets; 
#        $DATA[1] = key=>values pairs following
#        $DATA[2]key; DATA[3]=val ...
function mysql_query {
  local handle query affected line results_open row_open cols key val 
  if ! mysql_check ; then
    echo "ERROR: Connection not open!"
    return 1
  fi
  handle=(${1//_/ })

  #delimit query; otherwise we block forever/timeout
  query=
  if [[ ! "$query" =~ \;$ ]]; then
    query="$query;"
  fi
  #send query
  echo "$query" >&${handle[1]}

  #get output
  DATA=();
  DATA[0]=0
  DATA[1]=0
  results_open=0
  row_open=0
  cols=0
  while read -t $MYSQL_READ_TIMEOUT -ru ${handle[0]} line
  do 
    #WAS ERROR?
    if [[ "$line" == *"ERROR"* ]]; then
      echo "$line"
      return 1
    #WAS INSERT/UPDATE?
    elif [[ "$line" == *"Query OK"* ]]; then
      affected=$([[ "$line" =~ Query\ OK\,\ ([0-9]+)\ rows?\ affected ]] && echo ${BASH_REMATCH[1]})
      DATA[0]=$affected
      export DATA
      return 0
    fi

    #BEGIN OF RESULTS
    if [[ $line =~ \<resultset ]]; then
      results_open=1
    fi

    #RESULTS
    if [[ $results_open == 1 ]]; then
      if [[ $line =~ \<row ]]; then
    row_open=1
    cols=0
      elif [[ $line =~ \<field && $row_open == 1 ]]; then
    key=$([[ "$line" =~ name\=\"([^\"]+)\" ]] && echo ${BASH_REMATCH[1]})
    val=$([[ "$line" =~ \>(.*)\<\/ ]] && echo ${BASH_REMATCH[1]} || echo "NULL")
    DATA[${#DATA[*]}]=$key
    DATA[${#DATA[*]}]=$val
    cols=$[$cols+1]
      elif [[ $line =~ \<\/row ]]; then
    row_open=0
    DATA[0]=$[${DATA[0]}+1]
    DATA[1]=$cols
      fi
    fi

    #END OF RESULTS
    if [[ $line =~ \<\/resultset ]]; then
      export DATA
      return 0
    fi
  done
  #we can only get here
  #if read times out O_o
  echo "$FUNCNAME: Read timed out!"
  return 1
}

#args: handle
function mysql_close {
  local handle
  if ! mysql_check ; then
    echo "ERROR: Connection not open!"
    return 1
  fi
  handle=(${1//_/ })
  echo "exit;" >&${handle[1]}

  if ! mysql_check ; then
    return 0
  else
    echo "ERROR: Couldn't close connection!"
    return 1
  fi
}
############### END BASIC MYSQL SESSION IMPLEMENTATION FOR BASH ################################

# Example usage
#define timeout for read command, in case of server error etc.
export MYSQL_READ_TIMEOUT=10

# Connect to db and get $HANDLE
mysql_connect -u mydbuser -d mydb -H mydbserver

#query db and get $DATA
mysql_query $HANDLE "SELECT dt_whatever from tbl_lol WHERE dt_rofl=10"

#close connection
mysql_close $HANDLE

NOTES:

笔记:

  • Save $HANDLE to a new variable after connection to open as many connections as you like
  • You can't exchange $HANDLE between bash sessions
  • You need linux packages "coproc" "stdbuf" "mysql"
  • Return DATA is of a bash array
  • 连接后将 $HANDLE 保存到新变量以打开任意数量的连接
  • 你不能在 bash 会话之间交换 $HANDLE
  • 你需要 linux 包“coproc”“stdbuf”“mysql”
  • 返回数据是一个 bash 数组
$DATA[0] = affected rows/number of sets;
$DATA[1] = number of key=>values pairs following;
$DATA[2] = key1;
$DATA[3] = value1;
      [...]
$DATA[n-1] = keyn;
$DATA[n]   = valuen;
$DATA[0] = affected rows/number of sets;
$DATA[1] = number of key=>values pairs following;
$DATA[2] = key1;
$DATA[3] = value1;
      [...]
$DATA[n-1] = keyn;
$DATA[n]   = valuen;
  • Generally all queries should work, even "SELECT count(*)"
  • 通常所有查询都应该有效,即使是“SELECT count(*)”

Example how to loop the returned data of a two column query

示例如何循环两列查询的返回数据

eg. "SELECT dt_id, dt_name FROM ..."

例如。“选择 dt_id,dt_name 来自 ...”

fields=2
for ((i=2; $i<$((${DATA[0]}*${DATA[1]}*$fields)); i+=$((${DATA[1]}*$fields))))
do
    field1key   = ${DATA[$i]};   #this is "dt_id"
    field1value = ${DATA[$i+1]}; #this is the value for dt_id
    field2key   = ${DATA[$i+2]}; #this is "dt_name"
    field2value = ${DATA[$i+3]}; #this is the value  for dt_name
done