#!/bin/perl
#
# Script: global-replace.pl
# Description: Ask user for key-pairs to replace in multiple files.
#
# Version: 2.0
# Last Update: 06 February 2002
# Written by: Dusan U. Baljevic dusan.baljevic@ieee.org
#
# Copyright 2002-2014 Dusan Baljevic
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
#
# Make the script tight.
#
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/sbin';
$ENV{'SHELL'} = '/bin/sh' if $ENV{'SHELL'} ne '';
$ENV{'IFS'} = '' if $ENV{'IFS'} ne '';
$login = getlogin || getpwuid($<);
use strict;
use POSIX 'uname';
use POSIX qw(locale_h);
use Term::ReadKey;
use File::Basename;
use Getopt::Std;
require Mail::Send;
use File::Copy;
# Abort the execution if not interactive.
#
if ( !I_am_interactive() ) {
exit(1);
}
use vars qw($Groupname $Groupowner $Username $Setpwuid
$answer $DIRN @userinput @Extraarr $Oldstring $Newstring
$fh $Oldfile $Newfile $cnt $LINE $Onerecord @Onerecord $RecipientX
$opt_d $opt_a $opt_f $Findcomm $Arr $def $Commandname @find $msg $header @values
@VALIDARRAY $login $LC_CTYPE $input $Root_check $Oner @Oner
$Usage $Defext $Defdir $Extension $Olds $Fullsearch $News @Onemore);
setlocale( LC_CTYPE, "en_AU.ISO8859-1" );
# Flag to enforce that root cannot run the script.
#
$Root_check = 1;
# Effective UID
#
#if ($Root_check && $> == 0) {
#
# Real UID
#
#if ($Root_check && $< == 0) {
#
#if ($Root_check && ($login = "root")) {
# print "ERROR: You should not be running this as root!\n";
# exit(1);
#}
$Defext = "txt";
$Defdir = "/home/users1";
# Directory with files to replace.
$Commandname = basename($0);
$Usage = "USAGE: $Commandname [-d /dir/path] [-f file-extension].
$Commandname [-d /dir/path] -a
OPTIONS
-d search_directory (if not defined, default value is $Defdir)
-f file_extension (if not defined, default value is $Defext)
-a search all files in given directory\n";
getopts("d:f:a") or die "$Usage";
if ($opt_d) {
if ( grep( /^\//, $opt_d ) && ( -d "$opt_d" ) ) {
$DIRN = "$opt_d";
}
else {
print "ERROR: Bad directory definition \"$opt_d\".\n";
die "$Usage";
}
}
else {
$DIRN = "$Defdir";
}
if ($opt_f) {
if ( $opt_f =~ /^([-\@:\w.]+)$/ ) {
$Extension = "$opt_f";
}
else {
print "ERROR: Bad file extension definition \"$opt_f\".\n";
die "$Usage";
}
}
else {
$Extension = "$Defext";
}
if ($opt_a) {
$Fullsearch = "$opt_a";
$Extension = "No extensions - full directory search!";
}
# Owner of files.
#
$Groupname = "usergrp1";
$Username = "user1";
$Groupowner = getgrnam($Groupname);
$Setpwuid = getpwnam($Username);
# Who will receive warnings via email?
#
$RecipientX = "sysadmin";
sub Send_warn() {
$msg = new Mail::Send;
$msg->to("$RecipientX");
$msg->subject("WARNING: Change permissions on $Oldfile failed!");
$msg->set( $header, @values );
$msg->add( $header, @values );
$msg->delete($header);
$fh = $msg->open;
print $fh "Pathfind Admins/Operations: Check $Oldfile IMMEDIATELY.\n";
print $fh "File $Oldfile should have:\n";
print $fh "\t\tPermissions: 775\n";
print $fh "\t\tOwnership: $Setpwuid ($Username)\n";
print $fh "\t\tGroupOwnership: $Groupowner ($Groupname)\n";
$fh->close;
}
# Default mode for script execution.
# "n" stands for "do not run".
#
$def = "n";
undef $answer;
print "\nNOTE: Welcome to global parameter replacement in $DIRN.\n";
print "NOTE: Do you want to proceed (Y|N) (default is N)?\n";
# Ask user to confirm or aborth the script execution.
# In CBREAK mode, no need to type in key.
# Wait three seconds for user input. If nothing comes,
# assume "N" and quit.
#
&Raw_Term_Mode;
if ( lc("$answer") eq "$def" ) {
print "\nNOTE: Aborting global parameter replacement at your request.\n";
exit(0);
}
print
"\n\nNOTE: Type in space-separated key-pair values, one set per line.\n";
print "NOTE: Valid characters are: a-z A-Z 0-9 _ - : \@ \$ /\n";
print
"NOTE: Type in End-Of-File (Unix: CTRL-D) on a separate line when finished.\n";
print "EXAMPLE: newlove_2 newstring
attmtt aemt
l0ve376 \$BLAH_CURRENT
CTRL-D\n\n";
my @userinput;
chomp( @userinput = );
# Print all choices for user and ask to confirm.
#
local @Onemore = @userinput;
print "\n\nSUMMARY: You have chosen these values:
Directory: $DIRN\n
File Extension: $Extension\n";
while (@Onemore) {
($Oner) = splice( @Onemore, 0, 1 );
( $Olds, $News ) = split( /\s+/, $Oner );
chomp($Olds);
chomp($News);
if ( ( $Olds =~ /^([-\/\@\$\:\w.]+)$/ )
&& ( $News =~ /^([-\/\@\$:\w.]+)$/ ) )
{
print "String \"$Olds\" to be replaced by \"$News\"\n";
push( @VALIDARRAY, $Olds );
push( @VALIDARRAY, $News );
}
else {
print
"ERROR: Strings \"$Olds\" and/or \"$News\" contain invalid character.\n";
}
}
print "NOTE: Do you want to proceed (Y|N) (default is N)?\n";
&Raw_Term_Mode;
if ( lc("$answer") eq "$def" ) {
print "\nNOTE: Aborting global parameter replacement at your request.\n";
exit(0);
}
die "ERROR: Cannot cd to: $!\n" unless chdir $DIRN;
# We are looking for scripts with names $Extension.
#
if ( defined($Fullsearch) ) {
$Findcomm = "find $DIRN -type f -print";
}
else {
$Findcomm = "find $DIRN -type f -name '\*$Extension' -print";
}
open( FIND, "$Findcomm |" ) or die "Cannot find *$Extension files: $!\n";
@find = ;
close FIND;
foreach $Oldfile (@find) {
chomp($Oldfile);
if ( !( -T $Oldfile ) ) {
print "WARN: \"$Oldfile\" is not a text file. Skipping it...\n";
next;
}
else {
print "\nINFO: Processing file $Oldfile...\n";
}
chomp($Oldfile);
$Newfile = "${Oldfile}-NEW";
open( OLD, "<$Oldfile" ) or die "ERROR: Can't open $Oldfile: $!";
open( NEW, ">$Newfile" ) or die "ERROR: Can't open $Newfile: $!";
#
# Enforce file locks:
# Shared lock (1) for reading from an old file.
# Advisory exclusive lock (2) for writing into a new file.
#
flock( OLD, 1 );
flock( NEW, 2 );
while () {
$LINE = $_;
@Extraarr = @VALIDARRAY;
while (@Extraarr) {
@Oner = splice( @Extraarr, 0, 2 );
if ( $#Oner != 1 ) {
print
"ERROR: Record @Oner does not have two words [OLD, NEW]\n";
}
else {
( $Oldstring, $Newstring ) = ( @Oner[0], @Oner[1] );
$Oldstring =~ s/([\$])/\\$1/g;
$LINE =~ s#$Oldstring#$Newstring#g;
}
}
print NEW $LINE;
print "\tNew Line $LINE\n";
}
close OLD;
close NEW;
if ( -T $Newfile && -s $Newfile ) {
if ( move( "$Newfile", "$Oldfile" ) ) {
chmod 00775, $Oldfile;
$cnt = chown $Setpwuid, $Groupowner, $Oldfile;
if ( int($cnt) != 1 ) {
&Send_warn;
}
}
else {
warn "ERROR: Move $Oldfile to $Newfile failed: $!\n";
}
}
}
# POSIX system
#
#sub I_am_interactive {
# local *TTY;
# open(TTY, "/dev/tty") or die "Can't open /dev/tty: $!";
# open(TTY, "/dev/tty");
# my $tpgrp = tcgetpgrp(fileno(TTY));
# my $pgrp = getpgrp();
# close(TTY);
# print "Woila: it is intercative\n";
# return ($tpgrp == $pgrp);
#
# We do not want interactive sessions.
#
# exit(3);
#}
# non-POSIX system
#
sub I_am_interactive {
return -t STDIN && -t STDOUT;
}
sub Raw_Term_Mode {
ReadMode('cbreak');
$answer = ReadKey(3);
ReadMode('normal');
$answer ||= $def;
}
exit(0);