#!/bin/env perl # vim:fileencoding=utf-8:ft=perl # a better find use strict; use File::Find; use Getopt::Long; use Term::ProgressBar; use Pod::Usage; # Load options use vars qw($test $progress $caseSensitive $wantHelp); my $directory = "."; Getopt::Long::Configure ("bundling", "no_getopt_compat", "prefix_pattern=--|-"); GetOptions( "test|t!" => \$test, "proress|p!" => \$progress, "directory|d:s" => \$directory, "case-sensitive|s!" => \$caseSensitive, "help|h!" => \$wantHelp, ); pod2usage({ exitval => 1, msg => "gdo Usage:\n gdo [options] + []\n" }) if $wantHelp or $#ARGV < 0; my $search = shift @ARGV; my @ands; while($#ARGV != -1 and $ARGV[0] =~ m/^\+/) { push @ands, substr(shift @ARGV, 1); } if($#ARGV == -1) { push @ARGV, qw(echo %0); } if("@ARGV" !~ m/\$|%/) { push @ARGV, "%0"; } # The match function sub match { return unless -f; my $file = $_; if($caseSensitive) { $search = "(?i)" . $search; } $file =~ m/(?i)$search/ || return; for (@ands) { $file =~ m/(?i)$_/ || return; } # Build a result array with [ $filename, $1, $2, .. ] my @result; push @result, $file; for (1..@-) { push @result, substr($file, $-[$_], $+[$_] - $-[$_]); } # Substitute in substition my @execute = ( @ARGV ); map({ s/(?:\$|%)([0-9]+)/@result[$1]/g; } @execute); if($test) { local $" = '", "'; print 'system("' . "@execute" . '");' . "\n"; } else { if(my $returnValue = system(@execute)) { warn "Command failed for " . $file . " (Code: " . $returnValue . ")\n"; if($returnValue == 2) { # Sigint?! exiter(); } } } } # Exit after 2 Sig int's my $chldReceived = 0; sub exiter { $chldReceived = 0 if $chldReceived < 0; $chldReceived += 2; if($chldReceived == 2) { warn "Received interrupt. Send again to exit.\n"; } if($chldReceived > 2) { print "\n\n"; exit 1; } }; if($progress) { # Show progress bar $| = 1; print "Building filelist"; my @fileList; sub buildProgress { if($caseSensitive) { return unless -f and m/$search/; } else { return unless -f and m/$search/i; } push @fileList, $_; } find({ wanted => \&buildProgress, no_chdir => 1 }, ( $directory )); my $progressBar = Term::ProgressBar->new({count => scalar @fileList, ETA => "linear"}); my $progress = 0; for (@fileList) { $progressBar->update(++$progress); $chldReceived -= 1; open(OLDOUT, ">&STDOUT"); open(STDOUT, ">/dev/null"); match($_); close(STDOUT); open(STDOUT, ">&OLDOUT") } $progressBar->update(scalar @fileList); } else { # Plain old search find({ wanted => \&match, no_chdir => 1 }, ( $directory )); } __END__ =head1 DESCRIPTION gdo - a simple search/replace PCRE matching execution helper ==head1 SYNOPSIS gdo [options] [ ] gdo will iterate over all files in and match the files against . It will then substitute all $0-$n in with parts of the search match and execute . Substitution defaults to "echo %0". If you don't supply any %[0-9] parameter, this script will automatically add %0 in the end. =head1 OPTIONS =over =item -t Test. Don't execute the substituted command but output it only. =item -p Show a progress bar and hide output of the executed command. =item -d The directory to search in. Defaults to ./. =item -s Search case sensitive =back =head1 AUTHOR Phillip Berndt =cut