skjeiu has asked for the wisdom of the Perl Monks concerning the following question:
Hello all,
In the below script the grep if statement works but the while loop is not printing anything.
If I comment the if statement, the while loop works as I would expect it to, that is, printing the content of the file.
#!/bin/perl use strict; use warnings; my $input_file = 'input.txt'; open(my $in_file,'<',$input_file) or die "Can not open file $input_fil +e for reading: $!.\n"; if (grep{/Friday/} $in_file) { print "Match\n"; } else { print "No match\n"; } print "Before\n"; while (<$in_file>) { print; } print "After\n"; close($in_file);
My ultimate gall here is to search for the string 'Friday' in the file 'input.txt', if 'Friday' is missing, add'Friday' to 'input.txt on line 2.
The content of 'input.txt is:
Monday
Sunday
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: While loop not printing anything after using grep (updated)
by haukex (Archbishop) on Dec 29, 2020 at 11:33 UTC | |
Welcome to the Monastery, skjeiu. Please note the advice in How do I post a question effectively?: At the very least, please use <code> tags to format your code. Also, you posted this question in the incorrect section; I have moved it to Seekers of Perl Wisdom for you. if (grep{/Friday/} $in_file) This is unlikely to be the code you're actually running, as this would try to match the pattern against the filehandle stored in the variable, instead of the contents of the file. Instead, I assume your code looks like if (grep{/Friday/} <$in_file>), as that would explain the problem you're having: in list context, the <$filehandle> operator (see I/O Operators in perlop) reads all the lines from the file and returns them, this is what grep is matching against, but once you've read everything from the file, the second <> operator won't return anything. There are several possible ways to work around this. For example, if the file will always be small enough to comfortably fit into memory, you could read the contents of the file into an array and then loop over that with grep and foreach as many times as you like. Another approach would be to integrate everything into one while loop, i.e. move the /Friday/ match there. If you could explain more about what your input files look like and what you're actually trying to do, I'm sure we could suggest some approaches fitting to your application. Update: My ultimate gall here is to search for the string 'Friday' in the file 'input.txt', if 'Friday' is missing, add'Friday' to 'input.txt on line 2. The content of 'input.txt is: Monday Sunday Sorry, I must have missed this on the first read because of the formatting (unless you edited your node? please mark your updates when editing). Anyway, please also use <code> tags for sample input and output. Since you haven't shown your expected output, it's a little hard to guess what you mean - do you want the contents of the second line to be replaced, the string "Friday" to be appended, or do you want a new line to be inserted? And does the length of the input file matter at all? Anyway, your approach is a good start. Assuming your files aren't too long, then one way to do what you want would be to rewind the input file to the beginning with seek: insert seek $in_file, 0, 0 or die "seek: $!"; after the grep and before the while to start reading the file from the beginning again. You should then be able to use this as the basis to do what you want. (Note that this does have one disadvantage: it doesn't reset the special line counter variable $., in case you were planning to use that in your while loop - it is possible to do this manually via $.=0 right after the seek.) | [reply] [d/l] [select] |
|
Re: While loop not printing anything after using grep
by LanX (Saint) on Dec 29, 2020 at 11:33 UTC | |
Please put <code> </code> tags around your code and data! From what I can decipher
Cheers Rolf | [reply] [d/l] |
by skjeiu (Novice) on Dec 29, 2020 at 16:25 UTC | |
First of all, I'd like to apologize for not using the HTML tags.
I'm learning HTML together with Perl, so thanks for bearing with me during my learning curve and for the warm welcome.
| [reply] [d/l] [select] |
by haukex (Archbishop) on Dec 29, 2020 at 16:36 UTC | |
Else clause is alwayse executed, why (should only be executed if 'Friday' is not found in 'input.txt')? You haven't used the <> operator, as in grep {/Monday/} <$in_file>. if ($. == 1) { If you use seek, you need to reset $. as I showed. Empty block in the if statement; how can I avoid this? You can invert the condition in the if, as in if ( !( ... ) ) or if ( not ... ) (just be aware of Operator Precedence and Associativity). Perl also offers the equivalent unless (...) {...}, but IMHO sometimes if (not ... is still more readable than unless (.... As far as I understand it, in Perl you first need to create a new file with the desired output then rename that file to the original name. So in my case that would be with the rename function Yes, that is one way to do it - if you don't mind me plugging my own module, see File::Replace. | [reply] [d/l] [select] |
by eyepopslikeamosquito (Archbishop) on Dec 29, 2020 at 19:41 UTC | |
As far as I understand it, in Perl you first need to create a new file with the desired output then rename that file to the original name. So in my case that would be with the rename function ... If there is a better way to get what I want, I'm open to suggestion. You are on the right track. For some history and solutions to this fascinating and tricky problem see this old node Re-runnably editing a file in place, especially its "See Also" section. | [reply] |
|
Re: While loop not printing anything after using grep
by shmem (Chancellor) on Dec 30, 2020 at 00:24 UTC | |
My ultimate gall here is to search for the string 'Friday' in the file 'input.txt', if 'Friday' is missing, add'Friday' to 'input.txt on line 2. Just do it! But first a code review.
A filehandle is a filehandle, not the content of the file. In the while-loop you read the file line-wise, but you don't do anything but printing what you've read. Also, you don't look for line number 2. The line number read is stored in the perl special variable $. which you should test, if you want to add (append?) the string "Friday" to line two. Fix:
If you want inplace edit, you can place the directives for that after the first line of the script and omit the file opening and the while()-loop as well as the print statement. See perlrun. Read that.
Hope that helps (HTH).
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] [d/l] [select] |
by skjeiu (Novice) on Dec 30, 2020 at 14:13 UTC | |
| [reply] [d/l] [select] |
by AnomalousMonk (Archbishop) on Dec 31, 2020 at 00:10 UTC | |
open(my $in_file,'<', ...) or die "Can not open ... for writing: $!.\n";
<nit> Give a man a fish: <%-{-{-{-< | [reply] [d/l] [select] |
|
Re: While loop not printing anything after using grep
by Marshall (Canon) on Dec 29, 2020 at 23:07 UTC | |
I would open your input file, read it into a hash. Make sure that Friday is in the hash (whether it was in the input file or not). Sort the hash according the European sort order and print to a file. The safest way to do this is to output to a temp file. Then delete original file. Then rename temp file to the original file name. The purpose of that is to make sure that the data is not "lost" if program crashes while updating its input file. Below, I use the already opened DATA file handle and just print to stdout. I think you know how to open files for reading or writing - so I leave that part to you. The code does do some "unnecessary" work if Friday is already there. But the flow is simple with no "if" statements. Update: I guess if you want to know if Friday was there or not? The ternary op is good for that, but longer winded code is just fine and will execute just as fast. Also, you should be aware that file operations are "expensive" both for CPU and execution time wise. It is rare to seek to the beginning of a file and re-read it because reading the file from disk is so "expensive". A seek() will flush all buffers and you are basically starting from scratch again (Data has to be re-read from disk). For a small file, read it into memory - do necessary edits and then write it back out. "Small" is a relative term, but your file certainly qualifies! Another Update: The general approach of your code is seriously flawed in terms of extensibility (make "Friday" the second line if its not already the second line). What happens if the input file has "Tuesday" as the second line? Then Friday needs to go on the 3rd line! The above code would do that. | [reply] [d/l] [select] |