Verifique as opções do bash

5

Eu tenho uma função e gostaria de usar a opção pipefail dentro. Mas eu não quero simplesmente set -o pipefail , porque temo que outra parte do script não espere que pipefail seja definido. É claro que eu poderia fazer set +o pipefail depois, mas no caso de pipefail ter sido definido fora da minha função, isso também pode introduzir um comportamento inesperado.

É claro que eu poderia usar o código de saída de false | true como medida, se pipefail estiver definido, mas isso parece um pouco sujo.

Existe uma maneira mais geral (e talvez canônica?) de verificar as opções de bash set?

    
por red_trumpet 22.11.2017 / 09:26

5 respostas

2

A variável $SHELLOPTS contém todas as opções definidas separadas por dois pontos. Por exemplo, pode parecer

braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

Para verificar uma determinada opção, pode-se fazer o seguinte:

# check if pipefail is set, if not set it and remember this
if [[ ! "$SHELLOPTS" =~ "pipefail" ]]; then
    set -o pipefail;
    PIPEFAIL=0;
else
    PIPEFAIL=1;
fi

# do something here

# reset pipefail, if it was not set before
if [ "$PIPEFAIL" -eq 0 ]; then
    set +o pipefail;
fi
    
por 22.11.2017 / 09:26
5

Em bash-4.4 e acima, você pode usar:

myfunction() {
  local -
  set -o someoption
  ...
}

Algumas notas:

  • copiado de ash . Em ash , a idéia é tornar $- (que armazena o conjunto de opções) local. Ele funciona em ash , porque em ash , local não afeta o valor nem os atributos da variável (ao contrário de bash , onde inicialmente é desfeito). Ainda assim, ele também funciona com bash da mesma forma que em ash como um caso especial, ou seja, ele não faz com que $- seja cancelado ou reverta para um comportamento padrão
  • abrange apenas as opções definidas com set -o option , não as definidas por shopt -s option ( bash sendo o único shell com dois conjuntos de opções!)
  • como para variáveis locais, é um escopo dinâmico, não estático. O valor de $- será restaurado ao retornar da função, mas a nova configuração afetará as outras funções ou scripts de origem chamados pela sua função. Se você quiser escopo estático, você precisará mudar para o ksh93 e usar:

    function myfunction {
      set -o myoption
      ...
      other_function
    }
    

    Com o other_function não afetado, desde que seja declarado com a sintaxe function other_function { (e não a sintaxe do% Bourneother_function()...).

  • Como não redefine as opções, sua função ainda pode ser afetada pelas opções definidas por outro código. Para evitar isso, você deve usar zsh . O equivalente a zsh de local - é set -o localoptions , mas você também pode acessar um modo de emulação conhecido localmente . Por exemplo:

    myfunction() {
      emulate -L zsh
      set -o someoption
      ...
    }
    

    começaria com um conjunto de opções sane zsh-default (com algumas exceções, consulte o doc) sobre o qual você adiciona sua opção. Isso também é escopo dinâmico como em ash / bash, mas você também pode chamar outras funções com:

    emulate zsh -c 'other_function...'
    

    para que other_function seja chamado com um conjunto de opções zsh vanilla.

    O zsh também tem várias operadoras que podem evitar que você mude as opções globalmente (como (N) / (D) glob qualificadores para o operador nullglob / dotglob , (#i) glob para não diferenciar maiúsculas de minúsculas globbing, ${(s:x:)var} para evitar misturar com $IFS , ${~var} para solicitar globbing na expansão de parâmetro (você precisa de noglob em outras shells parecidas com Bourne para evitar (!) isso) ...

por 22.11.2017 / 16:47
3

Se você quiser definir uma opção de shell dentro de uma função, mas não quiser afetar o chamador, faça isso de duas maneiras diferentes:

Opção 1: subshell

Coloque a função dentro de um subshell (observe os parênteses envolvendo as declarações da função em vez de chaves):

function foo() (
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
)

Em seguida, o chamador pode ter pipefail não definido e não é afetado:

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

ou

Opção 2: teste e reinicialização

Você pode testar e redefinir a opção conforme necessário (observe as chaves ao redor das instruções de função - sem sub-lista):

function foo() {
  shopt -q -o pipefail
  local resetpipefail=$?
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
  # only reset pipefail if needed
  [[ $resetpipefail -eq 1 ]] && set +o pipefail  
}

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

Sugestão de Hat cuonglm para o shopt -q answer .

    
por 22.11.2017 / 16:10
2

O seguinte usa bash ' -o test para testar se a opção pipefail shell está definida ou não (isso funciona em [[ ... ]] e com o comando test interno, mas não com% código%). Se a opção não estiver definida, ela é configurada e instala uma interceptação que não será definida quando a função retornar:

foo () {
    if [[ ! -o pipefail ]]; then
        set -o pipefail
        trap 'set +o pipefail' RETURN
    fi

    # rest of function body
}

Codifique com alguma depuração incluída:

foo () {
    if [[ ! -o pipefail ]]; then
        set -o pipefail
        trap 'set +o pipefail' RETURN
    fi

    # rest of function body
    if [[ -o pipefail ]]; then
        echo 'pipefail is set (in function)'
    else
        echo 'pipefail is not set (in function)'
    fi
}

if [[ -o pipefail ]]; then
    echo 'pipefail is set'
else
    echo 'pipefail is not set'
fi

foo

if [[ -o pipefail ]]; then
    echo 'pipefail is set'
else
    echo 'pipefail is not set'
fi

Executando:

$ bash script.sh
pipefail is not set
pipefail is set (in function)
pipefail is not set

$ bash -o pipefail script.sh
pipefail is set
pipefail is set (in function)
pipefail is set
    
por 09.06.2018 / 08:38
1

Use uma armadilha RETURN

Commands specified with an RETURN trap are executed before execution resumes after a shell function ... returns...

The -p option [to shopt] causes output to be displayed in a form that may be reused as input.

https://www.gnu.org/software/bash/manual/bash.html

foo() {
  trap "$(shopt -p -o pipefail)" RETURN
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
}

Teste

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

O modelo de função a seguir manipula o caso geral de garantir que as opções set e shopt sejam restauradas para seus valores originais quando uma função retornar:

foo() {
  trap "$(shopt -p -o)$(shopt -p)" RETURN
  # ...
}
    
por 09.06.2018 / 06:40

Tags