Por que não consigo escapar de espaços em um script bash? [duplicado]

60

Estou tentando escapar do caractere de espaço para um caminho no Bash, mas não usando uma barra invertida ou citações funciona.

Script

.sh:

ROOT="/home/hogar/Documents/files/"
FILE=${ROOT}"bdd.encrypted"
DESTINATION="/home/hogar/Ubuntu\ One/folder"

mv ${FILE} ${DESTINATION}

Depois de executar o script ( ./file ), este é o resultado:

mv: target 'One/folder' is not a directory

Por que o comando mv divide a string e como posso impedir que isso aconteça?

    
por Lucio 10.01.2014 / 01:40

3 respostas

72

Você está expandindo a variável DESTINATION, se você usou echo , isso é o que você obteria:

echo ${DESTINATION}
/home/hogar/Ubuntu\ One/folder

Mas mv não entende isso:

mv ${FILE} ${DESTINATION}                                                
mv: cannot move '/home/hogar/Documents/files/bdd.encrypted' to '/home/hogar/Ubuntu\ One/folder': No such file or directory

(por alguma razão meu mv é mais detalhado)

Para evitar isso, você deve usar aspas em vez disso:

mv "${FILE}" "${DESTINATION}"

Se você não precisa de expansão (já que você já está expandindo antes), basta usar "$..." :

mv "$FILE" "$DESTINATION"
    
por 10.01.2014 / 01:49
22

Seu passo para preparar as variáveis é "próximo o suficiente" e funcionará, mas mostra uma fraqueza na compreensão (e é por isso que você postou!)

A designação para DESTINATION precisa ser apenas uma dessas duas:

DESTINATION="/home/hogar/Ubuntu One/folder"

ou

DESTINATION=/home/hogar/Ubuntu\ One/folder

As aspas duplas se transformam em citações (um formulário que possui alguma mágica) e a barra invertida é capaz de citar um único caractere após ele. Pessoalmente, acho que o formulário usando aspas duplas é preferível porque visualmente é melhor.

A propósito, por razões semelhantes, eu faria a atribuição do FILE como:

FILE="${ROOT}bdd.encrypted"

que mostra a ocasião bastante rara de usar ${NAME} em vez de simplesmente $NAME - para separar o nome da variável de qualquer letra seguinte. De fato, se você não tivesse um trailing / sobre a definição de ROOT, eu estaria escrevendo

FILE="$ROOT/bdd.encrypted"

que parece ainda melhor. Ele também lhe dará benefícios ocasionais que eu não vou entrar; vamos apenas dizer que, na minha experiência, os traços finais tendem a ser mais indesejados do que bem-vindos, e definitivamente não são necessários. (Isso tem consequências quando se trata de links simbólicos, mas isso é definitivamente muito detalhe nesta resposta já grande).

O ponto crucial do seu problema está realmente ocorrendo no comando mv , que deve ser escrito como

mv "$FILE" "$DESTINATION"

porque hoje em dia, você nunca sabe quando uma variável que representa um caminho terá espaços. Mais uma vez, observe a evitação de chaves para uma simples expansão de variáveis.

A razão pela qual você tem um problema é por causa do processo que o shell usa para construir linhas de comando. Se você ler atentamente o manual do shell, ele basicamente diz que as variáveis (e algumas outras coisas) são expandidas, então ele procura por espaços. Claro que há um pouco mais de complexidade no processo, mas essa é a essência do problema para você.

Mais um ponto relacionado e que vale a pena conhecer: a distinção entre $* , $@ , "$*" e "$@" . Então eu vou falar sobre isso!

Se você tem um script de shell que pode ser chamado como:

foo -x "attached is the software" "please read the accompanying manual"

, em seguida, foo verá -x como $1 e as duas strings como $2 e $3 (que você deve acessar como "$2" e "$3" por motivos óbvios). Se, no entanto, você quiser passar todo esse conjunto de parâmetros para outro script, o seguinte sofrerá uma divisão horrível em todos os espaços:

bar $*

e o seguinte (que era a única maneira na versão 0.X do Bourn shell original) fará com que todos os argumentos sejam passados como uma única string (também WRONG)

bar "$*"

então a seguinte sintaxe foi adicionada como um caso mágico muito especial:

bar "$@"

que cita deliberadamente os membros individuais de $* como sequências de texto completas, mas as mantém separadas. Todo mundo é um vencedor agora.

É meio divertido experimentar outros efeitos de $@ quando não usados como "$@" , mas você descobrirá rapidamente que não é realmente divertido ... é apenas um caso especial que resolve um grande problema.

Todas as shells modernas e decentes que suportam matrizes também usam @ em um caso especial:

${arr[*]}
"${arr[*]}"
"${arr[@]}"

