0

I want to have a conditional behavior depending on the number of files found:

found=$(find . -type f -name "$1")
numfiles=$(printf "%s\n" "$found" | wc -l)

if [ $numfiles -eq 0 ]; then
    echo "cannot access $1: No such file" > /dev/stderr; exit 2;
elif [ $numfiles -gt 1 ]; then
    echo "cannot access $1: Duplicate file found" > /dev/stderr; exit 2;
else
    echo "File: $(ls $found)"
    head $found
fi

EDITED CODE (to reflect more precisely what I need)

Though, numfiles isn't equal to 2(or more) when there are duplicate files found...

All the filenames are on one line, separated by a space.

On the other hand, this works correctly:

find . -type f -name "$1" | wc -l

but I don't want to do twice the recursive search in the if/then/else construct...

Adding -print0 doesn't help either.

What would?

PS- Simplifications or improvements are always welcome!

user3341592
  • 1,419
  • 1
  • 17
  • 36
  • Is the issue simply that `numfiles` is one less than you expect? Overall, I suspect there is a much cleaner way to do this, but it's hard to say without knowing what `do_this` , `do_that`, and `do_real_work` actually do. Try `printf "%s\n" "$found" | ...` to get the extra newline and correct the off by one error. – William Pursell Mar 02 '16 at 17:40
  • maybe it's because of "$1"? – MaxU - stand with Ukraine Mar 02 '16 at 17:49
  • @MaxU has a point. If you don't have this script in a callable file (or function) that you're calling with a parameter then this won't find anything. – Lucas Bonner Mar 02 '16 at 17:51
  • If you do have the script in a callable file (or function) then there could be a difference what directory it's searching vs what directory you want it to search. You could use `echo "$(pwd)"` at the top of the script to see if it's searching the directory you want it to search. – Lucas Bonner Mar 02 '16 at 18:00
  • Hello all. Yes, that's in a callable script file, where $1 is supposed to be the file we search for. Regarding what `do_this` and `do_that` do, they are in fact error situations. `do this` = `echo "No file found" > /dev/stderr; exit 2;` and `do_that` = `echo "Duplicate file found" > /dev/stderr; exit 2;`. Does this clarifies my question? – user3341592 Mar 02 '16 at 19:20
  • Yes, the issue is that `numfiles` is one less than expect. Though, I don't understand anymore how `wc` works, as I see the files simply separated by a space (even when applying `| cat -A`)... No that clear... Adding a `\n` solves the "-1 problem", but still not clear to me, as I see all file names on one single line. – user3341592 Mar 02 '16 at 19:25
  • @WilliamPursell Please put a cleaner solution, which I can accept... Thx! – user3341592 Mar 02 '16 at 19:26

2 Answers2

0

Using $() to store data to a variable trims tailing whitespace. Since the final newline does not appear in the variable numfiles, wc miscounts by one. You can recover the trailing newline with:

numfiles=$(printf "%s\n" "$found" | wc -l)

This miscounts if found is empty (and if any filenames contain a newline), emphasizing the fact that this entire approach is faulty. If you really want to go this way, you can try:

numfiles=$(test -z "$numfiles" && echo 0 || printf "%s\n" "$found" | wc -l)

or pipe the output of find to a script that counts the output and prints a count along with the first filename:

    find . -type f -name "$1" | tr '\n' ' ' | 
    awk '{c=NF; f=$1 } END {print c, f; exit c!=1}' c=0 |
    while read count name; do 
        case $count in 
        0) echo no files >&2;; 
        1) echo 1 file $name;; 
        *) echo Duplicate files >&2;; 
        esac;
    done

All of these solutions fail miserably if any pathnames contain whitespace. If that matters, you could change the awk to a perl script to make it easier to handle null separators and use -print0, but really I think you should stop worrying about special cases. (find -exec and find | xargs both fail to handle to 0 files matching case cleanly. Arguably this awk solution also doesn't handle it cleanly.)

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • Why don't I see `\n` in the variable when checking with `| cat -A`? – user3341592 Mar 02 '16 at 19:47
  • Do you have a cleaner solution for the above behavior to implement? – user3341592 Mar 02 '16 at 19:47
  • BTW, that does not work: when `$found` is empty, `numfiles` equals 1 with your above code. – user3341592 Mar 02 '16 at 19:57
  • No, no, no, I don't want to go to a faulty approach!!! Please show me the a right one, that implements the expected behavior (`do_real_work`) on the found file, and has 2 exception messages ("no file found" or "duplicate file found") -- trying to avoid running the `find` command twice... – user3341592 Mar 02 '16 at 21:22
  • It depends on what `do_real_work` is. If you can perform the operation on the first file found and generate an error on the second, then you can just run find to a pipe and do that (throw an error if there's no data, perform the operation on the first file, then throw an error on the second). If you can't do that, you might need to change your design so that you can. (I don't know why you need to store `$found`) Trying to count the number of files using find and wc is a bad idea. – William Pursell Mar 02 '16 at 21:44
  • In essence, think of `do_real_work` as being `cat`: I want to cat a file without going to the right directory. However, if there are no such file, I want an error msg that states it explicitly. And, if there are duplicated in the tree, I want to be aware of that, and can't tell which one is the right to display. So, in this case, how would you do it? – user3341592 Mar 02 '16 at 22:33
  • I've updated the code, so that it really maps my need. What would be your proposition to make it run properly? Change whatever needs to be so that it works nicely, and can output 2 different err msg depending on the problem seen (no found file, or duplicate file). Thanks! – user3341592 Mar 03 '16 at 19:25
0

You want to find files and count the files with a name "$1":

grep -c "/${1}$" $(find . 2>/dev/null)

And store the result in a var. In one command:

numfiles=$(grep -c "/${1}$" <(find . 2>/dev/null))
Walter A
  • 19,067
  • 2
  • 23
  • 43