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.