我有这个代码:
#!/bin/sh
echo "--------- Backup config files -------------"
find /vmfs/volumes/datastore1/ \
-type f \
-regex '.*\.\(vmx\|nvram\|vmsd\|vmdk\)$' \
! -name '*-flat.vmdk' \
-exec sh -c "$(cat << 'EOF'
addrs=XX.XX.XX.XX
for pth; do
drctry="$(printf '%s' "${pth%/*}" | sed "s/'/'\"'\"'/g")"
ssh "root@$addrs" "mkdir -p '$drctry'" && scp -pr "$pth" "$addrs:'$drctry/'"
done
EOF
)" sh {} +
echo "----------- Backup VM disks ---------------"
vim-cmd vmsvc/getallvms | sed -e '1d' -e 's/ \[.*$//' | while read -r line; do
addrs=XX.XX.XX.XX
vmid="$(printf '%s' "${line%}" | awk '{print $1;}'| sed "s/'/'\"'\"'/g")"
vmname="$(printf '%s' "${line%}" | awk '{for (i=2; i<=NF; i++) print $i}'| sed "s/'/'\"'\"'/g")"
vim-cmd vmsvc/snapshot.create $vmid backup 'Snapshot created by Backup Script' 0 0
scp -pr "/vmfs/volumes/datastore1/$vmname/$vmname-flat.vmdk" $addrs:"/vmfs/volumes/datastore1/$vmname"
vim-cmd vmsvc/snapshot.removeall $vmid
done
第一部分(备份配置文件)运行正常。第二部分(备份 VM 磁盘)则不行。vim-cmd 行(启动循环)的输出为:
1 VM_1
10 VM_2 with_space
11 VM_3 with space
12 VM_4 with_space
14 VM_5
我需要:
- 将第一个单词(数字 id)分配给 var $vmid
- 将其余单词分配给 var $vmname,这样我就可以连接 scp 命令的路径并成功运行它,而不会遇到目录名和文件名中的空格产生的问题
这次,我成功获取了所有名称中没有空间的虚拟机。对于其他虚拟机,我获取了:
Create Snapshot:
/vmfs/volumes/datastore1/VM_name
with_space/VM_name
with_space.vmdk: No such file or directory
sh: with_space: not found
Remove All Snapshots:
更新 1:对于那些需要知道“vim-cmd vmsvc/getallvm s”输出的人来说,它在那里:
Vmid Name File Guest OS Version Annotation
1 VM_1 [datastore1] VM_1/VM_1.vmx winXPProGuest vmx-13
10 VM_2 with_space [datastore1] VM_2 with_space/VM_2 with_space.vmx opensuse64Guest vmx-13
11 VM_3 with space [datastore1] VM_3 with space/VM_3 with space.vmx opensuse64Guest vmx-13
12 VM_4 with_space [datastore1] VM_4 with_space/VM_4 with_space.vmx opensuse64Guest vmx-13
14 VM_5 [datastore1] VM_5/VM_5.vmx winXPProGuest vmx-13
更新2:
感谢@Sorin,我将这个庞然大物减少了 60% 以上,只剩下 8 行。所以,这是穷人将免费 ESXi 6.5 主机克隆到另一台主机的解决方案:
#!/bin/sh
addrs=XXX.XXX.XXX.XXX
IFS="\t" vim-cmd vmsvc/getallvms | sed -e '1d' -e 's/ \[.*//g' -e "s/\s\+/\t/" | while read id name; do
echo "----- Backup "$name" --------"
scp -pr "/vmfs/volumes/datastore1/$name/$name.vmx" "/vmfs/volumes/datastore1/$name/$name.nvram" "/vmfs/volumes/datastore1/$name/$name.vmsd" "/vmfs/volumes/datastore1/$name/$name.vmdk" $addrs:"'/vmfs/volumes/datastore1/$name'"
vim-cmd vmsvc/snapshot.create $id backup 'Snapshot created by Backup Script' 0 0
scp -pr "/vmfs/volumes/datastore1/$name/$name-flat.vmdk" $addrs:"'/vmfs/volumes/datastore1/$name'"
vim-cmd vmsvc/snapshot.removeall $id
done
谢谢 !!
答案1
您可以用制表符替换第一组空格,并将 IFS 设置为“\t”,然后使用 read 读取 id 和 name,如下所示:
IFS="\t" cat test | sed -e '1d' -e 's/ \[.*//g' -e "s/\s\+/\t/" |\
while read id name; do echo -e "id:$id\nname:$name";done
id:1
name:VM_1
id:10
name:VM_2 with_space
id:11
name:VM_3 with space
id:12
name:VM_4 with_space
id:14
name:VM_5
但是,从 getallvms 的输出来看,我认为这并不安全。输出似乎是固定宽度的,但不清楚该宽度是否相同,或者取决于 VM 名称的长度,或者名称是否因太长而被截断。我搜索了 vim-cmd 的手册,看看您是否可以控制格式,但网上没有涵盖命令行选项的文档。检查 vim-cmd 是否有任何用于控制输出的选项(也许您可以为列设置显式分隔符,或者它可以导出 xml/json...)
如果不可能的话,我的建议是仅从 getallvms 获取 vmid 并使用它vim-cmd vmsvc/get.summary $vmid
来获取名称。
我需要一个 get.summary 输出的样本来给你提供准确的代码,但它主要看起来像这样:
vim-cmd vmsvc/getallvms | sed -e '1d' | awk "{print $1} | while read id; do
vmname=$(vim-cmd vmsvc/get.summary $vmid | \
sed '/ guest =/,/}/d' | egrep name | sed 's/.*= //;s/,//;s/ *$//')
...
done
这没有经过测试。大部分内容来自:http://unixetc.co.uk/2015/03/28/list-virtual-machines-on-esxi/
啊,现在我明白你的问题实际上是 SCP 命令,远程 scp 地址需要双重转义。尝试:
scp -pr "/vmfs/volumes/datastore1/$vmname/$vmname-flat.vmdk" $addrs:"'/vmfs/volumes/datastore1/$vmname'"
或者如果你有 bash,你可以使用 printf 来引用文件名:
SRC=$(printf /vmfs/volumes/datastore1/%q/%q-flat.vmdk "$VMNAME" "$VMNAME")
DEST=$(printf %s:'/vmfs/volumes/datastore1/%q' $addr "$VMNAME")
并在你的 scp 命令中使用它
答案2
也许这并不相关,但使用 perl,这很容易实现。
给定测试.out:
Vmid Name File Guest OS Version Annotation
1 VM_1 [datastore1] VM_1/VM_1.vmx winXPProGuest vmx-13
10 VM_2 with_space [datastore1] VM_2 with_space/VM_2 with_space.vmx opensuse64Guest vmx-13
11 VM_3 with space [datastore1] VM_3 with space/VM_3 with space.vmx opensuse64Guest vmx-13
12 VM_4 with_space [datastore1] VM_4 with_space/VM_4 with_space.vmx opensuse64Guest vmx-13
14 VM_5 [datastore1] VM_5/VM_5.vmx winXPProGuest vmx-13
该作业是使用以下命令行完成的:
$ cat test.out |perl -ne 'if (/(\d+)[ \t]*([^[]+)/) { print "id is $1, name is $2\n"; }'
id is 1, name is VM_1
id is 10, name is VM_2 with_space
id is 11, name is VM_3 with space
id is 12, name is VM_4 with_space
id is 14, name is VM_5
您可以轻松调整 perl 的输出以使其更适合 shell......
笔记:此一行程序记住名称直到遇到字符串“[datastore]”...如果文件列与“[datastore]”不同,则脚本被破坏...