Cara meneruskan argumen baris perintah ke tugas menyapu

1096

Saya punya tugas menyapu yang perlu memasukkan nilai ke beberapa basis data.

Saya ingin meneruskan nilai ini ke tugas menyapu dari baris perintah, atau dari tugas menyapu lain .

Bagaimana saya bisa melakukan ini?

Tilendor
sumber
4
rakefile rdoc
Brian Maltzan
3
Documents telah dicerminkan oleh SeattleRb.
Jonathan Allard
1
Bisakah Anda mengubah jawaban yang diterima ke yang dari @BlairAnderson, karena jawaban yang diterima sudah sangat ketinggalan zaman sekarang. Pertanyaan ini muncul tinggi di Google untuk topik ini!
rmcsharry

Jawaban:

377

opsi dan dependensi harus ada di dalam array:

namespace :thing do
  desc "it does a thing"
  task :work, [:option, :foo, :bar] do |task, args|
    puts "work", args
  end

  task :another, [:option, :foo, :bar] do |task, args|
    puts "another #{args}"
    Rake::Task["thing:work"].invoke(args[:option], args[:foo], args[:bar])
    # or splat the args
    # Rake::Task["thing:work"].invoke(*args)
  end

end

Kemudian

rake thing:work[1,2,3]
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

rake thing:another[1,2,3]
=> another {:option=>"1", :foo=>"2", :bar=>"3"}
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

CATATAN: variabel taskadalah objek tugas, tidak sangat membantu kecuali Anda tahu / peduli tentang Rake internal.

CATATAN KERETA API:

Jika menjalankan tugas dari rails, yang terbaik untuk preload lingkungan dengan menambahkan => [:environment]yang merupakan cara untuk mengatur tugas yang tergantung .

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
    puts "work", args
  end
Blair Anderson
sumber
28
Juga, pastikan Anda tidak menggunakan spasi di antara argumen. Misalnya jangan lakukan ini: rake thing:work[1, 2, 3]karena tidak akan berhasil dan Anda akan mendapatkan kesalahanDon't know how to build task
rpbaltazar
9
Juga, pastikan Anda melampirkan argumen dalam string. mis. dari baris perintah Anda menjalankan tugas menyapu seperti begitu rake thing:work'[1,2,3]'
theterminalguy
36
Sayangnya, zsh tidak dapat menguraikan panggilan dengan benar, Anda perlu mengetikkan perintah pada zsh seperti ini rake thing:work\[1,2,3\]rake 'thing:work[1,2,3]'
:,
1
@sakurashinken Anda dapat menghapus :environmentsimbol dari tugas Anda. aplikasi rel memiliki: tugas lingkungan ...
Blair Anderson
3
Alih-alih memiliki catatan untuk menjelaskan itu tartinya task, mengapa tidak hanya menggunakan tasksebagai nama param?
Joshua Pinter
1132

Anda bisa menentukan argumen formal di rake dengan menambahkan argumen simbol ke panggilan tugas. Sebagai contoh:

require 'rake'

task :my_task, [:arg1, :arg2] do |t, args|
  puts "Args were: #{args} of class #{args.class}"
  puts "arg1 was: '#{args[:arg1]}' of class #{args[:arg1].class}"
  puts "arg2 was: '#{args[:arg2]}' of class #{args[:arg2].class}"
end

task :invoke_my_task do
  Rake.application.invoke_task("my_task[1, 2]")
end

# or if you prefer this syntax...
task :invoke_my_task_2 do
  Rake::Task[:my_task].invoke(3, 4)
end

# a task with prerequisites passes its 
# arguments to it prerequisites
task :with_prerequisite, [:arg1, :arg2] => :my_task #<- name of prerequisite task

# to specify default values, 
# we take advantage of args being a Rake::TaskArguments object
task :with_defaults, :arg1, :arg2 do |t, args|
  args.with_defaults(:arg1 => :default_1, :arg2 => :default_2)
  puts "Args with defaults were: #{args}"
end

Kemudian, dari baris perintah:

> rake my_task [1, false]
Argumennya adalah: {: arg1 => "1",: arg2 => "false"} kelas Rake :: TaskArguments
arg1 adalah: '1' dari String kelas
arg2 adalah: 'false' dari String kelas

