Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Can't get correct directory after using a symbolic link 4

Status
Not open for further replies.

NewtownGuy

Technical User
Jul 27, 2007
146
0
0
US
Hello,

I am using Ubuntu Server 10.04 LTS. I have a problem specifying a directory within a bash script that I run via a symbolic link.

I start the task using a symbolic link to it in /etc/cron.hourly/. The scripts that I want to run are in /var/ and data each one needs are in /var/ My script does "cd ../" to try to get data from its parent directory, but the parent directory is /etc/cron.hourly/, the location of the symbolic link, so the reference fails.

I have tried "cd ." in the task, but that leaves me in /etc/cron.hourly/ rather than the location of the executable. I have many tasks like this and I don't want to have to hard code their folder names into each one.

How do I tell my executable to change the current working directory to its location ?

Thank you in advance.
 
Hi and Happy New Year,

Not sure if this helps but you have to realise that cron has no concept of the running users profile so this is usually resolved by either calling the users profile at the beginning of the script or adding it to the top of crontab (the latter is probably not so relevant for Ubuntu 'I work mainly with Solaris').

Code:
. /export/home/<user>/.profile

Obviously it is better practice to define the "full path" at the top of the scripts and then refer to it (the variable) in your script this prevents any accidental damage when a script looses its way. So define your paths as such:

Code:
HOMEDIR="/var/[URL unfurl="true"]www/name/cgi-bin"[/URL]
DATADIR="/var/[URL unfurl="true"]www/name"[/URL]

cd $HOMEDIR/    
<some-instruction-calling> $DATADIR/<dataitem>


IHTH

Laurie.
 
Thank you, but that doesn't solve the problem.

The crux of the problem is that I have many identical scripts that I want to run. Imagine that I want to service multiple users, each of whom has his own data and his own set of folders ("name/..."). If I have to manually define a HOMEDIR for each one, then every script is different. If I want to update the script, then I have to manually update each one individually.

The cron job knows where each task is because I set up a sym link in the cron folder to each script. I just want the location of that executable to become the HOMEDIR by reference. How do I do that ?

By the way, I'm running as root so there's no problem running my executables.

 
Ok can you paste a (non disclosure) example of one of your scripts and maybe I can understand better and help you with an answer .....

Laurie.
 
The concept is simple. Each script needs to read and write files into the home directory for its user. (Each 'user' is actually another server, not a person). This would be easy to do if each script could cd to its user's home directory.

Here are two symlinks in /etc/cron.whatever:

NAME1 -> /var/
NAME2 -> /var/
In the first case, scriptname needs to cd /var/ since the files it needs are relative to there. Similarly, in the second case, scriptname (same name as the other one) needs to cd /var/
There are many like this, all the same.

How can a task that is executing find out the folder it is in when it has been launched by a symlink ? And, how would the cron restore its current working directory to /etc/cron.whatever/ so it can execute subsequent tasks once a task it started changed the current working directory ?
 
