-1

I will create a file every day , I want to remove the line that end character is utc and output to other file in perl, I try to use grep an regexp, but get the error msg as below,

sh: -c: line 0: unexpected EOF while looking for matching `"'
sh: -c: line 1: syntax error: unexpected end of file

the grep code :

system("grep -v \"utc$ \" /doc/$date/before > /doc/$date/after");

the file loos like

config setting
^MMon Nov 13 10:45:52.401 utc   -->the line is I wnat to remove
start configuration...
clock timezone utc 8

Any suggestions? I am more then happy to try anything at this point.

georgetovrea
  • 537
  • 1
  • 8
  • 28

1 Answers1

3

There is no need to go to external tools for such a common task. It involves starting a shell and yet another program, and (doubly) escaping things just right; it is error prone and far less efficient, and inferior in terms of error checking. Why not use Perl in a Perl program?

Read a file and write its lines over to a new file, skipping the ones you don't want. See this post for details, for example.

Here is a quick way using Path::Tiny

use warnings;
use strict;

use Path::Tiny;

my $file     = '...';
my $new_file = '...';

my @new_lines = grep { not /utc\s*$/ } path($file)->lines; 

path($new_file)->spew(@new_lines);

The module's path($file) opens the file and lines returns the list of lines; they are filtered by grep and those that don't end in utc (with possible trailing space) are assigned to @new_lines.

Then the spew method writes those lines to $new_file.

For a couple of (other) ways to "edit" a file using this module see this post.


In a one-liner

perl -ne'print if not /utc\s*$/' file  > new_file

A direct answer may best illustrate (some of) the disadvantages of using external commands.

We need to pass to grep, via shell, particular sequences which would be interpreted by either or both Perl and shell; so they need be escaped correctly

system("grep -v 'utc\\s*\$' $old_file > $new_file");

This works on my system.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Your one liner would be better off with the `-p` flag I think. `perl -pe 'next if m/utc$/' file` should do the trick. – Sobrique Nov 10 '17 at 09:02
  • @Sobrique Huh? What's the purpose of the `next` here? I thought `-p` _always_ prints the line and there's no way to prevent that. Am I wrong? – PerlDuck Nov 10 '17 at 10:00
  • `-p` just inserts a `continue` block that prints `$_`. That can be bypassed by loop control statements like `next`. – Sobrique Nov 10 '17 at 10:03
  • @Sobrique Doesn't work for me. And the [`continue` docs](https://perldoc.perl.org/functions/continue.html) say _"Thus it can be used to increment a loop variable, even when the loop has been continued via the next statement"_. To me that sounds like the continue block is always executed. – PerlDuck Nov 10 '17 at 10:09
  • Hmm, OK. I stand corrected. I'm getting muddled obviously. I'll go and review, because I'm at least _fairly_ sure there's a 'skip' option with `-p`. I'll go and try and figure out what it is. – Sobrique Nov 10 '17 at 10:10
  • @zdim,I have try to using you one-liner,but it's still cannot remove the line that the end is utc – georgetovrea Nov 13 '17 at 03:07
  • @georgetovrea huh ... are you sure it's `utc` at the end? Any spaces or "funny" (non-printable) characters before the end? Is this UNIX -- can you try `\z` instead of `$`? – zdim Nov 13 '17 at 03:09
  • @zdim,I have follow as you said , $ perl -ne'print if not /utc\z/' file > new_file , but it's still cannot remove the line , by the way ,I have update my question – georgetovrea Nov 13 '17 at 03:16
  • @georgetovrea OK, let's try with some possible extra spaces: `/utc\s*$/`. (The shown file doesn't show whether there is anything _after_ `utc`; in your `grep` you have a space after `$`, which I don't know what to think of.) – zdim Nov 13 '17 at 03:30
  • @georgetovrea There could also be a loose `\r` (carriage return) so also try `/utc[\r\s]*$/` – zdim Nov 13 '17 at 03:42
  • @zdim, perl -ne'print if not /cst\s*$/' file > new_file work success, but I try to use grep in my perl, It's fail, system("grep -v \"utc\s*$\" /file/$year/$datetime > /file/$year/new"); please can you help me ? – georgetovrea Nov 13 '17 at 03:55
  • @georgetovrea This is exactly the point of my post -- it is much harder to do it that way, and any such solution is much less reliable. If there is something you don't like in this post there is also a link to a very simple approach. (I updated the code to the optional space, `/utc\s*$/`.) – zdim Nov 13 '17 at 04:52
  • @georgetovrea Having said that, I will add an attempt to fix your `grep` to the post shortly. I will let you know when I update. – zdim Nov 13 '17 at 04:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/158823/discussion-between-georgetovrea-and-zdim). – georgetovrea Nov 13 '17 at 04:53
  • @georgetovrea I have to go now, will be back in an hour and will update the post (and will let you know). Then we can chat as well if you wish... – zdim Nov 13 '17 at 04:54
  • @georgetovrea I added a direct answer to the end of my post. – zdim Nov 13 '17 at 07:10
  • @zdim,It's still cannot get I want, It's cannot work for me system("grep -Ev 'utc\\s*\$' $file > $new"); – georgetovrea Nov 13 '17 at 07:40
  • @georgetovrea That's another problem with running external commands out of Perl; there is no way for me to say why it works or not on your system. I tested what I posted, with a file like yours and it works. (That runs out of a Perl script, right?) But our systems and `grep`s may differ in many ways. Why don't you just use Perl? See that other post if anything here gives you trouble. – zdim Nov 13 '17 at 07:44
  • @georgetovrea That last thing in my answer, `system(...)`, is supposed to be a line in a Perl script. The `$old_file` and `$new_file` should be file names from your code. – zdim Nov 13 '17 at 08:08