[英]Execute bash commands from a Rakefile
我想從Rakefile
執行一些bash
命令。
我在我的Rakefile
嘗試了以下Rakefile
task :hello do
%{echo "World!"}
end
但是在執行rake hello
時沒有輸出? 如何從 Rakefile 執行 bash 命令?
注意:這不是重復的,因為它專門詢問如何從Rakefile執行 bash 命令。
我認為 rake 希望這種情況發生的方式是: http : //rubydoc.info/gems/rake/FileUtils#sh-instance_method示例:
task :test do
sh "ls"
end
內置的 rake 函數 sh 負責命令的返回值(如果命令的返回值不是 0,則任務失敗),此外它還輸出命令輸出。
在 ruby 中有多種執行 shell 命令的方法。 一個簡單的(可能也是最常見的)是使用反引號:
task :hello do
`echo "World!"`
end
反引號有很好的效果,shell 命令的標准輸出成為返回值。 因此,例如,您可以通過執行獲得ls
的輸出
shell_dir_listing = `ls`
但是還有許多其他方法可以調用 shell 命令,它們都有優點/缺點並且工作方式不同。 本文詳細解釋了這些選擇,但這里有一個快速總結的可能性:
stdout = %x{cmd} - 反引號的替代語法,在幕后它在做同樣的事情
exec(cmd) - 用新的 cmd 進程完全替換正在運行的進程
success = system(cmd) - 運行子進程並在成功/失敗時返回真/假(基於 cmd 退出狀態)
IO#popen(cmd) { |io| } -運行子和連接輸出和錯誤到IO
stdin、stdout、stderr = Open3.popen3(cmd) - 運行一個子進程並連接到所有管道(輸入、輸出、錯誤)
鑒於共識似乎更喜歡 rake 的#sh
方法,但 OP 明確要求 bash,這個答案可能有一些用處。
這是相關的,因為Rake#sh
使用Kernel#system
調用來運行 shell 命令。 Ruby 將其硬編碼到/bin/sh
,忽略用戶配置的 shell 或環境中的$SHELL
。
這是一個從/bin/sh
調用 bash 的解決方法,允許您仍然使用sh
方法:
task :hello_world do
sh <<-EOS.strip_heredoc, {verbose: false}
/bin/bash -xeu <<'BASH'
echo "Hello, world!"
BASH
EOS
end
class String
def strip_heredoc
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, ''.freeze)
end
end
#strip_heredoc
是從 rails 借來的:
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/strip.rb
您可以通過需要 active_support 來獲得它,或者當您在 Rails 項目中時它可能會自動加載,但我使用的是外部 rails,因此必須自己定義它。
有兩個 heredocs,一個帶有標記EOS
的外部一個和一個帶有標記BASH
的內部一個。
其工作方式是將 BASH 標記之間的內部 heredoc 提供給 bash 的標准輸入。 請注意,它在/bin/sh
的上下文中運行,因此它是一個 posix heredoc,而不是一個 ruby 。 通常,這要求結束標記位於第 1 列中,由於縮進,此處並非如此。
然而,因為它被包裹在一個 ruby heredoc 中,在strip_heredoc
應用的strip_heredoc
方法取消了它的縮進,在/bin/sh
看到它之前將內部 heredoc 的整個左側放置在第 1 列中。
/bin/sh
通常也會擴展 heredoc 中的變量,這可能會干擾腳本。 開始標記 'BASH' 周圍的單引號告訴/bin/sh
在將其傳遞給 bash 之前不要在heredoc 內展開任何內容。
但是/bin/sh
在將字符串傳遞給 bash 之前仍然對字符串應用轉義。 這意味着反斜杠轉義必須加倍才能通過/bin/sh
到 bash,即\\
變為\\\\
。
bash 選項-xeu
是可選的。
-eu
參數告訴 bash 以嚴格模式運行,這會在任何失敗或引用未定義變量時停止執行。 這將向 rake 返回一個錯誤,這將停止 rake 任務。 通常,這就是您想要的。 如果您想要正常的 bash 行為,可以刪除這些參數。
bash 的-x
選項和#sh
{verbose: false}
參數#sh
工作,因此 rake 只打印實際執行的 bash 命令。 如果您的 bash 腳本不打算完整運行,這將很有用,例如,如果它有一個允許它在腳本早期正常退出的測試。
如果您不希望 rake 任務失敗,請注意不要設置 0 以外的退出代碼。 通常,這意味着您不想使用任何|| exit
沒有明確設置退出代碼的|| exit
構造,即|| exit 0
|| exit 0
。
%{echo "World!"}
定義了一個字符串。 我希望你想要%x{echo "World!"}
。
%x{echo "World!"}
執行命令並返回輸出 (stdout)。 你不會看到結果。 但你可以這樣做:
puts %x{echo "World!"}
還有更多調用系統命令的方法:
system( cmd )
popen
Open3#popen3
有兩種方式:
sh " expr "
或者
%x( expr )
請注意 ( expr ) 可以是 { expr } , | 表達式 | 或`expr`
不同之處在於, sh "expr" 是一種執行某些操作的 ruby 方法,而 %x( expr ) 是 ruby 內置方法。 結果和行動是不同的。 這是一個例子
task :default do
value = sh "echo hello"
puts value
value = %x(echo world)
puts value
end
得到:
hello # from sh "echo hello"
true # from puts value
world # from puts value
您可以看到%x( expr )
只會執行 shell expr 但標准輸出不會顯示在屏幕上。 因此,當您需要命令結果時,最好使用%x( expr )
。
但是如果你只想做一個shell命令,我建議你使用sh "expr"
。 因為sh "irb"
會讓你進入 irb shell,而%x(irb)
會死。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.