Специальные символы — различия между версиями

Материал из Egghelp.ru - TCL/TK Eggdrop Wiki
Перейти к: навигация, поиск
(Новая: Eggdrop scripts that choke Many eggdrop scripts choke on nicks, usernames, or text, that contain characters that have a special meaning in Tcl, especially [, ], {, }, ", \, and $. For th...)
 
Строка 1: Строка 1:
Eggdrop scripts that choke
+
Многие [[TCL|скрипты]] [[Eggdrop]] не правильно обрабатывают имена, иденты или текст, содержащие символы, имеющие особо значание в TCL - '''[''' ''']''' '''{''' '''}''' '''"''' '''\''' '''$'''. По этой причине на некоторых [[IRC]] каналах банятся ники, содержащие символы [ или {.
Many eggdrop scripts choke on nicks, usernames, or text, that contain characters that have a special meaning in Tcl, especially [, ], {, }, ", \, and $. For that reason, some IRC channels ban nicks that contain [ or {.
+
  
Yet the problem can be avoided completely by writing correct Tcl code. There are two golden rules that must be followed.
+
Этих проблем можно избежать, используя некоторые приемы при написании Tcl скриптов. Есть два золотых правила, которыми не следует пренебрегать при написании скриптов.
  
The first golden rule of Tcl
+
===Первое золотое правило TCL===
Include all of the splits and joins that are necessary to convert between lists and strings.
+
'''Используйте все [[split]] и [[join]], необходимые для преобразования между [[list|списками]] и строками.'''
  
It is best to regard lists and strings as if they were two separate types, putting into our code all the splits and joins that are necessary to convert between them. If we do that, the interpreter will take the necessary actions with any special Tcl characters that may be present. We gain a big bonus that we do not need to understand how lists are represented (a topic that is not very straightforward when Tcl special characters are present) - instead, we simply let the interpreter take care of that aspect for us.
+
Лучше всего работать со списками и строками, как если бы они были двумя разными типами данных - использовать все необходимые операции преобразования. Если использовать все необходимые операции - интерпретатор сам обработает все специальные символы, присутствующие в переменных. В этом случае нам не обязательно иметь полное представление о списках TCL, вместо нас об этом позаботится сам интерпретатор.
  
For example, one eggdrop script that I was given contained three lines similar to the following:
+
Например, рассмотрим скрипт, содержащий следующие строки
  
bind dcc o tell do_dcc_tell
+
  bind dcc o tell do_dcc_tell
proc do_dcc_tell { hand idx arg } {
+
  proc do_dcc_tell { hand idx arg } {
set arg [lrange $arg 0 0]
+
  set arg [lrange $arg 0 0]
  
When the procedure is called, the first word of arg is a nick. The script choked on nicks that contained [ or {. It was the third line that caused the problem.
+
При вызове процедуры первым параметром является ник. Скрипт выдаст ошибку, если ник содержит [ или {. Ошибка будет вызвана при обработке третьей строки скрипта.
  
The script writer could have written instead
+
Вместо третьей строки можно так же использовать
set arg [lindex $arg 0]
+
  set arg [lindex $arg 0]
but that also can lead to failure if the nick contains Tcl special characters.
+
но и это приведет к ошибкам, если ник содержит другие специальные символы Tcl.
  
To see why the above code is incorrect, we can read in tcl-commands.doc that the value of arg will be "the argument string". Yet lrange and lindex should be applied only to lists. Therefore, before we apply lrange or lindex, we must convert the string to a list, which we can do by using split. So to correct the first version we change that line to
+
Чтобы понять почему приведенный код ошибочек - давайте заглянем в tcl-commands.doc. Там говорится, что значение переменной ''arg'' это "строковый параметр". К тому же [[lrange]] и [[lindex]] должны использоваться только со [[list|списками]]. Поэтому прежде чем использовать [[lrange]] или [[lindex]], мы должны преобразовать строковое значение в список, используя для этого соответствующую функцию [[split]]. Чтобы исправить первую версию скрипта мы должны заменить третью строку на следующую
set arg [lrange [split $arg] 0 0]
+
  set arg [lrange [split $arg] 0 0]
  
However, that is still incorrect. The rest of the procedure expects arg to be a string, not a list, yet lrange returns a list (in this case of one element). Therefore we must use join to convert it to a string. Our final, correct, version is
+
Однако и этот вариант не идеален. Во всей последующей процедуре ''arg'' будет по-прежнему восприниматься как строка, а не список, к тому же [[lrange]] вернет нам список (пусть и состоящий из одного элемента). Завершающим штрихом будет использование функции [[join]] для преобразования списка в строку. И так, наша последняя и наиболее правильная версия
set arg [join [lrange [split $arg] 0 0]]
+
  set arg [join [lrange [split $arg] 0 0]]
  
Alternatively (and, as it turns out, more neatly), we could correct the other version, i.e.
+
Так же мы можем доработать альтернативный вариант (к тому же, он более краткий)
set arg [lindex $arg 0]
+
  set arg [lindex $arg 0]
in the same way, giving
+
используя тот же подход
set arg [lindex [split $arg] 0]
+
  set arg [lindex [split $arg] 0]
  
In this case we do not need join, since lindex returns the nick that the user typed, which is therefore already a string.
+
В этом случае нам нет необходимости использовать [[join]], так как [[lindex]] вернет ник, указанный пользователем, который уже является строкой.
  
A point worthy of note is that if the last argument of do_dcc_tell were called args instead of arg, then the situation would be different. If a Tcl procedure has a last argument with the special name args, that argument behaves differently from arguments with any other name.
+
Стоит отметить, что если бы мы использовать последний аргумент процедуры ''do_dcc_tell'' с именем ''args'', а не ''arg'', ситуация была бы иная. Если процедура [[Tcl]] в качестве последнего аргумента использует имя ''args'', аргумент обрабатывается иначе, чем остальные аргументы.
  
Here is another example eggdrop script. This one is correctly written.
+
Вот пример скрипта для Eggdrop. Написан он абсолютно правильно.
  
bind pub B|B !orderfor pub_orderfor
+
  bind pub B|B !orderfor pub_orderfor
proc pub_orderfor {nick uhost hand chan rest} {
+
  proc pub_orderfor {nick uhost hand chan rest} {
  global botnick
+
    global botnick
  set rest [split $rest]
+
    set rest [split $rest]
  if {[llength $rest] < 2} {
+
    if {[llength $rest] < 2} {
    putnotc $nick "Syntax: !orderfor\
+
      putnotc $nick "Syntax: !orderfor\
    <nick to order something for>\
+
      <nick to order something for>\
    <what to order>"
+
      <what to order>"
 +
      return 0
 +
    }
 +
    putchan $chan "\001ACTION sets\
 +
    [join [lrange $rest 1 end]] in front of\
 +
    [lindex $rest 0], compliments of $nick.\001"
 
     return 0
 
     return 0
 
   }
 
   }
  putchan $chan "\001ACTION sets\
 
  [join [lrange $rest 1 end]] in front of\
 
  [lindex $rest 0], compliments of $nick.\001"
 
  return 0
 
}
 
  
Note that putnotc and putchan are defined in alltools.tcl which is distributed with eggdrop.
+
Замечу, что процедуры ''putnotc'' и ''putchan'' определены в ''alltools.tcl'', который распространяется вместе с дистрибутивом бота.
  
The original version (that I downloaded from a script library) did not contain
+
Исходная версия (которую я взял из скрипта) не содержала
set rest [split $rest]
+
  set rest [split $rest]
but did contain
+
вместо этого в ней была строка
set cmd [string tolower [lindex $rest 0]]
+
  set cmd [string tolower [lindex $rest 0]]
where it is clear that we are making the mistake of applying lindex to a string.
+
просмотров которую становится понятно, что здесь ошибочно применяется [[lindex]] к строковому параметру.
  
The original downloaded script would sometimes give incorrect replies if the input contained Tcl special characters.
+
Оригинал скрипта периодически будет выдавать ошибки, если ''$rest'' будет содержать особые символы [[TCL]].
  
The second golden rule of Tcl
+
===Второе золотое правило TCL===
If the script contains a command that is to be evaluated when a timer expires, it must be in the correct form.
+
'''Если скрипт содержит команды, которые будут выполнены по истечении таймера, эти команды должны быть оформлены правильно.'''
  
The list command provides a convenient way of putting the command into the correct form. It inserts backslashes and braces as appropriate to cope with any Tcl special characters that may be present.
+
[[List]] позволяет оформлять такие такие команды в надлежащей форме. Она добавляет бэкслеши (обратные слеши, backslashes) и фигурные скобки (braces) чтобы избежать проблем с любыми специальными символами TCL, которые могут встретиться в команде.
  
Here is a procedure where the second golden rule has been broken. It gives an autogreet to anyone who joins the channel, unless they have already received the autogreet within the previous three minutes.
+
Ниже приведен пример процедуры, где второе золотое правило TCL не соблюдено. Она реализует автоматическое приветствие всех зашедших на канал, если они еще не были поприветствованы ранее в течении трех последних минут.
  
bind join - * do_jn_msg
+
  bind join - * do_jn_msg
proc do_jn_msg {nick uhost hand chan} {
+
  proc do_jn_msg {nick uhost hand chan} {
  global botnick jn_msg_done
+
    global botnick jn_msg_done
  if {$nick == "X" || $nick == $botnick} {
+
    if {$nick == "X" || $nick == $botnick} {
 +
      return 0
 +
    }
 +
    if {[info exists jn_msg_done($nick:$chan)]} {
 +
      return 0
 +
    }
 +
    set jn_msg_done($nick:$chan) 1
 +
    timer 3 "unset jn_msg_done($nick:$chan)"
 +
    puthelp "NOTICE $nick :Welcome to $chan"
 
     return 0
 
     return 0
 
   }
 
   }
  if {[info exists jn_msg_done($nick:$chan)]} {
 
    return 0
 
  }
 
  set jn_msg_done($nick:$chan) 1
 
  timer 3 "unset jn_msg_done($nick:$chan)"
 
  puthelp "NOTICE $nick :Welcome to $chan"
 
  return 0
 
}
 
  
Suppose that nick has the value abc and chan has the value #room. The double quotes will cause variable substitution of $nick and $chan so that the command given to the timer will be
+
Предположим, что ник у нас ''abc'' а канал - '''#room''. Кавычки в команде [[timer]] подставляют в выражение значения ''$nick'' и ''$chan''
unset jn_msg_done(abc:#room)
+
  unset jn_msg_done(abc:#room)
  
When the timer expires, the unset command will work with no problem.
+
По истечении таймера команда будет успешно выполнена.
  
But suppose now that instead of having the value abc, nick has the value a[b]c. The double quotes will cause variable substitution of $nick and $chan so that the command given to the timer will be
+
Но представим, что вместо ника '''abc'' у нас будет ''a[b]c''. В результате подстановки значений ''$nick'' и ''$chan'' команда в кавычках будет выглядеть так
unset jn_msg_done(a[b]c:#room)
+
  unset jn_msg_done(a[b]c:#room)
  
When the timer expires, the Tcl interpreter will try to perform one round of substitutions on jn_msg_done(a[b]c:#room), since the first step in evaluating any command is to do one round of substitutions on the words of the command.
+
По истечении таймера интерпретатор TCL вначале подставит все необходимые значения в команду ''jn_msg_done(a[b]c:#room)''. Затем интерпретатор попробует выполнить подстановку команды [b]. В результате появится ошибка о том, что команды ''b'' не существует. Если бы в нашем случае ник был ''a[[[die]]]c'' - бот бы обработал и эту команду, в результате чего работа бота была бы завершена.
  
Therefore, the interpreter will try to do command substitution of [b]. It will give an error message to the effect that there is no command called b. Presumably if the nick were a[die]c, the die command would be executed, shutting down the bot.
+
Чтобы исправить скрипт мы изменим команду в таймере следующим образом
 +
  timer 3 [list unset jn_msg_done($nick:$chan)]
  
To correct the script, we replace the timer command with the following.
+
Если в параметре будут какие-либо специальные символы TCL - команда [[list]] добавит бэкслеши и/или фигурные скобки, чтобы выражение приняло нормальный вид. Нам абсолютно не обязательно знать какие специальные символы могут попасться. Команда [[unset]], теперь оформленная правильным образом, будет передана в таймер и сработает без ошибок по истечении промежутка времени.
  
timer 3 [list unset jn_msg_done($nick:$chan)]
+
В действительности, в случае с ''a[b]c'', команда [[list]] просто добавляет необходимые фигурные скобки. Если ник будет ''a[b]c{''  - она так же добавит бэкслеши, чтобы экранировать фигурную скобку в конце ника. Но нам не обязательно знать все это. Достаточно просто правильным образом использовать команду [[list]] и позволить интерпретатору произвести необходимые подстановки.
  
If any Tcl special characters are present, list adds backslashes and/or braces as appropriate to give the correct form for a command. We do not need to know what that form is or even think about it. The unset command, now in the correct form, is passed to the timer and works with no problem whatsoever when the timer expires.
+
Чаще всего я встречался с проблемами, аналогичной выше указанной, только там использовался не ''$nick'' пользователя, а ''$uhost'. Userhost так же может содержать специальные символы TCL, и проблема с ними решается точно таким же способом - корректным использованием команды [[list]].
  
In fact, in the case of a[b]c, the list command simply inserts some extra braces. If the nick were a[b]c{, it would insert several backslash characters. But we do not need to know any of that. We simply use the list command and let the interpreter look after those details.
+
===Другие методы решения проблем, связанных со специальными символами TCL===
 +
Так же есть и другие методы, позволяющие избежать проблем со специальными символами TCL.
  
Whenever we wish a command to be evaluated when a timer expires, the list command can be used to put it into the correct form.
+
Например, некоторые скрипты фильтруют входящие данные процедурами типа этой:
 
+
  proc filt {data} {
More usually in scripts that I have seen that are similar to the above example, it is $uhost that is used rather than $nick. But userhosts can contain Tcl special characters too, so exactly the same problem can arise, and can be solved in exactly the same way as above, by using the list command.
+
    regsub -all -- \\\\ $data \\\\\\\\ data
 
+
    regsub -all -- \\\[ $data \\\\\[ data
In fact, it seems better to use $uhost than to use $nick. I used $nick in the above example since that makes it easier for the reader to experiment with the script, having pasted it into a Tcl source file.
+
    regsub -all -- \\\] $data \\\\\] data
 
+
    regsub -all -- \\\} $data \\\\\} data
Other methods of dealing with problems that occur with Tcl special characters
+
    regsub -all -- \\\{ $data \\\\\{ data
There are other methods that are sometimes used to avoid the problems that can occur with Tcl special characters.
+
    regsub -all -- \\\" $data \\\\\" data
 
+
    return $data
E.g. some scripts filter their input with something like the following:
+
  }
 
+
proc filt {data} {
+
regsub -all -- \\\\ $data \\\\\\\\ data
+
regsub -all -- \\\[ $data \\\\\[ data
+
regsub -all -- \\\] $data \\\\\] data
+
regsub -all -- \\\} $data \\\\\} data
+
regsub -all -- \\\{ $data \\\\\{ data
+
regsub -all -- \\\" $data \\\\\" data
+
return $data
+
}
+
 
+
Such a filter can indeed sometimes cure the Tcl special character problem that can occur with a badly written eggdrop script. But whether it will or not depends on the details of the script. It may not, or it may solve the problem only for some cases. Adding such a filter to a script can even introduce problems.
+
  
If a script chokes on Tcl special characters, then it is far better to correct the code so that the two golden rules are obeyed than to apply a kludge such as the above filter, that isn't guaranteed to work in all circumstances and might even introduce further problems.
+
Подобный фильтр действительно может порой помочь со специальными символами TCL в плохих скриптах [[Eggdrop]]. Но результат и эффективность зависят от контекста скрипта. Данный фильтр может помочь решить проблемы с некоторыми специальными символами, но нарушить работоспособность и логику скрипта в целом.
  
A point worthy of note about args
+
Если в скрипте есть проблемы со специальными символами TCL - лучше исправить их корректным способом, учитывая два золотых правила, чем использовать фильтры подобно приведенному выше, что может работать далеко не во всех ситуациях или даже вызвать новые ошибки
Putting args as the last argument of a Tcl procedure allows the use of a variable number of input arguments. But there is a potential source of confusion if the procedure is one that is called by an eggdrop bind command.
+
  
In a script that I downloaded I saw code similar to the following:
+
===Важное замечание об args===
 +
Если ''args'' расположен как последний аргумент процедуры TCL - все введенные данные будут обрабатываться как один элемент. Но здесь стоит объяснить про некоторые неясности, связанные с процедурами, вызываемыми командой [[bind]].
 +
В одном из скриптов я видел примерно такой код:
  
bind dcc - m2f dcc_calc_m2f
+
  bind dcc - m2f dcc_calc_m2f
proc dcc_calc_m2f {hand idx args} {
+
  proc dcc_calc_m2f {hand idx args} {
  if {[llength $args] == "1"} {
+
    if {[llength $args] == "1"} {
  
The author was no doubt thinking that if a user typed
+
Скорее всего автор скрипта думал, что если пользователь наберет
.m2f aaa bbb ccc
+
  .m2f aaa bbb ccc
then the value of args would be a list of three elements, aaa, bbb and ccc, and that therefore the value of [llength $args] would be 3.
+
то значение ''args'' будет [[list|списком]] из трех элементов - '''aaa''', '''bbb''' и '''ccc''' - и, поэтому, результатом ''[llength $args]'' будет 3.
  
In fact, the value of args would be a list of one element, that element being the string aaa bbb ccc. Whatever string the user types, the value of [llength $args] will always be 1.
+
В действительности же значением '''args''' будет [[list|список]]] из ''одного'' элемента и этим элементом будет строка '''aaa bbb ccc'''. Какую бы строку не ввел пользователь - результатом [llength $args] '''всегда''' будет 1.
  
Similarly, the value of [lindex $args 0] will not be the string aaa, but the string aaa bbb ccc.
+
Аналогично, значением [lindex $args 0] будет не '''aaa''', а строка '''aaa bbb ccc'''.
  
The eggdrop pub and msg binds behave in the same way.
+
[[bind|Триггеры]] Eggdrop типа ''pub'' и ''msg'' ведут себя аналогичным образом.
  
The above properties of eggdrop's bind command have been confirmed by testing with eggdrop version 1.6.6.
+
Вышеуказанные свойства команды [[bind]] бота [[Eggdrop]] проверены на боте версии 1.6.6.
  
Peterre
+
Перевод статьи. Автор оригинала: [http://www.peterre.info/characters.html Peterre].
paperclip444 at peterre dot demon dot co dot uk
+
(email address deliberately obscured to
+
deter address collection by spammers)
+

Версия 11:03, 25 марта 2009

Многие скрипты Eggdrop не правильно обрабатывают имена, иденты или текст, содержащие символы, имеющие особо значание в TCL - [ ] { } " \ $. По этой причине на некоторых IRC каналах банятся ники, содержащие символы [ или {.

Этих проблем можно избежать, используя некоторые приемы при написании Tcl скриптов. Есть два золотых правила, которыми не следует пренебрегать при написании скриптов.

Первое золотое правило TCL

Используйте все split и join, необходимые для преобразования между списками и строками.

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

Например, рассмотрим скрипт, содержащий следующие строки

 bind dcc o tell do_dcc_tell
 proc do_dcc_tell { hand idx arg } {
 set arg [lrange $arg 0 0]

При вызове процедуры первым параметром является ник. Скрипт выдаст ошибку, если ник содержит [ или {. Ошибка будет вызвана при обработке третьей строки скрипта.

Вместо третьей строки можно так же использовать

 set arg [lindex $arg 0]

но и это приведет к ошибкам, если ник содержит другие специальные символы Tcl.

Чтобы понять почему приведенный код ошибочек - давайте заглянем в tcl-commands.doc. Там говорится, что значение переменной arg это "строковый параметр". К тому же lrange и lindex должны использоваться только со списками. Поэтому прежде чем использовать lrange или lindex, мы должны преобразовать строковое значение в список, используя для этого соответствующую функцию split. Чтобы исправить первую версию скрипта мы должны заменить третью строку на следующую

 set arg [lrange [split $arg] 0 0]

Однако и этот вариант не идеален. Во всей последующей процедуре arg будет по-прежнему восприниматься как строка, а не список, к тому же lrange вернет нам список (пусть и состоящий из одного элемента). Завершающим штрихом будет использование функции join для преобразования списка в строку. И так, наша последняя и наиболее правильная версия

 set arg [join [lrange [split $arg] 0 0]]

Так же мы можем доработать альтернативный вариант (к тому же, он более краткий)

 set arg [lindex $arg 0]

используя тот же подход

 set arg [lindex [split $arg] 0]

В этом случае нам нет необходимости использовать join, так как lindex вернет ник, указанный пользователем, который уже является строкой.

Стоит отметить, что если бы мы использовать последний аргумент процедуры do_dcc_tell с именем args, а не arg, ситуация была бы иная. Если процедура Tcl в качестве последнего аргумента использует имя args, аргумент обрабатывается иначе, чем остальные аргументы.

Вот пример скрипта для Eggdrop. Написан он абсолютно правильно.

 bind pub B|B !orderfor pub_orderfor
 proc pub_orderfor {nick uhost hand chan rest} {
   global botnick
   set rest [split $rest]
   if {[llength $rest] < 2} {
     putnotc $nick "Syntax: !orderfor\
     <nick to order something for>\
     <what to order>"
     return 0
   }
   putchan $chan "\001ACTION sets\
   [join [lrange $rest 1 end]] in front of\
   [lindex $rest 0], compliments of $nick.\001"
   return 0
 }

Замечу, что процедуры putnotc и putchan определены в alltools.tcl, который распространяется вместе с дистрибутивом бота.

Исходная версия (которую я взял из скрипта) не содержала

 set rest [split $rest]

вместо этого в ней была строка

 set cmd [string tolower [lindex $rest 0]]

просмотров которую становится понятно, что здесь ошибочно применяется lindex к строковому параметру.

Оригинал скрипта периодически будет выдавать ошибки, если $rest будет содержать особые символы TCL.

Второе золотое правило TCL

Если скрипт содержит команды, которые будут выполнены по истечении таймера, эти команды должны быть оформлены правильно.

List позволяет оформлять такие такие команды в надлежащей форме. Она добавляет бэкслеши (обратные слеши, backslashes) и фигурные скобки (braces) чтобы избежать проблем с любыми специальными символами TCL, которые могут встретиться в команде.

Ниже приведен пример процедуры, где второе золотое правило TCL не соблюдено. Она реализует автоматическое приветствие всех зашедших на канал, если они еще не были поприветствованы ранее в течении трех последних минут.

 bind join - * do_jn_msg
 proc do_jn_msg {nick uhost hand chan} {
   global botnick jn_msg_done
   if {$nick == "X" || $nick == $botnick} {
     return 0
   }
   if {[info exists jn_msg_done($nick:$chan)]} {
     return 0
   }
   set jn_msg_done($nick:$chan) 1
   timer 3 "unset jn_msg_done($nick:$chan)"
   puthelp "NOTICE $nick :Welcome to $chan"
   return 0
 }

Предположим, что ник у нас abc а канал - '#room. Кавычки в команде timer подставляют в выражение значения $nick и $chan

 unset jn_msg_done(abc:#room)

По истечении таймера команда будет успешно выполнена.

Но представим, что вместо ника 'abc у нас будет a[b]c. В результате подстановки значений $nick и $chan команда в кавычках будет выглядеть так

 unset jn_msg_done(a[b]c:#room)

По истечении таймера интерпретатор TCL вначале подставит все необходимые значения в команду jn_msg_done(a[b]c:#room). Затем интерпретатор попробует выполнить подстановку команды [b]. В результате появится ошибка о том, что команды b не существует. Если бы в нашем случае ник был a[[[die]]]c - бот бы обработал и эту команду, в результате чего работа бота была бы завершена.

Чтобы исправить скрипт мы изменим команду в таймере следующим образом

 timer 3 [list unset jn_msg_done($nick:$chan)]

Если в параметре будут какие-либо специальные символы TCL - команда list добавит бэкслеши и/или фигурные скобки, чтобы выражение приняло нормальный вид. Нам абсолютно не обязательно знать какие специальные символы могут попасться. Команда unset, теперь оформленная правильным образом, будет передана в таймер и сработает без ошибок по истечении промежутка времени.

В действительности, в случае с a[b]c, команда list просто добавляет необходимые фигурные скобки. Если ник будет a[b]c{ - она так же добавит бэкслеши, чтобы экранировать фигурную скобку в конце ника. Но нам не обязательно знать все это. Достаточно просто правильным образом использовать команду list и позволить интерпретатору произвести необходимые подстановки.

Чаще всего я встречался с проблемами, аналогичной выше указанной, только там использовался не $nick пользователя, а $uhost'. Userhost так же может содержать специальные символы TCL, и проблема с ними решается точно таким же способом - корректным использованием команды list.

Другие методы решения проблем, связанных со специальными символами TCL

Так же есть и другие методы, позволяющие избежать проблем со специальными символами TCL.

Например, некоторые скрипты фильтруют входящие данные процедурами типа этой:

 proc filt {data} {
   regsub -all -- \\\\ $data \\\\\\\\ data
   regsub -all -- \\\[ $data \\\\\[ data
   regsub -all -- \\\] $data \\\\\] data
   regsub -all -- \\\} $data \\\\\} data
   regsub -all -- \\\{ $data \\\\\{ data
   regsub -all -- \\\" $data \\\\\" data
   return $data
 }

Подобный фильтр действительно может порой помочь со специальными символами TCL в плохих скриптах Eggdrop. Но результат и эффективность зависят от контекста скрипта. Данный фильтр может помочь решить проблемы с некоторыми специальными символами, но нарушить работоспособность и логику скрипта в целом.

Если в скрипте есть проблемы со специальными символами TCL - лучше исправить их корректным способом, учитывая два золотых правила, чем использовать фильтры подобно приведенному выше, что может работать далеко не во всех ситуациях или даже вызвать новые ошибки

Важное замечание об args

Если args расположен как последний аргумент процедуры TCL - все введенные данные будут обрабатываться как один элемент. Но здесь стоит объяснить про некоторые неясности, связанные с процедурами, вызываемыми командой bind. В одном из скриптов я видел примерно такой код:

 bind dcc - m2f dcc_calc_m2f
 proc dcc_calc_m2f {hand idx args} {
   if {[llength $args] == "1"} {

Скорее всего автор скрипта думал, что если пользователь наберет

  .m2f aaa bbb ccc

то значение args будет списком из трех элементов - aaa, bbb и ccc - и, поэтому, результатом [llength $args] будет 3.

В действительности же значением args будет список] из одного элемента и этим элементом будет строка aaa bbb ccc. Какую бы строку не ввел пользователь - результатом [llength $args] всегда будет 1.

Аналогично, значением [lindex $args 0] будет не aaa, а строка aaa bbb ccc.

Триггеры Eggdrop типа pub и msg ведут себя аналогичным образом.

Вышеуказанные свойства команды bind бота Eggdrop проверены на боте версии 1.6.6.

Перевод статьи. Автор оригинала: Peterre.