my code :
if ($funcarg =~ /^test (.*)/) {
my $target1 = "http://$1/script/so.php";
system("wget $target1");
so, when i type perl a.pl 127.0.0.1 the script must download http://127.0.0.1/script/so.php, but unfortunately it doesn't. where is my mistake?
[root#localhost perl]# perl a.pl 127.0.0.1
http:///script/so.php: Invalid host name.
It's not clear how your command line argument gets from #ARGV to $funcarg. Or how it goes from 127.0.0.1 to (presumably) test 127.0.0.1. And I think that's where you're going wrong. I think that $funcarg doesn't contain what you think it does before you run the regex match.
This code does what I think you want. But I've had to make up two lines (as marked with a comment) and I'm pretty sure that your version of those two lines is where your misunderstanding is.
#!/usr/bin/perl
use strict;
use warnings;
#ARGV or die "No argument given\n";
# I've made up the next two lines. But I think
# this is where your bug is.
my $host = $ARGV[0];
my $funcarg = "test $host";
if ($funcarg =~ /^test (.*)/) {
my $target1 = "http://$1/script/so.php";
print "$target1\n";
system("wget $target1");
}
Related
I would like to inplace edit ssd_config file where i need to replace the #Port to a custom port.
Before:
#Port <portnum>
ex: #Port 22
After:
Port <customport>
ex: Port 2022
Here the custom port is coming in a variable $port.
I tried the below script but does nothing.
my $prt = "Port 2022";
my $cmd = "sed -i \'s/#Port [0-9]\+/$prt/g\' sshd_config";
system($cmd);
Tried even with tick operator.
`sed -i \"s/#Port [0-9]\+/\$prt/g\" sshd_config`;
I'd suggest to do all that in Perl, once you are running a Perl program. That way one doesn't have to run external programs, not to mention the benefits of not having to mess with all the quoting and escaping if you actually need a shell (not in the shown example though).
Then we need a few more lines of code, to read the file and and edit its content and write it back out. That shouldn't matter, given all the benefits -- but there are libraries that cut out even that as well, for example the very handy Path::Tiny
use Path::Tiny;
...
path($filename)->edit_lines( sub { s/#Port [0-9]+/Port $prt/ } );
I take it that $filename and $prt have been introduced earlier in the program.
There is also a edit method, which slurps the whole file.
Anything sed can do, Perl can do.
If this is your entire Perl program:
my $prt = "Port 2022";
my $cmd = "sed -i \'s/#Port [0-9]\+/$prt/g\' sshd_config";
system($cmd);
Then you can do it all in Perl itself from the command line.
perl -i -p -e's/#Port [0-9]+/Port 2022/g' sshd_config
system("sed ....") is invoking a shell to parse the command line, which means that everything needs to be properly escaped according to the rules of the shell. This together with string escaping inside Perl makes it hard to get right. A much simpler way is to skip the shell and call sed directly and also use string concatenation to add the $prt at the right place (and don't forget to also include the "Port" string itself since you want to have it in the output):
system('sed','-i','s/#Port [0-9]+/Port ' . $prt . '/', 'sshd_config');
Alternatively one could do it in Perl directly, i.e. something like this:
open(my $fh,'<','sshd_config') or die $!;
my #lines = map { s/#Port \d+/Port $prt/ } <$fh>;
open($fh,'>','sshd_config') or die $!;
print $fh #lines;
close($fh);
This is a bit longer but does not rely on starting an external program and is thus faster. And if there is more to do than a simple replacement it is also more flexible.
I'm trying to open an input file as an argument using the Getopt::Long module
This is the beginning of my script
#! /usr/bin/perl -s
use strict;
use warnings;
use Data::Dumper;
local $Data::Dumper::Useqq = 1;
use Getopt::Long qw(GetOptions);;
my $input='';
GetOptions('input|in=s' => \$input);
open(my $table1,'<', $input) or die "$! - [$input]"; #input file
And this is how I launch the script
$ script.pl -in /path/to/file.txt
I get as output:
No such file or directory - [] at script.pl line 13.
Line 13 is the line with open(....
Is there a mistake in the script?
You are using Perl's built-in option parsing with the -s in your shebang line. In a command like script.pl -in /path/to/file.txt that makes perl set the variable $in to 1 and remove the corresponding entry from #ARGV before Getopt::Long ever sees it
Just remove -s from the shebang line and everything it will work for you
No, there is no mistake in the script. Your code is doing what you told it to do.
It's calling the die "$! - [$input]"; part of that line, because the open returned a false value.
No such file or directory is the content of $!. That's the error it encountered. And between the [] there is the value of $input, which is empty. So there's your problem. You're passing an empty string to open, and that fails.
You are calling it the wrong way.
Getopt::Long requires options that are longer than one letter to be prefixed by --. This means that -in should be --in.
$ script.pl --in /path/to/file.txt
Because you didn't do that, Getopt::Long didn't see your option, and didn't parse it. A bit higher you initialized $input = '', so it stayed the empty string.
You could add a check to make sure that the input file is always provided.
use strict;
use warnings;
use Getopt::Long qw(GetOptions);;
my $input = '';
GetOptions('input|in=s' => \$input);
die 'the --input option is required!' unless $input;
open(my $table1,'<', $input) or die "$! - [$input]"; #input file
With many xterm's its nice to have a unique name on the window border that I can change on the fly. So I wrote this perl code in file header.pl:
#!/usr/bin/perl
my $saywhat = $ARGV[0];
my $mycmnd = <<"EOLzippo";
echo -n "\033]0;XROSS\007"
EOLzippo
$mycmnd =~ s!XROSS!$saywhat!g;
print $mycmnd;
Then with an alias:
alias header 'perl ~/perl/header.pl \!* '
I can change the text at the top of my xterms.
When the alias is called the code leaves a echo-n on my command line:
163 perl> header test
echo -n ""
Is there a way to execute the print without leaving the echo -n ""?
There's no need to print echo (did you try to shell out and echo?). print already prints.
#!/usr/bin/perl
use warnings;
use strict;
my $saywhat = shift;
my $string = "\033]0;$saywhat\007";
print $string;
Do it all in perl:
#!/usr/bin/perl
my $saywhat = $ARGV[0];
my $mycmnd = "\033]0;${saywhat}\007"
print $mycmnd;
Side note: While it is a convention to use a .pl suffix sometimes, it is by no means mandatory. If you rename header.pl to header and put it in ~/bin [or add ~/perl to $PATH] you can eliminate the alias. Just be sure to set execute permissions on the file (e.g. 755)
In Perl, it's normally easy enough to get a reference to the commandline arguments. I just use $ARGV[0] for example to get the name of a file that was passed in as the first argument.
When using a Perl one-liner, however, it seems to no longer work. For example, here I want to print the name of the file that I'm iterating through if a certain string is found within it:
perl -ne 'print $ARGV[0] if(/needle/)' haystack.txt
This doesn't work, because ARGV doesn't get populated when the -n or -p switch is used. Is there a way around this?
What you are looking for is $ARGV. Quote from perlvar:
$ARGV
Contains the name of the current file when reading from <> .
So, your one-liner would become:
perl -ne 'print $ARGV if(/needle/)' haystack.txt
Though be aware that it will print once for each match. If you want a newline added to the print, you can use the -l option.
perl -lne 'print $ARGV if(/needle/)' haystack.txt
If you want it to print only once for each match, you can close the ARGV file handle and make it skip to the next file:
perl -lne 'if (/needle/) { print $ARGV; close ARGV }' haystack.txt haystack2.txt
As Peter Mortensen points out, $ARGV and $ARGV[0] are two different variables. $ARGV[0] refers to the first element of the array #ARGV, whereas $ARGV is a scalar which is a completely different variable.
You say that #ARGV is not populated when using the -p or -n switch, which is not true. The code that runs silently is something like:
while (#ARGV) {
$ARGV = shift #ARGV; # arguments are removed during runtime
open ARGV, $ARGV or die $!;
while (defined($_ = <ARGV>)) { # long version of: while (<>) {
# your code goes here
} continue { # when using the -p switch
print $_; # it includes a print statement
}
}
Which in essence means that using $ARGV[0] will never show the real file name, because it is removed before it is accessed, and placed in $ARGV.
Say I have this perl "program" called simple.pl:
#!/usr/bin/perl
use xyz; # xyz is bogus and doesn't exist
And I also have this "program", called simple2.pl:
#!/usr/bin/perl
system("simple.pl");
my $abc = `simple.pl`;
printf("abc %s\n", $abc);
for both system and backtick, I get this message:
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 7.
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 9.
Not very useful for the user calling simple2.pl. Is there a way to get a more useful message?
Note. simple.pl does exist in the current directory. The real problem is that simple.pl doesn't compile. simple2 responds by saying simple doesn't exist. it's a misleading message.
If I had a way to even capture the compile message that would be a start.
This means system couldn't find an executable named "simple.pl" on your PATH. If your simple.pl is in the current directory, you could try to change "simple.pl" to "./simple.pl".
Actually, I don't see how to make this message more descriptive. If you were perl, how would you report this error?
BTW, I wouldn't try to run "simple2.pl" from inside of simple2.pl :)
Yes, check to see if the file exists and is executable, and if it isn't, print a more descriptive message.
unless (-ex $filename) {
print "I am unable to execute file $filename.";
}
If perl say it can't find the file, then it can't find the file. And the problem is more your code. Look at this example.
sidburn#sid:~/perl$ cat test.pl
#!/usr/bin/env perl
use strict;
use warnings;
use xyz;
sidburn#sid:~/perl$ cat test2.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('test.pl');
sidburn#sid:~/perl$ cat test3.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('./test.pl');
If you execute test2.pl you get:
sidburn#sid:~/perl$ ./test2.pl
Can't exec "test.pl": No such file or directory at ./test2.pl line 4.
If you execute test3.pl you get:
sidburn#sid:~/perl$ ./test3.pl
Can't locate xyz.pm in #INC (#INC contains: /home/sidburn/perl510/lib/5.10.1/i686-linux /home/sidburn/perl510/lib/5.10.1 /home/sidburn/perl510/lib/site_perl/5.10.1/i686-linux /home/sidburn/perl510/lib/site_perl/5.10.1 .) at ./test.pl line 4.
BEGIN failed--compilation aborted at ./test.pl line 4.
If you don't provide a relative or absolute path then perl lookup the command in your $PATH environment variable. If it is not there it can't find the file.
You need to provide "./" if it is in the current directory. But note "current directory" doesn't mean the directory where your script relies.
If you want the later then you probably want to do a
use FindBin;
with this you can do something like this:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use File::Spec::Functions;
my $exe = catfile($FindBin::RealBin, 'test.pl');
print $exe, "\n";
system($exe);
if you want to check if system returns correctly, you need to check the return value from the system() command or $? later that holds the value.
if ( $? != 0 ) {
die "Cannot execute $exe.\n";
}
if you want to suppress messages from your program you need to redirect STDOUT, STDERR before starting your program with system().
Or use something like IPC::System::Simple
Or IPC::Open3 (in the core).
Bonus points for enabling the warnings pragma! Have an upvote!
You want to use backticks or qx// to capture the output of an external program, not system. To substitute your own error message that will make sense to your users (more points for you!), then you might do something as in
#! /usr/bin/perl
use strict;
use warnings;
no warnings 'exec';
chomp(my $abc = `simple2.pl`);
if ($? == 0) {
printf("abc %s\n", $abc);
}
else {
die "$0: unable to calculate abc\n";
}
In case you're unfamiliar, $? is
$CHILD_ERROR
$?
The status returned by the last pipe close, backtick command, successful call to wait or waitpid, or from the system operator.
When $? is zero, it indicates success.
Remember that the warnings pragma is lexical, so rather than disabling the warning for the whole program, you might do it for just one sub:
sub calculate_abc {
no warnings 'exec';
# ...
}
If you are trying to execute something you know is a Perl script, why not invoke the interpreter directly rather than dealing with the system knowing how to execute the file?
my $file = 'simple.pl';
-e $file or die "file '$file' not found";
system "perl $file";
# or
print `perl $file`;
to run with the same installation of perl that is running your current script:
system "$^X $file"; # or `$^X $file`
$^X is a special Perl variable that contains the file name of the running interpreter.
I had the exact same issue and figured out that perl wasn't installed. So the bash script was trying to execute the perl without an interpreter.
ls /usr/bin/perl
Try specifying the full path to the "simple.pl" file.