Find string and replace each occurance with different string - list

I am looking for a simple batch solution for the following:
In a number of files I want to replace a string with another one. Can do this with Notepad++. However, each new string has to be unique, read from a list of new strings.
So, if 'abc' occurs in some files, and I have a list with new strings, replace it this way:
abc --> alex
abc --> ben
abc --> chris
abc --> dave
etc.
I can have a txt file with the new strings to read from.
Hope someone has a solution for me!
Many thanks,
Lennart

Would a Perl script work?
my #words = qw(alex ben chris dave ...);
while (<>) {
s/abc/shift #words/ge;
print;
}
If you want the word list to loop:
my #words = qw(...);
my $i = 0;
while (<>) {
# I know I should have written this in a more readable way...
s{abc}{$words[$i++] // $words[$i=0]}ge;
print;
}

Or as batch script
#echo off
setlocal Disabledelayedexpansion
set "wordlist=alex ben chris dave"
for /F "tokens=* delims=" %%a in (myFile.txt) do (
set "line=%%a"
setlocal Enabledelayedexpansion
for /F "tokens=1,*" %%b in ("!wordlist!") do (
set "newline=!line:abc=%%b!"
(echo(!newline!)
if !newline! NEQ !line! (
endlocal
set "wordlist=%%c"
) ELSE (
endlocal
)
)
)
Edit Change to a "!" safe variant

Try and find a sed executable for Windows which supports the -i (--inline) switch and your job will become a toddler's task!

Related

Replace with regular expression using Batch multiple text (Windows)

I have three text file.
C:\content1.txt
C:\content2.txt
C:\content3.txt
text file contains random links
https://example2.com/file/casdqwe/test1.rar
https://example2.com/file/casdqwe/test2.rar
https://example5.com/file/casdqwe/test3.rar
i want to remove the the domain names and save the last part only. output
test1.rar
test2.rar
test3.rar
and save it in original file no need any confirmation.
notepad++ regex replace work but i am not sure how to do it in
using Powershell/VBS script any will work fine.
Find What: https://example\.com[^\s\[\]<'\"]+/([^\s\[\]<'\"]+)
Replace with: $1
and powershell i have something like that
(Get-Content -path C:\content1.txt -Raw) -replace "https://example2.com/file/asdcb6af26/test.rar" -replace "https://example2.com/file/(.*)\/(.*)", '$2'
Use these simple powershell commands:
(Get-Content C:\content1.txt) -creplace '(?s)^.*/', '' | Set-Content C:\content1.txt
(Get-Content C:\content2.txt) -creplace '(?s)^.*/', '' | Set-Content C:\content2.txt
(Get-Content C:\content3.txt) -creplace '(?s)^.*/', '' | Set-Content C:\content3.txt
Let me know if you have any questions.
Refer to your previous question here, you can give a try for this batch file that use Regex in vbscript.
#echo off
Mode 85,35 & color 0A
Title Replace Multi String using Regex with vbscript into Folder with text files
Set "Source_Folder=C:\test"
Set "Backup_Folder=%userprofile%\Backup_Contents\"
Rem :: Just make a backup of your folder and its contents if something went wrong!
If Not Exist "%Backup_Folder%" XCopy "%Source_Folder%" "%Backup_Folder%" /D /Y /E /F >%~dp0BackupLogFile.txt
Set "VBSFILE=%tmp%\%~n0.vbs" & Call :CreateVBS
Set "TmpFile=%Temp%\%~n0.tmp"
for /R "%Source_Folder%" %%f in (*.txt) do (
echo( ------------------------------------------
echo Replacing Contents of "%%f"
echo( ------------------------------------------
Call :Search_Replace "%%f" "%TmpFile%"
Move /Y "%TmpFile%" "%%f">nul
)
If Exist "%VBSFILE%" Del "%VBSFILE%"
Timeout /T 3 /NoBreak>nul & Exit
::-----------------------------------------------------------------------------
:CreateVBS
(
echo WScript.StdOut.WriteLine Search_Replace(Data^)
echo Function Search_Replace(Data^)
echo Dim strPattern, strReplace, strResult,oRegExp
echo Data = "%~1"
echo Data = WScript.StdIn.ReadAll
echo strPattern1 = "(\x22<|<)([\s\S]*?)(/>\x22|>| />\x22| />| \x22>)"
echo strReplace1 = "[abc]$2[/abc]"
echo strPattern2 = "(http:\/\/|https:\/\/)(.+)[^\s\[\]<]\/([^\s\[\]<].+)"
echo strReplace2 = "$3"
echo Set oRegExp = New RegExp
echo oRegExp.Global = True
echo oRegExp.IgnoreCase = True
echo oRegExp.Pattern = strPattern1
echo strResult1 = oRegExp.Replace(Data,strReplace1^)
echo oRegExp.Pattern = strPattern2
echo strResult2 = oRegExp.Replace(strResult1,strReplace2^)
echo Search_Replace = strResult2
echo End Function
)>"%VBSFILE%"
Exit /b
::----------------------------------------------------------------------------
:Search_Replace <InputFile> <OutPutFile>
Cscript /nologo "%VBSFILE%" < "%~1" > "%~2"
Exit /B
::----------------------------------------------------------------------------
I'm guessing that maybe,
\bhttps?:\/\/.*\/
simply replaced with an empty string might work just fine.
If you wish to simplify/modify/explore the expression, it's been explained on the top right panel of regex101.com. If you'd like, you can also watch in this link, how it would match against some sample inputs.

Script or command to increment number in file name inside file

I have a file in which we have entries in following format. I would like to increment the numbers in file names inside this file. So some_v1.png will become some_v2.png. Is there a way with regex OR command line utility to achieve this.
Following is example file (file.config) with file entries as string.
something/some_v1.png
something/some_v4.png
something/some_v3.png
This looks like a great match for awk's "split" function:
awk '{n=split($0,a,"[1-9][0-9]*",s);for(i=1;i<n;++i)printf "%s%d",a[i],s[i]+1;print a[n]}'
The perl one-liner you already found also works great, with one exception: files with leading-zero numbers will lose the zeroes. Here is a fix for that using the magical auto-increment:
perl -pe 's/(\d+)/++($a=$1)/eg'
If you want to rename a bunch of files I'd use an auxiliary directory and a test to see if there is an actual file to rename.
mkdir aux
for i in {1..7} ; do
j=$($i + 1)
[ -f something/some_v${i}.png ] && mv something/some_v${i}.png aux/some_v${j}.png
done
mv aux/* something
rmdir aux
The use of a fixed name for the auxiliary directory could not stand a security review for repeated use in a dynamic production environment but I think it's fine for a one shot use in a controlled environment.
In perl:
#!/usr/bin/env perl
use strict;
use warnings;
foreach
my $filename (
sort { $b =~ s/.*(\d+).*/$1/r <=> $a =~ s/.*(\d+).*/$1/r }
glob "something/some_v*.png" )
{
chomp $filename;
if ( my ($vnum) = $filename =~ m/(\d+)\.png/ ) {
print "mv $filename ", $filename =~ s|\d+\.png|++$vnum.".png"|re,
"\n";
}
}
Note - sorting numerically, to ensure that you're never replacing 5 with 4, before you've renamed 5.

Get return params from Perl one-liner regular expression

How can I return the matched value from a regular expression Perl execution?
I want to further use the match in my batch file.
Batch file:
perl -p -e "ab{2}" c:\file.txt
REM echo %var% how can I get the result from perl?
REM do something with %var%
File file.txt:
abbreviation
Update for zb226:
test
Filename: dynamicFile.txt
Property: some property to neglect
Message: the message I want
Time: dynamicTime
You need the FOR command for that:
#ECHO OFF
FOR /F "delims=" %%I IN ('perl -ne "print $1.\"\n\" if ~/(ab{2})/" c:\file.txt') DO SET match=%%I
ECHO match: %match%
Note that this will return the last match - which does not matter with your example regexp ab{2}, as that will always return abb or nothing.
Edit 1: Forgot to add a newline in the perl onliner - alternatively you can use Andrey's perl -pe ... version, which does not require printing the newline.
Edit 2: Then again, Andrey's one-liner matches the regexp only at the beginning of a line, which is not what your question suggests. Also it prints non-matching lines, as you already noticed. I can't think of an easy fix for the perl -pe version...
Edit 3: Here's a quick and dirty solution for the additional information you've provided in the comment.
#ECHO OFF
FOR /F "usebackq delims=" %%I IN (`perl -e "local $/ = undef; open my $h, '<', $ARGV[0]; my $x = <$h>; print $1 if $x=~/Filename: dynamicFile.txt.*?Message: (.*?)Time/s;" c:\file.txt`) DO SET match=%%I
ECHO match: %match%
With a test file of...
test
Filename: dynamicFile.txt
Property: some property to neglect
Message: the message I want
Time: dynamicTime
----
Filename: someOtherFile.txt
Property: some property to neglect
Message: someMessage
Time: dynamicTime
...this outputs:
match: the message I want
perl -p -e "s/^(ab{2}).*$/\1/" c:\file.txt
perl -lne "print $1 if /(ab{2})/" C:\file.txt

Bulk renaming files with bash and Perl based on file name

I'm looking to bulk rename files in the current directory only and remove certain strings from the end of file names.
Sample:
foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt
the output should look like this:
foo-bar.txt
foo-bar-foo-bar.txt
foo-bar-foo-bar-bar.txt
I want to remove the string -(ab-2492201) at the end of each file name
knowing that the digits can vary in length.
A Perl regex is preferred over modules and without using any utilities and for bash oneliner command is highly preferred.
How to accomplish that in both Perl and Bash Shell on Linux? interested to know both solutions.
Try:
$ rename 's/-\(ab-\d+\)(?=\.txt$)//' *.txt
There's a rename command written in Perl. Its first argument is Perl code describing how to transform a filename. You could use the same s/// command in your own Perl program or one-liner.
If that doesn't work, try prename instead of rename; there's a different, non-Perl-based, rename command installed on some systems, in which case the Perl one may be called prename.
Using Perl Regex to Rename Files
With find, perl, and xargs, you could use this one-liner
find . -type f | perl -pe 'print $_; s/input/output/' | xargs -n2 mv
Results without calling mv should just be
OldName NewName
OldName NewName
OldName NewName
How does it work?
find . -type f outputs file paths (or file names...you control what gets processed by regex here!)
-p prints file paths to be processed by regex, -e executes inline script
print $_ prints the original file name first (independent of -p)
-n2 prints two elements per line
mv gets the input of the previous line
In bash, you could write something like:
for file in *-\(ab-[0-9]*\)*; do
newfile="${file/-(ab-[0-9]*)/}"
mv "$file" "$newfile"
done
When you say under the current directory, do you mean in the current directory, or anywhere in or beaneath the current directory and its descendants?
File::Find is a simple way to do the latter, and is a core module so won't need installing. Like so:
use strict;
use warnings;
use autodie;
use File::Find;
find(\&rename, '.');
sub rename {
return unless -f;
my $newname = $_;
return unless $newname =~ s/-\(ab-[0-9]+\)(\.txt)$/$1/i;
print "rename $_, $newname\n";
}
Update
This program will rename all the files with the given filename pattern only within the current directory.
Note that the initial open loop is there only to create sample files for renaming.
use strict;
use warnings;
use autodie;
open my $fh, '>', $_ for qw(
foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt
);
for (glob '*.txt') {
next unless -f;
my $newname = $_;
next unless $newname =~ s/-\(ab-[0-9]+\)(\.txt)$/$1/i;
print "rename $_, $newname\n";
rename $_, $newname;
}
output
rename foo-bar-(ab-4529111094).txt, foo-bar.txt
rename foo-bar-foo-bar-(ab-189534).txt, foo-bar-foo-bar.txt
rename foo-bar-foo-bar-bar-(ab-24937932201).txt, foo-bar-foo-bar-bar.txt
A simpler, shorter (better ? :) ) rename regex :
rename 's#-\(.*?\)##' foo*.txt
check this:
ls -1 | nawk '/foo-bar-/{old=$0;gsub(/-\(.*\)/,"",$0);system("mv \""old"\" "$0)}'
> ls -1 foo*
foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt
> ls -1 | nawk '/foo-bar-/{old=$0;gsub(/-\(.*\)/,"",$0);system("mv \""old"\" "$0)}'
> ls -1 foo*
foo-bar-foo-bar-bar.txt
foo-bar-foo-bar.txt
foo-bar.txt
>
For detailed explanation check here
Another way using just perl:
perl -E'for (<*.*>){ ($new = $_) =~ s/(^.+?)(-\(.+)(\..*$)/$1$3/; say $_." -> ".$new}'
(say ... is nice for testing, just replace it with rename $_,$new or rename($_,$new) )
<*.*> read every file in the current directory
($new = $_) =~ saves the following substitution in $new and leaves $_ as intact
(^.+?) save this match in $1 and non-greedy match from the beginning until...
(-\(.+) the sequence "-( ...anything..." is found. (this match would be saved in $2)
(\..*$) save everything from the last "." (period) before the end ($) of the line until and including the end of the line -> into $3
substitute the match with the string generated from $1$3
( you could also do it for a specific directory with perl -E'for (</tmp/my/directory/*.*>){ .....

Using Perl, how can I replace newlines with commas?

I gave up on sed and I've heard it is better in Perl.
I would like a script that can be called from the 'unix' command line and converts DOS line endings CRLF from the input file and replaces them with commas in the output file:
like
myconvert infile > outfile
where infile was:
1
2
3
and would result in outfile:
1,2,3
I would prefer more explicit code with some minimal comments over "the shortest possible solution", so I can learn from it, I have no perl experience.
In shell, you can do it in many ways:
cat input | xargs echo | tr ' ' ,
or
perl -pe 's/\r?\n/,/' input > output
I know you wanted this to be longer, but I don't really see the point of writing multi line script to solve such simple task - simple regexp (in case of perl solution) is fully workable, and it's not something artificially shortened - it's the type of code that I would use on daily basis to solve the issue at hand.
#!/bin/perl
while(<>) { # Read from stdin one line at a time
s:\r\n:,:g; # Replace CRLF in current line with comma
print; # Write out the new line
}
use strict;
use warnings;
my $infile = $ARGV[0] or die "$0 Usage:\n\t$0 <input file>\n\n";
open(my $in_fh , '<' , $infile) or die "$0 Error: Couldn't open $infile for reading: $!\n";
my $file_contents;
{
local $/; # slurp in the entire file. Limit change to $/ to enclosing block.
$file_contents = <$in_fh>
}
close($in_fh) or die "$0 Error: Couldn't close $infile after reading: $!\n";
# change DOS line endings to commas
$file_contents =~ s/\r\n/,/g;
$file_contents =~ s/,$//; # get rid of last comma
# finally output the resulting string to STDOUT
print $file_contents . "\n";
Your question text and example output were not consistent. If you're converting all line endings to commas, you will end up with an extra comma at the end, from the last line ending. But you example shows only commas between the numbers. I assumed you wanted the code output to match your example and that the question text was incorrect, however if you want the last comma just remove the line with the comment "get rid of last comma".
If any command is not clear, http://perldoc.perl.org/ is your friend (there is a search box at the top right corner).
It's as simple as:
tr '\n' , <infile >outfile
Avoid slurping, don't tack on a trailing comma and print out a well-formed text file (all lines must end in newlines):
#!/usr/bin/perl
use strict;
use warnings;
my $line = <>;
while ( 1 ) {
my $next = <>;
s{(?:\015\012?|\012)+$}{} for $line, $next;
if ( length $next ) {
print $line, q{,};
$line = $next;
}
else {
print $line, "\n";
last;
}
}
__END__
Personally I would avoid having to look a line ahead (as in Sinar's answer). Sometimes you need to but I have sometimes done things wrong in processing the last line.
use strict;
use warnings;
my $outputcomma = 0; # No comma before first line
while ( <> )
{
print ',' if $outputcomma ;
$outputcomma = 1 ; # output commas from now on
s/\r?\n$// ;
print ;
}
print "\n" ;
BTW: In sed, it would be:
sed ':a;{N;s/\r\n/,/;ba}' infile > outfile
with Perl
$\ = "\n"; # set output record separator
$, = ',';
$/ = "\n\n";
while (<>) {
chomp;
#f = split('\s+', $_);
print join($,,#f);
}
in unix, you can also use tools such as awk or tr
awk 'BEGIN{OFS=",";RS=""}{$1=$1}1' file
or
tr "\n" "," < file