How to grab the Unix command that I had run using Perl script? - perl

As I am still new to Unix and Perl, I'm finding a simple and direct method to grab the Unix command that I had run using Perl script.
What I know is "history" can track back the commands that I had run, but it is not working in Perl using back ticks history to run it.
I tried to put "history > filename" in vi text editor in a temporary file, use command "source" it, and it works, but command "source" also not working in Perl script using back ticks.
Can anyone guide me about my problems? direct me to correct method to solve my problems? T.T
Thanks.

You can't. Shells (well, bash and tcsh, anyway, your shell might, but probably doesn't, vary) only save command history in interactive mode. Commands run in a subshell by a perl script won't be added to the history file.

This will get the history of commands that were run by the user in interactive mode:
$data_file = "~/.bash_history";
open(DAT, $data_file) || die("Could not open file!");
#fileData = <DAT>;
close(DAT);
foreach $command (#fileData) {
# Do things here.
}
As mentioned by Wobble, though, this history file will not include commands run from a Perl script - you'll have to have the script append the command to a file when it runs it, thus creating it's own history file (or, append it to ~/.bash_history, which will have it share the history file with interactive shells).

If you have access to the perl script (that is, you can change it), you can simply write each command run in the perl script to a chosen text file:
sub run_program
{
my $program = shift;
open PROGS, ">>my-commands.txt", or die $!;
print PROGS $program."\n";
`$program`;
close(PROGS);
}
then just run `run_program($command) every time you wish to run a command in the script.

Related

Shell Programming inside Perl

I am writing a code in perl with embedded shell script in it:
#!/usr/bin/perl
use strict;
use warnings;
our sub main {
my $n;
my $n2=0;
$n=chdir("/home/directory/");
if($n){
print "change directory successful $n \n";
$n2 = system("cd", "section");
system("ls");
print "$n2 \n";
}
else {
print "no success $n \n";
}
print "$n\n";
}
main();
But it doesn't work. When I do the ls. The ls doesn't show new files. Anyone knows another way of doing it. I know I can use chdir(), but that is not the only problem, as I have other commands which I have created, which are simply shell commands put together. So does anyone know how to exactly use cli in perl, so that my compiler will keep the shell script attached to the same process rather than making a new process for each system ... I really don't know what to do.
The edits have been used to improve the question. Please don't mind the edits if the question is clear.
edits: good point made by mob that the system is a single process so it dies everytime. But, What I am trying to do is create a perl script which follows an algorithm which decides the flow of control of the shell script. So how do I make all these shell commands to follow the same process?
system spawns a new process, and any changes made to the environment of the new process are lost when the process exits. So calling system("cd foo") will change the directory to foo inside of a very short-lived process, but won't have any effect on the current process or any future subprocesses.
To do what you want to do (*), combine your commands into a single system call.
$n2 = system("cd section; ls");
You can use Perl's rich quoting features to pass longer sequences of commands, if necessary.
$n2 = system q{cd section
if ls foo ; then
echo we found foo in section
./process foo
else
echo we did not find foo\!
./create_foo > foo
fi};
$n2 = system << "EOSH";
cd section
./process bar > /tmp/log
cd ../sekshun
./process foo >> /tmp/log
if grep -q Error /tmp/log ; then
echo there were errors ...
fi
EOSH
(*) of course there are easy ways to do this entirely in Perl, but let's assume that the OP eventually will need some function only available in an external program
system("cd", "section"); attempts to execute the program cd, but there is no such program on your machine.
There is no such program because each process has its own current work directory, and one process cannot change another process's current work directory. (Programs would malfunction left and right if it was possible.)
It looks like you are attempting to have a Perl program execute a shell script. That requires recreating the shell in Perl. (More specific goals might have simpler solutions.)
What I am trying to do is create a perl script which follows an algorithm which decides the flow of control of the shell script.
Minimal change:
Create a shell script that prompts for instructions/commands. Have your Perl script launch the shell script using Expect and feed it answers/commands.

Unix commands in Perl?

I'm very new to Perl, and I would like to make a program that creates a directory and moves a file into that directory using the Unix command like:
mkdir test
Which I know would make a directory called "test". From there I would like to give more options like:
mv *.jpg test
That would move all .jpg files into my new directory.
So far I have this:
#!/usr/bin/perl
print "Folder Name:";
$fileName = <STDIN>;
chomp($fileType);
$result=`mkdir $fileName`;
print"Your folder was created \n";
Can anyone help me out with this?
Try doing this :
#!/usr/bin/perl
use strict; use warnings;
print "Folder Name:";
$dirName = <STDIN>;
chomp($dirName);
mkdir($dirName) && print "Your folder was created \n";
rename $_, "$dirName/$_" for <*.jpg>;
You will have a better control when using built-in perl functions than using Unix commands. That's the point of my snippet.
Most (if not all) Unix commands have a corresponding version as a function
e.g
mkdir - see here
mv - See here
Etc. either get a print out of the various manual pages (or probably have a trip down to the book shop - O'Reilly nut shell book is quite good along with others).
In perl you can use bash commands in backticks. However, what happens when the directory isn't created by the mkdir command? Your program doesn't get notified of this and continues on its merry way thinking that everything is fine.
You should use built in command in perl that do the same thing.
http://perldoc.perl.org/functions/mkdir.html
http://perldoc.perl.org/functions/rename.html
It is much easier to trap errors with those functions and fail gracefully. In addition, they run faster because you don't have to fork a new process for each command you run.
Perl has some functions similar to those of the shell. You can just use
mkdir $filename;
You can use backquotes to run a shell command, but it is only usefull if the command returns anything to its standard output, which mkdir does not. For commands without output, use system:
0 == system "mv *.jpg $folder" or die "Cannot move: $?";

Perl not executing command when in debugger or as a Win32::Daemon

Synopsis
I execute a shell command from Perl and when run from the command line it works, but when run in the debugger it does not work. Running it as a Win32::Daemon shows the same behaviour.
The Source Code
I execute a command either with backticks
print `$cmd`
or like this:
open FH, "$cmd |" or die "Couldn't execute $cmd: $!\n";
while(defined(my $line = <FH>)) {
chomp($line);
print "$line\n";
}
close FH;
The command reads like this:
$cmd = '"C:\path\to\sscep.exe" getca -f "C:\path\to\config\capi_sscep.cnf"'
Even creating a small test script that just executes this command does only work if run from command line.
The System
Windows x64
Active Perl v5.16.0, MSWin32-x64-multi-thread
Eclipse Juno 20120614-1722
What works
I works to open an administrator prompt (necessary for script execution) and to:
perl script.pl
Output gets printed to screen, $? is 0.
What does not work
Starting Eclipse and running a debug session with the same perl script.pl call.
Also not working is adding a service and executing the command (created with Win32::Daemon). The daemon itself is working perfectly fine and starting the perl script as expected. Only the command does not get executed. $? is 13568 or 53 if shifted with $? >> 8, no output gets printed. The exit code does not belong to the program.
Further Details
The tool I am calling is called sscep and is extended by me. It uses the OpenSSL API and loads the capi engine (Windows CryptoAPI). But the command itself does at least print output before any serious action starts. I can happily provide the source code for this, but I doubt it will help.
I was able to narrow this further down: The problem only exists in the combination of the Perl program (CertNanny) and the binary (sscep). Calling dir inside CertNanny works, calling sscep in a test Perl script works, too. So what could possibly be done in Perl to break a single binary from being called...?
Any ideas where this problem might originate from or how I can possibly narrow it down?
Here is what I believe the problem to be: when you run your program on the command line, the system() command goes through the shell (cmd.exe); when you run your program elsewhere, it does not. Unfortunately, the two methods handle command line arguments differently. Here is an article that seems like it should help you solve the problem.
In my experience, this sort of thing is a mess in Windows. I have had trouble with this issue in Perl, also.

How to set a crontab job for a perl script

I have a Perl script which I want to run every 4 hours through cron. But somehow it fails to execute through cron and runs fine if I run it through command line. Following is the command which I set in crontab:
perl -q /path_to_script/script.pl > /dev/null
Also, when I run this command on command prompt, it does not execute but when I go in the leaf folder in path_to_script and execute the file, it runs fine.
Also, where will the log files of this cron job be created so that I can view them?
You should probably change the working directory to "leaf folder".
Try this in your crontab command:
cd /path_to_script; perl script.pl >/dev/null
Wrt. log files. Cron will mail you the output. But since you sent stdout to /dev/null, only stderr will be mailed to you.
If you want the output saved in a log file, then pipe the stderr/stdout output of the script into a file, like so:
cd /path_to_script; perl script.pl 2>&1 >my_log_file
Usually cron will send you mail with the output of your program. When you're figuring it out, you probably want to check the environment. It won't necessarily be the same environment as your login shell (since it's not a login shell):
foreach my $key ( keys %ENV ) {
printf "$key: $$ENV{$key}\n";
}
If you're missing something you need, set it in your crontab:
SOME_VAR=some_value
HOME=/Users/Buster
If you need to start in a particular directory, you should chdir there. The starting directory from a cron job probably isn't what you think it is. Without an argument, chdir changes to your home directory. However, sometimes those environment variables might not be set in your cron session, so it's probably better to have a default value:
chdir( $ENV{HOME} || '/Users/Buster' );
At various critical points, you should give error output. This is a good thing even in non-cron programs:
open my $fh, '<', $some_file or die "Didn't find the file I was expecting: $!";
If you redirect things to /dev/null, you lose all that information that might help you solve the problem.
looks like you may have missed the
#!/usr/bin/perl
at the start of your perl script which is why you might need perl -q to run it
once you have added that line you can run it directly from the command line using
/path_to_script/script.pl
If you use a command in your perl program, i advise you to put the full path to the command in your program.
I have try to load environment but it is not more helpful.
After a oversee with one colleague, i think it's from interaction between perl and the system environment.
Best regards,
Moustapha Kourouma

How can I use vim to edit text from Perl while the script is still running?

I have a Perl script that outputs text. I want to import this text into vim, edit it, save it and then exit. On exit, I want the original Perl script to process the edited file.
E.G. how crontab -e works when you add a new job.
Thanks :)
Sounds like a very plain case to use system to run vim on the filename. That will wait until vim is done, at which point you can go ahead and read the file's new contents.
use File::Temp;
my $fh = new File::Temp();
my $fname = $fh->filename;
print $fh "My Text";
$fh->close();
system($ENV{EDITOR}, $fname);
open $fh, '<', $fname or die "Can't open temp file: $!";
while(<$fh>) { print }
close $fh;
A suggestion: Why don't you grab the source to crontab and see what it does?
Other than that, unwind has your answer.
And I think the way to think about this is:
"Okay, I need a program to write out a file, call vim, then do further processing on the file. So, here's my routine to write the file, here's my routine that calls vim (using system), and here's my routine to do the postprocessing after vim is done. Now I'll lace them all together in my main program, and I'm done!"
You seem to want to pipe the output of your script into vim:
script | vim
And then you want the script to somehow know that Vim is finished and resume. That's not how I/O redirection works. The script has no knowledge of what program its output is being piped into.
You mentioned other programs like crontab and cvs; one thing those have in common is that they invoke the editor themselves. They create temporary files, read the EDITOR or VISUAL environment variables (check the manual for details about how they choose which one), run the given program, and wait for the program to finish. Then they continue running and use the file they specified earlier.
Turns out I've done exactly that in Perl. I created a temporary file (with tempfile), wrote a bunch of text into it, and then used system to invoke the editor on the file. You don't even have to close the file while you run the editor.
Pipe the output to vim - and don't forget the dash at the end.
script.pl | vim -
This will open vim with the contents of the scripts output. Just save it :w filename and exit :q. This isn't how crontab -e works, but it comes close enough in an easy way.
Edit:
I missed this part (or maybe the question was edited afterward):
On exit, I want the original Perl
script to process the edited file.
This isn't the best way to do this, but going with my original answer, this works (if you save the file as file.txt):
perl -le 'print "1234"' | vim - && perl -pe '' file.txt
Just replace the perl commands with your perl commands and it should work. Like I said, this is not the best answer, but it should work.