Por que mv deletou um arquivo com mv id_rsa * .old? [duplicado]

8

Eu queria fazer backup do meu ~/.ssh/id_rsa para id_rsa.old e parece que foi excluído. Como isso é possível? :)

root@localhost:~/.ssh# ls -l
total 16
-rw------- 1 root  root  3326 Mar 12 11:22 id_rsa
-rw-r--r-- 1 root  root   756 Mar 12 11:22 id_rsa.pub
-rw------- 1 userx userx  666 Mar  8 11:09 known_hosts
-rw-r--r-- 1 userx userx  666 Feb 29 10:53 known_hosts.old
root@localhost:~/.ssh# mv id_rsa *.old
root@localhost:~/.ssh# ls -l
total 12
-rw-r--r-- 1 root  root   756 Mar 12 11:22 id_rsa.pub
-rw------- 1 userx userx  666 Mar  8 11:09 known_hosts
-rw------- 1 root  root  3326 Mar 12 11:22 known_hosts.old
root@localhost:~/.ssh# touch p
root@localhost:~/.ssh# mv p *.p
root@localhost:~/.ssh# ls -l
total 12
-rw-r--r-- 1 root  root   756 Mar 12 11:22 id_rsa.pub
-rw------- 1 userx userx  666 Mar  8 11:09 known_hosts
-rw------- 1 root  root  3326 Mar 12 11:22 known_hosts.old
-rw-r--r-- 1 root  root     0 Mar 12 11:28 *.p
root@localhost:~/.ssh# rm *.p
root@localhost:~/.ssh# ls -l
total 12
-rw-r--r-- 1 root  root   756 Mar 12 11:22 id_rsa.pub
-rw------- 1 userx userx  666 Mar  8 11:09 known_hosts
-rw------- 1 root  root  3326 Mar 12 11:22 known_hosts.old
userx@localhost:~$ uname -r
4.2.0-30-generic
userx@localhost:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 15.10
Release:    15.10
Codename:   wily
userx@localhost:~$ bash --version
GNU bash, version 4.3.42(1)-release (x86_64-pc-linux-gnu)
    
por Bojan Landekić 14.03.2016 / 02:24

2 respostas

32

Ele foi renomeado como known_hosts.old , portanto, substituiu o conteúdo anterior de known_hosts.old .

Como você já tem um arquivo chamado known_hosts.old , o padrão de glob *.old foi expandido para known_hosts.old .

Em poucas palavras, o seguinte:

mv id_rsa *.old

foi expandido para:

mv id_rsa known_hosts.old

Em bash , se não houver um arquivo chamado known_hosts.old presente, ele será expandido para literal *.old (dado que você não ativou nullglob ).

    
por 14.03.2016 / 02:29
19

Parece que você pensou que mv id_rsa *.old moveria id_rsa para id_rsa.old , com o * substituído pelo primeiro argumento, mas esse não é o caso. Os curingas são expandidos pelo shell, não pelo comando. No momento em que mv vê o comando, o shell expandiu o curinga. Existem quatro casos:

  • O padrão de curinga não corresponde a nenhum arquivo. Com a maioria dos shells, isso deixa o padrão de caractere curinga não expandido e, portanto, mv é chamado com os argumentos id_rsa e *.old . Em seguida, ele move id_rsa para um arquivo chamado *.old (com o asterisco sendo o primeiro caractere do nome do arquivo). Alguns shells (dependendo de sua configuração) exibirão um erro e não executarão o comando nesse caso.
  • O padrão de curinga corresponde exatamente a um arquivo que não é um diretório. Nesse caso, o shell substitui o padrão pelo nome do arquivo correspondente. Assim, mv move id_rsa para esse arquivo correspondente, sobrescrevendo o arquivo anterior. Foi o que aconteceu no seu caso: mv foi invocado com os argumentos id_rsa e known_hosts.old , para known_hosts.old foi sobrescrito.
  • O padrão de curinga corresponde a dois ou mais arquivos e o último (em ordem lexicográfica) não é um diretório. Nesse caso, mv reclama, porque todos os arquivos, exceto o último, são arquivos de origem e não faz sentido mover vários arquivos para o mesmo arquivo.
  • O padrão de curinga corresponde a um ou mais arquivos e a última correspondência (em ordem lexicográfica) é um diretório. O arquivo de origem é movido para esse diretório. Se já houver um arquivo com o mesmo nome, ele será sobrescrito. Se o padrão tiver mais de uma correspondência, isso também se aplica a todos os arquivos correspondentes ao padrão, exceto à última, pois mv os vê como arquivos de origem.

Para evitar que mv substitua inesperadamente os arquivos de destino, solicite confirmação. Coloque isso na inicialização do seu shell (por exemplo, .bashrc ):

alias cp='cp -i'
alias mv='mv -i'

Para renomear um arquivo com base no nome existente, mv alone não ajuda. Você precisa usar outra ferramenta ou organizar para fornecer mv com o nome completo do destino. Uma maneira de fazer o que você estava tentando fazer é com a expansão de chaves , que permite especificar palavras com um radical comum.

mv id_rsa{,.old}

O shell expande isso para mv com os argumentos id_rsa ( id_rsa concatenado com a string vazia) e id_rsa.old ( id_rsa concatenado com .old ).

Para renomear arquivos em massa de acordo com os padrões, as ferramentas mais comuns são zmv (somente zsh), prename e mmv . Para renomear todos os arquivos do formulário de id_SOMETHING para id_SOMETHING.old , você pode usar

zmv 'id_*' '$f.old'
mmv 'id_*' 'id_#1.old'
prename 's/$//' id_*
    
por 14.03.2016 / 02:52