10

I'd like to do the following in raku on windows

raku -n -e ".say if /mydatabegin/;" *.file

Failed to open file C:\..\*.file: Invalid argument

The glob isn't interpreted as a glob. I assume that's because windows requires your programs to do the globbing yourself? So is there a pre-processing directive or function or even a switch I might have missed or redirect or something that allows the glob to be expanded while keeping the simplicity of the -n (or -p) and -e switches?

Obviously, I can change it to a full program by removing the -n (or -p), just using -e to specify a main, and loop on the glob results. But I really like -n.

PS. I'm literally just learning raku, and was surprised this didn't work out of the box. So examples of full programs with easy syntax work also. But I really like -n..

Edit: re @chenyf

raku -e ".say for $*ARGFILES" *.file

Same error. Related:

raku -e ".say for $*ARGFILES.lines" *.file

Same error.

raku -e "use IO::Glob; .say for glob('*.file')"

Worked as expected! Expanding:

raku -e "use IO::Glob; .say for glob('*.file').lines"

No such method 'lines' for invocant of type 'IO::Glob'

Getting closer - perhaps expanding on this is a good enough workaround. But returning to one line glory attempts:

raku -e "use IO::Glob; .say for glob($*ARGFILES)" test.file

Cannot resolve caller glob(IO::ArgFiles:D); none of these signatures match.

Ok - let's retreat back to the safety of strings:

raku -e "use IO::Glob; .say for glob($*ARGFILES.Str)" test.file

Yes! SO..:

raku -e "use IO::Glob; .say for glob($*ARGFILES.Str).lines" test.file

No such method 'lines' for invocant of type 'IO::Glob'

I clearly need to read more of the manual. But let's retreat a little and see if my use case works:

raku -e "use IO::Glob; .say for glob($*ARGFILES.Str)" *.file

Failed to open file C:\..\*.file: Invalid argument

The same error I started off with. Could this just be a raku on windows error?

Edit:

raku -MIO::Glob -e "my @files = (map { glob($_).dir }, @*ARGS).flat; for @files -> $file { say $_ for $file.lines }" *file *file2 *5

I have three sets of files. I can almost live with this solution - except for some reason the lines are being printed with "s

Any ideas on shortening, and getting rid of the quotes?

EDIT Working around the auto-globbing of the $*ARGFILES variable:

raku -MIO::Glob -n -e "BEGIN { @*ARGS = (map { glob($_) }, @*ARGS).flat }; .say" *.file *.file2

This has the advantage of still looking like the original one liner; it uses -n! It just had to do the globbing that seems to be a bug when $*ARGFILES is created.

raku -MIO::Glob -e "BEGIN { @*ARGS = (map { glob($_) }, @*ARGS).flat }; .say for $*ARGFILES.lines" *.file *.file2

Converting to $*ARGFILES.lines above shows that $*ARGFILES gets its values from @*ARGS dynamically.

EDIT

lastly, it turns out the glob function doesn't work with directories, at least on windows (the documentation has an example that simply doesn't work).

#Example from https://github.com/zostay/raku-IO-Glob
for glob("src/core/*.pm") -> $file { say ~$file }

#mine that doesn't work
raku -MIO::Glob -e "for glob('..\*.file') -> $file { say ~$file }"

#mine that does work.
raku -MIO::Glob -e "for glob('*.file').dir('..') -> $file { say ~$file }"

