неділя, 5 липня 2009 р.

Ruby for System Administration → Bash quoting

Інколи використовую Ruby для задач системного адміністрування. Ruby з широким набором бібліотек чудово для цього підходить.
Однією з поширених задач є виклик зовнішніх команд Linux, передачі їм різноманітних параметрів, отримання і обробки результатів. І тут ви обов'язково стикнетеся з маскуванням символів в bash.


Маскування(quoting) в bash використовується для відміни спеціального значення для команд інтерпрeтатора визначених символів або слів.
Маскування можна використовувати для скасування специфічної обробки спеціальних символів, для запобігання розпізнавання зарезервованих слів, а також для запобігання підстановки параметрів.
Метасимвол - це символ, який розділяє слова, якщо він не замаскований. Є одним з наступних символів:
| & ; ( ) < > пробіл табуляція

Існує три механізми маскування:
символ маскування (escape character), одинарні лапки і подвійні лапки.

Незамаскована зворотна коса риска (\) є символом маскування(escape character). Вона вимагає використовувати наступний за нею символ(за винятком переведення рядка) буквально.

Усі символи в одиночних лапках (') використовуються буквально. Символ одиночної лапки(апостроф) не повинен вказуватися між лапках, навіть якщо його випереджає зворотна коса.

Символи в подвійних лапках(") використовуються буквально, за виключенням $, !, ` і \
Зворотна коса має спеціальне значення тільки якщо після неї йде один з спеціальних символів.

З точки зору програмування, маскування з допомогою одинарних лапок є найпростішим (але не дозволяє маскувати символ одинарних лапок). Тоді як з допомогою зворотної косої - найскладніший.
Ми не шукаємо простих шляхів :)

Напишемо просто функцію, яка отримуватиме cтроку і повертатиме масковану строку, придатну для використання в bash.
Зауважу, код буде працювати тільки в версіями Ruby вище 1.9, оскільки в Regexp використовується нова можливість Oniguruma - іменовані групи(named groups).
#!/usr/bin/env ruby1.9
# -*- encoding: utf8 -*-

def quote_for_bash(text)
text = text.dup
text.gsub!( /(?<backslash>\\)/, '\\\\\k<backslash>' )
text.gsub!( /(?<space>\s)/, '\\\\\k<space>' )
text.gsub!( /(?<double-quotes>")/, '\\\\\k<double-quotes>' )
text.gsub!( /(?<single-quotes>')/, '\\\\\k<single-quotes>' )
text.gsub!( /(?<apostrophe>`)/, '\\\\\k<apostrophe>' )
text.gsub!( /(?<brackets>[()<>])/, '\\\\\k<brackets>' )
text.gsub!( /(?<semicolon>;)/, '\\\\\k<semicolon>' )
text.gsub!( /(?<exclamation-mark>!)/, '\\\\\k<exclamation-mark>')
text.gsub!( /(?<ampersand>&)/, '\\\\\k<ampersand>' )
text.gsub!( /(?<dollar-sign>\$)/, '\\\\\k<dollar-sign>' )
text.gsub!( /(?<number-sign>#)/, '\\\\\k<number-sign>' )
text.gsub!( /(?<vertical>\|)/, '\\\\\k<vertical>' )
return text
end


Задамо текст, який, наприклад, в майбутньому будемо використовувати як параметр для якоїсь команди:
text = %{#<b>$a;say "hello" & |Profit!|  -  (`1$)</b>}


Початковий текст матиме такий вигляд:
puts text
#<b>$a;say "hello" & |Profit!| - (`1$)</b>


Маскований текст буде таким:
puts quote_for_bash(text)
\#\<b\>\$a\;say\ \"hello\"\ \&\ \|Profit\!\|\ \ -\ \ \(\`2\$\)\</b\>

Немає коментарів: