Parar a expansão do caractere curinga do shell?

91

Existe alguma maneira de um programa de linha de comando compilado dizer ao bash ou csh que ele não deseja que nenhum caractere curinga seja expandido em seus parâmetros?

Por exemplo, pode-se querer um comando shell como:

foo *

para simplesmente retornar o valor ASCII numérico desse caractere.

hotpaw2
fonte

Respostas:

122

Não. A expansão ocorre antes de o comando ser executado.
Você só pode desativar o glob antes de executar o comando ou citando a estrela.

$ # quote it
$ foo '*'

$ # or escape it
$ foo \*

$ # or disable the glob (noglob)
$ set -f
$ foo *

$ # alternative to set -f
$ set -o noglob
$ # undo it by 
$ set +o noglob
c00kiemon5ter
fonte
3
também, se não há nada para expandir, não há expansão. ou seja, com um vazio pwd, echo *é apenas*
sam boosalis
7
@sam Isso realmente depende de como suas opções são definidas. De tldp: "Se nenhum nome de arquivo correspondente for encontrado e a opção shell nullglobestiver desabilitada, a palavra não será alterada. Se a nullglobopção for definida e nenhuma correspondência for encontrada, a palavra será removida."
Chris Middleton
obrigado, eu deveria ter qualificado que isso foi apenas de observação, não de qualquer entendimento.
sam boosalis
@ c00kiemon5ter Como desfazer o set -f? É isso unset -f?
Nam G VU
1
@NamGVUset +f
Yngvar Kristiansen
63

Embora seja verdade que um comando em si não pode desativar o globbing, é possível para um usuário dizer a um shell Unix para não globalizar um comando específico. Isso geralmente é feito editando os arquivos de configuração de um shell. Assumindo que o comando foopode ser encontrado ao longo do caminho do comando, o seguinte precisa ser adicionado ao arquivo de configuração apropriado:

Para os shells sh, bash e ksh:

alias foo='set -f;foo';foo(){ command foo "$@";set +f;}

Para os shells csh e tcsh:

alias foo 'set noglob;\foo \!*;unset noglob'

Para o shell zsh:

alias foo='noglob foo'

O caminho do comando não precisa ser usado. Digamos que o comando foo esteja armazenado no diretório ~ / bin, então o acima se tornaria:

Para os shells sh, bash e ksh:

alias foo='set -f;foo';foo(){ ~/bin/foo "$@";set +f;}

Para os shells csh e tcsh:

alias foo 'set noglob;$home/bin/foo \!*;unset noglob'

Para o shell zsh:

alias foo='noglob ~/bin/foo'

Todos os itens acima foram testados usando o OSX 10.9.2 da Apple. Nota: Ao copiar o código acima, tome cuidado ao excluir quaisquer espaços. Eles podem ser significativos.

Atualizar:

O usuário geira apontou que, no caso de um bash shell

alias foo='set -f;foo';foo(){ ~/bin/foo "$@";set +f;}

poderia ser substituído por

reset_expansion(){ CMD="$1";shift;$CMD "$@";set +f;}
alias foo='set -f;reset_expansion ~/bin/foo'

o que elimina a necessidade da função foo.

Alguns sites usados ​​para criar este documento:

David Anderson
fonte
3
Essa deveria ter sido a resposta aceita, pois prova que é realmente possível fazer o que o OP deseja. Para alguns métodos ainda melhores em bash, este link documenta as várias opções: blog.edwards-research.com/2011/05/preventing-globbing
geira
@geira: Às vezes, uma resposta é aceita antes que uma resposta melhor seja postada. Acredito que esse seja um desses casos. Também acredito que as marcas de resposta como mais úteis para os usuários acabam primeiro. Observação: adicionei sua opinião à minha resposta.
David Anderson
@geira, o OP deseja que o programa compilado se comunique com o shell; Responder que é possível configurar o shell para se comportar conforme desejado em uma base de comando por comando é uma resposta útil, e eu mesmo aceitaria essa, mas vejo espaço para discordar se ela está totalmente correta.
Charles Duffy
O fato de que pode ser feito não significa que deva ser feito. Fazer com que o comportamento do shell mude da expectativa razoável do usuário para comandos selecionados parece surpreendente e, em última análise, indesejável.
tripleee
@tripleee: Houve um tempo em que as pessoas esperavam relés e tubos a vácuo. Esperados eram furos perfurados em cartões de papelão e fita de papel. As pessoas usavam réguas de cálculo em vez de calculadoras e telefones. Suponho que para você isso seria desejável. Não me importei muito com esses tempos. Eu gosto de ver o que vem a seguir.
David Anderson
8

A expansão é executada pelo shell antes de seu programa ser executado. Seu programa não tem ideia se a expansão ocorreu ou não.

   set -o noglob

irá desligar a expansão no shell de invocação, mas você terá que fazer isso antes de invocar seu programa.

A alternativa é citar seus argumentos, por exemplo

foo "*"
Brian Agnew
fonte
4
setopt parece ser um comando específico do zsh. Como outro postador disse, para bash você usa set -o noglobpara desabilitar a expansão de curinga.
Wirawan Purwanto
Agora alterado. Obrigado
Brian Agnew
1

Cuidado: se não houver nomes correspondentes à máscara, o bash passa o argumento como está, sem expansão!

Prova (pa.py é um script muito simples, que apenas imprime seus argumentos):

 $ ls
f1.cc  f2.cc  pa.py
 $ ./pa.py *.cc
['./pa.py', 'f1.cc', 'f2.cc']
 $ ./pa.py *.cpp
['./pa.py', '*.cpp']
Lesnik
fonte
3
Nem sempre. set -o nullglobou shopt -s failglob, e esse comportamento mudará (de duas maneiras muito diferentes).
Charles Duffy