как разделить строку на две части, а затем присвоить каждый сегмент переменной

как разделить строку на две части, а затем присвоить каждый сегмент переменной

У меня есть такой код:

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

Мне нужно:

  • присвоить первое слово (числовой идентификатор) переменной $vmid
  • присвойте оставшиеся слова переменной $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» и прочитать как 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, я не думаю, что это безопасно. Выход, похоже, имеет фиксированную ширину, но не ясно, является ли эта ширина одинаковой или зависит от длины имени виртуальной машины, или имя усекается, если оно слишком длинное. Я искал руководство для vim-cmd, чтобы узнать, можно ли управлять форматом, но в сети нет документации, охватывающей параметры командной строки. Проверьте, есть ли у vim-cmd какие-либо параметры для управления выводом (возможно, можно задать явный разделитель для столбцов или экспортировать xml/json ...)

Если это невозможно, я предлагаю получить только vmid из getallvms и использовать его 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, чтобы сделать его более удобным для использования в оболочке...

ПРИМЕЧАНИЕ: Этот однострочник запоминает имя до тех пор, пока не встретится строка «[datastore]»... если столбец файла отличается от «[datastore]», скрипт не работает.

Связанный контент