2

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.

Community
  • 1
  • 1
PSVSupporter
  • 248
  • 3
  • 16
  • 1
    Pass the name of the parent script with the call to the child `/scripts/talend.sh $0`.Then in `/scripts/talend.sh` do `cd ${1%/*}` –  May 11 '15 at 14:18
  • Thanks, to accept my answer please press the tick mark in the top left corner –  May 11 '15 at 14:26
  • I can't accept your comment as answer – PSVSupporter May 11 '15 at 14:27
  • Write yourself an answer with what you did and accept it then :) –  May 11 '15 at 14:29
  • It doesn't work for symbolic links... If I create a symlink, for example: /home/user1/links/test -> /talend-job_0.1/talend-job_0.1/talend-job.sh the script thinks /home/user1 is where the script is running from, but I must be /talend-job_0.1/talend-job_0.1/ I do not alwayse create symbolic links to the talend scripts. The basisscript /scripts/talend.sh should determine if it is run from a symbolic path or not. – PSVSupporter May 11 '15 at 14:54
  • This is BashFAQ #28: http://mywiki.wooledge.org/BashFAQ/028 – Charles Duffy May 11 '15 at 15:31
  • I've reopened this, since you've convinced me that there's more to the question, but it's still not clear on a quick read *what* that "more" is. I'd strongly suggest trying to refactor out details specific to the environment and getting more clarity on *exactly* what the task you don't grok how to do is. See http://stackoverflow.com/help/mcve for a good starting point. – Charles Duffy May 11 '15 at 15:54
  • Can you confirm that the title I've edited in accurately reflects your real question? – Charles Duffy May 11 '15 at 16:03

6 Answers6

6

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.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
1

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

Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
  • The scripts are not run from the directories where these scripts are placed. Most scripts are run as a cronjob. And multiple scripts are run from a symbolic link. – PSVSupporter May 11 '15 at 14:10
  • You can see/change the content of the `talend.sh` file ? @PSVSupporter – Thomas Ayoub May 11 '15 at 14:13
  • The talend.sh script file, is the script I want to create. To eliminate editing various script files if something changes to some basic things as: Java home directory and such things. – PSVSupporter May 11 '15 at 14:15
1

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)"
Community
  • 1
  • 1
Walter A
  • 19,067
  • 2
  • 23
  • 43
  • Won't work because each script inherits the working dir of the main invoker, and main script may be called by a cron. – aergistal May 11 '15 at 14:37
  • I thought `CD ***path-to-script***` would stay a part of the *job* scripts. I edited the solution to show how you can move the `cd` into the common script. – Walter A May 11 '15 at 15:14
1

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.

PSVSupporter
  • 248
  • 3
  • 16
1

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.

aergistal
  • 29,947
  • 5
  • 70
  • 92
  • $(ps -o args= $PPID) returns: COMMAND /bin/ksh – PSVSupporter May 11 '15 at 15:00
  • Try also with `cmd=`. There's no option for full command path, you have to check the resulting list. – aergistal May 11 '15 at 15:14
  • 1
    This is going to behave very, very badly with directory names with spaces (or glob symbols). – Charles Duffy May 11 '15 at 15:35
  • 1
    More reliable, if one doesn't need to be portable beyond Linux, would be to read directly from `"/proc/$PPID/cmdline"` -- the NUL-delimited content there is less ambiguous than the formatted-for-human-consumption output of `ps`. – Charles Duffy May 11 '15 at 15:57
  • Example: `{ IFS= read -r -d '' && IFS= read -r -d '' parent_cmd; } <"/proc/$PPID/cmdline"` will set `"$parent_cmd"` appropriately, even in light of names with whitespace, quote characters, glob characters, etc. – Charles Duffy May 11 '15 at 15:58
  • 1
    (Lower-case variable names are good practice by convention, since they don't conflict with the all-uppercase names for environment variables and reserved shell variables; since shell variables and environment variables share a namespace, the conventions from the POSIX spec in http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html are relevant -- see fourth paragraph). – Charles Duffy May 11 '15 at 16:01
  • 1
    BTW, if the OP wants to know the CWD of the caller, as opposed to its `argv[1]`, `cd /proc/$PPID/cwd` would do just as well in that case. Are you going to edit these suggestions in, or should I add my own answer? – Charles Duffy May 11 '15 at 16:04
  • Add your own. I will delete this since its bad practice – aergistal May 11 '15 at 16:06
  • 1
    Added. That said, I wouldn't object to this sticking around with a caveat about correctness, given as its portability is better than that of the procfs approach. – Charles Duffy May 11 '15 at 16:18
  • Unfortunately, I'm not certain that "a better parsing of the args" in `ps` output is an option at all. Try running `bash -c 'sleep 5; echo' "first argument" "second argument" "third argument" & sleep 1; ps -o args= $!` and figure out how you'd determine from that that `first` and the immediately following `argument` are grouped. – Charles Duffy May 11 '15 at 17:04
  • Yes, a general solution needs more investigation unless it's one of those holy-grail cases. In the OP's case I think a better parsing is achievable. – aergistal May 11 '15 at 17:26
1

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

Jahid
  • 21,542
  • 10
  • 90
  • 108
  • 1
    Using `$0` does not reliably give correct results in all cases. See the "Why $0 is NOT an option" section at the end of http://mywiki.wooledge.org/BashFAQ/028 – Charles Duffy May 11 '15 at 15:33
  • @CharlesDuffy, yap, added another one using `$BASH_SOURCE` – Jahid May 11 '15 at 15:38