Como posso higienizar ou escapar caminhos absolutos retornados pelo realpath ou readlink?

8

realpath e readlink retornam caminhos absolutos:

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

Um caminho assim é fácil de lidar. No entanto, algo como isto vai ter alguns problemas:

Por exemplo:

Assim, um nome como esse precisa ser higienizado para poder alimentá-lo com outros comandos. Um usecase pode ser algo assim:

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

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

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

    
por Akiva 14.03.2017 / 20:21

1 resposta

10

Como fazer isso corretamente

Primeiro de tudo, sempre cite 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 do porque você o escolheu a>) por uma questão de consistência.

Agora, vamos atribuir o caminho de pullingATerdon a uma variável e 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, conforme esperado. Mas, se agora citarmos 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á bem. OK, então vim e mais alguma coisa. Sua escolha de editor, embora infeliz, não é relevante.

Por que sua falha

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|<':

Isso mostra que ls foi executado com três argumentos separados: '/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 cadeias sem aspas. Nesse caso, o problema é a divisão de palavras, uma vez que o shell viu os espaços no caminho e leu cada string separada por espaços como um argumento separado.

O exemplo mkdir é um pouco diferente, mas isso é porque você está nos mostrando a mensagem de erro da chamada segundo do comando. Eu acho que você tentou uma vez, e depois correu uma segunda vez para obter a saída para sua pergunta. A primeira vez que você o executou, teria sido 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, devido à divisão de palavras. Primeiro, 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 ['-_-"]'

Então, também, com sucesso, criou um diretório chamado +'|'|_e|\|\|0rth em 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 então, tentou criar o diretório ['-_-"]/pullingATerdon . Isso falhou porque mkdir , por padrão, não cria subdiretórios (pode, se você executá-lo com -p ):

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

Como sua string não citada continha um / , mkdir considerou que um caminho de dois diretórios tentou localizar o primeiro e falhou.

É por isso que ele falhou, mas o que aconteceu é mais complicado. A string que você usou é na verdade um shell glob, especificamente um intervalo glob , que combina todos os arquivos no diretório atual cujo nome é um dos 5 caracteres ' , - , _ ou " . Como você não tem esses arquivos em seu diretório atual, o glob não corresponde a nada e, como é o comportamento padrão no bash, ele retorna:

$ 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ê der um globo que corresponda a algo:

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

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

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

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

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

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

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

Qualquer glob que identifique o arquivo de destino será o único. Dessa forma, você não precisa se preocupar com os caracteres especiais e pode simplesmente deixar o shell lidar com eles.

Algumas referências úteis:

  1. Como posso encontrar e lidar com segurança com nomes de arquivos que contenham novas linhas, espaços ou ambos? : Uma das perguntas frequentes sobre o excelente Wiki do gato cinzento.

  2. Implicações de segurança de esquecer de citar uma variável em shells bash / POSIX : o mesmo post que eu referenciado 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 sufoca em espaços em branco ou outros caracteres especiais? : tudo que você sempre quis para saber como lidar com nomes de arquivos arbitrários no shell.

  4. Quando é necessário cotar duas vezes?: Mais sobre cotações e variáveis e, especificamente, os poucos casos em que você não precisa citá-los

por terdon 14.03.2017 / 22:36