Come passare gli argomenti della riga di comando a un'attività di rake


1096

Ho un'attività di rake che deve inserire un valore in più database.

Vorrei passare questo valore nell'attività rake dalla riga di comando o da un'altra attività rake.

Come posso fare questo?



3
I documenti sono stati sottoposti a mirroring da SeattleRb.
Jonathan Allard,

1
Potete per favore cambiare la risposta accettata a quella di @BlairAnderson, poiché la risposta accettata è molto obsoleta ora. Questa domanda appare in alto su Google per questo argomento!
rmcsharry,

Risposte:


377

le opzioni e le dipendenze devono trovarsi all'interno di 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

Poi

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"}

NOTA: variabile taskè l'oggetto dell'attività, non molto utile se non si conosce / si preoccupa degli interni di Rake.

NOTA BINARIO:

Se si esegue l'attività da binari, è meglio precaricare l'ambiente aggiungendo => [:environment]quale è un modo per impostare attività dipendenti .

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
    puts "work", args
  end

28
Inoltre, assicurati di non usare spazi tra gli argomenti. Ad esempio, non farlo: rake thing:work[1, 2, 3]poiché non funzionerà e otterrai un erroreDon't know how to build task
rpbaltazar,

9
Inoltre, assicurati di racchiudere l'argomento nella stringa. ad es. dalla tua riga di comando esegui l'attività rake in questo modo rake thing:work'[1,2,3]'
theterminalguy

36
Sfortunatamente, zsh non può analizzare correttamente la chiamata, è necessario digitare il comando su zsh in questo modo: rake thing:work\[1,2,3\]o questorake 'thing:work[1,2,3]'
hutusi

1
@sakurashinken puoi rimuovere il :environmentsimbolo dalla tua attività. le applicazioni rotaie hanno un compito: ambiente ...
Blair Anderson,

3
Invece di avere una nota per spiegare ciò tsignifica task, perché non usare semplicemente taskcome nome param?
Joshua Pinter

1132

È possibile specificare argomenti formali nel rake aggiungendo argomenti simbolo alla chiamata attività. Per esempio:

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

Quindi, dalla riga di comando:

> rake my_task [1, false]
Gli argomenti erano: {: arg1 => "1",: arg2 => "false"} della classe Rake :: TaskArguments
arg1 era: '1' della classe String
arg2 era: 'false' della classe String

> rake "my_task [1, 2]"
Gli argomenti erano: {: arg1 => "1",: arg2 => "2"}

> rake invoke_my_task
Gli argomenti erano: {: arg1 => "1",: arg2 => "2"}

> rake invoke_my_task_2
Gli argomenti erano: {: arg1 => 3,: arg2 => 4}

> rake with_prerequisite [5,6]
Gli argomenti erano: {: arg1 => "5",: arg2 => "6"}

> rake with_defaults
Gli argomenti con valori predefiniti erano: {: arg1 =>: default_1,: arg2 =>: default_2}

> rake with_defaults ['x', 'y']
Gli argomenti con valori predefiniti erano: {: arg1 => "x",: arg2 => "y"}

Come dimostrato nel secondo esempio, se si desidera utilizzare gli spazi, le virgolette attorno al nome di destinazione sono necessarie per impedire alla shell di dividere gli argomenti nello spazio.

Guardando il codice in rake.rb , sembra che rake non analizzi le stringhe di attività per estrarre argomenti per i prerequisiti, quindi non puoi farlo task :t1 => "dep[1,2]". L'unico modo per specificare argomenti diversi per un prerequisito sarebbe invocarlo esplicitamente all'interno dell'azione dell'attività dipendente, come in :invoke_my_taske :invoke_my_task_2.

Nota che alcune shell (come zsh) richiedono di sfuggire alle parentesi: rake my_task\['arg1'\]


5
Per invocare un'attività all'interno di uno spazio dei nomi, fai semplicemente: Rake :: Task ['namespace: task'].
Invoke

