Como ligar e desligar a globulação?

4

Dentro do meu ~./bashrc em um alias eu mudei de globbing assim.

alias x='set -f;. any.sh'

Mas qual comando habilita globbing novamente ou devo definir essas opções em any.sh ?

Qualquer resposta é bem-vinda.

    
por John Goofy 27.08.2017 / 21:26

2 respostas

10

Se você quiser que os globs sejam desativados somente enquanto o shell estiver interpretando o código em any.sh , com os shells bash4.4+ ou ash -based, você pode fazer:

x() {
  local -
  set -o noglob
  . any.sh
}

Ou em zsh :

x() {
  set -o localoptions -o noglob
  . any.sh
}

Isso é usar uma função em vez de um alias (você não quer usar aliases para vários comandos, pois isso não faz o que você quer quando você faz cmd | x ou cmd && x , por exemplo), certifique-se de que as alterações para opções (para a variável $- como uma maneira de olhar para ela) são locais para a função, desabilitar glob e fonte do arquivo.

Com versões mais antigas de bash , você poderia fazer:

x() {
  local ret restore
  [[ $- = *f* ]] || restore='set +o noglob'
  set -o noglob
  . any.sh
  ret=$?
  eval "$restore"
  return "$ret"
}

Ou talvez uma função auxiliar mais genérica como:

withopt() {
  local ret option
  local -a restore
  option=$1; shift
  [[ -o $option ]] || restore=(set +o "$option")

  set -o "$option"
  "$@"
  ret=$?
  "${restore[@]}"
  return "$ret"
}

E então, você pode usar um apelido se quiser:

alias x='withopt noglob . any.sh'

Observe que isso não impede que * seja expandido se você fizer isso:

x *

Como a opção noglob acaba sendo ativada por muito tempo depois que o comando foi avaliado. Para isso, veja minha outra resposta (uma resposta para uma pergunta diferente).

    
por 27.08.2017 / 22:12
4

This was posted before the question was clarified, and now addresses a different need. I'm still leaving it here as that can be useful to others.

Suponho que você queira fazer:

x *.txt

e o *.txt a ser passado não expandido para any.sh e globs a serem reativados posteriormente.

Você não pode fazer isso com bash . Use zsh onde você pode fazer:

alias x='noglob any.sh'

Em que noglob desativa os alias apenas para esse comando.

$ echo /etc/p*d
/etc/pam.d /etc/passwd /etc/profile.d
$ noglob echo /etc/p*d
/etc/p*d

Observe que isso afeta a expansão de globs em argumentos apenas dos comandos echo . * ainda seria expandido em noglob echo $(echo *) ou noglob eval 'echo *' ou noglob . some-script , em que some-script faz um echo * .

Na verdade, pode haver uma maneira com bash :

oneshot_noglob() {
  case $- in
    (*f*) ;; # globs already disabled
    (*) set -f; shot=0; debug_trap=$(trap -p DEBUG)
        trap '
          if ((++shot == 2)); then
            set +f
            trap - DEBUG
            '"$debug_trap"'
          fi' DEBUG;;
  esac
}

alias x='oneshot_noglob; any.sh'

Que usa a armadilha DEBUG para restaurar set +f após um comando ter sido executado após o set -f .

Agora, com todos os aliases que contêm mais de um comando, isso tem algumas ressalvas.

echo foo | x

Torna-se:

echo foo | oneshort_noglob; any.sh

Portanto, a saída de echo é alimentada apenas para oneshort_noglob .

O mesmo para coisas como:

cmd && x

Em que any.sh seria executado independentemente de o cmd ser bem-sucedido ou não.

Observe também que afeta todos os globs em todos os níveis de subshell até pouco antes do segundo comando ser executado no processo principal do shell.

Por exemplo, em x $(echo *; echo *) * , nenhum desses * seria expandido porque a interceptação DEBUG não é herdada, a menos que você defina a opção extdebug .

    
por 27.08.2017 / 21:41