ファイルリダイレクトが機能しない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)'

私のGoogleは失敗しています。何が間違っているのかわかりますか?

答え1

これを試してみてください(私のコンピュータでテストしましたが、うまく動作しているようです。削除する重複項目はありませんが...)。

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

"にバックスラッシュがないため、-quote試行が機能しないため、$拡張$0されますbash

一重引用符を使用した試みは、'foo\'some thing\'foo'あなたが思うことを意味しないので動作しません。\- エスケープ文字は、一重引用符で囲まれた文字列内では解釈されません。したがって、これは実際には不均衡引用符と2つの引数です。最初のものは、foo\some2番目のものですが、最後の引数は単一引用符で囲まれた新しい文字列を開始するthing'fooので不完全です。'

シェル脱出は痛みを伴う可能性があり、特に二重脱出はさらにそうです。シェルスクリプトを最初に送信してから(などを使用して)実行する方がsftp簡単scpです。

答え2

必要な情報を提供するDeRobertの回答を受け入れます。それでも問題があり、それが私の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

シェルの拡張とトークン化から保護するために何かを引用することについて知っておくべきことはすべて次のとおりです。

  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'

同じ文字列を参照するより読みやすい方法がありますが、この方法は一般的に適用可能であり、正しいことを視覚的に簡単に確認できます。

私はこれを頻繁に行うので、これを行うためのシェル関数/スクリプトを作成し、常にそれを使用しました。ここにいる:

#!/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 "$@"

関連情報