Jak przekazać argumenty wiersza poleceń do zadania prowizji


1096

Mam zadanie prowizji, które musi wstawić wartość do wielu baz danych.

Chciałbym przekazać tę wartość do zadania prowizji z wiersza poleceń lub innego zadania prowizji.

W jaki sposób mogę to zrobić?



3
Dokumenty zostały dublowane przez SeattleRb.
Jonathan Allard

1
Czy możesz zmienić zaakceptowaną odpowiedź na @BlairAnderson, ponieważ zaakceptowana odpowiedź jest obecnie bardzo nieaktualna. To pytanie pojawia się wysoko w Google w tym temacie!
rmcsharry

Odpowiedzi:


377

opcje i zależności muszą znajdować się wewnątrz tablic:

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

Następnie

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

UWAGA: zmienna taskjest obiektem zadania, niezbyt pomocnym, chyba że wiesz / dbasz o elementy wewnętrzne Rake.

UWAGA SZYNY:

W przypadku uruchamiania zadania z szyn najlepiej jest wstępnie załadować środowisko, dodając => [:environment]sposób konfigurowania zadań zależnych .

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

28
Upewnij się również, że nie używasz spacji między argumentami. Np. Nie rób tego: rake thing:work[1, 2, 3]ponieważ to nie zadziała, a otrzymasz błądDon't know how to build task
rpbaltazar

9
Upewnij się również, że umieściłeś argument w ciągu. np. z wiersza poleceń uruchom takie zadanie rake rake thing:work'[1,2,3]'
theterminalguy

36
Niestety, zsh nie może poprawnie przeanalizować połączenia, musisz wpisać polecenie na zsh w ten sposób: rake thing:work\[1,2,3\]lub tenrake 'thing:work[1,2,3]'
hutusi

1
@sakurashinken możesz usunąć :environmentsymbol ze swojego zadania. aplikacje szynowe mają: zadanie środowiskowe ...
Blair Anderson

3
Zamiast mieć notatkę wyjaśniającą, co toznacza task, dlaczego nie użyć po prostu tasknazwy param?
Joshua Pinter

1132

Możesz określić formalne argumenty w rake, dodając argumenty symboli do wywołania zadania. Na przykład:

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

Następnie z wiersza poleceń:

> rake my_task [1, false]
Argumentami były: {: arg1 => „1”,: arg2 => „false”} klasy Rake :: TaskArguments
arg1 było: „1” klasy String
arg2 to: „false” klasy String

> rake „my_task [1, 2]”
Argumentami były: {: arg1 => „1”,: arg2 => „2”}

> rake invoke_my_task
Argumentami były: {: arg1 => „1”,: arg2 => „2”}

> rake invoke_my_task_2
Argumentami były: {: arg1 => 3,: arg2 => 4}

> rake with_prerequisite [5,6]
Argumentami były: {: arg1 => „5”,: arg2 => „6”}

> rake with_defaults
Argumenty z wartościami domyślnymi to: {: arg1 =>: default_1,: arg2 =>: default_2}

> rake with_defaults ['x', 'y']
Argumenty z wartościami domyślnymi to: {: arg1 => "x",: arg2 => "y"}

Jak pokazano w drugim przykładzie, jeśli chcesz używać spacji, cudzysłowy wokół nazwy celu są niezbędne, aby powłoka nie dzieliła argumentów w przestrzeni.

Patrząc na kod w rake.rb , okazuje się, że rake nie analizuje ciągów zadań w celu wyodrębnienia argumentów dotyczących wymagań wstępnych, więc nie możesz tego zrobić task :t1 => "dep[1,2]". Jedynym sposobem na określenie różnych argumentów dla warunku wstępnego byłoby jawne wywołanie go w ramach zależnej akcji zadania, jak w :invoke_my_taski :invoke_my_task_2.

Zauważ, że niektóre powłoki (jak zsh) wymagają ucieczki od nawiasów: rake my_task\['arg1'\]


5
Aby wywołać zadanie w przestrzeni nazw simpy: Rake :: Task ['namespace: task'].
Invoke

