How to use the source command with the system() function? - bash

I need to source a few environment variables in another file. If I use the source command with system() function, it's complaining about "No such file or directory". Am I missing something?
My code looks like below. In my code, I have only the system() function running the source command. The source file has just only one command: pwd (Present working directory).
perl_system.pl
#!/usr/bin/perl
system "source env.mk"
env.mk (contents of env.mk which I want to source has just pwd for now"
pwd
When I run this command, I see the below error
$ perl -w perl_system.pl
Can't exec "source": No such file or directory at perl_system.pl line 2.

source is a shell built-in that executes a shell script using the current shell interpreter. So it doesn't work as an external command and won't change the environment of your perl process even if you change your system call to invoke a shell instead of it trying to run an external program directly.
You could run your env.mk and then output the resulting environment and update perl's environment accordingly, though:
for my $env (`bash -c 'source env.mk;env'`) {
chomp $env;
my ($var,$val) = split /=/, $env, 2;
$ENV{$var} = $val;
}
(with obvious problems if environment variables contain newlines).
Update: just read all of your question, not just the beginning. If all you want to do is execute a shell script, just do:
system "sh env.mk";
source is completely unnecessary for this.

Related

Setting environment variable to shell executed from perl script

I am running a perl file a.pm which invokes b.sh via system command.
Here, b.sh is using find utility whose path is /usr/local/bin.
If I run env on shell directly on machine, I get output as below for PATH variable.
PATH=/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/bin:/home/bin/samba::/home/venv/bin/:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin`
Thats why if I run the b.sh directly from shell, it is able to execute find utility.
Now, If I run b.sh via a.pm as mentioned earlier using system(), and when I print PATH env variable in b.sh, its coming as
/bin:/usr/bin:/usr/X11R6/bin:/home/bin:/home/perl5/bin
which does not have /usr/local/bin, and thats why find command is failing.
If I tried to print all ENV variables in perl before invoking system(b.sh), PATH variable is not printed.
Now, I tried adding path variable in a.pm file as follows just before invoking system(b.sh).
$ENV{'PATH'} = '/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:/home/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/';
Now, if I try to print all ENV variables in perl before invoking system(b.sh), PATH variable is printed with above value.
Still executing the a.pm file, the PATH variable printed in b.sh is same:
/bin:/usr/bin:/usr/X11R6/bin:/home/bin:/home/perl5/bin
How can I add corresponding path /usr/local/bin to shell of b.sh invoked using a.pm?
I suspect that the Perl program is either modifying the path it gets from the shell that invokes it, or that you have left out a step somewhere. For example, if you invoke the Perl program from a different environment, it will likely have a different PATH.
You seemed to have found your answer though. Add the necessary directory to the PATH in the Perl program. But, you say this doesn't work. Again, I think there's some step that you haven't included. I suspect that the way in which you run system overwrites the PATH inherited from the parent.
For example, here's a small Perl program the merely runs a shell script:
#!perl
use v5.10;
$ENV{PATH} = '/bin:/usr/bin';
say "PATH in Perl is $ENV{PATH}";
system( "sh ./pather.sh" );
The shell script echos the PATH:
#!/bin/sh
echo "PATH in shell:" $PATH
When I run this, both PATHs match:
PATH in Perl is /bin:/usr/bin
PATH in shell: /bin:/usr/bin
But, maybe the command in system is something else. The -l switch treats the shell as a login shell, so it will load the various profiles and whatnot:
system( "sh -l ./pather.sh" );
Now the PATH is different in the shell script because my particular profiles overwrote PATH:
PATH in Perl is /bin:/usr/bin
Path in shell: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/usr/local/go/bin:...
Our answers can be more targeted you can produce a minimal working example where we see actual code that demonstrates the problem. Since we don't see what you actually run in system, we can only guess.

Adding bash functions to the $PATH or to bash shell automatically

Is there a way to add bash functions to the $PATH, or to the bash shell, without requiring an end-user to source them manually?
In other words, if we have a software library that exports only bash functions, we normally require the end-user to source the bash scripts with
. "$HOME/.the_scripts/"*.sh
and then using them. But is there a way to somehow get the bash functions into the shell without requiring the user to add a line of code to ~/.bashrc or ~/.bash_profile, etc?
What am I trying to do? I am trying to obviate the need for users to add a call to source a bash script for a library they just installed.
One suggestion I got was to write a container script, to a folder, where that folder is already in the $PATH.
Say I have a script like so:
#!/usr/bin/env bash
my_func(){
echo "this is my func, $1, $2, $3"
export foo="my_func"
}
my_func a b c
I could write that script to a folder in $PATH and then execute the script, which will then call the bash function(s).
Not sure how great/universal a solution this is, but it would work for some use cases I suppose. This will not work if you want to export env variables to the current shell, etc, because the bash function(s) would be run in a subshell as far as I know from the command line / current script.
If you read about the Bash Startup Files, you notice that /etc/profile is one of the files that is processed. If you read that file, you'll see that it sources all *.sh files in /etc/profile.d
If you can have your script libraries installed in /etc/profile.d, the functions will be available for all interactive login shell sessions.

How can I store and execute the command "export PATH=$PREFIX/bin" from a script?

I would like to write a script that has several commands of the kind
> export PATH=$PREFIX/bin
Where
> $PREFIX = /home/usr
or something else. Instead of typing it into the the Shell (/bin/bash) I would run the script to execute the commands.
Tried it with sh and then with a .py script having the line,
> commands.getstatusoutput('export PATH=$PREFIX/bin')
but these result into the error "bad variable name".
Would be thankful for some ideas!
If you need to adjust PATH (or any other environment variable) via a script after your .profile and equivalents have been run, you need to 'dot' or 'source' the file containing the script:
. file_setting_path
source file_setting_path
The . notation applies to all Bourne shell derivatives, and is standardized by POSIX. The source notation is used in C shell and has infected Bash completely unnecessarily.
Note that the file (file_setting_path) can be specified as a pathname, or if it lives in a directory listed on $PATH, it will be found. It only needs to be readable; it does not have to be executable.
The way the dot command works is that it reads the named file as part of the current shell environment, rather than executing it in a sub-shell like a normal script would be executed. Normally, the sub-shell sets its environment happily, but that doesn't affect the calling script.
The bad variable name is probably just a complaint that $PREFIX is undefined.
Usually a setting of PATH would look something like
export PATH=$PATH:/new/path/to/programs
so that you retain the old PATH but add something onto the end.
You are best off putting such things in your .bashrc so that they get run every time you log in.

bash shell in Cygwin

when I am in a Cygwin terminal, I can easily use the "source" command.
For example, let's say I have a file called "my_aliases.sh", that contains the following
#!/bin/bash -f
alias clear='cmd /c cls'
#unalias clear
Then on the Cygwin terminal, I can type
$source my_aliases.sh
And it just works fine, and whenever I type "clear", I can see that it works.
But I don't know why doing the same thing inside another shell script, and calling that shell script doesn't work.
For example, let's say that I have a file called "run_alias.sh", with the following content:
#!/bin/bash -f
#
a=`source my_aliases.sh`
b=`ls -ltr`
echo $a
echo $b
And when I try to run my file
$ ./run_alias.sh
It just doesn't do anything. For example, I can see that the command (b) takes place, but nothing happens for command (a).
But after I run "run_alias.sh", and type "clear", I get the following error:
$ clear
bash: clear: command not found
I even tried to change run_alias.sh as follows:
#!/bin/bash -f
echo `source my_aliases.sh`
But now when run run_alias.sh, and type clear, I get the exact same error message !!!
Any idea how to call the "source" command from some other shell script in Cygwin?
A child process cannot alter its parent's environment.
When you execute the run_alias.sh script, you launch a new bash process, which sources your alias file. Then the script ends, that bash process terminates and it takes its modified environment with it.
If you want your aliases to be automatically available, source it from your $HOME/.bashrc file.
Backticks create a subshell. The changes made to your environment in that subshell do not affect the calling environment.
Id you want your script (run_alias.sh) to have access to the environment in my_aliases.sh, call source directly.
source my_aliases.sh
b=`ls -lrt`
echo $b
and if you want the changes that run_alias.sh makes to its environment to propagate to it's parent, run source on the command line.
$ source run_alias.sh

How to run 'cd' in shell script and stay there after script finishes?

I used 'change directory' in my shell script (bash)
#!/bin/bash
alias mycd='cd some_place'
mycd
pwd
pwd prints some_place correctly, but after the script finished my current working directory doesn't change.
Is it possible to change my path by script?
You need to source the file as:
. myfile.sh
or
source myfile.sh
Without sourcing the changes will happen in the sub-shell and not in the parent shell which is invoking the script. But when you source a file the lines in the file are executed as if they were typed at the command line.
While sourcing the script you want to run is one solution, you should be aware that this script then can directly modify the environment of your current shell. Also it is not possible to pass arguments anymore.
Another way to do, is to implement your script as a function in bash.
function cdbm() {
cd whereever_you_want_to_go
echo arguments to the functions were $1, $2, ...
}
This technique is used by autojump:
http://github.com/joelthelion/autojump/wiki
to provide you with learning shell directory bookmarks.
The script is run in a separate subshell. That subshell changes directory, not the shell you run it in. A possible solution is to source the script instead of running it:
# Bash
source yourscript.sh
# or POSIX sh
. yourscript.sh
It can be achieved by sourcing. Sourcing is basically execute the script in the same shell whereas normal execution(sh test.sh or ./test.sh) will create sub shell and execute script there.
test.sh
cd development/
ls
# Do whatever you want.
Execute test.sh by
source test.sh
. is shortest notation for source. So you can also do by
. test.sh
This will execute the script and change the directory of current shell to development/.
whenever you run a script on your login shell, a new subprocess is spawned and the script execution is done in a subshell.Once the script completes, the subshell exits and you are returned to the login shell.Hence whenever you do a cd through a script,the directory is changed to the path specified by cd, but by the time script finishes you come back to your login shell to the working directory from where you started the script.
The way to overcome this is use,
source yourscript.sh
what source does is it executes the script as TCL script, i.e it has the same effect as when you typed each line on the command line of your login shell and it executed from there. So this way when the script finishes after cd , it stays in that directory.
Another practical solution is to end your script by opening another shell session.
For instance:
#!/bin/bash
cd some_place
bash
This is useful, in my case, for scripts located in my ~/bin for instance, called from any other place. It is just a bit painful to type source ~/bin/mygoodoldscript instead of mygoo<TAB><ENTER>.
The downside is that the additional shell takes up a few more resources (not much).
Though there are answers. I think the intention of question is to use script to navigate to specific path.
Here is a simple practical solution works here without cancel out existing terminal environment flag.
provide a bash/tch/sh script to work for path generation
/* .goto.sh */
#!/usr/bin/env bash
echo '~/workspace'
add alias to the script output
alias goto 'cd `.goto.sh`'

Resources