如何在断开foreach循环后重新启动“do-while”循环?
我的配置脚本中有一小段代码,想法是配置被加载,然后检查每个键是否已输入主机名。但是,如果发现某个配置包含相同的主机名,则它将被拒绝并显示一条警告消息,指出该主机名的配置已存在。如何在断开foreach循环后重新启动“do-while”循环?
问题是我需要foreach循环检查散列键的存在以重新启动do-while循环,以便可以尝试另一个主机名,或者用户可以将^C
排除在脚本之外。
这是摘录;
my $host;
do {
print "Enter the hostname or IP of the ESXi server: ";
chomp($host = <STDIN>);
if ($host eq '') {
print "You must enter a hostname or IP address!\n";
} elsif ($host ne '') {
# We need to catch duplicate configurations for we don't do the same work twice
foreach (keys %config) {
if ($config{$_}{host} ne $host) {
last;
} elsif ($config{$_}{host} eq $host) {
warn "Configuration for $host already exists!\n";
}
}
if ($ping_obj->ping($host)) {
$config{$config_tag}{host} = $host;
} elsif (! $ping_obj->ping($host)) {
print RED . "Ping test for \'$host\' failed" . RESET . "\n";
}
$ping_obj->close();
}
} while ($config{$config_tag}{host} eq 'undef');
这就是模板哈希的样子。
my %template = (
host => 'undef',
port => 'undef',
login => {
user => 'undef',
password => 'undef',
},
options => {
snapshots => "0",
compress => "0",
# This is expressed as an array
exclude => 'undef',
},
);
如果在Perl中有一个goto LABEL
语句的用法,就是这样。
do {
START: # could also go right before the "do"
...
if (...) {
warn "Configuration exists. Start over.\n";
goto START;
}
} while (...);
如果将标签放在循环中,循环结束时标签将被销毁,对吗? – ianc1215 2011-12-22 01:19:37
@Solignis - 不对。标签的范围规则有点复杂 - 请参阅perldoc中的详细信息 - 但可以在此“do-while”循环之前或之后执行“goto START”。 (这不是一个好地方,使用'goto') – mob 2011-12-22 03:25:49
'perl -e'do {START:} while(0); goto START'' on 5.14打印出使用goto跳转到构造中在-e行1.已弃用(多次)。我完全认为它在5.18出现时将不再工作。事实上,在5.16出来后,我想我可能会把它放在[p5p]上(http://lists.perl.org/list/perl5-porters.html)。 – 2011-12-22 05:08:37
我不知道你为什么要使用do ... while
,当while
看起来更自然。
一些注意事项:
- 你并不需要仔细检查您的if语句。如果例如
$host eq ''
为真,那么$host ne ''
必须为假。每个定义。 - 如果你不打算在循环外部使用
$host
,我假设你不是 ,因为你将它存储在散列中,所以你应该在循环内部放置my $host
来限制范围。
一些提示:
- 您可以使用
redo
重新启动循环。 - 您可以使用smart matching来取消for循环。
while ($config{$config_tag}{host} eq 'undef') {
print "Enter the hostname or IP of the ESXi server: ";
chomp(my $host = <STDIN>);
if ($host eq '') {
print "You must enter a hostname or IP address!\n";
redo;
} else {
# We need to catch duplicate configurations
my @host_list = map { $_->{host} } values %config
if ($host ~~ @host_list) {
warn "Configuration for $host already exists!\n";
redo;
}
}
if ($ping_obj->ping($host)) {
$config{$config_tag}{host} = $host;
} else {
print RED . "Ping test for \'$host\' failed" . RESET . "\n";
}
$ping_obj->close();
}
'my @host_list = map {$ _-> {host}} values%config' – 2011-12-22 01:23:08
@BradGilbert谢谢。当我重写代码时,我想我对删除按钮有点太急切了。 – TLP 2011-12-22 01:29:23
为什么你有3个elsif
S其中一个简单的else
会做什么?
我的意思是,他们只测试相关的if
测试的完全相反。
if ($host eq '') {
...
} elsif ($host ne '') {
...
}
if ($config{$_}{host} ne $host) {
...
} elsif ($config{$_}{host} eq $host) {
...
}
if ($ping_obj->ping($host)) {
...
} elsif (! $ping_obj->ping($host)) {
...
}
我会用一个正常的while
循环,而不是do{...}while(...)
循环。
do{
RESTART:
if(...){
goto RESTART;
}
}while(...);
VS
while(...){
if(...){
redo;
}
}
在这个循环中,您只使用%config
的钥匙,找到关联的值,那么你为什么不使用values %config
代替。
foreach (keys %config) {
if ($config{$_}{host} ne $host) {
last;
} elsif ($config{$_}{host} eq $host) {
warn "Configuration for $host already exists!\n";
}
}
VS
for(values %config){
if($_->{host} ne $host){
...
} else {
...
}
}
如果您使用5.10.0或更高版本,你可以使用一个smart match (~~
)相反,这将使你测试更清楚什么。
my @hosts = map{ $_->{host} } values %config;
if($host ~~ @hosts){
...
}
%配置指向键不值。 '%config => server ## => host => $ address'服务器##是我需要使用循环提取的键。所以除非我错误地使用'values%config'对我来说什么都不会做。 – ianc1215 2011-12-22 04:13:03
@Solignis在您的问题中包含的代码中,除了获取与其关联的值之外,您从未使用过任何键。如果你有,我会发布其他东西。 'map {$ _-> {host}} values%config'与map {$ config {$ _} {host}}键%config'相同,这实际上就是你在做的。 – 2011-12-22 04:25:56
哦,我明白了,所以'%config'的值是'server ##'?对? – ianc1215 2011-12-22 04:35:31
这是应该做的:'eq'undef''?我希望你没有试图去检查这个值是否是未定义的,因为那不是那么做的。 – TLP 2011-12-22 00:41:30
我正在检查它是否是'undef',但是它检查的散列是用值undef'硬编码的。 – ianc1215 2011-12-22 01:18:36
不,不,不是,你要么检查它是否是'undef'(字符串),要么检查'undef'中的'未定义'。你的措辞是不明确的。 – TLP 2011-12-22 01:26:54