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.

  • Why not find -name "*special*.ft1 rather than find | grep? – DopeGhoti Nov 13 '15 at 20:09
  • special is part of the file contents, not the name. – tschoppi Nov 13 '15 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 ? – Sergiy Kolodyazhnyy Nov 13 '15 at 20:20
  • Horrible pseudo code, i know . . .but hopefully it makes sense – Sergiy Kolodyazhnyy Nov 13 '15 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 '15 at 20:27
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 {} \;
  • You could simply run ... \; -exec sh -c 'ls "${0%.ft1}.log" 2>/dev/null' {} \; – don_crissti Nov 14 '15 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.

  • 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 '15 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 '15 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 '15 at 20:24
  • 1
    What is the logic of not quoting your vars and then saying "Assuming no spaces in your file names" ? – don_crissti Nov 14 '15 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 '15 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).

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.