1

In bash shell I can get a full path of a script even if the script is called by source, link, ./..., etc. These magic bash lines:

 #Next lines just find the path of the file.
 #Works for all scenarios including:
 #when called via multiple soft links.
 #when script called by command "source" aka . (dot) operator.
 #when arg $0 is modified from caller.
 #"./script" "/full/path/to/script" "/some/path/../../another/path/script" "./some/folder/script"
 #SCRIPT_PATH is given in full path, no matter how it is called.
 #Just make sure you locate this at start of the script.
 SCRIPT_PATH="${BASH_SOURCE[0]}";
 if [ -h "${SCRIPT_PATH}" ]; then
   while [ -h "${SCRIPT_PATH}" ]; do SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
 fi
 pushd `dirname ${SCRIPT_PATH}` > /dev/null
 SCRIPT_PATH=`pwd`;
 popd  > /dev/null

How can you get the script path under the same conditions in TCSH shell? What are these 'magic lines'?

P.S. It is not a duplicate of this and similar questions. I'm aware of $0.

Community
  • 1
  • 1
MajesticRa
  • 13,770
  • 12
  • 63
  • 77
  • 3
    [If you can possibly avoid scripting in tcsh, you should.](http://www.grymoire.com/Unix/CshTop10.txt) – johnsyweb Aug 23 '11 at 03:53
  • I absolutely agree with you. But my aim is to make users life as easy as possible. And it is easy for them if they could just run "source environment.csh" from anywhere they whish. TCSH requirment is because RHEL 6 uses it as a default shell for users. – MajesticRa Aug 23 '11 at 04:03
  • If you are using tsch just because it is the default shell, why not change the default shell? – carlpett Aug 23 '11 at 11:35
  • Because it is not MY default shell, it is USERS default shell. Haven't you heard that sometimes something is developed for users?.. – MajesticRa Aug 23 '11 at 13:52
  • If the script is invoked with current directory `/home/user1` using the name `/usr/local/bin/xyz`, but that is a symlink containing `../libexec/someprog/executable`, then the code snippet is going to produce the wrong answer (it will likely say `/home/user1` because `/home/libexec/someprog/executable` does not exist). Wrapping the `while` loop in an `if` is pointless; the code should simply contain the `while` loop. You should look up the `realpath()` function; there may even be a command that uses it already available. It certainly is not hard to write a command that does use `realpath()`. – Jonathan Leffler Aug 23 '11 at 15:27
  • I'm not asking about this script. I ask about TCSH. If you are so much a GURU maybe you'd better give a solution? – MajesticRa Aug 23 '11 at 15:32
  • @MajesticRa: First off, maybe it's not meant as such, but I read your tone in the last two comments as quite aggressive. Please consider that in future comments. Back to the question, what I meant was to change the default shell for the users. To change for existing users, and not only coming ones, you could loop over all users and `usermod` them to use for instance `bash`. – carlpett Aug 23 '11 at 18:17
  • My comments are not aggressive, they are irritated. Irritated by persons who, looks like, have nothing to do except of leaving stupid dump remarks. – MajesticRa Aug 23 '11 at 20:15

2 Answers2

2

IF your csh script is named test.csh, then this will work:

/usr/sbin/lsof +p $$ | \grep -oE /.\*test.csh

engtech
  • 2,791
  • 3
  • 20
  • 24
  • I nearly dismissed this as too risky, because of working too hard at listing all open files, but in practice I find that my regular user doesn't have too many files open, and searching for setup.csh is constrained enough. It's not pretty, but the only solution I've found for those of my stubborn users who refuse to move with the times and switch to bash. – Karl Horton Oct 16 '18 at 21:18
0

I don't use tcsh and do not claim guru status in it, or any other variant of C shell. I also firmly believe that Csh Programming Considered Harmful contains much truth; I use Korn shell or Bash.

However, I can look at manual pages, and I used the man page for tcsh (tcsh 6.17.00 (Astron) 2009-07-10 (x86_64-apple-darwin) on MacOS 10.7.1 Lion).

As far as I can see, there is no analogue to the variable ${BASH_SOURCE[0]} in tcsh, so the starting point for the script fragment in the question is missing. Thus, unless I missed something in the manual, or the manual is incomplete, there is no easy way to achieve the same result in tcsh.

The original script fragment has some problems, too, as noted in comments. If the script is invoked with current directory /home/user1 using the name /usr/local/bin/xyz, but that is a symlink containing ../libexec/someprog/executable, then the code snippet is going to produce the wrong answer (it will likely say /home/user1 because the directory /home/libexec/someprog does not exist).

Also, wrapping the while loop in an if is pointless; the code should simply contain the while loop.

SCRIPT_PATH="${BASH_SOURCE[0]}";
while [ -h "${SCRIPT_PATH}" ]; do SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done

You should look up the realpath() function; there may even be a command that uses it already available. It certainly is not hard to write a command that does use realpath(). However, as far as I can tell, none of the standard Linux commands wrap the realpath() function, which is a pity as it would help you solve the problem. (The stat and readlink commands do not help, specifically.)

At its simplest, you could write a program that uses realpath() like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
    int rc = EXIT_SUCCESS;
    for (int i = 1; i < argc; i++)
    {
        char *rn = realpath(argv[i], 0);
        if (rn != 0)
        {
            printf("%s\n", rn);
            free(rn);
        }
        else
        {
            fprintf(stderr, "%s: failed to resolve the path for %s\n%d: %s\n",
                    argv[0], argv[i], errno, strerror(errno));
            rc = EXIT_FAILURE;
        }
    }
    return(rc);
}

If that program is called realpath, then the Bash script fragment reduces to:

SCRIPT_PATH=$(realpath ${BASH_SOURCE[0]})
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278