12 stycznia 2008

Uzupełnianie w bashu i pierwsze kroki z JRuby

Moja saga rozpoznawania uzupełniania w bashu nadal trwa. Do zabawy przyłączył się koziołek, który na swoim blogu opublikował skrypt uzupełnieniowy dla maven - Podpowiadanie dla mavena w bashu. Wystarczy zatem zapisać ów skrypcik do katalogu /etc/bash_completion.d jako mvn, w którym koniecznie dodajemy jeszcze complete -F _mvn mvn na końcu i uruchomić uzupełnianie basha poprzez wczytanie skryptu /etc/bash_completion.

jlaskowski@dev /etc/bash_completion.d
$ pwd
/etc/bash_completion.d

jlaskowski@dev /etc/bash_completion.d
$ cat mvn
_mvn()
{

local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-Dversion= -DarchetypeArtifactId= -DartifactId= -DgroupId= -q --quiet -C
--strict-checksum -c --lax-checksums -P --activate-profiles -ff --fail-fast
-fae --fail-at-end -B --batch-mode -fn --fail-never
-up --update-plugins -N --non-recursive -npr --no-plugin-registr
-U --update-snapshots -cpu --check-plugin-updates -npu --no-plugin-updates
-D --define -X --debug -e --errors -f --file -h --help
-o --offline -r --reactor -s --settings -v --version -?
clean clean:clean compiler:compile compiler:testCompile compile
deploy:deploy deploy:deploy-file deploy install:install install:install-file
install resources:resources resources:testResources site:deploy
site:attach-descriptor site:site
site:run site:stage-deploy site:stage site surefire:test test verifier:verify verify ear:ear
ear:generate-application-xml ejb:ejb jar:jar jar:test-jar jar:sign jar:sign-verify rar:rar
war:war war:exploded war:inplace changelog:changelog changelog:dev-activity
changelog:file-activity changes:announcement-mail changes:announcement-generate
changes:changes-report changes:jira-report checkstyle:checkstyle checkstyle:check
clover:aggregate clover:check clover:instrumentInternal clover:instrument clover:log
clover:clover clover:save-history doap:generate docck:check javadoc:javadoc
javadoc:test-javadoc javadoc:jar jxr:jxr jxr:test-jxr pmd:pmd pmd:cpd pmd:check pmd:cpd-check
project-info-reports:cim project-info-reports:dependencies
project-info-reports:dependency-convergence project-info-reports:issue-tracking
project-info-reports:license project-info-reports:mailing-list project-info-reports:index
project-info-reports:summary project-info-reports:scm project-info-reports:project-team
surefire-report:report surefire-report:report-only ant:ant ant:clean ant antrun:run
archetype:create archetype:create-from-project -DarchetypeArtifactId=maven-archetype-archetype
-DarchetypeArtifactId=maven-archetype-j2ee-simple -DarchetypeArtifactId=maven-archetype-mojo
-DarchetypeArtifactId=maven-archetype-portlet -DarchetypeArtifactId=maven-archetype-profiles
-DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeArtifactId=maven-archetype-simple
-DarchetypeArtifactId=maven-archetype-site -DarchetypeArtifactId=maven-archetype-site-simple
-DarchetypeArtifactId=maven-archetype-webapp assembly:assembly assembly:attached
assembly:directory assembly:directory-inline assembly:unpack assembly:single
assembly:directory-single dependency:copy dependency:copy-dependencies dependency:unpack
dependency:unpack-dependencies dependency:resolve dependency:list dependency:sources
dependency:resolve-plugins dependency:list dependency:go-offline
dependency:purge-local-repository dependency:build-classpath dependency:analyze
dependency:analyze-only dependency:analyze-dep-mgt dependency:tree enforcer:enforce
enforcer:enforce-once enforcer:display-info gpg:sign gpg:sign-and-deploy-file
help:active-profiles help:describe help:effective-pom help:effective-settings invoker:run
one:convert one:deploy-maven-one-repository one:install-maven-one-repository one:maven-one-plugin
plugin:descriptor plugin:report plugin:updateRegistry plugin:xdoc
plugin:addPluginArtifactMetadata release:clean release:perform release:prepare release:rollback
release:branch remote-resources:bundle remote-resources:process scm:add scm:changelog scm:checkin
scm:checkout scm:diff scm:edit scm:status scm:tag scm:unedit scm:update scm:validate
source:aggregate source:jar source:test-jar eclipse:configure-workspace eclipse:eclipse
eclipse:clean eclipse:m2eclipse eclipse:to-maven eclipse:install-plugins eclipse:make-artifacts
eclipse:myeclipse eclipse:myeclipse-clean eclipse:rad eclipse:rad-clean idea:idea idea:project
idea:module idea:workspace idea:clean build-helper:add-source build-helper:add-test-source
build-helper:attach-artifact castor:generate jdepend:generate native:initialize native:compile
native:link native:javah native:ranlib native:resource-compile native:compile-message sql:execute
taglist:taglist cargo:start cargo:stop cargo:deployer-deploy cargo:deploy
cargo:deployer-undeploy cargo:undeploy cargo:deployer-start cargo:deployer-stop
cargo:deployer-redeploy cargo:uberwar cargo:install jaxme:jaxme jetty:run jetty:run-war
jetty:run-exploded"

if [[ ${cur} == * ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi

}
complete -F _mvn mvn

jlaskowski@dev /etc/bash_completion.d
$ . /etc/bash_completion

jlaskowski@dev /etc/bash_completion.d
$ mvn [TAB][TAB]
Display all 248 possibilities? (y or n)n

jlaskowski@dev /etc/bash_completion.d
$ mvn --[TAB][TAB]
--activate-profiles --define --fail-never --no-plugin-registr --quiet --update-plugins
--batch-mode --errors --file --no-plugin-updates --reactor --update-snapshots
--check-plugin-updates --fail-at-end --help --non-recursive --settings --version
--debug --fail-fast --lax-checksums --offline --strict-checksum
Fajnie byłoby utworzyć uzupełnianie dla innych poleceń, a zaoszczędzimy kilka minut na pisanie tych samych poleceń każdorazowo przy ich uruchamianiu.

Nie jest to jedyna możliwość polecenia complete w bashu. Można również wykonać polecenie, które zbuduje potrzebne informacje dla complete. Wykonanie skryptu następuje poprzez wywołanie z opcją -C. Spójrzmy na przykład skryptu napisanego w...ruby.

jlaskowski@dev /etc/bash_completion.d
$ cat rake
complete -C ~/rake-completion.rb -o default rake

jlaskowski@dev /etc/bash_completion.d
$ cat ~/rake-completion.rb
#!/usr/bin/env ruby

# Complete rake tasks script for bash
# Save it somewhere and then add
# complete -C path/to/script -o default rake
# to your ~/.bashrc
# Nicholas Seckar

exit 0 unless File.file?(File.join(Dir.pwd, 'Rakefile'))
exit 0 unless /^rake(?:\s+([-\w]+))?\s*$/ =~ ENV["COMP_LINE"]

task_prefix = $1

tasks = `rake --tasks`.split("\n")[1..-1].collect {|line| line.split[1]}
tasks = tasks.select {|t| /^#{Regexp.escape task_prefix}/ =~ t} if task_prefix
puts tasks
exit 0


jlaskowski@dev /etc/bash_completion.d
$ rake [TAB][TAB]
bitkeeper clisp freeciv gnatmake isql lisp modules openejb rake sitecopy unace
bittorrent cygport gcl harbour larch mailman mtx p4 ri snownews unrar
cksfv dsniff gkrellm hg lilypond mcrypt mvn povray sbcl svk
Tym razem lista zawiera nazwy plików w bieżącym katalogu, czyli domyślne zachowanie complete w bashu. Wynika to z działania samego skryptu rake-completion.rb, który zakończy działanie, kiedy w bieżącym katalogu nie ma pliku Rakefile.

exit 0 unless File.file?(File.join(Dir.pwd, 'Rakefile'))
Pozostaje zatem dodać plik Rakefile i wypróbować polecenie ponownie. Niestety po chwili okazuje się, że rake dostarczany z JRuby 1.1 RC1 odmawia współpracy kończąc się z następującym komunikatem pod Cygwin:

jlaskowski@dev /etc/bash_completion.d
$ rake
Error opening script file: C:/apps/cygwin/etc/bash_completion.d/cygdrive/c/apps/jruby/bin/rake (The system cannot find the path specified)
Mimo wszystko warto rozpocząć poznawanie konstrukcji językowych Ruby na przykładzie zaprezentowanego wyżej skryptu, który poza sprawdzeniem, czy w bieżącym katalogu istnieje plik Rakefile, odczytuje podane polecenie na linii poleceń.

exit 0 unless /^rake(?:\s+([-\w]+))?\s*$/ =~ ENV["COMP_LINE"]
Na szczęście z analizą w/w polecenia pomogła mi książka Learning Ruby Michaela Fitzgeralda (wydawnictwo O'Reilly), gdzie w rozdziale Regular Expressions na stronie 74 znalazłem potrzebne informacje.

  • /pattern/ to specyfikacja wzorca
  • ^rake określa, że interesujące są wyłącznie linie rozpoczynające się ciągiem rake
  • (?:..) oznacza, że grupujemy wyrażenie bez zapamiętania pasującego tekstu
  • \s pasuje do dowolnego łańcucha białych znaków (tabulacja, nowa linia, powrót karetki i \f - a to nie wiem, co oznacza)
  • + pasuje do wystąpienia pojedyńczego bądź więcej wyrażeń regularnych, których dotyczy
  • [..] pasuje do dowolnego znaku w nawiasach
  • \w dowolny ciąg znaków
W ten sposób polecenie rozpoznaje dowolne polecenie wydane z rake. W przypadku tego skryptu niepoprawnie zakończy się linia rake --tasks, które kończy się komunikatem błędu, więc cały skrypt nic nie zwróci i w ten sposób complete również.

jlaskowski@dev /etc/bash_completion.d
$ rake --tasks
Error opening script file: C:/apps/cygwin/etc/bash_completion.d/cygdrive/c/apps/jruby/bin/rake (The system cannot find the path specified)

jlaskowski@dev /etc/bash_completion.d
$ rake polecenie[TAB][TAB]

W zamian można popróbować się z jruby i jego poleceniami. Chętni?