What is written in one file write to another - perl

I have one file, and I need everything that is written in some time frame to that file to be written to a second file.
What is the best way to do so? Open some thread that will read the file and do so ?
Any ideas ?

The tee utility might be what you're looking for:
#! /usr/bin/perl
use warnings;
use strict;
my #files = qw/ file1 file2 /;
open my $fh, "| tee #files >/dev/null"
or die "$0: start tee failed: $!";
print $fh "$_\n" for map int rand 10, 1 .. 5;
close $fh or warn "$0: close tee: $!";
Sample run:
$ ./write-both
$ cat file1
0
7
5
8
2
$ cat file2
0
7
5
8
2

Sounds like a job for tail -f or the poor man's tail -f emulation.

Sounds like a job for File::Copy

Related

awk usage in perl scripting

Hi am writing a script which its need to grep the 6th column of the output using awk command but am getting other output.
What is the exact syntax in perl to extract 6th column using awk?
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
my $filesystem=`df -h |grep -i dev|grep -vE '^Filesystem|proc|none|udev|tmpfs'`;
print "(\"$filesystem\"|awk '{print \$6}')"
Output :
7831c1c4be8c% ./test.pl
("/dev/disk1 112Gi 43Gi 69Gi 39% 11227595 18084674 38% /
devfs 183Ki 183Ki 0Bi 100% 634 0 100% /dev
"|awk '{print $6}')%
Am trying to remove the % how it can be done ?
7831c1c4be8c% cat test.pl
#!/usr/bin/perl
use warnings;
use strict;
open my $FS, q(df -h |) or die $!;
while (<$FS>) {
print +(split)[4], "\n"
if /dev/i and not /devfs/;
}
7831c1c4be8c% ./test.pl
40%
You don't need awk inside Perl.
#!/usr/bin/perl
use warnings;
use strict;
open my $FS, '-|', q(df -h) or die $!;
while (<$FS>) {
print +(split)[5], "\n"
if /dev/i and not /^Filesystem|proc|none|udev|tmpfs/;
}
as the previous answer says, you don't need awk or grep system calls in perl. however, I will tell you that one reason your code isn't working is because you never made the awk system call. print does not execute the system call. you would have to use system() to execute it.
anyway fwiw you can also do what you want in a one-liner like so:
df -h | perl -lnae 'next if $F[0] =~ /regex/; print $F[5]'

Use sed to read file into middle of line

File A contains this text (assume that "alpha" and "bravo" are arbitrarily long chunks on a single line):
alpha {FOO} bravo
File B contains an arbitrary amount of text, including all sorts of wacky characters.
I want to replace the string "{FOO}" in file A with the contents of file B. Using sed's 'r' command as follows doesn't work because it inserts the content of file B after that line:
cat A | sed -e "/{FOO}/r B"
Is there any way, using sed, to end up with a file that consists of:
alpha [the contents of B] bravo
? If it would be easier to do this with, say, perl, that's fine too. But I know even less about perl than I do about sed. ;)
Short perl solution:
FOO="$( cat replacement.txt )" perl -pe's/\{FOO\}/$ENV{FOO}/g'
This will work with any character except 00 NUL. If you have to deal with binary files, you can use:
perl -pe'
BEGIN {
open(my $fh, "<", shift(#ARGV)) or die $!;
local $/;
$FOO = <$fh>;
}
s/\{FOO\}/$FOO/g
' replacement.txt
Usage:
perl -i~ -pe'...' file # In-place edit with backup
perl -i -pe'...' file # In-place edit without backup
perl -pe'...' file.in >file.out # Read from named file(s)
perl -pe'...' <file.in >file.out # Read from STDIN
If this is Bash on Linux, this seems to work:
sed -i "s/{FOO}/$(cat B.txt)/g" A.txt
This will directly edit the file A.txt - they don't have to be .txt files, I just added those in to make it more obvious.
As #ikegami points out, this will have problems if there any / in the file - also any \ will probably be ignored.
So in an attempt to solve that, you should be able to use:
sed -i "s/\//%2F/g" B.txt
sed -i "s/{FOO}/$(cat B.txt)/g" A.txt
sed -i "s/%2F/\//g" B.txt
You won't have to use %2F though.
#!/usr/bin/perl
use strict;
use warnings;
open my $fh_a, '<', $ARGV[0] or die "Failed to open $ARGV[0] for reading";
open my $fh_b, '<', $ARGV[1] or die "Failed to open $ARGV[1] for reading";
my $a;
my $b;
{
local $/;
$a = <$fh_a>;
$b = <$fh_b>;
}
close $fh_a;
close $fh_b;
$a =~ s/{FOO}/$b/;
print $a;
As long as your files both fit in memory twice, this should be fine. The local $/; puts the I/O system into 'slurp' mode, reading the whole file in a single operation.
Usage:
perl replace_foo.pl fileA fileB

Difference in behaviour between Perl versions

I have the following script
#!/usr/bin/perl -w
print "sample\n";
syswrite STDIN, "script";
my $input = <STDIN>;
print "$input";
This scripts executes properly in Perl 5.8.8 version giving the following output:
sample
script
However when executed in Perl 5.14.2 it gives the following error:
Filehandle STDIN opened only for input at ./sample.pl line 5.
What has been changed between the Perl versions?
Presumably, you're asking to replicate the 5.8.8 behaviour. I'm not sure how much sense makes to do so, but you can create a Perl read-write handle attached to the same file descriptor as follows:
$ perl -e'
open(my $fh, "+>&=", 0) or die $!;
print($fh "foo\n") or die $!;
' >/dev/null
foo
or
$ perl -e'
{
open(my $fh, "+>&=", 0) or die $!;
close(STDIN);
open(STDIN, "+>&=", $fh) or die $!;
}
print(STDIN "foo\n") or die $!;
' >/dev/null
foo

Perl - One liner file edit: "perl -n -i.bak -e "print unless /^$id$,/" $filetoopena;" Not working

I cannot get this to work.
#!/usr/bin/perl -w
use strict;
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
my $id='123456';
my $filetoopen = '/home/user/public/somefile.txt';
file contains:
123456
234564
364899
437373
So...
A bunch of other subs and code
if(-s $filetoopen){
perl -n -i.bak -e "print unless /^$id$,/" $filetoopen;
}
I need to remove the line that matches $id from file $filetoopen
But, I don't want script to "crash" if $id is not in $filetoopen either.
This is in a .pl scripts sub, not being run from command line.
I think I am close but, after reading for hours here, I had to resort to posting the question.
Will this even work in a script?
I tried TIE with success but, I need to know alternatively how to do this without TIE::FILE.
When I tried I got the error:
syntax error at mylearningcurve.pl line 456, near "bak -e "
Thanks for teaching this old dog...
First of all (this is not the cause of your problem) $, (aka $OUTPUT_FIELD_SEPARATOR) defaults to undef, I'm not sure why you are using it in the regex. I have a feeling the comma was a typo.
It's unclear if you are calling this from a shell script or from Perl?
If from Perl, you should not call a nested Perl interpreter at all.
If the file is small, slurp it in and print:
use File::Slurp;
my #lines = read_file($filename);
write_file($filename, grep { ! /^$id$/ } #lines);
If the file is large, read line by line as a filter.
use File::Copy;
move($filename, "$filename.old") or die "Can not rename: $!\n";
open(my $fh_old, "<", "$filename.old") or die "Can not open $filename.old: $!\n";
open(my $fh, ">", $filename) or die "Can not open $filename: $!\n";
while my $line (<$fh_old>) {
next if $line =~ /^id$/;
print $fh $_;
}
close($fh_old);
close($fh);
If from a shell script, this worked for me:
$ cat x1
123456
234564
364899
437373
$ perl -n -i.bak -e "print unless /^$id$/" x1
$ cat x1
234564
364899
437373
if(-s $filetoopen){
perl -n -i.bak -e "print unless /^$id$,/" $filetoopen;
}
I'm not at all sure what you expect this to do. You can't just put a command line program in the middle of Perl code. You need to use system to call an external program. And Perl is just an external program like any other.
if(-s $filetoopen){
system('perl', '-n -i.bak -e "print unless /^$id$,/"', $filetoopen);
}
The functionality of the -i command line argument can be accessed via $^I.
local #ARGV = $filetoopen;
local $^I = '.bak';
local $_;
while (<>) {
print if !/^$id$/;
}

Perl: Redirect STDOUT to two files

How can I redirect the STDOUT stream to two files (duplicates) within my Perl script? Currently I am just streaming into a single log file:
open(STDOUT, ">$out_file") or die "Can't open $out_file: $!\n";
What do I have to change? Thx.
You can also use IO::Tee.
use strict;
use warnings;
use IO::Tee;
open(my $fh1,">","tee1") or die $!;
open(my $fh2,">","tee2") or die $!;
my $tee=IO::Tee->new($fh1,$fh2);
select $tee; #This makes $tee the default handle.
print "Hey!\n"; #Because of the select, you don't have to do print $tee "Hey!\n"
And yes, the output works:
> cat tee1
Hey!
> cat tee2
Hey!
Use the tee PerlIO layer.
use PerlIO::Util;
*STDOUT->push_layer(tee => "/tmp/bar");
print "data\n";
$ perl tee_script.pl > /tmp/foo
$ cat /tmp/foo
data
$ cat /tmp/bar
data
File::Tee provides the functionality you need.
use File::Tee qw( tee );
tee(STDOUT, '>', 'stdout.txt');
If you're using a Unix-like system, use the tee utility.
$ perl -le 'print "Hello, world"' | tee /tmp/foo /tmp/bar
Hello, world
$ cat /tmp/foo /tmp/bar
Hello, world
Hello, world
To set up this duplication from within your program, set up a pipe from your STDOUT to an external tee process. Passing "|-" to open makes this easy to do.
#! /usr/bin/env perl
use strict;
use warnings;
my #copies = qw( /tmp/foo /tmp/bar );
open STDOUT, "|-", "tee", #copies or die "$0: tee failed: $!";
print "Hello, world!\n";
close STDOUT or warn "$0: close: $!";
Demo:
$ ./stdout-copies-demo
Hello, world!
$ cat /tmp/foo /tmp/bar
Hello, world!
Hello, world!