나는 이 코드를 가지고 있습니다 :
#!/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
나는 다음을 수행해야 합니다:
- var $vmid에 첫 번째 단어(숫자 ID)를 할당합니다.
- scp 명령의 경로를 연결하고 dir 이름과 파일 이름의 공백으로 인해 발생하는 문제에 부딪치지 않고 성공적으로 실행할 수 있도록 나머지 단어를 var $vmname에 할당합니다.
현재 이름에 공백이 없는 모든 VM에 대해 성공합니다. 다른 사람들에게는 다음을 얻습니다.
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"로 설정하고 다음과 같이 id와 name을 모두 read로 읽을 수 있습니다.
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을 사용하면 달성하기가 매우 쉽습니다.
주어진 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
작업은 다음 명령줄을 사용하여 수행됩니다.
$ 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의 출력을 쉽게 조정하여 쉘에서 더 유용하게 만들 수 있습니다...
메모: 이 oneliner는 "[datastore]" 문자열이 충족될 때까지 이름을 기억합니다... 파일 열이 "[datastore]"와 다르면 스크립트가 손상됩니다...