> rake "my_task [1, 2]"
Argumennya adalah: {: arg1 => "1",: arg2 => "2"}

> rake invoke_my_task
Argumennya adalah: {: arg1 => "1",: arg2 => "2"}

> rake invoke_my_task_2
Argumennya adalah: {: arg1 => 3,: arg2 => 4}

> rake with_prerequisite [5,6]
Argumennya adalah: {: arg1 => "5",: arg2 => "6"}

> rake with_defaults
Argumen dengan default adalah: {: arg1 =>: default_1,: arg2 =>: default_2}

> rake with_defaults ['x', 'y']
Argumen dengan default adalah: {: arg1 => "x",: arg2 => "y"}

Seperti yang ditunjukkan dalam contoh kedua, jika Anda ingin menggunakan spasi, tanda kutip di sekitar nama target diperlukan untuk menjaga shell agar tidak memecah argumen di spasi.

Melihat kode di rake.rb , tampaknya rake tidak menguraikan string tugas untuk mengekstraksi argumen untuk prasyarat, jadi Anda tidak bisa melakukannya task :t1 => "dep[1,2]". Satu-satunya cara untuk menentukan argumen yang berbeda untuk prasyarat adalah dengan memanggilnya secara eksplisit dalam aksi tugas dependen, seperti pada :invoke_my_taskdan :invoke_my_task_2.

Perhatikan bahwa beberapa cangkang (seperti zsh) mengharuskan Anda untuk keluar dari kurung: rake my_task\['arg1'\]

Nick Desjardins
sumber
5
Untuk menjalankan tugas di dalam namespace simpy, lakukan: Rake :: Task ['namespace: task'].
Invoke
1
Itu pertanyaan terpisah, Igoru, tetapi alasan panggilan Anda untuk memanggil hanya berjalan sekali adalah bahwa rake berorientasi pada ketergantungan, jadi itu hanya akan menjalankan tugas jika diperlukan. Untuk tugas umum, itu berarti jika belum berjalan. Untuk secara eksplisit menjalankan tugas terlepas dari ketergantungannya atau jika diperlukan, panggil eksekusi alih-alih memanggil.
Nick Desjardins
10
Catatan: Menurut rake, sintaks untuk menerima variabel dalam tugas ini sudah usang:WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
Ajedi32
57
Perhatikan bahwa zsh gagal untuk mengurai argumen baris perintah dengan benar ( zsh: no matches found: ...), sehingga Anda perlu untuk melarikan diri kurung: rake my_task\['arg1'\]. Dari robots.thoughtbot.com/post/18129303042/…
Seth Bro
2
@SethBro YA. Andai saja komentar Anda tidak disembunyikan di balik tautan "Lihat lebih banyak komentar", saya tidak akan menyia-nyiakan waktu 10 menit untuk membuat ini berhasil.
GMA
342

Selain menjawab dengan kch (saya tidak menemukan cara untuk meninggalkan komentar itu, maaf):

Anda tidak harus menentukan variabel sebagai ENVvariabel sebelum rakeperintah. Anda bisa mengaturnya sebagai parameter baris perintah seperti biasa:

rake mytask var=foo

dan akses yang dari file rake Anda sebagai variabel ENV seperti:

p ENV['var'] # => "foo"
timurb
sumber
2
Ini adalah jawaban IMO paling sederhana terbaik. Itu bekerja segera. Apa sebenarnya partinya?
stevec
1
@ user5783745 Suka menempatkan tetapi bukannya mencatat value.to_s ke standar keluar itu panggilan Obj.inspect dan mencatat itu ke standar keluar. ruby-doc.org/core-2.0.0/Kernel.html#method-ip
kqcef
Dan menimpa variabel lingkungan? Fantastis!
Damien Roche
Rake adalah kekacauan yang sangat direkayasa dan ini adalah satu-satunya cara yang berhasil. Dan bukan hanya saya, jawaban ini memiliki jumlah suara yang sama dengan jawaban "benar".
lzap
108