1
To osobne pytanie, Igoru, ale powodem, dla którego twoje wywołanie jest uruchamiane tylko raz, jest to, że rake jest zależny od zależności, więc wykona zadanie tylko wtedy, gdy będzie potrzebne. W przypadku zadań ogólnych oznacza to, że jeszcze się nie uruchomił. Aby jawnie wykonać zadanie niezależnie od jego zależności lub jeśli jest ono potrzebne, wywołaj polecenie execute zamiast invoke.
Nick Desjardins

10
Uwaga: Zgodnie z prowizją ta składnia do akceptowania zmiennych w zadaniach jest przestarzała:WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
Ajedi32

57
Zauważ, że zsh nie trafia do analizowania argumenty wiersza polecenia prawidłowo ( zsh: no matches found: ...), więc trzeba się uciec wsporniki: rake my_task\['arg1'\]. Z robots.thoughtbot.com/post/18129303042/…
Seth Bro.

2
@SethBro TAK. Gdyby tylko Twój komentarz nie był ukryty za linkiem „Zobacz więcej komentarzy”, nie zmarnowałbym 10 minut, nie mogąc tego zrobić.
GMA

342

Oprócz odpowiedzi autorstwa kcha (przepraszam, nie znalazłem sposobu na pozostawienie komentarza):

Nie musisz określać zmiennych jako ENVzmiennych przed rakepoleceniem. Możesz po prostu ustawić je jako zwykłe parametry wiersza poleceń:

rake mytask var=foo

i uzyskaj dostęp do tych z pliku rake jako zmienne ENV takie jak:

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

2
To jest najprostsza odpowiedź IMO. Od razu zadziałało. Co dokładnie poznacza?
stevec

1
@ user5783745 Podobnie jak puts, ale zamiast rejestrować value.to_s do standardowego wyjścia, wywołuje Obj.inspect i loguje to do standardowego wyjścia. ruby-doc.org/core-2.0.0/Kernel.html#method-ip
kqcef

I zastąpić zmienne środowiskowe? Fantastyczny!
Damien Roche,

Rake jest całkowicie przepracowanym bałaganem i jest to jedyny sposób, który zadziałał. I to nie tylko ja, ta odpowiedź ma tyle samo głosów, co „poprawna” odpowiedź.
lzap

108

Jeśli chcesz przekazać nazwane argumenty (np. Ze standardem OptionParser), możesz użyć czegoś takiego:

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

zwróć uwagę --, że jest to konieczne do ominięcia standardowych argumentów Rake. Powinien pracować z prowizji 0.9.x , <= 10.3.x .

Nowszy Rake zmienił swoje parsowanie --, a teraz musisz się upewnić, że nie został przekazany do OptionParser#parsemetody, na przykład zparser.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 na końcu upewni się, że dodatkowe argumenty nie będą interpretowane jako zadanie Rake.

Skrót do argumentów powinien również działać:

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

Kiedy skrypty rake wyglądają tak, być może nadszedł czas, aby poszukać innego narzędzia, które pozwoliłoby na to po wyjęciu z pudełka.


13
Z mojej perspektywy to naprawdę najlepsza odpowiedź. Obejście zmiennej środowiskowej kludges, dziwna składnia z argumentami zadań, dodatkowa korzyść dla standardu --option-names. Moją jedyną propozycją byłoby użycie exitzamiast, abortponieważ abortpozostawi ci kod powrotu 1 do powłoki. Jeśli zadanie prowizji jest częścią skryptu wyższego poziomu, częściej przyjmuje się, że niezerowe wyjście jest rodzajem błędu.
Joe

1
Zgadzam się z Joe, to najlepsza odpowiedź. Naturalną rzeczą jest użycie tego samego interfejsu do przekazywania opcji rake, tak jak przy przekazywaniu opcji do skryptu.
Rik Smith-Unna

1
Zgadzam się, że to najlepsza odpowiedź. Czy nie ma sposobu na obejście brzydoty --? Jak przekazywanie rakeargumentów do faktycznego zadania czy coś? Lubisz task :my_task, :*args do |t, args|czy coś?
Augustin Riedinger

1
Poza tym nie rozumiem, po co {username}tu jest. Gdzie jest używany? Dlaczego tego nie ma -u {username}? Na zdrowie
Augustin Riedinger

2
Sposób, w jaki Rake analizuje ARGV został zmieniony 10.4.1i przywrócony 10.4.2. github.com/ruby/rake/commit/…
Tombart

58

