Replacing a character from a file by Perl script - perl

I am trying to replace a windows line ending character \r\n with linux style line ending character \n in a text file using a perl script. What I have written in the script is as the following:
system("perl -pi -e s/\r\n/\n/g input.txt");
And the resulting warning is:
Substitution pattern not terminated at -e line 1
I'd glad if I learn where this command is wrong.
Edit: When I used:
system("perl -pi -e 's/\r\n/\n/g' words.txt");
I got this warning:
Can't find string terminator "'" anywhere before EOF at -e line 1
Thanks in advance.

Separate the args for your system call. That will help you avoid needing to use the proper quoting and escaping:
system('perl', '-i', '-pe', 's/\r\n/\n/g', 'input.txt');
However, I'd recommend just doing this with $INPLACE_EDIT:
my $file = 'input.txt';
local #ARGV = $file;
local $^I = '';
while (<>) {
s/\R/\n/;
print;
}

perl -p -i -e 's/\r\n/\n/g' input.txt
You are missing '' in your regex. hence the error Substitution pattern not terminated at -e line 1

you are running the script on Windows, so you have to change the simple quotes and double quotes, and define the extension of the -i flag.
system('perl -i.bak -p -e "s/\r\n/\n/g" words.txt');

Related

Insert linebreak in a file after a string

I have a unique (to me) situation:
I have a file - file.txt with the following data:
"Line1", "Line2", "Line3", "Line4"
I want to insert a linebreak each time the pattern ", is found.
The output of file.txt shall look like:
"Line1",
"Line2",
"Line3",
"Line4"
I am having a tough time trying to escape ", .
I tried sed -i -e "s/\",/\n/g" file.txt, but I am not getting the desired result.
I am looking for a one liner using either perl or sed.
You may use this gnu sed:
sed -E 's/(",)[[:blank:]]*/\1\n/g' file.txt
"Line1",
"Line2",
"Line3",
"Line4"
Note how you can use single quote in sed command to avoid unnecessary escaping.
If you don't have gnu sed then here is a POSIX compliant sed solution:
sed -E 's/(",)[[:blank:]]*/\1\
/g' file.txt
To save changes inline use:
sed -i.bak -E 's/(",)[[:blank:]]*/\1\
/g' file.txt
Could you please try following. using awk's substitution mechanism here, in case you are ok with awk.
awk -v s1="\"" -v s2="," '{gsub(/",[[:blank:]]+"/,s1 s2 ORS s1)} 1' Input_file
Here's a Perl solution:
perl -pe 's/",\K/\n/g' file.txt
The substitution pattern matches the ",, but the \K says to ignore anything to the left for the replacement (so, ",) will not be replaced. The replacement then effectively inserts the newline.
I used the single quote for the argument to -e, but that doesn't work on Windows where you have to use ". Instead of escaping the ", you can specify it in another way. That's code number 0x22, so you can write:
perl -pe "s/\x22,\K/\n/g" file.txt
Or in octal:
perl -pe "s/\042,\K/\n/g" file.txt
Use this Perl one-liner:
perl -F'/"\K,\s*/' -lane 'print join ",\n", #F;' in_file > out_file
Or this for in-line replacement:
perl -i.bak -F'/"\K,\s*/' -lane 'print join ",\n", #F;' in_file
The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-n : Loop over the input one line at a time, assigning it to $_ by default.
-l : Strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
-a : Split $_ into array #F on whitespace or on the regex specified in -F option.
-F'/"\K,\s*/' : Split into #F on a double quote, followed by comma, followed by 0 or more whitespace characters, rather than on whitespace. \K : Cause the regex engine to "keep" everything it had matched prior to the \K and not include it in the match. This causes to keep the double quote in #F elements, while comma and whitespace are removed during the split.
-i.bak : Edit input files in-place (overwrite the input file). Before overwriting, save a backup copy of the original file by appending to its name the extension .bak.
SEE ALSO:
perldoc perlrun: how to execute the Perl interpreter: command line switches
perldoc perlrequick: Perl regular expressions quick start

