So teilen Sie eine Zeichenfolge in zwei Hälften und weisen dann jedem Segment eine Variable zu

So teilen Sie eine Zeichenfolge in zwei Hälften und weisen dann jedem Segment eine Variable zu

Ich habe diesen Code:

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

Der erste Teil (Sicherungskonfigurationsdateien) funktioniert einwandfrei. Der zweite Teil (Sicherungs-VM-Festplatten) nicht. Die Ausgabe der vim-cmd-Zeile (die die Schleife startet) lautet:

1      VM_1           
10     VM_2 with_space 
11     VM_3 with space 
12     VM_4 with_space 
14     VM_5 

Ich muss:

  • Weisen Sie der Variable $vmid das erste Wort (die numerische ID) zu.
  • Weisen Sie den Rest der Wörter der Variable $vmname zu, damit ich den Pfad für den SCP-Befehl verketten und ihn erfolgreich ausführen kann, ohne auf Probleme zu stoßen, die durch Leerzeichen im Verzeichnisnamen und im Dateinamen verursacht werden

Derzeit erhalte ich Erfolg für alle VMs, deren Name kein Leerzeichen enthält. Für die anderen erhalte ich:

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:

UPDATE 1: Für diejenigen, die die Ausgabe von „vim-cmd vmsvc/getallvm s“ wissen müssen, hier ist sie:

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

UPDATE 2:

Dank @Sorin konnte ich dieses Monster um mehr als 60 % auf 8 Zeilen reduzieren. Dies ist also die Klonlösung für Arme, die einen kostenlosen ESXi 6.5-Host auf einen anderen Host klonen:

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

Danke !!

Antwort1

Sie können die erste Gruppe von Leerzeichen durch einen Tabulator ersetzen, das IFS auf „\t“ setzen und sowohl die ID als auch den Namen mit „read“ auslesen, etwa so:

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

Aufgrund der Ausgabe, die Sie von getallvms erhalten, halte ich das jedoch nicht für sicher. Die Ausgabe scheint eine feste Breite zu haben, aber es ist nicht klar, ob diese Breite gleich ist oder von der Länge des VM-Namens abhängt oder ob der Name abgeschnitten wird, wenn er zu lang ist. Ich habe nach einem Handbuch für vim-cmd gesucht, um zu sehen, ob Sie das Format steuern können, aber es gibt online keine Dokumentation, die Befehlszeilenoptionen abdeckt. Überprüfen Sie, ob vim-cmd Optionen zur Steuerung der Ausgabe hat (vielleicht können Sie ein explizites Trennzeichen für die Spalten festlegen oder es kann XML/JSON exportieren ...)

Wenn dies nicht möglich ist, schlage ich vor, nur die virtuelle Mid von getallvms abzurufen und zu verwenden, vim-cmd vmsvc/get.summary $vmidum den Namen abzurufen.

Um Ihnen den genauen Code zu geben, bräuchte ich ein Beispiel der get.summary-Ausgabe, aber im Großen und Ganzen würde er so aussehen:

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

Dies wurde nicht getestet. Das meiste davon habe ich von:http://unixetc.co.uk/2015/03/28/list-virtual-machines-on-esxi/

Ah, jetzt sehe ich, dass Ihr Problem eigentlich der SCP-Befehl ist. Remote-SCP-Adressen müssen doppelt maskiert werden. Versuchen Sie:

scp -pr "/vmfs/volumes/datastore1/$vmname/$vmname-flat.vmdk" $addrs:"'/vmfs/volumes/datastore1/$vmname'"

Oder wenn Sie Bash haben, können Sie printf verwenden, um den Dateinamen in Anführungszeichen zu setzen:

SRC=$(printf /vmfs/volumes/datastore1/%q/%q-flat.vmdk "$VMNAME" "$VMNAME")
DEST=$(printf %s:'/vmfs/volumes/datastore1/%q' $addr "$VMNAME")

und verwenden Sie das in Ihren SCP-Befehlen

Antwort2

Vielleicht ist es nicht relevant, aber mit Perl ist es recht einfach zu erreichen.

Gegeben: 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

Die Aufgabe wird mit der folgenden Befehlszeile erledigt:

$ 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

Sie können die Ausgabe von Perl problemlos anpassen, um sie mit der Shell besser nutzbar zu machen ...

NOTIZ: Dieser Einzeiler merkt sich den Namen, bis die Zeichenfolge „[datastore]“ erreicht wird. Wenn die Dateispalte von „[datastore]“ abweicht, wird das Skript beschädigt …

verwandte Informationen