Ir ao diretório usando variáveis bash não funciona quando os nomes dos diretórios possuem espaços

11

Digamos que eu queira 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 Program Files, assim, quando eu digito $ dir, ele me leva para esse diretório. Para verificar se as cotações foram corretamente escapadas, eu digito

echo $dir

que me dá

cd "/cygdrive/c/Program Files/"

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

$dir

Eu obtenho

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

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

    
por gsingh2011 19.11.2011 / 21:44

4 respostas

8

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

Resposta longa: quando o bash analisa um comando, ele analisa as cotações antes de substituir as variáveis; ele nunca volta e re-analisa as cotações nos valores das variáveis, então elas acabam não fazendo nada de útil. Usar echo para verificar o comando é completamente enganador porque mostra o comando após ter sido analisado; Se você quiser ver o que realmente está sendo executado, use set -x para ter os comandos de impressão do shell à medida que são executados ou use printf "%q " $dir; echo em vez de echo simples.

Se você quiser armazenar um comando complexo (isto é, um com espaços ou outros caracteres especiais nas "palavras"), será necessário colocá-lo em uma matriz em vez de uma variável de texto simples e, em seguida, expandi-lo com a expressão idiom ' "$ {array [@]}", assim:

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

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

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

ou

dir() { cd "/cygdrive/c/Program Files/"; }
dir
    
por 19.11.2011 / 22:15
7

Quando o bash expande $dir , ele somente executa a divisão e globalização de palavras nele. A divisão de palavras produz três palavras: cd , "/cygdrive/c/Program e Files/" . Então o comando para executar é cd com dois argumentos; cd apenas analisa seu primeiro argumento, "/cygdrive/c/Program , que não é um diretório existente.

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

eval "$dir"

Observe que você precisa das aspas duplas em torno de $dir , caso contrário, a divisão de palavras seria executada primeiro e, em seguida, eval concatenaria seus argumentos com espaços. Isso funcionaria aqui, mas ficaria ruim em geral (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 estar usando uma função:

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

Se você estiver realmente interessado em digitar $dir para alternar para um diretório específico e este não é apenas um exemplo simples, desde o bash 4, coloque shopt -s autocd no seu .bashrc e 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 das aspas duplas em torno de $dir ; se você não gostar disso, use zsh em vez de bash.

    
por 19.11.2011 / 22:15
6

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

Em vez de

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

experimente 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 digitar um pouco mais, mas fica mais claro quando você executa o comando que está fazendo em cd . O segundo é o mais próximo do que você está tentando realizar. (Eu não uso aliases muito no bash; não tenho certeza de qual vantagem eles têm sobre as funções.)

EDITAR:

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

    
por 19.11.2011 / 22:38
-1

Tente

bash$ echo  'This   is   "a     long"  line' > mylist
bash$ echo @mylist
@mylist
bash$ cmd
c:\> c:\cygwin\bin\echo @mylist
This is a     long line

Mas ainda mais especificamente:

link

    
por 19.11.2011 / 22:02