What's the meaning of `unless -d`? - perl

Reading the code,I find this:
use File::Path;
mkpath($hinitdir) unless -d $hinitdir;
mkpath($hrestdir) unless -d $hrestdir;
So what's the meaning of the option -d?

Perl has "file test" operators of the form -X, where the letter after the dash tells Perl what you want to test. These are documented in perlfunc collectively under -X.
The -d tests that the target (in this case $hinitdir or $hrestdir) is a directory. The target of a file test operator can be either a named file of a file handle.
Apart from that, recent File::Path versions use make_path over the old mkpath.

Related

How to find (offline) perl documentation - "case insensitive" or with regular expression

How do I get information from perldoc (offline) when I don't know the exact syntax?
For example, when searching for the "lwp" module, it's not sufficient to look for perldoc -m lwp or perldoc -m lwp::simple (in linux/unix).
As a beginner I would think, perl would provide something like perldoc -m /lwp/ to search for the explression.
But you have to find out the exact syntax of the module, and then you can search for the documentation (or, you would write a oneliner/grep to search all the pod files for the expression) - but sometimes I don't remember the exact syntax and need a hint.
Do I miss something?
Searching the faq can be done with an expression perldoc -q something but not anything else (-f / -m / ...) ?
Thank you, for your support.
Update:
- Windows: (by its case-insensitive nature) one can run perldoc [-m] lwp::simple and will find LWP::Simple
- Linux/Unix: perldoc has an -i Parameter for insensitive search. (see perldoc -h) Running perldoc -i [-m] lwp::simple will find LWP::Simple
(the -m Parameter displays the module code and plaintext POD documentation - one can leave it, because it's not that pretty)
By exact syntax, do you mean the name of the module? Why are you looking for documentation for a module you don't know the name of?
If you want to find some module names installed locally, cpan(1) will give you the list:
% cpan -l
From that, you can search a name all you like (as you mentioned):
% cpan -l | grep -i lwp
This follows the basic unix idea of reusing tools that already do a good job. cpan give you the list and grep searches it.
In that list would be LWP. Since module names are case sensitive, you have to use the proper case (although some case-insensitive file systems fake this for you):
% perldoc LWP
Mostly, I get the name of the module I want to read about because it's noted in source code or I'm using an object of that type. I take the names from that:
print "The object is of type: ", ref $obj;
Beyond that, there is current work (discussed this week at The Perl Conference) to index various types of data and let you search for them.

Receiving strange error when piping yes into rm -r command

I receive the following
yes: standard output: broken pipe
yes: write error
when executing the following line in perl
system("cd /tmp; yes | rm -r directory1 directory2 file3 > /dev/null 2>&1");
The files and directories still get deleted but the message is annoying and would prefer not to see it.
any help Thanks
Not really a Perl question.
The error comes from yes, so you would have to redirect the error stream of yes, too.
... ; yes 2>/dev/null | rm -r ...
But why don't you forget about yes and try rm -rf ... ?
You say Perl, but your question says Unix shell.
If you're going to use Perl, do it in Perl:
use File::Path qw(remove_tree);
use Cwd;
my $current_directory = getcwd();
chdir "tmp";
remove_tree("directory1", "directory2, "file3");
chdir $current_directory;
This will do the same thing as your system command. But, this will work not only on Unix systems, but also on strange and peculiar operating systems that no man should be subject to (Cough, Windows! Cough).
It also will work even if someone has created an alias rm command, or has implemented their own rm command. Whenever you shell out via the system command, you leave that nice safe Perl environment and head directly into the dangerous neighborhood of the Operating System. Even worse, you're placing your life into the hands of a shell that might not be the shell you use either.
The module File::Path is a standard module that comes with Perl. Same goes with Cwd I used to save your original directory, so we could come back to it at the end of your code.
By the way, you probably want some error checking there too:
use File::Path qw(remove_tree);
use Cwd;
my $current_directory = getcwd();
if chdir "tmp" {
remove_tree("directory1", "directory2, "file3");
chdir $current_directory;
}
else {
print "Whoops! I can't change to directory 'tmp'"
}
This way, you are only doing the remove_tree if you've actually changed into directory tmp.
If this is your temporary working directory. That is, you've created it, so you have a place to put files, , you might should look at the File::Temp module.
File::Temp (which is also a standard Perl module) can create the temporary directory for you, and makes sure that it's unique, so you're not trampling over someone else running your program at the same time. Even better, it will also clean up after itself and delete that temporary directory once complete.
use File::Temp;
my $temp_dir = File::Temp->newdir;
Now, you can put all of your temporary files into $temp_dir. When the program ends, the temporary directory and all files you placed there are automatically deleted.
Although not part of the standard Perl functions, these modules are part of Perl's distribution and are always available. There should be no reason at all not to use them. If you're using the system command, there is probably a standard Perl module that you should be using instead.
Browse through the list of Perl modules in the Perdoc Webpage. Just make sure you select the version of Perl you're using.
To prevent questions, use -f or --interactive=never if implemented. See man rm.

What is the significance of -T or -w in #!/usr/bin/perl?

I googled about #!/usr/bin/perl, but I could not find any satisfactory answer. I know it’s a pretty basic thing, but still, could explain me what is the significance of #!/usr/bin/perl in Perl? Moreover, what does -w or -T signify in #!/usr/bin/perl? I am a newbie to Perl, so please be patient.
The #! is commonly called a "shebang" and it tells the computer how to run a script. You'll also see lots of shell-scripts with #!/bin/sh or #!/bin/bash.
So, /usr/bin/perl is your Perl interpreter and it is run and given the file to execute.
The rest of the line are options for Perl. The "-T" is tainting (it means input is marked as "not trusted" until you check it's format). The "-w" turns warnings on.
You can find out more by running perldoc perlrun (perldoc is Perl's documentation reader, might be installed, might be in its own package).
For scripts you write I would recommend starting them with:
#!/usr/bin/perl
use warnings;
use strict;
This turns on lots of warnings and extra checks - especially useful while you are learning (I'm still learning and I've been using Perl for more than 10 years now).
Both -w and -T are sort of "foolproof" flags.
-w is the same as use warning statement in your code, and it's an equivalent of warning option in many compilers. A simplest example would be a warning about using uninitialized variable:
#!/usr/bin/perl -w
print "$A\n";
print "Hello, world!\n";
Will print:
Name "main::A" used only once: possible typo at ./perl-warnings line 3.
Use of uninitialized value $A in concatenation (.) or string at
./perl-warnings line 3.
Hello, world!
The -T flag means that any value that came from the outside world (as opposite to being calculated inside the program) is considered potential threat, and disallows usage of such values in system-related operations, like writing files, executing system command, etc. (That's why Perl would activate the "taint" mode when the script is running under setuid/setgid.)
The "tainted" mode is "enforcing" you to double-check the value inside the script.
E.g., the code:
#!/usr/bin/perl -T
$A = shift;
open FILE, ">$A";
print "$A\n";
close FILE;
Will produce a fatal error (terminating the program):
$ ./perl-tainted jkjk
Insecure dependency in open while running with -T switch at
./perl-tainted line 3.
And that's only because the argument value came from "outside" and was not "double-checked". The "taint" mode is drawing your attention to that fact. Of course, it's easy to fool it, e.g.:
#!/usr/bin/perl -T
$A = shift;
$A = $1 if $A =~ /(^.*$)/;
open FILE, ">$A";
print "$A\n";
close FILE;
In this case everything worked fine. You "fooled" the "taint mode". Well, the assumption is that programer's intentions are to make the program safer, so the programmer wouldn't just work around the error, but would rather take some security measures. One of Perl's nicknames is "the glue and the duct tape of system administrators". It's not unlikely that system administrator would create Perl script for his own needs and would run it with root permissions. Think of this script doing something normal users are not allowed to do... you probably want to double-check things which are not part of the program itself, and you want Perl to remind you about them.
Hope it helps.
about Taint Mode(-T):
require and use statements change when taint mode is turned on.
The path to load libraries/modules no longer contains . (the current directory) from its path.
So if you load any libraries or modules relative to the current working directory without explicitly specifying the path, your script will break under taint mode.
For ex: Consider perl_taint_ex.pl
#!/usr/bin/perl -T
require "abc.pl";
print "Done";
would fail like this
D:\perlex>perl perl_taint_ex.pl
"-T" is on the #! line, it must also be used on the command line
at perl_taint_ex.pl line 1.
D:\perlex>perl -T perl_taint_ex.pl
Can't locate abc.pl in #INC (#INC contains: C:/Perl/site/lib C:/Perl/lib)
at perl_taint_ex.pl line 3.
So when taint mode is on, you must tell the require statement explicitly where to load the library since . is removed during taint mode from the #INC array.
#INC contains a list of valid paths to read library files and modules from.
If taint mode is on, you would simply do the following:
D:\perlex>perl -ID:\perlex -T perl_taint_ex.pl
Done
-ID:\perlex will include directory D:\perlex in #INC.
You can try other ways for adding path to #INC,this is just one example.
It's called a shebang. On Unix based systems (OSX, Linux, etc...) that line indicates the path to the language interpreter when the script is run from the command line. In the case of perl /usr/bin/perl is the path to the perl interpreter. If the hashbang is left out the *nix systems won't know how to parse the script when invoked as an executable. It will instead try to interpret the script in whatever shell the user happens to be running (probably bash) and break the script.
http://en.wikipedia.org/wiki/Hashbang
The -W and -T are arguments that controll the way the perl interpreter operates. They are the same arguments that you could invoke when calling perl interpreter directly from the command line.
-W shows warnings (aka debuging information).
-T turns on taint / security checking.

How to find path of find2perl script on Unix using bash or perl

We (the company I work for) need to run the find2perl script on over a thousand different Unix servers of different flavors (Linux, Solaris, HP-UX, AIX) and different versions.
The one thing that all the servers have in common, is that they all have at least one implementation of perl installed. However, not all systems have it configured the same way.
Finding the location of perl is easy enough using the which command. However, on 70% of the servers, the actual directory containing find2perl (the bin folder of perl) is not present in the $PATH variable and can't be located that way.
On some servers, perl is actually a symbolic link pointing another location, in which case I can use ls -l and sed to extract the target of the link to find where perl is actually installed.
On other servers however, it's more complicated, as it seems perl was compiled to a custom location and the binary of perl present in /bin or /usr/bin (or wherever perl is found) is not a symbolic link, but rather a full blown executable. In this case, I thought about using the #INC variable of perl to try to find find2perl but it seems rather excessive.
What would be the better/best/fullproof method (one-liner if possible) to always get the location of find2perl on a Unix system?
Ways to locate find2perl
Two ways, both of which rely on asking the perl install how it was configured:
Config.pm
Its probably scriptdirexp from Config.pm.
$ perl -MConfig -E 'say $Config{scriptdirexp}'
/usr/bin
And indeed, that's where find2perl is on my system. You can use Config; in your perl scripts, which is its major advantage over the next method.
perl -V:varname
As per Yanick Girouard's comment, you can also use perl -V:scriptdirexp to get this, in a format suitable to passing to eval in a shell script. There are actually several formats available (so, you don't need to use e.g., cut to parse it):
OPTION OUTPUT (\n = actual newline) NOTES
-V:scriptdirexp scriptdirexp='/usr/bin';\n full shell syntax, even if multiple -V options
-V:scriptdirexp: scriptdirexp='/usr/bin' trailing colon omits semicolon and newline
-V::scriptdirexp '/usr/bin'; \n extra leading colon omits var= part
-V::scriptdirexp: '/usr/bin' you can combine them.
Full documentation is in the perlrun manpage.
Ways to embed find2perl
If you decide to copy over find2perl, as per evil otto's comment, you can actually do that by embedding it in your shell script. There are many ways. If neither of the two below work, then you can certainly use shar (which has an extremely long history, and is likely compatible with everything).
Quoted here-document
The easiest way is if your shell supports quoted here-documents. They all should, as its a POSIX requirement:
#!/bin/sh
perl - -name 'foo' -mtime 2 -print <<'FIND2PERL'
#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$#"}'
if $running_under_some_shell;
⋮
FIND2PERL
Hex dump in a non-quoted here-document
If some of your shells don't implement quoted here-documents (POSIX‽ what's that!), then you have to protect find2perl from shell expansion. An easy way is to hex dump it, as 0–9 and a–f are all safe from shell expansion. The dump is easily done with xxd -p /usr/bin/find2perl, which only requires xxd on one machine. To read back the dump, you can use plain perl:
#!/bin/sh
perl -n -e 'chomp; print pack("H*", $_)' <<HEX | perl - -name 'foo'
23212f7573722f62696e2f7065726c0a202020206576616c202765786563
202f7573722f62696e2f7065726c202d5320243020247b312b222440227d
⋮
HEX
Using find2perl several times
Naturally, with either approach, you could also write find2perl to a temporary file (if you need to invoke it multiple times, for example). You could also embed it in a shell function.
perl -lwe '$_ = $^X; s/perl$/find2perl/; -f or die qq($_ not -f); print'
Copy the interpreter executable path into dollar default argument. Patch the value, assuming that find2perl is in the same directory as perl itself. (This is specified as UNIX only, so you don't have to cater for perl.exe, which would be easy enough to deal with.) Then test the file exists, and die if it doesn't. (You might invent some better error handling.) Then print the path if we're still alive. That's it.
Okay, here's a version that works for Windows, too:
perl -lwe "$_ = $^X; s/perl(\.exe)?$/find2perl/;
-f or -f qq($_.bat) or die qq($_ not -f); print"
Note the double quotes, de rigueur on Windows for cmd.exe. And it has to go on one line, I just wrapped it for readability.

How do I create a directory and parent directories in one Perl command?

In Perl, how can I create a subdirectory and, at the same time, create parent directories if they do not exist? Like UNIX's mkdir -p command?
use File::Path qw(make_path);
make_path("path/to/sub/directory");
The deprecated mkpath and preferred make_path stemmed from a discussion in Perl 5 Porters thread that's archived here.
In a nutshell, Perl 5.10 testing turned up awkwardness in the argument parsing of the makepath() interface. So it was replaced with a simpler version that took a hash as the final argument to set options for the function.
Use mkpath from the File::Path module:
use File::Path qw(mkpath);
mkpath("path/to/sub/directory");
Kindly ignore if you are looking for a Perl module with 'mkdir -p' functionality but the following code would work:
my $dir = '/root/example/dir';
system ("mkdir -p $dir 2> /dev/null") == 0
or die "failed to create $dir. exiting...\n";
You can use a module but then you have to install it on each server you are going to port your code on. I usually prefer to use system function for a work like mkdir because it's a lesser overhead to import and call a module when I need it only once to create a directory.
ref http://perldoc.perl.org/File/Path.html
"The make_path function creates the given directories if they don't exists [sic!] before, much like the Unix command mkdir -p"
mkdir() allows you to create directories in your Perl script.
Example:
use strict;
use warnings;
my $directory = "tmp";
unless(mkdir($directory, 0755)) {
die "Unable to create $directory\n";
This program create a directory called "tmp" with permissions set to 0755 (only the owner has the permission to write to the directory; group members and others can only view files and list the directory contents).