Eu tenho essas funções em ~/.bashrc
:
function guard() {
if [ -e 'Gemfile' ]; then
bundle exec guard "$@"
else
command guard "$@"
fi
}
function rspec() {
if [ -e 'Gemfile' ]; then
bundle exec rspec "$@"
else
command rspec "$@"
fi
}
function rake() {
if [ -e 'Gemfile' ]; then
bundle exec rake "$@"
else
command rake "$@"
fi
}
Como você vê, essas funções são muito semelhantes. Eu quero definir essas 3 funções ao mesmo tempo. Existe uma maneira de fazer isso?
meio Ambiente
bash --version
GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
for loop?
eu quero dizer, variáveis declaradasfor loop
geralmente desaparecem - eu esperaria o mesmo de funções pelas mesmas razões.bash -c 'for i in 1; do :; done; echo $i'
=>1
. Ostype
mostra claramente que as funções existem fora do âmbito do loop.bash
o escopo dinâmico de tudo o que você pode obter é umalocal
variável local para o escopo de uma função inteira , as variáveis definitivamente não "desaparecem" após um loop. De fato, como não há função envolvida aqui, nem mesmo é possível definir umalocal
variável nesse caso.A vontade acima,
. source /dev/fd/3
que é inserida na_gem_dec()
função toda vez que é chamada comohere-document. _gem_dec's
tarefa apenas pré-avaliada, recebe um parâmetro e a pré-avalia comobundle exec
alvo e como nome da função na qual é direcionada.NOTE: . sourcing shell expansions results in twice-evaluated variables - just like eval. It can be risky.
No caso acima, porém, não acho que possa haver qualquer risco.
Se o bloco de código acima for copiado em um
.bashrc
arquivo, o shell não apenas funcionará_guard(), _rspec()
e_rake()
será declarado no login, mas a_gem_dec()
função também estará disponível para execução a qualquer momento, no prompt do shell (ou outro) e, portanto, novas funções de modelo podem seja declarado sempre que quiser com apenas:E obrigado a @ Andrew por me mostrar que estes não seriam comidos por um
for loop.
MAS COMO?
Eu uso o
3
descritor de arquivo acima para manterstdin, stdout, and stderr, or <&0 >&1 >&2
o hábito aberto - embora, como também seja o caso de algumas das outras precauções padrão que eu implemente aqui - porque a função resultante é muito simples, não é realmente necessário. É uma boa prática, no entanto. Ligarshift $#
é outra dessas precauções desnecessárias.Ainda assim, quando um arquivo é especificado como
<input
ou>output
com[optional num]<file
ou[optional num]>file
redireciona, o kernel o lê em um descritor de arquivo, que pode ser acessado através doscharacter device
arquivos especiais em/dev/fd/[0-9]*
. Se o[optional num]
especificador for omitido,0<file
será assumido para entrada e1>file
saída. Considere isto:E como a
here-document
é apenas um meio de descrever um arquivo embutido dentro de um bloco de código, quando fazemos:Nós também podemos:
Com uma distinção muito importante . Se você não fizer
"'\quote'"
o<<"'\LIMITER"'
de ahere-document
, o shell o avaliará$expansion
como:Portanto, para
_gem_dec()
, o3<<-FUNC here-document
é avaliado como um arquivo na entrada, o mesmo que seria se fosse,3<~/some.file
exceto que, porque deixamos oFUNC
limitador livre de aspas, ele é primeiro avaliado para$expansion.
o importante: isso é entrada, significando existe apenas para,_gem_dec(),
mas também é avaliado antes da_gem_dec()
função ser executada, porque nosso shell precisa ler e avaliar seus dados$expansions
antes de entregá-los como entrada.Vamos fazer,
guard,
por exemplo:Então, primeiro o shell precisa lidar com a entrada, o que significa ler:
No descritor de arquivo 3 e avaliando-o para expansão do shell. Se nesse momento você executou:
Ou:
Como os dois comandos são equivalentes, você verá *:
... antes de qualquer código na função ser executado. Esta é a função
<input
, afinal. Para mais exemplos, veja minha resposta para uma pergunta diferente aqui .(* Tecnicamente, isso não é completamente verdade. Como eu uso uma liderança
-dash
antes dahere-doc limiter
, as opções acima seriam justificadas à esquerda. Mas eu usei o-dash
que eu pude<tab-insert>
para facilitar a leitura em primeiro lugar, para não tirar o<tab-inserts>
antes oferecendo a você para ler ...)A parte mais legal sobre isso é a citação - observe que as
'"
aspas permanecem e apenas as\
aspas foram retiradas. Provavelmente, é por esse motivo, mais do que qualquer outro, que se você precisar avaliar duas vezes um shell$expansion
, recomendarei ohere-document
porque as aspas são muito mais fáceis do queeval
.De qualquer forma, agora o código acima é exatamente como um arquivo alimentado, como
3<~/heredoc.file
apenas esperando a_gem_dec()
função continuar e aceitar sua entrada/dev/fd/3
.Então, quando começamos,
_gem_dec()
a primeira coisa que faço é lançar todos os parâmetros posicionais, porque nosso próximo passo é uma expansão de shell avaliada duas vezes e não quero que nenhum dos contidos$expansions
seja interpretado como qualquer um dos meus$1 $2 $3...
parâmetros atuais . Então eu:shift
descarta quantospositional parameters
você especificar e começa$1
com o que resta. Então, se eu liguei_gem_dec one two three
para os alertas_gem_dec's $1 $2 $3
parâmetros posicionais seriaone two three
ea contagem posicional corrente total, ou$#
seria 3. Se eu então chamadoshift 2,
os valores deone
etwo
seriashift
ed longe, o valor de$1
se mudar parathree
e$#
iria expandir a 1. Assim,shift $#
apenas joga todos eles fora. Fazer isso é estritamente preventivo e é apenas um hábito que desenvolvi depois de fazer esse tipo de coisa por um tempo. Aqui está(subshell)
um pouco espalhado por uma questão de clareza:De qualquer forma, o próximo passo é onde a mágica acontece. Se você
. ~/some.sh
solicitar no shell, todas as funções e variáveis de ambiente declaradas~/some.sh
serão chamadas no prompt do shell. O mesmo acontece aqui, exceto. source
ocharacter device
arquivo especial para o nosso descritor de arquivo, ou. /dev/fd/3
- que é onde ohere-document
arquivo in-line foi localizado - e declaramos nossa função. E é assim que funciona.Agora faz o que sua
_guard
função deve fazer.Termo aditivo:
Uma ótima maneira de dizer salve suas posições:
EDITAR:
Quando respondi pela primeira vez a essa pergunta, concentrei-me mais no problema de declarar um shell
function()
capaz de declarar outras funções que persistissem no$ENV
ferro atual do shell do que no que o solicitante faria com as referidas funções persistentes. Desde então, percebi que minha solução oferecida originalmente na qual3<<-FUNC
assumia a forma:Provavelmente não teria funcionado como esperado para o autor da pergunta, porque alterei especificamente o nome da função declarativa
$1
para a_${1}
qual, se chamado como_gem_dec guard
por exemplo, resultaria na_gem_dec
declaração de uma função nomeada_guard
em oposição a justguard
.Nota: Esse comportamento é uma questão de hábito para mim - normalmente opero na presunção de que as funções do shell devem ocupar apenas as suas
_namespace
para evitar a intrusão nopróprionamespace
shellcommands
.Porém, este não é um hábito universal, como é evidenciado no uso de quem
command
pede$1
.Um exame mais aprofundado me leva a acreditar no seguinte:
Isso não teria funcionado anteriormente, porque também alterei o
$1
chamadocommand
para ler:O que não resultaria na execução da
ruby
função que a função shell compilou como:Espero que você possa ver (como acabei vendo ) que parece que o solicitante está usando apenas
command
para especificar indiretamente,namespace
porquecommand
preferirá chamar um arquivo executável em$PATH
uma função shell com o mesmo nome.Se minha análise estiver correta (como espero que o solicitante confirme), então isto:
Deve atender melhor a essas condições, com a exceção de que a chamada
guard
no prompt apenas tentará executar um arquivo executável em$PATH
named,guard
enquanto a chamada_guard
no prompt verificará aGemfile's
existência e compilará de acordo ou executará oguard
executável no$PATH
. Dessa maneira,namespace
é protegida e, pelo menos como eu a percebo, a intenção do solicitante ainda é cumprida.De fato, presumindo que nossa função de shell
_${1}()
e o executável${PATH}/${1}
sejam as duas únicas maneiras pelas quais nosso shell pode interpretar uma chamada para$1
ou_${1}
então o uso decommand
na função é agora totalmente redundante. Ainda assim, deixei como não gosto de cometer o mesmo erro duas vezes ... de qualquer maneira.Se isso for inaceitável para o solicitante e ele preferir acabar com o processo
_
totalmente, então, em sua forma atual, editar a_underscore
saída deve ser tudo o que o solicitante precisa fazer para atender aos requisitos que eu os entendo.Além dessa alteração, também editei a função para usar
&&
e / ou||
shell condicionais de curto-circuito , em vez daif/then
sintaxe original . Desta forma, acommand
declaração só é avaliado em tudo , seGemfile
não está na$PATH
. Essa modificação requer a adição de,return $?
no entanto, para garantir que abundle
instrução não seja executada caso o eventoGemfile
não exista, mas aruby $1
função retornará algo diferente de 0.Por fim, devo observar que esta solução implementa apenas construções de shell portáteis. Em outras palavras, isso deve produzir resultados idênticos em qualquer shell que reivindique compatibilidade com POSIX. Embora, obviamente, seja absurdo eu afirmar que todo sistema compatível com POSIX deve lidar com a
ruby bundle
diretiva, pelo menos os imperativos do shell que o invocam devem se comportar da mesma maneira, independentemente de o shell de chamada sersh
oudash
. Além disso, o acima funcionará conforme o esperado (presumindo pelo menos meio caminhoshopts
normal ) em ambosbash
ezsh
.fonte
~/.bashrc
e chamo. ~/.bashrc
, então essas três funções são executadas. Talvez o comportamento seja diferente por ambiente, então eu adicionei meu ambiente à pergunta. Além disso, eu não conseguia entender por que a última linha_guard ; _rspec ; _rake
é necessária. Eu pesquisei sobreshift
e descritor de arquivo, parece que estes estão além do meu entendimento atual.fonte