SSH find 指令無法辨識儲存在本機變數中的目錄

SSH find 指令無法辨識儲存在本機變數中的目錄

我能夠透過 SSH 存取儲存在本地變數中的目錄$out_dir。但是,我無法弄清楚為什麼該find | head命令不起作用。在嘗試#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 中得到「沒有這樣的檔案或目錄」的原因是,heredoc (the <<EOF) 的所有變數和命令插值都是在本地完成的,然後才發送到遠端主機。

正如您正確地註意到的那樣,$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} 的 home 相關的所有內容 - 在指定 ${out_dir} 時,您似乎想要使用 home 以外的目錄!再次,讓我們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幫您刪除。

如果您需要 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
'

筆記:預期的遠端命令可以變得更簡單、更安全,但對此吹毛求疵只會混淆要點。

基本上,您應該通過以下部分A)應該擴展至當地的機器和那些b)應該僅有的擴展至偏僻的機器作為分離ssh 的參數,前者用雙引號,後者用單引號。

ssh 會將所有「命令」參數與空格連接起來,然後將它們作為單一參數傳遞給-c遠端電腦上使用者登入 shell 的選項。

如果遠端命令本身應包含單引號,則可以使用命令替換+此處文件組合。另外,如果「local」變數的值可以包含空格和其他特殊字符,則應該對它們進行轉義:

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.

透過標準輸入傳遞遠端腳本很少(如果有的話!)是必要的。

相關內容