リモートディレクトリ内の最後に変更されたファイルをSCPで取得する

リモートディレクトリ内の最後に変更されたファイルをSCPで取得する

リモート ホストのディレクトリで最近変更されたファイルを SCP する簡単なコマンドはありますか? ローカルからリモートにそれを実行する方法はわかります... たとえば、 scp ``ls -Art | tail -n 1\`` usr@remote:/var/log/yeet 同じことをリモートからローカルに実行するにはどうすればよいですか。 (つまり、yeet から最近変更されたファイルを取得し、それをローカル ホストにコピーします)

答え1

ここでは問題はほとんどありません。

解析ls

まず第一にあなたは解析すべきではないls。 にはls -Art | tail -n 1欠陥があり、確実に修正することはできません。 標準的な信頼できる代替手段はfind、および/またはヌル終端行で動作することです。

さらに、現在のディレクトリ(サブディレクトリではない)に直接、最後に変更されたファイル(ディレクトリではない)が必要であると想定します。

POSIX では、入力項目を null で終了するリストの形式で解析する機能は通常は要求されません。ツールに十分なオプションがある場合は、これが の強力な代替手段になりますls -Art | tail -n 1

find . -maxdepth 1 -type f -printf '%T@ %p\0' |
   sort -zrn |
   head -zn 1 |
   cut -z -d " " -f 2-

結果は null で終わるファイル名として返されます。これを使って何かを行うにはxargs -0 …、次のように にパイプする必要があります。

… | xargs -0r cp -t "/target/dir/" --

または( がcpサポートされていない場合-t):

… | xargs -0r -I {} cp -- {} "/target/dir/"

これらのコマンドには、POSIX では必須ではない (したがって移植性がない) ものが多くあります。たとえば、--cpオプションの解析を停止するので、たとえば、ファイルは-Rコピーされる代わりに再帰をトリガーしません。 から始まるファイル名は必ずfind . …で始まるので、を省略しても問題ないことに注意してください。 を扱う際の一般的な良い方法を示すために、これを使用しました。もう 1 つの良い方法は、パスを引用符で囲むことです。リテラルは引用符なしでも機能しますが、この例を特定のターゲット パスに置き換えた後は引用符が必要になる可能性があるため、とにかく引用符を使用しています。.--cp/target/dir/

あなたの場合(ローカルからリモートへ)では、scpの代わりにを使用しますcp。引数を解析する際に、独自の癖がある可能性があります。

POSIX 準拠だがパフォーマンスが低いコマンドとしては、次のようなものがあります。

find . ! -name . -prune -type f -exec sh -c '
   [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; |
   wc -c)" -eq 0 ]' sh {} \; -print

それは、これは別の答えです代替(非POSIX)-maxdepth 1sh確実に生成されますfind … -exec …2つの節をネストする外側の はfindすべてのファイルを調べ、それらを 1 つずつ内側の に渡しますfind。内側の は、指定されたファイルよりも新しいすべてのファイルを検索し、新しいファイルごとにfind1 つの文字 ( ) を出力します。これらの文字をカウントします。何もない場合は、指定されたファイルが最も最近変更されたことを意味します。その場合にのみ、外側の はそれを出力します。awc -cfind

アウターがfind複数のファイルを印刷するシナリオはいくつかあります。

  • 同じ「最新」の mtime を持つファイルが 2 つ以上あります。
  • コマンドの実行中にディレクトリ内のファイルが変更されます。
  • 内部ではfind一部のファイルを stat できません。

このため-quit、外側の最終アクションとしてfind役立つでしょう (内側でも役立つでしょうfindが、理由は異なります)。残念ながら、-quitPOSIX ではありません。

私は-print(-print0は POSIX ではありません) を使用しましたが、出力を別のコマンドにパイプする必要がないため、非標準のファイル名でも問題はありません。-execすべての可能なファイル名を適切に処理する を使用してください。たとえば、 の代わりに以下を使用-printします。

-exec yet_another_command {} \;

これで、解析せずにローカル ディレクトリ内の最後に変更されたファイルを見つける方法がわかりましたls

リモートシステム上のファイルの検索

