LINUX.ORG.RU

[tcl] Выполнить скрипт вне текущего контекста

 


0

1

Есть у меня скрипт. Совершенно непредсказуемый и сохранённый в переменной. Нужно его выполнить и забрать его stdout в переменную.

Но как бы я не изголялся, скрипт выполняется в текущем контексте.

Например есть такой скрипт:

puts «Привет, я скрипт tcl»

Сохранен он в переменной $scriptText.

Делаю так:

set resultCode [catch { eval $scriptText } scriptResult]

Но вместо того, чтобы получить в переменной scriptResult Привет, я скрипт tcl, я получаю вывод этой строки в stdout.

Что можно придумать?

★★★★★

> Совершенно непредсказуемый и сохранённый в переменной.

Очень предсказуемо, так и должно быть. В scriptResult у тебя результат, который возвращает puts.

твой скрипт соответствует примерно такому:

set scriptResult [puts Привет]

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

1 Я имею ввиду, что содержимое скрипта непредсказуемо.

2 Поведение же действительно предсказуемо, но обратное тому, что я хочу, и что ты описываешь - в scriptResult конечно у меня ничего нет. А вот вывод на stdout есть...

Suntechnic ★★★★★
() автор топика
Ответ на: комментарий от Suntechnic

catch нужен для ловли ошибок, анализа кодов возврата break, continue и всяких return, чтобы типа метапрограммировать.

Думаю, тебе надо выучить TCL, а уже потом хотеть.

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

Сам понял что сказал? При чем здесь catch?

Давай подумаем вместе? Может затем, чтобы поймать ошибку выполняемого скрипта?

Suntechnic ★★★★★
() автор топика
Ответ на: комментарий от Suntechnic

> При чем здесь catch?

причем здесь stdout? с чего ты решил, что должен catch перенаправить вывод puts в переменную?

Вот тебе бред на коленке за основу, который до ума доведешь сам.

[code] proc dirty_catch {script varName} { upvar $varName var # сохраняем старый puts rename puts _orig_puts # делаем новый puts proc puts {s} { return $s } set code [catch {uplevel 1 $script} var] # восстанавливаем старый puts rename puts {} rename _orig_puts puts return $code }

set script {puts «Hello, world!»}

set code [dirty_catch $script var]

puts $var puts $code [/code]

Только хорошо подумай зачем тебе stdout перенаправлять в переменную? Ничего хорошего от этого не жди.

anonymous
()
Ответ на: комментарий от anonymous
proc dirty_catch {script varName} {
 upvar $varName var
 # сохраняем старый puts
 rename puts _orig_puts
 # делаем новый puts
 proc puts {s} { return $s }
 set code [catch {uplevel 1 $script} var]
 # восстанавливаем старый puts
 rename puts {}
 rename _orig_puts puts

 return $code
}

set script {puts "Hello, world!"}

set code [dirty_catch $script var]

puts $var puts $code
anonymous
()
Ответ на: комментарий от anonymous

с чего ты решил, что должен catch перенаправить вывод puts в переменную?

Почему это я так решил? Я же вроде написал, как это работает, и что это предсказуемо.

За код спасибо - сейчас попробую разобраться.

Suntechnic ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Э... Мне все таки надо перенаправить stdout в переменную, а не подменить puts - содержимое скрипта неизвестно зараннее. А если там запись в файл? Несколько puts? И т.д.

Suntechnic ★★★★★
() автор топика
Ответ на: комментарий от Suntechnic

Конечно я могу закрыть тупо stdout и сделать свой, но как его открыть снова?

Тогда POSIX-спцеифично с TclX: fork, dup, за подробностями к Стивенсу. И, кстати, лучше бы выполнять этот скрипт в отдельном интерпретаторе (man interp), возможно безопасном.

Begemoth ★★★★★
()

Есть у меня скрипт. Совершенно непредсказуемый и сохранённый в переменной

есть два варианта : 1) скрипт исполнять в отдельном «безопасном» интерпретаторе (man n iterp), переопределив puts и собирая его вывод 2) исполнить скрипт через exec.

set s {puts "hello" }
set cmd "interp eval \[ interp create -safe \] {$s}"
# путь к интерпретатору можно вычислить
if [ catch {exec -ignorestderr /usr/bin/tclsh << $s} ret ] {
  ...
} else {
  ...
}

MKuznetsov ★★★★★
()
Ответ на: комментарий от Begemoth

И, кстати, лучше бы выполнять этот скрипт в отдельном интерпретаторе (man interp), возможно безопасном.

Ну в реальности я так и делаю.

interp create tmptcl

# тут выполнение:
tmptcl eval  "$scriptText"

interp delete tmptcl
Просто счел лишней подробностью. А небезопасный просто на время отладки.

Про TclX подумаю...

Suntechnic ★★★★★
() автор топика
Ответ на: комментарий от Suntechnic

Я пока еще размышляю над предложением перепределить puts - что-то типа:

tmptcl eval {rename puts _orig_puts}
                    # делаем новый puts
                    tmptcl eval {proc puts {args} {
                        if {[llength $args] > 1} {
                            puts $args
                        } else {
                            append ::scriptResult $args
                        }
                        return "$scriptResult"
                    }}
                    set resultCode [catch { tmptcl eval "$scriptText" } scriptResult]
Но идея мне не нравится... Есть еще одно - запасное решение у меня, связанное с особенностями реализации, но тоже кривое. Почему бы не иметь нечто типа:

redefining stdout var

;)

Suntechnic ★★★★★
() автор топика

В Clojure это делается в одну строчку.

(defmacro my-print [& forms] `(binding [*out* my-writer] ~@forms))

Выбирайте правильные языки для своих задач.

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

Почему бы не иметь нечто типа:

redefining stdout var

А еще лучше: redirect stdout var { script... }

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

язык правильный - просто я еще не имею с ним большого опыта, и многие очевидные решения сразу не приходят в голову, хотя я вроде и знал про них...

Почему никто, особенно из советчиков учить Tcl, не посоветовал, тупо создать еще один интерпретатор, но безопасный? У него тупо нет доступа к stdout!

Suntechnic ★★★★★
() автор топика

Используйте spawn. Получить stdout можно по spawn_id

bcormorant
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.