1
Questa è una domanda separata, Igoru, ma il motivo per cui la tua chiamata a invocare viene eseguita solo una volta è che il rake è orientato alla dipendenza, quindi eseguirà un'attività solo se è necessario. Per attività generiche ciò significa che non è già stato eseguito. Per eseguire esplicitamente un'attività indipendentemente dalle sue dipendenze o se è necessaria, chiamare execute invece di invoke.
Nick Desjardins,

10
Nota: secondo il rake, questa sintassi per accettare le variabili nelle attività è obsoleta:WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
Ajedi32

57
Si noti che zsh non riesce ad analizzare gli argomenti della riga di comando in modo corretto ( zsh: no matches found: ...), quindi è necessario sfuggire le parentesi: rake my_task\['arg1'\]. Da robots.thoughtbot.com/post/18129303042/…
Seth Bro

2
@SethBro YES. Se solo il tuo commento non fosse stato nascosto dietro il link "Visualizza altri commenti" non avrei perso 10 minuti incapace di farlo funzionare.
GMA

342

Oltre a rispondere per kch (non ho trovato il modo di lasciare un commento, mi dispiace):

Non è necessario specificare le variabili come ENVvariabili prima del rakecomando. Puoi semplicemente impostarli come al solito parametri della riga di comando come questo:

rake mytask var=foo

e accedi a quelli dal tuo file rake come variabili ENV come queste:

p ENV['var'] # => "foo"

2
Questa è la migliore risposta IMO più semplice. Ha funzionato subito. Cosa significa esattamente p?
Stevec,

1
@ user5783745 Like put ma invece di registrare value.to_s per standard out chiama Obj.inspect e registra quello per standard out. ruby-doc.org/core-2.0.0/Kernel.html#method-ip
kqcef

E sovrascrivere le variabili di ambiente? Fantastico!
Damien Roche,

Il rastrello è un disastro assolutamente riprogettato e questo è l'unico modo che ha funzionato. E non sono solo io, questa risposta ha lo stesso numero di voti della risposta "corretta".
lzap,

108

Se si desidera passare argomenti denominati (ad esempio con standard OptionParser) è possibile utilizzare qualcosa del genere:

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

notare --che è necessario per bypassare gli argomenti standard di Rake. Dovrebbe funzionare con Rake 0.9.x , <= 10.3.x .

Il rake più recente ha modificato l'analisi --e ora è necessario assicurarsi che non sia passato al OptionParser#parsemetodo, ad esempio conparser.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 alla fine si assicurerà che gli argomenti extra non vengano interpretati come attività Rake.

Anche il collegamento per gli argomenti dovrebbe funzionare:

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

Quando gli script rake sembrano così, forse è il momento di cercare un altro strumento che lo consenta immediatamente.


13
Dal mio punto di vista questa è davvero la risposta migliore. Ignora kludges di variabili di ambiente, strana sintassi con argomenti di attività, il vantaggio aggiuntivo per lo standard --option-names. Il mio unico suggerimento sarebbe di usare exitpiuttosto che abortcome abortti lascerà con un codice di ritorno di 1 alla shell. Se l'attività di rake fa parte di uno script di livello superiore è più comune supporre che un'uscita diversa da zero sia un tipo di errore.
Joe

1
Sono d'accordo con Joe, questa è la risposta migliore. La cosa naturale è usare la stessa interfaccia per passare le opzioni da rastrellare come faresti quando si passano le opzioni a uno script.
Rik Smith-Unna,

1
Sono d'accordo che questa è la risposta migliore. Non c'è un modo per aggirare i brutti --? Come passare rakeargomenti al compito reale o qualcosa del genere? Ti piace task :my_task, :*args do |t, args|o qualcosa del genere?
Augustin Riedinger,

1
Inoltre, non capisco a cosa {username}serva. Dove è usato? Perché non c'è -u {username}? Saluti
Augustin Riedinger

2
Il modo in cui Rake analizza ARGV è stato modificato 10.4.1e ripristinato 10.4.2. github.com/ruby/rake/commit/…
Tombart

58

