Eu tenho uma variável no meu script bash cujo valor é algo como isto:
~/a/b/c
Observe que é um til não expandido. Quando eu faço ls -lt nessa variável (chame-a de $ VAR), não recebo esse diretório. Eu quero deixar o bash interpretar / expandir essa variável sem executá-la. Em outras palavras, quero que o bash execute eval, mas não execute o comando avaliado. Isso é possível no bash?
Como consegui passar isso para o meu script sem expansão? Passei o argumento entre aspas duplas.
Tente este comando para ver o que quero dizer:
ls -lt "~"
Esta é exatamente a situação em que estou. Quero que o til seja expandido. Em outras palavras, com o que devo substituir a magia para tornar esses dois comandos idênticos:
ls -lt ~/abc/def/ghi
e
ls -lt $(magic "~/abc/def/ghi")
Observe que ~ / abc / def / ghi pode ou não existir.
bash
scripting
tilde
tilde-expansion
madiyaan damha
fonte
fonte
eval
.foo=~/"$filepath"
oufoo="$HOME/$filepath"
dir="$(readlink -f "$dir")"
Respostas:
Devido à natureza do StackOverflow, não posso apenas tornar essa resposta inaceitável, mas nos cinco anos seguintes que publiquei isso houve respostas muito melhores do que a minha resposta admitidamente rudimentar e muito ruim (eu era jovem, não mate mim).
As outras soluções neste segmento são melhores e mais seguras. De preferência, eu iria com um destes dois:
Resposta original para fins históricos (mas não use isso)
Se não me engano,
"~"
não será expandido por um script bash dessa maneira porque é tratado como uma string literal"~"
. Você pode forçar a expansãoeval
dessa maneira.Como alternativa, basta usar
${HOME}
se você quiser o diretório inicial do usuário.fonte
${HOME}
mais atraente. Existe algum motivo para não fazer desta a sua recomendação principal? De qualquer forma, obrigado!eval
é uma sugestão horrível, é muito ruim que receba tantos votos. Você terá todos os tipos de problemas quando o valor da variável contiver metacaracteres do shell.Se a variável
var
for inserida pelo usuário, nãoeval
deve ser usado para expandir o til usandoO motivo é: o usuário pode acidentalmente (ou por objetivo) digitar, por exemplo,
var="$(rm -rf $HOME/)"
com possíveis conseqüências desastrosas.Uma maneira melhor (e mais segura) é usar a expansão do parâmetro Bash:
fonte
#
em"${var/#\~/$HOME}"
?$var
.\~
ou seja, escapar~
? (2) Sua resposta assume que esse~
é o primeiro caractere$var
. Como podemos ignorar os principais espaços em branco$var
?:
em uma sequência não citada. Mais informações nos documentos . Para remover o espaço em branco inicial, consulte Como aparar o espaço em branco de uma variável do Bash?Plagar-me a partir de uma resposta anterior , para fazer isso de maneira robusta, sem os riscos de segurança associados a
eval
:...usado como...
Como alternativa, uma abordagem mais simples que use com
eval
cuidado:fonte
printf %q
para escapar tudo, mas o til, e , em seguida, usareval
sem risco.Uma maneira segura de usar eval é
"$(printf "~/%q" "$dangerous_path")"
. Observe que é específico do bash.Veja esta pergunta para detalhes
Observe também que, no zsh, isso seria tão simples quanto
echo ${~dangerous_path}
fonte
echo ${~root}
me dê nenhuma saída em zsh (Mac OS X)export test="~root/a b"; echo ${~test}
Que tal agora:
Ou:
fonte
realpath
comando no exemplo C. Para, você pode gerar um executávelrealpath.exe
usando o bash e gcc a partir desta linha de comando:gcc -o realpath.exe -x c - <<< $'#include <stdlib.h> \n int main(int c,char**v){char p[9999]; realpath(v[1],p); puts(p);}'
. Cheersrealpath ~
->/home/myhome
<workingdir>/~
.Expandir (sem trocadilhos) as respostas de birryree e halloleo: A abordagem geral é usar
eval
, mas vem com algumas advertências importantes, como espaços e redirecionamento de saída (>
) na variável. O seguinte parece funcionar para mim:Experimente com cada um dos seguintes argumentos:
Explicação
${mypath//>}
retira>
personagens que poderia espancar um arquivo durante aeval
.eval echo ...
que é a expansão de til atual-e
argumento são para suporte de nomes de arquivos com espaços.Talvez exista uma solução mais elegante, mas foi isso que consegui encontrar.
fonte
$(rm -rf .)
.>
caracteres?Eu acredito que é isso que você está procurando
Exemplo de uso:
fonte
printf %q
não escape dos principais estilos - é quase tentador arquivar isso como um bug, pois é uma situação em que falha com o objetivo declarado. Entretanto, entretanto, uma boa ligação!Aqui está a minha solução:
fonte
echo
meios queexpandTilde -n
não vão se comportar conforme o esperado, e o comportamento com nomes de arquivos contendo barras invertidas não é definido pelo POSIX. Veja pubs.opengroup.org/onlinepubs/009604599/utilities/echo.htmlBasta usar
eval
corretamente: com validação.fonte
printf %q
para escapar das coisas com segurança mais do que confio no código de validação escrito à mão para não haver bugs .printf
é um$PATH
comando 'd.bash
? Se assimprintf
for , é um builtin e%q
é garantida a presença.bash
suficiente antes para saber que não confio nele. tente:x=$(printf \\1); [ -n "$x" ] || echo but its not null!
Aqui está a função POSIX equivalente à resposta Bash de Håkon Hægland
10-12-2017 editar: adicione
'%s'
por @CharlesDuffy nos comentários.fonte
printf '%s\n' "$tilde_less"
, possivelmente? Caso contrário, ele se comportará mal se o nome do arquivo expandido contiver barras invertidas%s
ou outra sintaxe significativaprintf
. Fora isso, porém, essa é uma ótima resposta - correta (quando as extensões bash / ksh não precisam ser cobertas), obviamente segura (sem sujeiraeval
) e concisa.por que não investigar diretamente o diretório home do usuário com o getent?
fonte
Apenas para estender a resposta da birryree para caminhos com espaços: Você não pode usar o
eval
comando como está, porque ele separa a avaliação por espaços. Uma solução é substituir espaços temporariamente pelo comando eval:Este exemplo depende, é claro, da suposição de que
mypath
nunca contém a sequência de caracteres"_spc_"
.fonte
$(rm -rf .)
Você pode achar isso mais fácil de fazer em python.
(1) Na linha de comando do unix:
Resulta em:
(2) Dentro de um script bash como único - salve-o como
test.sh
:bash ./test.sh
Resultados em execução :(3) Como um utilitário - salve-o como
expanduser
um local no seu caminho, com permissões de execução:Isso poderia ser usado na linha de comando:
Ou em um script:
fonte
echo $thepath
é buggy; precisaecho "$thepath"
corrigir os casos menos incomuns (nomes com guias ou execuções de espaços sendo convertidos em espaços únicos; nomes com globs expandidos) ouprintf '%s\n' "$thepath"
corrigir também os incomuns (por exemplo, um arquivo nomeado-n
ou um arquivo com literais de barra invertida em um sistema compatível com XSI). Da mesma forma,thepath=$(expanduser "$1")
echo
se comportar de uma maneira completamente definida pela implementação, se algum argumento contiver barras invertidas; as extensões XSI opcionais ao POSIX exigem comportamentos de expansão padrão (não-e
ou-E
necessários) para esses nomes.Mais simples : substitua 'magic' por 'eval echo'.
Problema: você terá problemas com outras variáveis porque avaliar é mau. Por exemplo:
Observe que o problema da injeção não ocorre na primeira expansão. Então, se você tivesse que substituir simplesmente
magic
comeval echo
, você deve estar bem. Mas se você fizerecho $(eval echo ~)
isso, seria suscetível à injeção.Da mesma forma, se você fizer isso
eval echo ~
, em vez deeval echo "~"
, que contam como duas vezes ampliada e, portanto, a injeção seria possível de imediato.fonte
s='echo; EVIL_COMMAND'
. (Ele irá falhar porqueEVIL_COMMAND
não existe no seu computador Mas se esse comando tinha sido.rm -r ~
Por exemplo, teria removido o seu diretório home.)Eu fiz isso com a substituição de parâmetro variável depois de ler no caminho usando read -e (entre outros). Assim, o usuário pode preencher com tabulação o caminho e, se o usuário digitar um ~ caminho, será classificado.
O benefício adicional é que, se não houver til, nada acontece à variável e, se houver um til, mas não na primeira posição, ele também será ignorado.
(Incluo o -i para leitura, pois o uso em loop, para que o usuário possa corrigir o caminho se houver algum problema.)
fonte