3

I have many different files with the same ending .ft1 that may or may not contain one specific word special. For the files that contain this word, I want to check whether another file with a different ending .log exists in that directory.

What I got so far is this:

find . -name "*.ft1" -exec grep -l "special" {} \; -execdir ls *log \;

However, this gives me an error each time the ls is executed:

ls: cannot access *log: No such file or directory

But I know that there are such files in that directory. I also tried escaping the star, so the expression becomes -execdir ls \*log \;, but the error persists.

I also had a look at this similar question, but I cannot currently see how that helps me with my problem.

How do I get this to behave correctly? Bonus points for a solution that only lists .log files with the same name stem as the found .ft1 files.

6
  • Why not find -name "*special*.ft1 rather than find | grep?
    – DopeGhoti
    Nov 13, 2015 at 20:09
  • special is part of the file contents, not the name.
    – tschoppi
    Nov 13, 2015 at 20:12
  • 1
    Does .log file also contain special string ? Or you're trying to do something like this if (myFile.ft1 exists) ; if (has "special");then check whether myFile.log exists ? Nov 13, 2015 at 20:20
  • Horrible pseudo code, i know . . .but hopefully it makes sense Nov 13, 2015 at 20:20
  • @Serg Heh, what a simple solution to my problem. In this case, the .log also contains the special string. But the general solution by @DopeGhoti works for more use-cases.
    – tschoppi
    Nov 13, 2015 at 20:27

5 Answers 5

4

Your issue is that you need a shell to interpret the *.log glob. So you need -execdir to invoke a shell. The following snippet will also address your "same name stem" requirement

find . -name "*.ft1" -exec grep -l "special" {} \; \
-execdir bash -c 'x=$1; x=${x%.txt}; ls "$x".log' bash {} \;
1
  • You could simply run ... \; -exec sh -c 'ls "${0%.ft1}.log" 2>/dev/null' {} \; Nov 14, 2015 at 1:47
4

Assuming no spaces in your file names (and that your heart isn't set on using find), this should do the trick:

for file in *.ft1; do
   if grep -q 'special' ${file}; then
      ls -l ${file%.ft1}.log 2> /dev/null
   fi
done

It uses bash string manipulation to pull .ft1 off of the end. I could use ${file/ft1/log}, but that would also match any filenames that happen to have ft1 before the extension.

I redirect stderr from ls to hide attempts to ls the .log files that do not exist.

7
  • I use find because there are many occurrences of .ft1 files at different depths in the tree. I guess you could do something like for file in $(find . -name "*.ft1"); do ?
    – tschoppi
    Nov 13, 2015 at 20:18
  • You absolutely could. I just tested that, and it works identically, with the added bonus of recursive directory tree walking.
    – DopeGhoti
    Nov 13, 2015 at 20:19
  • Sweet, that did the trick. I'll mark your answer accepted if no find-only solution comes along in the next couple of days. Thanks!
    – tschoppi
    Nov 13, 2015 at 20:24
  • 1
    What is the logic of not quoting your vars and then saying "Assuming no spaces in your file names" ? Nov 14, 2015 at 2:11
  • 1
    @don_crissti - i bit my tongue, but ive come back to it, head sideways, like three times now.
    – mikeserv
    Nov 14, 2015 at 2:17
3

Using grep -r and awk

grep -rl 'special' *.ft1 | awk '{sub(/\.ft1$/,"");system("sh -c \"[ -f "$0".log ] && echo \\\"["$0".log\\\"] exists for ["$0".ft1]\"")}'

or with the output of ls

grep -rl 'special' *.ft1 | awk '{sub(/\.ft1$/,"");system("sh -c \"[ -f "$0".log ] && ls "$0".log \"")}'

Example

% grep -rl 'special' *.ft1 | awk '{sub(/\.ft1$/,"");system("sh -c \"[ -f "$0".log ] && echo \\\"["$0".log\\\"] exists for ["$0".ft1]\"")}'
[one.log] exists for [one.ft1]

% grep -rl 'special' *.ft1 | awk '{sub(/\.ft1$/,"");system("sh -c \"[ -f "$0".log ] && ls "$0".log \"")}'
one.log

% ls -laog                        
total 100
drwxrwx--x   4  4096 Nov 13 22:03 .
drwxr-xr-x 272 86016 Nov 13 22:03 ..
drwxrwxr-x   2  4096 Nov 13 20:37 bar
drwxrwxr-x   2  4096 Nov 13 20:37 foo
-rw-rw-r--   1     8 Nov 13 21:55 one.ft1
-rw-rw-r--   1     0 Nov 13 21:54 one.log
-rw-rw-r--   1     0 Nov 13 21:54 three.ft1
-rw-rw-r--   1     0 Nov 13 21:54 two.ft1
-rw-rw-r--   1     0 Nov 13 21:54 two.log

% cat one.ft1
special

% cat two.ft1
3

A very crude solution with find , awk, and bash. Basic idea, find all files with ft1 extension, give them as list of command line args to grep, which outputs a list of files with do contain "special" string, then pipe that output to awk that will string out ft1 extension and replace it with "log" extension. Finally give it to bash, which will check if a log file with same extension exists

find . -type f -iname "*.ft1"  -exec grep "special" {} + | awk -F':' '{gsub(/\.ft1/,"");print $1".log"}'  | xargs -I {} bash -c " [ -f {}  ] && echo {} exists"

Small demo

xieerqi@eagle:~/testdir2$ ls
one.ft1  one.log  three.ft1  two.ft1  two.log
xieerqi@eagle:~/testdir2$ find . -type f -iname "*.ft1"  -exec grep "special" {} + | awk -F':' '{gsub(/\.ft1/,"");print $1".log"}'  | xargs -I {} bash -c " [ -f {}  ] && echo {} exists"
./two.log exists
./one.log exists
1

Here's a zsh alternative:

print -rl -- **/*.ft1(.e_'grep -q special $REPLY && REPLY=$REPLY:r.log && [[ -f $REPLY ]]'_)

This will recursively search for .ft1 files containing that string and print only the corresponding .log files. If you want to print all the .ft1 files that contain the string special and the corresponding .log files (if any) then just replace grep's -q with -l.
See here how it works (this one has an additional condition - the grep part, the rest is the same).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .