如何將字串分成兩部分,然後將每個段分配給一個變數

如何將字串分成兩部分,然後將每個段分配給一個變數

我有這個代碼:

#!/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

第一部分(備份設定檔)工作完美。第二部分(備份虛擬機器磁碟)則不然。 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 的輸出來看,我認為這不安全。輸出似乎是固定寬度,但不清楚該寬度是否相同,或取決於虛擬機器名稱的長度,或者如果名稱太長,名稱是否會被截斷。我搜尋了 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,它很容易實現。

給出測試:

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...

筆記:這個 oneliner 會記住名稱,直到遇到字串「[datastore]」...如果檔案列與「[datastore]」不同,腳本就會損壞...

相關內容