Eu tenho este código:
#!/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
a primeira parte (arquivos de configuração de backup) funciona perfeitamente. a segunda parte (discos VM de backup) não. A saída da linha vim-cmd (que inicia o loop) é:
1 VM_1
10 VM_2 with_space
11 VM_3 with space
12 VM_4 with_space
14 VM_5
Preciso:
- atribua a primeira palavra (o id numérico) a var $vmid
- atribua o resto das palavras a var $vmname para que eu possa concatenar o caminho para o comando scp e executá-lo com sucesso sem encontrar problemas gerados por espaços no nome do diretório e no nome do arquivo
Neste momento obtenho sucesso para todas as VMs que não possuem espaço no nome. Para os outros eu recebo:
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:
ATUALIZAÇÃO 1: Para quem precisa saber a saída de "vim-cmd vmsvc/getallvm s", aí está:
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
ATUALIZAÇÃO 2:
Obrigado a @Sorin, consegui reduzir esse monstro em mais de 60% para 8 linhas. Então, esta é a solução de clonagem do pobre homem de um host ESXi 6.5 gratuito para outro host:
#!/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
Obrigado !!
Responder1
Você pode substituir o primeiro conjunto de espaços por uma tabulação e definir o IFS como "\t" e ler o id e o nome com read, algo assim:
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
No entanto, pela saída que você obteve do getallvms, não acho que seja seguro. A saída parece ter largura fixa, mas não está claro se essa largura é a mesma ou depende do comprimento do nome da VM, ou se o nome está truncado se for muito longo. Procurei um manual do vim-cmd para ver se você pode controlar o formato, mas não há documentação on-line que cubra as opções de linha de comando. Verifique se o vim-cmd tem alguma opção para controlar a saída (talvez você possa definir um separador explícito para as colunas ou exportar xml/json ...)
Se isso não for possível, minha sugestão é pegar apenas o vmid do getallvms e usar vim-cmd vmsvc/get.summary $vmid
para pegar o nome.
Eu precisaria de uma amostra da saída get.summary para fornecer o código exato, mas principalmente seria assim:
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
Isso não foi testado. A maior parte eu peguei de:http://unixetc.co.uk/2015/03/28/list-virtual-machines-on-esxi/
Ah, agora vejo que seu problema é na verdade o comando SCP, os endereços scp remotos precisam ter escape duplo. Tentar:
scp -pr "/vmfs/volumes/datastore1/$vmname/$vmname-flat.vmdk" $addrs:"'/vmfs/volumes/datastore1/$vmname'"
Ou se você tiver o bash, poderá usar printf para citar o nome do arquivo:
SRC=$(printf /vmfs/volumes/datastore1/%q/%q-flat.vmdk "$VMNAME" "$VMNAME")
DEST=$(printf %s:'/vmfs/volumes/datastore1/%q' $addr "$VMNAME")
e use isso em seus comandos scp
Responder2
Talvez não seja relevante, mas usando perl é muito fácil de conseguir.
Dado test.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
O trabalho é feito usando a seguinte linha de comando:
$ 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
Você pode adaptar facilmente a saída do Perl para torná-la mais utilizável com o shell...
OBSERVAÇÃO: Este oneliner memoriza o nome até que a string "[datastore]" seja atendida... se a coluna do arquivo for diferente de "[datastore]", o script está quebrado...