`(cd X; pwd)` sometimes returns two-line - unix

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!

Related

Recursively copy the *contents* of a directory

Using any of the standard Robot libraries, is it possible to recursively copy the contents of a directory to an existing destination directory?
Basically, I'm looking for the equivalent of the following shell command: cp -r foo/. bar (note the trailing dot)
I tried Copy Directory but this creates a directory foo inside bar (as documented) and it doesn't stop doing that even when supplying the trailing dot. Copy Files chokes when it encounters a directory.
Is there anything I overlooked? Or do I need to just call cp -r myself?
As I only need this to work on Linux, I ended up implementing a custom keyword calling cp -r. If this is ever needed cross-platform, then I'll follow the suggestions to directly implement it in Python.
Copy Directory Contents
[Documentation] Recursively copies the contents of the source directory into the destination.
[Arguments] ${source} ${destination}
Directory Should Exist ${source}
Directory Should Exist ${destination}
${result} = Run Process cp -r ${source}/. ${destination}/
Should Be Equal As Integers ${result.rc} 0

ZSH + Prezto: CD Tab Completion Issue

I've been thinking about switching over to zsh and prezto for some time now, but there are some nuisances that I'd like to solve first.
Biggest of these is the fact that it seems like the tab completion for 'cd' is messed up somehow. For instance, I've a directory called "git" in my home directory for my git projects and I frequently want to move to it from the home directory, that is, I write this:
~ >>> cd g[TAB]
~ >>> cd git
Since "git" is the only folder starting with a "g" I obviously expect that to be the result of the tab completion. However, with ZSH and prezto, it instead completes it like this:
~ >>> cd g[TAB]
~ >>> cd gnats
Using tab again will also try to complete a subfolder to "gnats", neither of which exists!
So far I've at figured out that this is most likely caused by prezto, since disabling it reverts back to the expected behavior.
Any ideas what configuration I have to add to fix this?
The most likely culprit is the shell option CDABLE_VARS. You can check if it is set with setopt | grep cdablevars. As it is not a default setting you can either find where it is set within prezto or explicitly unset it with setopt nocdablevars after prezto is sourced.
Explanation:
If CDABLE_VARS is set, zsh handles arguments to cd that are not directories and do not begin with / as if they begin with ~. This extends to the autocompletion for cd.
If a directory name starts with ~ (as is implied by CDABLE_VARS), zsh checks if the element up to the first / can be subsituted in a few different ways. In your case, ~gnats is interpreted as static named directory, which may defined by the following means:
automatically for home directories: ~someone is the home directory of the user someone.
by setting a shell parameter to a string value which begins with /:
% SOMEWHERE=/some/directory
% print ~SOMEWHERE
/some/directory
by using hash -d: hash -d SOMEWHERE=/some/directory
I would guess that in this case it is the home directory of the user gnats. Which - as far as I can tell - seems to be created by default on at least Debian and Ubuntu.

pwd output after moving current working directory to a new location

I have a general question as to why this occurs, and a misconception about 'pwd'.
You start with directory /test and in it you have /test/folder1.
Folder 1 has: file1.txt
In 2 separate terminals we "cd /test", and do an "ls" and discover folder1 as the output for both of these terminals.
We now "cd folder1" on terminal one. Terminal two remains in /test.
If we then "mv folder1 folder2" on terminal two and run an "ls" we get folder2 as the output. Clearly indicating our mv was successful.
However, within terminal 1 (which was in /test/folder1) if we run a "pwd" the output remains /test/folder1. Ie: it does NOT reflect that we have since moved the folder to /test/folder2.
Why is this the case? I can understand why if we were to edit the file1.txt it is just a pointer within the file system that should be pointing to the same file. Indeed it is as you can modify the file in each terminal and see the edits in the other. However, why is it the case that the 'pwd' command no longer reflects the actual path to that directory?
Thanks!
Assuming you're using bash, pwd is showing you the value of the PWD environment variable, which is updated when you change directory with cd. The folder1 directory changing name does not cause bash to update PWD. However you can find evidence that the directory has changed name:
pwd -P will show the new name of the directory.
ls -l /proc/self/cwd will link to the new name.
I think it is just the case that the first terminal has no reason to re-evaluate where it is. If you do the following command in the first terminal
cd .
you will see your current working directory has indeed changed per the rename (mv).

Correct Wildcard Notation for UNIX systems?

I am currently trying to remove a number of files from my root directory. There are about 110 files with almost the exact same file name.
The file name appears as wp-cron.php?doing_wp_cron=1.93 where 93 is any integer from 1-110.
However when I try to run the code: sudo rm /root/wp-cron.php?doing_wp_cron=1.* it actually tries to find the file with the asterisk * in the filename, leaving me with a file not found error.
What is the correct notation for removing a series of files using wildcard notation?
NOTE: I have already tried delimiting the filepath with both single ' and double quotes ". This did not avail.
Any thoughts on the matter?
Take a look at the permission on the /root directory with ls -ld /root, typically a non-root user will not have r-x permissions, which won't allow them to read the directory listing.
In your command sudo rm /root/wp-cron.php?doing_wp_cron=1.* the filename expansion attempt happens in the shell running under your non-root user. That fails to expand to the individual filenames as you do not have permissions to read /root.
The shell then execs sudo\0rm\0/root/wp-cron.php?doing_wp_cron=1.*\0. (Three separate, explicit arguments).
sudo, after satisfying its conditions, execs rm\0/root/wp-cron.php?doing_wp_cron=1.*\0.
rm runs and attempts to unlink the literal path /root/wp-cron.php?doing_wp_cron=1.*, failing as you've seen.
The solution to removing depends on your sudo permissions. If permitted, you may run a bash sub-process to do the file-name expansion as root:
sudo bash -c "rm /root/a*"
If not permitted, do the sudo rm with explicit filenames.
Brandon,
I agree with #arkascha . That glob should match, so something is amiss here. Do you get the appropriate list of files if you use a different binary, say 'ls' ? Try this:
ls /root/wp-cron.php?doing_wp_cron=1.*
If that returns the full list of files, then you know there's something funny with your environment regarding rm. This could be an alias as suggested.
If you cannot determine what is different or wrong with your environment you could run the list of files through a for loop and remove each one as a work-around:
for file in `ls /root/wp-cron.php?doing_wp_cron=1.*`
do
rm $file
done

shell built in pwd versus /bin/pwd

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.

Resources