Perl has three select() functions with widely differing purposes. One form takes four arguments and is a wrapper around the POSIX select(3) call. A second form takes zero arguments and returns the currently selected file handle: the handle of the output stream used by print; it normally defaults to standard output. The third form takes one argument, a file handle, and makes it the currently selected file handle. That is, this form of select() changes the file that is used by all print statements (unless they specify their own file handle).

Modifying the file handle used by print is counterintuitive because subsequent print statements will no longer print to standard output. Furthermore, the globally selected file handle is not garbage-collected; it remains open even if the file handle goes out of scope. Therefore, do not modify the selected file handle with select().

Noncompliant Code Example

This noncompliant code example tries to log a message while interacting with the user.

sub output_log {
  my $action = shift;
  open( my $log, ">>", "log.txt");
  select( $log);
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  $year += 1900;
  $mon += 1;
  print "$year-$mon-$mday $hour:$min:$sec: $action\n";
}

# ...

print "Hello!\n";
output_log("Greeted user");
print "How are you?\n";

Unfortunately, the select() method causes the last print statement to print "How are you?" not to standard output but to the log file.

Compliant Solution

This compliant solution avoids select() and directs the print() statement to use the log file handle for output.

sub output_log {
  my $action = shift;
  open( my $log, ">>", "log.txt");
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  $year += 1900;
  $mon += 1;
  print $log "$year-$mon-$mday $hour:$min:$sec: $action\n";
}

Noncompliant Code Example (Autoflush)

This noncompliant code example uses the one-argument select() function temporarily to modify the autoflush property associated with the file. The one-argument select() returns the old file handle (normally standard output). After the $log file handle is selected, the modification of $| instructs Perl to autoflush everything sent to the $log file handle. That is, every output to the $log file handle is flushed immediately. After the modification, select() is called again to restore the original selected file handle.

select(( select($log), $| = 1)[0]);

Compliant Solution

This compliant solution causes output to $log to be autoflushed without using select(). It uses the autoflush() object method from the IO::Handle module.

use IO::Handle;
# ...
$log->autoflush();

Risk Assessment

Failure to handle error codes or other values returned by functions can lead to incorrect program flow and violations of data integrity.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

EXP37-PL

Medium

Unlikely

Medium

P4

L3

Automated Detection

Tool

Diagnostic

Perl::Critic

InputOutput::ProhibitOneArgSelect

Bibliography

 


3 Comments

  1. The Perl community seems to prefer saying 'filehandle' rather than 'file handle'. See http://learn.perl.org/faq/perlfaq5.html for examples. This is prob because 'filehandle' is a widely-used data type in Perl.

    As for 'filename' vs 'file name', that same PERL FAQ page contains both, so I don't think it matters which we use, as long as we are consistent.

    1. The 2008 SEI Style Guide lists filename but doesn't address file handle. Using filename suggests that filehandle, filemanager, and filewhatever would be correct. Oxford Dictionary of Computing and the Microsoft Computer Dictionary both list file name. Main thing is consistency (and I'll go back through everything when we have a definitive answer here). Username/user name is another one we should decide on: if we close filename, we should close username.

  2. Anonymous

    While IO::Handle is much clearer, and that alone is reason to prefer it, under the hood it's autoflush() method is implemented in an equivalent fashion to the incorrect idiom, via a package which saves the selected filehandle and changes it within a limited scope.