Perl using system to extract tar - perl

I am trying to extract a directory with tar files, but it doesn't seem to work correctly .
The command works in command line, but not with the "system" in perl. I really need this to work in perl.
$tar_dir = "/root/updates/*.tar.bz2";
system ("for i in $tar_dir ; do tar xvfj $i; done");
Works on command line like this : for i in *.bz2 ; do tar xvfj $i; done
Am i missing something ?
Thanks

Or, you could move the loop into the Perl code:
use warnings;
use strict;
for (glob '/root/updates/*.tar.bz2') {
system "tar xvfj $_";
}

Yes, you forgot to escape $i in the string in perl.
Try:
system ("for i in $tar_dir ; do tar xvfj \$i; done");
If you don't escape it, then it will get expanded just like $tar_dir. Make sure you have use strict; and use warnings; at the top of your perl script, it will help a bit with this sort of thing (if $i wasn't defined in the perl script in this case, you'd have got a message from perl telling you about the problem.)

Related

execute shell commands from perl script

I want to rename *.DIF files to *.SUC files
But the following script is giving "sh: bad substitution" error
I cannot use "rename" becuase my OS is not Linux.
$com="for i in *.DIF; do mv \$i \${i/DIF/SUC}; done;";
print $com."\n";
print `$com`;
Output :
for i in *.DIF; do mv $i ${i/DIF/SUC}; done;
sh: bad substitution
If you are having problems with Perl's rename, use File::Copy, a platform-independent module for moving and copying files. It is a core module, so it should be already part of your Perl installation.
If the system command works when you enter it in the shell, but not in Perl, the most likely reason is that Perl isn't using the shell you expect. We would need more information about your system to be sure, though.
There's no need to shell out for file operations that you can easily do within Perl.
The following renames all of your .dif extension files as .suc.
use strict;
use warnings;
use File::Copy qw(move);
move($_, s/dif$/suc/r) for glob('*.dif');
be default perl was using sh, instead of bash, which allows {//}
this question helped.
Finally I used :
open my $bash_handle, '| bash' or die "Cannot open bash: $!";
print $bash_handle 'for i in *.SUC; do mv $i ${i/SUC/DIF}; done;';

How to delete a bunch of lines in perl (adapting a known one-liner)?

context: I'm a beginner in Perl and struggling, please be patient, thanks.
the question: there is a one-liner that seems to do the job I want (in a cygwin console it does fine on my test file). So now I would need to turn it into a script, but I can't manage that unfortunately.
The one-liner in question is provided in the answer by Aki here Delete lines in perl
perl -ne 'print unless /HELLO/../GOODBYE/' <file_name>
Namely I would like to have a script that opens my file "test.dat" and removes the lines between some strings HELLO and GOODBYE. Here is what I tried and which fails (the path is fine for cygwin):
#!/bin/perl
use strict;
use warnings;
open (THEFILE, "+<test.dat") || die "error opening";
my $line;
while ($line =<THEFILE>){
next if /hello/../goodbye/;
print THEFILE $line;
}
close (THEFILE);
Many thanks in advance!
Your one-liner is equivalent to the following
while (<>) {
print unless /HELLO/../GOODBYE/;
}
Your code does something quite different. You should not attempt to read and write to the same file handle, that usually does not do what you think. When you want to quickly edit a file, you can use the -i "in-place edit" switch:
perl -ni -e 'print unless /HELLO/../GOODBYE/' file
Do note that changes to the file are irreversible, so you should make backups. You can use the backup option for that switch, e.g. -i.bak, but be aware that it is not flawless, as running the same command twice will still overwrite your backup (by saving to the same file name twice).
The simplest and safest way to do it, IMO, is to simply use shell redirection
perl script.pl file.txt > newfile.txt
While using the script file I showed at the top.

perl mktemp and echo

i am trying to put some word in tempfile via commandline
temp file creat but word not past in tempfile
#!/usr/bin/perl -w
system ('clear');
$TMPFILE = "mktemp /tmp/myfile/devid.XXXXXXXXXX";
$echo = "echo /"hello world/" >$TMPFILE";
system ("$TMPFILE");
system ("$echo");
Please Help to Solve This
To capture the name output by mktemp, do this instead:
chomp($TMPFILE = `mktemp /tmp/myfile/devid.XXXXXXXXXX`);
But Perl can do all the things you are doing without resorting to the shell.
Avoid using external commands from perl script as much as possible.
you can use: File::Temp module in this case, see this
Here's a specific demonstration of the advice that others have given you: where possible, use Perl directly rather than invoking system. Also, you should get in the habit of including use strict and use warnings in your Perl scripts.
use strict;
use warnings;
use File::Temp;
my $ft = File::Temp->new(
UNLINK => 0,
TEMPLATE => '/tmp/myfile/devid.XXXXXXXXXX',
);
print "Writing to temp file: ", $ft->filename, "\n";
print $ft "Hello, world.\n";

How can I convert Perl one-liners into complete scripts?

I find a lot of Perl one-liners online. Sometimes I want to convert these one-liners into a script, because otherwise I'll forget the syntax of the one-liner.
For example, I'm using the following command (from nagios.com):
tail -f /var/log/nagios/nagios.log | perl -pe 's/(\d+)/localtime($1)/e'
I'd to replace it with something like this:
tail -f /var/log/nagios/nagios.log | ~/bin/nagiostime.pl
However, I can't figure out the best way to quickly throw this stuff into a script. Does anyone have a quick way to throw these one-liners into a Bash or Perl script?
You can convert any Perl one-liner into a full script by passing it through the B::Deparse compiler backend that generates Perl source code:
perl -MO=Deparse -pe 's/(\d+)/localtime($1)/e'
outputs:
LINE: while (defined($_ = <ARGV>)) {
s/(\d+)/localtime($1);/e;
}
continue {
print $_;
}
The advantage of this approach over decoding the command line flags manually is that this is exactly the way Perl interprets your script, so there is no guesswork. B::Deparse is a core module, so there is nothing to install.
Take a look at perlrun:
-p
causes Perl to assume the following loop around your program, which makes it iterate over filename arguments somewhat like sed:
LINE:
while (<>) {
... # your program goes here
} continue {
print or die "-p destination: $!\n";
}
If a file named by an argument cannot be opened for some reason, Perl warns you about it, and moves on to the next file. Note that the lines are printed automatically. An error occurring during printing is treated as fatal. To suppress printing use the -n switch. A -p overrides a -n switch.
BEGIN and END blocks may be used to capture control before or after the implicit loop, just as in awk.
So, simply take this chunk of code, insertyour code at the "# your program goes here" line, and viola, your script is ready!
Thus, it would be:
#!/usr/bin/perl -w
use strict; # or use 5.012 if you've got newer perls
while (<>) {
s/(\d+)/localtime($1)/e
} continue {
print or die "-p destination: $!\n";
}
That one's really easy to store in a script!
#! /usr/bin/perl -p
s/(\d+)/localtime($1)/e
The -e option introduces Perl code to be executed—which you might think of as a script on the command line—so drop it and stick the code in the body. Leave -p in the shebang (#!) line.
In general, it's safest to stick to at most one "clump" of options in the shebang line. If you need more, you could always throw their equivalents inside a BEGIN {} block.
Don't forget chmod +x ~/bin/nagiostime.pl
You could get a little fancier and embed the tail part too:
#! /usr/bin/perl -p
BEGIN {
die "Usage: $0 [ nagios-log ]\n" if #ARGV > 1;
my $log = #ARGV ? shift : "/var/log/nagios/nagios.log";
#ARGV = ("tail -f '$log' |");
}
s/(\d+)/localtime($1)/e
This works because the code written for you by -p uses Perl's "magic" (2-argument) open that processes pipes specially.
With no arguments, it transforms nagios.log, but you can also specify a different log file, e.g.,
$ ~/bin/nagiostime.pl /tmp/other-nagios.log
Robert has the "real" answer above, but it's not very practical. The -p switch does a bit of magic, and other options have even more magic (e.g. check out the logic behind the -i flag). In practice, I'd simply just make a bash alias/function to wrap around the oneliner, rather than convert it to a script.
Alternatively, here's your oneliner as a script: :)
#!/usr/bin/bash
# takes any number of arguments: the filenames to pipe to the perl filter
tail -f $# | perl -pe 's/(\d+)/localtime($1)/e'
There are some good answers here if you want to keep the one-liner-turned-script around and possibly even expand upon it, but the simplest thing that could possibly work is just:
#!/usr/bin/perl -p
s/(\d+)/localtime($1)/e
Perl will recognize parameters on the hashbang line of the script, so instead of writing out the loop in full, you can just continue to do the implicit loop with -p.
But writing the loop explicitly and using -w and "use strict;" are good if plan to use it as a starting point for writing a longer script.
#!/usr/bin/env perl
while(<>) {
s/(\d+)/localtime($1)/e;
print;
}
The while loop and the print is what -p does automatically for you.

Can I run a Perl script from stdin?

Suppose I have a Perl script, namely mytest.pl. Can I run it by something like cat mytest.pl | perl -e?
The reason I want to do this is that I have a encrypted perl script and I can decrypt it in my c program and I want to run it in my c program. I don't want to write the decrypted script back to harddisk due to secruity concerns, so I need to run this perl script on-the-fly, all in memory.
This question has nothing to do with the cat command, I just want to know how to feed perl script to stdin, and let perl interpreter to run it.
perl < mytest.pl
should do the trick in any shell. It invokes perl and feeds the script in via the shell redirection operator <.
As pointed out, though, it seems a little unnecessary. Why not start the script with
#!/usr/bin/perl
or perhaps
#!/usr/bin/env perl
? (modified to reflect your Perl and/or env path)
Note the Useless Use of Cat Award. Whenever I use cat I stop and think whether the shell can provide this functionality for me instead.
Sometimes one needs to execute a perl script and pass it an argument. The STDIN construction perl input_file.txt < script.pl won't work. Using the tip from How to assign a heredoc value to a variable in Bash we overcome this by using a "here-script":
#!/bin/bash
read -r -d '' SCRIPT <<'EOS'
$total = 0;
while (<>) {
chomp;
#line = split "\t";
$total++;
}
print "Total: $total\n";
EOS
perl -e "$SCRIPT" input_file.txt
perl mytest.pl
should be the correct way. Why are you doing the unnecessary?
cat mytest.pl | perl
…is all you need. The -e switch expects the script as a command line argument.
perl will read the program from STDIN if you don't give it any arguments.
So you could theoretically read an encrypted file, decrypt it, and run it, without saving the file anywhere.
Here is a sample program:
#! /usr/bin/perl
use strict;
use warnings;
use 5.10.1;
use Crypt::CBC;
my $encrypted = do {
open my $encrypted_file, '<', 'perl_program.encrypted';
local $/ = undef;
<$encrypted_file>;
};
my $key = pack("H16", "0123456789ABCDEF");
my $cipher = Crypt::CBC->new(
'-key' => $key,
'-cipher' => 'Blowfish'
);
my $plaintext = $cipher->decrypt($encrypted);
use IPC::Run qw'run';
run [$^X], \$plaintext;
To test this program, I first ran this:
perl -MCrypt::CBC -e'
my $a = qq[print "Hello World\n"];
my $key = pack("H16", "0123456789ABCDEF");
my $cipher = Crypt::CBC->new(-key=>$key,-cipher=>"Blowfish");
my $encrypted = $cipher->encrypt($a);
print $encrypted;
' > perl_program.encrypted
This still won't stop dedicated hackers, but it will prevent most users from looking at the unencrypted program.