using slash in Perl Command

Below the command i want to run.
perl -pi -w -e 's//apps/LIVE/appl/xx/11.5.0//$XXTOP//g;' prog.txt
Here, source and replacement both have slashes in them.
How to handle this?
--
Update
I tried with curly braces and tilde that was suggested.
perl -pi -w -e 's{apps/LIVE/appl/xx/11.5.0}{$XXTOP}g;' prog.txt
In this case, Dollar sign in giving issue, else it works fine..
Error:
Name "main::XXTOP" used only once: possible typo at -e line 1.
Use of uninitialized value at -e line 1, <> chunk 1.
Use of uninitialized value at -e line 1, <> chunk 2.
Use another delimiter:
perl -pi -w -e 's~/apps/LIVE/appl/xx/11.5.0/~$XXTOP/~g;' prog.txt
You could access environment variable thru %ENV, like:
perl -pi -w -e 's~/apps/LIVE/appl/xx/11.5.0/~$ENV{XXTOP}/~g;' prog.txt
Do you want the text $XXTOP to appear in the output. In which case escape the $ - {\$XXTOP}
perl -pi -w -e 's{apps/LIVE/appl/xx/11.5.0}{\$XXTOP}g;' prog.txt
Perl is thinking that $XXTOP is a Perl variable.
Of if you are want to expand an environment variable, use $ENV{$XXTOP}
solved:
Solution:
perl -pi -w -e 's{apps/LIVE/appl/xx/11.5.0}{\$XXTOP}g;' prog.txt

Perl -pi -e 's/ / /' filename not working inside perl script. but it working in command line

I have a perl script, in that script I need to replace a string in every line of file. I used below line inside that perl script. It shows a syntax error near -pi -e. What am I doing wrong?
perl -pi -e 's/ / /' filename
I have a pipe delimited file to be used in perl script. am using that file contents to be loaded into table using BCP. I have created the table with 8 clomuns, but pipe delimited file has only 7 columns. In that file i need to add '| ' (pipe followed by space) symbol at the end of every record. So that it will match the number of columns in both table and BCP file. Hence in part of the perl script i need to use 's/$/\| /' concept. but i like to do this in single line itself inside the perl script. (like perl -pi -e 's/$/\| /' which is working in command line)
The command-line code is written for the shell to parse before it passes control to the perl compiler/interpreter program.
We can use the B::Deparse backend processor to see what Perl code is being executed like this
$perl -MO=Deparse -pi -e 's/ / /' filename
and it shows the equivalent Perl program to be
BEGIN { $^I = ""; }
LINE: while (defined($_ = <ARGV>)) {
s/ / /;
}
continue {
die "-p destination: $!\n" unless print $_;
}
We can drop the BEGIN block as long as we make sure $^I is defined first. The label LINE can go because it is never used. We can also avoid the check on the success of print. Together with some syntax conveniences that Perl allows in real-life code, we get this
$^I = "";
while (<>) {
s/ / /;
print;
}
But why are you replacing every space with another space before printing?!
The line is shell command. Not perl script. This line calls perl and gives it program:
s/ / /
Plus sets certain options (-p, -i), but it is still shell program call, not Perl expression.

Perl one-liner to remove trailing space from a file

