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 通常不需要以空終止列表的形式解析輸入項的能力。如果您的工具選項足夠豐富,那麼這是您的強大替代品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 . …以開頭開頭,因此可以安全地省略。我用它只是為了指出處理.另一個好的做法是引用路徑:文字可以在沒有引號的情況下工作,但是在用特定的目標路徑替換此範例後,您可能需要引號,所以無論如何我都會使用它們。.--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 1。它sh可靠地生成嵌套兩個find … -exec …子句。外部find探測所有文件並將它們一一提供給內部find。內部尋找比給定文件更新的所有文件,為每個較新的文件find列印一個字元 ( )。計算這些字元。如果沒有,則表示給定的文件是最近修改的;然後才在外面列印它。awc -cfind

在極少數情況下,外部find可能會列印多個文件:

  • 有兩個或多個檔案具有相同的“最新”mtime;
  • 命令運行時目錄中的檔案被修改;
  • 內部find無法統計某些文件。

因此-quit,外部的最終動作find將是有用的(請注意,它對於內部也很有用find,但出於不同的原因)。不幸的-quit是不是 POSIX。

我使用-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並使用find /source/dir …,那麼還有更多.-s 需要替換為/source/dir。使用 就容易多了cd

您需要可以透過 SSH 從遠端系統存取本機系統。如果途中有 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。請注意,解析-printyield 的結果僅比解析 稍好ls。使用-print0(非 POSIX,可能無法使用) 或-exec printf "%s\0" {} \;(應該可以) 來-print取得所需的檔案名稱作為空終止字串。現在由您決定在本地如何處理它。如果您需要具有 POSIX 相容的遠端命令,但本機系統中的工具有豐富的選項,這非常有用。

值得注意的例外:sshfs

sshfs允許您安裝usr@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遠端的工具,那麼它們的可用選項就不再重要了。如果您可以從外部存取本機系統也沒關係。而且方法都是一樣的:遠端到本地或本地到遠端,都沒有關係。

相關內容