I'm very new to Perl and I'm trying to figure out just how to get this thing to work.
I found an example in this thread: Perl Regex - Print the matched value
Here is the example I'm trying to work with:
perl -e '$str="the variable Xy = 3 in this case"; print $str =~ /(Xy = 3)/;'
I've tried running it using cmd, but that produces this error:
"Can't find string terminator "'" anywhere before EOF at -e line 1."
When I run it in powershell nothing happens.
My ultimate goal is to set a variable at the command line, run a regexp find (and sometimes replace), and to print the result. This way I don't have to write a script every time I write a regexp pattern.
I've tried using the debugger, but nothing happens when I do this after setting the variable:
print $str =~ /(Xy = 3)/;
It is better to put your statements inside a Perl script on a windows environment because you will need the double quotes for most of your Perl stuff, but escaping on the command line gets messy eventually.
Ok, well I figured it out for powershell. I had to escape the double quotes.
perl -e '$str=\"the variable Xy = 3 in this case\"; print $str =~ /(Xy = 3)/;'
And the same goes for cmd except that I had to replace the single quotes with double quotes since single quotes aren't a string delimiter there.
perl -e "$str=\"the variable Xy = 3 in this case\"; print $str =~ /(Xy = 3)/;"
Related
This is related to this question
Where combination of all lines that end with a backslash character is done. The following Perl command works in terminal but not in Perl script:
perl -i -p -e 's/\\\n//' input_file
In Perl script, I set it this way but it does not work:
`perl -i -p -e 's/\\\n//' $input_file_path_variable`;
**Updated the file content and code used
Input file content as follows:
foo bar \
bash \
baz
dude \
happy
Desired output content:
foo bar bash baz
dude happy
Current script:
#!/usr/bin/env perl
use Getopt::Long;
use FindBin;
use File::Path;
use File::Copy;
use DirHandle;
use FileHandle;
use File::Basename;
use Env;
use File::Slurp;
my $dummy_file = "/wdir/dummy.txt";
my $file_content = read_file($dummy_file);
print "$file_content\n";
print "==============================================\n"
my $ttest = $file_content =~ s/\\\n//;
print "$ttest\n";
Current Output
foo bar \
bash \
baz
dude \
happy
==============================================
1
I think I realized what your problem is. You need to make your substitution match multiple times.
You are working with this one-liner:
perl -i -p -e 's/\\\n//' input_file
Which will replace the backslash and newline once per line in a file read in line-by-line mode. And now you are trying to implement it inside another program, where you are slurping a whole file into a variable, as mentioned in your comment on another answer:
I tried reading the whole file into a variable and applied your solution, not working for me: my $file_content = read_file($input_file_path_variable) and then $file_content =~ s/\\n//;
This will only replace one time. The first match in the file. You need to add the modifier /g to make the match global, as many times as possible:
$file_content =~ s/\\\n//g;
I am not sure how you are using your code, since you are not showing us. This makes answering simple questions hard. But if my assumptions are correct, this will fix your problem.
I think you've changed your code/approach at least once since originally asking the question, but based on your current code, your issue is this line:
my $ttest = $file_content =~ s/\\\n//;
Firstly, the s/// operator needs a g at the end so that it's a global search and replace, instead of stopping after the first substitution.
Secondly, $file_content =~ s/\\\n// doesn't return the final string; it modifies the string in-place. (So it's changing $file_content.) In recent versions of Perl, you can add an r modifier to the end to get it to return the modified string.
So either of these will work for you:
my $ttest = ( $file_content =~ s/\\\n//gr );
Or:
( my $ttest = $file_content ) =~ s/\\\n//g;
Full script which produces the output you want:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Slurp;
my $file_content = read_file('/tmp/dummy.txt');
(my $ttest = $file_content) =~ s/\\\n//g;
print "$file_content\n";
print "==============================================\n";
print "$ttest\n";
In a Perl you should write:
$input_file_variable =~ s/\\\n//;
I'm new to Perl and I found behaviour which I don't understand and can't solve.
I'm making a small find and replace program and there are some things I need to do. I have bunch of files that I need to process. Then I have a list of find / replace rules in an external text file. In replacing there I need three special things:
Replacing utf-8 characters (Czech diacritics)
Work with adding/removing lines (so working in a slurp mode)
Use a regular expressions
I want a program that works alone, so I wrote it so that it takes three arguments:
The file to work on
What to find
What to replace.
I'm sending parameters in a loop from a bash script which parse the rules list and loads other files.
My problem is when I have a "\n" string in a rules list and I send it to the Perl script. If it's in the first part of replacement (in the find section) it looks for a newline correctly, but when it's in the second part (the replace section) it just prints \n instead of a newline.
I tried hardcoding "\n" to the string right into the variable instead of passing it from the list and then it works fine.
What's the reason Perl doesn't interpret the "\n" string there, and how can I make it work?
This is my code:
list.txt - One line from the external replacement list
1\. ?\\n?NÁZEV PŘÍPRAVKU;\\n<<K1>> NÁZEV PŘÍPRAVKU;
farkapitoly.sh - The bash script for parsing list.txt and cycling through all of the files and calling the Perl script
...
FILE="/home/tmp.txt"
while read LINE
do
FIND=`echo "$LINE" | awk -F $';' 'BEGIN {OFS = FS} {print $1}'`
REPLACE=`echo "$LINE" | awk -F $';' 'BEGIN {OFS = FS} {print $2}'`
perl -CA ./pathtiny.pl "$FILE" "$FIND" "$REPLACE"
done < list.txt
...
pathtiny.pl - The Perl script for find and replace
#!/usr/bin/perl
use strict;
use warnings;
use Modern::Perl;
use utf8; # Enable typing Unicode in Perl strings
use open qw(:std :utf8); # Enable Unicode to STDIN/OUT/ERR and filehandles
use Path::Tiny;
my $file = path("$ARGV[0]");
my $searchStr = "$ARGV[1]";
my $replaceStr = "$ARGV[2]";
# $replaceStr="\n<<K1>> NÁZEV PRÍPRAVKU"; # if I hardcode it here \n is replaced right away
print("Search String:", "$searchStr", "\n");
print("Replace String:", "$replaceStr", "\n\n");
my $guts = $file->slurp_utf8;
$guts =~ s/$searchStr/$replaceStr/gi;
$file->spew_utf8($guts);
If it's important, I'm using Linux Mint 13 64-bit on VirtualBox (under Win 8.1) and I have Perl v5.14.2. Every file is UTF-8 with Linux endings.
Example files can be found on pastebin. this should end up like this.
But examples varies a lot. I need a universal solution to write down newline in a replacement string so it replaces correctly.
The problem is that the replacement string is read literally from the file, so if your file contains
xx\ny
then you will read exactly those six characters. Also, the replacement part of a substitution is evaluated as if it was in double quotes. So your replacement string is "$replaceStr" which interpolates the variable and goes no further, so you will again have xx\nyy in the new string. (By the way, please avoid using capital letters in local Perl identifiers as in practice they are reserved for globals such as Module::Names.)
The answer lies in using eval, or its equivalent - the /e modifier on the substitution.
If I write
my $str = '<b>';
my $r = 'xx\ny';
$str =~ s/b/$r/;
then the replacement string is interpolated to xx\ny, as you have experienced.
A single /e modifier evaluates the replacement as an expression instead of just a double-quoted string, but of course $r as an expression is xx\ny again.
What you need is a second /e modifier, which does the same evaluation as a single /e and then does an additional eval of the result on top. For this it is cleanest if you use qq{ .. } as you need two levels of quotation.
If you write
$str =~ s/b/qq{"$r"}/ee
then perl will evaluate qq{"$r"} as an expression, giving "xx\nyy", which, when evaluated again will give you the string you need - the same as the expression 'xx' . "\n" . 'yy'.
Here's a full program
use strict;
use warnings;
my $s = '<b>';
my $r = 'xx\nyy';
$s =~ s/b/qq{"$r"}/ee;
print $s;
output
<xx
yy>
But don't forget that, if your replacement string contains any double quotes, like this
my $r = 'xx\n"yy"'
then they must be escaped before putting the through the substitution as the expression itself also uses double quotes.
All of this is quite hard to grasp, so you may prefer the String::Escape module which has an unbackslash function that will change a literal \n (and any other escapes) within a string to its equivalent character "\n". It's not a core module so you probably will need to install it.
The advantage is that you no longer need a double evaluation, as the replacement string can be just unbackslash $r which give the right result if it evaluated as an expression. It also handles double quotes in $r without any problem, as the expression doesn't use double quotes itself.
The code using String::Escape goes like this
use strict;
use warnings;
use String::Escape 'unbackslash';
my $s = '<b>';
my $r = 'xx\nyy';
$s =~ s/b/unbackslash $r/e;
print $s;
and the output is identical to that of the previous code.
Update
Here is a refactoring of your original program that uses String::Escape. I have removed Path::Tiny as I believe it is best to use Perl's built-in inplace-edit extension, which is documented under the General Variables section of perlvar.
#!/usr/bin/perl
use utf8;
use strict;
use warnings;
use 5.010;
use open qw/ :std :utf8 /;
use String::Escape qw/ unbackslash /;
our #ARGV;
my ($file, $search, $replace) = #ARGV;
print "Search String: $search\n";
print "Replace String: $replace\n\n";
#ARGV = ($file);
$^I = '';
while (<>) {
s/$search/unbackslash $replace/eg;
print;
}
You got \n as a content of a string. (as two chacters 1: \ and second n, and not as one newline.
Perl interprets the \n as newline when it is as literal (e.g. it is in your code).
The quick-fix would be:
my $replaceStr=eval qq("$ARGV[2]"); #evaling a string causes interpreting the \n as literal
or, if you don't like eval, you can use the String-Escape cpan module. (the unbackslash function)
You're wanting a literal string to be treated as if it were a double quoted string. To do that you'll have to translate any backslash followed by another character.
The other experts have shown you how to do that over the entire string (which is risky since it uses eval with unvalidated data). Alternatively, you could use a module, String::Escape, which requires an install (not a high bar, but too high for some).
However, the following does a translation of the return value string itself in a safe way, and then it can be used like a normal value in your other search and replace:
use strict;
use warnings;
my $r = 'xx\nyy';
$r =~ s/(\\.)/qq{"$1"}/eeg; # Translate \. as a double quoted string would
print $r;
Outputs:
xx
yy
Hi i am inserting image location into particular line of file using Perl one-liner code as below
my $image="/home/users/images/image1.tar";
system(q(perl -pi -e 'print "\n$image" if ($. == 5 && $_=~ /^\s*$/ )' myfile.txt));
i am not able to insert a image location into the file .
please can anyone help me out?
As was pointed out, using single quotes q{} did not allow your $image variable to interpolate.
To fix, just concatenate that variable into your string:
#!/usr/bin/perl
use strict;
use warnings;
my $image = '/home/users/images/image1.tar';
system(q{perl -pi -e 'print "\n} . $image . q{" if ($. == 5 && $_=~ /^\s*$/ )' myfile.txt});
However, a much better solution is just to do this processing local to your perl script.
The following does the exact same processing without the secondary call to perl by using $INPLACE_EDIT:
my $image = "/home/users/images/image1.tar";
local #ARGV = 'myfile.txt';
local $^I = '';
while (<>) {
print "\n$image" if $. == 5 && $_ =~ /^\s*$/;
print;
}
For additional methods for editing a file, just read perlfaq5 - How do I change, delete, or insert a line in a file, or append to the beginning of a file?
I'm not sure why you're shelling out to a second Perl process to do this. Why not just do the processing within the original Perl program?
But your problem seems to be that the string you're passing to system is in single quotes (using q(...)) which means that the $image variable won't be expanded. You probably want to change that to a double-quoted string (using qq(...)).
Update:
This is why shelling out to an external process is fraught with difficulty. You have one variable ($image) which needs to be passed though and another variable ($_) which needs to be internal to the second process. You also have an escape sequence (\s) which the shell is trying (but failing) to interpret.
Liberal application of backslashes to escape special characters gives this:
#!/usr/bin/perl
use strict;
use warnings;
my $image='/home/users/images/image1.tar';
system(qq(perl -pi -e 'print "\n$image" if (\$. == 5 && /^\\s*\$/ )' myfile.txt));
Which seems to work. But I still think you'd be far better off doing this all in one Perl program.
I am trying to extract everything between braces from a text file and write the output to another text file. I was able to construct a regular expression to match everything between {} and it works fine (I wrote a simple java program to test it) but I not very strong in unix hence not sure how to use this regular expression in unix.
The below regular expression matches everything between {} (works in jedit too)
\{([^}]+)\}
I tried the below sed command,
cat samplefile | sed -e 's/.*\{\([^}]+\)\}.*/\1/g'
I am getting the below error.
sed: -e expression #1, char 24: Invalid preceding regular expression
Between I found a regex to match everything between [] and it works great. Not sure where I am going wrong. Can someone help me fix my regex issu?
cat file |sed -e 's/.*\[\([^]]*\)\].*/\1/g'
Edit 1:
Solution:
cat file | sed -e 's/.*{\([^}]\+\)}.*/\1/g' --> works
You must escape the + quantifier
I know that you already solved the question, but most unix machines have a perl interpreter and that language has a built-in module that does the hard job of parsing text whit this kind of delimiters, it's Text::Balanced. Here a test:
Assuming this random text (taken from the question :-), added some text between curly braces and saved as infile file:
I am trying to extract {everything between braces} from a text file and
write the output to another text file. I was able to {construct a regular
expression} to match everything between {} and it works fine (I wrote a
simple {java program} to test it) but I not {very strong} in unix hence not
sure how to use this regular expression in unix.
The program script.pl:
#!/usr/bin/env perl
use warnings;
use strict;
use Text::Balanced qw<extract_bracketed>;
my $str = do { undef $/; <> };
while ( my #result = extract_bracketed( $str, '{}', '[^{]*' ) ) {
last unless defined $result[0];
$result[0] =~ s/\n//g;
$result[0] = substr $result[0], 1, length( $result[0] ) - 2;
printf qq|%s\n|, $result[0];
}
reads the whole file in a variable and parses it looking for a pair of curly braces, for each loop saves in first position of the array #result the text inside them, so then I remove any newline character, leading and trailing curly braces and print it.
Run it like:
perl script.pl infile
That yields:
everything between braces
construct a regular expression
java program
very strong
Note that it parses correctly a blank pair in the third line. Also other with a newline inside them (second line) and when there are several in the same line, like in the fourth one.
I want to use regular expression to remove string with $ , % , # these three characters , but it seems can't remove $ and the error information shows undefined variable
How can I solve this problem?
here is my code
perl Remove.pl $ABC#60%
#!/usr/bin/perl
$Input = $ARGV[0];
$Input =~ s/\$|%|#//g;
print $Input;
thanks
I think your problem is with the shell, not with the Perl code. Single quote the argument to the script:
perl remove.pl '$ABC#60%'
The shell can interpret '$ABC' as a variable name in which case the script will receive no arguments. Perl will then complain about undefined variable in substitution.
$Input =~ s/[\$%#]//g;
ought to work
if you just want to remove some charactor, it will be better use tr
try this:
perl -e '$arg = shift; $arg =~ tr/$%#//d; print $arg' '$asdf#$'
your code is just fine, but the parameter you pass to the program will expand in bash. you should put single quote.
try this:
perl Remove.pl '$ABC#60%'