Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
like image 757
Gerard ONeill Avatar asked Sep 30 '21 00:09

Gerard ONeill


2 Answers

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

like image 100
jubilatious1 Avatar answered Oct 18 '22 00:10

jubilatious1


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.

like image 1
Gerard ONeill Avatar answered Oct 18 '22 01:10

Gerard ONeill