cómo dividir una cadena en dos y luego asignar cada segmento a una variable

cómo dividir una cadena en dos y luego asignar cada segmento a una variable

Tengo 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

la primera parte (archivos de configuración de respaldo) funciona perfectamente. la segunda parte (discos de VM de respaldo) no. La salida de la línea vim-cmd (que inicia el ciclo) es:

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

Necesito:

  • asigne la primera palabra (la identificación numérica) a var $vmid
  • asigne el resto de las palabras a var $vmname para poder concatenar la ruta del comando scp y ejecutarlo correctamente sin encontrar problemas generados por espacios en el nombre del directorio y el nombre del archivo.

En este momento obtengo éxito para todas las máquinas virtuales que no tienen espacio en el nombre. Para los demás obtengo:

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:

ACTUALIZACIÓN 1: Para aquellos que necesitan conocer el resultado de "vim-cmd vmsvc/getallvm s", ahí 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

ACTUALIZACIÓN 2:

Gracias a @Sorin, logré reducir este monstruo en más de un 60% a 8 líneas. Entonces, esta es la solución de clonación del pobre de un host ESXi 6.5 gratuito a otro 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

Gracias !!

Respuesta1

Puedes reemplazar el primer conjunto de espacios con una pestaña y configurar el IFS en "\t" y leer tanto el id como el nombre con read, algo como esto:

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

Sin embargo, según el resultado que tiene de getallvms, no creo que sea seguro. La salida parece tener un ancho fijo, pero no está claro si ese ancho es el mismo o depende de la longitud del nombre de la VM, o si el nombre se trunca si es demasiado largo. Busqué un manual para vim-cmd para ver si puedes controlar el formato, pero no hay documentación en línea que cubra las opciones de la línea de comandos. Compruebe si vim-cmd tiene alguna opción para controlar la salida (tal vez pueda establecer un separador explícito para las columnas, o puede exportar xml/json...)

Si eso no es posible, mi sugerencia es obtener solo el vmid de getallvms y usarlo vim-cmd vmsvc/get.summary $vmidpara obtener el nombre.

Necesitaría una muestra del resultado de get.summary para darle el código exacto, pero principalmente se vería así:

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

Esto no está probado. La mayor parte lo obtuve de:http://unixetc.co.uk/2015/03/28/list-virtual-machines-on-esxi/

Ah, ahora veo que su problema es en realidad el comando SCP, las direcciones scp remotas deben tener doble escape. Intentar:

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

O si tiene bash, puede usar printf para citar el nombre del archivo:

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

y úsalo en tus comandos scp

Respuesta2

Tal vez no sea relevante, pero usando Perl es bastante fácil de lograr.

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

El trabajo se realiza utilizando la siguiente línea 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

Puede adaptar fácilmente la salida de Perl para que sea más utilizable con Shell...

NOTA: Este oneliner memoriza el nombre hasta que se cumple la cadena "[almacén de datos]"... si la columna del archivo difiere de "[almacén de datos]", el script no funciona...

información relacionada