Рассмотрим следующую короткую программу Expect:
#!/usr/bin/expect
puts $::argc
puts $::argv
Если я вызову его следующим образом, он правильно определит, что имеется четыре элемента, но естественное tcl-представление массива argv не может быть напрямую передано в оболочку.
./Test.exp x y z "a b c"
4
x y z {a b c}
Можно ли заставить TCL выводить аргументы в удобном для оболочки виде, чтобы я мог передать их в другую программу через send
?
Для ясности: я знаю, что могу напрямую передать аргументы spawn
или exec
.
Однако я хотел бы знать, возможно ли send
передать аргументы порожденной оболочки (например, bash
), что требует правильного цитирования оболочки.
Это полезно для отправки последовательности команд в bash
с использованием аргументов, переданных в сценарий ожидания, и для возможности протоколировать весь псевдоинтерактивный сеанс.
Да, я это понимаю, как я указал в вопросе.
Не уверен, но этот вопрос может быть тем, что вам нужно.
Нет, я уже видел это. Мне известно о решении exec, и этот вопрос конкретно касается цитирования оболочки.
Ленивый способ — использовать сам bash
для экранирования. Таким образом, вам не придется учитывать каждый специальный символ и крайний случай, который может возникнуть; оболочка уже знает о них и может обработать их за вас.
#!/usr/bin/env tclsh
# Take a list and return a single string with the elements of that list
# escaped in a way that bash can split back up into individual elements
proc quote_args {raw} {
string map {"\n" " "} [exec bash -c {printf "%q\n" "$@"} bash {*}$raw]
}
# Demonstrate usage by calling a shell with a single argument including the
# escaped arguments
set quoted_argv [quote_args $argv]
puts "Escaped: $quoted_argv"
puts [exec bash -c "printf \">%s<\\n\" $quoted_argv"]
Пример использования:
$ ./args.tcl x y z "a b c"
Escaped: x y z a\ b\ c
>x<
>y<
>z<
>a b c<
$ ./args.tcl x y z $'a b\nc' # Works with embedded newlines
Escaped: x y z $'a b\nc'
>x<
>y<
>z<
>a b
c<
$ ./args.tcl 'a b$c' # And things that look like shell parameters that shouldn't be substituted
Escaped: a\ b\$c
>a b$c<
Насколько я могу судить, это самое надежное решение. Сегодня я узнал о printf '%q'
!
Если пойти другим путем, в tcllib есть пакет для разбиения строки кавычками оболочки (но только sh
, никаких расширений вроде bash
анси-кавычек).
Строку для вывода необходимо построить:
#!/usr/bin/expect
set output {}
foreach arg $argv {
set escaped_arg [string map {"\\" "\\\\" "\"" "\\\""} $arg]
if {[regexp { } $escaped_arg] || [regexp {'} $escaped_arg]} {
lappend output "\"$escaped_arg\""
} else {
lappend output $escaped_arg
}
}
puts $argc
puts [join $output " "]
Также необходимо экранировать символы табуляции, $ и, возможно, другие символы.
В итоге я реализовал свою собственную версию, ожидая других ответов, поэтому публикую для полноты картины, поскольку конечный результат немного отличается от других.
#!/usr/bin/expect
puts $::argc
puts $::argv
# tcl 8.5 does not have lmap, so this implementation is placed here.
proc map {fun list} {
set res {}
foreach element $list {lappend res [$fun $element]}
set res
}
# Turn an individual argument into a string that the shell likes.
proc transformArg {arg} {
if {[string first "'" $arg] == -1} {
return "'$arg'"
}
proc wrapInSingleQuote {x} {return "'$x'"}
return [join [map wrapInSingleQuote [split $arg {'}] ] {"'"}]
}
foreach arg $::argv {
lappend changedArg [transformArg $arg]
}
puts [join "$changedArg" " "]
Вы понимаете, что можете запустить оболочку, отправляя аргументы вне диапазона кода для выполнения?