I have to find the symbolic link which contains the longest folder name in a folder full of symbolic links. So far I have this:

find <folder> -type l -printf "%l\n"

I was wondering if there's any way to save the folder names while searching, something like this pseudo code:

if [length > max]
  max = length
  var = link

Thanks :)

  • What do you mean by longest folder name? Can you give some example and expect outcome? – Stéphane Chazelas Oct 31 '18 at 17:32
  • 1
    When you change your requirements, you should ask a separate question instead. Otherwise, that invalidates all the already given answers (which may still be useful for people having similar requirements as your original ones). – Stéphane Chazelas Oct 31 '18 at 17:34
find /path/to/base -type l | awk -F/ 'BEGIN {maxlength = 0; longest = "" } length( $NF ) > maxlength { maxlength = length( $NF ); longest = $NF } END { print "longest was", longest, "at", maxlength, "characters." }'

To make the awk more readable:

   maxlength = 0
   longest = ""

length( $NF ) > maxlength { 
   maxlength = length( $NF )
   longest = $NF
END { 
   print "longest was", longest, "at", maxlength, "characters." 

awk is great at dealing with delimited data. Since paths are delimited by /s, we use that as the field separator (with the -F switch), track the longest name we've seen with a longest variable, and its length in the maxlength variable. Some care and feeding to make the output sane if no links are found I shall leave as an exercise for the reader.

  • I'm really sorry, I asked a wrong question. Could you edit your comment to answer the new question? :) – user45124 Oct 31 '18 at 16:43
  • Done. If this answers your question, please click the ✔ to the left of the answer to indicate it has been answered to your satisfaction. – DopeGhoti Oct 31 '18 at 17:19

With zsh:

zmodload zsh/stat
by_link_depth() {
  zstat -A REPLY +link -- ${1-$REPLY}

defines a function that returns the slashes in the target of the symlink, then you can use it as a sorting method for your globs:

ls -ld -- **/*(D@O+by_link_depth[1])

Would list the symlink (@), including hidden ones (D) with the deepest target (O+by_link_depth, reverse-sorts by link depth, and [1] selects the first).

Here in /usr/bin:

$ ls -ld -- **/*(D@O+by_link_depth[1])
lrwxrwxrwx 1 root root 59 Oct  9 03:08 mptopdf -> ../share/texlive/texmf-dist/scripts/context/perl/mptopdf.pl
$ ls -lUd -- **/*(D@O+by_link_depth[1,3])
lrwxrwxrwx 1 root root 59 Oct  9 03:08 mptopdf -> ../share/texlive/texmf-dist/scripts/context/perl/mptopdf.pl*
lrwxrwxrwx 1 root root 61 Oct  9 03:09 pkfix-helper -> ../share/texlive/texmf-dist/scripts/pkfix-helper/pkfix-helper*
lrwxrwxrwx 1 root root 60 Oct  9 03:09 mkjobtexmf -> ../share/texlive/texmf-dist/scripts/mkjobtexmf/mkjobtexmf.pl*

If you only care about the max-depth link target and not the symlink that points to it, you can run zstat +link instead of ls -ld on that symlink, or you could instead define a resolve and by_depth function:

resolve() zstat -A REPLY +link -- ${1-$REPLY}
by_depth() REPLY=${REPLY//[^\/]}


printf '%s\n' **/*(D@O+by_depth+resolve[1])

Where +resolve translates the symlink to its target for the glob expansion, and O+by_depth reverse-sorts by depth.

With bash (though the code below is in no way bash specific) and GNU utilities (your -printf is already GNU-specific), you could get something approaching with:

find . -type l -printf '%l\0%p\0' | gawk -v RS='\0' -v ORS='\0' -F / '
   {n = NF; getline; if (n > max) {max = n; file = $0}}
   END {if (max) print file}' | xargs -r0 ls -ld

Or for the deepest target only:

find . -type l -printf '%l\0' | gawk -v RS='\0' -F / '
   NF > max {max = NF; target = $0}
   END {if (max) print target}'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.