Jika Anda ingin memberikan argumen bernama (misalnya dengan standar OptionParser), Anda dapat menggunakan sesuatu seperti ini:

$ rake user:create -- --user test@example.com --pass 123

perhatikan --, itu perlu untuk melewati argumen Rake standar. Harus bekerja dengan Rake 0.9.x , <= 10.3.x .

Rake yang lebih baru telah mengubah penguraiannya --, dan sekarang Anda harus memastikan itu tidak lolos ke OptionParser#parsemetode, misalnya denganparser.parse!(ARGV[2..-1])

require 'rake'
require 'optparse'
# Rake task for creating an account

namespace :user do |args|
  desc 'Creates user account with given credentials: rake user:create'
  # environment is required to have access to Rails models
  task :create do
    options = {}
    OptionParser.new(args) do |opts|
      opts.banner = "Usage: rake user:create [options]"
      opts.on("-u", "--user {username}","User's email address", String) do |user|
        options[:user] = user
      end
      opts.on("-p", "--pass {password}","User's password", String) do |pass|
        options[:pass] = pass
      end
    end.parse!

    puts "creating user account..."
    u = Hash.new
    u[:email] = options[:user]
    u[:password] = options[:pass]
    # with some DB layer like ActiveRecord:
    # user = User.new(u); user.save!
    puts "user: " + u.to_s
    puts "account created."
    exit 0
  end
end

exit pada akhirnya akan memastikan bahwa argumen tambahan tidak akan ditafsirkan sebagai tugas Rake.

Juga jalan pintas untuk argumen harus berfungsi:

 rake user:create -- -u test@example.com -p 123

Ketika skrip rake terlihat seperti ini, mungkin sudah waktunya untuk mencari alat lain yang akan memungkinkan ini keluar dari kotak.

Tombart
sumber
13
Dari sudut pandang saya ini benar-benar jawaban terbaik. Lewati variabel lingkungan kludges, sintaks aneh dengan argumen tugas, manfaat tambahan untuk standar --option-names. Saya hanya saran akan menggunakan exitdaripada abortsebagai abortakan meninggalkan Anda dengan kode kembali 1 ke shell. Jika tugas menyapu adalah bagian dari skrip tingkat yang lebih tinggi itu lebih umum untuk menganggap keluar tidak nol adalah beberapa jenis kesalahan.
Joe
1
Saya setuju dengan Joe, ini adalah jawaban terbaik. Hal yang wajar adalah menggunakan antarmuka yang sama untuk meneruskan opsi untuk menyapu seperti yang Anda lakukan saat meneruskan opsi ke skrip.
Rik Smith-Unna
1
Saya setuju ini adalah jawaban terbaik. Apakah tidak ada cara untuk memotong yang jelek --? Suka menyampaikan rakeargumen ke tugas aktual atau sesuatu? Suka task :my_task, :*args do |t, args|atau apalah?
Augustin Riedinger
1
Selain itu, saya tidak mengerti untuk apa di {username}sini. Di mana itu digunakan? Kenapa tidak ada di sana -u {username}? Cheers
Augustin Riedinger
2
Cara Rake mem-parsing ARGV diubah 10.4.1dan dikembalikan 10.4.2. github.com/ruby/rake/commit/…
Tombart
58

Saya telah menemukan jawaban dari dua situs web ini: Net Maniac dan Aimred .

Anda harus memiliki versi> 0.8 rake untuk menggunakan teknik ini

Deskripsi tugas menyapu normal adalah ini:

desc 'Task Description'
task :task_name => [:depends_on_taskA, :depends_on_taskB] do
  #interesting things
end

Untuk menyampaikan argumen, lakukan tiga hal:

  1. Tambahkan nama argumen setelah nama tugas, dipisahkan dengan koma.
  2. Masukkan dependensi di akhir menggunakan: needs => [...]
  3. Tempat | t, args | setelah do. (t adalah objek untuk tugas ini)

Untuk mengakses argumen dalam skrip, gunakan args.arg_name

desc 'Takes arguments task'
task :task_name, :display_value, :display_times, :needs => [:depends_on_taskA, :depends_on_taskB] do |t, args|
  args.display_times.to_i.times do
    puts args.display_value
  end
