How do I get Perl to run an alias I've defined in BASH? - perl

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"';

Related

Perl backtick ignores everything past the first space

I have a command
my $output = `somecommand parm1 parm2`;
When I try to run this Perl script I get the message
Can't exec "somecommand" at .....
It seems it is not seeing anything past the first space in between the backticks. I have a friend who runs this in a different environment and it runs fine.
What could I have in my environment that would cause this? I am running Perl v5.20 but so is my friend.
Perl's not ignoring the command parameters, it's just mentioning only the part of the command that it has a problem with -- it can't find somecommand
Whatever your somecommand is, it's not a shell command and it's not in a directory listed in your PATH variable
Change PATH to add its location to the end and it will work for you. You can do that system-wide or you dan modify it temporarily in your Perl code by manipulating $ENV{PATH} before you run the command

Series of Perl Scripts. BASH, BATCH, Shell?

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.

Change shell within Perl 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 can I find out what script, program, or shell executed my Perl script?

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

Sourcing shell scripts in Perl

I want to source a shell script from within Perl and have the environment variables be available in Perl, but I'm not sure if there's an elegant way to do it. Obviously, using system() won't work since it runs in a forked process, and all environment changes will be lost. I think there's a CPAN module that can do it, but I prefer not to use external modules.
I've seen two solutions that would not work in my case:
Have a wrapper that calls the shell script, and then calls the Perl script. I do not know ahead of time which of my shell scripts I need to call.
Manually opening the shell script and scraping for arg=value pairs. This won't work either because the shell script is not a simple list of ARG=VALUE, but rather contain a bunch of conditionals, and variables can have different values depending on certain conditions.
sh -c "source script; env" should output the environment at the end of script as name=value pairs, which you then can parse from your perl script (as Perl is a language made for parsing, this should be easy).
You can do this by installing external module from CPAN which is Shell::Source
$env_path= Shell::Source->new(shell=>"tcsh",file=>"../path/to/file/temp.csh");
$env_path->inherit;
As perl creates its own instance while running on a shell, so we can not set environment path for the main shell as the perl's instance will be like sub shell of the main shell. Child can not set environment paths for parents.
Now till the perl's sub shell will run you'll be able to access all the paths present in temp.csh by using Shell::Source