我在perl中编写了这段代码:
shift( @interfaces = qx'ifconfig -s' );
得到了这个错误:
Type of arg 1 to shift must be array (not list assignment)
当我这样写的时候:
@interfaces = qx'ifconfig -s';
shift @interfaces;
它做了我想要的,即将ifconfig命令的输出作为一个行数组并删除数组中的第一个元素(这是一个标题,而不是一个实际的接口)。
我个人的偏好是把它写成一个班轮。在我看来,第一个例子中的括号应该使得赋值被完全解析,因此允许转换将@interfaces视为一个数组,但显然perl认为它是一个“列表赋值”。
对于perl大师来说,这肯定是一个简单的问题,但我用Google搜索并用Google搜索并没有找到启示。
如果有人愿意提供具体的语义来完成我想要的一行,我将不胜感激。如果你也请花时间解释为什么我的第一个版本不起作用我会永远感激(教一个人钓鱼等等)。
提前感谢您的帮助。
答案 0 :(得分:13)
如您所见,shift
需要一个文字数组,而不是作业的结果。这是因为当perl解析shift @interfaces
时,它实际上将其重写为类似&CORE::shift(\@interfaces)
的内容,并且您无法获取赋值的引用并获得数组引用。
你可以将它分成两行,如你所发现的那样,你可以隐藏在 mob 显示的括号内取消引用中的作业,或者你可以简单地扔掉第一个值:
(undef, @interfaces) = qx'ifconfig -s';
左值位置的 undef
是占位符,表示您不需要的值。
(shift
的解析在perl 5.14+中有所改变,但上面的论点仍然存在)
您可能不应该使用的其他几种方法,仅通过增加长度来命令:)
my @interfaces = sub {shift; @_}->(qx'ifconfig -s');
my @interfaces = sub {@_[1..$#_]}->(qx'ifconfig -s');
my @interfaces = map {@$_[1..$#$_]} [qx'ifconfig -s'];
my @interfaces = map {shift @$_; @$_} [qx'ifconfig -s'];
our @interfaces; shift @{*interfaces = [qx'ifconfig -s']};
my @interfaces = sub {*_ = [qx'ifconfig -s']; shift; @_}->();
答案 1 :(得分:7)
shift @{EXPR}
是有效的语法,所以
shift @{$interfaces = [qx'ifconfig -s']}
将为您提供一个删除了第一个元素的数组引用。
我在diagnostics
输出中发现了这个问题,即从列表分配中调用shift
:
$ perl -Mdiagnostics -e 'print shift(@a = (2,3,4))'
Type of arg 1 to shift must be array (not list assignment) at -e line 1, at end of line Execution of -e aborted due to compilation errors (#1) (F) This function requires the argument in that position to be of a certain type. Arrays must be @NAME or @{EXPR}. Hashes must be %NAME or %{EXPR}. No implicit dereferencing is allowed--use the {EXPR} forms as an explicit dereference. See perlref.
Perl对任何用户定义的子例程或使用\@
或\%
字符原型化的内置函数强制执行此行为。原型是解释器的线索,Perl应该将数组或散列函数参数视为数组或散列类型,而不是尝试将列表展开为多个参数。
考虑它的一种方法(虽然我不确定这对于内置函数是否准确)是Perl会从函数调用的参数列表中读取数组或哈希变量,但实际上是通过了将该变量引用到prototyped函数。因此解释器需要在参数列表中标识数组或散列,并且它需要能够获得对该数组或散列的引用。 Perl没有或者不能(在这里挥手)用列表赋值表达式做到这一点 - 请注意
的结果\(@a = (1,2,3))
是对标量的3个引用的列表,而不是对包含3个标量的列表的引用。
您可以使用prototype
函数查看大多数Perl内置函数的原型(如果有):
$ perl -e 'print prototype("CORE::shift")' ===> \@
$ perl -e 'print prototype("CORE::each")' ===> \%
$ perl -e 'print prototype("CORE::push")' ===> \@@
答案 2 :(得分:3)
最接近我可以将其变成一个班轮:
perl -e '@interfaces = (qx|ifconfig -s|)[1 .. 1000]; print @interfaces'
这是从索引1到索引1000的切片,并假设您的ifconfig输出不超过1000行。 显然这是一种可怕的编程习惯,但它确实适用于大多数情况,并做了问题。
答案 3 :(得分:3)
Perl可能不是最简单的方法(或者大多数可读方式,我应该说),如果你想限制自己只使用一行。这是一个修复shell命令的解决方案:
@interfaces = qx(ifconfig -s | tail -n +2);
答案 4 :(得分:2)
从Perl版本5.10开始,您可以使用state
变量声明来管理变量的持久性,而无需在循环外预先声明my
。
use 5.10.0;
my @interfaces = grep {state $i++} qx'ifconfig -s';
是的,你可以在没有状态的情况下做到这一点,但它是一个完美的用例。
这是类似的代码,没有state
和相同的词汇w.r.t $i
。
my @interfaces;
{
my $i;
@interfaces = grep {$i++} qx'ifconfig -s';
}
或
my @interfaces = do { my $i; grep {$i++} qx'ifconfig -s' };
当然你不关心$i
的词汇,只是做
my $i;
my @interfaces = grep {$i++} qx'ifconfig -s';
或者你可以作弊
my @interfaces = grep {$|++} qx'ifconfig -s'
但如果您依赖其他地方的$|
,那就会中断。不要紧,只需使用state
。
答案 5 :(得分:1)
试试这个:
shift qw(this that the other);
您将收到相同的错误消息。 shift
命令 必须 采用列表变量而不是列表本身。毕竟,shift
命令有两个主要影响。
shift
它就没有任何意义。在您的示例中,(@interfaces = qx 'ifconfig -s')
正在设置@interfaces
,并且正在将列表@interfaces
的值而不是变量本身返回到shift
命令。
Mob's回答对你有所帮助。你会得到一个列表引用,但是,你要么必须继续取消引用它,要么设置一个实际的列表变量:
shift @{$interfaces = [qx'ifconfig -s']}
foreach my $entry (@{$interfaces}) { #Have to dereference
say "Interface: $entry";
}
@interfaces = @{$interfaces}; #This will also work.
如果整个目的是保存一些输入,则从dereferece引用变量设置实际列表变量不会保存任何内容。并且,在程序的其余部分中使用引用而不是实际列表只会增加一层复杂性和虚化。
如果你真的只是设置@interfaces
不包含返回列表的第一个元素,你可以这样做:
(my $garbage, @interfaces) = qw(ifconfig -s);
列表的第一个值将返回$garbage
,这是一个扔掉的变量。列表的其余部分将由@interfaces
填满。这很干净,而且很容易理解发生了什么。
Eric Strom我现在看到已经做得更好了:
(undef, @interfaces) = qw(ifconfig -s);
你甚至没有要扔掉的变量。
现在我要熬夜担心Perl 5.14在解析shift
命令时所做的更改。