Pergunta Geral:
No Bash, eu sei que o uso da variável myvar
pode ser feito de duas maneiras:
# Define a variable:
bash$ myvar="two words"
# Method one to dereference:
bash$ echo $myvar
two words
# Method two to dereference:
bash$ echo "$myvar"
two words
No caso acima, o comportamento é idêntico. Isso é por causa de como echo
funciona. Em outros utilitários Unix, se as palavras estão agrupadas com aspas duplas fará uma enorme diferença:
bash$ myfile="Cool Song.mp3"
bash$ rm "$myfile" # Deletes "Cool Song.mp3".
bash$ rm $myfile # Tries to delete "Cool" and "Song.mp3".
Gostaria de saber qual é o significado mais profundo dessa diferença. Mais importante, como posso visualizar exatamente o que será passado para o comando, para que eu possa ver se foi citado corretamente?
Exemplo ímpar específico:
Vou apenas escrever o código com o comportamento observado:
bash$ mydate="--date=format:\"%Y-%m-%d T%H\""
bash$ git log "$mydate" # This works great.
bash$ git log $mydate
fatal: ambiguous argument 'T%H"': unknown revision or path not in the working tree.
Por que preciso de aspas duplas? O que exatamente o git-log está vendo depois que a variável é desreferenciada sem aspas duplas?
Mas agora veja isso:
bash$ nospace="--date=format:\"%Y-%m-%d\""
bash$ git log $nospace # Now THIS works great.
bash$ git log "$nospace" # This kind of works, here is a snippet:
# From git-log output:
Date: "2018-04-12"
Eca, por que a saída impressa tem aspas duplas agora? Parece que as aspas duplas são desnecessárias, elas não são removidas, são interpretadas como caracteres de aspas literais se e somente se não forem necessárias.
O que o Git está sendo passado como argumentos? Eu gostaria de saber como descobrir.
Para tornar as coisas mais complexas, escrevi um script Python usando argparse
isso apenas imprime todos os argumentos (como Bash os interpretou, portanto, com literais de aspas duplas em que Bash pensa que fazem parte do argumento e com palavras agrupadas ou não agrupadas como Bash achar melhor), e o argparse
script Python se comporta de maneira muito racional. Infelizmente, acho que argparse
pode estar silenciosamente corrigindo um problema conhecido com o Bash e, assim, obscurecendo as coisas bagunçadas que o Bash está passando para ele. Isso é apenas um palpite, não faço ideia. Talvez o git-log esteja estragando secretamente o que o Bash está passando para ele.
Ou talvez eu simplesmente não saiba o que está acontecendo.
Obrigado.
Edição editada: Deixe-me dizer isso agora, antes que haja respostas: eu sei que talvez eu possa usar aspas simples ao redor da coisa toda e depois não escapar das aspas duplas. Na verdade, isso funciona um pouco melhor para o meu problema inicial usando o git-log, mas eu testei em alguns outros contextos e é praticamente igualmente imprevisível e não confiável. Algo estranho está acontecendo ao citar dentro de variáveis. Eu nem vou postar todas as coisas estranhas que aconteceram com aspas simples.
Edit 2 - Isso também não funciona: eu só tive essa ideia maravilhosa, mas não funciona:
bash$ mydate="--date=format:%Y-%m-%d\ T%H"
bash$ git log "$mydate"
# Git log output has this:
Date: 2018-04-12\ T23
Portanto, ele não possui aspas, mas possui um caractere de barra invertida literal na string de data. Além disso, git log $mydate
sem erros de cotação, o espaço de barra invertida na variável
fonte
Respostas:
Abordagem diferente:
Quando você executa
git log --format="foo bar"
, essas aspas não são interpretadas pelo git - elas são removidas pelo shell (e protegem o texto entre aspas). Isso resulta em um único argumento:--format=foo bar
No entanto, quando as variáveis não citadas são expandidas, os resultados passam pela divisão de palavras, mas não entre aspas. Portanto, se sua variável contiver
--format="foo bar"
, ela será expandida para estes argumentos:--format="foo
bar"
Isso pode ser verificado usando:
... bem como qualquer script simples que imprima seus argumentos recebidos.
Se você sempre tiver o bash disponível, a solução preferida é usar variáveis de matriz :
Com isso, a análise usual é feita durante a atribuição, não durante a expansão. Você usa esta sintaxe para expandir o conteúdo da variável, cada elemento obtendo seu próprio argumento:
fonte
mydate="--date=format:%Y-%m-%d T%H"
isso não usa variáveis de matriz (que são impressionantes) e enfatiza a solução de trabalho que altera apenas um caractere do código do problema original. Obrigado.mydate="--date=format:%Y-%m-%d T%H"
egit log "$mydate"
?printf
no próprio Bash (testei) ou Python (testei) ou Perl (não testei, confio nele por extrapolação). Essa resposta também enfatiza o que está essencialmente acontecendo no seu snippet útil: o Bash está adicionando aspas duplas de que precisa, então não devo digitá-lo.Por que seu comando original não funciona?
Você pergunta:
Se você não usar aspas duplas
$mydate
, a variável será expandida literalmente e a linha do shell será a seguinte antes de ser executada:Aqui, você (desnecessariamente) adicionou aspas literais usando
\"
a atribuição de variável.Desde o comando será submetido a repartição de palavras ,
git
receberá três argumentos,log
,--date-format:"%Y-%m%-d
eT%H"
, portanto, reclamando de não encontrar qualquer confirmação ou objeto nomeadoT%H"
.Qual é a abordagem correta?
Se você deseja manter os argumentos juntos, se esse argumento contiver espaço em branco, será necessário agrupar o argumento entre aspas. Geralmente, sempre envolva variáveis entre aspas duplas.
Isso funciona mesmo se houver um espaço dentro da variável:
Agora o terceiro argumento para
git
será$mydate
, incluindo o espaço que você especificou originalmente. Todas as aspas são removidas pelo shell antes de serem passadas paragit
.Você simplesmente não precisa de
git
aspas adicionais - se tudo o que você deseja é ver um argumento, coloque-o entre aspas ao passar a variável"$mydate"
.Além disso, você pergunta:
Sua pergunta:
Como você incluiu novamente aspas literais no argumento (escapando-as), que são transformadas em, digamos, aspas "reais" quando você esquece de citar a variável no seu comando real. Eu digo "esqueça" porque o uso de variáveis não citadas nos comandos do shell geralmente causa problemas - e aqui está revertendo um erro que você cometeu ao especificar a variável em primeiro lugar.
PS: Eu sei que tudo isso é confuso, mas isso é Bash, e segue algumas regras claras. Não há bug aqui. Um ensaio relacionado sobre nomes de arquivos no shell também é muito revelador, pois aborda a questão do manuseio de espaços em branco no Bash.
fonte
git log --date=format:"%Y-%m-%d T%H"
Você disse como se não funcionasse, mas funciona. Além disso, não há outra maneira de fazê-lo funcionar.