Subject: multi find with different file outputs
To: None <tech-userlevel@netbsd.org>
From: Jeremy C. Reed <reed@reedmedia.net>
List: tech-userlevel
Date: 09/24/2005 23:15:39
I'd like to patch up find(1) to add option to have it output to different 
file outputs.

I have a patch for my idea #3 below.

Please share your comments (and examples) of how you would do this for the 
user interface.

Three different ideas below:

1)
   find --output 1 /etc -perm -0002 \; --output my.files / -user ${USER}

Man page description under options (not primaries):

  -O _file_
  --output _file_
     Write to this output file. The following options and expressions
     up to the semicolon (or end of command arguments) will be used for
     this output. If _file_ is numeric, then write to the file descriptor
     instead. This option can be used so one find process can do
     multiple tasks. If no -O option is used, then it writes to default
     standard output.

2) Or maybe I should only allow the [file ...] to be set one time, so then 
a example could be like:

  find / -O 3 -perm -0002 \; -user nobody > list.of.nobody.owned.files \
    3>world.writable.files

If using -exec maybe two semicolons would be needed for example:

  find / -name "lyx.core" -exec rm "{}" \; \; -O my.files -user $USER

To do one find over / and remove all lyx.core files and save to a file 
called my.files all files owned by me.

I'll also have to think about -prune and -maxdepth, -mindepth related 
behaviour. Because just because one output doesn't descent into a 
directory, another output may need to.

3) Another idea could be:

   find / \( -name "*.core" -output 5 \) -o \( -user joe -output 6 \) \
     5>core.dumps 6>joes.files

or

  find / \( -name "*.core" -output core.dumps \) \
 	-also \( -user joe -output joes.files \)

I use -also because -o means it would skip next if first was successful 
and -and means that both need to be successful. I am not sure yet.

And this could be similar to -print and could also have a -output0 to 
separate with null characters and also outputx.

I have coded up a test:

Index: extern.h
===================================================================
RCS file: /cvsroot/src/usr.bin/find/extern.h,v
retrieving revision 1.20
diff -b -u -r1.20 extern.h
--- extern.h	7 Aug 2003 11:13:40 -0000	1.20
+++ extern.h	25 Sep 2005 06:13:34 -0000
@@ -73,6 +73,7 @@
  PLAN	*c_newer __P((char ***, int));
  PLAN	*c_nogroup __P((char ***, int));
  PLAN	*c_nouser __P((char ***, int));
+PLAN	*c_output __P((char ***, int));
  PLAN	*c_path __P((char ***, int));
  PLAN	*c_perm __P((char ***, int));
  PLAN	*c_print __P((char ***, int));
Index: find.c
===================================================================
RCS file: /cvsroot/src/usr.bin/find/find.c,v
retrieving revision 1.18.2.1
diff -b -u -r1.18.2.1 find.c
--- find.c	31 Mar 2004 18:07:46 -0000	1.18.2.1
+++ find.c	25 Sep 2005 06:13:34 -0000
@@ -102,7 +102,7 @@
  	 * is assumed so we bracket the current expression with parens, if
  	 * necessary, and add a -print node on the end.
  	 */
