write filename as first line in a txt file + text around it / osx perl - perl

I'm complete newby to perl and I hope you can help me with this line of code.
The issue is related to this one here, but it doesn't quite answer my question. I tried looking for it, but I just get more confused.
I have a txt input (batch) that I want to have a filename printed in the first line, but wrapped in a specific text. I am converting these files later into html and so I would like the .name to have "<div class="head">" printed before and "</div>" afterwards.
Here is the code I have and it works to print the name:
perl -i -pe 'BEGIN{undef $/;} s/^/$ARGV\n/' `find . -name '*.txt'`
I run this by first navigating to the directory where all the files are.
example of filename: 2016-05-20_18.32.08.txt
the files are plane text poetry and in the output i get:
./2016-05-20_18.32.08.txt
in the first line.
I tried something like this:
perl -i -pe 'BEGIN{undef $/;} s/^/$ARGV\n/' `find . -name ‘“<div class="head”>”’*.txt’”</div>”’
but of course it doesn't work. it just give me a >
I need to add the arguments in this part s/^/$ARGV\n/' but i already have troubles defining it.
Can you help pls?
In addition, the filename prints with ./ in the beginning, is there a simple way to exclude that?

perl -i -pe 'BEGIN{undef $/;} s/^/<div class=head> $ARGV <\/div>\n<div class=poem>\n/; s/$/\n<\/div>/' `find . -name '*.txt'`
This should work. But if you are new to perl, I suggest you try working with scripts rather than one-liners.
The -i flag will edit the file inplace. so if you want a html file, remove -i and redirect to another .html file.

I'm sure there is a more elegant way of doing it, but something like this will work
#!/usr/bin/perl
undef $/;
for (#ARGV){
open($fh,$_);
$content=<$fh>;
close($fh);
open($fh,">$_");
print $fh "<div class=\"head\">$_</head>\n$content";
close($fh)
}

Related

Can't exec "/bin/sh": Argument list too long at perl

Below command in perl script is failing if there are many file in #file_to_tar, but it is working fine if we have less files in the array (#file_to_tar).
my $tar_command = "cd $ProcVars->{dropbox_dir}; tar -cvzf SmartMiles.$ProcVars->{batch_nb}.tar.gz -P #file_to_tar --remove-files";
Can some one please help me to fix the issue.
The best way is pass a very long list of files to tar is using the --from-file option:
tar -czf myarchive.tar.gz -P --from-file=$filelist --remove-files
You can also make it read the list of files from standard input by using --from-file=-
See https://www.gnu.org/software/tar/manual/html_node/files.html for more information.
You might consider, rather than calling a shell command, using the Archive::Tar Perl module instead. In particular, the "create_archive" method. This also would sidestep any potential problems arising from special characters or whitespace in the file names being interpreted by the shell your Perl code is invoking.
it is working now.
my $file_location = "$ProcVars->{dropbox_dir}/Archive_Files.csv"; open(DATA, ">$file_location") or die $!; foreach $a (#file_to_tar) { print DATA "$a\n"; } close DATA;
my $tar_command = "cd $ProcVars->{\dropbox_dir};tar -czf ABC.$ProcVars->{batch_nb}.tar.gz -P --files-from $file_location --remove-files";

Inserting headers into multiple files

I found some command line with Perl that inserts headers into my files without going through the tedious process of inserting them one by one. Can someone walk me through the Perl aspect of this command line? I'm new to this and can't seem to find the right explanations for what I wrote.
cat header.txt | perl -0 -i -pe 'BEGIN{$h = <STDIN>}; print $h' 1*
-e
rather than provide a script in a xxxx.pl file, provide it on the command line
-p
makes it iterate over filename arguments somewhat like sed but also prints the contents of $_ at the end of the script.
the two above are combined in -pe
-i
indicate you want to edit the file in place and write the output to the same file. In practice, Perl renames the input file and reads from this renamed version while writing to a new file with the original name
-0
redefines the end of record character (\n by default) so that you can read the entire input file as a single line
1*
is the command line argument to your script, so I guess you are modifying any file with a name that starts with 1 (you could have used *.c, or whatever depending on the type of files you are trying to modify)
print $h
prints the variable $h that is the "main" of your script. if it was initialized with the content of the header file (the intent of this one-liner) then it will print the header file
BEGIN{ some code here }
this is stuff you execute before the script starts. this is where I'm stumped. this doesn't seem like valid perl code
so basically:
this will supposedly slurp the entire header file (because of -0) in the BEGIN block and store it in the variable $h
iterate over all the files specified by the wildcards at the end of the command line
for each file: print the header (print $h) then print hte file itself (because of -pe)
so it's equivalent to spelling the script out:
$h = gets content of the entire header file
while (<>){ #loop implied by -pe, iterates over all the 1* files
# the main contents of the "-e" script are inserted below as part of executing -pe
print h$; #print the header we saved
print $_; # implied by -pe, and since we are using -0, this prints the entire content in one shot
# end of the "-e" script. again it was a single print $h statement, the second print is implied by -pe
}
It's a bit hard to explain, take a look at the perlrun documentation for details (run man perlrun).
This is not 100% complete explanation because I don;t think the BEGIN block is right. I tried it on my ubuntu machine and it complained about its syntax too
Here's something similar, with an explanation. The program in the question doesn't run on my mac.
I needed to add the #nullable disable directive to the top of all my csharp files as part of migrating to nullable reference types.
perl -w -i -p -0777 -e 's/^/#nullable disable\n\n/' $(find . -iname '*.cs')
-w enable warnings
-i edit files in place
-p read each file block by block, printing each block after applying a perl expression. the default block size is one line
-0777 changes the default block size to the entire file
-e the perl expression to execute
The final argument uses shell command substitution to create a list of files. It passes that list of file paths to the perl command. The find command searches for files that end in .cs.
The perl program is a single substitution command. It matches the very beginning of the block and replaces (prepends, really) with "#nullable disable" and a couple new-lines.

How to delete a bunch of lines in perl (adapting a known one-liner)?

context: I'm a beginner in Perl and struggling, please be patient, thanks.
the question: there is a one-liner that seems to do the job I want (in a cygwin console it does fine on my test file). So now I would need to turn it into a script, but I can't manage that unfortunately.
The one-liner in question is provided in the answer by Aki here Delete lines in perl
perl -ne 'print unless /HELLO/../GOODBYE/' <file_name>
Namely I would like to have a script that opens my file "test.dat" and removes the lines between some strings HELLO and GOODBYE. Here is what I tried and which fails (the path is fine for cygwin):
#!/bin/perl
use strict;
use warnings;
open (THEFILE, "+<test.dat") || die "error opening";
my $line;
while ($line =<THEFILE>){
next if /hello/../goodbye/;
print THEFILE $line;
}
close (THEFILE);
Many thanks in advance!
Your one-liner is equivalent to the following
while (<>) {
print unless /HELLO/../GOODBYE/;
}
Your code does something quite different. You should not attempt to read and write to the same file handle, that usually does not do what you think. When you want to quickly edit a file, you can use the -i "in-place edit" switch:
perl -ni -e 'print unless /HELLO/../GOODBYE/' file
Do note that changes to the file are irreversible, so you should make backups. You can use the backup option for that switch, e.g. -i.bak, but be aware that it is not flawless, as running the same command twice will still overwrite your backup (by saving to the same file name twice).
The simplest and safest way to do it, IMO, is to simply use shell redirection
perl script.pl file.txt > newfile.txt
While using the script file I showed at the top.

How to process a file in Perl - handling variables from Bash: disappearing "." character

I need to find the age of a file in seconds but when I give my Perl line:
perl -e ' my #st=stat("$name"); print time - $st[9];'
a variable from Bash with "." in the filename, it won't find the file and prints the systime instead. Otherwise, if I create a file with a name such "something", it works perfectly well.
I tried escaping the character with \ but that does not work. I really do not know Perl and I don't know how to figure this out.
Thanks for the help!
(Please, do not suggest any BASH only workarounds, lot of stuff here is old, truncated...)
EDIT: I found a workaround but I can't post an answer to my own question since I am a newbie, so here it is:
So I finally found a workaround.
You export the variable in Bash:
export name=".file.txt"
and then call it from the Perl like this: $ENV{name}
and it works just fine.
EDIT 2:
The export idea was just a temporary solution. Better one is using single quotes as perreal suggested.
You need to put the shell variable out of the single quotes:
perl -e ' my #st=stat("'"$name"'"); print time - $st[9];'
or pass it through arguments:
perl -se 'my #st=stat("$name"); print time - $st[9];' -- -name="$name"

How do I run a Perl script on multiple input files with the same extension?

How do I run a Perl script on multiple input files with the same extension?
perl scriptname.pl file.aspx
I'm looking to have it run for all aspx files in the current directory
Thanks!
In your Perl file,
my #files = <*.aspx>;
for $file (#files) {
# do something.
}
The <*.aspx> is called a glob.
you can pass those files to perl with wildcard
in your script
foreach (#ARGV){
print "file: $_\n";
# open your file here...
#..do something
# close your file
}
on command line
$ perl myscript.pl *.aspx
You can use glob explicitly, to use shell parameters without depending to much on the shell behaviour.
for my $file ( map {glob($_)} #ARGV ) {
print $file, "\n";
};
You may need to control the possibility of a filename duplicate with more than one parameter expanded.
For a simple one-liner with -n or -p, you want
perl -i~ -pe 's/foo/bar/' *.aspx
The -i~ says to modify each target file in place, and leave the original as a backup with an ~ suffix added to the file name. (Omit the suffix to not leave a backup. But if you are still learning or experimenting, that's a bad idea; removing the backups when you're done is a much smaller hassle than restoring the originals from a backup if you mess something up.)
If your Perl code is too complex for a one-liner (or just useful enough to be reusable) obviously replace -e '# your code here' with scriptname.pl ... though then maybe refactor scriptname.pl so that it accepts a list of file name arguments, and simply use scriptname.pl *.aspx to run it on all *.aspx files in the current directory.
If you need to recurse a directory structure and find all files with a particular naming pattern, the find utility is useful.
find . -name '*.aspx' -exec perl -pi~ -e 's/foo/bar/' {} +
If your find does not support -exec ... + try with -exec ... \; though it will be slower and launch more processes (one per file you find instead of as few as possible to process all the files).
To only scan some directories, replace . (which names the current directory) with a space-separated list of the directories to examine, or even use find to find the directories themselves (and then perhaps explore -execdir for doing something in each directory that find selects with your complex, intricate, business-critical, maybe secret list of find option predicates).
Maybe also explore find2perl to do this directory recursion natively in Perl.
If you are on Linux machine, you could try something like this.
for i in `ls /tmp/*.aspx`; do perl scriptname.pl $i; done
For example to handle perl scriptname.pl *.aspx *.asp
In linux: The shell expands wildcards, so the perl can simply be
for (#ARGV) {
operation($_); # do something with each file
}
Windows doesn't expand wildcards so expand the wildcards in each argument in perl as follows. The for loop then processes each file in the same way as above
for (map {glob} #ARGV) {
operation($_); # do something with each file
}
For example, this will print the expanded list under Windows
print "$_\n" for(map {glob} #ARGV);
You can also pass the path where you have your aspx files and read them one by one.
#!/usr/bin/perl -w
use strict;
my $path = shift;
my #files = split/\n/, `ls *.aspx`;
foreach my $file (#files) {
do something...
}