Программирование на языке Ruby.
14.1. Запуск внешних программ.
Никакой язык не может использоваться в качестве «клея», если он не позволяет запускать внешние программы. В Ruby для этого есть несколько способов.
Не могу не обмолвиться о том, что перед запуском внешней программы неплохо бы понять, что она делает. Я имею в виду вирусы и другие потенциально разрушительные программы. Не запускайте произвольную командную строку, особенно поступившую из внешнего источника. Это касается не только приложений, ориентированных на Web.
14.1.1. Методы system и exec.
Метод system (из модуля Kernel) эквивалентен одноименной функции из библиотеки языка С. Он выполняет указанную команду в отдельной оболочке.
System("/usr/games/fortune").
# Вывод направляется, как обычно, на stdout...
Второй параметр, если он задан, должен содержать список аргументов; как правило, аргументы можно задавать и в командной строке — эффект будет тот же. Разница лишь в том, что алгоритм расширения имени файла применяется только к первой из переданных строк.
System("rm", "/tmp/file1").
System("rm /tmp/file2").
# Оба варианта годятся.
# А тут есть различие...
System("echo *") # Печатается список всех файлов.
System("echo","*") # Печатается звездочка (расширение.
# имени файла не производится).
# Более сложные командные строки тоже работают.
System("ls -l | head -n |").
Посмотрим, как это будет работать в семействе операционных систем Windows. В случае с простой исполняемой программой поведение должно быть таким же, как в UNIX. В зависимости от варианта Ruby для вызова встроенных в оболочку команд может потребоваться запуск cmd.ехе — интерпретатора команд в Windows (в некоторых версиях ОС он называется command.com). Ниже приведены примеры запуска внешней и встроенной команды:
System("notepad.ехе","myfile.txt") # Никаких проблем...
System("cmd /с dir","somefile") # 'dir' - встроенная команда!
Другое решение — воспользоваться библиотекой Win32API и определить собственный вариант метода system.
Require "Win32API".
Def system(cmd).
sys = Win32API.new("crtdll", "system", ['P'], 'L').
sys.Call(cmd).
End.
System("dir") # cmd /с необязательно!
Таким образом, можно добиться более-менее системно-независимого поведения system. Но если вы хотите запомнить выведенную программой информацию (например, в переменной), то system — не лучший способ (см. следующий раздел).
Упомяну еще метод exec. Он ведет себя аналогично system с тем отличием, что новый процесс замещает текущий. Поэтому код, следующий за exec, исполняться не будет.
Puts "Содержимое каталога:".
Exec("ls", "-l").
Puts "Эта строка никогда не исполняется!".
14.1.2. Перехват вывода программы.
Простейший способ перехватить информацию, выведенную программой, — заключить команду в обратные кавычки, например:
Listing = `ls -l` # Одна строка будет содержать несколько строчек (lines).
Now = `date` # "Mon Mar 12 16:50:11 CST 2001".
Обобщенный ограничитель %x вызывает оператор обратных кавычек (который в действительности является методом модуля Kernel). Работает он точно так же:
Listing = %x(ls -l).
Now = %x(date).
Применение %x бывает полезно, когда подлежащая исполнению строка содержит такие символы, как одиночные и двойные кавычки.
Поскольку обратные кавычки — это на самом деле метод (в некотором смысле), то его можно переопределить. Изменим его так, чтобы он возвращал не одну строку, а массив строк. Конечно, при этом мы создадим синоним старого метода, чтобы его можно было вызвать.
Alias old_execute `
Def `(cmd).
out = old_execute(cmd) # Вызвать исходный метод обратной кавычки.
out.split("\n") # Вернуть массив строк!
End.
Entries = `ls -l /tmp`
Num = entries.size # 95.
First3lines = %x(ls -l | head -n 3).
How_many = first3lines.size # 3.
Как видите, при таком определении изменяется также поведение ограничителя %x.
В следующем примере мы добавили в конец команды конструкцию интерпретатора команд, которая перенаправляет стандартный вывод для ошибок в стандартный вывод:
Alias old_execute `
Def `(cmd).
old_execute(cmd + " 2>&1").
End.
Entries = `ls -l /tmp/foobar`
# "/tmp/foobar: No such file or directory\n".
Есть, конечно, и много других способов изменить стандартное поведение обратных кавычек.
14.1.3. Манипулирование процессами.
В этом разделе мы обсудим манипулирование процессами, хотя создание нового процесса необязательно связано с запуском внешней программы. Основной способ создания нового процесса — это метод fork, название которого в соответствии с традицией UNIX подразумевает разветвление пути исполнения, напоминая развилку на дороге. (Отметим, что в базовом дистрибутиве Ruby метод fork на платформе Windows не поддерживается.).
Метод fork, находящийся в модуле Kernel (а также в модуле Process), не следует путать с одноименным методом экземпляра в классе Thread.
Существуют два способа вызвать метод fork. Первый похож на то, как это обычно делается в UNIX, — вызвать и проверить возвращенное значение. Если оно равно nil, мы находимся в дочернем процессе, в противном случае — в родительском. Родительскому процессу возвращается идентификатор дочернего процесса (pid).
Pid = fork.
If (pid == nil).
puts "Ага, я, должно быть, потомок.".
puts "Так и буду себя вести.".
Else.
puts "Я родитель.".
puts "Пора отказаться от детских штучек.".
End.
В этом не слишком реалистичном примере выводимые строки могут чередоваться, а может случиться и так, что строки, выведенные родителем, появятся раньше. Но сейчас это несущественно.
Следует также отметить, что процесс-потомок может пережить своего родителя. Для потоков в Ruby это не так, но системные процессы — совсем другое дело.
Во втором варианте вызова метод fork принимает блок. Заключенный в блок код выполняется в контексте дочернего процесса. Так, предыдущий вариант можно было бы переписать следующим образом:
Fork do.
puts "Ага, я, должно быть, потомок.".
puts "Так и буду себя вести.".
End.
Puts "Я родитель.".
Puts "Пора отказаться от детских штучек.".
Конечно, pid по-прежнему возвращается, мы просто не показали его.
Чтобы дождаться завершения процесса, мы можем вызвать метод wait из модуля Process. Он ждет завершения любого потомка и возвращает его идентификатор. Метод wait2 ведет себя аналогично, только возвращает массив, содержащий РМ, и сдвинутый влево код завершения.
Pid1 = fork { sleep 5; exit 3 }.
Pid2 = fork { sleep 2; exit 3 }.
Process.wait # Возвращает pid2.
Process.wait2 # Возвращает [pid1,768].
Чтобы дождаться завершения конкретного потомка, применяются методы waitpid и waitpid2.
Pid3 = fork { sleep 5; exit 3 }.
Pid4 = fork { sleep 2; exit 3 }.
Process.waitpid(pid4,Process::WNOHANG) # Возвращает pid4.
Process.waitpid2(pid3, Process::WNOHANG) # Возвращает [pid3,768].
Если второй параметр не задан, то вызов может блокировать программу (если такого потомка не существует). Второй параметр можно с помощью ИЛИ объединить с флагом Process::WUNTRACED, чтобы перехватывать остановленные процессы. Этот параметр системно зависим, поэкспериментируйте.
Метод exit! немедленно завершает процесс (не вызывая зарегистрированных обработчиков). Если задан целочисленный аргумент, то он возвращается в качестве кода завершения; по умолчанию подразумевается значение 1 (не 0).
Pid1 = fork { exit! } # Вернуть код завершения -1.
Pid2 = fork { exit! 0 } # Вернуть код завершения 0.
Методы pid и ppid возвращают соответственно идентификатор текущего и родительского процессов.
Proc1 = Process.pid.
Fork do.
if Process.ppid == proc1.
puts "proc1 - мой родитель" # Печатается это сообщение.
else.
puts "Что происходит?".
end.
End.
Метод kill служит для отправки процессу сигнала, как это понимается в UNIX. Первый параметр может быть целым числом, именем POSIX-сигнала с префиксом SIG или именем сигнала без префикса. Второй параметр — идентификатор процесса-получателя; если он равен нулю, подразумевается текущий процесс.
Process.kill(1,pid1) # Послать сигнал 1 процессу pid1.
Process.kill ("HUP",pid2) # Послать SIGHUP процессу pid2..
Process.kill("SIGHUP",pid2) # Послать SIGHUP процессу pid3.
Process.kill("SIGHUP",0) # Послать SIGHUP самому себе.
Для обработки сигналов применяется метод Kernel.trap. Обычно он принимает номер или имя сигнала и подлежащий выполнению блок.
Trap(1) { puts "Перехвачен сигнал 1" }.
Sleep 2.
Process.kill(1,0) # Послать самому себе.
О применениях метода trap в более сложных ситуациях читайте в документации по Ruby и UNIX.
В модуле Process есть также методы для опроса и установки таких атрибутов процесса, как идентификатор пользователя, действующий идентификатор пользователя, приоритет и т.д. Дополнительную информацию вы отыщете в справочном руководстве по Ruby.
14.1.4. Стандартный ввод и вывод.
В главе 10 мы видели, как работают методы IO.popen и IO.pipe, но существует еще небольшая библиотека, которая иногда бывает удобна.
В библиотеке Open3.rb есть метод popen3, который возвращает массив из трех объектов IO. Они соответствуют стандартному вводу, стандартному выводу и стандартному выводу для ошибок того процесса, который был запущен методом popen3. Вот пример:
Require "open3".
Filenames = %w[ file1 file2 this that another one_more ].
Inp, out, err = Open3.popen3("xargs", "ls", "-l").
Filenames.each { |f| inp.puts f } # Писать в stdin процесса.
Inp.close # Закрывать обязательно!
Output = out.readlines # Читать из stdout.
Errout = err.readlines # Читать также из stderr.
Puts "Послано #{filenames.size} строк входных данных.".
Puts "Получено #{output.size} строк из stdout".
Puts "и #{errout.size} строк из stderr.".
В этом искусственном примере мы выполняем команду ls -l для каждого из заданных имен файлов и по отдельности перехватываем стандартный вывод и стандартный вывод для ошибок. Отметим, что вызов close необходим, чтобы порожденный процесс увидел конец файла. Также отметим, что в библиотеке Open3 используется метод fork, не реализованный на платформе Windows; для этой платформы придется пользоваться библиотекой win32-open3 (ее написали и поддерживают Дэниэль Бергер (Daniel Berger) и Парк Хисоб (Park Heesob)). См. также раздел 14.3.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
- 402.
- 403.
- 404.
- 405.
- 406.
- 407.
- 408.
- 409.
- 410.
- 411.
- 412.
- 413.
- 414.
- 415.
- 416.
- 417.
- 418.
- 419.
- 420.
- 421.
- 422.
- 423.
- 424.
- 425.
- 426.
- 427.
- 428.
- 429.
- 430.
- 431.
- 432.
- 433.
- 434.
- 435.
- 436.
- 437.
- 438.
- 439.
- 440.
- 441.
- 442.
- 443.
- 444.
- 445.
- 446.
- 447.
- 448.
- 449.
- 450.
- 451.
- 452.
- 453.
- 454.
- 455.
- 456.
- 457.
- 458.
- 459.
- 460.
- 461.
- 462.
- 463.
- 464.
- 465.
- 466.
- 467.
- 468.
- 469.
- 470.
- 471.
- 472.
- 473.
- 474.
- 475.
- 476.
- 477.
- 478.
- 479.
- 480.
- 481.
- 482.
- 483.
- 484.
- 485.
- 486.
- 487.
- 488.
- 489.
- 490.
- 491.
- 492.
- 493.
- 494.
- 495.
- 496.
- 497.
- 498.
- 499.
- 500.
- 501.
- 502.
- 503.
- 504.
- 505.
- 506.
- 507.
- 508.
- 509.
- 510.
- 511.
- 512.
- 513.
- 514.
- 515.
- 516.
- 517.
- 518.
- 519.
- 520.
- 521.
- 522.
- 523.
- 524.
- 525.
- 526.
- 527.
- 528.
- 529.
- 530.
- 531.
- 532.
- 533.
- 534.
- 535.
- 536.
- 537.
- 538.
- 539.
- 540.
- 541.
- 542.
- 543.
- 544.
- 545.
- 546.
- 547.
- 548.
- 549.
- 550.
- 551.
- 552.
- 553.
- 554.
- 555.
- 556.
- 557.
- 558.
- 559.
- 560.
- 561.
- 562.
- 563.
- 564.
- 565.
- 566.
- 567.
- 568.
- 569.
- 570.
- 571.
- 572.
- 573.