How do I use raku -e and -n with multiple file glob
Asked Answered
V

2

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
Vatic answered 30/9, 2021 at 0:30 Comment(12)
perhaps $*ARGFILES helps, for example: andrewshitov.com/2018/12/24/…Nephograph
Edited the answer to show testing with $*ARGFILES.Vatic
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.Vatic
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 *.txtfiles from my current directory).Tragedy
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).Tragedy
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 ?Tragedy
@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.Vatic
@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?Tragedy
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.Vatic
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.Tragedy
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.Tragedy
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.Vatic
V
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.

Vatic answered 10/10, 2021 at 21:55 Comment(2)
You've edited your OP to state that example code for the IO::Glob module isn't working. Have you opened an issue on Github in the appropriate repo?Tragedy
No not yet. I admit I spent too much time on this already. But I will eventually.Vatic
T
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

Tragedy answered 1/10, 2021 at 0:52 Comment(8)
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";' *.txtTragedy
Thanks @Tragedy 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.Vatic
Like your ".lines.say @files" construct - however it's printing parens and quotes. ARRG.Vatic
@GerardONeill I've been answering common text-processing questions in Raku over at Unix & Linux, 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.Tragedy
FYI - .put doesn't print the parens, in contrast to .say. Something about the Str method vs the gist method.Vatic
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).Tragedy
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.Vatic
Nevermind - the glob method seems to not work with windows directories. I added the final solution to the main post.Vatic
V
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.

Vatic answered 10/10, 2021 at 21:55 Comment(2)
You've edited your OP to state that example code for the IO::Glob module isn't working. Have you opened an issue on Github in the appropriate repo?Tragedy
No not yet. I admit I spent too much time on this already. But I will eventually.Vatic

© 2022 - 2024 — McMap. All rights reserved.