-	if (!isoutput) {
+	if (0 == isoutput) {
  		if (plan == NULL) {
  			new = c_print(NULL, 0);
  			tail = plan = new;
Index: find.h
===================================================================
RCS file: /cvsroot/src/usr.bin/find/find.h,v
retrieving revision 1.18
diff -b -u -r1.18 find.h
--- find.h	7 Aug 2003 11:13:41 -0000	1.18
+++ find.h	25 Sep 2005 06:13:34 -0000
@@ -44,7 +44,7 @@
  	N_EXEC, N_EXECDIR, N_EXPR, N_FLAGS, N_FOLLOW, N_FSTYPE, N_GROUP,
  	N_INAME, N_INUM, N_IREGEX, N_LINKS, N_LS, N_MINDEPTH, N_MAXDEPTH,
  	N_MMIN, N_MTIME, N_NAME, N_NEWER, N_NOGROUP, N_NOT, N_NOUSER, N_OK,
-	N_OPENPAREN, N_OR, N_PATH, N_PERM, N_PRINT, N_PRINT0, N_PRINTX,
+	N_OPENPAREN, N_OR, N_OUTPUT, N_PATH, N_PERM, N_PRINT, N_PRINT0, N_PRINTX,
  	N_PRUNE, N_REGEX, N_SIZE, N_TYPE, N_USER, N_XDEV
  };

Index: function.c
===================================================================
RCS file: /cvsroot/src/usr.bin/find/function.c,v
retrieving revision 1.46
diff -b -u -r1.46 function.c
--- function.c	7 Aug 2003 11:13:41 -0000	1.46
+++ function.c	25 Sep 2005 06:13:37 -0000
@@ -61,7 +61,7 @@
  #include <unistd.h>

  #include "find.h"
-#include "stat_flags.h"
+#include "../../bin/ls/stat_flags.h"

  #define	COMPARE(a, b) {							\
  	switch (plan->flags) {						\
@@ -102,6 +102,7 @@
  	int	f_newer __P((PLAN *, FTSENT *));
  	int	f_nogroup __P((PLAN *, FTSENT *));
  	int	f_nouser __P((PLAN *, FTSENT *));
+	int	f_output __P((PLAN *, FTSENT *));
  	int	f_path __P((PLAN *, FTSENT *));
  	int	f_perm __P((PLAN *, FTSENT *));
  	int	f_print __P((PLAN *, FTSENT *));
@@ -1202,6 +1203,40 @@
  	return (palloc(N_NOUSER, f_nouser));
  }

+/* -output functions --
+ *
+ *	Causes the current pathame to be written to
+ *	the defined output file or file handle.
+ */
+int
+f_output(plan, entry)
+	PLAN *plan;
+	FTSENT *entry;
+{
+/* testing with file handle 2 for now */
+	(void)fprintf(&__sF[2], "%s\n", entry->fts_path);
+	return(0); /* return false so it will continue */
+}
+
+PLAN *
+c_output(argvp, isok)
+	char ***argvp;
+	int isok;
+{
+/* need to do */
+	char *filename = **argvp;
+	PLAN *new;
+
+	isoutput = 2; /* do not default to -print */
+
+	(*argvp)++;
+	new = palloc(N_OUTPUT, f_output);
+	new->c_data = filename;
+	return (new);
+/* todo: need to use this */
+}
+
+
  /*
   * -path functions --
   *
Index: option.c
===================================================================
RCS file: /cvsroot/src/usr.bin/find/option.c,v
retrieving revision 1.20
diff -b -u -r1.20 option.c
--- option.c	7 Aug 2003 11:13:43 -0000	1.20
+++ option.c	25 Sep 2005 06:13:37 -0000
@@ -92,6 +92,7 @@
  	{ "-o",		N_OR,		c_or,		0 },
  	{ "-ok",	N_OK,		c_exec,		1 },
  	{ "-or",	N_OR,		c_or,		0 },
+	{ "-output",	N_OUTPUT,	c_output,	1 },
  	{ "-path", 	N_PATH,		c_path,		1 },
  	{ "-perm",	N_PERM,		c_perm,		1 },
  	{ "-print",	N_PRINT,	c_print,	0 },


(Ignore the patch for including stat_flags.h above.)

To use above patch's idea, you must use -print for other expressions. For 
example:

rainier:/usr/src/src/usr.bin/find$ ./find /etc \( -group operator -output 
5 \) -o \( -name "*pass*" -print \) 2>J
/etc/rc.d/yppasswdd
/etc/passwd.conf
/etc/master.passwd
/etc/passwd
rainier:/usr/src/src/usr.bin/find$ cat J
/etc/dumpdates
/etc/skeykeys
find: /etc/cgd: Permission denied

I am only using stderr for testing now. But that works great.

Any thoughts on this?

  Jeremy C. Reed

  	  	 	 technical support & remote administration
 	  	 	 http://www.pugetsoundtechnology.com/