9

Linux has a /proc directory and file‑system, which as far as I can tell, is not part of POSIX. In each /proc/$PID subdirectories, is a symbolic link, cwd, pointing to the actual working directory of the process of this PID (the cwd link is always up to date).

This symbolic link is convenient for some use case, like working with distinct shells and exchanging files between the two shells (formally, their working directories).

Is there a simple way to get something similar, only using POSIX feature?

More on the question

After a comment, more precision: it does not have to necessarily be a link and an environment variable lile $<PID>_CWD, would be as much fine too, although at first sigh, I don't believe such a solution exist. It just has to be easy to refer to (ex. symbolic link or environment variable) and be always up to date each time the other process switch it's working directory.

The solution does not need to necessarily be POSIX, and the most important aspect is portability, but POSIX is surely a guarantee.

Hibou57
  • 905
  • What do you want exactly to acheive? A file with cwd of a process? – enedil Jul 08 '14 at 13:38
  • @enedil, yes, a way to get the current working directory of a process (here, a shell), without computation. However, very little computation would be fine too if there is no other option. As an example, an environment variable which would be always up to date, could be as much fine, it does not have to necessarily be a file‑system link. – Hibou57 Jul 08 '14 at 13:43
  • 1
    Are you wanting POSIX, or portable? Things can be portable without being defined in POSIX. – phemmer Jul 08 '14 at 13:50
  • @Patrick, portable :p. – Hibou57 Jul 08 '14 at 13:51
  • 1
    I don't think POSIX would concern itself with this fine of a detail and narrow of a use case. Solaris and AIX both have a pwdx command, so you may just create an alias or function called pwdx on Linux that wraps around readlink if you're looking for something portable. – Bratchley Jul 08 '14 at 14:07
  • 1
    Actually, on my RHEL5 system there is a pwdx command that comes with procps so that may be your answer. – Bratchley Jul 08 '14 at 14:10
  • @Joel Davis, are you thinking about something like cp foo $(pwdx $PID)/bar?. That's an option I though about indeed. Great you can confirm pwdx is widely known. I will still wait for any future replies. Feel free to add your own answer using pwdx. – Hibou57 Jul 08 '14 at 14:16
  • 1
    @JoelDavis Actually, almost every Linux and Solaris has pwdx. But FreeBSD doesn't. – enedil Jul 08 '14 at 14:18
  • 2
    Fun fact: getcwd(3) used to work by calling stat(".") and readdir(".."), finding a matching inode number, and repeating the process upward until it hit the root directory. Good luck doing that in the context of another process. (I suppose one could use ptrace to inject a call to getcwd...) – zwol Jul 08 '14 at 20:22

3 Answers3

7

I have a solution that uses lsof. It is not installed on BSD by default so if anyone want to use it on BSD, it is required to install it.

Make a shell script:

#!/bin/sh
lsof -p $1 | grep cwd | awk '{print $9}'

Copy it to a directory in your path. It prints the working directory of PID given in the first argument, I.E.

$ script 1987
/home/enedil
enedil
  • 1,674
5

POSIX doesn't offer much in terms of getting information about unrelated processes. There's only ps, really, and it doesn't give any information about the current directory. The C-level APIs aren't any better (in fact most of the information retrieved by ps can only be retrieved by parsing its output¹).

Funnily enough POSIX does offer a portable way to go the other way round: given a file, you can find out which processes have it open by calling fuser. The following snippet lists the PIDs of the processes that have a particular working directory:

fuser -f "$directory_name" 2>&1 | sed -e '$!d' -e 's/.*://' -e 's/  */\
/' | sed -n 's/c$//p'

If you want information about processes in a way that's portable in practice, use lsof. The author of lsof has done the work of implementing all the different ways of retrieving information on different unix variants.

For casual browsing:

lsof -a -p "$pid" -d cwd

For automated parsing:

lsof -a -p "$pid" -d cwd -F n | sed -e '1d' -e '2s/^n//'

Note that lsof replaces newlines by the string \n.

Some Unix variants offer methods that don't require third-party software, but these methods will be perforce specific to each variant. On a very related note, see Portability of file descriptor links

¹ Some old unices has ps setuid root and reading kernel memory, so using that setuid binary was the only way to obtain this information.

  • I am not sure about the "it doesn't give any information about the current directory" : in most unixes, you can use ps in 2 ways: ps -options [= unix options, with a dash], or ps options [=BSD style options, without a dash]. Each comes with their own set of options (-X and X are probably not the same option). Using BSD options, one can: ps auexwww |grep [f]oo |tr ' ' '\n' |grep 'CWD=' to get foo's CWD. It works on a (non-gnu ps) aix, on a (gnu-ps) linux, but it fails in cygwin :( And on solaris you need to invoque /usr/ucb/ps to get the right ps. So probably non-posix? – Olivier Dulac Nov 20 '19 at 14:12
  • 1
    @OlivierDulac ps is perhaps the classic Unix command that's diverged the most between Unix variants. It has tended to converge recently, but POSIX only standardizes a few SysV-style options, and none of them give the current directory. Your command doesn't work, did you mean PWD= rather than CWD=? That doesn't actually give you the CWD, that gives you the value of the PWD environment variable (or a false match due to approximate parsing), which is usually correct only if the program didn't change its current directory after being launched by a shell. – Gilles 'SO- stop being evil' Nov 20 '19 at 19:25
  • you are absolutely right, it is to get the PWD ( |grep 'PWD=') and only shows where the command was started "from" (ie, what the shell's PWD was when the command was launched). This is sometimes usefull (ie on old aix, it helps find the actual script's directory when ps only shows "./script", and BASH_SOURCE[@] isn't available), but indeed it won't give out the actual CWD of the script. Sorry, and thanks for the corrections. lsof seems the most portable bet for the question's needs. (I already had +1 your answer, but I'd double that now if I could ^^) – Olivier Dulac Nov 21 '19 at 10:25
0

I will contribute my own answer.

An option, in the context of interactive shell (the question focuses on this context), could be automatically maintaining a link à la /proc/$PID/cwd, using the automation opportunity Bash provides:

PROMPT_COMMAND="ln -sfT \$(pwd) ~/$LINK_NAME"

The f option is required, as the link will be frequently overridden. The T option is required, as otherwise it seems the f option does work properly.

Shells may be made cooperative, using this in ex., ~/.bashrc:

if [ -v CWD_LINK_NAME ]; then
   PROMPT_COMMAND='ln -sfT "$(pwd)" "'$CWD_LINK_NAME'";'$PROMPT_COMMAND
   declare -r CWD_LINK_NAME
   function rm_cwd_link() {
      rm "$CWD_LINK_NAME"
   }
   trap rm_cwd_link EXIT
fi

A shell or a terminal may be executed with CWD_LINK_NAME set to any relevant value. Ex. CWD_LINK_NAME="~/$SOME_ROLE_NAME" gnome-terminal

Another alternative, is using the same concept to track the pear working directory in a variable, this tracking being based on both @Gilles and @enedil contributions:

PROMPT_COMMAND="PEAR_WD=\$(lsof -p $PID | awk '/cwd/{print \$9}')"

However, this is less accurate and less handy, while it may still be the only way for the rare cases the $PROMPT_COMMAND cannot be set in the other shell.

I'm in favour of the first option (still waiting before selecting an answer, if ever there are other contributions to come).

Hibou57
  • 905