I wrote this code in perl:
shift( @interfaces = qx'ifconfig -s' );
And got this error:
Type of arg 1 to shift must be array (not list assignment)
When I write it this way:
@interfaces = qx'ifconfig -s';
shift @interfaces;
It does what I want, which is to get the output of the ifconfig command as an array of lines and remove the first element in the array (which is a header, not an actual interface).
My personal preference is to write this as a one liner. It seems to me the parentheses in the first example should cause the assignment to be resolved fully, therefore allowing shift to see @interfaces as an array, but clearly perl thinks it's a "list assignment."
This is surely an easy question for the perl gurus but I've googled and googled and haven't found enlightenment.
If someone would please provide the specific semantics to accomplish what I want in one line I would appreciate it. If you would also please take the time to explain why my first version isn't working I would be eternally grateful (teach a man to fish and all that).
Thank you in advance for your help.
As you saw, shift
requires a literal array, not the result of an assignment. This is because when perl parses shift @interfaces
it is actually rewriting it into something like &CORE::shift(\@interfaces)
and you can not take a reference of an assignment and get an array ref.
You could break it into two lines as you have found, you could hide the assignment inside a bracketed dereference as mob shows, or you could simply throw away the first value:
(undef, @interfaces) = qx'ifconfig -s';
undef
in an lvalue position is a placeholder for values that you don't need.
(parsing of shift
has changed a bit in perl 5.14+, but the argument above still holds)
a few more ways that you probably shouldn't use, ordered only by increasing length :)
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; @_}->();
shift @{EXPR}
is valid syntax, so
shift @{$interfaces = [qx'ifconfig -s']}
will give you an array reference that has the first element removed.
I discovered this from the diagnostics
output about calling shift
from list assignment:
$ 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 enforces this behavior on any user-defined subroutine or builtin that is prototyped with \@
or \%
characters. The prototype is a clue for the interpreter that Perl should treat an array or hash function argument as an array or hash type, and not try to unroll the list into several arguments.
One way to think about it (though I'm not sure if this is accurate for the builtins) is that Perl will read the array or hash variable from your list of arguments to a function call, but actually pass a reference to that variable to the prototyped function. So the interpreter needs to identify an array or hash in your argument list, and it needs to be able to get a reference to that array or hash. Perl doesn't or can't (hands waving here) do that with a list assignment expression -- note that the result of
\(@a = (1,2,3))
is a list of 3 references to scalars, not a reference to a list with 3 scalars.
You can see the prototypes (if any) for most of the Perl builtins with the prototype
function:
$ perl -e 'print prototype("CORE::shift")' ===> \@
$ perl -e 'print prototype("CORE::each")' ===> \%
$ perl -e 'print prototype("CORE::push")' ===> \@@
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With