How to recursively add subdirectories to the PATH?

风格不统一 提交于 2019-12-29 14:16:47

问题


How do you do it? My directory code/, at work, is organized in folders and subfolders and subsubfolders, all of which (at least in theory) contain scripts or programs I want to run on a regular basis.


回答1:


At the end of your script, put the line:

PATH=${PATH}:$(find ~/code -type d | tr '\n' ':' | sed 's/:$//')

This will append every directory in your ~/code tree to the current path. I don't like the idea myself, preferring to have only a couple of directories holding my own executables and explicitly listing them, but to each their own.

If you want to exclude all directories which are hidden, you basically need to strip out every line that has the sequence "/." (to ensure that you don't check subdirectories under hidden directories as well):

PATH=${PATH}:$(find ~/code -type d | sed '/\/\\./d' | tr '\n' ':' | sed 's/:$//')

This will stop you from getting directories such as ~/code/level1/.hidden/level3/ (i.e., it stops searching within sub-trees as soon as it detects they're hidden). If you only want to keep the hidden directories out, but still allow non-hidden directories under them, use:

PATH=${PATH}:$(find ~/code -type d -name '[^\.]*' | tr '\n' ':' | sed 's/:$//')

This would allow ~/code/level1/.hidden2/level3/ but disallow ~/code/level1/.hidden2/.hidden3/ since -name only checks the base name of the file, not the full path name.




回答2:


The following Does The Right Thing, including trimming hidden directories and their children and properly handling names with newlines or other whitespace:

export PATH="${PATH}$(find ~/code -name '.*' -prune -o -type d -printf ':%p')"

I use a similar trick for automatically setting CLASSPATHs.




回答3:


If you really need to go down this road, you could try minimizing that PATHs list some more: drop folders that contain no executables. Of course, at the cost of even more stats. ;-/

PATH=$PATH$(find ~/code -name '.*' -prune -o -type f -a -perm /u+x -printf ':%h\n' | sort | uniq | tr -d '\n')

I'd avoid doing this at each shell spawn. Some kind of caching should be used. For instance, add this line to your ~/.bashrc:

[ -s ~/.codepath ] && export PATH=$PATH$(<~/.codepath)

and run

find ~/code -name '.*' -prune -o -type f -a -perm /u+x -printf ':%h\n' |sort |uniq |tr -d '\n' > ~/.codepath

only when you know something really changed.

EDIT: here's a rewrite without your missing -printf

find ~/code -name '.*' -prune -o -type f -a -perm /u+x -print | sed 's@/[^/]\+$@:@' | sort | uniq | tr -d '\n' | sed 's/^/:/; s/:$//'



回答4:


Something like

my_path=$(find $root -type d | tr '\n' ':')

or

my_path=$(find $root -type d -printf '%p:')



回答5:


In bash 4.0 you can just use the newly supported ** operator.

You have to enable it first on some with :

shopt -s globstar

You can then do

echo ** 

which recursively echos all files that are descendant of the current dir.

Beware it does tend to bail out on overly complicated dirs sometimes, so use the ** at the lowest recucurring point.

echo **/  

Coincidentally, emits recursively all directory names, and only directory names. ( Excluding the current dir )

echo ./**/ 

Includes the current dir. ( Incidentally, it also skips hidden directories )

This should thuswise be suited for creating a path string:

echo ./**/ | sed 's/\s\s*/:/g'

And if you don't want relative paths,

echo $PWD/**/ | sed 's/\s\s*/:/g' 

Ack

From your comment on one of the other posts it sounds like you're wanting behaviour much like 'Ack' provides. If you were intending to use a find + grep combination, this tool is generally much more efficient and easier to use for this task.

  • ack

Example:

# search for 'mystring' in all c++ files recursively ( excluding SCM dirs and backup files ) 
ack  "mystring" --type=cpp 

# finds all text files not in an SCM dir ( recursively) and not a backup using type heuristics. 
ack -f --type=text  



回答6:


I've been looking for a solution to this problem too. It would be great if bash had a way to say that for certain paths, you want it to search for the files recursively. For example

PATH="$PATH:/usr/local/bin:~/bin**"

where ~/bin would search that directory and all of its subdirectories without making a mess out of your PATH variable.

Since that's not implemented, my temporary solution is to put everything in my bin directory and then create another directory "bindir" that contains symbolic links to the actual executables in "bin", but their arranged neatly into subdirectories to make them easier to find.

My only question is whether I should hard links instead of symbolic links.




回答7:


I have a single bin directory $HOME/bin and that gets an installed copy of any programs I build (or scripts, or symlinks to programs or scripts). It currently has almost 600 commands in it (ls | wc -l says 604, but there are a dozen or so sub-directories for various reasons).

When I'm testing a program, I execute it where I build it; once I've done testing for the time being, I acquire it with my acquire script, which copies the file and sets the permissions on it.

This leaves me with a nice tidy profile (I don't use .bashrc; I'd rather do the setup once when the login shell starts, and the sub-shells inherit the working environment without having to parse .bashrc again), but a rather ungainly bin directory. It avoids the cost of resetting PATH each time a shell starts, for example, and given how complex my path-setting code is, that is just as well!




回答8:


Something like this:

_path="$(
  find <path> -name '.*' -prune -o -type d -print
  )" 

[[ $_path ]] && _path="${_path//$'\n'/:}" PATH="$PATH:${_path%:}"   

If you have GNU find you may use -printf ':%p' directly.



来源:https://stackoverflow.com/questions/657108/how-to-recursively-add-subdirectories-to-the-path

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!