I have a list of about 500 folders. Inside each of those folders is a functions.php file.
I need to search every functions.php file for the following text:
function wp_initialize_the_theme_finish()
I need to replace any line that has the above text with this:
function wp_initialize_the_theme_finish() { $uri = strtolower($_SERVER["REQUEST_URI"]); if(is_admin() || substr_count($uri, "wp-admin") > 0 || substr_count($uri, "wp-login") > 0 ) { /* */ } else { $l = 'mydomain.com'; $f = dirname(__file__) . "/footer.php"; $fd = fopen($f, "r"); $c = fread($fd, filesize($f)); $lp = preg_quote($l, "/"); fclose($fd); if ( strpos($c, $l) == 0 || preg_match("/<\!--(.*" . $lp . ".*)-->/si", $c) || preg_match("/<\?php([^\?]+[^>]+" . $lp . ".*)\?>/si", $c) ) { wp_initialize_the_theme_message(); die; } } } wp_initialize_the_theme_finish();
NOTE: I need to replace the entire line with my new line, not just the beginning.
Any help would be greatly appreciated.
There's a pretty detailed article written on it. Seems to relate to your question very well. Essentially the command is:
find . -name "*/function.php" -print | xargs sed -i 's/foo/bar/g'
Where foo is :
function wp_initialize_the_theme_finish().+\n
and bar is:
function wp_initialize_the_theme_finish() { $uri = strtolower($_SERVER["REQUEST_URI"]); if(is_admin() || substr_count($uri, "wp-admin") > 0 || substr_count($uri, "wp-login") > 0 ) { /* */ } else { $l = 'mydomain.com'; $f = dirname(__file__) . "/footer.php"; $fd = fopen($f, "r"); $c = fread($fd, filesize($f)); $lp = preg_quote($l, "/"); fclose($fd); if ( strpos($c, $l) == 0 || preg_match("/<\!--(.*" . $lp . ".*)-->/si", $c) || preg_match("/<\?php([^\?]+[^>]+" . $lp . ".*)\?>/si", $c) ) { wp_initialize_the_theme_message(); die; } } } wp_initialize_the_theme_finish();
Use the following rules to escape special characters in foo and bar:
In a nutshell, for sed:
Write the regex between single quotes.
Use '\'' to search for asingle quote.
Put a backslash before $.*/[]^ and only those characters.
Use find command, to search for all the relevant files, then use sed -i to update the files
Since search and replace strings are reasonably long, first store them in variables.
Then try using find along with sed using -exec option
#!/bin/bash
search='^.*function wp_initialize_the_theme_finish().*$'
replace='function wp_initialize_the_theme_finish() { $uri = strtolower($_SERVER["REQUEST_URI"]); if(is_admin() || substr_count($uri, "wp-admin") > 0 || substr_count($uri, "wp-login") > 0 ) { /* */ } else { $l = "mydomain.com"; $f = dirname(__file__) . "/footer.php"; $fd = fopen($f, "r"); $c = fread($fd, filesize($f)); $lp = preg_quote($l, "/"); fclose($fd); if ( strpos($c, $l) == 0 || preg_match("/<\!--(.*" . $lp . ".*)-->/si", $c) || preg_match("/<\?php([^\?]+[^>]+" . $lp . ".*)\?>/si", $c) ) { wp_initialize_the_theme_message(); die; } } } wp_initialize_the_theme_finish();'
find -type f -name 'function.php' -exec sed -i "s/${search}/${replace}/g" {} \;
Other alternative using xargs
find -type f -name 'function.php' -print0 | xargs -0 sed -i "s/${search}/${replace}/g"
Related
I would like to group my rows of inputs according to the first string of many comma-separated.
Basically, there will be 3 groups, which are "Motifs", "Chromatin_Structure" and "Protein_Binding"
The important output is the third one after 2 "|".
There might be some duplicates too like K562. The duplicates are not needed.
If the strings are not present, simply put a ". (dot)"
Input:
Motifs|PWM|Sox17|,Motifs|PWM|Sox8|,Chromatin_Structure|DNase-seq|K562|Znf4g7d3,Chromatin_Structure|DNase-seq|K562|,Chromatin_Structure|DNase-seq|TCF7L2|Znfe103c6,Protein_Binding|ChIP-seq|CTCF|HeLa-S3|,Protein_Binding|ChIP-seq|CTCF|HeLa-S3|
.
Motifs|PWM|TCF11|
Protein_Binding|ChIP-seq|MAFF|HepG2|
Desired output:
Sox17,Sox8 K562,TCF7L2 CTCF
. . .
TCF11 . .
. . MAFF
Codes that I tried.
sed 's/Motifs|PWM|//'
Appreciate your helps!
Perl one-liner (Using the term loosely):
$ perl -F, -lane '
my (%groups, #output);
for my $grp (#F) {
my #x = split /\|/, $grp;
$groups{$x[0]}{$x[2]} = 1;
}
for my $n (qw/Motifs Chromatin_Structure Protein_Binding/) {
if (exists $groups{$n}) {
push #output, join(",", sort keys %{$groups{$n}});
} else {
push #output, ".";
}
}
print join("\t", #output);' input.csv
Sox17,Sox8 K562,TCF7L2 CTCF
. . .
TCF11 . .
. . MAFF
And because I think it's woefully underappreciated as a scripting language, a tcl version:
#!/usr/bin/env tclsh
proc main {} {
while {[gets stdin line] >= 0} {
foreach grp [split $line ,] {
set x [split $grp |]
dict set groups [lindex $x 0] [lindex $x 2] 1
}
foreach n {Motifs Chromatin_Structure Protein_Binding} {
if {[dict exists $groups $n]} {
lappend output [join [dict keys [dict get $groups $n]] ,]
} else {
lappend output .
}
}
puts [join $output \t]
unset groups output
}
}
main
$ ./example.tcl < input.csv
Sox17,Sox8 K562,TCF7L2 CTCF
. . .
TCF11 . .
. . MAFF
I have a standardized way of writing comments before and after a function.
For example before declaring any function I write,
!---------------------------
! NAME_OF_FUNC (no)
!---------------------------
where no is the nth function in a given file containing multiple functions.
I know that a function e.g begins with (Fortran convention) either subroutine NAME_OF_SUB or function NAME_OF_FUNC. Hence, my end result would be
!---------------------------
! NAME_OF_FUNC (no)
!---------------------------
function NAME_OF_FUNC(...)
end function
!---------------------------
Can somebody show an example of how to write a bash script or in any other scripting language a code that can go through all my source files and the standard convention I just showed an example of?
Here is an example in Perl. It does not take backup before overwriting (I would recommend that you try to improve this script and add backup functionality). It also does not add the end of subroutine marker. But it would easy to add that functionality, please try. It also assumes that you want to modify all *.f95 files in the current directory and all its sub directories:
use feature qw(say state);
use strict;
use warnings;
use File::Find::Rule;
my #files = File::Find::Rule->new->name('*.f95')->in('.');
for my $fn (#files) {
open ( my $fh, '<', $fn ) or die "Could not open file '$fn': $!";
my $txt = do {local $/; <$fh>};
close $fh;
process_txt( \$txt );
save_txt( $fn, \$txt );
}
sub save_txt {
my ( $fn, $txt ) = #_;
open ( my $fh, '>', $fn ) or die "Could not open file '$fn': $!";
print $fh $$txt;
close $fh;
}
sub process_txt {
my ( $txt ) = #_;
my $i = 1;
$$txt =~ s/^(.*(?i:function|subroutine)\s+)([a-zA-Z_]+)(\s*\(.*$)/
do_substitution($1, $2, $3, $i++)/egmx;
}
sub do_substitution {
my ( $head, $name, $end, $i ) = #_;
my $line = $head . $name . $end;
$line =~ s/\s+$//;
my $N = length $line;
my $cline = '!' . '-' x $N;
my $mline = '! ' . $name;
my $snum = "($i)";
my $M = (length $mline) + (length $snum);
my $mspc = ($N > $M) ? (' ' x ($N-$M)) : ' ';
$mline = $mline . $mspc . $snum;
my $new_txt = join "\n", $cline, $mline, $cline, $line;
return $new_txt;
}
I have two source directories...
one directory has automatically generated code.
The other directory has code that takes the automatically generated code and adds customizations to the code...
I want to merge these two source directories automatically such that the basename of the source path for each file replaces the `basename in the automatically generated source directory. Example:
SRC_AUTO := $(wildcard path1/path2/src_auto/*.cpp)
SRC_WORK := $(wildcard path3/path4/src_work/*.cpp)
# How to achieve this with GNU Make without going out to $shell or perl...
SRC_MERGED := overlay_basename($SRC_WORK, $SRC_AUTO)
Example:
SRC_AUTO := \
./path1/path2/src_auto/pathz/file1.cpp \
./path1/path2/src_auto/pathx/pathy/file2.cpp \
./path1/path2/src_auto/file3.cpp
SRC_WORK := \
./path3/path4/src_work/file1.cpp \
./path3/path4/src_work/file3.cpp
#This is what I need to generate automatically
SRC_MERGED := \
path1/path2/src_auto/pathx/pathy/file2.cpp \
path3/path4/src_work/file1.cpp \
path3/path4/src_work/file3.cpp
except with more files... SRC_WORK overlays SRC_AUTO keyed off of the basename of the filepath.
Here's what I basically want to do from GNU make in perl pseudo-code:
#SRC_AUTO = ( ...list of automatically generated files.. );
#SRC_WORK = ( ...list of customized files copied from auto directory );
%unique = ();
foreach $file (#SRC_WORK) {
$base = basename($file);
$unique{$base} = $file;
}
foreach $file (#SRC_AUTO) {
$base = basename($file);
if (!defined($unique{$base}) {
$unique{$base} = $file;
}
}
#merged_list = ();
foreach $key (keys %unique) {
$file = $unique{$key};
push(#merged_list, $file);
}
#sorted_merged_list = sort(#merged_list);
return #sorted_merged_list;
Update, After some more refinement, I now have the perl script below: (now if only you could do this auto-matically from gnu make with a single built-in function without a breaky script environment of scripts that call scripts..):
#!/usr/bin/perl
# Automatically Generated Source Code
$dir_auto = "../src_auto";
# Overlayed Source Code
$dir_work = "../src_work";
#Merge File Output
$merge_list = "source.txt";
use File::Find;
sub OverlaySource($$)
{
my $dir_auto = shift;
my $dir_work = shift;
my %unique = ();
my #cpp_work;
find( sub {
my $file = $_;
if ($file =~ /\.cpp$/) {
$path = $dir_work . "/" . $file;
$unique{$file} = $path;
}
}, $dir_work);
my #cpp_auto;
find( sub {
my $file = $_;
return if (defined($unique{$file}));
if ($file =~ /\.cpp$/) {
$path = $dir_auto . "/" . $file;
$unique{$file} = $path;
}
}, $dir_auto);
my #tmp = ();
foreach $key (keys %unique) {
$file = $unique{$key};
push(#tmp, $file);
}
my #cpp_files = sort #tmp;
return #cpp_files;
}
$dir_auto = "../src_auto";
$dir_work = "../src_work";
my #cpp_files = OverlaySource($dir_auto, $dir_work);
print "Creating File: $merge_list\n";
open(F, ">${merge_list}");
foreach $file (#cpp_files) {
print F "$file\n";
}
close(F);
This should do what you want:
SRC_MERGED := $(filter-out $(addprefix %/,$(notdir $(SRC_WORK))),$(SRC_AUTO)) $(SRC_WORK)
Explanation:
$(notdir $(SRC_WORK)) returns the base names of all words in $(SRC_WORK): file1.cpp file3.cpp.
$(addprefix %/,$(notdir $(SRC_WORK))) adds the %/ prefix to the result of 1; this is a list of make patterns (% is the wildcard): %/file1.cpp %/file3.cpp.
$(filter-out $(addprefix %/,$(notdir $(SRC_WORK))),$(SRC_AUTO)) returns all words in $(SRC_AUTO) that do not match any pattern in $(addprefix %/,$(notdir $(SRC_WORK))): ./path1/path2/src_auto/pathx/pathy/file2.cpp.
$(filter-out $(addprefix %/,$(notdir $(SRC_WORK))),$(SRC_AUTO)) $(SRC_WORK) just concatenates $(SRC_WORK) to the result of 3:
./path1/path2/src_auto/pathx/pathy/file2.cpp ./path3/path4/src_work/file1.cpp ./path3/path4/src_work/file3.cpp
I'm trying to find some lines in a text file that begin with only one (+).
my file:
--- step31.php 2017-03-10 18:34:59.430949110 +0330
+++ step32.php 2017-03-10 18:34:59.430949110 +0330
## -1,6 +1,6 ##
<?php
defined('_JEXEC') or die;
-JLoader::register('BannersHelper', JPATH_COMPONENT . '/helpers/banners.php');
+JLoader::register('BannersHelper', JPATH_ADMINISTRATOR . '/components/com_banners/helpers/banners.php');
class BannersViewBanner extends JViewLegacy{
protected $form;
protected $item;
## -32,7 +32,7 ##
JToolbarHelper::save2copy('banner.save2copy');}
if (empty($this->item->id)) {
JToolbarHelper::cancel('banner.cancel');} else {
- if ($this->state->params->get('save_history', 0) && $user->authorise('core.edit')) {
+ if ($this->state->params->get('save_history', 0) && $canDo->get('core.edit')) {
JToolbarHelper::versions('com_banners.banner', $this->item->id);}
desired output:
+JLoader::register('BannersHelper', JPATH_ADMINISTRATOR . '/components/com_banners/helpers/banners.php');
+ if ($this->state->params->get('save_history', 0) && $canDo->get('core.edit')) {
I use grep '^+' but output is:
+++ step32.php 2017-03-10 18:34:59.430949110 +0330
+JLoader::register('BannersHelper', JPATH_ADMINISTRATOR . '/components/com_banners/helpers/banners.php');
+ if ($this->state->params->get('save_history', 0) && $canDo->get('core.edit')) {
You can use:
grep '^+[^+]' file
+JLoader::register('BannersHelper', JPATH_ADMINISTRATOR . '/components/com_banners/helpers/banners.php');
+ if ($this->state->params->get('save_history', 0) && $canDo->get('core.edit')) {
Regex ^+[^+] will match + at start followed by anything except + thus giving you expected output.
This is my find command:
find /test-data -type f -mtime +2m
I then run find2perl /test-data -type f -mtime +2m. It generates:
#! /usr/bin/perl -w
eval 'exec /usr/bin/perl -S $0 ${1+"$#"}'
if 0; #$running_under_some_shell
use strict;
use File::Find ();
# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.
# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name = *File::Find::name;
*dir = *File::Find::dir;
*prune = *File::Find::prune;
sub wanted;
# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, '/test-data');
exit;
sub wanted {
my ($dev,$ino,$mode,$nlink,$uid,$gid);
(($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
-f _ &&
(int(-M _) > 2m)
&& print("$name\n");
}
This code generates errors. I am missing what is wrong.
syntax error at ./test_older_files.pl line 32, near "&& print("$name\n")"
(Might be a runaway multi-line )) string starting on line 31)
-mtime 2m is not supported by find2perl (nor by GNU's find).
Put the following before the call to find:
my $time = time();
Replace the wanted sub with the following:
sub wanted {
my ($mtime);
( ($mtime) = ( lstat($_) )[9] ) &&
-f _ &&
( $time-$mtime >= 2*60 )
&& print("$name\n");
}