end

Untuk memanggil tugas ini dari baris perintah, berikan argumen di [] s

rake task_name['Hello',4]

akan menampilkan

Hello
Hello
Hello
Hello

dan jika Anda ingin memanggil tugas ini dari tugas lain, dan meneruskannya argumen, gunakan invoke

task :caller do
  puts 'In Caller'
  Rake::Task[:task_name].invoke('hi',2)
end

lalu perintahnya

rake caller

akan menampilkan

In Caller
hi
hi

Saya belum menemukan cara untuk meneruskan argumen sebagai bagian dari dependensi, karena kode berikut rusak:

task :caller => :task_name['hi',2]' do
   puts 'In Caller'
end
Tilendor
sumber
15
Format untuk fungsi ini telah berubah ketika peringatan ini menyatakan: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
madh
29

Pilihan lain yang umum digunakan adalah untuk melewatkan variabel lingkungan. Dalam kode Anda, Anda membacanya melalui ENV['VAR'], dan dapat mengirimkannya tepat sebelum rakeperintah, seperti

$ VAR=foo rake mytask
kch
sumber
Terus terang saya berharap untuk tugas menyapu - ini - pergi - ke-program dan tugas saya bisa mendapatkannya dari ARGV. Sayangnya saya tidak yakin apakah itu mungkin namun saya saat ini menggunakan solusi Anda: rake var1 = val1 var2 = val2
JasonSmith
3
@ jhs: rake blah -- --these --go --to --a-program(perhatikan --untuk memberi tahu rake bahwa switch-nya telah berakhir), lihat stackoverflow.com/questions/5086224/…
mu terlalu pendek
28

Sebenarnya @Nick Desjardins menjawab sempurna. Tetapi hanya untuk pendidikan: Anda dapat menggunakan pendekatan kotor: menggunakan ENVargumen

task :my_task do
  myvar = ENV['myvar']
  puts "myvar: #{myvar}"
end 

rake my_task myvar=10
#=> myvar: 10
fl00r
sumber
28

Saya tidak tahu bagaimana cara melewatkan argumen dan juga: lingkungan sampai saya menyelesaikannya:

namespace :db do
  desc 'Export product data'
  task :export, [:file_token, :file_path] => :environment do |t, args|
    args.with_defaults(:file_token => "products", :file_path => "./lib/data/")

       #do stuff [...]

  end
end

Dan kemudian saya sebut seperti ini:

rake db:export['foo, /tmp/']
Nate Flink
sumber
Terima kasih untuk ini, solusi hebat sambil mempertahankan: lingkungan
Olivier JM
24
desc 'an updated version'
task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
    puts args[:arg1]
end
Feng
sumber
Untuk memanggil ini, buka:rake task_name[hello, world]
Dex
2
from rake.rubyforge.org/files/doc/rakefile_rdoc.html "Hanya beberapa kata peringatan. Nama tugas rake dan argumennya harus berupa argumen baris perintah tunggal untuk menyapu. Biasanya ini berarti tidak ada spasi. Jika diperlukan spasi , maka seluruh string argumen rake + harus dikutip. Sesuatu seperti ini: rake "name [billy bob, smith]" "
Gayle
19

Saya hanya ingin bisa berlari:

$ rake some:task arg1 arg2

Sederhana bukan? (Nggak!)

Rake penafsiran arg1dan arg2sebagai tugas, dan mencoba menjalankannya. Jadi kita batalkan saja.

namespace :some do
  task task: :environment do
    arg1, arg2 = ARGV

    # your task...

    exit
  end
end

Ambillah itu, kurung!

Penafian : Saya ingin dapat melakukan ini dalam proyek hewan peliharaan yang cukup kecil. Tidak dimaksudkan untuk penggunaan "dunia nyata" karena Anda kehilangan kemampuan untuk melakukan tugas menyapu (yaitu rake task1 task2 task3). IMO tidak sepadan. Cukup gunakan yang jelek rake task[arg1,arg2].

