문자열을 둘로 나눈 다음 각 세그먼트를 변수에 할당하는 방법

문자열을 둘로 나눈 다음 각 세그먼트를 변수에 할당하는 방법

나는 이 코드를 가지고 있습니다 :

#!/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]"와 다르면 스크립트가 손상됩니다...

관련 정보