The Default Variable
Perl uses special variables for several reasons and we have already seen an example of this with the default variable used in some loops when an optional variable is omitted. For convenience, an example that we saw earlier was
say foreach @array;
This outputs the value of each element in turn. It works like this. Consider an alternative version of the foreach loop.
1. foreach my $v ( @array ) { 2. say $v; 3. }
Here, we have declared a variable. The variable takes the value of one element of the array (in order) for each element. That is, it starts with the first element (the element with an index value of 0) and continues to the last element.
In the next line, using the variable $v, we output the value of the variable. As a result, for each iteration, we are outputting the value of each element.
Now, the variable is optional in this case since Perl would use the default variable so in some sense, we actually are using the default variable, but we are using it explicitly and giving it a name. We can also use it explicitly by using the name of the default variable which is $_ so this gives us an amended version of the loop as follows.
1. foreach my $_ ( @array ) { 2. say $_; 3. }
Here, we are explicitly using the default variable but since it is optional, we can omit it. If we do, we are still using the default variable, but this time we are using it implicitly and this means amnding the loop so that it looks like this.
1. foreach ( @array ) { 2. say; 3. }
A couple of points should be made here. Firstly, we do need the parentheses here because we have a code block.
1. say foreach ( @array ) ;
We can get rid of the code block, but only if we are using the postfix version of the foreach loop. This is really important because if you forget that and try to use the, I guess you would call it prefix version of foreach without the curly braces, you will see a confusing error.
Another point worth making is that there are actually several default variables, but since this one is used so often, it is normally referred to as the default variable. The others are arrays used for different reasons, but we won’t be looking at them here.
The default variable is used in a number of places where you don’t specify a variable (or in some cases, an argument). It is also used in a large number of built-in functions and may also be used in the standard regular expression pattern matching operators. We will cover these operators later and we will also mention when the default variable is being used in other places.
More information on special variables can be found on the Perldoc page here.
Function Arguments
Another usage of the default variable is to pass an argument to a function and this is demonstrated in figure 43.
1. #!/usr/bin/perl 2. # deffunc.pl by Bill Weinman <http://bw.org/contact/> 3. 4. use 5.16.0; 5. use warnings; 6. 7. func1('one', 'two', 'three'); 8. 9. sub func1 { 10. say 'this is func1'; 11. say foreach @_; 12. }
Figure 44 - deffunc.pl where 3 arguments are being passed to the function func1
On line 11 we have an array (@_) and this is the default array. Note that the arguments on line 7 are in the form of a list. We haven’t created an array and added them to it or created them in the form of an array.
However, we have already seen, in countlines3.pl for example, that arguments are passed to a function in the form of an array so in this case, the default array is the array of arguments received by the function. We can then iterate over them using foreach.
If we want to use these values as scalar variables inside the function, we can assign them to variables like this.
my ($a, $b, $c) = @_;
We can then use these variables inside the function. For instance, we could output all three values with a single say statement like this.
say “$a $b $c”;
This is quite common in Perl. Another common way to do this (and again, we saw this in countlines3.pl) is to use shift to extract the variables from the array of arguments passed to the function. For instance, we can extract the first variable with
my $a = shift;
and we can do the same with $b and $c and again, we can display all three variables with
say “$a $b $c”;
This works because the shift function, by default, uses the default array and it does this sequentially so we can remove the variables from the array in the order in which they appear there.
Note that in addition to shift, the built-in functions pop, push and unshift also use the default array.
The Autoflush Variable
Perl uses buffering to handle large amounts of data which it then outputs when the buffer is full or according to some algorithm. In general, there is no easy way to flush the buffers. This is demonstrated with buffering.pl in figure 45.
1. #!/usr/bin/perl 2. # buffering.pl by Bill Weinman <http://bw.org/contact/> 3. 4. use 5.16.0; 5. use warnings; 6. 7. main(); 8. 9. sub main { 10. print "What is your favorite number? "; 11. my $num = <STDIN>; 12. my $mine = better_number($num); 13. print "\nReally? My favorite number is $mine, which is a much better number.\n"; 14. } 15. 16. sub better_number { 17. my $num = shift || 6; 18. $num = 6 unless $num =~ /^[0-9]+$/; 19. return $num + int(rand(10)) + 1; 20. }
Figure 45 - buffering.pl which provides an example of a buffering problem
Note that there is a prompt being displayed and then a request to input some number. A second string is output and this also outputs a second number which had been obtained from the better_number function (which adds a random value between 1 and 10 to the first number).
Now, output is sent to the output buffer and in some circumstances, this is not immediately passed to the output device (the screen in most cases). When I run the code, it is immediately displayed but when the code is run on the course video, it isn’t displayed until after the number has been input. The prompt has been sent to the output buffer, but it doesn’t know that the data needs to be flushed (sent to the output device). When the script is completed, anything in the output buffer is flushed so in the example shown on the course video, we see all the program’s output appearing in one burst.
This can be a problem since the user is being asked to input some data but does not see the prompt asking him/her to do that until after they input the data.
One solution is to use the autoflush variable which looks like this
1. $| = 1;
This is a configuration variable and setting its value to 1 switches it on. This causes the buffer to autoflush after each write operation. This is not the most efficient way to work but it can be preferable to a scenario where output is not being displayed immediately.
Just a side note here, on the system I am working on, I was not able to amend the value of the autoflush variable. It has been set to read-only so I assume this is because it is switched on and making it read-only prevents it from being switched off. Presumably, this is why the code worked first time for me.
This kind of buffering problem can lead to a variety of problems such as output in the wrong order, webpage headers may go out before content, output may bee incomplete if a script terminates due to an error and so on. In such circumstances, and assuming the autoflush variable is not read-only, you might try switching it on to see if that solves the problem.
The System Error Variable
The error variable contains the current value of the operating system’s global error condition. To demonstrate this, we have errno.pl which is shown in figure 46.
1. #!/usr/bin/perl 2. # errno.pl by Bill Weinman <http://bw.org/contact/> 3. 4. use 5.16.0; 5. use warnings; 6. 7. my $filename = 'notfound.txt'; 8. 9. if (-e $filename ) { 10. say 'found!'; 11. } else { 12. say "error: $!"; 13. }
Figure 46 - errno.pl which uses the variable holding the contents of the global error condition
On line 9, we are testing to see if the filename, which was specified on line 7, exists. If it does, the message on line 10 is displayed, otherwise the message on line 12 is displayed.
The message on line 12 is “error: “ followed by a special variable, $!, and this is the system error variable. In this example, the file does not exist and therefore we see the error message. Now, since we had tried to test for the existence of the file and it didn’t exist, the global error message has been set to “No such file or directory”. This is the string that is displayed when we output the value of the system error variable.
An error message has a string value like the one we saw above and a numeric value. Just as we used $! to access the string value, we can also use $!+0. This might look a little weird. What we are doing here is taking $! and adding a zero to it. This makes Perl treat it as a numeric value (thanks to duck typing) and so we get the numeric value for the error.
We can create a numeric variable to hold the error number with something like:
my $errnum=$!+0;
which we can insert inside the else clause in errno.pl, between lines 11 and 12 in figure 46. To include this in the output we can then include the variable in the output string so on line 12 (or what was line 12 in figure 46 and is now line 13) we amend this to
say "error number $errnum: $!";
The output from this is
error number 2: No such file or directory
Note that it is important that we add zero to this value (in order to get the numeric value of the error) otherwise it may be changed and we would see an incorrect error number.
For most modern operating systems, the value that we are displaying with the system error variable is provided in numeric form by the standard C library errno variable and in string form by the standard C library function strerror(). It is a very useful variable and we will see it again in this course.
Other Special Variables
Perl uses special variables for a lot of reasons, some of which we have already seen. In this section, we will look at some of the others by using them in a script, we will use a copy of hello.pl for this purpose. At the end of the section, we will have the full script with all the variables covered in this section.
The Environment Variable
This is a hash that stores some of the system environment variables. That is, some or all of these are accessible directly from a Command Line interface, they are not specific to Perl.
We can access this and output the results with
1. foreach my $k ( sort keys %ENV ) { 2. say "$k = $ENV{$k}"; 3. } 4.
In this case, the special variable is a hash and that’s the %ENV on the first line and notice that we have sorted the keys in alphabetical order. The say statements takes each key in sequence, represented by $k, and uses that key to access the corresponding value with $ENV{$k}.
I’m running this on a remote Centos 7 machine and the output is shown in figure 47.
1. izoom@localhost:Ch07 $ perl hello.pl 2. DBINT = /home/izoom/EZAccess/DB_INTEGRATION 3. EDITOR = /usr/bin/vim 4. EZBIN = /home/izoom/EZAccess/bin 5. HISTCONTROL = ignoredups 6. HISTSIZE = 1000 7. HISTTIMEFORMAT = %F %T 8. HOME = /home/izoom 9. HOSTNAME = localhost.localdomain 10. LANG = en_GB.UTF-8 11. LD_LIBRARY_PATH = /home/izoom/IGUtils/lib:/home/izoom/IGUtils:/home/izoom/IGUtils 12. LESSOPEN = ||/usr/bin/lesspipe.sh %s 13. LOGNAME = izoom 14. LS_COLORS = no=00:fi=00:di=01;32:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;33:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.ogg=01;35:*.mp3=01;35:*.wav=01;35: 15. MAIL = /var/spool/mail/izoom 16. OLDPWD = /home/izoom/philo/perl5EssTraining 17. PATH = /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/izoom/bin:/home/izoom/IGUtils:/sbin:/usr/sbin:/home/izoom/bin:/home/izoom/IGUtils 18. PS1 = \[\e]0;\w\a\]\[\e[1;32m\]\u@\[\e[1;33m\]\h:\[\e[1;36m\]\W \[\e[0;35m\]$([ \j -gt 0 ] && echo [\j] )\[\e[0m\]\$ 19. PWD = /home/izoom/philo/perl5EssTraining/Ch07 20. SHELL = /bin/bash 21. SHLVL = 1 22. SSH_CLIENT = 192.168.2.139 64418 22 23. SSH_CONNECTION = 192.168.2.139 64418 192.168.2.96 22 24. SSH_TTY = /dev/pts/0 25. SYBASE = /usr/local 26. TERM = xterm 27. USER = izoom 28. XDG_RUNTIME_DIR = /run/user/1000 29. XDG_SESSION_ID = 211452 30. _ = /usr/bin/perl
Figure 47 - the output showning the contents of %ENV, the hash that holds the environment variables
This can be quite useful, especially if you are creating web applications, you can use this to get the parameters that the browser passes to the script.
The script name/path ($0)
Another useful variable is $0 (note, is a zero, it is not the letter O) and this holds the name and/or path of the current script so if we put this in a say statement such as
say $0;
we will get output which will be something like
hello.pl
You will notice that on my Centos 7 system, only the script name is returned, not the path name. Why was that? When I executed the program, I was in the directory which held the script so it seems that it is including the path name, but it is a relative pathname. In other words, you might say that if you want to execute the script, this shows the command you need to execute including path and since I am already in the right directory, nothing is required for path. If I cd to the root directory and run the program again, I get this displayed instead.
/home/izoom/philo/perl5EssTraining/Ch07/hello.pl
If I then cd into my directory (philo) and run the program again, this is what is displayed.
./perl5EssTraining/Ch07/hello.pl
Note that the dot indicating the current directory has been included there.
The Operating System Name ($^O)
Similar to this is $^O (and this time that is the letter O, not the number 0) which displays the operating system name. I can add this to the script using a say statement similar to the one we used to display the script name and path
say $^O;
and this gives me the output
linux
This can be useful when your script is going to run on different platforms because it allows you to detect the operating system being used and then you might run some custom code or set some configuration options depending on the result of that.
Perl Version ($^V)
We can display the name of the operating system with $^V, and again, it’s a scalar variable so we can put it straight into a say statement such as
say $^V;
and this gives the output
v5.16.3
More Information
There are a lot of other special variables, and more information can be found on the Perldoc page linked to earlier. For convenience, the link is here as well! It is a worthwhile exercise to look through these to see what is available and to experiment with them.