I want to expand the recursive search of symlinks to include only .tex files find . -type l -name "Math*" -exec grep -d "recurse" word {} +. Failed pseudocode 1

find . -type l -name "Math*" \
  -exec WHERE CHOOSE ONLY .tex files and Directories \
  -exec grep -d "recurse" word {} +

I cannot type a command for choosing .tex files and directories. In words, pseudocode 2

  1. Find all symlinks with name "Math" which point to directories.
  2. Recurse all symlinked directories (I think grep is limiting here possibly)
  3. Do basic grep word in file list

How can you do the step (2)?

  • Excuse my ignorance, but what's "sequ"? – Kusalananda Jun 30 '16 at 15:22
  • @Kusalananda It is just a word to be searched. Thank you for pointing it out! I made it clearer. – Léo Léopold Hertz 준영 Jun 30 '16 at 15:22
  • So you have a bunch of symlinks called "Math*", and they point to LaTeX/TeX documents that you'd like to search for the string "sequ"? Or they point to directories with documents? – Kusalananda Jun 30 '16 at 15:24
  • 1
    @Kusalananda Symlinks point to directories which have other directories. End things are .tex files. – Léo Léopold Hertz 준영 Jun 30 '16 at 15:26
  • 1
    Don't have time for a proper answer right now, but something along the lines of basename $(readlink '{}') should be your friend. – user Jun 30 '16 at 15:30

I think this must be one of the silliest command piplines I ever have concocted:

$ find . -type l -name "Math*" -print0 |
  xargs -0 -n 1 -IXXX find XXX/ -type f -name "*.tex" -print0 |
  xargs -0 fgrep "word"
  1. Find all symbolic links called Math*.
  2. Do find again on each found path, looking for *.tex files. The xargs need to use -n 1 to call find with no more than one pathname. The pathname will be put into the XXX placeholder.
  3. Call fgrep (i.e. grep -F since we have a fixed search string) with the string on found files.
  • Can you compare it to Michael's proposal about basename $(readlink) '{}')? Do you understand his point? – Léo Léopold Hertz 준영 Jun 30 '16 at 15:35
  • @Masi I'm wondering if that helps or not, actually. Well, possibly if you want to try to do it in a shorter pipeline, but my brain just started leaking after calling find from xargs so I can't think properly right now... – Kusalananda Jun 30 '16 at 15:40
  • 1
    @Masi, yes, -n 1, because I need to give exactly one path for to each of the find. I also need the / after XXX to be sure the Math symlink is followed. You can probably use a flag or other for that instead, but it was a cheap solution. – Kusalananda Jun 30 '16 at 15:42
  • 2
    Some formal people may certainly tells you that fgrep is deprecated. I will not. – Emmanuel Jun 30 '16 at 15:59
  • 2
    @Masi - If you change the first -print0 for -exec readilnk -f {} \; it will then be similar to what Michael Kjörling suggests. But I do not believe it is needed since there is no real need to explicitly resolve the symlinks. – grochmal Jun 30 '16 at 17:21

One way to do it:

find . -type l -name 'Math*' -print0 | \
xargs -0 sh -c \
    'find -L "$@" -type f -name "*.tex" -exec fgrep word /dev/null {} +' sh

The sh -c '...' sh abomination is necessary to deal with the case when Math* can have spaces. Otherwise, when Math* doesn't expand to filenames with spaces, something like this would work:

find -L $(find . -type l -name 'Math*') -name '*.tex' \
    -exec fgrep word /dev/null {} +

The /dev/null makes sure fgrep prints a filename even when it only has a single file to search.

If you insist on the resolving links before grepping, it can be done too, at the price of making the assumptions that (1) your filenames don't contain newlines, and (2) you're using xargs from GNU findutils (BSD xargs doesn't accept -d):

find . -type l -name 'Math*' -exec readlink -f {} + | \
xargs -d '\n' sh -c \
    'find "$@" -type f -name "*.tex" -exec fgrep word /dev/null {} +' sh
  • Your second command. Your find . -type l -name 'Math* gives relative paths etc ./Mathematics. Your find -L $(find . -type l -name 'Math*') -name '*.tex' gives find: ‘./Mathematics’: No such file or directory. I think it is because the first command is giving a relative path. – Léo Léopold Hertz 준영 Jul 1 '16 at 4:07
  • 1
    @Masi Did you read the paragraph before that? It explains why the (simpler) second command doesn't work, and why you actually need the first one, or the last. You just re-discovered that out on your own. – Satō Katsura Jul 1 '16 at 4:18
  • I am considering how to trim long one-liners of the content here unix.stackexchange.com/q/293207/16920 – Léo Léopold Hertz 준영 Jul 1 '16 at 4:37
  • 1
    @Masi Pipe the result to something like cut -c -80. – Satō Katsura Jul 1 '16 at 4:46
  • Then you have a broken cut, that doesn't deal well with locales. shrug – Satō Katsura Jul 1 '16 at 4:58

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.