1

我正在编写一个 bash 脚本来自动化一些工作,同时练习 bash。我正在编写一个脚本来自动化一些 git 克隆和更新的东西。我的脚本将具有三个选项(-g、-f、-h)。当有人键入 -h 时,它应该显示帮助消息(我已经写过但在下面省略了),当有人写 -g 时,它应该接受至少一个参数,但它也可以接受第二个作为可选参数。其中,第一个将是要克隆的存储库的 url,第二个将是目录,克隆它的位置。另一方面,如果有人键入 -f,脚本应该只得到一个参数,只是一个文件名。然后我想逐行读取文件,并对文件中的每个 git url 进行 git 克隆和更新。

如果我运行脚本,我会为每个选项收到以下错误消息,如果我在没有任何选项的情况下调用它,或者在一个有效选项后跟一些其他参数的情况下调用它,它仍然不执行任何操作,只是返回:

./gitupdate.sh: option requires an argument -- g

我想它不会在我的代码中以某种方式使用 $2 和 $3 。但是话又说回来,当我通过 -h 时,它所要做的就是调用帮助函数并显示消息,不需要使用任何其他参数。

我想问题是因为我在底部有问题,我使用 getopts 来获取用户指定的选项名称。在我的代码中,我假设选项是第一个参数 $1,然后第二个参数 $2 是 url 或文件名,根据指定的选项,$3 是仅适用于 -g 选项的可选参数。

您可以在下面找到我的代码:

#!/bin/bash

declare default_directory=$HOME
declare action
declare src

function clone_repo() {
    if [ -n "$2" ]; then
        if [ "$(ls -A "$2" 2>/dev/null)" ]; then
            cd "$2"
        else
            git clone "$1" "$2"
        fi
    else
        git clone "$1"
        #TODO: Get the directory name created by cloning
                #      and cd to it.
    fi

    git remote add upstream "$1"
    git fetch upstream
}

function read_repos_from_file() {
    if [ -f "$1" ]; then
        while read -r line; do
            clone_repo "$line" "$2"
        done < "$1"
    else
        echo -e "Error: The specified file could not be found."
        exit 1
    fi
}

while getopts "f:h:r" option
do
    case "${option}" in
        f) action=read_repos_from_file; src="$OPTARG";;
        g) action=clone_repo; src="$OPTARG";;
        h) help ; exit 1 ;;
    esac
done

shift $((OPTIND-1))

[ -z "$action" ] && ( help; exit 1 )

如果有人可以帮助我,我会很高兴。

编辑:更正了上述代码中的一些拼写错误。并更新了错误信息。脚本无法正常工作。我想我需要更改我的代码中的某些内容,使其执行某些操作,并实际获得 $2 和 $3 参数。它甚至不显示帮助消息,当 -h 选项被传递时,我所做的只是调用我之前创建的帮助函数。也许我需要以某种方式改变我的 getopts 部分。

编辑 2:进行了建议的更改,并更改了上述代码。

4

1 回答 1

1

git()是函数定义的开头(function当函数名后跟括号时,关键字是可选的)。如果你想调用一个函数git(),你需要先定义它,然后在没有括号的情况下调用它:

function git() {
  # do stuff
}

git

但是,创建与现有二进制文件同名的函数并不是一个好习惯。在您的情况下,您可能应该只git clone使用从文件中读取的行调用:

while read -r line; do
  git clone "$line"
done < "${file}"

编辑:更新,因为问题发生了很大变化。

坦率地说,你的论点处理是……很奇怪。当您使用选项解析器时,您不应该绕过选项解析器的工作方式。"g:"表示-g只有一个参数的选项。不要试图使它成为具有多个参数的选项,其中一个参数是可选的。如果您需要输出目录的附加(可选)参数,请将其设为另一个选项(例如"d:")或非选项参数。

我建议将您的选项处理更改为以下内容:

while getopts "f:g:h" option; do
  case "$option" in
    f) action=file; src="$OPTARG";;
    g) action=repo; src="$OPTARG";;
    h) help; exit 1;;
  esac
done

shift $((OPTIND-1))

[ -z "$action" ] && ( help; exit 1 )

在这个“$@”之后只包含非选项参数(在这种情况下是可选的输出目录),所以你可以这样调用你的函数:

$action $src "$@"

将功能简化为如下所示:

function repo() {
  if [ -n "$2" ]; then
    if [ "$(ls -A "$2" 2>/dev/null)" ]; then
      cd "$2"
    else
      git clone "$1" "$2"
    fi
  else
    git clone "$1"
  fi
  ...
}

function file() {
  if [ -f "$1" ]; then
    while read -r line; do
      repo "$line" "$2"
    done < "$1"
  else
    echo "Error: The specified file could not be found."
    exit 1
  fi
}

更笼统地说,你应该让你的名字更不言自明。用于克隆存储库或从文件读取存储库的函数的更好名称分别类似于clone_reporead_repos_from_file。此外,-c或者-r对于触发存储库克隆的选项来说是更好的助记符。

于 2013-07-11T12:30:57.850 回答