Znalazłem odpowiedź z tych dwóch stron internetowych: Net Maniac i Aimred .

Aby skorzystać z tej techniki, musisz mieć wersję> 0,8 rake'u

Normalny opis zadania rake jest następujący:

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

Aby przekazać argumenty, wykonaj trzy czynności:

  1. Dodaj nazwy argumentów po nazwie zadania, oddzielając je przecinkami.
  2. Umieść zależności na końcu, używając: need => [...]
  3. Miejsce | t, args | po zrobieniu. (t jest obiektem tego zadania)

Aby uzyskać dostęp do argumentów w skrypcie, użyj 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

Aby wywołać to zadanie z wiersza poleceń, przekaż mu argumenty w [] s

rake task_name['Hello',4]

wyjdzie

Hello
Hello
Hello
Hello

a jeśli chcesz wywołać to zadanie z innego zadania i przekazać mu argumenty, użyj invoke

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

następnie polecenie

rake caller

wyjdzie

In Caller
hi
hi

Nie znalazłem sposobu na przekazywanie argumentów jako części zależności, ponieważ następujący kod się psuje:

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

15
Format tej funkcjonalności zmienił się, gdy to ostrzeżenie brzmi: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
madh

29

Inną często stosowaną opcją jest przekazywanie zmiennych środowiskowych. W swoim kodzie czytasz je przez ENV['VAR']i możesz przekazać je tuż przed rakepoleceniem, na przykład

$ VAR=foo rake mytask

Szczerze mówiąc, miałem nadzieję na rake task - te --go - do - programu, a moje zadanie może je zdobyć z ARGV. Niestety nie jestem pewien, czy to możliwe, ale obecnie używam Twojego rozwiązania: rake var1 = val1 var2 = val2
JasonSmith

3
@jhs: rake blah -- --these --go --to --a-program(zwróć uwagę na --prowizję, że jej przełączniki się zakończyły), patrz stackoverflow.com/questions/5086224/…
mu jest za krótki

28

Właściwie @Nick Desjardins odpowiedział doskonale. Ale tylko dla edukacji: możesz zastosować brudne podejście: używając ENVargumentów

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

rake my_task myvar=10
#=> myvar: 10

27

Nie mogłem wymyślić, jak przekazywać argumenty, a także: środowisko, dopóki nie opracowałem tego:

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

A potem dzwonię w ten sposób:

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

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

Aby to nazwać, idź:rake task_name[hello, world]
Dex

2
from rake.rubyforge.org/files/doc/rakefile_rdoc.html "Tylko kilka słów ostrzeżenia. Nazwa zadania rake i jego argumenty muszą być pojedynczym argumentem wiersza poleceń, co oznacza, że ​​nie ma spacji. Jeśli spacje są potrzebne , następnie należy zacytować cały ciąg rake + argumentu. Coś w stylu: rake „name [billy bob, smith]” ”
Gayle

19

Chciałem tylko móc biegać:

$ rake some:task arg1 arg2

Proste, prawda? (Nie!)

Rake interpretuje arg1i arg2jako zadania, i próbuje je uruchomić. Więc po prostu przerywamy, zanim to nastąpi.

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

    # your task...

    exit
  end
end

Weź to, nawiasy!

Oświadczenie : Chciałem móc to zrobić w dość małym projekcie dla zwierząt. Nie jest przeznaczony do użycia w „świecie rzeczywistym”, ponieważ tracisz możliwość łączenia zadań rake (tj rake task1 task2 task3.). IMO nie jest tego warte. Po prostu użyj brzydkiego rake task[arg1,arg2].


3
Musiałem to zrobić, _, arg1, arg2 = ARGVponieważ pierwszy argument był nazwą zadania rake. Ale exitto fajna sztuczka.
tłusty

rake task[arg1,arg2] && rake task2 && rake task3Nie jestem pewien, czy to mniej brzydkie niż rake task[arg1,arg2] task2 task3. Prawdopodobnie mniej wydajny.
Nuclearman,

_, *args = ARGVjest idealny do przechwytywania wszystkich kolejnych argumentów! Dzięki stosy!
XtraSimplicity

12

Używam zwykłego argumentu ruby ​​w pliku rake:

DB = ARGV[1]

następnie usuwam zadania rake na dole pliku (ponieważ rake wyszuka zadanie na podstawie nazwy tego argumentu).

