Change directory to path of parent/calling script in bash - linux

I have dozens of scripts, all in different directories. (exported/expanded Talend jobs)
At this moment each job has 1 or 2 scripts, starting with the same lines, most important one:
CD ***path-to-script***
and several lines to set the Java path and start the job.
I want to create a script, which will be ran from all these scripts.
e.g.:
/scripts/talend.sh
And in all talend scripts, the first line will run /scripts/talend.sh, some examples of where these scripts are ran from:
/talend-job1_0.1/talend-job1_0.1/talend-job1/talend-job1.sh
/talend-task2_0.1/talend-task2_0.1/talend-task2/talend-task2.sh
/talend-job3_0.1/talend-job3_0.1/talend-job3/talend-job3.sh
How can I determine where the /scripts/talend.sh is started from, so I can CD to that path from within /scripts/talend.sh.
The Talend scripts are not run from within the directory itself, but from a cronjob, or a different users home directory.
EDIT:
The question was marked as duplicate, but Getting the source directory of a Bash script from within is not answering my question 100%.
Problem is:
- The basic script is being called from different scripts
- Those different scripts can be run from command line, with, and with or without a symbolic link.
- The $0, the $BASH_SOURCE and the pwd all do some things, but no solution mentioned covers all the difficulties.
Example:
/scripts/talend.sh
In this script I want to configure the $PATH and $HOME_PATH of Java, and CD to the place where the Talend job is placed. (It's a package, so that script MUST be run from that location).
Paths to the jobs are, for example:
/u/talend/talendjob1/sub../../talendjob1.sh
/u/talend/talendjob2/sub../../talendjob2.sh
/u/talend/talendjob3/sub../../talendjob3.sh
Multiple jobs are run from a TMS application. This application cannot run these scripts with the whol name (to long, name can only be 6 long), so in a different location I have symbolic links:
/u/tms/links/p00001 -> /u/talend/talendjob1/sub../../talendjob1.sh
/u/tms/links/p00002 -> /u/talend/talendjob1/sub../../talendjob2.sh
/u/tms/links/p00003 -> /u/talend/talendjob1/sub../../talendjob3.sh
/u/tms/links/p00004 -> /u/talend/talendjob1/sub../../talendjob4.sh
I think you get an overview of the complexity and why I want only one basic talend script, where I can leave all basic stuff. But I only can do that, if I know the source of the Talend script, because there I have to be to start that talend job.

These answers (beyond the first) are specific to Linux, but should be very robust there -- working with directory names containing spaces, literal newlines, wildcard characters, etc.
To change to your own source directory (a FAQ covered elsewhere):
cd "$(basename "$BASH_SOURCE")"
To change to your parent process's current directory:
cd "/proc/$PPID/cwd"
If you want to change to the directory passed as the first command-line argument to your parent process:
{ IFS= read -r -d '' _ && IFS= read -r -d '' argv1; } <"/proc/$PPID/cmdline"
cd "$argv1"
That said, personally, I'd just export the job directory to the environment variable in the parent process, and read that environment variable in the children. Much, much simpler, more portable, more accurate, and compliant with best process.

You can store pwd in a variable and then cd to it when you want to go back

This works for me:
In
/scripts/talend.sh
do
cd ${1%/*}
${1%/*} will strip off everything after the last / effectively providing a dirname for $1, which is the path to the script that calls this one.
and than call the script with the line:
/scripts/talend.sh $0.
Calling the script with $0 passes the name of the current script as an argument to the child which as shown above can be used to cd to the correct directory.

When you source /scripts/talend.sh the current directory is unchanged:
The scripts
# cat /scripts/talend.sh
echo "Talend: $(pwd)"
# cat /talend-job1_0.1/talend-job1_0.1/talend-job1/talend-job1.sh
echo Job1
. /scripts/talend.sh
Executing job1
# cd /talend-job1_0.1/talend-job1_0.1
# talend-job1/talend-job1.sh
Job1
Talend: /talend-job1_0.1/talend-job1_0.1
When you want to see the dir where the calling script is in, see get dir of script.
EDIT:
When you want to have the path of the callling script (talend-job1.sh) without having to cd to that dir first, you should get the dir of the script (see link above) and source talend.sh:
# cat /scripts/talend.sh
cd "$( dirname "${BASH_SOURCE[0]}" )"
echo "Talend: $(pwd)"

In talend.sh get the name of the calling script and then the directory:
parent_cmd=$(ps -o args= $PPID)
set -- $parent_cmd
parent_cmd=$(dirname $2)
Update: as pointed by Charles Duffy in the comments below this will cause havoc when used with paths containing white-space or glob patterns.
If procfs is available you could read the content of /proc/$PPID/cmdline or if portability is a concern do a better parsing of the args.

In /scripts/talend.sh:
cd "$(dirname "$0")"
Or:
cd "$(dirname "$BASH_SOURCE")"
Another one is:
cd "$(dirname "$_")"
#This must be the first line of your script after the shebang line
#Otherwise don't use it
Note: The most reliable of the above is $BASH_SOURCE

Related

How to execute from any directory Bash script that sources other Bash scripts (not using path variable)

I am not trying to execute a Bash script from any directory by adding the script to my Path variable.
I want to be able to execute the script from any directory using the directory path to that file ... but the file I want to execute sources other files, that is the problem.
If I am in directory file with two scripts myFunctions.sh and sourceFunctions.sh
sourceFunctions.sh
#!/bin/bash
source ./myFunctions.sh
echoFoo
myFunctions.sh
function echoFoo()
{
echo "foo"
}
I can run myFunctions.sh and foo will print to console, but If I go up a directory and run myFunctions.sh I get error
cd ..
file/sourceFunctions.sh
-bash: doFoo.sh: command not found
Unless I changed source file/myFunctions.sh to source file/myFunctions.sh in sourceFunctions.sh.
So how can I source independent of my working directory so I can run sourceFunctions.sh from any working directory I want?
Thanks
You have the right idea. Doesn't need to be that complicated though:
source `dirname $0`/myFunctions.sh
I often compute "HERE" at the top of my script:
HERE=`dirname $0`
and then use it as needed in my script:
source $HERE/myFunctions.sh
One thing to be careful about is that $HERE will often be a relative path. In fact, it will be whatever path you actually used to run the script, or "." if you provided no path. So if you "cd" within your script, $HERE will no longer be valid. If this is a problem, there's a way (can't think of it off hand) to make sure $HERE is always an absolute path.
I ended up just using a variable of the directory path to the script itself for the source directory
so
#!/bin/bash
source ./myFunctions.sh
echoFoo
becomes
#!/bin/bash
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
source ${SCRIPTPATH}/myFunctions.sh
echoFoo
source

on the "local" toolchain in linux

long time lurker first time poster.
I've looked everywhere and I'm sure it's easily found but I couldn't properly word the search.
I working on some coding exercises from a textbook and I've got all my work in directories with the following hierarchy:
r00t
/ \
tools code
\
chapts
In the tools directory are a couple scripts to make my life easier. What I want, and the reason I'm posting, is to be able to call the scripts in r00t/tools wherever I am, as long as I'm inside r00t. I know I could just add them to the global PATH but that seems lazy and I don't want my PATH to balloon any more (is this even sensible?).
So, can I add scripts or programs to the "local PATH" inside the dir somehow?
Take a look over here towards the ondir (link) program letting you execute scripts upon entering or leaving directories.
With that you could dynamically change your path variable. I must however admit to never have used the program nor to be informed about its development status.
Alternatively you could replace your cd command with a script checking pwd versus your r00t directory and updating $PATH based on the outcome. of course your cd will be slowed down but probably not even noticeably.
an example:
#!/bin/bash
#alternative cd
cd $*
#check for r00t directory
if [ "$( pwd | grep -o 'r00t')" == "r00t" ] ; then
#check path variable:
if [ "$(echo $PATH | grep -o 'r00t')" != "r00t" ] ; then
export PATH=$PATH:/DIR/r00t/bin
fi
else
#remove r00t from PATH when not in r00t
if [ "$(echo $PATH | grep -o 'r00t')" == "r00t" ] ; then
export PATH=$( echo $PATH | sed 's~:/DIR/r00t/bin~~' )
fi
fi
note that now you'll have to intoduce the alias as follows:
alias cd='. ./path/to/script/alternative_cd.sh'
as the exported PATH needs to be sourced in order to work for your current shell (if using bash alternative_cd.sh, you'd only get the new PATH for the subshell the script is run in)
I tested it and it seemed to be working. Have fun.
The usual way to handle this is to put an rc file in the root directory of each of your project directory trees. In this case, r00t. Call the file r00trc or make your own naming convention.
The rc file should include an identifying change to the command line prompt in order to remind you constantly what environment the shell is using, and a re-assignment of the PATH variable to suite the needs of the project, and any other project-specific environment variables, aliases or color setting that you need.
From your default login environment, spawn a sub-shell by running 'bash' either before or after or without changing the current working directory to r00t and then source the r00trc file. This provides you with a Bash shell with the project environment and an identifying prompt. Use exit to exit the sub-shell and return to your default environment.
Avoid the temptation to collect your project rc files in your home directory or anywhere other than the root of the project directories so that they do not get lost when you tar up a project and archive it or send it to a colleague.

Bash script failing to execute bash script

I'm trying to run a script that installs some files in a directory a user specifies. Once the user specifies the directory, I'd like to transfer the main file to that directory so it can perform so more tasks there before ultimately deleting itself once complete.
#prompt for directory in which to build project
read -p "Drag and drop the directory in which you'd like to build this project: "
echo "reply is $REPLY"
cp ./myScript.sh $REPLY
/bin/bash $REPLY/myScript.sh
I've got the script to execute the file from this question. I tried doing it with source $REPLY/myScript.sh as well as simply sh $REPLY/myScript.sh. I get the error /path/to/file/ is a directory
It must be that it doesn't known I'm trying to run myScript.sh, but I don't understand how I've given it a directory.
A likely cause is that drag-and-drop is putting whitespace after the directory name.
Thus:
/bin/bash $REPLY/myScript.sh
would be running
/bin/bash /path/to/directory /myScript.sh
A simple fix, if that's only a standard space, would be:
/bin/bash "${REPLY% }/myScript.sh"
You are missing the variable in read command so obiously it will fail as whatever you are reading is not getting stored. You can replace the read command as follows.
#prompt for directory in which to build project
read -p "Drag and drop the directory in which you'd like to build this project: " REPLY

Assign directory to variable in a source file

I am building a source file with some alias to executable files (these are working just fine) and assigning directories to variables in order to get to the directory quicker, with less typing. For example, if I source example.source:
#!/usr/bin/bash
mydir="/path/to/some/dir"
I can get to /path/to/some/dir with
cd $mydir
However, I am not being able to use tab complete to navigate through other sub-directories like I would do by typing the complete path. I mean, if I use the tab key to complete the variable I get cd $mydir but not cd $mydir/ (I have to delete the last space character and manually type the slash / to see the next sub-directories). Hope this is an understandable question. Is there any workaround for this?
EDIT: the linux distribution I'm using is Slackware Linux 3.2.31.c x86_64 GenuineIntel GNU/Linux
EDIT2: GNU bash, version 4.2.37(2)-release
Apparently this feature is starting to be implemented in bash 4.3, release 26-Feb-2014 09:25.
Reading the NEWS file in bash 4.3 I found this:
i. The word completion code checks whether or not a filename
containing a
shell variable expands to a directory name and appends `/' to the word
as appropriate. The same code expands shell variables in command names
when performing command completion.
Unfortunately I cannot do a de novo installation of bash (because I'm working on a server) but I hope this can help others.
If I understand your question, then I believe it can be solved by putting this at the top of your example.source. This will list your contents every-time that you cd.
#!/usr/bin/bash
# Make cd change directories and then list the contents
function cd() {
builtin cd $*;
ls;
}
mydir="/path/to/some/dir"
cd $mydir
My other suggestion is to try to put cd within your alias. Something like this:
mydir="cd /path/to/some/dir"
$mydir

One command to create and change directory

I'm searching for just one command — nothing with && or | — that creates a directory and then immediately changes your current directory to the newly-created directory. (This is a question someone got for his exams of "linux-usage", he made a new command that did that, but that didn't give him the points.) This is on a debian server if that matters.
I believe you are looking for this:
mkdir project1 && cd "$_"
define a bash function for that purpose in your $HOME/.bashrc e.g.
function mkdcd () {
mkdir "$1" && cd "$1"
}
then type mkdcd foodir in your interactive shell
So stricto sensu, what you want to achieve is impossible without a shell function containing some && (or at least a ; ) ... In other words, the purpose of the exercise was to make you understand why functions (or aliases) are useful in a shell....
PS it should be a function, not a script (if it was a script, the cd would affect only the [sub-] shell running the script, not the interactive parent shell); it is impossible to make a single command or executable (not a shell function) which would change the directory of the invoking interactive parent shell (because each process has its own current directory, and you can only change the current directory of your own process, not of the invoking shell process).
PPS. In Posix shells you should remove the functionkeyword, and have the first line be mkdcd() {
For oh-my-zsh users: take 'directory_name'
Reference: Official oh-my-zsh github wiki
Putting the following into your .bash_profile (or equivalent) will give you a mkcd command that'll do what you need:
# mkdir, cd into it
mkcd () {
mkdir -p "$*"
cd "$*"
}
This article explains it in more detail
I don't think this is possible but to all people wondering what is the easiest way to do that (that I know of) which doesn't require you to create your own script is:
mkdir /myNewDir/
cd !$
This way you don't need to write the name of the new directory twice.
!$ retrieves the last ($) argument of the last command (!).
(There are more useful shortcuts like that, like !!, !* or !startOfACommandInHistory. Search on the net for more information)
Sadly mkdir /myNewDir/ && cd !$ doesn't work: it retrieves the last of argument of the previous command, not the last one of the mkdir command.
Maybe I'm not fully understanding the question, but
>mkdir temp ; cd temp
makes the temp directory and then changes into that directory.
mkdir temp ; cd temp ; mv ../temp ../myname
You can alias like this:
alias mkcd 'mkdir temp ; cd temp ; mv ../temp ../'
You did not say if you want to name the directory yourself.
cd `mktemp -d`
Will create a temp directory and change into it.
Maybe you can use some shell script.
First line in shell script will create the directory and second line will change to created directory.

Resources