I think I understand you have a number of sym-links in /etc/cron.hourly each that call a script in the web servers cgi-bin for a number of SERVERS (directories /var/ and in the script in cgi-bin there is a call back to the SERVER/data directory ?

I'm not sure if you have one script in cgi-bin that is called for each SERVER of many scripts?

If its one script in cgi-bin then the problem is likely to be that (assuming your using Apache) then ~/cgi-bin is not part of the SERVER's directory structure but is dynamically aliased by Apache.

And is if it is just one script then all you have to do is pass the SERVER path (or just the SERVER name)to the script in cgi-bin so it can cd back there to the SERVER/data path

Therefore I'd have a script in /etc/cron.hourly that went something like:

Code:
# !/bin/bash

BasePath="/var/[URL unfurl="true"]www/"[/URL]

for SERVER in NAME1 NAME2 NAME3 NAME4 NAME5; do 
# or reference list of servers from one file with: #
# for SERVER in `cat /root/server.list; do         #
#                                                  #

$BasePath/$SERVER/cgi-bin/scriptname $SERVER

done

Then in the ~/cgi-bin/script I'd have:

Code:
USERDATA=$1

cd $USERDATA/data
# this should result in the script cd'ing back to SERVER/data 
# or what ever is passed in $1 which from the first run of 
# the script above NAME1/data

Sorry if I have the wrong end of the stick but its difficult to fully understand how, where and why you are trying to cd back to SERVER/data.

Laurie.
 
Use cd -P dirname when you change into the directory initially.

Annihilannic
[small]tgmlify - code syntax highlighting for your tek-tips posts[/small]
 
@Annihilannic

Thanks for that, never come across the -P option and struggling to find a man page relating to that option!

Does it set up the path much like pushd/popd and the cd - in ksh

Would you mind explaining?

(that's why I love these forums, "one helps ones self" while helping others)

Laurie.
 
This is from the bash man page:

man bash said:
cd [-L|-P] [dir]
Change the current directory to dir. The variable HOME is the
default dir. The variable CDPATH defines the search path for
the directory containing dir. Alternative directory names in
CDPATH are separated by a colon :)). A null directory name in
CDPATH is the same as the current directory, i.e., ‘‘.’’. If
dir begins with a slash (/), then CDPATH is not used. [red]The -P
option says to use the physical directory structure instead of
following symbolic links (see also the -P option to the set
builtin command)[/red]; the -L option forces symbolic links to be fol-
lowed. An argument of - is equivalent to $OLDPWD. If a non-
empty directory name from CDPATH is used, or if - is the first
argument, and the directory change is successful, the absolute
pathname of the new working directory is written to the standard
output. The return value is true if the directory was success-
fully changed; false otherwise.

And this is in ksh:

Code:
$ cd --help
Usage: cd [ options ] [directory]
   Or: cd [ options ] old new
OPTIONS
  -L              Handle each pathname component .. in a logical fashion by moving up one level by name in the present working
                  directory.
  -P              The present working directory is first converted to an absolute pathname that does not contain symbolic link
                  components and symbolic name components are expanded in the resulting directory name.
$

Annihilannic
[small]tgmlify - code syntax highlighting for your tek-tips posts[/small]
 
And a demonstration:

Code:
# using this directory structure:

