Module: Kernel
- Defined in:
- lib/qbash.rb
Overview
Execute one bash command.
- Author
-
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
-
Copyright © 2024-2025 Yegor Bugayenko
- License
-
MIT
Instance Method Summary collapse
-
#qbash(cmd, stdin: '', env: {}, log: Loog::NULL, accept: [0], both: false, level: Logger::DEBUG) ⇒ String
Execute a single bash command.
Instance Method Details
#qbash(cmd, stdin: '', env: {}, log: Loog::NULL, accept: [0], both: false, level: Logger::DEBUG) ⇒ String
Execute a single bash command.
For example:
year = qbash('date +%Y')
If exit code is not zero, an exception will be raised.
To escape arguments, use Shellwords.escape() method.
Stderr automatically merges with stdout.
If you need full control over the process started, provide a block, which will receive process ID (integer) once the process is started.
Read this <a href=“github.com/yegor256/qbash”>README</a> file for more details.
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 |
# File 'lib/qbash.rb', line 62 def qbash(cmd, stdin: '', env: {}, log: Loog::NULL, accept: [0], both: false, level: Logger::DEBUG) env.each { |k, v| raise "env[#{k}] is nil" if v.nil? } cmd = cmd.reject { |a| a.nil? || (a.is_a?(String) && a.empty?) }.join(' ') if cmd.is_a?(Array) mtd = case level when Logger::DEBUG :debug when Logger::INFO :info when Logger::WARN :warn when Logger::ERROR :error else raise "Unknown log level #{level}" end if log.nil? # nothing to print elsif log.respond_to?(mtd) log.__send__(mtd, "+ #{cmd}") else log.print("+ #{cmd}\n") end buf = '' e = 1 start = Time.now Open3.popen2e(env, "/bin/bash -c #{Shellwords.escape(cmd)}") do |sin, sout, thr| sin.write(stdin) sin.close if block_given? Thread.new do until sout.closed? begin ln = sout.gets rescue IOError => e ln = Backtrace.new(e).to_s end ln = "##{thr.pid}: #{ln}" if log.nil? # no logging elsif log.respond_to?(mtd) log.__send__(mtd, ln) else log.print("#{ln}\n") end buf += ln end end yield thr.pid else until sout.eof? begin ln = sout.gets rescue IOError => e ln = Backtrace.new(e).to_s end if log.nil? # no logging elsif log.respond_to?(mtd) log.__send__(mtd, ln) else log.print("#{ln}\n") end buf += ln end e = thr.value.to_i if !accept.nil? && !accept.include?(e) raise "The command '#{cmd}' failed with exit code ##{e} in #{start.ago}\n#{buf}" end end end return [buf, e] if both buf end |