1

I am using glob to read *_file.log exist in certain directory but below code seems not working.

Example path having these few files:

/user/home/xpath/logs/
abc_file.log
def_file.log
x.txt

Code:

my $home = "/user/home";

if (glob ("${home}/xpath/logs/\*_file.log")){
print "work\n";
}
else {
print "something wrong\n"
}
walker
  • 157
  • 5
  • 1
    `if glob` makes no sense. See docs for how `glob` behaves in scalar context. – ikegami Mar 03 '23 at 13:51
  • 1
    The slash in `"\*"` is useless since `"\*"` is the same thing as `"*"`. But worse, the slash is extremely confusing because it looks like you're trying to prevent the `*` from having its normal effect. Get rid of that useless, confusing slash. – ikegami Mar 03 '23 at 13:53
  • @ikegami, i am thinking to use `-e` in `if`, but not sure if it works for wildcard * character `if (-e "$home/xpath/logs/\*_file.log")` – walker Mar 03 '23 at 14:09
  • i thought the slash required for `*` ? – walker Mar 03 '23 at 14:10
  • Only `$`, `@`, `\ ` and the delimiter(s) are special in double-quoted strings. No other non-word character needs to be escaped. /// You also don't want to escape it for the glob, which would be `"\\*"` (to produce `\*`) because you want `*` to match "anything", not just `*`. – ikegami Mar 03 '23 at 14:17
  • The `*` does not need to be escaped in a double-quoted string. Escaping it does no harm in this case, but simpler code is easier to debug. The curly brackets around `home` are also not needed because it is not followed by a word character. That said, when I run your script it prints `"work\n"`. The only modification I made was to replace your value of `$home` with `getcwd` because I'm running a variant of Unix that does not use `/home` and does not let me muck around with the root easily. – user20284150 Mar 03 '23 at 16:40
  • @user20284150, The OP's code is indeed buggy. As mentioned earlier, it makes no sense to use `if glob` – ikegami Mar 03 '23 at 18:31
  • @ikegami. so you mean `if (-e "$xpath/logs/*_file.log")` works? – walker Mar 06 '23 at 06:25
  • Where did you get that from? I didn't talk about `stat` (`-e`) at all. It expects a file name, so that checks if there's a file named `.../logs/*_file.log` – ikegami Mar 06 '23 at 06:44

2 Answers2

2

As explained, all matches from the glob need be retrieved. Given that glob is a "very" global creature with long memory better use it in list context to exhaust its return right away, and then investigate it. One way to do what the question tries

if ( my @entries = glob "$home/xpath/logs/*_file.log" ) {
    print "file: $_\n" for @entries;
}
else {
    print "No such entries found\n"
}

I cleaned up some of unneeded syntax elements.

A few comments

  • If you really mean to use the home directory, the environment variable is available in the %ENV hash, as $ENV{HOME} (or see File::HomeDir)

  • The feature say adds a newline to prints making them far nicer. For example, we can say

    say for @files;
    

    It need be enabled by use feature qw(say);, or by use-ing a Perl version 5.10 or newer, or by use-ing some of a number of packages/frameworks that enable it

  • Once you reach for glob also have a look at File::Glob, which provides a drop-in replacement that handles spaces in names nicely.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • `$ENV{HOME}` will be the current user's home dir instead of a hard coded one, which may be a notable difference. As I recall you can also use `~/` to emulate that, but I suppose that might be OS specific. – TLP Mar 04 '23 at 11:08
  • @TLP "_which may be a notable difference_" -- indeed, which is why I introduce that by saying "_If you really mean to use the home directory_". The `~` doesn't work as home -- it will "work" though -- as a string :). So once it's used for a filesystem entry it will look for an entry named that way. From what I recall there are modules that interpret it as home directory. (I don't think it's worthed. The `~` is a shell thing and I think it's best left that way.) – zdim Mar 04 '23 at 18:03
  • "_interpret it as home directory_" -- `glob` and `File::Glob` do (but I wouldn't recommend using that) – zdim Mar 05 '23 at 05:57
1

Using if glob is wrong.

$ ls
a.log
b.log

$ perl -Mv5.14 -e'
   sub test { say "$_[0]: ", glob( $_[0] ) ? "at least one match" : "no matches" }
   test( "*.log" );
   test( "*.txt" );
   test( "*.log" );
'
*.log: at least one match      # Correct
*.txt: at least one match      # Incorrect
*.log: no matches              # Incorrect and a contradiction

glob in scalar context acts as an iterator.

  • The first time it's called, it returns the first match.
  • The second time it's called, it returns the second match (regardless of the argument).
  • When it's called after all results have been returned, it returns undef.

If you're going to use glob, you're going to have to get all the results, not just the first.

For example, the following uses the scalar( () = ... ) trick to call glob in list context (getting all the results) and count the number of items it returns.

$ perl -Mv5.14 -e'
   sub test { say "$_[0]: ", ( () = glob( $_[0] ) ) ? "at least one match" : "no matches" }
   test( "*.log" );
   test( "*.txt" );
   test( "*.log" );
'
*.log: at least one match
*.txt: no matches
*.log: at least one match
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Somewhere, someone (maybe you) listed all the Perl functions with state. I can't remember where I saw that. – brian d foy Mar 03 '23 at 22:57
  • @brian d foy, If so, I don't remember. I can just think of `glob`, scalar `..`, scalar `...` right now. Indirectly, m//g sets `pos($_)` (where `$_` is the bound var). – ikegami Mar 03 '23 at 23:16
  • any other suggestion other than glob? – walker Mar 06 '23 at 06:45
  • Why do you want to avoid glob? I can't suggest solutions to a problem you didn't identify! Not sure the comments is the place to ask new questions, though... – ikegami Mar 06 '23 at 07:12