使用while循环迭代参数列表会进行无限循环

时间:2018-05-10 19:06:18

标签: bash shell

我将一些日期参数放入脚本

MySqlDataAdapter.SelectCommand.CommandTimeout = 600

在set-x上我进入无限循环。它今天永远不会打印出来。我通过shellcheck运行它,它说它没问题。

#!/bin/bash
set -x
today=$(date +%Y%m%d)
while  [ ! $# -eq 0 ] #while number of arguments is NOT zero, do this
    do
    if     [[ "$1" = "--test" ]] || [[ "$1" = "-t" ]] ;     then
            today=$(date +%Y%m%d).test
    elif  [[ "$1" = "--yesterday" ]]  || [[ "$1" =  "-y" ]] ; then
            DOW=$(date +%u)
            if [[ "$DOW" =  "1" ]] ; then
                today=$(date --date="3 days ago" +%Y%m%d)
            else
                today=$(date -d "yesterday 13:00" +%Y%m%d)
            fi
    fi
done

echo "$today"
exit 

3 个答案:

答案 0 :(得分:5)

是的,非常明显,位置参数count $#将始终是传递的参数数量,除非你对它进行等效的减量操作,循环永远不会退出。

您需要在shift条件之后使用else,以便将位置参数移动1,然后循环决定是否还有其他参数需要处理。

你最终进入了这个循环,因为首先使用while循环处理参数是一个坏主意,应该使用for循环

for arg; do

或传统的C风格循环

for ((i=1; i<=$#; i++)); do

为了满足您在while循环中的目的,您可以执行以下操作,但建议 。请注意shift这是原始帖子中缺少的导致无限循环的内容。

while [ -n "$1" ]; do
  shift
  # Your code logic goes here
done

答案 1 :(得分:3)

错误来自循环中缺少shift,阻止循环查看所有命令行参数。

建议:

#!/bin/sh

fmt='%Y%m%d'
when='now'

while [ "$#" -ne 0 ]; do
    case "$1" in
        -t|--test)                   # or just:  -t|--t*)
            fmt="$fmt.test" ;;
        -y|--yesterday)              # or just:  -y|--y*)
            if [ "$(date +%u)" -eq 1 ]; then
                when='3 days ago'    # or 'last friday'
            else
                when='yesterday 13:00'
            fi ;;
        [!-]*) break ;;        # non-option detected, break out of loop
        --)    shift; break ;; # explicit end of options detected, break
        *)  echo 'Error in command line options' >&2
            exit 1
    esac
    shift
done

today=$(date --date="$when" +"$fmt")
echo "$today"

这是相同的脚本,但是我选择使用case ... esac而不是if语句来查看参数,除了更容易阅读之外,还允许您支持缩写命令行选项(参见代码中的注释)。

我还将today的最终设置移到脚本的末尾,并且只在循环中设置whenfmt。请注意,这可以让您同时使用--test--yesterday

如果您的脚本不需要长选项,可以编写

#!/bin/sh

fmt='%Y%m%d'
when='now'

while getopts 'ty' opt; do
    case "$opt" in
        t)
            fmt="$fmt.test" ;;
        y)
            DOW=$(date +%u)
            if [ "$(date +%u)" -eq 1 ]; then
                when='3 days ago'
            else
                when='yesterday 13:00'
            fi ;;
        *)  echo 'Error in command line options' >&2
            exit 1
    esac
done

shift "$(( OPTIND - 1 ))"

today=$(date --date="$when" +"$fmt")
echo "$today"

这样就可以使用-ty同时指定-t-y

只有在稍后需要在脚本中使用命令行中的非选项参数时才需要最后的shift

由于上述脚本未使用bash特定内容,因此我选择使用/bin/sh运行它们。

答案 2 :(得分:1)

或使用棘手的getopt命令:

#!/bin/bash
tmp=$(getopt -o ty --long test,yesterday -- "$@")
[[ $? -eq 0 ]] || exit
eval set -- "$tmp"

date_fmt="+%Y%m%d"

while true; do
    case $1 in
        -t|--test) 
            today=$(date "$date_fmt").test
            shift
            ;;
        -y|--yesterday)
            dow=$(date +%w)     # (0..6), 0 is Sunday
            if (( $dow <= 1 )); then
                rel_date="last friday"
            else
                rel_date=yesterday
            fi
            today=$(date -d "$rel_date" "$date_fmt")
            shift
            ;;
        --) shift; break ;;
        *) echo "Oops, something is wrong" >&2; exit 2 ;;
    esac
done
echo "${today:=$(date "$date_fmt")}"