OS X Mountain Lion: how does path_helper work? - path

I installed rbenv through homebrew, and now I don't know why path_helper put ~/.rbenv/shims at the end of the path instead of the beginning. And most importantly, how did path_helper get this information?
According to the man page of path_helper, it reads entries from /etc/paths and from files in /etc/paths.d. But I cannot find the string ".rbenv/shims" there.
~% cat /etc/paths
/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/bin
~% ls -la /etc/paths.d
total 0
drwxr-xr-x 2 root wheel 68 Jun 21 03:16 .
drwxr-xr-x 107 root wheel 3638 Sep 10 09:59 ..
~% /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/Users/gordon/.rbenv/shims"; export PATH;

I suspect that your .bash_profile or .bashrc is adding
.rbenv/shims to your PATH, and that is running at some point before
path_helper is invoked during the shell start-up.
The man page for path_helper opens with:
The path_helper utility reads the contents of the files in the directo-
ries /etc/paths.d and /etc/manpaths.d and appends their contents to the
PATH and MANPATH environment variables respectively.
The crucial point here is that the path_helper utility is intended to
add contents to an existing PATH setting, not replace them. (And in
actuality, what it really does is prepend contents, not append them,
which matters for PATH variables...)
So, if I start out with an entry on my PATH, the setting generated by
path_helper will ensure that entry continues on the PATH it generates.
% echo $SHELL
/bin/bash
% uname
Darwin
% /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin"; export PATH;
% PATH="" /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin"; export PATH;
% PATH=foo /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo"; export PATH;
Note that foo has been included in my PATH in the last line, even though
the contents of /etc/paths and /etc/paths.d/* have not changed.
At the same time, the path_helper utility also seems to be careful not
to produce paths with duplicate entries; it removes duplicate entries
after concatenating /etc/paths and /etc/paths.d/* and the current
PATH.
This latter detail can be especially confusing since it can cause
entry reorderings compared to the original PATH setting (!).
Below are some examples of this behavior: The first case shows a duplicate foo being removed. The second and third case illustrate entry reordering: the generated PATH is the same in both cases, but in the third case, the /usr/bin entry has been moved from in-between foo and bar to the front of the PATH. (This duplicate-entry removal seems to be based on just simple string-matching on the pairs of entries, as illustrated by the fourth case below where the string /usr/bin/ remains between foo/ and bar.)
% PATH=foo:foo /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo"; export PATH;
% PATH=foo:bar /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo:bar"; export PATH;
% PATH=foo:/usr/bin:bar /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo:bar"; export PATH;
% PATH=foo/:/usr/bin/:bar /usr/libexec/path_helper
PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:foo/:/usr/bin/:bar"; export PATH;
Finally, to give credit where credit is due:
While all of the command sequences above are the result of my own investigations, I was originally inspired to look into the behavior of path_helper after reading the note here,
which pointed out that path_helper reuses the PATH environment variable set by the parent process.

The path_helper man page is incorrect. The man page says path_helper is appending /etc/paths.d onto the PATH. But actually, path_helper is APPENDing /etc/paths.d onto the list from /etc/paths and then effectively PREPENDing that result onto actual pre-existing PATH (as well as purging PATH of overridden duplicates from that list).
To be exact, speaking only of PATH for instance, what it does is:
read the list of paths in the file /etc/paths
APPEND onto it the lists of paths in the files in the directory /etc/paths.d
mutate the PATH variable to remove any items in the list
APPEND onto the list the value of the mutated PATH variable
Save this list as the new PATH
To rephrase this in pseudocode, what it's doing is:
NEWPATH = Read(/etc/paths)
NEWPATH = $NEWPATH +append+ Read(/etc/paths.d/*)
PATH = $PATH -minus- $NEWPATH
NEWPATH = $NEWPATH +append+ $PATH
PATH = $NEWPATH
What's treacherous about this is that, if you are manually configuring your PATH, it's probably in order to add components that override system path components. If path_helper gets called when you don't expect it, then it will undo your changes by putting the system-wide path components ahead of your path components.
How would path helper get called unexpectedly? For instance, it gets called by /etc/zshenv, which is called every time you start a zsh shell, whether it's a subshell, or a zsh instance called from another app like emacs, or whatever.
I've written it in more detail as an OpenRadar bug report on path_helper.

Related

How to copy multiple files in directory and move each into their correct directory

Unix shell ksh
I created a file list and am currently trying to copy each file to their correct path.
(mylist)
-1111
-2222
-3333
-4444
-5555
current directory
/sample/dir/unknown/
-1111fileneeded.txt
-2222fileneeded.txt
-3333fileneeded.txt
-4444fileneeded.txt
-5555fileneeded.txt
-6666dontneed.txt
-7777dontneed.txt
-8888dontneed.txt
...etc
The first 4 characters of each file matches with their correct path to where they need to go.
/sample/dir/1111/
/sample/dir/2222/
/sample/dir/3333/
/sample/dir/4444/
So here is what I currently have..
for i in `cat mylist`
do echo "$i"
find /sample/dir/unknown/mylist*
this is where I am kinda stuck and trying to figure out what needs to be done to have each file moved into their correct directory.
This should work
#!/bin/ksh
while IFS=\| read -r line; do
dir=`echo $line | cut -c 2-5`
mv "$line /sample/$dir/$line"
done > filelist.txt
IFS is escape special char, just in case.
cut -c 2-5 is taking all char from 2 to 5 (because there is a dash at the start of your file name)
Let me know if there is something else you don't understand.

How to get the full path of the parent dir of a file in fish?

In fish, say I have a file path from somewhere:
/aaa/bbb/ccc.txt
How can I get the full path of its parent dir? So I will get:
/aaa/bbb
This is the dirname command, and it is a Unix utility, not specific to fish.
For example, cd (dirname /aaa/bbb/ccc.txt)

how to check the size of the folder on terminal using fast method

I know following command gives the size of a particular folder. But it takes longer time when I am using this command on HPC. Is there any other fast command to get the size of folder.
du -sh
It's only slow because it's recursive. The deeper and larger the subfolder structure, the longer it will take to traverse.
You tagged both iOS and high performance computing -- I am assuming iOS was a mistake. Depending on your filesystem you may have other options.
For example, on OS X you can use the index created by spotlight in a command-line fashion:
Usage: mdfind [-live] [-count] [-onlyin directory] [-name fileName | -s smartFolderName | query]
list the files matching the query
query can be an expression or a sequence of words
-attr <attr> Fetches the value of the specified attribute
-count Query only reports matching items count
-onlyin <dir> Search only within given directory
-live Query should stay active
-name <name> Search on file name only
-reprint Reprint results on live update
-s <name> Show contents of smart folder <name>
-0 Use NUL (``\0'') as a path separator, for use with xargs -0.
example: mdfind image
example: mdfind -onlyin ~ image
example: mdfind -name stdlib.h
example: mdfind "kMDItemAuthor == '*MyFavoriteAuthor*'"
example: mdfind -live MyFavoriteAuthor
The details on the MDItem used by mdfind is here.
For the most part though, this is something you could calculate yourself, then check periodically. Store the result in a cache like memcached or ehcache. Depending on which journaled file system you use, you could get notifications when a folder is changed and recalculate that folder only.

Grep --exclude-dir (root directory only)

I'm trying to setup a grep command, that searches my current directory, but excludes a directory, only if it's the root directory.
So for the following directories, I want #1 to be excluded, and #2 to be included
1) vendor/phpunit
2) app/views/vendor
I originally started with the below command
grep -Ir --exclude-dir=vendor keywords *
I tried using ^vendor, ^vendor/, ^vendor/, ^vendor, but nothing seems to work.
Is there a way to do this with grep? I was looking to try to do it with one grep call, but if I have to, I can pipe the results to a second grep.
With pipes:
grep -Ir keywords * | grep -v '^vendor/'
The problem with exclude-dir is, it tests the name of the directory and not the path before going into it, so it is not possible to distinguish between two vendor directories based on their depths.
Here is a better solution, which will actually ignore the specified directory:
function grepex(){
excludedir="$1"
shift;
for i in *; do
if [ "$i" != "$excludedir" ]; then
grep $# "$i"
fi
done
}
You use it as a drop-in replacement to grep, just have the excluded dir as the first argument and leave the * off the end. So, your command would look like:
grepex vendor -Ir keywords
It's not perfect, but as long as you don't have any really weird folders (e.g. with names like -- or something), it will cover most use cases. Feel free to refine it if you want something more elaborate.

How to refer to the source directory in qmake?

I added
version.target = version.h
version.commands = bash generate-version.sh
QMAKE_EXTRA_TARGETS += version
PRE_TARGETDEPS += version.h
to the project, but it attempts to run "generate-version.sh" in destination directory:
make: Leaving directory `.../qqq-build-desktop'
make: Entering directory `.../qqq-build-desktop'
Makefile:236: warning: overriding commands for target `version.h'
Makefile:233: warning: ignoring old commands for target `version.h'
bash generate-version.sh
bash: generate-version.sh: No such file or directory
make: Leaving directory `.../qqq-build-desktop'
There is $$DESTDIR, but I don't see $$SRCDIR. How to refer to the project directory in qmake (or how to rewrite this)?
My first thought is to try to rewrite
version.commands = bash generate-version.sh
so as not to have to invoke a shell script. Perhaps you can combine all of the statements into one line:
version.commands = echo \'char VERSION[]=\"1.0\";\' > version.h && ls && echo Done
If you are stuck with invoking the script, probably PWD or OUT_PWD are what you are looking for. From the qmake Variable Reference
PWD
This variable contains the full path leading to the directory where the qmake project file (project.pro) is located.
OUT_PWD
This variable contains the full path leading to the directory where qmake places the generated Makefile.
The one caveat that is not mentioned in the documentation is that if you are doing a recursive qmake, PWD refers to where the top level .pro file was read from. Thus if you run qmake -r from {proj-root}, when sub/sub/sub/dir-proj.pro is finally read in, PWD will still point to {proj-root}.
Assuming that generate-version.sh is in the same directory as your top level .pro file, you might try:
version.commands = bash $$PWD/generate-version.sh
I found a better and cleaner solution
version.target = version.h
version.commands = bash ${QMAKE_VAR__PRO_FILE_PWD_}/generate-version.sh
QMAKE_EXTRA_TARGETS += version
The variable _PRO_FILE_PWD_ is documented since qt 4.5 and contains the path to the directory containing the project file in use (Contains the .pro file)
But to access this variable for QMAKE_EXTRA_TARGETS, QMAKE_VAR_ must be appended.
PWD
Specifies the full path leading to the directory containing the
current file being parsed. This can be useful to refer to files within
the source tree when writing project files to support shadow builds.
I use (Linux and g++)
DEFINES += SVN_VERSION=\\\"\""`svnversion $$PWD`\""\\\"
DEFINES += COMPILE_DATE=\\\"\""`date`\""\\\"
DEFINES += SW_VERSION=\\\"\"0.5\"\\\"
which defines the macro SVNVERSON to be the svn version.
To access it from C++:
QString svnVersion = SVN_VERSION;
QString swVersion = SW_VERSION;
Explanation: On the shell I want to see this call:
-DSVN_VERSION=\""`svnversion /path/to/my/source`"\"
As you see some escapes are necessary on shell level. In the .pro-file it then has to be escaped twice.
This works and is easy to understand.
version.commands = ( cd $${PWD}; generate-version.sh )

Resources