Operating System - Linux
1748259 Members
3660 Online
108760 Solutions
New Discussion юеВ

How do "find .... -exec cat /dev/null > {} \; "

 
SOLVED
Go to solution
Victor M. Gomez Hornill
Occasional Advisor

How do "find .... -exec cat /dev/null > {} \; "


Hello everyone,

I am looking for a way of reset files (cat /dev/null > MY_FILE) using the "find ... -exec" construction. This doesn't work:

find /tmp -name "my_file.pp.*" -mtime "+15" -exec cat /dev/null > {} \;

neither:

find /tmp -name "my_file.pp.*" -mtime "+15" -exec cat /dev/null \> {} \;

find /tmp -name "my_file.pp.*" -mtime "+15" -exec " cat /dev/null > {} " \;


I also know this other method to do the job (it works OK):

find /tmp -name "my_file.pp.*" -mtime "+15" -print | while read file_to_reset
do
cat /dev/null > $file_to_reset
done


Someone knows how to write a working "-exec" versi├│n (please try before) similar to the first example ?

Thanks,
Victor
8 REPLIES 8
Robert-Jan Goossens
Honored Contributor
Solution

Re: How do "find .... -exec cat /dev/null > {} \; "

well this works
# find . -type f -exec cp /dev/null {} \;

Edited your command.
# find /tmp -type f -name "my_file.pp.*" -mtime "+15" -exec cp /dev/null {} \;

Regards,
Robert-Jan
Victor M. Gomez Hornill
Occasional Advisor

Re: How do "find .... -exec cat /dev/null > {} \; "

Robert,

OK. You are rigth.

But my question is a particular case of a more general one. Really I am looking for a way of use the shell redirection (">") inside the "-exec" clause of "find".

Any idea?
James R. Ferguson
Acclaimed Contributor

Re: How do "find .... -exec cat /dev/null > {} \; "

Hi Victor:

In cases like this, create a small shell script that is 'exec'ed. For example:

# find /tmp -type f -name "my_file.pp*" -mtime +15 -exec /tmp/wrapper {} \;

...where:

# cat /tmp/wrapper
#!/usr/bin/sh
cat /dev/null > $@
exit 0

NOTE that I deliberately chose the form of '-exec' that spawns ONE process per invocation. This allows the executed script to be simple insofar as only one file argument is expected. If you use '-exec /tmp/wrapper {} +' instead, multiple file names would be passed and the wrapper would need to look like:

# cat /tmp/wrapper
#!/usr/bin/sh
while [ $# -gt 0 ]
do
cat /dev/null > $1
shift
done
exit 0


Regards!

...JRF...
Dennis Handly
Acclaimed Contributor

Re: How do "find .... -exec cat /dev/null > {} \; "

>I am looking for a way of use the shell redirection (">") inside the "-exec" clause of "find".

It looks like you can't do it. I tried using \> and even using sh -c "> {}". Unfortunately -exec doesn't replace the {} and so the shell creates a file "{}".

So you need to use cp as Robert-Jan suggests. Or do as JRF suggests, write an auxiliary script. Or use your while read loop.
Note: you don't need to use cat(1) in your script. "> $1" works just as well.


Matti_Kurkela
Honored Contributor

Re: How do "find .... -exec cat /dev/null > {} \; "

To make shell redirection work inside the "find ... -exec" clause, you must first cause a shell to be started inside the -exec.

Think about how the entire command line is parsed:

1.) The shell expands any unescaped variables and performs any unescaped redirections before the find command is executed. The "{}" construct has no special meaning for the shell, so it's left unchanged. Any escaped characters become unescaped.

2.) The find command begins executing, and when finding a file that matches the conditions, it fork()s a new process, replaces the "{}" with the name of the file and exec()s the command.
NOTE: there is no shell involved in starting the command.

3.) To use the shell redirection for each matching file separately, a shell must start up at this point. Furthermore, it must take its command input from the command line. Looks like "sh -c" might be needed here.

So, here's my first idea:

find /tmp -type f -name "my_file.pp*" -mtime +15 -exec sh -c "> {}" \;

Works on Linux... but not on HP-UX. Hmm.

After a bit of experimentation, it seems that HP-UX "find -exec" does not like having its {} enclosed in double quotes. In addition, the sh of HP-UX (on my "toy" 11.00 at least) apparently does not need nor want the -c option for this.

So, let's try an alternative method of quoting:

find /tmp -type f -name "my_file.pp*" -mtime +15 -exec sh \> {} \;

MK
MK
Dennis Handly
Acclaimed Contributor

Re: How do "find .... -exec cat /dev/null > {} \; "

>MK: sh apparently does not need nor want the -c option for this.

The rotten man page doesn't go into any detail on mixing -c "string" with arg. Probably because it thinks the first arg must be a script.

>let's try an alternative method of quoting:
... -exec sh \> {} \;

Well this works. But adding any other tokens like cat would make it fail.

I'm not sure I'd depend on this.
Matti_Kurkela
Honored Contributor

Re: How do "find .... -exec cat /dev/null > {} \; "

Yes, that's true.

I don't have POSIX standard documents handy, but I did some tests on a Sun Solaris. It seems to behave the same, so I think this might be POSIXLY_CORRECT behaviour. Maybe the "most obvious" implementation of the standard is not the most useful one?

The problem seems to be two-fold:

1.) The "-c" option of sh seems to have some hidden assumptions and the man page of "sh-posix" does not offer any details.
If you do

sh -c 'echo !$0!$1!$2!' foo

the output is

!foo!!!

which seems to indicate the first arg becomes $0, not $1 as one might expect.

2.) The "find ... -exec" requires "{}" as a separate command line parameter. It cannot be embedded in a longer string. With GNU find, this is allowed.

The interaction between these two things makes it hard or impossible to write a reliable one-liner for this kind of job.

MK
MK
James R. Ferguson
Acclaimed Contributor

Re: How do "find .... -exec cat /dev/null > {} \; "

Hi (again) Victor:

There is yet another way to trim your files; use Perl:

# perl -MFile::Find -e 'die unless @ARGV;find(sub{truncate($File::Find::name,0) if -f && -M>15 && m/my_file\.pp\..*/},@ARGV)' /tmp

This performs the same selection that you showed in your opening post with the addition that the action is restricted only to *files* in the directory offered.

For safety, I have let the script fail unless a directory (path) is provided as the command-line script's argument.

You will probably find this to be faster than 'exec'ing any wrapper script as I first suggested since everything is done in the one Perl process.

Regards!

...JRF...