Ir para o diretório usando variáveis ​​bash não funciona quando os nomes de diretório têm espaços

11

Digamos que eu quero armazenar o seguinte comando em uma variável

cd "/cygdrive/c/Program Files/"

Então eu faço isso

dir="cd \"/cygdrive/c/Program Files/\""

Isso deve armazenar o comando para navegar para o diretório Arquivos de Programa, portanto, quando eu digito $ dir, ele me leva para esse diretório. Para verificar se as cotações foram escapadas corretamente, digitei

echo $dir

o que me dá

cd "/cygdrive/c/Program Files/"

Então, tudo deve estar funcionando bem. No entanto, quando eu digito,

$dir

eu recebo

bash: cd: "/cygdrive/c/Program: No such file or directory

O que estou fazendo de errado? Estou usando o Cygwin, mas presumo que esse problema se aplique ao bash em geral.

gsingh2011
fonte
Tente remover as aspas ao redor do nome do diretório e escape do espaço com uma barra invertida.
91111 Mike Onitz19:

Respostas:

8

Resposta curta: consulte BashFAQ # 050 ("Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham!").

Resposta longa: quando o bash analisa um comando, analisa aspas antes de substituir variáveis; ele nunca volta e analisa novamente as cotações nos valores das variáveis; portanto, elas acabam não fazendo nada útil. Usar echo para verificar o comando é completamente enganador, pois mostra o comando após ser analisado; se você quiser ver o que realmente está sendo executado, use set -xos comandos de impressão do shell conforme são executados ou use em printf "%q " $dir; echovez de eco simples.

Se você deseja armazenar um comando complexo (por exemplo, um com espaços ou outros caracteres especiais nas "palavras"), é necessário colocá-lo em uma matriz, em vez de uma variável de texto simples, e depois expandi-lo com o idioma `" $ { array [@]} ", assim:

dir=(cd "/cygdrive/c/Program Files/")
"${dir[@]}"

Agora, se o objetivo é criar uma abreviação fácil de digitar, esse claramente não é o caminho a percorrer. Use um alias ou função de shell:

alias dir='cd "/cygdrive/c/Program Files/"'
dir

ou

dir() { cd "/cygdrive/c/Program Files/"; }
dir
Gordon Davisson
fonte
7

Quando o bash se expande $dir, ele apenas executa a divisão de palavras e a globbing. Repartição de palavras produz três palavras: cd, "/cygdrive/c/Programe Files/". Então o comando a executar é cdcom dois argumentos; cdapenas analisa seu primeiro argumento,, "/cygdrive/c/Programque não é um diretório existente.

Se você deseja executar uma avaliação completa do shell no conteúdo de uma variável, use eval:

eval "$dir"

Observe que você precisa de aspas duplas $dir, caso contrário, a divisão de palavras seria realizada primeiro e evalconcatenaria seus argumentos com espaços. Isso funcionaria aqui, mas geralmente seria ruim (por exemplo, se houvesse dois espaços consecutivos em um nome de arquivo).

No entanto, uma string não é o caminho certo para armazenar um comando shell que você deseja executar. A menos que você tenha um requisito incomum, você deve usar uma função:

dir () {
  cd "/cygdrive/c/Program Files/"
}

Se você está realmente interessado em digitar $dirpara mudar para um diretório específico e este não é apenas um exemplo simples, desde o bash 4, insira shopt -s autocdseu .bashrce defina

dir="/cygdrive/c/Program Files/"

após o qual você pode digitar apenas "/cygdrive/c/Program Files/"ou "$dir"no prompt do shell para alternar para esse diretório. Você ainda precisa de aspas duplas $dir; se você não gosta disso, use zsh em vez de bash.

Gilles 'SO- parar de ser mau'
fonte
6

Armazenar comandos em variáveis ​​geralmente não é uma boa ideia. É para isso que servem as funções e aliases.

Ao invés de

dir="cd \"/cygdrive/c/Program Files/\""

tente um destes:

dir="/cygdrive/c/Program Files"
...
cd "$dir"

ou

dir() { cd "/cygdrive/c/Program Files"; }
dir

ou

alias dir='cd "/tmp/Program Files"'
...
dir

O primeiro formulário, armazenando o nome do diretório em uma variável, significa um pouco mais de digitação, mas fica mais claro quando você executa o comando que está executando a cd. O segundo é o mais próximo do que você está tentando realizar. (Não uso muito pseudônimo no bash; não tenho certeza de que vantagem eles têm sobre as funções.)

EDIT:

E dirprovavelmente é um nome ruim para um apelido ou função, já que existe um comando existente com esse nome (é basicamente uma versão do ls, pelo menos se você tiver o GNU coreutils).

Keith Thompson
fonte
voto positivo paraStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob