shell命令操作文件时有些特殊的顺序问题:
比如:
1. 管理员修改用户的问题:
user | old | new |
---|---|---|
u1 | 200 | 300 |
u2 | 300 | 400 |
u1用户要从原先的uid:200改为uid:300,要将文件系统内所有属于200的文件全部改为300
u1改为300后与u2用户相同,就造成冲突,u2却要从300改为400
对于这个问题首先想到的是先改变u2的用户ID,这样文件系统内就不存在300这个ID的文件,然后再改u1用户的,命令较少的情况下可以人为的控制命令的执行顺序,命令很多时,就不行了
这个问题所反映出来的是,资源的关系问题,文件系统是个公共资源,被很多用户共同拥有,多用户的操作必然会存在竞争,这种冲突就是因为目标资源已经存在造成的。
像这种从源到目标的操作,必须要先确定目标是否会作为另一个操作的源,如果是,则必须先执行那个操作。
将从源到目标的一次操作定义为op,多个op之间的执行顺序需要排定,以保证op之间不会冲突:
一个op的目标,是否会作为op1的源,就确定了op1要先于op执行,这样就确定了一个局部的op1和op的顺序
将所有的op都按照这个规则确定相互之间的顺序,就得到了很多个局部的op和op之间的顺序,这样由局部的顺序问题推理出全局的顺序问题就是拓扑排序。
我们再分析,op的顺序中却蕴含着另外一种顺序,就是目标到源的顺序,目标作为另一个op的源的先执行,也就是目标先于源。
因此我们现将 目标->源 这样的局部顺序先进行拓扑排序。
这个操作是简单的,直接将每个op里面的new和old取出来送入拓扑排序器:tsort命令。
duanyongchao@zhaoyuke-Android:~$ tsort <<EOF
> 300 200
> 400 300
> EOF
400
300
200
排序后的输出就是一串ID顺序,但还不是op的顺序,此时需要将ID顺序编号,第一个编号0,第二个为1,依次类推。
这个ID编号顺序就反映着op的顺序,u1 200 300 这个op中200的编号和300的编号会大于u2 300 400这个op中300的编号和400的编号,因此,u1 200 300 这个op赋值为300对应的ID编号1,u2 300 400 这个op对应的ID编号0,这样每个op有一个编号,将op的编号从小到大排序就是op的执行顺序。
使用shell脚本很容易将这个算法实现:
#! /bin/bash
OPFILE="op.txt" #存放命令以空格分隔字段:u1 200 300
SEEPIPE='tee >(while read line ;do echo $line >&2 ;done ;echo "------" >&2 )' #管道数据查看器
awk '
BEGIN{
file = ARGV[1]
delete ARGV[1]
while(getline < file)
{
uid[$0] = i++
}
}
{
print uid[$3] ":" $0
}
' <(awk '{print $3,$2}' $OPFILE | eval $SEEPIPE | tsort | eval $SEEPIPE) "$OPFILE" |
eval $SEEPIPE |
sort -t: -k1n |
eval $SEEPIPE |
awk -F: '{print $2}'
此命令的执行结果如下:
duanyongchao@zhaoyuke-Android:~$ cat op.txt
u1 200 300
u2 300 400
u3 500 700
u4 400 500
u5 800 900
duanyongchao@zhaoyuke-Android:~$ ./topsort
300 200
400 300
700 500
500 400
900 800
------
700
900
500
800
400
300
200
------
5:u1 200 300
4:u2 300 400
0:u3 500 700
2:u4 400 500
1:u5 800 900
------
0:u3 500 700
1:u5 800 900
2:u4 400 500
4:u2 300 400
5:u1 200 300
------
u3 500 700
u5 800 900
u4 400 500
u2 300 400
u1 200 300
最终命令的执行顺序为 u3->u5->u4->u2->u1,完全符合操作顺序
2.搬家具的问题:
要将一个家具挪动到另一个位置,必须先确定另一个位置是否有家具
3.拼图的问题
要移动一格,必须保证移动到的格子是空的
等等,还有很多关于资源占有的导致的op顺序的排定问题