Ho trovato la risposta da questi due siti Web: Net Maniac e Aimred .

Per utilizzare questa tecnica devi avere una versione> 0.8 del rake

La normale descrizione dell'attività del rake è questa:

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

Per passare argomenti, fai tre cose:

  1. Aggiungi i nomi degli argomenti dopo il nome dell'attività, separati da virgole.
  2. Metti le dipendenze alla fine usando: needs => [...]
  3. Luogo | t, args | dopo il do. (t è l'oggetto per questa attività)

Per accedere agli argomenti nello script, utilizzare 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

Per chiamare questa attività dalla riga di comando, passagli gli argomenti in [] s

rake task_name['Hello',4]

uscirà

Hello
Hello
Hello
Hello

e se si desidera chiamare questa attività da un'altra attività e passare argomenti, utilizzare invoke

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

quindi il comando

rake caller

uscirà

In Caller
hi
hi

Non ho trovato un modo per passare argomenti come parte di una dipendenza, poiché il seguente codice si rompe:

task :caller => :task_name['hi',2]' do
   puts 'In Caller'
end

15
Il formato di questa funzionalità è cambiato quando questo avviso indica: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
madh

29

Un'altra opzione comunemente usata è passare variabili d'ambiente. Nel tuo codice li leggi tramite ENV['VAR']e puoi passarli subito prima del rakecomando, come

$ VAR=foo rake mytask

Francamente speravo in un compito rake - questi --go - in un programma e il mio compito poteva ottenerli dall'ARGV. Sfortunatamente non sono sicuro che sia possibile, ma attualmente sto usando la tua soluzione: rake var1 = val1 var2 = val2
JasonSmith

3
@jhs: rake blah -- --these --go --to --a-program(nota --per dire a rake che i suoi switch sono terminati), vedi stackoverflow.com/questions/5086224/…
mu è troppo corto il

28

In realtà @Nick Desjardins ha risposto perfettamente. Ma solo per l'istruzione: puoi usare un approccio sporco: usando l' ENVargomento

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

rake my_task myvar=10
#=> myvar: 10

28

Non sono riuscito a capire come superare args e anche l'ambiente: fino a quando non ho capito:

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

E poi chiamo così:

rake db:export['foo, /tmp/']

Grazie per questo, ottima soluzione pur mantenendo l'ambiente
Olivier JM

24
desc 'an updated version'
task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
    puts args[:arg1]
end

Per chiamare questo, vai:rake task_name[hello, world]
Dex,

2
da rake.rubyforge.org/files/doc/rakefile_rdoc.html "Solo alcune parole di cautela. Il nome dell'attività del rake e i suoi argomenti devono essere un singolo argomento della riga di comando da rastrellare. Questo generalmente significa che non ci sono spazi. Se sono necessari spazi , quindi è necessario citare l'intera stringa rake + argomento. Qualcosa del genere: rake "name [billy bob, smith]" "
Gayle,

19

Volevo solo essere in grado di eseguire:

$ rake some:task arg1 arg2

Semplice vero? (No!)

Rake interpreta arg1e arg2come attività e cerca di eseguirle. Quindi interrompiamo prima di farlo.

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

    # your task...

    exit
  end
end

Prendi questo, parentesi!

Disclaimer : volevo essere in grado di farlo in un progetto piuttosto piccolo per animali domestici. Non destinato all'uso nel "mondo reale" poiché perdi la capacità di incatenare attività (ad es rake task1 task2 task3.). IMO non ne vale la pena. Usa solo il brutto rake task[arg1,arg2].


3
Dovevo farlo _, arg1, arg2 = ARGVperché il primo argomento era visto come il nome dell'attività di rake. Ma questo exitè un trucco accurato.
grasso,

rake task[arg1,arg2] && rake task2 && rake task3Non sono sicuro che sia meno brutto di rake task[arg1,arg2] task2 task3. Probabilmente meno efficiente però.
Nuclearman,

_, *args = ARGVè perfetto per catturare tutti gli argomenti successivi! Grazie mille!
XtraSimplicity,

12

Uso un normale argomento ruby ​​nel file rake:

DB = ARGV[1]

poi stub le attività di rake nella parte inferiore del file (poiché rake cercherà un'attività basata sul nome di quell'argomento).

task :database_name1
task :database_name2

riga di comando:

rake mytask db_name

questo mi sembra più pulito della var = foo ENV var e il compito args [blah, blah2] soluzioni.
lo stub è un po 'ingenuo, ma non troppo male se hai solo alcuni ambienti che sono una configurazione singola


2
Per evitare problemi con le stringhe bloccate, utilizzare dupalla fine: db = ARGV [1] .dup
Juanda

Evento migliore db = ARGV[1].dup unless ARGV[1].nil?per evitare l'eccezione di duping zero.
Andre Figueiredo,

5

I modi per passare l'argomento sono corretti nella risposta sopra. Tuttavia, per eseguire attività di rake con argomenti, è presente un piccolo tecnicismo nella versione più recente delle rotaie

Funzionerà con rake "namespace: taskname ['argomento1']"

Nota le virgolette invertite nell'esecuzione dell'attività dalla riga di comando.


3

Per passare argomenti all'attività predefinita, puoi fare qualcosa del genere. Ad esempio, dire "versione" è il tuo argomento:

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

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

Quindi puoi chiamarlo così:

$ rake
no version passed

o

$ rake default[3.2.1]
version is 3.2.1

o

$ rake build[3.2.1]
version is 3.2.1

Tuttavia, non ho trovato un modo per evitare di specificare il nome dell'attività (impostazione predefinita o build) durante il passaggio degli argomenti. Mi piacerebbe sapere se qualcuno conosce un modo.


3

Mi piace la sintassi "querystring" per il passaggio degli argomenti, specialmente quando ci sono molti argomenti da passare.

Esempio:

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

La "querystring" è:

width=10&height=20

Attenzione: nota che la sintassi è rake "mytask[foo=bar]"e NON rake mytask["foo=bar"]

Quando analizzato all'interno dell'attività del rake usando Rack::Utils.parse_nested_query, otteniamo un Hash:

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

(Il bello è che puoi passare hash e array, più sotto)

Ecco come raggiungere questo obiettivo:

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

Ecco un esempio più esteso che sto usando con Rails nella mia gemma delayed_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]"