jassa
sumber
3
Diperlukan untuk menjadikan ini _, arg1, arg2 = ARGVsebagai argumen pertama yang dilihat sebagai nama tugas menyapu. Tapi itu exittrik yang rapi.
lemak
rake task[arg1,arg2] && rake task2 && rake task3Tidak yakin apakah itu kurang jelek dari itu rake task[arg1,arg2] task2 task3. Mungkin kurang efisien.
Nuclearman
_, *args = ARGVsempurna untuk menangkap semua argumen selanjutnya! Terima kasih banyak!
XtraSimplicity
12

Saya menggunakan argumen ruby ​​biasa dalam file rake:

DB = ARGV[1]

kemudian saya mematikan tugas rake di bagian bawah file (karena rake akan mencari tugas berdasarkan nama argumen itu).

task :database_name1
task :database_name2

garis komando:

rake mytask db_name

ini terasa lebih bersih bagi saya daripada solusi var = foo ENV var dan args [blah, blah2].
rintisannya sedikit jenky, tetapi tidak terlalu buruk jika Anda hanya memiliki beberapa lingkungan yang merupakan pengaturan satu kali

djburdick
sumber
2
Untuk mencegah masalah string beku, gunakan dupdi akhir: db = ARGV [1] .dup
Juanda
Acara yang lebih baik db = ARGV[1].dup unless ARGV[1].nil?untuk mencegah pengecualian duping nol.
Andre Figueiredo
5

Cara menyampaikan argumen sudah benar dalam jawaban di atas. Namun untuk menjalankan tugas menyapu dengan argumen, ada sedikit teknis yang terlibat dalam versi rel yang lebih baru

Ini akan bekerja dengan rake "namespace: taskname ['argument1']"

Perhatikan kutipan terbalik dalam menjalankan tugas dari baris perintah.

Asim Mushtaq
sumber
3

Untuk meneruskan argumen ke tugas default, Anda dapat melakukan sesuatu seperti ini. Misalnya, katakan "versi" adalah argumen Anda:

task :default, [:version] => [:build]

task :build, :version do |t,args|
  version = args[:version]
  puts version ? "version is #{version}" : "no version passed"
end

Maka Anda dapat menyebutnya seperti ini:

$ rake
no version passed

atau

$ rake default[3.2.1]
version is 3.2.1

atau

$ rake build[3.2.1]
version is 3.2.1

Namun, saya belum menemukan cara untuk menghindari menentukan nama tugas (default atau build) saat menyampaikan argumen. Akan sangat senang mendengar jika ada yang tahu cara.

Gal
sumber
3

Saya suka sintaks "querystring" untuk argumen yang lewat, terutama ketika ada banyak argumen yang harus disampaikan.

Contoh:

rake "mytask[width=10&height=20]"

The "querystring" adalah:

width=10&height=20

Peringatan: perhatikan bahwa sintaksnya adalah rake "mytask[foo=bar]"dan TIDAK rake mytask["foo=bar"]

Ketika diuraikan di dalam tugas menyapu menggunakan Rack::Utils.parse_nested_query, kita mendapatkan Hash:

=> {"width"=>"10", "height"=>"20"}

(Yang keren adalah Anda bisa melewati hash dan array, lebih lanjut di bawah)

Inilah cara mencapai ini:

require 'rack/utils'

task :mytask, :args_expr do |t,args|
  args.with_defaults(:args_expr => "width=10&height=10")
  options = Rack::Utils.parse_nested_query(args[:args_expr])
end

Berikut adalah contoh yang lebih luas yang saya gunakan dengan Rails di blog saya permata menunda_job_active_record_threaded :

bundle exec rake "dj:start[ebooks[workers_number]=16&ebooks[worker_timeout]=60&albums[workers_number]=32&albums[worker_timeout]=120]"

Diurai dengan cara yang sama seperti di atas, dengan ketergantungan lingkungan (untuk memuat lingkungan Rails)

namespace :dj do
  task :start, [ :args_expr ] => :environment do |t, args|
    # defaults here...
    options = Rack::Utils.parse_nested_query(args[:args_expr])  
  end
end

Memberi yang berikut di options

=> {"ebooks"=>{"workers_number"=>"16", "worker_timeout"=>"60"}, "albums"=>{"workers_number"=>"32", "worker_timeout"=>"120"}}
Abdo
sumber
3

