2016-02-18 2 views
2

Я хочу выполнить сценарий оболочки при обработке вывода stdout и stderr. В настоящее время я выполняю команды с использованием Process.run, с shell=false и тремя трубами для stdin, stdout и stderr. Я порою волокна, чтобы читать из stdout и stderr и регистрировать (или иначе обрабатывать) выход. Это очень хорошо работает для отдельных команд, но неудачно для скриптов.Как выполнить скрипт оболочки в Crystal при захвате вывода?

Я мог бы просто установить shell=true при вызове Process.run, но, глядя на источник кристалла, кажется, что просто добавляет «sh» к командной строке. Я пробовал добавить «bash», и это не помогло.

вещь, как перенаправление (>file) и трубы (например, curl something | bash) не похож на работу с Process.run

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

ЦМД =% {bash -c "curl http://dist.crystal-lang.org/apt/setup.sh" | баш}

Process.run (CMD, ...)

bash Начальное был добавлен в надежде, что это позволит оператору трубы. Кажется, это не помогает. Я также попытался выполнения каждой команды отдельно:.

script.split ("\ п") отклонять (/^# /, "") .each {Process.run (...)}

Но, конечно, это все еще не удается, когда команда использует перенаправление или каналы. Например, команда echo "deb http://dist.crystal-lang.org/apt crystal main" >/etc/apt/sources.list.d/crystal.list просто выводит:

"Deb http://dist.crystal-lang.org/apt кристалл главный">/и т.д./кв/sources.list.d/crystal.list`

Это может сработать, если я использовал `` backticks метод исполнения вместо этого; но тогда я не смог бы захватить вывод в режиме реального времени.

+0

Вам необходимо переместить трубопровод в 'bash -c' в первом случае, чтобы заставить его вызвать. Вы сражаетесь с 'shell = false', но это то, что позволит вам напрямую использовать синтаксис оболочки. – Petesh

+0

@Petesh К сожалению, установка 'shell = true' не помогает. –

+0

@Petesh Перемещение конвейера в вызов 'bash -c', похоже, работает. Итак ... почему не работает 'shell = true'? Неужели это почти то же самое? –

ответ

3

Я основываю свое понимание на чтении исходного кода файла run.cr. Поведение очень похоже на другие языки в том, как оно относится к командам и аргументам.

Без shell=true, поведение по умолчанию Process.run является использование команды как исполняемого для запуска. Это означает, что строка должна быть именем программы без каких-либо аргументов, например. uname будет действительным именем, так как есть программа в моей системе под названием uname в /usr/bin.

Если вы когда-либо получали поведение успешно используют %{bash -c "echo hello world"} с shell=false, то что-то не так - поведение по умолчанию должно быть, чтобы попытаться запустить программу под названием bash -c "echo hello world", которая вряд ли существует в любой системе.

Как только вы передадите 'shell = true', тогда он будет sh -c <command>, что позволит использовать строки, такие как echo hello world как команда для работы; это также позволит работать с перенаправлениями и трубопроводами.

shell=true поведения обычно можно интерпретировать как делать следующее:

cmd = "sh" 
args = [] of String 
args << "-c" << "curl http://dist.crystal-lang.org/apt/setup.sh | bash" 
Process.run(cmd, args, …) 

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

Причина первая версия, с или без shell=true не работает потому, что трубопровод вне в -c, что команда, которую вы отправки колотить.

+0

Я думаю, что проблема была связана с тем, что я разделил командную строку на 'cmd' (строку) и' args' (массив), а затем вызвал 'Process.new (cmd, args, ...)'. Даже с 'shell = true', это не сработало - возможно, потому, что я уже разделил' | 'на собственный аргумент ... –

+0

В основном, я закончил тем, что вызывал' Process.new («curl», [ «http://dist.crystal-lang.org/apt/setup.sh», «|», «bash»], shell: true) '. И, конечно, это не сработало. Чтобы поддерживать «shell = true», мне нужно было изменить мою функцию-оболочку, чтобы избежать разделения команды на слова, если «shell» является истиной. –

8

Проблема Проблема UNIX. Родительский процесс должен иметь возможность доступа к STDOUT дочернего процесса. Используя трубу, вы должны запустить процесс оболочки, который будет запускать всю команду, включая | bash, а не только curl $URL. В кристалле это:

command = "curl http://dist.crystal-lang.org/apt/setup.sh | bash" 
io = MemoryIO.new 
Process.run(command, shell: true, output: io) 
output = io.to_s 

Или, если вы хотите, чтобы дублировать то, что делает кристалл для вас:

Process.run("sh", {"-c", command}, output: io) 
1

или если вы хотите, чтобы вызвать скрипт и получить на выходе я только что попробовал с кристаллом 0,23 .1 и это работа!

def screen 
    output = IO::Memory.new 
    Process.run("bash", args: {"lib/bash_scripts/installation.sh"}, output: output) 
    output.close 
    output.to_s 
end