带文件重定向的 SSH 命令不起作用

带文件重定向的 SSH 命令不起作用

我希望以下命令的结果重定向到远程服务器上的文件中,因为我只需要authorized_keys文件中的唯一行:

ssh [email protected] awk '!seen[$0]++' /root/.ssh/authorized_keys

我尝试了以下方法但没有成功:

ssh [email protected] "awk '!seen[$0]++' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp"

ssh [email protected] "awk \'!seen[$0]++\' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp"

ssh [email protected] '(awk \'!seen[$0]++\' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp)'

我的谷歌让我失望了......关于我做错了什么有什么想法吗?

答案1

试试这个(我在我的机器上测试过,它似乎有效——好吧,我没有任何重复项要删除,但是......):

ssh [email protected] "awk '!seen[\$0]++' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp"

您的"-quote 尝试不起作用,因为您没有反斜杠 in $$0导致它扩展为类似bash.

您的单引号尝试不起作用,因为'foo\'some thing\'foo'它并不意味着您认为它的作用。\-转义符不会在单引号字符串内解释。所以这实际上是不平衡的引号和两个参数;第一个是foo\some,第二个是thing'foo,但它不完整,因为最后一个'开始一个新的单引号字符串。

外壳转义可能会很痛苦,尤其是双重转义。首先发送 shell 脚本(使用sftpscp等)然后运行通常会更容易。

答案2

我将接受德罗伯特的回答,因为它为我提供了我需要的信息。我仍然遇到问题,我认为这可能是我的awk命令,我用另一种方法替换了它。以下是我为确保远程服务器上的authorized_keys 文件仅包含唯一条目所做的操作:

ssh [email protected] "sort /root/.ssh/authorized_keys | uniq > /root/.ssh/temp"
ssh [email protected] "mv -f /root/.ssh/temp /root/.ssh/authorized_keys"

答案3

以下是您需要了解的有关引用某些内容以保护其免受 shell 扩展和分词影响的所有信息:

  1. 将撇号以外的所有内容括在撇号之间。例如,do not变为'do not'.
  2. 使用反斜杠来转义撇号,即'变成\'
  3. 将字符串分解为撇号和其他字符,应用前面的 2 个规则,然后连接结果。例如,don't变为'don'\''t'.

有了这些一般规则,您问题中的命令就可以正确引用,如下所示:

ssh [email protected] 'awk '\''!seen[$0]++'\'' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp'

有更易读的方法来引用同一字符串,但这种方法普遍适用并且易于直观地验证是否正确。

我发现自己经常这样做,以至于我编写了一个 shell 函数/脚本来为我做这件事,并且我一直在使用它。这里是:

#!/bin/sh -

# shellquote foo => foo
# shellquote foo&bar => 'foo&bar'
# shellquote foo'bar => 'foo'\''bar'
shellquote() {
  local input="$1"
  local output=""
  local backslash='\'
  local apostrophe="'"
  if [ -z "${input}" ]; then
    # Empty string => pair of apostrophes
    output="''"
  fi
  while [ -n "${input}" ]; do
    case "${input}" in
    "'"*)
      # Escape the apostrophe.
      output="${output}${backslash}${apostrophe}"
      input="${input#${apostrophe}}"
      ;;
    *"'"*)
      # Quote everything before the first apostrophe, and then escape
      # the apostrophe.
      output="${output}${apostrophe}${input%%${apostrophe}*}${apostrophe}${backslash}${apostrophe}"
      input="${input#*${apostrophe}}"
      ;;
    *[!+,./0-9:=@A-Z_a-z-]*)
      # There are no apostrophes, but at least one character needs quoting.
      # So quote the entire word.
      output="${output}${apostrophe}${input}${apostrophe}"
      input=""
      ;;
    *)
      # Nothing needs quoting. Output everything literally.
      output="${output}${input}"
      input=""
    esac
  done
  printf '%s' "${output}"
}

main() {
  local sep=''
  for arg; do
    printf '%s' "${sep}"
    shellquote "${arg}"
    sep=' '
  done
}

main "$@"

相关内容