onde o primeiro sofrerá divisão em todos os espaços, levando a um número imprevisível de palavras separadas, o segundo produzirá todos os membros da matriz em uma sequência e o terceiro oferecerá muito bem cada membro da matriz como uma única referência ininterrupta string.

Aproveite!

    
por 10.01.2014 / 06:31
13

Sim, você escapou do espaço quando atribuiu o valor a $DESTINATION

Mas quando você o usa com o comando mv, você não fez

mv ${FILE} ${DESTINATION}

Use isso:

mv "${FILE}" "${DESTINATION}"
    
por 10.01.2014 / 01:42

Tags

Como posso criar uma interface ethernet virtual em uma máquina sem um adaptador físico? Parêntese na expr aritmética: 3 * (2 + 1) ______ qstntxt ___

%code% parece não gostar de parênteses (usado em matemática para especificar a prioridade do operador):

%pre%

Como expressar a prioridade do operador no bash?

    
______ azszpr149832 ___

Outra maneira de usar %code% bash builtin:

%pre%

Nota

Como @ Stéphane Chazelas apontou , em %code% você deve usar %code% para fazer aritmética %code% ou %code% para legibilidade.

Para portabilidade, use %code% como @Bernhard answer .

    
______ azszpr149830 ___

Você pode usar a expansão aritmética.

%pre%

Na minha opinião pessoal, isso parece um pouco melhor do que usar %code% .

De %code%

%bl0ck_qu0te%     
______ azszpr149916 ___

Não há razão para usar %code% para aritmética em shells modernos.

POSIX define o operador de expansão %code% . Então você pode usar isso em todos os shells compatíveis com POSIX (o %code% de todos os Unix-gostos modernos, traço, bash, yash, mksh, zsh, fino, ksh ...).

%pre%

%code% também introduziu um %code% builtin que é transmitido com o mesmo tipo de expressão aritmética, não expande para algo, mas retorna um status de saída com base em se a expressão é resolvida como 0 ou não, como em %code% :

%pre%

No entanto, como a cotação a torna inábil e não muito legível (não na mesma proporção que %code% , é claro), %code% também introduziu uma forma alternativa %code% :

%pre%

, que é muito mais legível e deve ser usado em seu lugar.

%code% e %code% só estão disponíveis em %code% , %code% e %code% . A sintaxe %code% deve ser preferida se a portabilidade para outros shells for necessária, %code% é necessário apenas para shells pré-POSIX semelhantes a Bourne (normalmente o shell Bourne ou versões antigas do shell Almquist).

Na frente não-Bourne, existem alguns shells com operador aritmético integrado:

  • %code% / %code% (na verdade, o primeiro shell Unix com avaliação aritmética incorporada):

    %pre%
  • %code% (com base em %code% )

    %pre%
  • como uma nota de história, a versão original do shell Almquist, como publicado no usenet em 1989, tinha um %code% construído (na verdade, mesclado com %code% ), mas foi removido posteriormente.

______ azszpr149991 ___

%code% é um comando externo, não é uma sintaxe especial do shell. Portanto, se você quiser que %code% veja os caracteres especiais do shell, será necessário protegê-los da análise do shell, citando-os. Além disso, %code% precisa que cada número e operador sejam passados como um parâmetro separado. Assim:

%pre%

A menos que você esteja trabalhando em um antigo sistema unix dos anos 1970 ou 1980, há muito poucas razões para usar %code% . Antigamente, os shells não tinham uma maneira interna de realizar aritmética, e você precisava chamar o utilitário %code% . Todos os shells POSIX possuem aritmética interna por meio da sintaxe expansão aritmética .

%pre%

A construção %code% se expande para o resultado da expressão aritmética (escrita em decimal). Bash, como a maioria das shells, suporta apenas módulos aritméticos inteiros 2 64 (ou módulo 2 32 para versões mais antigas do bash e algumas outras shells em máquinas de 32 bits). p>

O Bash oferece uma sintaxe de conveniência adicional quando você deseja realizar atribuições ou para testar se uma expressão é 0, mas não se importa com o resultado. Esta construção também existe em ksh e zsh, mas não em sh simples.

%pre%

Além da aritmética inteira, %code% oferece algumas funções de manipulação de string. Eles também são incluídos pelos recursos de shells POSIX, exceto por um: %code% testa se a string corresponde ao regexp especificado. Um shell POSIX não pode fazer isso sem ferramentas externas, mas o bash pode com %code% (com um sintaxe diferente de regexp - %code% é uma ferramenta clássica e usa BRE, bash usa ERE).

A menos que você esteja mantendo scripts executados em sistemas de 20 anos, não é necessário saber que %code% já existiu. Use aritmética de shell.

    
______ azszpr149824 ___

Use parênteses com aspas:

%pre%

As citações evitam que o bash interprete os parênteses como a sintaxe bash.

    
______ azszpr349827 ___

Se você tiver bc ...

%pre%     
___