Как отфильтровать определенную строку из выходного файла, связанного со скалярной переменной? - PullRequest
2 голосов
/ 04 января 2011

Я пытаюсь использовать регулярные выражения, чтобы отфильтровать все, кроме очень специфической строки текста из файла VMware VMX, который я запускаю в цикле foreach, потому что для каждой виртуальной машины есть несколько файлов.Каждый раз, когда цикл запускается, он связывает вывод Net :: OpenSSH , который запускает cat с файлом, находящимся на сервере VM, с скалярной переменной.

Я не уверен, что этона самом деле имеет какой-то смысл.

В любом случае проблема, с которой я сталкиваюсь, заключается в том, что когда скрипт запускается, он не соответствует никому в моем выражении регулярного выражения, а просто отображает все приведенные в архив файлы VMX один за другим.Я не могу понять, что мне не хватает.

Вот пример кода, над которым я работаю.

sub get_virtual_machines {
my $esx_host = config_file()->{ESX}{host};
my $ssh_port = config_file()->{ESX}{port};
my $esx_user = config_file()->{ESX}{user};
my $esx_password = config_file()->{ESX}{password};
my %options = (
    port => $ssh_port,
    user => $esx_user, 
    password => $esx_password
);
my $ssh1 = Net::OpenSSH->new($esx_host, %options);
print color 'blue';
print "Collecting virtual machine data for $esx_host\n";
my @virtual_machines = $ssh1->capture('vim-cmd vmsvc/getallvms');
shift @virtual_machines;
print color 'reset';
# Filter data from ESX\ESXi output
my %virtual_machines = ();

foreach my $vm (@virtual_machines) {

    # Replace "[" with "/"

    $vm =~ s/\[/\//;

    # Replace "]" with "/"

    $vm =~ s/\]/\//;

    # Match ID, NAME and VMX location
    $vm =~  m/^(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\D+)(\D)(\d)(\d)/x;
    # Build hash table of discovered virtual machines
    $virtual_machines{"$2"}{"ID"} = "$1";
    $virtual_machines{"$2"}{"VMX"} = "/vmfs/volumes$3$4";
    $virtual_machines{"$2"}{"Version"} = "$9";
}
undef @virtual_machines;
foreach my $vm (keys %virtual_machines) {
$vm = $ssh1->capture("cat $virtual_machines{$vm}{VMX}");
$vm =~ m/^(\bguestOSAltName\b)/x;
print "$1\n";
}
#print Dumper (\%virtual_machines);

}

Часть, о которой идет речь,после строки "undef @virtual_machines".Строка 38 в образце. Моя первая цель - сопоставить строку со словом «guestOSAltName». Думаю, что когда я выполню эту часть, я снова буду в пути, просто столкнусь с дорожным блоком.

Вотобразец файла VMX, чтобы посмотреть тоже.

.encoding = "UTF-8"
config.version = "8"
virtualHW.version = "7"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
vmci0.present = "TRUE"
nvram = "NS02.nvram"
deploymentPlatform = "windows"
virtualHW.productCompatibility = "hosted"
unity.customColor = "|23C0C0C0"
tools.upgrade.policy = "useGlobal"
powerType.powerOff = "default"
powerType.powerOn = "default"
powerType.suspend = "default"
powerType.reset = "default"

displayName = "NS02"
extendedConfigFile = "NS02.vmxf"

scsi0.present = "TRUE"
scsi0.sharedBus = "none"
scsi0.virtualDev = "lsilogic"
memsize = "512"
scsi0:0.present = "TRUE"
scsi0:0.fileName = "NS02.vmdk"
scsi0:0.deviceType = "scsi-hardDisk"
ide1:0.present = "TRUE"
ide1:0.clientDevice = "FALSE"
ide1:0.deviceType = "cdrom-image"
ide1:0.startConnected = "FALSE"
ethernet0.present = "TRUE"
ethernet0.virtualDev = "e1000"
ethernet0.networkName = "solignis.local"
ethernet0.addressType = "generated"
chipset.onlineStandby = "FALSE"
guestOSAltName = "Ubuntu Linux (64-bit)"
guestOS = "ubuntu-64"
uuid.location = "56 4d ab a6 1e 7b c5 43-02 45 7c 24 1f fc 28 d9"
uuid.bios = "56 4d ab a6 1e 7b c5 43-02 45 7c 24 1f fc 28 d9"
vc.uuid = "52 50 c1 4b be 91 07 d5-22 0e 86 ee db 88 6d 8a"
snapshot.action = "keep"
sched.cpu.min = "0"
sched.cpu.units = "mhz"
sched.cpu.shares = "normal"
sched.mem.minsize = "0"
sched.mem.shares = "normal"

