Adding space to more symbols - preg-replace

i have this string for example
$string="put#returns-between|par/agra\phs"; // #-|/\
how to add space to each sympol with one command
this is result
$result :"put # returns - between | par / agra \ phs
i have used this solution but I would like to know if there is better than this
<?php
$string="put#returns-between|par/agra\phs"; // #-|/\
$str=preg_replace("/[#]/", " # ", $string);
$str=preg_replace("/[-]/", " - ", $str);
$str=preg_replace("/[|]/", " | ", $str);
$str=preg_replace("/[\/]/", " / ", $str);
thanks.

<?php
$string="put#returns-between|par/agra\phs"; // #-|/\
$str=preg_replace("/([#|\-|\||\\\\|\/])/", " $1 ", $string);
echo $str;
see the demo, hope it helps.

Use a character class:
$string = "put#returns-between|par/agra\phs";
$str = preg_replace("~([-#|\\\\/])~", " $1 ", $string);
echo $str;
Output:
put # returns - between | par / agra \ phs

Related

How to get many matched patterns from multiple patterns match in perl

Is there a way to get multi-matched-patterns from "many '|' regex" matching.
Here is my code,
#! /usr/bin/perl
#matches = qw(google intel hp qualcomm app);
$keyword = join('|', #matches);
$string = "hello google app";
#founded = ($string =~ /($keyword)/);
print "Founded keyword is:" . join(" ", #founded);
I hope to get 'google and app', because this keywords are both matched in strings. But How sad, just get 'google'
Simply add a /g modifier to your match:
#found = ($string =~ /($keyword)/g);
You'll get all the matches that way.
I think you are looking for the intersection of two lists:
use Array::Utils qw(:all);
my #matches = qw(google intel hp qualcomm app);
my #find = qw(hello google app);
my #result = intersect(#matches, #find);
print "Founded keyword(s): ", join(" ", #result) . "\n";
This solution uses the Array::Utils module

Concatenating strings with spaces in Perl. Any built-ins?

In Perl I can concatenate multiple strings with spaces between them as follows:
my $long_string = $one_string . " " . $another_string . " " . $yet_another_string . " " .
$and_another_string . " " $the_lastr_string
However, typing this is a bit cumbersome.
Is there a built-in that can make this task easier?
e.g. something like:
concatenate_with_spaces($one_string, $another_string, $yet_another_string, ...)
You want join:
my $x = 'X';
my #vars = ( 1, 'then', 'some' );
my $long_string = join ' ', $x, 2, #vars; # "X 2 1 then some"
Zaid has given the idiomatic solution, using join. However, there are more ways to do it.
my #vars = ($one, $two, $three);
my $str1 = "#vars"; # Using array interpolation
my $str2 = "$one $two $three"; # interpolating scalars directly
Interpolating an array uses the predefined variable $" (list separator), which is by default set to space. When interpolating variables, you do not need to use . to concatenate spaces to your strings, they can be used directly in a double quoted string.
my #list_of_strings = ($one_string, $two_strings );
my $string = join(' ', #list_of_strings );
print $string;

perl substr to get a substring

#!/usr/bin/perl
my $str = "abc def yyy ghi";
print substr($str, 0 , index($str,' '));
I want substr to print def yyy
print substr ($str, index ($str, ' '), rindex($str, ' ') does not work?
Any idea?
You didn't specify EXACTLY what you want as far as logic but the best guess is you want to print characters between first and last spaces.
Your example code would print too many characters as it prints # of characters before the last space (in your example, 10 instead of 7). To fix, you need to adjust the # of characters printed by subtracting the # of characters before the first space.
Also, you need to start one character to the right of your "index" value to avoid printing the first space - this "+1" and "-1" in the example below
$cat d:\scripts\test1.pl
my $str = "abc def yyy ghi";
my $first_space_index = index ($str, ' ');
my $substring = substr($str, $first_space_index + 1,
rindex($str, ' ') - $first_space_index - 1);
print "|$substring|\n";
test1.pl
|def yyy|
The third argument is length, not offset. But it can be negative to indicate chars from the end of the string, which is easily gotten from rindex and length, like so:
my $str = "abc def yyy ghi";
print substr( $str, 1 + index( $str, ' ' ), rindex( $str, ' ' ) - length($str) );
(Note adding 1 to get the offset after the first space.)
If you want to print text between first and last space, wouldn't it be easier with regex?
print $1 if "abc def yyy ghi" =~ / (.*) /
frankly, substr/index/rindex are really not the way to go there. You are better off doing something like:
my $str = "abc def yyy ghi";
my #row = split ' ', $str;
pop #row and shift #row;
print "#row";
Which is more inefficient, but captures the actual intent better

How can I reformat the GECOS field with Perl or awk?

I want to scan the passwd file and change the order of words in the comment field from firstname lastname to lastname firstname, and force the surname to capitals.
So, change every line from:
jbloggs:x:9999:99:Joe Bloggs:/home/jbloggs:/bin/ksh
to:
jbloggs:x:9999:99:BLOGGS Joe:/home/jbloggs:/bin/ksh
I'm new to Perl and I'm having problems with different field separators in awk.
Appreciate any help.
Use Passwd::Unix or Passwd::Linux.
$ awk -v FS=":" '{split($5, a, " "); name = toupper(a[2]) " " a[1]; gsub($5, name); print $0}' passwd
Won't work if you have middle names though.
Edit: easier to read version
awk -v FS=":" '
{
split($5, name_parts, " ")
name = toupper(name_parts[2]) " " name_parts[1]
gsub($5, name)
print $0
}' passwd
Stand-alone example:
use strict;
use warnings;
my $s = 'jbloggs:x:9999:99:Joe Bloggs:/home/jbloggs:/bin/ksh';
my #tokens = split /:/, $s;
my ($first, $last) = split /\s+/, $tokens[4];
$tokens[4] = uc($last) . " $first";
print join(':', #tokens), "\n";
__END__
jbloggs:x:9999:99:BLOGGS Joe:/home/jbloggs:/bin/ksh
As a script (output to STDOUT; must redirect output to a file):
use strict;
use warnings;
while (<>) {
chomp;
my #tokens = split /:/;
my ($first, $last) = split /\s+/, $tokens[4];
$tokens[4] = uc($last) . " $first";
print join(':', #tokens), "\n";
}
This will process the file as you read it and put the new format entries into the array #newEntries.
open PASSWD, "/etc/passwd";
while(<PASSWD>) {
#fields = split /:/;
($first, $last) = split (/\s/, $fields[4]);
$last = uc $last;
$fields[4] = "$last $first";
push #newEntries, join(':', #fields);
}
$ awk -F":" ' { split($5,a," ");$5=toupper(a[2])" "a[1] } 1 ' OFS=":" /etc/passwd
jbloggs:x:9999:99:BLOGGS Joe:/home/jbloggs:/bin/ksh
$ perl -pe 's/^((.*:){4})(\S+)\s+(\S+?):/$1\U$4\E, $3:/' \
/etc/passwd >/tmp/newpasswd
To rewrite only those users who have logged in within the past sixty days according to lastlog, you might use
#! /usr/bin/perl -n
use warnings;
use strict;
use Date::Parse;
my($user) = /^([^:]+):/;
die "$0: $ARGV:$.: no user\n" unless defined $user;
if ($user eq "+") {
next;
}
my $lastlog = (split " ", `lastlog -u "$user" | sed 1d`, 4)[-1];
die "$0: $ARGV:$.: lastlog -u $user failed\n"
unless defined $lastlog;
my $time_t = str2time $lastlog;
# rewrites users who've never logged in
#next if defined $time_t && $time_t < ($^T - 60 * 24 * 60 * 60);
# users who have logged in within the last sixty days
next unless defined $time_t && $time_t >= ($^T - 60 * 24 * 60 * 60);
s/^((.*:){4})(\S+)\s+(\S+?):/$1\U$4\E, $3:/;
print;
as in
$ ./fixusers /etc/passwd >/tmp/fixedusers

How can I split a pipe-separated string in a list?

Here at work, we are working on a newsletter system that our clients can use. As an intern one of my jobs is to help with the smaller pieces of the puzzle. In this case what I need to do is scan the logs of the email server for bounced messages and add the emails and the reason the email bounced to a "bad email database".
The bad emails table has two columns: 'email' and 'reason'
I use the following statement to get the information from the logs and send it to the Perl script
grep " 550 " /var/log/exim/main.log | awk '{print $5 "|" $23 " " $24 " " $25 " " $26 " " $27 " " $28 " " $29 " " $30 " " $31 " " $32 " " $33}' | perl /devl/bademails/getbademails.pl
If you have sugestions on a more efficient awk script, then I would be glad to hear those too but my main focus is the Perl script. The awk pipes "foo#bar.com|reason for bounce" to the Perl script. I want to take in these strings, split them at the | and put the two different parts into their respective columns in the database. Here's what I have:
#!usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbpath = "dbi:mysql:database=system;host=localhost:3306";
my $dbh = DBI->connect($dbpath, "root", "******")
or die "Can't open database: $DBI::errstr";
while(<STDIN>) {
my $line = $_;
my #list = # ? this is where i am confused
for (my($i) = 0; $i < 1; $i++)
{
if (defined($list[$i]))
{
my #val = split('|', $list[$i]);
print "Email: $val[0]\n";
print "Reason: $val[1]";
my $sth = $dbh->prepare(qq{INSERT INTO bademails VALUES('$val[0]', '$val[1]')});
$sth->execute();
$sth->finish();
}
}
}
exit 0;
Something like this would work:
while(<STDIN>) {
my $line = $_;
chomp($line);
my ($email,$reason) = split(/\|/, $line);
print "Email: $email\n";
print "Reason: $reason";
my $sth = $dbh->prepare(qq{INSERT INTO bademails VALUES(?, ?)});
$sth->execute($email, $reason);
$sth->finish();
}
You might find it easier to just do the whole thing in Perl. "next unless / 550 /" could replace the grep and a regex could probably replace the awk.
I'm not sure what you want to put in #list? If the awk pipes one line per entry, you'll have that in $line, and you don't need the for loop on the #list.
That said, if you're going to pipe it into Perl, why bother with the grep and AWK in the first place?
#!/ust/bin/perl -w
use strict;
while (<>) {
next unless / 550 /;
my #tokens = split ' ', $_;
my $addr = $tokens[4];
my $reason = join " ", #tokens[5..$#tokens];
# ... DBI code
}
Side note about the DBI calls: you should really use placeholders so that a "bad email" wouldn't be able to inject SQL into your database.
Have you considered using App::Ack instead? Instead of shelling out to an external program, you can just use Perl instead. Unfortunately, you'll have to read through the ack program code to really get a sense of how to do this, but you should get a more portable program as a result.
Why not forgo the grep and awk and go straight to Perl?
Disclaimer: I have not checked if the following code compiles:
while (<STDIN>) {
next unless /550/; # skips over the rest of the while loop
my #fields = split;
my $email = $fields[4];
my $reason = join(' ', #fields[22..32]);
...
}
EDIT: See #dland's comment for a further optimisation :-)
Hope this helps?
my(#list) = split /\|/, $line;
This will generate more than two entries in #list if you have extra pipe symbols in the tail of the line. To avoid that, use:
$line =~ m/^([^|]+)\|(.*)$/;
my(#list) = ($1, $2);
The dollar in the regex is arguably superfluous, but also documents 'end of line'.