我如何修改这个 bash 脚本以便端口参数是可选的?

我如何修改这个 bash 脚本以便端口参数是可选的?

我经常通过 ssh 连接到远程服务器上运行的 Docker 容器。每次停止或删除容器时,我都需要再次将 ssh 密钥复制到容器上,以便进行连接。为此,我在 Mac 笔记本电脑上放置了这个简单的 bash 脚本,其目的只是将我的 ssh 密钥添加到我想要连接的 Docker 容器中。

#!/bin/bash

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

# Default parameter value:
PORT=...
PUBKEY=...
HOST_IP=...

# show_help function
show_help() {
    echo "Usage: $(basename "$0") [-h] [-p PORT] [-f PUBKEY] [-i HOST_IP]"
    echo
    echo "   -p PORT           add key to container running on port PORT (default: ...)"
    echo "   -f PUBKEY         add key file PUBKEY (default: ...)"
    echo "   -i HOST_IP        connect to host HOST_IP (default: ...)"
    echo
    return
}


while getopts ":h:p:f:i:" option; do
  case "$option" in
    \?)
      echo "invalid argument"
      show_help
      exit 1
      ;;
    h)
      show_help
      exit 0
      ;;
    p)  PORT=$OPTARG
      ;;
    f)  PUBKEY=$OPTARG
      ;;
    i)  HOST_IP=$OPTARG
      ;;
  esac
done

shift $((OPTIND-1))

USER_AT_HOST="root@$HOST_IP"
PUBKEYPATH="$HOME/.ssh/$PUBKEY"
ssh-copy-id -i "$PUBKEYPATH" "$USER_AT_HOST" -p "$PORT"

exit 0

我有两个问题:

  1. (较小)参数具有非直观字母(f对于PUBKEYi对于HOST_IP),因为如果我没记错的话,getopts仅支持单字母参数。有什么方法可以规避此限制吗?如果没有,您能否为参数建议一些更具创意/不言自明的字母?

答案1

通过此实现,您无需预先指定默认值。

的参数ssh-copy-id由数组组成。请注意+=使用附加到数组。

其他说明:

  • OPTIND=1仅当您使用此脚本时才需要source。如果您将其作为脚本运行,则在您到达之前变量将被取消设置getopts

  • 我会删除exit 0脚本底部的内容:让它以 ssh-copy-id退出状态退出。

  • 您的代码忘记了igetopts 的 opt 字符串。请仔细检查一下。

  • ?*是一个与至少 1 个字符匹配的 shell 模式(又名“通配符”)。

  • 摆脱使用全大写变量名的习惯,让它们保留在 shell 中。有一天你会写PATH=something,然后 想知道为什么 你的脚本损坏

  • 设置默认值的另一种方法是使用:命令

    : ${var:="default value"}
    

    但这有点不透明。


#!/bin/bash

# show_help function
show_help() {
    echo "Usage: $(basename "$0") [-h] [-p PORT|-u] [-f PUBKEY] [-i HOST_IP]"
    echo
    echo "   -p PORT           add key to container running on port PORT (default: ...)"
    echo "   -u USER           help string here..."
    echo "   -f PUBKEY         add key file PUBKEY (default: ...)"
    echo "   -i HOST_IP        connect to host HOST_IP (default: ...)"
    echo
    return
}

while getopts ":hi:p:f:u:" option; do
  case "$option" in
    p)  port=$OPTARG ;;
    f)  pubkey=$OPTARG ;;
    i)  host_ip=$OPTARG ;;
    u)  user=$OPTARG ;;
    h)  show_help; exit 0 ;;
    ?)  echo "invalid argument"; show_help; exit 1 ;;
  esac
done

shift $((OPTIND-1))     # not needed if there are no positional parameters

[[ -z $host_ip ]] && host_ip="default value"
[[ -z $pubkey ]] && pubkey="default value"

cmd_args=( -i "$HOME/.ssh/$pubkey" )

case "$port,$user" in
    ,)  # empty port and empty user
        echo "If you don't specify a port, you need to specify a user" >&2
        exit 1
        ;;
    ,?*) # empty port but user is specified
        cmd_args+=( "$user@$host_ip" )
        ;;
    ?*,*) # port has been given. user doesn't matter
        cmd_args+=( -p "$port" "root@$host_ip" )
        ;;
esac

ssh-copy-id "${cmd_args[@]}"

针对您的评论:

  • 只有带参数的选项才使用冒号。为了演示

    有了冒号,

    $ set -- -h
    $ getopts :h: opt
    $ declare -p opt OPTARG OPTIND
    declare -- opt=":"
    declare -- OPTARG="h"
    declare -i OPTIND="2"
    

    作为手册getopts说:

    如果未找到所需参数 [...],则会在名称中放置冒号 ( :),并将 OPTARG 设置为找到的选项字符。

    如果没有冒号,选项解析将按预期进行

    $ unset opt OPTARG OPTIND
    $ set -- -h
    $ getopts :h opt
    $ declare -p opt OPTARG OPTIND
    declare -- opt="h"
    declare -- OPTARG
    declare -- OPTIND="2"
    
  • 关于默认值,进行选项解析第一的。然后,如果变量未通过选项设置,提供默认值。

    [[ -z $host_ip ]] && host_ip="default value"
    

    这意味着:如果的值为host_ip空(-z),则将该值设置为默认值。

相关内容