script full name and path $0 not visible when called - bash

I have a script "task.sh" with the following content:
#!/bin/bash
CUR_DIR=`pwd`
SCRIPTPATH="${CUR_DIR}/`dirname $0`"
when I call it with "bash task.sh" it works as expected but when it is called with ". task.sh"
$ . log/task.sh
dirname: invalid option -- b
Try `dirname --help' for more information.
When the script is being scheduled in crontab it is not working as well.
Can someone tell me what am I doing wrong or a different way in order to get the directory of a script that is not the current directory
?

When you invoke it as bash task.sh, bash assigns "task.sh" to $0 (from the bash manual: "If Bash is invoked with a file of commands [...] $0 is set to the name of that file.").
When you source the file, bash does not alter $0, it just executes the script in the current environment. What's in $0 in your current enviroment?
$ echo "$0"
-bash
The leading dash will be interpreted by dirname as an option.
If it's in a cron job, why are you sourcing it?
If you need to source your script, this will work if your shell is bash:
SCRIPTPATH="${CUR_DIR}/${BASH_ARGV[0]}"
However, cron's shell is, I believe, /bin/sh. Even if /bin/sh is a symlink to bash, when bash is invoked as sh it will try to behave POSIXly: the BASH_ARGV array probably won't be available to you.

There is no reason to call external binaries such as pwd and dirname when using bash. The functionality of these two binaries can be replicated with pure shell syntax.
Try the following:
#!/bin/bash
CUR_DIR="$PWD"
SCRIPTPATH="${CUR_DIR}/${0#*/}"

When you type,
bash foo.sh
you are executing script foo.sh, and bash sets the input argument $0 to the name of the script which is being run.
When you type,
. foo.sh
you are sourcing the script and the input argument $0 is not set.
In this situation you can use the automatic variable $_ which contains the argument of the last executed command.
In your script you could type,
SCRIPTPATH=$(dirname "$_")
to get the path of foo.sh.
Notice that, for this to work, this has to be the first command executed in the file.
Otherwise $_ will not contain the path of the sourced script.
Kudos to Dennis Williamson for providing this answer to a similar question.

I have used this for a long time without issues.
SCRIPTPATH=$(cd `dirname -- $0` && pwd)
The -- disable further processing of parameters.

Related

shell program to build a stack in Linux ubuntu [duplicate]

How to set a global environment variable in a bash script?
If I do stuff like
#!/bin/bash
FOO=bar
...or
#!/bin/bash
export FOO=bar
...the vars seem to stay in the local context, whereas I'd like to keep using them after the script has finished executing.
Run your script with .
. myscript.sh
This will run the script in the current shell environment.
export governs which variables will be available to new processes, so if you say
FOO=1
export BAR=2
./runScript.sh
then $BAR will be available in the environment of runScript.sh, but $FOO will not.
When you run a shell script, it's done in a sub-shell so it cannot affect the parent shell's environment. You want to source the script by doing:
. ./setfoo.sh
This executes it in the context of the current shell, not as a sub shell.
From the bash man page:
. filename [arguments]
source filename [arguments]
Read and execute commands from filename in the current shell
environment and return the exit status of the last command executed
from filename.
If filename does not contain a slash, file names in PATH are used to
find the directory containing filename.
The file searched for in PATH need not be executable. When bash is not
in POSIX mode, the current directory is searched if no file is found
in PATH.
If the sourcepath option to the shopt builtin command is turned off,
the PATH is not searched.
If any arguments are supplied, they become the positional parameters
when filename is executed.
Otherwise the positional parameters are unchanged. The return status
is the status of the last command exited within the script (0 if no
commands are executed), and false if filename is not found or cannot
be read.
source myscript.sh is also feasible.
Description for linux command source:
source is a Unix command that evaluates the file following the command,
as a list of commands, executed in the current context
#!/bin/bash
export FOO=bar
or
#!/bin/bash
FOO=bar
export FOO
man export:
The shell shall give the export attribute to the variables corresponding to the specified names, which shall cause them to be in the environment of subsequently executed commands. If the name of a variable is followed by = word, then the value of that variable shall be set to word.
A common design is to have your script output a result, and require the cooperation of the caller. Then you can say, for example,
eval "$(yourscript)"
or perhaps less dangerously
cd "$(yourscript)"
This extends to tools in other languages besides shell script.
In your shell script, write the variables to another file like below and source these files in your ~/.bashrc or ~/.zshrc
echo "export FOO=bar" >> environment.sh
In your ~/.bashrc or ~/.zshrc, source it like below:
source Path-to-file/environment.sh
You can then access it globally.
FOO=bar
export FOO

How can I find the path to a script being sourced in dash?

I am trying to have a sourced shell script determine its own location, and I have found that this is a difficult task for dash.
In bash, sh, and csh, I can use: $_.
In fish, I can use (status -f).
In dash, I have had no luck...
I have tried sourcing the path.sh file shown below with the following results:
# path.sh
called=$_
echo called: $called
echo underscore: $_
echo zero: $0
echo dash_source: $DASH_SOURCE
echo bash_source: $BASH_SOURCE
dash -c ". path.sh"
outputs:
called: /usr/local/bin/dash
underscore: /usr/local/bin/dash
zero: dash
dash_source:
bash_source:
How can I get the path to path.sh in dash?
There does not appear to be any portable (POSIX-standard) way to do this.
POSIX refers to sourcing as "dot scripts." While several other parts of the shell language reference do discuss "dot scripts," none of these instances appear to provide any way to find out the path to the currently-executing dot script. In particular, $0 is reserved for "shell scripts" which are a different thing from "dot scripts." The word "source" does not appear on the page in any capitalization or as part of any larger word (so no $BASH_SOURCE-like thing). None of the standard environment variables which affect the shell seem relevant either. I'm going to say this is not possible within the POSIX spec. Since Dash follows POSIX quite closely, it is unlikely to have a Dashism for this specific case (if it was going to do that, it would've borrowed $BASH_SOURCE or created an analogous variable).
I agree with the accepted answer, though I was able to make it work using lsof:
x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
This works because dash keeps the script open when it is executing. This needs to be the first line of the script, otherwise it may fail if dash later opens additional file descriptors.
This is not possible under POSIX shell standard (which dash implements, nothing more). . is a built-in, not a shell script, therefore, the $0 positional argument refers to the caller of ., and $1 refers to an argument to that caller, if one exists. In particular, none of these things refer to the . itself or its argument script.
If you can change the code that sources, put this function before all source calls.
.() {
command . "$1"
}
. script.sh
# more dot scripts
what is does it to hook the 'dot script' function, now inside the function, positional parameters are the function parameters.
So inside your sourced script, positional parameters are:
. # $0
script.sh # $1
Inside the script being sourced:
# script.sh
echo "$(basename "$1")" # this is the name of the script itself

Is there a good way to preload or include a script prior to executing another script?

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

how to write commands into shell script?

I want to write a shell script to execute commands like "export JAVA_HOME=....."
How could I write a script?
I try:
#!/bin/sh
echo "test"
export PATH=$JAVA_HOME/bin:$PATH
export AWS_AUTO_SCALING_HOME=/usr/local/CLI
export PATH=$PATH:$AWS_AUTO_SCALING_HOME/bin
export AWS_CREDENTIAL_FILE=/usr/local/CLI/credential-file-path.template
But the commands are not executed.
But the commands are not executed.
They are executed, but in a sub-shell. The parent shell does not inherit these values.
Instead of executing your script, source it:
source /path/to/myscript.sh
Or
. /path/to/myscript.sh
Further reading: What is the difference between executing a bash script and sourcing a bash script?
How are you executing your script? If you use:
$ script.sh
the environment is set for the duration of the script, but the parent shell is completely unaffected by this (Unix is not DOS!).
To get the results of the commands into your shell, use:
$ . script.sh
or in Bash you can use:
$ source script.sh
(This is a synonym for the . (dot) command, which has been in shells since the Bourne shell. The source command was in C shell first, then added to Bash.)
These read the script into the current process. Any environment variable settings affect the current process. Your profile is effectively read using . $HOME/.profile, for example.
Note that the file for the dotted command is searched for in the directories on $PATH, but the file only needs to be readable, not executable too.
Have you tried setting permission to execute the file??
chmod +x filename

Global environment variables in a shell script

How to set a global environment variable in a bash script?
If I do stuff like
#!/bin/bash
FOO=bar
...or
#!/bin/bash
export FOO=bar
...the vars seem to stay in the local context, whereas I'd like to keep using them after the script has finished executing.
Run your script with .
. myscript.sh
This will run the script in the current shell environment.
export governs which variables will be available to new processes, so if you say
FOO=1
export BAR=2
./runScript.sh
then $BAR will be available in the environment of runScript.sh, but $FOO will not.
When you run a shell script, it's done in a sub-shell so it cannot affect the parent shell's environment. You want to source the script by doing:
. ./setfoo.sh
This executes it in the context of the current shell, not as a sub shell.
From the bash man page:
. filename [arguments]
source filename [arguments]
Read and execute commands from filename in the current shell
environment and return the exit status of the last command executed
from filename.
If filename does not contain a slash, file names in PATH are used to
find the directory containing filename.
The file searched for in PATH need not be executable. When bash is not
in POSIX mode, the current directory is searched if no file is found
in PATH.
If the sourcepath option to the shopt builtin command is turned off,
the PATH is not searched.
If any arguments are supplied, they become the positional parameters
when filename is executed.
Otherwise the positional parameters are unchanged. The return status
is the status of the last command exited within the script (0 if no
commands are executed), and false if filename is not found or cannot
be read.
source myscript.sh is also feasible.
Description for linux command source:
source is a Unix command that evaluates the file following the command,
as a list of commands, executed in the current context
#!/bin/bash
export FOO=bar
or
#!/bin/bash
FOO=bar
export FOO
man export:
The shell shall give the export attribute to the variables corresponding to the specified names, which shall cause them to be in the environment of subsequently executed commands. If the name of a variable is followed by = word, then the value of that variable shall be set to word.
A common design is to have your script output a result, and require the cooperation of the caller. Then you can say, for example,
eval "$(yourscript)"
or perhaps less dangerously
cd "$(yourscript)"
This extends to tools in other languages besides shell script.
In your shell script, write the variables to another file like below and source these files in your ~/.bashrc or ~/.zshrc
echo "export FOO=bar" >> environment.sh
In your ~/.bashrc or ~/.zshrc, source it like below:
source Path-to-file/environment.sh
You can then access it globally.
FOO=bar
export FOO

Resources