/tmp $ ls -ld a b a/* b/*
drwxr-xr-x 2 user sysadm 4096 Jan  4 12:26 a
lrwxrwxrwx 1 user sysadm    6 Jan  5 09:06 a/d -> ../b/c
drwxr-xr-x 3 user sysadm 4096 Jan  4 12:25 b
drwxr-xr-x 2 user sysadm 4096 Jan  4 12:25 b/c

# cd-ing to the symlink and then back to its parent returns to 
# the symlink's parent

/tmp $ cd a/d
/tmp/a/d $ cd ..

# cd-ing with -P to the symlink takes you to the "real" directory,
# so of course when you return to its parent you end up in the 
# parent of the real directory

/tmp/a $ cd ..
/tmp $ cd -P a/d
/tmp/b/c $ cd ..
/tmp/b $

Annihilannic
[small]tgmlify - code syntax highlighting for your tek-tips posts[/small]
 
Thanks too much ;-) .... it just shows you can be in this business for years jogging along doing your own thing but still learn something new ... I love [X]nix 101 ways to do almost the same thing.

Laurie.
 
Unless I misunderstand Annihilannic's comments, my fundamental question still has not been answered:

...How can a program find out where its executable is ?

For example, in bash:

cd /root
/home/scriptname

...causes scriptname to be executed from /home, but if scriptname tries to reference any files, the files will be with respect to /root not /home.

In comparison, if I have web pages, and I try to run HTML or PHP code, all of their file references are with respect to the location of their code, regardless of how those programs came to be executed.

So bash programs do not operate like HTML or PHP programs with respect to how they find files. I find this difference frustrating.

 
OK its a little difficult still to know how your scripts in ~/cgi-bin are coded so not easy to help you much further.

I can however; show you how Annihilannic was suggesting how the use of "cd -P" can help:

OK I have a script in /export/home/bobby/bendontest/ called bendon.ksh it's just a simple test menu script that offers 4 choices (1=view, 2=edit, 3=print, 4=exit) for a file defined in the script as ../test.ksh

I have a symbolic link in / (root of root-disk) called bendon that links to /export/home/bobby/bendontest/

I just happen to be in /export/home as root and want to run the menu script /export/home/bobby/bendontest/bendon.ksh and edit (or create) the file /export/home/bobby/test.ksh

Now if I just do:

ksh /bendon/bendon.ksh and use option2 then option4 I will indeed end up with a file called test.ksh created in / (root of root-disk) not what I wanted !!

However; if while I'm still in /export/home I do the following:

"cd -P bendon" and then "ksh bendon.ksh" and use option2 and then option4 I will have created /edited a file in /export/home/bobby/ called test.ksh which is indeed ../back from where the script was run ....

The proof is here:

Code:
bash-3.00# id
uid=0(root) gid=0(root)
bash-3.00# find / -name test.ksh
bash-3.00# pwd
/export/home
bash-3.00# ls -ald /bendon
lrwxrwxrwx   1 root     root          30 Jan  5 20:29 /bendon -> /export/home/bobby/bendontest

Above we see we are root, we are in /export/home and there is no file called test.txt found on the server. We also see we have a soft/sym-link as described above.

Code:
bash-3.00# cat /export/home/bobby/bendontest/bendon.ksh
#!/bin/ksh
clear
print "TEST Script MENU"
PS3="Test Menu, enter choice:"
select clean_menu in "View script" "Edit script" "Print script" "Exit"
do
case $clean_menu in
"View script")
pg ../test.ksh;;

"Edit script")
vi ../test.ksh;;

"Print Report")
lp ../test.ksh;;

"Exit") break ;;
esac
done
bash-3.00#

Above you can see the simple menu script that performs the ../ for the location of the file.

Code:
bash-3.00#pwd
/export/home
bash-3.00# cd -P /bendon

Above we prove our present location and then use the cd -P option to do exactly as the man page says...

Code:
bash-3.00# ksh bendon.ksh
TEST Script MENU
1) View script
2) Edit script
3) Print script
4) Exit
Test Menu, enter choice:2




"../test.ksh" [New file]
This is a test to prove cd -P works
~
~

~
"../test.ksh" [New file] 1 line, 36 characters
Test Menu, enter choice:4
bash-3.00#

Above we have run the menu and created/edited a file in ../ in relation to where the scrip is running from, we prove this now with the find ..

Code:
bash-3.00# pwd
/export/home/bobby/bendontest
bash-3.00# find / -name test.ksh
/export/home/bobby/test.ksh
bash-3.00# cat /export/home/bobby/test.ksh
This is a test to prove cd -P works
bash-3.00#


Now I'm not so sure that this helps your existing set-up but it will enhance the suggestion I made above where in your /etc/cron.<whatever> you have a for loop script that runs through your SERVER list and fires off the script in cgi-bin how you wanted it to. You would not need all those sym-links in /etc/cron.<whatever> instead just one script with a for loop.

Obviously it would be a bit better coded than this with checks that its not already running and maybe some logging but you get the idea.

Code:
# !/bin/bash

BasePath="/var/[URL unfurl="true"]www/"[/URL]

for SERVER in NAME1 NAME2 NAME3 NAME4 NAME5; do 
# or reference list of servers from one file with: #
# for SERVER in `cat /root/server.list; do         #
#                                                  #
cd -P $BasePath/$SERVER/cgi-bin/

./<somescript.xxx>

done


Well I'm glad its been a crap TV night I just hope this is useful to you or someone, it has helped me prove the -P and I'v already said my thanks for that ;)

Good night

Laurie.
 
Hi Laurie (tarn),

Wow ! You put a lot of thought into this. Thank you.

However, you seem to have made my point. In bash, it's up to the calling program to set the working directory for the program being executed ** IF ** the program being executed does not know where it is located and it has been referenced by a symbolic link. This is different than the way HTML and PHP programs work, for example, since they can reference directory structures relative to their own location, where-ever it is.

 
Regardless of whether Laurie's response fully answers the OP, the comprehensiveness at least deserves a star - so there you go Laurie.

The internet - allowing those who don't know what they're talking about to have their say.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top