LINUX.ORG.RU

Несколько вопросов по Raku

 , , ,


0

1

Здравствуйте, я хочу задать несколько вопросов. Гугление и поиск на raku.guide/ru/ не дали ответов.

1 [решено]. Как записать только вывод команды shell в переменную?

Пробовал так:

#!/usr/bin/env rakudo
use v6

my Str $EDITOR = "emacs -nw";
my Str $tmp = shell "mktemp";
my Rat $VERSION = 0.1;

print $tmp; print "\n";

Вывод:

/tmp/tmp.XXXXXXXXXX
Type check failed in assignment to $tmp; expected Str but got Proc (Proc.new(in => IO::Pipe, ...)
  in block <unit> at rakutoolz-raku line 5

Пробовал тип Any, после имени tmp-файла появляется Proc<XXXXXXXXX>.

Как это убрать?

2 [решено]. Как получить аргументы командной строки, переданные скрипту?

Например, я передаю программе script.pl6 аргументы first second third. Хотелось бы получать их как по отдельности строками, так и массивом.

3 [решено]. Меньше ли места занимает программа в ОЗУ, если использовать переменные с фиксированным типом (Str, Num) вместо переменных с «плавающим» типом (не знаю, как правильно назвать), которые были единственными в Perl и если да, то насколько?

upd. 4. Не понимаю, как дописать что-либо в файл? Не записать, а дописать, что-то типа commmand >> file в Sh?

1. А просто shell «mktemp» что говорит?
2. В массив @*ARGS
3. Явный тип по идее экономит память. Но как это в перл6 не знаю.

gnu_linux ()

shell даёт тебе объект запущенного процесса, у которого можно спросить exitcode, pid, in/out/err и всё остальное, поэтому присвоение в Str явно не работает. Как записать только вывод:

my $mktemp-proc = shell 'mktemp', :out;
my $output-returned = $mktemp-proc.out.slurp(:close);

(ну или с явными скобками и именованным параметром так, суть одинаковая:)

my $mktemp-proc = shell('mktemp', out => True);
my $output-returned = $mktemp-proc.out.slurp(:close);

Где именованный параметр out у shell включает запись stdout, после чего берёшь этот объект, обращаешься к его методу out, который даёт тебе IO::Pipe, т.е. поток ввода-вывода, и slurp-ишь всё его содержимое ради всего святого с параметром :close.

  1. Как получить аргументы командной строки, переданные скрипту?

Обращаешься к массиву строк в переменной @*ARGS.

  1. Меньше ли места занимает программа в ОЗУ, если использовать переменные с фиксированным типом (Str, Num) вместо переменных с «плавающим» типом (не знаю, как правильно назвать), которые были единственными в Perl и если да, то насколько?

Нет. Ради памяти это вообще не самый подходящий язык, тут динамики выше крыши, JIT, рантаймовые оптимизации и всё это не бесплатное. GC делает свою работу и жить можно, но если нужно считать байты памяти и экономить на спичках, то это не тот профиль.

Lilly ()
Последнее исправление: Lilly (всего исправлений: 1)
  1. Есть разные варианты, включая асинхронный, но самый простой будет выглядеть как-то так:

    run('cat', '--', 'some-file', :out).out.slurp(:close)
    

    У shell вроде должен быть аналогичный синтаксис. Но зачем привлекать шелл, когда этого можно не делать? Что такого от шелла нужно, чего нельзя в пределах Raku сделать (run вероятно должно быть достаточно)? Привлекать шелл — это всегда небезопасная затея с подводными камнями, т.к. по факту ты эвалюируешь код из строки, и если там есть какая-то интерполяция, то будь готов отстрелить себе ногу или заиметь проблемы с безопасностью.

    Помимо прочего можно получить список строк, без символов окончания строки (см. chomp) например вот так:

    run('cat', '--', 'some-file', :out).out.slurp(:close).chomp.lines
    
  2. Тебе доступен особенный список (особенный, потому что там звёздочка в имени): @*ARGS, весь тебе список аргументов. Но тебе скорее всего такой «голый» парсинг аргументов и не нужен, т.к. в Raku встроен свой генератор парсинга на основе описания и сигнатур типов аргументов для функции MAIN, см. https://docs.raku.org/routine/MAIN (для позиционных аргументов используй , *@positional-args).

  3. Точно могу сказать только то, что типы там вводились совсем не для экономии ресурсов. Плюс на фоне общего потребления ОЗУ MoarVM ты этой байтовой (или даже битовой) разницы точно не заметишь. Там на пук выделяются десятки мегабайт, на их фоне пара байт тебе погоды не сделает.

unclechu ()
Ответ на: комментарий от unclechu

Всё правильно написано, только добавлю, что в @*ARGS ничего «особенного» нет, такое описание добавляет лишней магии, что ли. Просто UPPERCASE показывает, что эта штука предоставляется реализацией (как с друзьями EVAL и подобным, любой капс - почти всегда про поставку из реализации), а * даёт динамическую видимость (но тут без разницы, т.к. переменная регистрируется в PROCESS:: и видно будет везде).

Lilly ()
Последнее исправление: Lilly (всего исправлений: 1)
Ответ на: комментарий от unclechu

1:

Но зачем привлекать шелл, когда этого можно не делать? Что такого от шелла нужно, чего нельзя в пределах Raku сделать mktemp

А так спасибо, попробую.

2: спасибо, не знал, что так можно.

3: окей, так я и знал – один геморрой.

zagatov_lev ()
Ответ на: комментарий от gnu_linux
  1. Просто shell "mktemp" выдает только /tmp/tmp.XXXXXXXXXX.
  2. Спасибо, только мне ниже посоветовали более удобный способ с функцией MAIN.
  3. Экономит, но, как оказалось, совсем немного по сравнению с общим потреблением.
zagatov_lev ()
Ответ на: комментарий от zagatov_lev

Типизация — это не гемморой, а способ заставить инструмент исключать часть глупых ошибок автоматически и многократно, не возлагая эту ответственность на и без того ограниченного человека. Важнее не то, что может делать код, а то, чего он делать не может.

Возможность экономить байтики при использовании типизации — это скорее случайный побочный эффект.

unclechu ()
Последнее исправление: unclechu (всего исправлений: 1)

Еще вопрос

@Lilly @unclechu, спасибо, вы помогли. Но у меня появился еще один вопрос по мере написания программы, не поможете?

В Sh работа с файлами происходит просто (command > file, command >> file, cat file > otherfile) . В Raku я нашел запись в файл с помощью spurt, но она перезаписывает файл. А мне нужно дописать, в Sh это делается перенаправлением >>, command >> file. Как это сделать в Raku? Неужели нужно велосипедить?

zagatov_lev ()
Последнее исправление: zagatov_lev (всего исправлений: 1)
Ответ на: Еще вопрос от zagatov_lev

Пример: 'file'.IO.open(:a).say: 'foo'
Можешь на место 'foo' вывод команды подставить. Но имей в виду что это будет не тоже самое что на баше. Тебе сначала нужно весь вывод команды считать строку целиком (slurp-нуть), а потом также разом дописать в файл. Если хочешь делать это построково, как в баше, то будет выглядеть несколько иначе.

См. примеры в документации https://docs.raku.org/type/Proc:
Тут показаны примеры с пайпами, почти как в баше (см. примеры после этого абзаца):

Piping several commands is easy too. To achieve the equivalent of the pipe echo "Hello, world" | cat -n in Raku, and capture the output from the second command, you can do

unclechu ()
Ответ на: комментарий от unclechu

Хотя тебе тут не пайпы нужны. Можешь написать что-то такое:

my \file = 'file'.IO.open: :a;
my \p = run qw<printf %s\n foo bar baz>, :out;
loop { $_ = p.out.get; last if p.out.eof; file.say: $_ }
p.out.close;
file.close;

Или так:

my \file = 'file'.IO.open: :a;
my \p = run qw<printf %s\n foo bar baz>, :out;

given p.out {
	loop { my \x = .get; last if .eof; file.say: x }
	.close;
	file.close;
}
unclechu ()
Последнее исправление: unclechu (всего исправлений: 1)
Ответ на: комментарий от unclechu

Спасибо за ответы, обычно в питоне используют конструкцию

with open(file) as f:
    lines = f.readlines()

lines = ["line1", "line2", "line3"....]

что бы не закрывать файл явно, а питон закроет файл и почистит как надо память если операции выйдут за пределы конструкции (и выглядит в итоге как-то декларативно что-ли).

^^^ По вот этому типу, в псевдокоде, можно ли так? :

open "file", :r -> f {
    line-42-of-file = f.line(42) #получить доступ к 42 строке
}
f.close #Ошибка, объекта f не существует
Cirno ()
Последнее исправление: Cirno (всего исправлений: 1)
Ответ на: комментарий от Cirno

Для того что вы привели на Python вам не нужен контроль ресурсов, достаточно:

file.IO.lines

Можно вывести на экран:

file.IO.lines.join("\n").say

Насчёт подобной конструкции для контроля ресурсов не припомню, но возможно что-то такое есть. Так или иначе при выходе из области видимости имени с файловым дескриптором сборщик мусора его соберёт и файл закроется. Но это всё-таки немного другое.

unclechu ()
Ответ на: комментарий от unclechu

Так или иначе стоит обратить внимание на заметку из документации:

Note that garbage collection is not guaranteed to happen, so you must NOT rely on DESTROY for closing the handles you write to and instead close them yourself. Programs that open a lot of files should close the handles explicitly as well, regardless of whether they were open for writing, since too many files might get opened before garbage collection happens and the no longer used handles get closed.

unclechu ()
Ответ на: комментарий от unclechu

Для построчной обработки без ручного контроля ресурсов также можно использовать циклы (пример из документации):

for 'huge-csv'.IO.lines -> $line {
    # Do something with $line 
}

Также есть with (по идее работает как given только с булевой проверкой значения), но он не отвечает за контроль ресурсов, просто значение помещает внутрь блока как default variable (файл по прежнему нужно закрывать, пример из документации):

with IO::CatHandle.new: @bunch-of-handles {
    say .readchars: 42;
    .close; # we are done; close all the open handles 
}
unclechu ()
Ответ на: комментарий от unclechu

Также можно взглянуть на LEAVE (который сработает и в случае ошибки, и обязательно в конце выполнения блока, но его можно поставить в начале).

Пример:

with 'file'.IO.open: :r {
	LEAVE { .close; 'closed'.note }
	.get.say; # first line
	.get.say; # second line
}
unclechu ()
Последнее исправление: unclechu (всего исправлений: 2)
Ответ на: комментарий от unclechu

Второй пример уже то, что нужно, спасибо за разные подходы.

Cirno ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей