对于具有自定义端口的服务器循环(for i in "user1@server1 -p 12345" "user2@server2 -p 54321" ...; do)

对于具有自定义端口的服务器循环(for i in "user1@server1 -p 12345" "user2@server2 -p 54321" ...; do)

我正在尝试通过 ssh 在一系列服务器上运行命令。我最近更改了 ssh 端口以避免互联网扫描仪,但这破坏了我的脚本。有谁知道在 for 循环中为每个 user@server 条目指定不同端口的“简单方法”?

for i in '[email protected] -p 12345' '[email protected] -p 54321'
 do printf "\e[%sm%s\e[00m\n" 32 $i
 ssh $i crontab -l
done

第二行只是在输出之前打印服务器名称(为了可读性)。

它给我的不是幸福,而是:ssh: Could not resolve hostname server1.domain1.com -p 12345: Name or service not known

以前,当所有端口都是默认的 22 并且不需要指定时,它就可以工作。

答案1

从7.7版本开始、OpenSSH ssh(以及它的scpsftp)将接受 URI 形式的目的地。如果您愿意,可以将端口号指定为 URI 的一部分,例如“ssh://someuser@somehost:42”连接到端口 42。就 shell 而言,URI 的优点是它是单个字符串被关注到。所以你可以这样做:

for i in 'ssh://[email protected]:12345' 'ssh://[email protected]:54321'
 do printf ...
 ssh "$i" ...
done

SSH URI 的一般形式是“ssh://user@host:port”。 “用户”和“端口”部分都是可选的,如果您不需要为其指定值,则可以省略它们(以及“@”或“:”标点符号)。

答案2

正确的方法是将其存储在配置中,而不是在每个连接上指定所有内容。

~/.ssh/config你可以有:

Host server1.domain1.com  # When connecting to this
User user1  # Use this user
Port 12345  # with this port

Host serv2  # We can also use a different, internal, name
Hostname server1.domain1.com  # The actual hostname or IP it will use to connect
User user2
Port 54321

然后ssh server1.domain1.com就会ssh serv2做正确的事™ 无需记住每台机器使用的用户名或端口。对脚本和人类都有好处。

如果您稍后更改端口,则可以在一个位置进行修改。

还可以使用通配符,因此如果所有计算机domain1.com的 sshd 都在端口 1234 上侦听,您可以将它们全部与 匹配Host *.domain1.com,而不必单独列出它们。

答案3

对于for可以循环多个变量的循环,请切换到zsh.您已经zsh通过不引用变量来使用语法:

# zsh
for colour host port (
  green [email protected] 12345
  blue  [email protected] 54321
) {
  print -rP "%F{$colour}%B$host:$port%b%f"
  ssh -p $port $host 'crontab -l'
}

要循环参数列表,您可以使用具有多维数组的 shell,例如ksh93

# ksh93
ssh_args=(
  (-p 12345 [email protected])
  (-p 54321 [email protected])
)
for i in "${!ssh_args[@]}"}; do
  printf '\e[1;32m%s\e[m\n' "${ssh_args[i][2]}"
  ssh "${ssh_args[i][@]}" 'crontab -l'
done

或者让包含字符串的数组元素与参数使用您知道字符串中不会出现的字符连接起来,然后将它们分开。zsh确实具有${(s[x])var}分裂特征。

ksh 和 bash 也可以,但只能通过笨重的 split+glob 来实现,当您忘记在那里引用扩展时,会隐式调用它。您可以将其用作:

# ksh93 / bash / yash / mksh / zsh --emulate ksh
ssh_args=(
  -p:12345:[email protected]
  -p:54321:[email protected]
)
IFS=:; set -o noglob # tune your split+glob operator by specifying
                     # the separator and disabling globbing
for joined_args in "${ssh_args[@]}"; do
  args=( $joined_args ) # invoke split+glob
  printf '\e[1;32m%s\e[m\n' "${args[2]}"
  ssh "${args[@]}" 'crontab -l'
done

这基本上就是您在方法中所做的,只是您使用空格而不是:分隔元素,但没有设置$IFS也没有禁用全局。

事实上,你$i没有被分割可以通过$IFS设置为空字符串来解释,或者代码由 zsh 而不是 bash 运行,其中分割或通配符都不是在参数扩展时隐式完成的。

在 zsh 中,如果您想要 IFS 分割,请使用,$=var而不是$var,如果您想要通配:$~var。 bash 的$var类似于 zsh 的$=~var。所以在 zsh 中,你需要:

# zsh
ssh_args=(
  -p:12345:[email protected]
  -p:54321:[email protected]
)
IFS=:
for joined_args in $ssh_args; do
  args=( $=joined_args ) # split on $IFS or better:
  args=( ${(s[:])joined_args} ) # split explicitly on : without
                               # having to touch $IFS
  print -rP "%F{green}%B$host[-1]%b%f"
  ssh $args 'crontab -l'
done

一种可行的可移植方法sh是编写它:

# sh / bash / dash / ksh / yash / zsh...
while IFS=' ' read<&3 host port; do
  {
    printf '\33[1;32m%s\e[m\n' "$host:$port"
    ssh -p "$port" "$host" 'crontab -l'
  } 3<&-
done 3<< 'EOF'
[email protected] 12345
[email protected] 54321
EOF

省略-r选项以read允许值包含分隔符(此处为空格),方法是在它们前面加上反斜杠,如下所示:

john\ doe@server1 1234

但与其他方法相反,它不允许这些值包含换行符(对于用户、主机、服务名称或端口号来说可能不是问题)。

另一种可移植的循环列表,所有列表都具有相同$n数量的元素,并且没有该限制,即将所有元素存储在位置参数中,并在循环中循环它们while [ "$#" -ge "$n" ]; do something with "$1" "$2"...; shift "$n"; done。所以在这里:

# sh / bash / dash / ksh / yash / zsh...
eval "$(
  printf "fg_%s='\33[3%sm' " \
    black 0 red     1 green 2 yellow 3 \
    blue  4 magenta 5 cyan  6 white  7
  printf "bg_%s='\33[4%sm' " \
    black 0 red     1 green 2 yellow 3 \
    blue  4 magenta 5 cyan  6 white  7
  printf "attr_%s='\33[%sm' " \
    reset '' bold     1 dim     2 italics 3 underline 4 \
    blink 5  standout 7 reverse 7 secure  8
)"
    
set -- \
  "$fg_green$attr_bold" [email protected] 12345 \
  "$bg_blue$fg_white"   [email protected] 54321

while [ "$#" -ge 3 ]; do
  colour=$1 host=$2 port=$3
  printf '%s\n' "$colour$host:$port$attr_reset"
  ssh -p "$port" "$host" 'crontab -l'
  shift 3
done

答案4

多主机使用pdsh。广泛应用于拥有大量集群主机的数据中心。

从 pdsh 文档 ..

在主机 h0、h1、h2 上使用用户“foo”运行,在主机 h3、h5 上使用用户“bar”运行:

pdsh -w foo@h[0-2],bar@h[3,5]

https://code.google.com/archive/p/pdsh/wikis/UsingPDSH.wiki

正如 Ángel 提到的,该工具与 ssh 配置文件一起使用。

下面的完整示例..

〜/ .ssh /配置

Host server1
Hostname server1
User user1  
Port 12345  

Host server2
Hostname server2
User user2
Port 54321

现在简单运行

pdsh -w server1,server2 crontab -l 

相关内容