Executando `exec` com um Bash embutido


9

Eu defini uma função shell (vamos chamá-lo clock), que eu quero usar como invólucro para outro comando, semelhante à timefunção, por exemplo clock ls -R.

Minha função shell executa algumas tarefas e termina com exec "$@".

Eu gostaria que essa função funcionasse mesmo com os shell embutidos, por exemplo, clock time ls -Rdeve gerar o resultado do timeembutido, e não do /usr/bin/timeexecutável. Mas execsempre acaba executando o comando.

Como posso fazer com que minha função Bash funcione como um invólucro que também aceite argumentos internos do shell?

Edit : Acabei de saber que timenão é um Bash embutido, mas uma palavra reservada especial relacionada a pipelines. Ainda estou interessado em uma solução para embutidos, mesmo que não funcione time, mas uma solução mais geral seria ainda melhor.


Você precisa invocar um shell explicitamente usando exec bash -c \' "$@" \'. A menos que seu comando no primeiro parâmetro seja reconhecido como um script de shell, ele será interpretado como um binário para ser executado diretamente. Alternativamente, e de forma mais simples, apenas perca a execchamada e "@"do seu shell original.
AFH

Respostas:


9

Você definiu uma função bash. Então você já está em um shell bash ao invocar essa função. Portanto, essa função poderia simplesmente se parecer com:

clock(){
  echo "do something"
  $@
}

Essa função pode ser chamada com bash builtins, palavras reservadas especiais, comandos e outras funções definidas:

Um apelido:

$ clock type ls
do something
ls is aliased to `ls --color=auto'

Um bash embutido:

$ clock type type
do something
type is a shell builtin

Outra função:

$ clock clock
do something
do something

Um executável:

$ clock date
do something
Tue Apr 21 14:11:59 CEST 2015

Nesse caso, existe alguma diferença entre executar $@e exec $@, se eu sei que estou executando um comando real?
Anol 21/04

3
Quando você usa exec, o comando substitui o shell. Portanto, não há mais builtins, aliases, palavras reservadas especiais, palavras definidas, porque o executável é executado via chamada do sistema execve(). Esse syscall espera um arquivo executável.
caos

Mas, do ponto de vista de um observador externo, ainda é possível distingui-los, por exemplo, com exec $0um único processo, enquanto $@ainda há dois.
anol 21/04/2015

4
Sim, $@possui o shell em execução como pai e o comando como processo filho. Mas quando você quiser usar builtins, aliases e assim por diante, precisará preservar o shell. Ou comece um novo.
caos

4

A única maneira de iniciar um shell embutido ou uma palavra-chave do shell é iniciar um novo shell, porque o exec "substitui o shell pelo comando especificado". Você deve substituir sua última linha por:

IFS=' '; exec bash -c "$*"

Isso funciona com palavras internas e reservadas; O princípio é o mesmo.


3

Se o wrapper precisar inserir código antes do comando fornecido, um alias funcionará à medida que forem expandidos em um estágio muito inicial:

alias clock="do this; do that;"

Os aliases são quase literalmente inseridos no lugar da palavra com alias, portanto, o final ;é importante - ele faz a clock time fooexpansão para do this; do that; time foo.

Você pode abusar disso para criar aliases mágicos que ignoram as aspas.


Para inserir código após um comando, você pode usar o gancho "DEBUG".

shopt -s extdebug
trap "<code>" DEBUG

Especificamente:

shopt -s extdebug
trap 'if [[ $BASH_COMMAND == "clock "* ]]; then
          eval "${BASH_COMMAND#clock }"; echo "Whee!"; false
      fi' DEBUG

O gancho ainda é executado antes do comando, mas, ao retornar, falseele diz ao bash para cancelar a execução (porque o gancho já o executou via eval).

Como outro exemplo, você pode usar isso como alias command pleasepara sudo command:

trap 'case $BASH_COMMAND in *\ please) eval sudo ${BASH_COMMAND% *}; false; esac' DEBUG

1

A única solução que eu poderia encontrar até agora seria realizar uma análise de caso para distinguir se o primeiro argumento é um comando, interno ou palavra-chave e falhar no último caso:

#!/bin/bash

case $(type -t "$1") in
  file)
    # do stuff
    exec "$@"
    ;;
  builtin)
    # do stuff
    builtin "$@"
    ;;
  keyword)
    >&2 echo "error: cannot handle keywords: $1"
    exit 1
    ;;
  *)
    >&2 echo "error: unknown type: $1"
    exit 1
    ;;
esac

No timeentanto, ele não funciona , portanto pode haver uma solução melhor (e mais concisa).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.