I would like to know the code implementation of built-in pwd and /bin/pwd especially when the directory path is a symbolic link.
Example:
hita#hita-laptop:/home$ ls -l
lrwxrwxrwx 1 root root 31 2010-06-13 15:35 my_shell -> /home/hita/shell_new/hita_shell
hita#hita-laptop:/home$ cd my_shell
hita#hita-laptop:/home/my_shell$ pwd <SHELL BUILT-IN PWD>
/home/my_shell
hita#hita-laptop:/home/my_shell$ /bin/pwd
/home/hita/shell_new/hita_shell
The output is different in both the cases. Any clue?
thanks
The kernel maintains a current directory (by inode) and when you need the current directory, it determines its name by walking up the directory tree (using ..) to find the names of all the path components. This is the 'real' or sometimes called 'physical' working directory. There is a library function getcwd(3) which does this for you; on more-recent Linux systems this is actually a system call, which helps with getting a consistent view should the parent directories be in the process of being renamed.
Some shells, notably bash, maintain a environment variable PWD to keep track of where you are, and if you changed directory through a symbolic link, this environment variable will show that. They call this the 'logical' path.
/bin/pwd shows the result of getcwd(3), ie the real path; if you give it -L it will tell you the value of PWD (unless it's rubbish, then you get the real path). (Gnu's version of /bin/pwd does more work than this to deal with complexities of parent directories without read permission and very long path names.)
Bash's built-in pwd shows you the 'logical' path with whatever symlinks you used to get there; even if it's now rubbish (ie deleted or renamed since you used it). The default of the built-in pwd can be changed with set -o physical (on) or set +o physical (off is plus!) The default prompt (containing the current directory) follows the option too.
# make a directory with a symlink alias
cd /tmp
mkdir real
ln -s real sym
cd sym
pwd # will say sym
pwd -L # will say sym
pwd -P # will say real
/bin/pwd # will say real
/bin/pwd -L # will say sym
/bin/pwd -P # will say real
rm /tmp/sym
pwd # says sym, though link no longer exists
/bin/pwd -L # will say real!
rmdir /tmp/real
pwd # says sym, though no directory exists
/bin/pwd # says error, as there isn't one
For what it's worth, my opinion is that all the 'logical' business is just adding to the confusion; the old way was the better way. It's true that symbolic links can be confusing, but this makes it more confusing, because any file operations which open .. don't do the same thing as any directory changes which use .. for example in this rather nasty example:
mkdir -p /tmp/dir/subdir
ln -s /tmp/dir/subdir /tmp/a
cd /tmp/a
ls .. # shows contents of /tmp/dir
(cd .. ; ls) # shows contents of /tmp
To avoid all this, you can put the following in your ~/.bashrc
set -o physical
Hope that helps!
Kind regards,
J.
PS The above is pretty specific to Linux and Gnu bash; other shells and systems are similar but different.
The shell's builtin pwd has the advantage of being able to remember how you accessed the symlinked directory, so it shows you that information. The standalone utility just knows what your actual working directory is, not how you changed to that directory, so it reports the real path.
Personally, I dislike shells that do what you're describing because it shows a reality different than that which standalone tools will see. For example, how a builtin tool parses a relative path will differ from how a standalone tool parses a relative path.
The shell keeps track in its own memory what your currenct directory is by concatenating it with whatever you cd to (and eliminating . and .. entries). It does this so that symbolic links don't mess up cd ... The /bin/pwd implementation walks the directory tree upwards trying to find inodes with the right names.
The built-in pwd shows symbolic links by default, but won't do if you give it the -P option.
In contrast, the pwd command doesn't show symbolic links by default, but will do if given the -L option.
Related
When uploading changed files to a live webserver, how do I enforce at any given moment, my entire file set is either in the old state, or new, but never something in between?
With 'in between' I mean some files old and some new, or some particular file being overwritten halfway, et cetera.
Suppose I have a directory called foo/ on my live webserver containing a framework or bunch of script files including eachother, and I need to replace a whole bunch of files at the same time. I'd call this an 'atomic update'.
The closest I can get is uploading my new foo/ directory (containing the new files) to a different name, e.g. bar/, and then on a shell on the webserver I do:
mv foo foo-old; mv bar foo;
But this way there is still a tiny fraction of a second where foo/ doesn't exist, when the old dir has just been renamed and the new dir is about to.
Is there a 100% correct way of doing this? I guess I need some sort of 'atomic swap', to rename or swap two directory names as a single, atomic action on file system level.
In case it's OS dependent: I'm using a webserver running CentOS and got SSH access.
Near-atomic
The simplest way to achieve a near-atomic change of directories is to use a symbolic link as your web root, which you can re-point to a different location when upgrading.
$ mkdir old
$ mkdir new
$ ln -s old live
$ ls -l
live -> old
new
old
...
$ ln -snf new live
live -> new
new
old
Changing the target of a symbolic link is actually a 2 step operation internally, unlink followed by symlink.
Atomic
An atomic change of directories can be achieved by creating a secondary symbolic link which points to a new directory, and then renaming the new symbolic link into the old symbolic link.
$ mkdir old new
$ ln -s old live
$ ln -s new live_new
$ mv -fT live_new live
mv command will use a single atomic operation (rename) to overwrite the old symbolic link with a new one.
Network layer atomic
Use 2 separate hosts (physical or virtual) and route every new user from a defined moment in time to a new host which contains the upgraded website.
So this doesn't seem like a terribly complicated question I have, but it's one I can't find the answer to. I'm confused about what the -p option does in Unix. I used it for a lab assignment while creating a subdirectory and then another subdirectory within that one. It looked like this:
mkdir -p cmps012m/lab1
This is in a private directory with normal rights (rlidwka). Oh, and would someone mind giving a little explanation of what rlidwka means? I'm not a total noob to Unix, but I'm not really familiar with what this means. Hopefully that's not too vague of a question.
The man pages is the best source of information you can find... and is at your fingertips: man mkdir yields this about -p switch:
-p, --parents
no error if existing, make parent directories as needed
Use case example: Assume I want to create directories hello/goodbye but none exist:
$mkdir hello/goodbye
mkdir:cannot create directory 'hello/goodbye': No such file or directory
$mkdir -p hello/goodbye
$
-p created both, hello and goodbye
This means that the command will create all the directories necessaries to fulfill your request, not returning any error in case that directory exists.
About rlidwka, Google has a very good memory for acronyms :). My search returned this for example: http://www.cs.cmu.edu/~help/afs/afs_acls.html
Directory permissions
l (lookup)
Allows one to list the contents of a directory. It does not allow the reading of files.
i (insert)
Allows one to create new files in a directory or copy new files to a directory.
d (delete)
Allows one to remove files and sub-directories from a directory.
a (administer)
Allows one to change a directory's ACL. The owner of a directory can always change the ACL of a directory that s/he owns, along with the ACLs of any subdirectories in that directory.
File permissions
r (read)
Allows one to read the contents of file in the directory.
w (write)
Allows one to modify the contents of files in a directory and use chmod on them.
k (lock)
Allows programs to lock files in a directory.
Hence rlidwka means: All permissions on.
It's worth mentioning, as #KeithThompson pointed out in the comments, that not all Unix systems support ACL. So probably the rlidwka concept doesn't apply here.
-p|--parent will be used if you are trying to create a directory with top-down approach. That will create the parent directory then child and so on iff none exists.
-p, --parents
no error if existing, make parent directories as needed
About rlidwka it means giving full or administrative access. Found it here https://itservices.stanford.edu/service/afs/intro/permissions/unix.
mkdir [-switch] foldername
-p is a switch, which is optional. It will create a subfolder and a parent folder as well, even if parent folder doesn't exist.
From the man page:
-p, --parents no error if existing, make parent directories as needed
Example:
mkdir -p storage/framework/{sessions,views,cache}
This will create subfolder sessions,views,cache inside framework folder irrespective of whether 'framework' was available earlier or not.
PATH: Answered long ago, however, it maybe more helpful to think of -p as "Path" (easier to remember), as in this causes mkdir to create every part of the path that isn't already there.
mkdir -p /usr/bin/comm/diff/er/fence
if /usr/bin/comm already exists, it acts like:
mkdir /usr/bin/comm/diff
mkdir /usr/bin/comm/diff/er
mkdir /usr/bin/comm/diff/er/fence
As you can see, it saves you a bit of typing, and thinking, since you don't have to figure out what's already there and what isn't.
Note that -p is an argument to the mkdir command specifically, not the whole of Unix. Every command can have whatever arguments it needs.
In this case it means "parents", meaning mkdir will create a directory and any parents that don't already exist.
I'm a newbie of zsh.
Could I type something like cd %wiki to jump to ~/prj/golang/gowiki if it's unique.
But if there are more than two directories posible for cd %unix, just show the matching directories.
Here is my sample dirs history.
$ dirs -v
0 ~/prj/golang
1 ~
2 ~/prj/unixconf
3 ~/prj/unixconf/srv
4 ~/memo
5 ~/prj/golang/gowiki
I do not think you can get that without writing a custom version of cd (i.e. creating a function called cd that would take over from the builtin cd.
You could do something like:
DIRSTACKSIZE=20
setopt auto_pushd # Make cd push the old directory onto the directory stack.
setopt pushd_ignore_dups # Ignore duplicates at the directory stack.
setopt pushd_minus # makes the whole pushd list easier to use from 'cd'
Then if you did
% cd -[TAB]
1 -- /tmp
2 -- /etc
You could just use the number:
cd -2 # jumps to /etc
Also notice that you can use the directory stack from other commands (mv, cp etc) through ~-NUMBER
mv notes.txt ~-[TAB]
1 -- /tmp
2 -- /etc
3 -- /my/very/complicated/dir/path
Refer to the zshall meta-manpage (man zshall) for easiest access to learn about many wonderful zsh tricks. Also notable are the zshcontrib and zshmisc manpages.
Here is an excerpt to help make remembering directories in the dirstack easier.
REMEMBERING RECENT DIRECTORIES
The function cdr allows you to change the working directory to a previous
working directory from a list maintained automatically. It is similar in
concept to the directory stack controlled by the pushd, popd and dirs
builtins, but is more config‐ urable, and as it stores all entries
in files it is maintained across sessions and (by default) between
terminal emulators in the current session. (The pushd directory stack is
not actually modified or used by cdr unless you configure it to do so as
described in the configuration section below.)
Installation
The system works by means of a hook function that is called every time
the directory changes. To install the system, autoload the required
functions and use the add-zsh-hook function described above:
autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
add-zsh-hook chpwd chpwd_recent_dirs
Now every time you change directly interactively, no matter which command
you use, the directory to which you change will be remembered in
most-recent-first order.
Use
All direct user interaction is via the cdr function.
The argument to cdr is a number N corresponding to the Nth most recently
changed-to directory. 1 is the immediately preceding directory; the
current directory is remembered but is not offered as a destination.
Note that if you have multiple windows open 1 may refer to a directory
changed to in another window; you can avoid this by having per-terminal
files for storing directory as described for the recent-dirs-file style
below.
If you set the recent-dirs-default style described below cdr will behave
the same as cd if given a non-numeric argument, or more than one
argument. The recent directory list is updated just the same however you
change directory.
If the argument is omitted, 1 is assumed. This is similar to pushd's
behaviour of swapping the two most recent directories on the stack.
Completion for the argument to cdr is available if compinit has been run;
menu selection is recommended, using:
zstyle ':completion:*:*:cdr:*:*' menu selection
to allow you to cycle through recent directories; the order is
preserved, so the first choice is the most recent directory before the
current one. The verbose style is also recommended to ensure the
directory is shown; this style is on by default so no action is required
unless you have changed it.
For named directories, you will want to use the hash -d name=/path and throw it in your zshrc. You can then cd to those directories with cd ~name
I have a shared library(.so file) on UNIX.
I need to know what all running processes are using it.
Do unix provide any such utility/command?
You can inspect the contents of /proc/<pid>/maps to see which files are mapped into each process. You'll have to inspect every process, but that's easier than it sounds:
$ grep -l /lib/libnss_files-2.11.1.so /proc/*/maps
/proc/15620/maps
/proc/22439/maps
/proc/22682/maps
/proc/32057/maps
This only works on the Linux /proc filesystem, AFAIK.
A quick solution would be to use the lsof command
[root#host]# lsof /lib/libattr.so.1
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
gdm-binar 11442 root mem REG 8,6 30899 295010 /lib/libattr.so.1.1.0
gdm-binar 12195 root mem REG 8,6 30899 295010 /lib/libattr.so.1.1.0
This should work not only for .so files but any other files, dirs, mount points, etc.
N.B. lsof displays all processes that use a file, so there is a very remote possibility of a false positive if is a process that opens the *.so file but not actually use it. If this is an issue for you, then Marcelo's answer would be the way to go.
Do in all directories of interest
ldd * >ldd_output
vi ldd_output
Then look for the the library name, e.g. “aLib.so”. This shows all modules linked to e.g. "aLib.so"
I have shell script which starts with:
sdir=`dirname $0`
sdir=`(cd "$sdir/"; pwd)`
And this usually gets expanded (with 'sh -h') into
++ dirname /opt/foo/bin/bar
+ sdir=/opt/foo/bin
++ cd /opt/foo/bin/
++ pwd
+ sdir=/opt/foo/bin
but for single user for single combination of parameters in expands into (note two lines at the result sbin value)
++ dirname bin/foo
+ sdir=bin
++ cd bin/
++ pwd
+ sdir='/opt/foo/bin
/opt/foo/bin'
I tried different combinations but was not able to reproduce this behavior. With different input parameters for that user it started producing correct single line result. I am new to shell scripting, so please advice when such (cd X; pwd) can return two line.
it was observed on CentOS, but not sure it that matters. Please advice.
The culprit is cd, try this instead
sdir=`dirname $0`
sdir=`(cd "$sdir/" >/dev/null; pwd)`
This happens because when you specify a non absolute path and the directory is found in the environment variable CDPATH, cd prints to stdout the value of the absolute path to the directory it changed to.
Relevant man bash sections:
CDPATH The search path for the cd command. This is a
colon-separated list of directories in which the
shell looks for destination directories specified
by the cd command. A sample value is ``.:~:/usr''.
cd [-L|-P] [directory]
Change the current working directory to directory. If
directory is not given, the value of the HOME shell
variable is used. If the shell variable CDPATH exists,
it is used as a search path. If directory begins with a slash,
CDPATH is not used.
The -P option means to not follow symbolic links; symbolic
links are followed by default or with the -L option. If
directory is ‘-’, it is equivalent to $OLDPWD.
If a non-empty directory name from CDPATH is used, or if ‘-’
RELEVANT -\ is the first argument, and the directory change is successful,
PARAGRAPH -/ the absolute pathname of the new working directory is written
to the standard output.
The return status is zero if the directory is successfully
changed, non-zero otherwise.
OLDPWD The previous working directory as set by the cd
command.
CDPATH is a common gotcha. You can also use "unset CDPATH; export CDPATH" to avoid the problem in your script.
It's possible that user has some funky alias for "cd". Perhaps you could try making it do "/usr/bin/cd" (or whatever "cd" actually runs by default) instead.
Some people alias pwd to "echo $PWD". Also, the pwd command itself can either be a shell built-in or a program in /usr/bin. Do an "alias pwd" and "which pwd" on both that user and any user that works normally.
Try this:
sdir=$( cd $(dirname "$0") > /dev/null && pwd )
It's just a single line and will keep all special characters in the directory name intact. Remember that on Unix, only two characters are illegal in a file/dir name: 0-byte and / (forward slash). Especially, newlines are valid in a file name!