没有指定目的地的 `mv ./*` 有什么作用?

Tim*_*Tim 30 wildcards rename

在按回车键之前,我不小心忘记指定目的地。不mv ./*指定目的地将当前目录下的文件和目录移动到哪里?

小智 42

如果最后一个参数是一个目录,则您只需将当前工作目录中的所有文件和目录(名称以点开头的文件和目录除外)移动到该目录中。如果有两个文件,第一个文件可能覆盖了第二个文件。

以下是一些演示:

两个以上的文件,最后一个参数是一个文件

$ mkdir d1 d2 d3
$ touch a b c e
$ mv *
mv: target 'e' is not a directory
Run Code Online (Sandbox Code Playgroud)

两个以上的文件,最后一个参数是一个目录

$ mkdir d1 d2 d3
$ touch a b c
$ mv -v *
'a' -> 'd3/a'
'b' -> 'd3/b'
'c' -> 'd3/c'
'd1' -> 'd3/d1'
'd2' -> 'd3/d2'
Run Code Online (Sandbox Code Playgroud)

两个文件

$ touch a b
$ mv -v *
'a' -> 'b'
Run Code Online (Sandbox Code Playgroud)

进一步说明

shell 将 glob( *)扩展为 的参数mv。glob 通常按字母顺序展开。mv总是看到文件和目录的列表。它永远不会看到 glob 本身。

该命令mv支持两种类型的移动。一个是mv file ... directory。另一个是mv old-file-name new-file-name(或mv old-file-name directory/new-file-name)。


mik*_*erv 31

首先,我将创建一个测试库 - 5 个文件和一个文件夹:

touch file1 file2 file3 file4 file5
mkdir folder
Run Code Online (Sandbox Code Playgroud)

接下来我将运行一个测试命令。该-v选项指定我希望 shell 执行的每个命令都打印到stderr. 该-x选项指定我希望将相同的内容打印到stderr-我希望评估命令之后shell 运行它之前完成它。

sh -cxv 'echo mv *'
Run Code Online (Sandbox Code Playgroud)

输出

echo mv *
+ echo mv file1 file2 file3 file4 file5 folder
mv file1 file2 file3 file4 file5 folder
Run Code Online (Sandbox Code Playgroud)

所以你看到我提供给 shellecho mv *的命令是,shell *展开执行的命令是echo mv所有这些文件和文件夹。

默认情况下,shell 将展开globs,如:

sh -cxv 'echo file[1-5]'
Run Code Online (Sandbox Code Playgroud)

输出

echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5
Run Code Online (Sandbox Code Playgroud)

这是set [+-]fglob 函数的结果:

sh -cxvf 'echo file[1-5]'
Run Code Online (Sandbox Code Playgroud)

输出

echo file[1-5]
+ echo 'file[1-5]'
file[1-5]
Run Code Online (Sandbox Code Playgroud)

因此,当您在配置了默认选项mv *的 shell 中运行命令时,例如shell 会扩展为*当前目录中根据语言环境排序的所有文件的参数列表。它exec(ve)mv (基本上)附加了这个参数列表的系统调用。因此mv,当 shell 对它们进行 glob 处理并对其进行排序时,会获取所有参数。除了做strace看看这些效果,你可以再次使用调试出来,如:

sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
Run Code Online (Sandbox Code Playgroud)

输出

sh\000-s\000--\000mv\000file1\000file2\000file3\000file4\000file5\000folder\
\000$
mv file1 file2 file3 file4 file5 folder
Run Code Online (Sandbox Code Playgroud)

并且便携:

( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1
Run Code Online (Sandbox Code Playgroud)

输出

: /mv/file1/file2/file3/file4/file5/folder/
Run Code Online (Sandbox Code Playgroud)

基本上,shellmv使用目录的内容(如果它不为空并且不包括名称以 开头的文件/文件夹.作为其参数列表来执行。mvis POSIX 指定将其最终参数解释为目录,如果它使用两个以上的参数调用 - 以相同的方式lnis (因为,事实上,它们在底层函数中是非常相似的工具)

足够了echo

sh -cxv 'mv *' ; ls
Run Code Online (Sandbox Code Playgroud)

输出

mv *
+ mv file1 file2 file3 file4 file5 folder
folder/
Run Code Online (Sandbox Code Playgroud)

所有文件都移到最后一个参数中 - 因为它是一个文件夹。现在如果它不是文件夹怎么办?

sh -cxv 'cd *; mv *'; ls . *
Run Code Online (Sandbox Code Playgroud)

输出

cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory

.:
folder/

folder:
file1  file2  file3  file4  file5
Run Code Online (Sandbox Code Playgroud)

这是POSIX 指定 mv在这种情况下的行为方式:

mv [-if] source_file target_file

mv [-if] source_file... target_dir
Run Code Online (Sandbox Code Playgroud)

在第一种概要形式中,mv实用程序应将 source_file 操作数命名的文件移动到target_file指定的目的地。当最终操作数未命名现有目录并且不是引用现有目录的符号链接时,将采用第一种概要形式。在这种情况下,如果source_file命名一个非目录文件并且target_file以尾随/slash字符结尾,mv则应将其视为错误并且不会处理source_file操作数。

在第二种概要形式中,mv应将 source_file 操作数命名的每个文件移动到target_dir操作数命名的现有目录中的目标文件,或者如果target_dir是引用现有目录的符号链接,则引用。每个 source_file 的目标路径应是目标目录的串联,/slash如果目标不是以 a 结尾,则为单个字符/slash,以及source_file的最后一个路径名组件。当最终操作数命名现有目录时,采用第二种形式。

所以如果*扩展为:

  • 两个文件

    • 你应该只有一个文件,它是renamed第二个之后的第一个到第二个是unlinked.
  • 一个或多个文件,最后是一个目录或一个链接

    • 您应该只有一个目录或指向一个目录的链接,这是其所有父级以前的内容刚刚被移动的地方。
  • 还要别的吗

    • 你应该有一个错误信息和一个令人满意的松了一口气。

  • @mikeserv tl;博士,`*` 不是单个参数吗?对?:) (6认同)
  • @user2485710,关键是在 Unix 中,shell 负责在将命令行参数传递给命令之前扩展通配符。在 Windows 中,每个命令都必须扩展通配符本身。这允许 Unix shell 提供与任何命令一起使用的高级通配符工具。 (5认同)
  • @mikeserv 考虑到这些文件并不重要,我急忙清理并跳到下一步,但我可以确认它们现在出现在我编辑的第一篇文章/问题中。 (2认同)

jof*_*fel 19

首先,shell 扩展./*到当前目录中的所有文件(以点开头的文件除外)。

  • 如果没有或只有一个文件:mv失败
  • 如果有两个文件:第一个移动到第二个(因此丢失)
  • 如果有两个以上的文件:
    • 如果最后一个是目录:所有文件都移动到这个目录中
    • 否则mv失败。

  • 只需调用 `echo ./*` 来查看你的 shell 使用的顺序(通常是字母顺序)。 (7认同)