#And therefore the final modification of the script above:
raku -MIO::Glob -e "BEGIN { @*ARGS = (map { glob(.IO.basename).dir(.IO.dirname) }, @*ARGS).flat };  .say for $*ARGFILES.lines" ..\*.file
Gerard ONeill
  • 3,914
  • 39
  • 25
  • 2
    perhaps `$*ARGFILES` helps, for example: https://andrewshitov.com/2018/12/24/reading-file-contents-with-argfiles-in-perl-6/ – chenyf Sep 30 '21 at 05:42
  • Edited the answer to show testing with $*ARGFILES. – Gerard ONeill Sep 30 '21 at 12:24
  • 1
    BTW, thanks chenyf for the link - I did check it out, and his examples studiously avoided multiple files, much less globbing. I did leave a question in the comments. – Gerard ONeill Sep 30 '21 at 13:16
  • Both `raku -e '$*ARGFILES.lines.say;' *.txt` and `raku -e '.say for $*ARGFILES.lines;' *.txt` work on my older MacOS system. The first one-liner returns each `*.txt` file on a single line, and the second one-liner gives a linewise return indistinguishable from `cat` (each returning five `*.txt`files from my current directory). – jubilatious1 Oct 01 '21 at 04:37
  • 1
    Maybe a Window's (directory) permissions error? Or improperly specified filepath? The code `raku -ne ".say if /mydatabegin/;" *.txt` works fine here (note initial `.` dot). – jubilatious1 Oct 01 '21 at 05:10
  • The one-liner `raku -e '.lines.say for $*ARGFILES;' *.txt` works on MacOS. Guessing `raku -MIO::Glob -e ".lines.say for glob($*ARGFILES.Str)" *.file` _should_ work for you. Maybe try slash-escaping the "*" in `$*ARGFILES`: either `$\*ARGFILES` or `$/*ARGFILES` ? – jubilatious1 Oct 01 '21 at 06:05
  • @jubilatious Thanks for trying out these things. As you noted, my original formulation works on other OSs., and it definitely isn't a permissions error. So I am looking for a workaround while I learn / use it; I'll file a bug if one isn't there. And the * part of $*ARGFILES has meaning in Raku - its a dynamic variable. – Gerard ONeill Oct 01 '21 at 15:16
  • @GerardONeill Yes, the two characters at the beginning of `$*ARGFILES` both have meaning. I'm just wondering if you have some sort of Window's Service that inspects your command line input? For some Raku one-liners when entering them on the Vim command line I've had to escape things like stray `!` exclamation marks. Wondering if you're having a similar issue with `*` on Windows? Also, wondering if you could tell us you Windows version? – jubilatious1 Oct 01 '21 at 17:16
  • Windows 10. And if you look at my last example, I unpack the @*ARGS variable, and there is the unglobbed.. glob. This can be applied to the function glob and the files show up fine. And on top of that I get the same error when using @*ARGFILES, which is *after* the command line. At some point you have to conclude its a windows version of Raku bug. – Gerard ONeill Oct 01 '21 at 20:49
  • We'd love for you to have success with Raku and welcome bug reports. I think it's important to know if this is Windows WSL (WSL1 or WSL2) or Powershell. (AFAIK they are two distinct systems). Nota bene, if you look up `glob` in Raku's "P5-to-P6" glossary, there seems to have been a design decision NOT to implement `glob` in Raku. – jubilatious1 Oct 06 '21 at 14:26
  • I showed in one of my answer sthat `dir()` works fine on MacOS without any extra modules (e.g. `IO::Glob`). Also, the last line of code you presented contains a call to `.dir`, which appears to be doing all the heavy-lifting. Note Raku doesn't have a `glob()` function in core so what the `IO::Glob` module appears to do is use/abuse `dir()` calls to simulate Perl5's `glob` function. Hopefully the `IO::Glob` module developer will chime in. – jubilatious1 Oct 06 '21 at 14:31
  • perl nee raku is soo good at finding and implementing short cuts and shorter cuts - it'd be a shame to relugate windows users to handling what seems to be a very intuitive default use of the command line. Especially since the solution is pretty easy to implement. Windows programs don't have things globbed for them by the shell, sure. But that doesn't mean that windows users don't expect a program to implement it. – Gerard ONeill Oct 06 '21 at 20:43

2 Answers2

4

My rudimentary understanding of file-globbing is that the shell handles that--and since it seems that you're on Windows, all bets may be off. The only exception may be if you're using WSL Windows-Subsystem-for-Linux, which should give you a more Unix/Linux-like experience:

https://learn.microsoft.com/en-us/windows/wsl/about

According to the Microsoft document below Windows has two built-in shells, CMD.exe and Powershell (I believe the WSL shell above is optional):

"Command Shell Overview"

"Windows has two command shells: The Command shell and PowerShell. Each shell is a software program that provides direct communication between you and the operating system or application, providing an environment to automate IT operations."

https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/windows-commands


Below is what works on a fairly old bash/MacOS system. With the -ne (non-autoprinting) command line flag:

raku -ne 'print $_ ~ "\n";'
raku -ne 'say $_ ;' 
raku -ne '.say;'

With the -pe (autoprinting) command line flag:

raku -pe 'print "";'
raku -pe ''

I literally just printed the contents of five (5) *.txt files using the commands above. The -e flag always ends your command-line flag cluster, but can be combined (see the -M flag below for an exception).

Now because you're on Windows, you may have to swap "" double-quotes with '' single-quotes, (and vice versa!). But hopefully that gets you started. [Any further problems and you may have to specify the full-path to your Rakudo executible].

If you'd like Raku to handle the directory-munging for you, there's Raku's dir() command, which can be used in conjunction with its :test adverb (parameter). Compare below when in the afore-mentioned directory with five (5) *.txt text files, (remove .elems calls to print actual text):

