在shell脚本中的while循环内增加一个变量值

时间:2014-03-04 17:24:07

标签: shell while-loop increment

我正在尝试在while循环中递增变量值,但我想将它递增两次。 “两次”我的意思是第一次增加变量值,然后做一些操作,然后再增加已增加的值,这都是在while循环中。我的代码如下所示:

    i=1
    setglobal="SET GLOBAL "
    while [ $i -le $# ]
    do
         assign=$setglobal$i=$($i+1)
         START=`date +%s`
         mysql $database -u $user -se "$assign;select
         here goes my database query, not important"
         END=`date +%s`
         echo $END - $START | bc>>output.txt
         i=$(($i+1))
         mysqld restart
    done

我有一个发送到我的shell的参数列表:innodb_change_buffer_max_size 16 key_buffer_size 1431655770,分别作为第1,第2,第3和第4个参数。所以我想要while循环:

    SET GLOBAL innodb_change_buffer_max_size = 16

之后

    assign=$setglobal$i=$($i+1)

    SET GLOBAL key_buffer_size = 1431655770

之后

    i=$(($i+1))
    assign=$setglobal$i=$($i+1)

结果,在我的output.txt中,我应该得到每个查询的运行时间,但是我只得到四个零。所以我想我的循环要么没有正确地执行“SET GLOBAL key_buffer_size = 512”这一部分,要么它没有做正确的增量。谁能告诉我我的代码可能有什么问题?

2 个答案:

答案 0 :(得分:1)

我相信mysql可以从stdin读取命令,比a更具可读性 单双引号。

while (( $# > 1 )); do
    var=$1
    val=$2
    shift 2
    start=$SECONDS

    mysql $database -u $user -s <<ENDSQL
SET GLOBAL $var=$val;
select ...
ENDSQL

    echo $(( $SECONDS - $start )) >> output.txt
    mysqld restart
done

如果你有bash 4,我会处理这样的论点:

declare -A vars
while (( $# > 1 )); do
    vars[$1]=$2
    shift 2
done

foreach var "${!vars[@]}"; do
    start=$SECONDS

    mysql $database -u $user -s <<ENDSQL
SET GLOBAL $var=${vars[$var]};
# ... rest is the same
done

假设您可以在一个会话中设置多个sql全局:

sql_commands=()
while (( $# > 1 )); do
    sql_commands+=( "SET GLOBAL $1=$2;" )
    shift 2
done
sql_commands+=( "select ... from ... where ...;" )

start=$SECONDS

printf "%s\n" "${sql_commands[@]}" |  mysql $database -u $user -s

echo $(( $SECONDS - $start )) >> output.txt
mysqld restart

答案 1 :(得分:1)

glenn jackman's helpful answer提供bash解决方案。

但是,鉴于您通常标记了问题shell并且您的代码似乎只使用符合POSIX的功能(错误除外),我认为您正在寻找符合POSIX标准的解决方案

#!/bin/sh

# Make sure that parameters were passed and that they were passed in pairs.
if [ $# -eq 0 ] || [ $(( $# % 2 )) -ne 0 ]; then
  echo "ERROR: Please pass parameters in pairs, and pass at least one pair." >&2
  exit 2
fi

while [ $# -gt 0 ]; do # Continue while arguments remain.

  # Store the (next) pair of parameters in variables.
  name=$1
  val=$2
  shift 2 # Remove the (next) 2 positional parameters from the start of the array.

  # Synthesize the mysql SET statement.
  assign="SET GLOBAL $name=$val"

  # Get start timestanp (resolution in seconds).
  start=$(date +%s)

  # Invoke the mysql command.
  mysql $database -u $user -se "$assign;select
  here goes my database query, not important"

  # Get end timestamp.
  end=$(date +%s)

  # Calculate the execution time span and append it to the output file.
  echo $(( end - start )) >>output.txt

  # Restart mysql.
  mysqld restart

done

至于您尝试过的内容

  • $($i+1)无法作为对$i位置参数的引用:

    • 由于$(...)命令替换(现代等效于`...`),$i+1的扩展结果实际上会被解释为命令执行,这不是意图。
  • 仅使用POSIX功能,没有直接通过 index 引用位置参数的方法。

    • 相比之下,在bash中您可以使用${@:i:1},但这是非标准扩展名。)
  • 因此,最简单的方法是在将值保存在变量中后,使用shift 消耗位置参数(从参数数组的开头删除它们),以及然后检查是否有[ $# -gt 0 ]。这也消除了对辅助索引变量$i的需求。

  • 虽然echo $END - $START | bc有效,但不需要涉及外部实用程序bc,因为简单的算术扩展$(( END - START ))就可以了。

  • 最好避免变量名称,例如$START$END,因为全大写的shell变量名称可以conflict with environment variables and special shell variables

    < / LI>