I am calling a perl one-liner inside a Perl script.
The intention of the one-liner is to remove the trailing space from a file.
Inside the main perl script:
`perl -pi -e 's/\s+$//' tape.txt`;
Now it is throwing me an error Substitution replacement not terminated at -e line 2.
Where is it going wrong?
It's because of the $/ (special variable) inside your main perl script. Note that variables are interpolated inside `` strings just like inside "" strings, and the fact that there are some single quotes in there doesn't change that. You need to escape that $:
`perl -pi -e 's/\s+\$//' tape.txt;`
The backtick syntax invokes a shell and when invoked, the shell assumes it should interpolate the string passed.
A cleaner syntax might be:
system('perl -pli -e "s/\s*$//" tape.txt');
Since you aren't capturing the output of the command, using backticks or qx in lieu of system isn't an issue.
Too, adding the -l switch autochomps each line read and then adds a newline back --- probably what you want.
\s matches [ \t\n\r\f] and do not want to match \n.
Notice use of {} for subst delimiters:
$ echo -e 'hi \nbye'| perl -pe 's{[\t\040]+$}{};' | cat -A
hi$
bye$

Perl command line search and replace with multiple expressions

I am using Perl to search and replace multiple regular expressions:
When I execute the following command, I get an error:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g' -pe 's/(\W)##/\1/g'
syntax error at -e line 2, near "s/(\W)##/\1/g"
Execution of -e aborted due to compilation errors.
xargs: perl: exited with status 255; aborting
Having multiple -e is valid in Perl, then why is this not working? Is there a solution to this?
Several -e's are allowed.
You are missing the ';'
find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g;' -pe 's/(\W)##/\1/g;'
Perl statements has to end with ;.
Final statement in a block doesn't need a terminating semicolon.
So a single -e without ; will work, but you will have to add ; when you have multiple -e statements.
Having multiple -e values are valid, but is it useful? The values from the multiple -e are merely combined into one program, and it's up to you to ensure that together they make a syntactically correct program. The B::Deparse program can show you what perl thinks the program is:
$ perl -MO=Deparse -e 'print' -e 'q(Hello' -e ')'
print "Hello\n";
-e syntax OK
A curious thing to note is that a newline snuck in there. Think about how it got there to see what else perl is doing to combine multiple -e values.
In your program, you are substituting on the current line, then taking the modified line and substituting again. That's better written as:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g; s/(\W)##/\1/g'
Now, if you are building up this command line by adding more and more -e through some automated process and you don't know ahead of time what you get, maybe those -e make sense. However, you might consider that you can do the same thing to build up the string you give to -e. I don't know what might be better because you didn't explain why you are doing it that way.
But, I suspect that in some cases, people are actually thinking about having only one substitution work. They want to try one and if its pattern doesn't work, try a different one until one succeeds. In that case you don't want to separate the substitutions by semicolons. Use the short-circuiting || instead. The s/// returns the number of substitutions it made and || will stop (short circuit) when it finds a true value:
prompt> find "*.cpp" | xargs perl -i -pe 's/##(\W)/\1/g || s/(\W)##/\1/g'
And note, you only need one -p. It only does its job once. Here's the program with multiple -p deparsed:
$ perl -MO=Deparse -i -pe 's/##(\W)/\1/g;' -pe 's/(\W)##/\1/g;'
BEGIN { $^I = ""; }
LINE: while (defined($_ = readline ARGV)) {
s/##(\W)/$1/g;
s/(\W)##/$1/g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
It's the same thing as having only one -p:
$ perl -MO=Deparse -pi -e 's/##(\W)/\1/g;' -e 's/(\W)##/\1/g;'
BEGIN { $^I = ""; }
LINE: while (defined($_ = readline ARGV)) {
s/##(\W)/$1/g;
s/(\W)##/$1/g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
Thanks so much! You helped me reduce my ascii / decimal / 8-bit binary table printer enough to fit in a tweet:
for i in {32..126};do printf "'\x$(printf %x $i)'(%3i) = " $i; printf '%03o\n' $i | perl \
-pe 's#0#000#g;' -pe 's#1#001#g;' -pe 's#2#010#g;' -pe 's#3#011#g;' \
-pe 's#4#100#g;' -pe 's#5#101#g;' -pe 's#6#110#g;' -pe 's#7#111#g' ; done | \
perl -pe 's#= 0#= #'