sched.scsi0:0.shares = "normal"
bios.forceSetupOnce = "FALSE"
floppy0.present = "FALSE"

ethernet0.generatedAddress = "00:0c:29:fc:28:d9"
tools.syncTime = "FALSE"
cleanShutdown = "FALSE"
replay.supported = "FALSE"
sched.swap.derivedName = "/vmfs/volumes/4cbcad5b-b51efa39-c3d8-001517585013/NS02/NS02-510988a0.vswp"
scsi0:0.redo = ""
vmotion.checkpointFBSize = "4194304"
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"
scsi0.pciSlotNumber = "16"
ethernet0.pciSlotNumber = "32"
vmci0.pciSlotNumber = "33"
ethernet0.generatedAddressOffset = "0"
vmci0.id = "536619225"
hostCPUID.0 = "0000000a756e65476c65746e49656e69"
hostCPUID.1 = "000006fb000408000000e3bdbfebfbff"
hostCPUID.80000001 = "00000000000000000000000120100800"
guestCPUID.0 = "0000000a756e65476c65746e49656e69"
guestCPUID.1 = "000006fb00010800800022010febfbff"
guestCPUID.80000001 = "00000000000000000000000120100800"
userCPUID.0 = "0000000a756e65476c65746e49656e69"
userCPUID.1 = "000006fb000408000000e3bdbfebfbff"
userCPUID.80000001 = "00000000000000000000000120100800"
evcCompatibilityMode = "FALSE"
ide1:0.fileName = "/usr/lib/vmware/isoimages/linux.iso"

Ответы [ 3 ]

5 голосов
/ 04 января 2011

Трудно сказать с информацией, которую вы дали, но я думаю, что проблема в том, что регулярное выражение

$vm =~ m/^(\bguestOSAltName\b)/x;

не соответствует файлу, который вы дали, потому что утверждение ^соответствует началу строка , а не началу строка .Поскольку регулярное выражение не совпадает, $1 сохраняет свое старое значение ранее в программе, которая выводится на печать.В целях безопасности вы должны проверить регулярное выражение, которое фактически соответствует, перед использованием захватов:

if ($vm =~ m/^(\bguestOSAltName\b)/x) {
    print "$1\n";
}
else {
    carp "Couldn't find guestOSAltName!";
}

Или захватить захваты, поместив совпадение в контекст списка:

# $result gets $1 if the match succeeds, undef if it fails.
my ($result) = $vm =~ m/^(\bguestOSAltName\b)/x

Чтобы сопоставить ^для начала, вам нужен модификатор /m, который меняет ^$, чтобы соответствовать строке вместо строчной:

if ($vm =~ m/^(\bguestOSAltName\b)/xm) { ... }

Вот почему Дамиан Конвей в Perl Best Practices рекомендует вам всегда используйте /m - потому что тогда ^$ всегда делают то, что вы интуитивно думаете, что они должны делать.[На самом деле он рекомендует всегда использовать /xms.Вы проходите треть пути :)]


PS: С этого момента все является общей критикой рецензирования кода, не связанной напрямую с вопросом.Я надеюсь, что это полезно, но не стесняйтесь игнорировать это.

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