Analizzato come sopra, con una dipendenza dall'ambiente (per caricare l'ambiente 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

Fornisce quanto segue options

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

3

Se non puoi essere disturbato a ricordare quale posizione dell'argomento è per cosa e vuoi fare qualcosa come un hash dell'argomento rubino. È possibile utilizzare un argomento per passare una stringa e quindi regexare quella stringa in un hash delle opzioni.

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

E dalla riga di comando si ottiene.

$ 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

1
Il codice richiede alcune righe vuote ben posizionate. Non so come hai letto quel muro di testo.
Joshua Pinter

2

La maggior parte dei metodi sopra descritti non ha funzionato per me, forse sono deprecati nelle versioni più recenti. La guida aggiornata è disponibile qui: http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

un copia-e-incolla ans dalla guida è qui:

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

Invocalo in questo modo

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

1

Per eseguire attività di rake con lo stile degli argomenti tradizionali:

rake task arg1 arg2

E poi usa:

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

Aggiungi la seguente patch di rake gem:

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

-5

Mentre passa i parametri, è meglio l'opzione è un file di input, può essere un eccellente json o qualunque cosa tu abbia bisogno e da lì leggere la struttura dei dati e le variabili di cui hai bisogno tra cui il nome della variabile come è necessario. Per leggere un file può avere la seguente struttura.

  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

Esempio JSON

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

Esecuzione

rake :name_task 

4
Se hai bisogno di un file di istruzioni JSON per la tua attività Rake, probabilmente stai facendo troppe cose nella tua attività Rake.
ZiggyTheHamster

Questo è modo di complicare eccessivamente qualcosa che è incredibilmente semplice.
jeffdill2,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.