SSH find コマンドがローカル変数に格納されているディレクトリを認識しません。

SSH find コマンドがローカル変数に格納されているディレクトリを認識しません。

SSHを介してローカル変数に格納されているディレクトリにアクセスできます$out_dir。しかし、なぜこのfind | headコマンドが機能しないのかわかりません。存在する試み #2、コマンドの最後に2番目のバックスラッシュを追加しましたfind

試行#1エラー: find: `/dir/on/remote/server': No such file or directory

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

試み #2 エラー: syntax error near unexpected token `|'

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \\; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

答え1

試行#1で「ファイルまたはディレクトリがありません」というメッセージが表示されるのは、<<EOFheredoc(the)のすべての変数とコマンド補間がリモートホストに送信される前にローカルで実行されるためです。

正しく気づいたように$out_dir補間が進んでいます。見たいディレクトリが表示されます。これはSSH呼び出しを行う前にローカルコンピュータで発生します。コマンドの置き換え($())もローカルで発生します(ただしリモートで実行しようとしています)。つまり、全体の検索結果は、リモートコンピュータに送信される前にローカルに処理されます。ここに含まれるすべての内容は$()heredocによって処理されます。したがって、${out_dir}ローカルコンピュータには存在しないようです。 「該当するファイルやディレクトリはありません。」

これをよりよく理解したい場合は、この例を単純化してみましょう。この試み:

$ ssh foo@localhost <<EOF
  echo "using account: $(whoami)"
EOF                            
Pseudo-terminal will not be allocated because stdin is not a terminal.
<truncated>
using account: vagrant

私のローカルアカウントは「vagrant」なので、明らかに$()の内容はローカルで実行されます。正常に機能した場合、アカウントは「foo」になりますssh foo@localhost

";"代わりに「\」をエスケープしたため、試行#2が中断されました。 Findを-exec終了する必要がありますが、今ではありません。代わりに、コマンドは突然、bashコマンドを終了する吊り下げ";"で終了します。 '|'は一部の入力が渡されると予想していますが、何も指定されていません。効果的に以下を生成します。

$ | cat
-bash: syntax error near unexpected token `|'

それでは、考えられる解決策は何ですか?

まあ、find削除することができます:

ssh $user@$ip <<EOF
  find $out_dir -delete
EOF

シンプルでシンプルです。特定のファイルを選択して他のファイルを除外することを心配している場合find

find現在のバージョンでは、いくつかの極端なケースを処理しようとしているので、"修正"提案を提供します。

  • デフォルト名の使用
  • 何かの目的を指します。head -n -1

しかし、私はこれがあなたが期待するように働くとは思わない。

まず、呼び出しはbasenameほとんどのパスを削除し、sshコマンドでディレクトリを変更するために何もしません。 SSH実行は、$ {user}の家に関連するすべてを削除しようとします。 ${out_dir}を指定するときに家以外のディレクトリを使用したいようです!今回もfind削除作業を行ってみましょう。

第二に、私は誤解しているかもしれませんが、ターゲットhead -n -1ファイルのリストから$ {out_dir}を削除したことがあるようです。

$ find junk/
junk/
junk/one
junk/two
junk/three

そうですね。を削除したくありませんjunk/

ただし、次のことを試してください。

$ seq 1 4
1
2
3
4
$ seq 1 4 | head -n -1
1
2
3

最初の行は保持されず、最後の行は維持されます。尾を試してみてください:

$ seq 1 4 | tail -n +2
2
3
4

しかし、もう一度申し上げますが、findそれを取り除くのを手伝います。

ファイルの削除のみが必要な場合は確認してください-type f。ただし、高度なフィルタリング用の除外モードと埋め込みモードもあります。

答え2

この試み:

ssh "$user@$ip" "out_dir=$out_dir;" '
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
'

メモ:意図したリモートコマンドをより簡単で安全にすることができますが、これについて少し言うと、ポイントはあいまいになります。

デフォルトでは、次のセクションを通過する必要があります。ㅏ)しなければならない返品次に展開地元の機械とそのもの雨)しなければならないただ次に展開離れて機械炉分離sshパラメータの場合、前者には二重引用符を使用し、後者には一重引用符を使用します。

-csshはすべての「コマンド」引数を空白で連結し、それをリモートシステム上のユーザーログインシェルのオプションに単一の引数として渡します。

リモートコマンド自体に一重引用符を含める必要がある場合は、コマンドの置き換え+ドキュメントここで組み合わせを使用できます。また、「ローカル」変数の値にスペースやその他の特殊文字を含めることができる場合は、エスケープする必要があります。

out_dir='/path/to/ * happiness * '
out_dir=$(printf %s "$out_dir" | sed 's/[^a-zA-Z0-9_/.]/\\&/g')
ssh localhost out_dir="$out_dir;" "$(cat <<'EOT'
  echo '<$out_dir>' = "<$out_dir>"
EOT
)"

<<'EOF';の一重引用符が省略されている場合は、$out_dirここの文書で拡張されます(ローカルコンピュータで)。

bashの最新バージョンは次の形式を持っているので、醜い形式の代わりに${var@Q}簡単に使用できます。"out_dir=${outdir@Q};"echo | sed

標準入力を介してリモートスクリプトを渡すことはほとんど必要ありません。

関連情報