$vm =~ s/\[/\//;

часто лучше переписать вquotish context:

$vm =~ s'['/';

Кроме того, это регулярное выражение довольно трудно читать:

$vm =~  m/^(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\D+)(\D)(\d)(\d)/x;

Вы используете тег /x, почему бы не воспользоваться им?

$vm =~  m/^(\d+) \s+ # $1: number of some sort
           (\S+) \s+ # $2: identifier we're interested in
           (\S+) \s+ # $3: VMX filename part a
           (\S+) \s+ # $4: VMX filename part b
           (\S+) \s+ # $5: another identifier
           (\D+)(\D) # $6, $7: at least two nondigits
           (\d)   # $8: digit
           (\d)   # $9: version digit
           /x;

Я бы также рассмотрел использование именованных захватов:

$vm =~  m/^(?:      \d+) \s+ # number of some sort
           (?<ID>   \S+) \s+ # $+{ID}: identifier we're interested in
           (?<VMXa> \S+) \s+ # $+{VMXa}: VMX filename part a
           (?<VMXb> \S+) \s+ # $+{VMXb}: VMX filename part b
           (?:      \S+) \s+
           (?:\D+)(?:\D)     # at least two nondigits
           (?:\d)            # one digit
           (?<VERSION> \d)   # $+{VERSION}: version digit
           /x;

Теперь вместо загадочных ссылок на $2 и $9 впоследствии у вас есть четкие, очевидные, самодокументированные ссылкидо $+{ID} и $+{VERSION}.Я превратил остальные группы в группы без захвата (?:regex), но если я захочу захватить одну из них позже, я могу превратить ее в именованный захват без изменения индексов всех других захватов, в отличие от позиционныхcapturing.

Именованные захваты также реже страдают от проблемы со старым значением, упомянутой выше, когда неудачный захват оставляет все переменные $1 в их старом состоянии.

1 голос
/ 04 января 2011

Если я угадаю, что вы хотите, это, вероятно, что-то вроде

if( $vm =~ /^guestOSAltName = (.+)\n/ )
{
  print "$1\n";
}
0 голосов
/ 04 января 2011

Как сказал @canavanin, проблема в том, что у вас есть многострочный текст, поэтому вам нужно использовать m//m, чтобы ^ и $ означали начало и конец строки (вместо начала и конца строки ). Также лучше (безопаснее) захватить совпадение с переменной (в perl> 5.10 также вы назвали захваты как указано @Potter). Наконец, m // x очень полезен, но только если вы пишете свое регулярное выражение в несколько строк, чтобы разрешить комментарии и забыть о пробелах, но в одной строке бесполезно и подвержено ошибкам, потому что люди забывают о явном написании пробелов \s или \s+ и поместите реальные (но избежавшие x) пробелы.

Также, как вы сказали, что хотите напечатать строку, а не только 'guestOSAltName', вам нужно захватить до конца строки: m/(^guestOSAltName .+$)/m (если вы добавляете однострочный режим к многострочному //ms тогда вам нужно будет сделать .+ не жадным .+?, чтобы $ соответствовал концу строки, прежде чем он будет использован жадным .+ в однострочном режиме)

[not working code]
$vm =~ m/^(\bguestOSAltName\b)/x;
print "$1\n";

[working code]
# make list context with parentheses
(my $guest_os_alias_line) = $vm=~m/^  # start of line (using /m)
                                   (   #start capturing
                                     guestOSAltName
                                     \b  # just in case guestOSAltName is a substring in an unwanted line
                                     .+  # everything else in the line (not matching \n because no /s)
                                   )  # end capturing
                                   $  # end of line (because /m)
                                 /xm; # multiline mode      
print "$guest_os_alias_line\n";

Если у вас более одной из таких строк, вы хотели бы иметь режим множественного соответствия / g и захватить его в массиве:

(my @guest_os_alias_lines) = $vm=~m/^  # start of line (using /m)
                                   (   #start capturing
                                     guestOSAltName
                                     \b  # just in case guestOSAltName is a substring in an unwanted line
                                     .+  # everything else in the line (not matching \n because no /s)
                                   )  # end capturing
                                   $  # end of line (because /m)
                                 /xmg; # multiline mode (m) and multi-matching(g)      
print "@guest_os_alias_line\n"; # not needed `join ("\n",@guest_os_alias_line)` because the lines contain the `\n` already
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...