2

I have ~/bin folder that contains multiple subfolders with arbitrary directory levels.

This folder only for executable files that need to be added to path when start zsh, how to recursively add all files under its folder and its subfolders to $path in a short and effective way?

Tuyen Pham
  • 1,805
  • 1
  • 1
    I'm always curious when people ask for short or one-liner solutions. Are you in a situation that is scarce on bytes? Is readability/maintainability a concern at all? – Jeff Schaller Nov 09 '18 at 16:34
  • Thanks @BarathVutukuri, what if we use fd instead of find?. @Jeff: for a neat config file, minimal config that fit all of your needs. three if/else or one line using ternary doesn't make sense of readability at all. Personally I have bunch lines of comment but keep using one-line commands. – Tuyen Pham Nov 09 '18 at 16:40
  • @JeffSchaller In my personal opinion, one liners are good. They make the scripts readable, are most of the times elegant, and easy to share over chat or quick voice calls. Also, they are more maintainable/manageable in large scripts; unless of course they are overly complicated bash porn (e.g: https://askubuntu.com/a/113228 ) like this. This is only my opinion. – BhaveshDiwan Sep 30 '22 at 14:40

2 Answers2

4
mkdir -p ~/tmp/a/b/c/{d,e}
path+=(~/tmp/a ~/tmp/a/**/*(N/))
print -l $path

Which should add both ~/tmp/a to the PATH variable as well as any directories *(/) recursively **. Unlike the linked question this avoids a slow fork out to find(1).

The N qualifier is so as not to consider it an error if there's no (non-hidden) directory in there.

thrig
  • 34,938
  • Very nice one, I also think use find or depends on other utils is overkill for this task, luckily. – Tuyen Pham Nov 09 '18 at 16:54
  • 3
    Or combined into path+=(~/tmp/a{,/**/*(N/)}) – Stéphane Chazelas Nov 09 '18 at 16:57
  • @StéphaneChazelas: How to exclude folder e.g: .bak from adding to path? – Tuyen Pham Jan 26 '19 at 05:44
  • 1
    @TuyenPham, you can change **/* to **/^*.bak (with extendedglob) or to also exclude foo.bak/bar, still with extendedglob: (^*.bak/)#^*.bak. (**/* is actually short for (*/)#* where # is a 0-or-more operator like regex *) – Stéphane Chazelas Jan 26 '19 at 09:28
  • I wanted to only include one level of directories within ~/tmp/bin and not end up having lib or any other sub-directories in my path. Following worked for me path+=("${MYTMP}"/bin{,/*(N/)}) – BhaveshDiwan Sep 30 '22 at 14:33
2

Consider also stow.

stow is a tool that allows you to create a set of packages in sub-directories: It helps keep things separate, while keeping them together. It does this by create links to them in a root directory (e.g. ~/bin), and helping you to manage these links when files are added and removed.

It is often used for /usr/local it will link to the various files in various sub-directories (etc, bin, lib …). This way there is no need to update $PATH.

So if you have

  • package1
    • bin
      • bin1
      • bin2
  • package2
    • bin
      • bin3
      • bin4

then you will end up with

  • bin1 -> package1/bin/bin1
  • bin2 -> package1/bin/bin2
  • bin3 -> package2/bin/bin3
  • bin4 -> package2/bin/bin4
  • package1
    • bin
      • bin1
      • bin2
  • package2
    • bin
      • bin3
      • bin4