Quando abro um prompt do bash e digite:
$ set -o xtrace
$ x='~/someDirectory'
+ x='~/someDirectory'
$ echo $x
+ echo '~/someDirectory'
~/someDirectory
Eu esperava que a 5ª linha acima tivesse passado + echo /home/myUsername/someDirectory
. Existe uma maneira de fazer isso? No meu script Bash original, a variável x está realmente sendo preenchida a partir de dados de um arquivo de entrada, através de um loop como este:
while IFS= read line
do
params=($line)
echo ${params[0]}
done <"./someInputFile.txt"
Ainda assim, estou obtendo um resultado semelhante, com o echo '~/someDirectory'
invés de echo /home/myUsername/someDirectory
.
x='~'; print -l ${x} ${~x}
. Desisti depois de vasculhar obash
manual por um tempo.Respostas:
O padrão POSIX impõe que a expansão de palavras seja feita na seguinte ordem (a ênfase é minha):
O único ponto que nos interessa aqui é o primeiro: como você pode ver, a expansão til é processada antes da expansão do parâmetro:
echo $x
, não há nenhum til a ser encontrado e, portanto, prossegue.echo $x
,$x
é encontrado e expandido e a linha de comando se tornaecho ~/someDirectory
.~
personagem permanece como está.Ao usar as aspas ao atribuir o
$x
, você estava solicitando explicitamente para não expandir o til e tratá-lo como um caractere normal. Uma coisa que muitas vezes se perde é que, nos comandos do shell, você não precisa citar toda a cadeia, para que você possa fazer a expansão acontecer corretamente durante a atribuição de variável:E você também pode fazer com que a expansão ocorra na
echo
linha de comando, desde que isso ocorra antes da expansão do parâmetro:Se, por algum motivo, você realmente precisar afetar o til da
$x
variável sem expansão e poder expandi-lo noecho
comando, prossiga duas vezes para forçar$x
a ocorrência de duas expansões da variável:No entanto, esteja ciente de que, dependendo do contexto em que você usa essa estrutura, ela pode ter efeitos colaterais indesejados. Como regra geral, prefira evitar usar qualquer coisa que exija
eval
quando você tiver outra maneira.Se você deseja resolver especificamente o problema de til, em oposição a qualquer outro tipo de expansão, essa estrutura seria mais segura e portátil:
Essa estrutura verifica explicitamente a presença de um líder
~
e o substitui pelo diretório inicial do usuário, caso ele seja encontrado.Após o seu comentário, isso
x="${HOME}/${x#"~/"}"
pode ser realmente surpreendente para alguém que não é usado na programação de shell, mas está de fato vinculado à mesma regra POSIX citada acima.Conforme imposto pelo padrão POSIX, a remoção da cotação ocorre por último e a expansão dos parâmetros ocorre muito cedo. Assim,
${#"~"}
é avaliado e expandido muito antes da avaliação das aspas externas. Por sua vez, conforme definido nas regras de expansão de parâmetros :Portanto, o lado direito do
#
operador deve ser citado ou escapado adequadamente para evitar a expansão do til.Portanto, para dizer de maneira diferente, quando o interpretador de shell olha
x="${HOME}/${x#"~/"}"
, ele vê:${HOME}
e${x#"~/"}
deve ser expandido.${HOME}
é expandido para o conteúdo da$HOME
variável${x#"~/"}
aciona uma expansão aninhada:"~/"
é analisado, mas, sendo citado, é tratado como um literal 1 . Você poderia ter usado aspas simples aqui com o mesmo resultado.${x#"~/"}
a própria expressão agora é expandida, resultando na~/
remoção do prefixo do valor de$x
.${HOME}
, o literal/
, a expansão${x#"~/"}
.a=$b
, geralmente acho mais claro adicionar aspas duplas.A propósito, se olharmos mais de perto a
case
sintaxe, você verá a"~/"*
construção que se baseia no mesmo conceito quex=~/'someDirectory'
eu expliquei acima (aqui novamente, aspas simples e duplas podem ser usadas de forma intercambiável).Não se preocupe se essas coisas parecerem obscuras à primeira vista (talvez até na segunda ou mais tarde!). Na minha opinião, a expansão de parâmetros é, com subconjuntos, um dos conceitos mais complexos a serem compreendidos ao se programar em linguagem shell.
Sei que algumas pessoas podem discordar vigorosamente, mas se você gostaria de aprender mais sobre a programação do shell, incentivo-o a ler o Advanced Bash Scripting Guide : ele ensina o Bash scripting, portanto, com muitas extensões e sinos. assobios em comparação ao script de shell POSIX, mas achei bem escrito com vários exemplos práticos. Depois que você gerencia isso, é fácil restringir-se aos recursos do POSIX quando necessário; pessoalmente, acho que entrar diretamente no domínio POSIX é uma curva de aprendizado desnecessária para iniciantes (compare meu substituto do POSIX com o Bash do mexdular, como o regex equivalente a ter uma idéia do que quero dizer;)!).
1 : O que me leva a encontrar um bug no Dash que não implementa a expansão de til aqui corretamente (uso verificável
x='~/foo'; echo "${x#~/}"
). A expansão de parâmetros é um campo complexo, tanto para o usuário quanto para os próprios desenvolvedores de shell!fonte
x="${HOME}/${x#"~/"}"
? Parece que uma concatenação de 3 cordas:"${HOME}/${x#"
,~/
, e"}"
. O shell permite aspas duplas aninhadas quando o par interno de aspas duplas está dentro de um${ }
bloco?Uma resposta possível:
Como você está lendo a entrada de um arquivo, eu não faria isso.
Você pode procurar e substituir o ~ pelo valor de $ HOME, assim:
Dá-me:
fonte
${parameter/pattern/string}
expansão é uma extensão do Bash e pode não estar disponível em outros shells.case
estrutura POSIX ilustra bem como o script do Bash é mais fácil de usar, especialmente para iniciantes.