raku -e '.lines.elems.say for dir(test => / \.txt $ /);'
14
16
34
1
16

VERSUS:

raku -e '.lines.elems.say for $*ARGFILES;' *.txt
81

https://docs.raku.org/routine/dir


ADDENDUM: My inspection of the Raku P5-to-P6 Glossary suggests that the designers deliberately left Perl5's glob function out of Perl6/Raku, opting instead for a more-powerful built-in dir() routine. Links below:

https://docs.raku.org/language/5to6-perlfunc#index-entry-glob_-_perlfunc https://docs.raku.org/routine/dir

Raku module IO::Glob is non-core (and..I've never tried it). But if you're working at the command line and want to load a module, you use (for example) -MIO::Glob followed by -e, -ne, or -pe, etc. (Also, no need to incorporate a use IO::Glob; line within your Raku one-liner, it's loaded via -M already).

See Raku one-liner examples with the Txt::CSV module and/or XML module at the links below:

https://unix.stackexchange.com/search?q=%5Bcsv%5D+Raku
https://unix.stackexchange.com/search?q=%5Bxml%5D+Raku

Follow additional Windows CMD.exe discussion/resolution below:

https://github.com/rakudo/rakudo/issues/4550

jubilatious1
  • 1,999
  • 10
  • 18
  • The rather odd looking `raku -pe 'print "";'` is included above because it can be easily modified to _spice-up_ your files with line numbers: `raku -pe 'print ++$,"\t";' *.txt` OR `raku -pe 'print ++$~"\t";' *.txt` – jubilatious1 Oct 01 '21 at 05:42
  • Thanks @jubilatious1 First,, it has to be on windows so I'm still solving the windows issue. Second, I don't want to change the code once the script / program is written, which is why I'm using *.files. – Gerard ONeill Oct 01 '21 at 16:24
  • Like your ".lines.say @files" construct - however it's printing parens and quotes. ARRG. – Gerard ONeill Oct 01 '21 at 16:26
  • @GerardONeill I've been answering common text-processing questions in Raku over at [unix.se], simply do a search for "Raku" in the box at the top. If occasionally you see a Raku one-liner answer with extra parens and quotes, delete any calls to `.raku` and/or `.perl`. I'll include those calls once in a while to visualize `\n` newlines, etc. – jubilatious1 Oct 01 '21 at 17:04
  • FYI - .put doesn't print the parens, in contrast to .say. Something about the Str method vs the gist method. – Gerard ONeill Oct 02 '21 at 15:24
  • I'll generally use `.put` for all output to be passed on, unless I'm still working in Raku (and want a quick peek), then I'll use `.say`. (Also, calls to `.say` have the advantage of handling `Nil` returns more gracefully than `.put`). – jubilatious1 Oct 02 '21 at 18:03
  • Thanks for your input in my "feature request" on github. Back to this - could you show an example of how to glob using Glob? Or even using dir - but using the globbing syntax if it supports it? Feel free to make that different answer. – Gerard ONeill Oct 10 '21 at 18:10
  • Nevermind - the glob method seems to not work with windows directories. I added the final solution to the main post. – Gerard ONeill Oct 10 '21 at 19:49
1

The general answer - Its not a bug. Windows programs have to deal with their own globbing if they want it. Making it work in the raku executable makes sense to me; it removes platform specific surprises, and makes one-liners easier.

But others didn't see it that way, and there is an easy enough solution - create your own module so that the code can remain consistent and be called relatively simply.

Here's a module for starters. There is room to add things like

  • a switch for making a successful match mandatory
  • a switch to indicate that a failed glob should stay in the @*ARGS variable
  • a switch to only glob after it.
  • a routine to apply the globbing instead of automatically doing it. This would allow you to remove the non-file switches
  • gather each glob into its own list (perhaps with a switch).

The module:

unit module CLGlob:ver<1.0>:auth<pureabsolute>;

use IO::Glob;

@*ARGS = map { .Str }, (map { glob(.IO.basename).dir(.IO.dirname) }, @*ARGS ).flat;

Note: the expression in the second map can be simplified when the glob function works for windows with directories.

Note: I convert each element to .Str to make @*ARGS consistent. The $*ARGFILES worked without doing that so some processing can be saved if you'll never look at the @*ARGS again.

Finally, the end result:

raku -MCLGlob -ne ".say" ..\*.file ..\*.file2

Yay.

Gerard ONeill
  • 3,914
  • 39
  • 25