É um? tratado de maneira especial na expansão de parâmetros?

2

Estou tentando analisar a saída de git status --porcelain -b para uso no meu prompt, mas estou encontrando um comportamento estranho ao executar a expansão de parâmetros.

Este fragmento deve demonstrar o problema:

#!/bin/bash
IFS=$'\n'
touch ab
status_arr=( $(git status --porcelain -b) )
for (( i=0; i<${#status_arr[@]}; i++ )); do
    echo ${status_arr[$i]}
    echo ${status_arr[$i]:0:2}
done

Quando executado em um diretório git limpo, recebo esta saída:

$ bash sandbox/statusline/issue.sh 
## master...origin/master
##
?? ab
ab

Espero que ?? seja ecoado na linha 4 do resultado e, na verdade, se eu alterar o script para touch abc ou mesmo touch a , é isso que recebo.

Estou muito confuso com isso, acho que devo estar perdendo algo óbvio no bash, mas o Google não está produzindo nada de útil.

Se isso é uma "coisa" conhecida, existe uma maneira de contornar isso / evitá-lo completamente?

    
por Sean Kenny 05.08.2014 / 21:09

2 respostas

4

O ? é um caractere shell glob usado para corresponder aos nomes dos arquivos. Corresponde a um único caractere. Assim, como você tem um arquivo chamado ab , o padrão ?? corresponde a ele.

A razão pela qual isso acontece é porque a expansão do seu parâmetro não é .

    
por 05.08.2014 / 21:31
0

Uma variável sem nome ou substituição de comando não é interpretada como uma cadeia, mas como uma lista de padrões de caracteres curinga de nome de arquivo. Ou seja, o valor da variável ou a saída do comando é dividido em partes separadas, separadas por caracteres em IFS (essa etapa é chamada de divisão de campo); cada parte é interpretada como um padrão curinga e, se o padrão corresponder a alguns arquivos, ele será substituído pela lista de nomes de arquivos correspondentes, caso contrário, o padrão será deixado intacto (essa etapa é chamada de geração de nome de arquivo).

Por exemplo, status_arr=( $(git status --porcelain -b) ) define status_arr para a matriz de um elemento que contém a string de 5 caracteres ?? ab' porque IFS contém apenas uma nova linha e não há arquivo correspondente ao padrão ?? ab . Se IFS tivesse seu valor padrão contendo um espaço, então status_arr seria definido como a matriz de dois elementos contendo duas ocorrências da string de 2 caracteres ab .

Se a variável ou substituição de comando estiver entre aspas duplas, a sequência resultante será usada como está: a divisão de campo e a geração de nome de arquivo serão aplicadas apenas às substituições sem aspas.

Você pode desativar completamente a geração de nome de arquivo executando set -f . Isso é útil se você quiser aproveitar a divisão IFS . Observe que set -f desativa completamente a geração de nome de arquivo, não apenas na saída de substituições: set -f; echo * sempre imprime * .

#!/bin/bash
IFS=$'\n'
set -f
touch ab
status_arr=( $(git status --porcelain -b) )
for (( i=0; i<${#status_arr[@]}; i++ )); do
    echo "${status_arr[$i]}"
    echo "${status_arr[$i]:0:2}"
done

(Aqui, com a geração de nome de arquivo ainda desabilitada, e dado que os elementos de status_arr não podem conter caracteres de IFS por construção, é seguro deixar as aspas duplas nas instruções echo . muito frágil - depende muito da maneira como o array foi construído e do fato de que o status de set -f e o valor de IFS não mudaram desde então. Sempre use aspas duplas em torno de substituições de variáveis e comandos a menos que você tenha um bom motivo para deixá-los de fora e você sabe que não há problema em fazê-lo.)

    
por 06.08.2014 / 02:05