どのようなアプローチを選択した場合でも (欠陥のある を含むls … | tail …)、リモート ディレクトリで目的のファイルを見つけるには、リモート システムでコマンドを実行する必要があります (または実行しない必要があります。この例外については後で説明します)。

最も明白なアプローチは、sshリモート システムに入ることです。新しいコンテキストでは、リモート システムはローカルで、ローカル コンピュータはリモートです。(ここでは、上記の POSIX 準拠のコマンドを例として使用していますが、find … | sort -z … | head -z … | cut -z … | xargs -0 …リモート システムが必要なオプションをすべてサポートしている場合は、最初のコマンドを使用できます)。

ssh usr@remote
# now on the remote system
cd "/source/dir/" &&
find . ! -name . -prune -type f -exec sh -c '
   [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; |
   wc -c)" -eq 0 ]' sh {} \; -exec scp {} usr@local:"/target/dir/" \;

cdを避けてを使用する場合は、 で置き換える必要がある -s がfind /source/dir …さらにあることに注意してください。 を使用すると、はるかに簡単になります。./source/dircd

ローカル システムをリモート システムから SSH 経由で利用できるようにする必要があります。途中に NAT がある場合は、次のようにリモート ポート転送を使用して NAT をバイパスできます。

ssh -R 12322:127.0.0.1:22 usr@remote
# now on the remote system the same cd + find as above
# only the scp part is different 
… -exec scp -P 12322 {} [email protected]:"/target/dir/" \;

これによってリモート ポートがローカル ポート12322につながるsshdため、リモート システムのユーザーがそれを悪用しようとする可能性があることに注意してください。別の問題として、リモート システムがポート転送を許可しないように設定されている場合があります。

ローカル システムで単一のコマンドを呼び出したい場合があります。この場合、適切な引用符が必要になり、さらに面倒になります。

ssh usr@remote '
   cd "/source/dir/" &&
   find . ! -name . -prune -type f -exec sh -c '"'"'
      [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; |
      wc -c)" -eq 0 ]'"'"' sh {} \; -exec scp {} usr@local:"/target/dir/" \;
   '

ただし、リモートでscpパスワードを要求する必要がある場合は、問題が発生する可能性があります。このため、またはローカルを実行/アクセスできない場合はsshd、さらに別の方法が必要になる場合があります。

このローカル コマンドは、リモート システムから取得した目的のファイル名を出力します。

ssh usr@remote '
   cd "/source/dir/" &&
   find . ! -name . -prune -type f -exec sh -c '"'"'
      [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; |
      wc -c)" -eq 0 ]'"'"' sh {} \; -print
   '

一緒に使えます地元ツールはxargsや のように動作しますscp。 を解析すると が-print返される方が を解析するよりもわずかに優れていることに注意してくださいls。の代わりに-print0(非 POSIX なので使用できない可能性があります) または-exec printf "%s\0" {} \;(動作するはずです)-printを使用して、目的のファイル名をヌル終端文字列として取得します。これで、ローカル側でどうするかはあなた次第です。これは、POSIX 準拠のリモート コマンドが必要で、ローカル システムのツールにオプションが豊富な場合に便利です。

注目すべき例外:sshfs

sshfsusr@remote:"/source/dir/"としてマウントできます/local/path/私のこの答えすべての詳細を網羅するために繰り返しはしません。あなたの場合、豊富な(POSIXに限定されない)ツールを使用した(ローカル)手順は次のようになります。

sshfs usr@remote:"/source/dir/" "/local/path/"
find "/local/path/" -maxdepth 1 -type f -printf '%T@ %p\0' |
   sort -zrn |
   head -zn 1 |
   cut -z -d " " -f 2- |
   xargs -0r cp -t "/target/dir/" --
fusermount -u "/local/path/"

これは素晴らしい。あなたがすることはすべて地元ツール。リモート側でツールを使用できればsshfs、利用可能なオプションは問題になりません。外部からローカル システムにアクセスできるかどうかも問題ではありません。方法は同じです。リモートからローカルへ、またはローカルからリモートへ、どちらでも問題ありません。

関連情報