我能夠透過 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
.
透過標準輸入傳遞遠端腳本很少(如果有的話!)是必要的。