Jika Anda tidak dapat diganggu untuk mengingat apa posisi argumen untuk apa dan Anda ingin melakukan sesuatu seperti hash argumen ruby. Anda bisa menggunakan satu argumen untuk meneruskan string dan kemudian regex string itu menjadi hash opsi.

namespace :dummy_data do
  desc "Tests options hash like arguments"
  task :test, [:options] => :environment do |t, args|
    arg_options = args[:options] || '' # nil catch incase no options are provided
    two_d_array = arg_options.scan(/\W*(\w*): (\w*)\W*/)
    puts two_d_array.to_s + ' # options are regexed into a 2d array'
    string_key_hash = two_d_array.to_h
    puts string_key_hash.to_s + ' # options are in a hash with keys as strings'
    options = two_d_array.map {|p| [p[0].to_sym, p[1]]}.to_h
    puts options.to_s + ' # options are in a hash with symbols'
    default_options = {users: '50', friends: '25', colour: 'red', name: 'tom'}
    options = default_options.merge(options)
    puts options.to_s + ' # default option values are merged into options'
  end
end

Dan di baris perintah Anda dapatkan.

$ rake dummy_data:test["users: 100 friends: 50 colour: red"]
[["users", "100"], ["friends", "50"], ["colour", "red"]] # options are regexed into a 2d array
{"users"=>"100", "friends"=>"50", "colour"=>"red"} # options are in a hash with keys as strings
{:users=>"100", :friends=>"50", :colour=>"red"} # options are in a hash with symbols
{:users=>"100", :friends=>"50", :colour=>"red", :name=>"tom"} # default option values are merged into options
xander-miller
sumber
1
Kode Anda memerlukan beberapa baris kosong yang ditempatkan dengan baik. Saya tidak tahu bagaimana Anda membaca dinding teks itu.
Joshua Pinter
2

Sebagian besar metode yang dijelaskan di atas tidak bekerja untuk saya, mungkin mereka sudah usang dalam versi yang lebih baru. Panduan terbaru dapat ditemukan di sini: http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

copy dan paste ans dari panduan ada di sini:

task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args|
  # You can use args from here
end

Panggil seperti ini

bin/rake "task_name[value 1]" # entire argument string should be quoted
hexinpeter
sumber
1

Untuk menjalankan tugas menyapu dengan gaya argumen tradisional:

rake task arg1 arg2

Dan kemudian gunakan:

task :task do |_, args|
  puts "This is argument 1: #{args.first}"
end

Tambahkan sepetak permata menyapu berikut:

Rake::Application.class_eval do

  alias origin_top_level top_level

  def top_level
    @top_level_tasks = [top_level_tasks.join(' ')]
    origin_top_level
  end

  def parse_task_string(string) # :nodoc:
    parts = string.split ' '
    return parts.shift, parts
  end

end

Rake::Task.class_eval do

  def invoke(*args)
    invoke_with_call_chain(args, Rake::InvocationChain::EMPTY)
  end

end
Daniel Garmoshka
sumber
-5

Ketika melewati parameter, pilihan yang lebih baik adalah file input, dapatkah ini menjadi excel json atau apa pun yang Anda butuhkan dan dari sana baca struktur data dan variabel yang Anda perlukan dari itu termasuk nama variabel sebagaimana kebutuhan. Untuk membaca file dapat memiliki struktur berikut.

  namespace :name_sapace_task do
    desc "Description task...."
      task :name_task  => :environment do
        data =  ActiveSupport::JSON.decode(File.read(Rails.root+"public/file.json")) if defined?(data)
    # and work whit yoour data, example is data["user_id"]

    end
  end

Contoh json

{
  "name_task": "I'm a task",
  "user_id": 389,
  "users_assigned": [389,672,524],
  "task_id": 3
}

Eksekusi

rake :name_task 
tundervirld
sumber
4
Jika Anda memerlukan file instruksi JSON untuk tugas Rake Anda, Anda mungkin melakukan terlalu banyak hal dalam tugas Rake Anda.
ZiggyTheHamster
Ini adalah cara yang over-rumit sesuatu yang sangat sederhana.
jeffdill2