I am trying to enter storage commands like symmask in Unix. I would like to build a script that would take a variable from Perl like standard input and then use that variable in a bash shell. I have been trying to do both in Perl but I can't run the storage command in Perl script. Unless I am just missing it.
You can run all external programs and commands from Perl with system, exec and the backtick-operator (`` and qx()).
Please refer to:
http://perldoc.perl.org/functions/system.html
http://perldoc.perl.org/perlop.html#%60STRING%60
http://perldoc.perl.org/functions/exec.html
If you want to, say, copy stuff to another server, you can use the backticks like this:
my $file = 'foo.csv';
`scp foo.csv someone#otherserver:dir/foo.csv`;
Related
I have a script that opens up different files at the same time.
My problem here is that when I run system() on the alias I've defined in bash which points to /home/user1/Software/nc, perl tells me that it can't execute the alias because there is no file/directory at the current location.
I know that the alias works because when I invoke it directly in a shell, it opens fine.
Funny enough, I can do system("firefox") within my script fine, but not the alias. How do I use this alias in this script without it breaking?
Perl won't run bash, it will execute the command directly. You can call
bash -c your_command
instead of calling the command itself in Perl.
As it is, this doesn't load your aliases. You need to open an interactive shell, as in #MortezaLSC's answer. There supposedly is a way of loading aliases correctly in a non-interactive shell, but I can't figure it out.
But why don't you just use the command you have aliased to directly in perl? The only reason I could see not to do this, is if your alias is going to change in the future, but you will still want to run whatever command it points to. This seems weird and dangerous to say the least.
Aliases are designed to reduce the typing you do if you invoke commands with the same options etc all the time. They're not all-purpose macros for bash. Bash has functions for doing more complicated stuff, but why would you want to call non-trivial bash code from a perl script? It doesn't seem like you really need this here. Keep the complexity, and the potential for modification and failure, in one place (the perl script).
See a couple of answers to similar questions:
https://unix.stackexchange.com/a/1499/41977
https://superuser.com/a/183980/187150
If you're smart, you made it so your alias is only defined for interactive shells, so you'll have to launch bash and specify that you want an interactive shell using -i.
system('bash', '-i', '-c', 'shell command');
Is it working?
system 'bash -i -c "your alias parameter"';
I have a small perl script which runs the /scripts/pkgacct command in cPanel using system(). The code looks like so;
print "\n/scripts/pkgacct --skiphomedir --nocompress $acc_name /my_backup\n\n";
system("/scripts/pkgacct --skiphomedir --nocompress $acc_name /my_backup");
my $bk_path = "/my_backup/cpmove-$acc_name.tar";
system("tar -xvf $bk_path -C /my_backup/");
When I run the script, only cPanel's default roundcube and horde databases are backed up. When I replace system() with exec"", the script runs as expected but terminates as soon as exec is executed, i.e the subsequent statements in the perl script aren't executed. Using backticks shows the same behaviour as system() - i.e doesn't backup all the databases.
Could someone tell me what mistake I am making?
Alternately, how can I get the remaining statements to execute after the exec command?
Try using system like so:
system('/scripts/pkgacct', '--skiphomedir', '--nocompress', $acc_name, '/my_backup');
I've found that system works best when you break up the command and parameters like it expects.
Try using IPC::Run (https://metacpan.org/pod/IPC::Run). Your code would look something like:
use IPC::Run qw(run);
print "\n/scripts/pkgacct --skiphomedir --nocompress $acc_name /my_backup\n\n";
run ['/scripts/pkgacct', '--skiphomedir', '--nocompress', $acc_name];
my $bk_path = "/my_backup/cpmove-$acc_name.tar";
run ['tar','-xvf',$bk_path,'-C','/my_backup/'];
I have a series of perl scripts that I want to run one after another on a unix system. What type of file would this be / could I reference it as in documentation? BASH, BATCH, Shell Script File?
Any help would be appreciated.
Simply put the commands you would use to run them manually in a file (say, perlScripts.sh):
#!/bin/sh
perl script1.pl
perl script2.pl
perl script3.pl
Then from the command line:
$ sh perlScripts.sh
Consider using Perl itself to run all of the scripts. If the scripts don't take command line arguments, you can simply use:
do 'script1.pl';
do 'script2.pl';
etc.
do 'file_name' basically copies the file's code into the current script and executes it. It gives each file its own scope, however, so variables won't clash.
This approach is more efficient, because it starts only one instance of the Perl interpreter. It will also avoid repeated loading of modules.
If you do need to pass arguments or capture the output, you can still do it in a Perl file with backquotes or system:
my $output = `script3.pl file1.txt`; #If the output is needed.
system("script3.pl","file1.txt"); #If the output is not needed.
This is similar to using a shell script. However, it is cross-platform compatible. It means your scripts only rely on Perl being present, and no other external programs. And it allows you to easily add functionality to the calling script.
I am looking for a nice way to get the following done:
So I have a script that I need to run in Python in Unix by calling from a Perl script that was, in turn, called from my Excel VBA macro in Windows using Plink. The Python script, due to dependency issues, has to run in either csh or bash, and I will need to use export/setenv to add a few libraries before running the script. However by default, perl runs in sh shell and as such, there is no way I can add in all the dependencies and have the Python script to run.
So, I am just wondering if there is EITHER: 1. a way for me to add dependencies to sh shell in the perl script, OR 2. force my perl script to run in csh (preferred, since for some reason .bashrc for the account runs into permission issues).
Thanks a lot!
How about "3. Set the appropriate environment variable in the Perl or Python scripts"?
$ENV{'PATH'} = ...
...
os.environ['PATH'] = os.pathsep.join(newpaths + os.environ['PATH'].split(os.pathsep))
(dunno how to get the path separator in Perl, sorz)
To force the shell to csh, try the following in Perl :
`/bin/csh -c "command_name"`;
Edit:
You can use ENV variable, like this. Try that :
$s = `/bin/bash -c 'VAR_FOO=753; echo \$VAR_FOO'`;
print $s;
I ended up just change the .cshrc script, apparently the addition to PATH, for some reason, did not work for me. After that, everything runs smoothly by putting all into one line
so basically it looks something like this
/path/to/.cshrc && /python/path/to/python
Hope that helps!
How would I determine what script, program, or shell executed my Perl script?
Example: I might want to have human readable output if executed from shell (customized for each type of shell), a different type of output if called as a script from another perl script, and a machine readable format if executed from a program such as a continuous integration server.
Motivation: I have a tool that changes its output based on which shell executes it. I'd normally implement this behavior as an option to the script, but this tool's design doesn't allow for options. Other shells have environment variables that indicate what shell is running. I'm working on a patch to support Powershell, which has no such special variable.
Edit: Many of these answers happen to be linux specific. Unfortuantely, Powershell is for Windows. getppid, the $ENV{SHELL} variable, and shelling out to ps won't help in this case. This script needs to run cross-platform.
You use getppid(). Take this snippet in child.pl:
my $ppid = getppid();
system("ps --no-headers $ppid");
If you run it from the command line, system will show bash or similar (among other things). Execute it with system("perl child.pl"); in another script, e.g. parent.pl, and you will see that perl parent.pl executed it.
To capture just the name of the process with arguments (thanks to ikegami for the correct ps syntax):
my $ppid = getppid();
my $ps = `ps --no-headers -o cmd $ppid`;
chomp $ps;
EDIT: An alternative to this approach, might be to create soft links to your script, make the different contexts use different links to access your script and inspect $0 to build logic around that.
I would suggest a different approach to accomplish your goal. Instead of guessing at the context, make it more explicit. Each use case is wholly separate, so have three different interfaces.
A function which can be called inside a Perl program. This would likely return a Perl data structure. This is far easier, faster and more reliable than parsing script output. It would also serve as the basis for the scripts.
A script which outputs for the current shell. It can look at $ENV{SHELL} to discover what shell is running. For bonus points, provide a switch to explicitly override.
A script which can be called inside a non-Perl program, such as your continuous integration server, and issue machine readable output. XML and/or JSON or whatever.
2 and 3 would be just thin wrappers to format the data coming out of 1.
Each is tailored to fit its specific need. Each will work without heuristics. Each will be far simpler than trying to guess the context and what the user wants.
If you can't separate 2 and 3, have the continuous integration server set an environment variable and look for it.
Depending on your environment, you may be able to pick it up from the environment variables. Consider the following code:
/usr/bin/perl -MData::Dumper -e 'print Dumper(\%ENV);' | grep sh
On my Ubuntu system, it gets me:
'SHELL' => '/bin/bash',
So I guess that says I'm running perl from a bash shell. If you use something else, the SHELL variable may give you a hint.
But let's say you know you're in bash, but perl is run from a subshell. Then try:
/bin/sh -c "/usr/bin/perl -MData::Dumper -e 'print Dumper(\%ENV);'" | grep sh
You will find:
'_' => '/bin/sh',
'SHELL' => '/bin/bash',
So the shell is still bash, but bash has a variable $_ which also show the absolute filename of the shell or script being executed, which may also give a valuable hint. Similarily, for other environments there will most probably be clues left in the perl %ENV hash that should give you valuable hints.
If you're running PowerShell 2.0 or above (most likely), you can infer the shell as a parent process by examining the environment variable %psmodulepath%. By default, it points to the system modules under %windir%\system32\windowspowershell\v1.0\modules; this is what you would see if you examine the variable from cmd.exe.
However, when PowerShell starts up, it prepends the user's default module search path to this environment variable which looks like: %userprofile%\documents\windowspowershell\modules. This is inherited by child processes. So, your logic would be to test if %psmodulepath% starts with %userprofile% to detect powershell 2.0 or higher. This won't work in PowerShell 1.0 because it does not support modules.
This is on Windows XP with PowerShell v2.0, so take it with a grain of salt.
In a cmd.exe shell, I get:
PSModulePath=C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\
whereas in the PowerShell console window, I get:
PSModulePath=E:\Home\user\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsP
owerShell\v1.0\Modules\
where E:\Home\user is where my "My Documents" folder is. So, one heuristic may be to check if PSModulePath contains a user dependent path.
In addition, in a console window, I get:
!::=::\
in the environment. From the PowerShell ISE, I get:
!::=::\
!C:=C:\Documents and Settings\user