task :database_name1
task :database_name2

wiersz poleceń:

rake mytask db_name

wydaje mi się to czystsze niż var = foo ENV var i zadanie argumentuje [bla, bla2] rozwiązania.
odgałęzienie jest trochę szarpane, ale nie jest takie złe, jeśli masz tylko kilka środowisk, które są jednorazową konfiguracją


2
Aby zapobiec problemom z zablokowanymi łańcuchami, użyj dupna końcu: db = ARGV [1] .dup
Juanda

Wydarzenie lepiej, db = ARGV[1].dup unless ARGV[1].nil?aby uniknąć wyjątku kopiowania zera.
Andre Figueiredo

5

Sposoby przekazywania argumentu są poprawne w powyższej odpowiedzi. Jednak aby uruchomić zadanie rake z argumentami, w nowszej wersji szyn występuje niewielka technika

Będzie działał z prowizją „namespace: taskname ['argument1']"

Zwróć uwagę na odwrócone cudzysłowy podczas uruchamiania zadania z wiersza poleceń.


3

Aby przekazać argumenty do domyślnego zadania, możesz zrobić coś takiego. Na przykład powiedz „wersja” to twój argument:

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

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

Możesz to tak nazwać:

$ rake
no version passed

lub

$ rake default[3.2.1]
version is 3.2.1

lub

$ rake build[3.2.1]
version is 3.2.1

Jednak nie znalazłem sposobu, aby uniknąć podania nazwy zadania (domyślnej lub kompilacji) podczas przekazywania argumentów. Chciałbym usłyszeć, jeśli ktoś zna sposób.


3

Podoba mi się składnia „querystring” do przekazywania argumentów, szczególnie gdy jest wiele argumentów do przekazania.

Przykład:

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

„Querystring” to:

width=10&height=20

Ostrzeżenie: zauważ, że składnia jest rake "mytask[foo=bar]"i NIE rake mytask["foo=bar"]

Po przeanalizowaniu za pomocą zadania prowizji Rack::Utils.parse_nested_queryotrzymujemy Hash:

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

(Fajne jest to, że możesz przekazywać skróty i tablice, więcej poniżej)

Oto jak to osiągnąć:

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

Oto bardziej rozbudowany przykład, którego używam z Railsami w moim klejnocie z opóźnieniem_obrotu_aktywnego_rejestrowania_recordu :

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

Przetwarzane w taki sam sposób jak powyżej, z zależnością środowiska (w celu załadowania środowiska 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

Daje następujące w options

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

3

Jeśli nie możesz zaprzątać sobie głowy zapamiętywaniem, która pozycja argumentu jest dla czego, i chcesz zrobić coś w rodzaju skrótu argumentu ruby. Możesz użyć jednego argumentu, aby przekazać ciąg, a następnie ponownie wyrejestruj ten ciąg do skrótu opcji.

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

I w wierszu poleceń pojawi się.

$ 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
Twój kod potrzebuje kilku dobrze umieszczonych pustych wierszy. Nie wiem, jak czytasz tę ścianę tekstu.
Joshua Pinter

2

Większość opisanych powyżej metod nie działała dla mnie, być może są one przestarzałe w nowszych wersjach. Aktualny przewodnik można znaleźć tutaj: http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

Skopiuj i wklej ans z przewodnika jest tutaj:

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

Wywołaj to w ten sposób

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

1

Aby uruchomić zadania prowizji z tradycyjnym stylem argumentów:

rake task arg1 arg2

A następnie użyj:

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

Dodaj następującą łatkę klejnotu prowizji:

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

Podczas przekazywania parametrów lepszym rozwiązaniem jest plik wejściowy, czy może to być excel json lub cokolwiek potrzebujesz, a stamtąd odczytaj potrzebną strukturę danych i zmienne, w tym nazwę zmiennej. Aby odczytać plik, może mieć następującą strukturę.

  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

Przykład json

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

Wykonanie

rake :name_task 

4
Jeśli potrzebujesz pliku instrukcji JSON do zadania Rake, prawdopodobnie robisz zbyt wiele rzeczy w swoim zadaniu Rake.
ZiggyTheHamster

Jest to sposób nadmiernie komplikować coś, co jest niezwykle proste.
jeffdill2
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.