您当前的位置: 首页 >  linux

暂无认证

  • 0浏览

    0关注

    92582博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Linux基础:从一个奇怪的mv命令说起

发布时间:2020-04-30 08:35:43 ,浏览量:0

在这里插入图片描述 mv操作非常简单,但是在实际时这么简单的命令可以变得非常奇怪,这篇文章以mv *的命令来看一下可能会出现的几种有意思的事情,同时进行一些扩展的思考。

示例问题现象

以前曾经碰到过的一个问题,现象中就包括mv命令执行出错,说是第一次成功,第二次出错,后来通过系统日志和审计日志查询到执行的命令是mv *,实际上是不懂技术的操作人员执行时命令考漏了后面的第2个参数。

现象重现

这个示例主要想说明的是需要注意执行时的现场,因为执行的动作可能会对现场进行破坏,确认执行时点的当时状态是非常重要的,比如在一个空目录下,我们执行这条命令会发现mv * 是出错的

liumiaocn:testmv liumiao$ ls
liumiaocn:testmv liumiao$ mv *
usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory
liumiaocn:testmv liumiao$

*在执行的时候会被展开,由于当前目录下没有任何文件,而mv需要两个参数,自然会提示使用上的错误信息,我们将*展开的信息设定为两个文件,就会满足mv命令执行时所需要的条件,比如进行如下准备:

liumiaocn:testmv liumiao$ echo "hello " >file1
liumiaocn:testmv liumiao$ echo "liumiao " >file2
liumiaocn:testmv liumiao$ ls file1	file2
liumiaocn:testmv liumiao$

然后执行mv *,结果如下所示:

liumiaocn:testmv liumiao$ ls file1	file2
liumiaocn:testmv liumiao$ 
liumiaocn:testmv liumiao$ mv *
liumiaocn:testmv liumiao$

结果是什么呢:

liumiaocn:testmv liumiao$ ls file2
liumiaocn:testmv liumiao$ cat file2 
hello 
liumiaocn:testmv liumiao$

实际上mv *执行的操作是mv file1 file2,所以从结果上来看完全一致,这种小问题说穿了没有一点技术含量,而且其执行的前提非常严格,比如*展开是一个文件,或者多余两个文件都无法成功:

liumiaocn:testmv liumiao$ touch file1
liumiaocn:testmv liumiao$ ls file1
liumiaocn:testmv liumiao$ mv *
usage: mv [-f | -i | -n] [-v] source target mv [-f | -i | -n] [-v] source ... directory
liumiaocn:testmv liumiao$ 
liumiaocn:testmv liumiao$ touch file2 file3
liumiaocn:testmv liumiao$ ls file1	file2	file3
liumiaocn:testmv liumiao$ mv *
usage: mv [-f | -i | -n] [-v] source target mv [-f | -i | -n] [-v] source ... directory
liumiaocn:testmv liumiao$ ls file1	file2	file3
liumiaocn:testmv liumiao$
扩展思考示例

很多人会说这么简单的拷贝缺参数的问题,肯定不是我。那我们把这条mv *稍微改一下:

示例代码:mv * ${FILE_DEST_DIR}

在执行过程中,程序运行的某种异常情况或者分支中导致了FILE_DEST_DIR变量为空的情况呢?结果很有会导致最初的判断走上歧途,浪费了不必要的根因分析的时间。

解决方式 事前详细的判断其实即可解决这个问题,比如确实保证此目录存在的前提下才进行mv操作,写脚本的时候也要像写其他高级语言一样考虑所有的情况,对有经验的开发者来说不是问题,但是很多不是专门写脚本的开发者经常会碰到这个问题,另外还有脚本的一个使用场景就是“一行语句”,在一行语句中写详细和复杂的逻辑分支非常不现实,但是又需要的情况下怎么办是需要进一步思考的

扩展场景示例1

当然mv * 这样简单的示例一眼就能看出问题所在,这里我们再使用一个一行的语句把这个情况变得稍微复杂,来看看有趣的事情,首先做如下事前准备

liumiaocn:testmv liumiao$ mkdir -p dir1 dir2 dir3
liumiaocn:testmv liumiao$ touch dir1/file dir2/fie2 dir3/file
liumiaocn:testmv liumiao$ echo "hello" >dir1/file 
liumiaocn:testmv liumiao$ echo "liumiao" >dir3/file 
liumiaocn:testmv liumiao$ cat dir1/file 
hello
liumiaocn:testmv liumiao$ cat dir3/file 
liumiao
liumiaocn:testmv liumiao$

然后将当前目录下的文件全部拷贝到/tmp下新建的一个名为testdestmv目录中,执行日志如下所示:

liumiaocn:testmv liumiao$ mkdir -p /tmp/testdestmv
liumiaocn:testmv liumiao$ ls /tmp/testdestmv
liumiaocn:testmv liumiao$ 
liumiaocn:testmv liumiao$ find . -type f
./dir2/fie2
./dir3/file
./dir1/file
liumiaocn:testmv liumiao$ 
liumiaocn:testmv liumiao$ find . -type f |xargs -I @ mv @ /tmp/testdestmv/
liumiaocn:testmv liumiao$

结果如下:

liumiaocn:testmv liumiao$ find . -type f |xargs -I @ mv @ /tmp/testdestmv/
liumiaocn:testmv liumiao$ ls /tmp/testdestmv/
fie2	file
liumiaocn:testmv liumiao$ cat /tmp/testdestmv/file 
hello
liumiaocn:testmv liumiao$

结合前面所讲的内容也可以理解,但是我们需要思考的是,如果代码中无法定位现象,这样一行语句的问题分析将会是非常麻烦的事情,这些是需要进一步考虑的。

扩展场景示例2

我们这里将上述扩展场景示例1和*的使用进一步结合,准备如下内容

liumiaocn:testmv liumiao$ echo "hello" >dir1/file 
liumiaocn:testmv liumiao$ echo "liumiao" >dir3/file 
liumiaocn:testmv liumiao$ tree . . ├── dir1
│   └── file ├── dir2
└── dir3
    └── file 3 directories, 2 files
liumiaocn:testmv liumiao$

执行结果如下所示:

liumiaocn:testmv liumiao$ find . -type f |xargs mv liumiaocn:testmv liumiao$ find . -type f
./dir1/file
liumiaocn:testmv liumiao$ cat dir1/file 
liumiao
liumiaocn:testmv liumiao$
总结

从结果去理解非常容易,但是如果我们没有意识到不同文件夹下有相同的文件,同时在实际代码编写中又有变量赋值为空的异常分支的情况,综合起来的问题有可能会让我们觉得非常奇怪,而这些只需要我们更加规范的编码,对于异常系的更好控制,一般都能够解决,但是在很多既有的项目代码中可能已经存在类似的代码,这些是我们重构时需要多加注意的。

关注
打赏
1653961664
查看更多评论
立即登录/注册

微信扫码登录

0.4058s