tags:

views:

74

answers:

2

I have a directory containing a bunch of files, some text some binary, with no consistent naming. I want to search and replace a string in text files only. So I went with:

perl -i -pne 's#/some/text/to/replace#/replacement/text#' *

Remove the -i option and you will see that binary files get caught. How do I modify this one-liner to skip binary files?

+2  A: 
ack -n --text --sort -f . | xargs perl -i -pne 's…'

Abusing ack goes much quicker than writing your own solution with -T.

daxim
Do we know the OP is not Windows based?
drewk
Windows people can have an [`xargs`](http://gnuwin32.sf.net/packages/findutils.htm), too.
daxim
@drewk the OP probably isn't windows based because `cmd` doesn't like single quoted strings. It is possible that the OP is on Windows but using Cygwin, but in that case, he or she will have xargs.
Chas. Owens
+2  A: 

Well, this is all based on what your definition of a text file is. Perl 5 has the -T filetest operator that will tell you if a filename or filehandle is a text file (using Perl 5's definition):

perl -i -pne 'BEGIN{@ARGV=grep-T,@ARGV}s#regex#replacement#' *

The BEGIN block will filter out any files that don't pass the -T test, so they won't even be read (except for their first block because that is what -T uses to determine if they are text).

From perldoc -f -X

The -T and -B switches work as follows. The first block or so of the file is examined for odd characters such as strange control codes or characters with the high bit set. If too many strange characters (>30%) are found, it's a -B file; otherwise it's a -T file. Also, any file containing a zero byte in the first block is considered a binary file. If -T or -B is used on a filehandle, the current IO buffer is examined rather than the first block. Both -T and -B return true on an empty file, or a file at EOF when testing a filehandle. Because you have to read a file to do the -T test, on most occasions you want to use a -f against the file first, as in next unless -f $file && -T $file .

Chas. Owens