我希望以下命令的结果重定向到远程服务器上的文件中,因为我只需要authorized_keys
文件中的唯一行:
ssh [email protected] awk '!seen[$0]++' /root/.ssh/authorized_keys
我尝试了以下方法但没有成功:
ssh [email protected] "awk '!seen[$0]++' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp"
ssh [email protected] "awk \'!seen[$0]++\' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp"
ssh [email protected] '(awk \'!seen[$0]++\' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp)'
我的谷歌让我失望了......关于我做错了什么有什么想法吗?
答案1
试试这个(我在我的机器上测试过,它似乎有效——好吧,我没有任何重复项要删除,但是......):
ssh [email protected] "awk '!seen[\$0]++' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp"
您的"
-quote 尝试不起作用,因为您没有反斜杠 in $
,$0
导致它扩展为类似bash
.
您的单引号尝试不起作用,因为'foo\'some thing\'foo'
它并不意味着您认为它的作用。\
-转义符不会在单引号字符串内解释。所以这实际上是不平衡的引号和两个参数;第一个是foo\some
,第二个是thing'foo
,但它不完整,因为最后一个'
开始一个新的单引号字符串。
外壳转义可能会很痛苦,尤其是双重转义。首先发送 shell 脚本(使用sftp
、scp
等)然后运行通常会更容易。
答案2
我将接受德罗伯特的回答,因为它为我提供了我需要的信息。我仍然遇到问题,我认为这可能是我的awk
命令,我用另一种方法替换了它。以下是我为确保远程服务器上的authorized_keys 文件仅包含唯一条目所做的操作:
ssh [email protected] "sort /root/.ssh/authorized_keys | uniq > /root/.ssh/temp"
ssh [email protected] "mv -f /root/.ssh/temp /root/.ssh/authorized_keys"
答案3
以下是您需要了解的有关引用某些内容以保护其免受 shell 扩展和分词影响的所有信息:
- 将撇号以外的所有内容括在撇号之间。例如,
do not
变为'do not'
. - 使用反斜杠来转义撇号,即
'
变成\'
。 - 将字符串分解为撇号和其他字符,应用前面的 2 个规则,然后连接结果。例如,
don't
变为'don'\''t'
.
有了这些一般规则,您问题中的命令就可以正确引用,如下所示:
ssh [email protected] 'awk '\''!seen[$0]++'\'' /root/.ssh/authorized_keys > /root/.ssh/authorized_keystemp'
有更易读的方法来引用同一字符串,但这种方法普遍适用并且易于直观地验证是否正确。
我发现自己经常这样做,以至于我编写了一个 shell 函数/脚本来为我做这件事,并且我一直在使用它。这里是:
#!/bin/sh -
# shellquote foo => foo
# shellquote foo&bar => 'foo&bar'
# shellquote foo'bar => 'foo'\''bar'
shellquote() {
local input="$1"
local output=""
local backslash='\'
local apostrophe="'"
if [ -z "${input}" ]; then
# Empty string => pair of apostrophes
output="''"
fi
while [ -n "${input}" ]; do
case "${input}" in
"'"*)
# Escape the apostrophe.
output="${output}${backslash}${apostrophe}"
input="${input#${apostrophe}}"
;;
*"'"*)
# Quote everything before the first apostrophe, and then escape
# the apostrophe.
output="${output}${apostrophe}${input%%${apostrophe}*}${apostrophe}${backslash}${apostrophe}"
input="${input#*${apostrophe}}"
;;
*[!+,./0-9:=@A-Z_a-z-]*)
# There are no apostrophes, but at least one character needs quoting.
# So quote the entire word.
output="${output}${apostrophe}${input}${apostrophe}"
input=""
;;
*)
# Nothing needs quoting. Output everything literally.
output="${output}${input}"
input=""
esac
done
printf '%s' "${output}"
}
main() {
local sep=''
for arg; do
printf '%s' "${sep}"
shellquote "${arg}"
sep=' '
done
}
main "$@"