为什么 mv 删除了一个带有 mv id_rsa *.old 的文件?

Boj*_*kić 8 shell bash wildcards rename files

我想备份我的~/.ssh/id_rsaid_rsa.old,看起来它被删除了!这怎么可能?:)

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)
Run Code Online (Sandbox Code Playgroud)

hee*_*ayl 32

它已被重命名为known_hosts.old,因此覆盖了known_hosts.old.

由于您已经在其中命名known_hosts.old了一个文件,因此 glob 模式*.old已扩展为known_hosts.old.

简而言之,以下几点:

mv id_rsa *.old
Run Code Online (Sandbox Code Playgroud)

已扩展为:

mv id_rsa known_hosts.old
Run Code Online (Sandbox Code Playgroud)

在 中bash,如果那里没有名为known_hosts.old存在的文件,它将扩展为文字*.old(假设您尚未启用nullglob)。

  • 在显示的输出中,*filesize* 会很快出现。请注意,**id_rsa** 的大小为 3326,而 **known_hosts.old** 的大小为 666。移动后,**id_rsa** 消失了,**known_hosts.old** 的大小为 3326。 (2认同)

Gil*_*il' 19

看起来您认为这mv id_rsa *.old会移动id_rsaid_rsa.old,而*由第一个参数代替,但事实并非如此。通配符由 shell 扩展,而不是由命令扩展。当mv看到命令时,shell 已经扩展了通配符。有四种情况:

  • 通配符模式不匹配任何文件。对于大多数 shell,这会使通配符模式未扩展,因此mv使用参数id_rsa和调用*.old。然后它移动id_rsa到一个名为的文件*.old(星号是文件名的第一个字符)。在这种情况下,某些 shell(取决于它们的配置)将显示错误并且不运行命令。
  • 通配符模式正好匹配一个不是目录的文件。在这种情况下,shell 用匹配文件的名称替换模式。因此mv移动id_rsa到那个匹配的文件,覆盖之前的文件。这就是您的情况发生的情况:mv使用参数id_rsaknown_hosts.old,调用known_hosts.old被覆盖。
  • 通配符模式匹配两个或多个文件,最后一个(按字典顺序)不是目录。在这种情况下,mv抱怨,因为除了最后一个文件之外的所有文件都是源文件,将多个文件移动到同一个文件中是没有意义的。
  • 通配符模式匹配一​​个或多个文件,最后一个匹配(按字典顺序)是一个目录。源文件被移动到该目录中。如果已经存在同名文件,则将其覆盖。如果模式有多个匹配项,这也适用于除最后一个之外的模式匹配的所有文件,因为mv将它们视为源文件。

为避免mv意外覆盖目标文件,请提示确认。把它放在你的 shell 初始化中(例如.bashrc):

alias cp='cp -i'
alias mv='mv -i'
Run Code Online (Sandbox Code Playgroud)

根据现有名称重命名文件,mv仅凭这一点是没有帮助的。您需要使用其他工具,或安排提供mv完整的目的地名称。做您想做的事情的一种方法是使用大括号扩展,它可以让您指定具有公共词干的单词。

mv id_rsa{,.old}
Run Code Online (Sandbox Code Playgroud)

shell 将其扩展为mv使用参数id_rsaid_rsa与空字符串连接)和id_rsa.oldid_rsa与 连接.old)。

要根据模式批量重命名文件,最常用的工具是zmv(仅限 zsh)prenamemmv. 要将表单的所有文件从to重命名,您可以使用id_SOMETHINGid_SOMETHING.old

zmv 'id_*' '$f.old'
mmv 'id_*' 'id_#1.old'
prename 's/$//' id_*
Run Code Online (Sandbox Code Playgroud)

  • 在您的最后一种情况下(通配符扩展为一个或多个元素,最后一个是目录),那么源文件 _ 和其他与扩展匹配的文件 _ 不会移动到目录中吗? (2认同)