Como posso limpar ou escapar de caminhos absolutos retornados pelo caminho real ou pelo readlink?

9

realpathe readlinkretorne caminhos absolutos:

+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome

Um caminho como esse é fácil de lidar. No entanto, algo como isso terá alguns problemas:

insira a descrição da imagem aqui

Por exemplo:

insira a descrição da imagem aqui

Portanto, um nome como esse precisa ser higienizado para poder alimentá-lo com outros comandos. Um caso de usuário pode ser algo como isto:

+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon 

Escusado será dizer vim $baconque não funcionará como esperado.

O que posso fazer para limpar esse caminho absoluto para que ele funcione com outros comandos?

Akiva
fonte
3
Sempre cite suas variáveis:vim "$bacon"
steeldriver
@steeldriver Por que isso?
Akiva 14/03
3
Porque se você não fizer isso, você entrar em situações como este :)
terdon

Respostas:

12

Como fazer isso corretamente

Primeiro, cite sempre suas variáveis . O que você está tentando fazer funciona bem se você citá-lo corretamente:

$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon

Eu mantive o nome de arquivo estranho que você escolheu ( embora eu não tenha idéia de por que você o escolheu ) por uma questão de consistência.

Agora, vamos atribuir o caminho de pullingATerdonpara uma variável e, em seguida, tentar abrir o arquivo:

$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Isso falha, como esperado. Mas, se agora citamos corretamente:

$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'

Funciona como esperado. E sim, você também pode abrir o caminho em um editor (adequado): emacs "$bacon"funcionará perfeitamente. OK, assim será vime qualquer outra coisa. Sua escolha de editor, apesar de infeliz, não é relevante.


Por que o seu falhou

Uma maneira rápida de rastrear o que realmente aconteceu no seu caso é usar set -x(desligue-o novamente com set +x), o que faz com que o shell imprima cada comando que será executado antes de executá-lo. ative as mensagens de depuração do shell com set -x:

$ set -x
$ /bin/ls $bacon 
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':

Que nos mostra que lsfoi executado com três argumentos distintos: '/home/terdon/foo/\e[92mM@r|<', '+'\''|'\''|_e|\|\|0rth'e '[`-_-"]/pullingATerdon'. Isso acontece porque o shell executa divisão de palavras e expansão glob em seqüências de caracteres não citadas. Nesse caso, o problema é a divisão de palavras, uma vez que o shell viu os espaços no caminho e leu cada sequência separada por espaços como um argumento separado.

O mkdirexemplo é um pouco diferente, mas é porque você está nos mostrando a mensagem de erro da segunda chamada do comando. Eu acho que você tentou uma vez e depois executou uma segunda vez para obter o resultado da sua pergunta. A primeira vez que você o executou, seria assim:

$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory

Novamente, isso tentará criar três diretórios, não um, por causa da divisão de palavras. Primeiro, ele criou (com sucesso) o diretório /home/terdon/foo/\e[92mM@r|<:

$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'

Em seguida, também com sucesso, criou um diretório chamado +'|'|_e|\|\|0rthno seu diretório atual:

$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon    0 Mar 15 00:36  pullingATerdon

E, em seguida, tentou criar o diretório [`-_-"]/pullingATerdon. Isso falhou porque mkdir, por padrão, não cria subdiretórios (pode, se você o executar -p):

$ mkdir baz/bar
mkdir: cannot create directory baz/bar’: No such file or directory

Como sua string não citada continha a /, mkdirconsiderou que o caminho de dois diretórios tentou encontrar o principal e falhou.

Por isso falhou, mas o que aconteceu é mais complicado. A seqüência de caracteres que você usou na verdade é um glob shell, especificamente uma gama glob , que corresponde a todos os arquivos no diretório atual, cujo nome é um dos 5 caracteres `, -, _ou ". Como você não possui esses arquivos no diretório atual, o glob não corresponde a nada e, como é o comportamento padrão no bash, retorna automaticamente:

$ echo "[\`-_-\"]/pullingATerdon"  ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon'    ## but it echoes the right thing
[`-_-"]/pullingATerdon             ## and matches nothing, so returns itself.

Para esclarecer, eis o que acontece se você fornecer uma glob que corresponda a algo:

$ echo [p]*   ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*

O não citado [p*]é expandido para a lista de nomes de arquivos correspondentes (apenas um, neste caso) e é para isso que é passado echo. Outra razão pela qual você deve citar todas as coisas.

Finalmente, o erro real que você mostra é da segunda vez em que você executou o comando e falha na primeira etapa, ao tentar criar /home/terdon/foo/\e[92mM@r|<, porque a chamada anterior já havia criado esse diretório.


De maneira mais geral, sempre que você estiver trabalhando com nomes de arquivos arbitrários, use sempre shell globs. Coisas assim:

for file in *; do command "$file"; done

Isso funcionará para qualquer nome de arquivo. Não importa o que aconteça. No nosso exemplo acima, você poderia ter feito:

emacs /home/terdon/*92mM*/pullingATerdon

Qualquer glob que identifique o arquivo de destino exclusivamente fará isso. Dessa forma, você não precisa se preocupar com os caracteres especiais e pode deixar que o shell lide com eles.


Algumas referências úteis:

  1. Como posso encontrar e lidar com segurança com nomes de arquivos contendo novas linhas, espaços ou ambos? : Uma das perguntas frequentes no excelente Wiki de Gray Cat.

  2. Implicações de segurança de se esquecer de citar uma variável nos shells bash / POSIX : o mesmo post que referenciei no início desta resposta. Uma explicação excelente e muito detalhada de todas as coisas que podem dar errado se você não citar corretamente as variáveis ​​do shell.

  3. Por que meu script de shell engasga com espaços em branco ou outros caracteres especiais? : tudo o que você sempre quis saber sobre como lidar com nomes de arquivos arbitrários no shell.

  4. Quando é necessário aspas duplas? : Mais sobre aspas e variáveis ​​e, especificamente, os poucos casos em que você não precisa citá-las

Terdon
fonte