I am still learning to write shell scripting so i don't know whether this can be done.
I have a main script called main.sh
Main.sh
#!/bin/bash
function log {
echo "[${USER}][`date`] - ${*}" >> ${LOG_FILE}
}
home/script/loadFile.sh && home/script/processData.sh
So my question is can i call my log function of main.sh inside loadFile.sh and processData.sh script file ?
I tried it but i got error
line 1: log: command not found
Thanks.
This is not portable, but in bash you can simply export the function definition:
export -f log
home/script/loadFile.sh && home/script/processData.sh
you need to prompt like this:
. home/script/loadFile.sh && . home/script/processData.sh
But if you have an exit command in your loadFile.sh or processData.sh then your main.sh will exist as well
When you start loadFile.sh and processData.sh like you do, they are started as ordinary executables, so parent shell does not recognize then as shell scripts and new instance of shell interpreter is started for each script. New shell interpreter does not know anything about your log function.
When you run loadFile.sh and processData.sh like this:
. home/script/loadFile.sh && . home/script/processData.sh
Shell treats them as shell scripts rather than as ordinary executables and executes in current context, thus making function log visible to them. Also, any functions/variables defined inside loadFile.sh and processData.sh will be visible in parent shell after they will exit, and thus these scripts has many ways to damange parent shell, which makes such way unsafe in some situations.
Related
I am looking to execute a script but have it include another script before it executes. The problem is, the included script would be generated and the executed script would be unmodifiable. One solution I came up with, was to actually reverse the include, by having the include script as a wrapper, calling set to set the arguments for the executed script and then dotting/sourcing it. E.g.
#!/bin/bash
# Generated wrapper or include script.
: Performing some setup...
target_script=$1 ; shift
set -- "$#"
. "$target_script"
Where target_script is the script I actually want to run, importing settings from the wrapper.
However, the potential problem I face is that callers of the target script or even the target script itself may be expecting $0 to be set to the path of it's location on the file system. But because this wrapper approach overrides $0, the value of $0 may be unexpected and could produce undefined behaviour.
Is there another way to perform what is in effect, an LD_PRELOAD but in the scripted form, through bash without interfering with its runtime parameters?
I have looked at --init-file or --rcfile, but these only seem to be included for interactive shells.
Forcing interactive mode does seem to allow me to specify --rcfile:
$ bash --rcfile /tmp/x-include.sh -i /tmp/xx.sh
include_script: $0=bash, $BASH_SOURCE=/tmp/x-include.sh
target_script: $0=/tmp/xx.sh, $BASH_SOURCE=/tmp/xx.sh
Content of the x-include.sh script:
#!/bin/bash
echo "include_script: \$0=$0, \$BASH_SOURCE=$BASH_SOURCE"
Content of the xx.sh script:
#!/bin/bash
echo "target_script: \$0=$0, \$BASH_SOURCE=$BASH_SOURCE"
From the bash documentation:
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in
the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read
and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the file name.
So that settles it then:
BASH_ENV=/tmp/x-include.sh /bin/bash /tmp/xx.sh
I'm trying to set an alias that applies to the current shell (the shell I'm running the script from) from a shell script. The alias is for cd-ing into the folder of the script. Here's the (not working) script:
#!/bin/bash
shopt -s expand_aliases
DIR=$(cd $(dirname "$0"); pwd) # Detect the folder of the script.
alias cdr="cd $DIR" # cd into the folder.
I quickly realized that this didn't work because the alias it made was pertinent to the script's subshell.
Then, I tried to source the file (as in . makeAlias.sh). However, this produced an error: dirname: illegal option -- b.
How do I write a bash script that makes an alias relevant to the outer shell (the shell running the script)?
The immediate problem is that the value of $0 is now -bash. You might want to refactor your code to use a different reference point, or simply hard-code the path.
To answer the "how do I ...?" you aren't doing anything wrong, it's just that the logic has to be adapted to a different environment -- specifically, when you source a script, $0 is that of the parent process, not the name of the script you are sourcing.
Depending on what you are trying to accomplish, maybe this alternative design could work?
newr () { r=$(pwd); }
cdr () { cd "$r"; }
newr
That is, cdr simply changes directory to whatever the variable r contains. The function newr can be used to conveniently set r to your current working directory. You'd define these in your .bashrc or similar, and use them interactively
./makeAlias.sh will be executed in a sub-shell and the changes made apply only the to sub-shell. Once the command terminates, the sub-shell goes and so do the changes.
Sourcing the file using . ./makeAlias.sh or source ./makeAlias.sh will read and execute commands from the file-name argument in the current shell context, that is when a script is run using source it runs within the existing shell, any variables created or modified by the script will remain available after the script completes.
Consider this script I wrote, which should go into parent directory, when no argument is given (the if ... part).
#/bin/bash
if (($# == 0))
then
cd ..
else
for basename
do
cd ${PWD%$basename*}$basename
done
fi
The problem is, that if I execute it like this
./up.sh
the cd is executed in a subshell, rendering it useless.
If I execute the script using source, it works, but I don't want to call it that way (I makes calling the script to complicated, also you would expect to call it directly if found in the PATH).
An arbitrary program (such as your bash program) cannot change the working directory of the parent process, as that would pretty much break all existing processes that spawn children.
You should define a bash alias or function instead. As you have discovered, typing source ./up.sh (or shorter: . ./up.sh) works too.
I suggest using a function instead of a script
function myscript()
{
// use $1, $2, "$#" as usual in scripts
local v1="bla" # can use globals
export PATH="$PATH" # but global shell env too
somedirectory=..
cd $somedirectory
}
Alternatively, alias would work (but it doesn't support redirection, argument passing, flow control etc well, and you cannot nest them in $() IIRC).
Lastly source the existing script in the current shell like so:
source ./script.sh
ksh and bash have shorthands for that:
. ./script.sh
Beware of scripts with 'exit' statements though: they will exit the parent shell!
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`'
I just found this very usefull shell script here on SO but unfortunately it's not working on Mac OS X 10.5.
This is the script in question(copied it for convenience):
#!/bin/bash
LIMIT=$1
P=$PWD
for ((i=1; i <= LIMIT; i++))
do
P=$P/..
done
cd $P
I tried to echo $P at the very end and it's returning the right path, but still cd $P doesn't seem to be working.
So I tried to manually enter P="some/path" and cd $P in the terminal and it worked.
I don't get why the same command isn't working in the script. Could it be a security thing?
Any suggestions?
I've had the same issue on Linux, actually, if I understood correctly what I've found after some searching, this is what happens:
The command is launched in a subshell, and in that subshell the path gets changed, you don't see the change because when the script finishes you get back to the starting (parent) shell.
I solved this by putting that useful script in my .bashrc as a function, like this:
up(){
#code goes here
}
Another option is to source the script every time you launch it but I prefer the first one.
once the shell script ends it will put you right back in the directory it was executed from. The cd will only effect the cwd of the script process
You are only changing the working directory for the copy of the shell that is running the script as an interpreter, not the original shell program that you launched the script from.
For a bash-like shell, in order to run a sequence of commands that operate on the interactive shell session, you can define them as a shell function.
e.g. type the following
up() { LIMIT=$1; P=$PWD; for ((i=1; i <= LIMIT; i++)); do P=$P/..; done; cd $P; }
and you'll define an up command that works the way you intended.
You could put this function definition into a file that is sourced when you login, such as .bashrc, to keep it conveniently defined on login.
If you want to run the script within the context of your current shell just do one of the following (assuming your shell script is called cdup)
. cdup 3
source cdup 3
The source command (and its alias .) run the provided script within the context of your current shell, i.e. they do not start a separate sub-shell to run the command so your cd will work as it is within the current shell
A small addition to the up() - function; add the test for no value:
LIMIT=$1
if [ -z "$LIMIT" ]; then
LIMIT=1
fi
and no more "cd .." - just "up"