はじめに
findの出力結果をdotで図示する例を紹介します。
単純な例
【ファイル・ディレクトリ構成】
find d1 | xargs ls -ld
drwxrwxr-x. xxxxxxx d1
drwxrwxr-x. xxxxxxx d1/d11
drwxrwxr-x. xxxxxxx d1/d11/d111
drwxrwxr-x. xxxxxxx d1/d11/d112
-rw-rw-r--. xxxxxxx d1/d11/d112/f1121
drwxrwxr-x. xxxxxxx d1/d12
drwxrwxr-x. xxxxxxx d1/d12/d121
※ ユーザ名、グループ名、タイムスタンプはxxxxxxxで伏せています。
【出力イメージ】
【shスクリプト】
basic_find_v1.sh
#!/bin/sh
_find(){
top_dir=$1
find ${top_dir} -print -exec dirname {} \; -exec basename {} \; | paste - - -
}
_find $1 | gawk '
BEGIN{
printf("digraph %s {\n", "xxx" );
}
{
id = $1;
parent = $2;
label = $3;
printf("\"%s\" [label=\"%s\"]\n", id, label );
printf("\"%s\" -> \"%s\"\n", parent, id );
}
END{
printf("}\n");
}
' | tee /dev/tty | dot -T png -o q.png
【実行】
スクリプトの起動
sh basic_find_v1.sh d1
実行結果
digraph xxx {
"d1" [label="d1"]
"." -> "d1"
"d1/d11" [label="d11"]
"d1" -> "d1/d11"
"d1/d11/d111" [label="d111"]
"d1/d11" -> "d1/d11/d111"
"d1/d11/d112" [label="d112"]
"d1/d11" -> "d1/d11/d112"
"d1/d11/d112/f1121" [label="f1121"]
"d1/d11/d112" -> "d1/d11/d112/f1121"
"d1/d12" [label="d12"]
"d1" -> "d1/d12"
"d1/d12/d121" [label="d121"]
"d1/d12" -> "d1/d12/d121"
}
shスクリプトの解説
1 #!/bin/sh
2 _find(){
3 top_dir=$1
4 find ${top_dir} -print -exec dirname {} \; -exec basename {} \; | paste
- - -
★4行★ find で出力される パス名、ディレクトリ、ファイル名が3行に別れるので
paste - - -で1行にまとめています。
5 }
6 _find $1 | gawk '
7 BEGIN{
8 printf("digraph %s {\n", "xxx" );
9 }
10 {
11 id = $1;
12 parent = $2;
13 label = $3;
14 printf("\"%s\" [label=\"%s\"]\n", id, label );
15 printf("\"%s\" -> \"%s\"\n", parent, id );
★14、15行★ 二重引用符(")で囲まれた文字列の中で二重引用符を使用するために、
二重引用符を\でエスケープしています。
16 }
17 END{
18 printf("}\n");
19 }
20 ' | tee /dev/tty | dot -T png -o q.png
★20行★ | tee /dev/tty はデバッグ用です。書かなくてもイメージの出力には影響しません。
★その他★ ファイル名、ディレクトリ名に二重引用符(")やバックスラッシュ(\)が含まれる場合
は想定していません、おそらく正しく動作しません。対応するのはちょっと面倒です。
フォルダとファイルの図形を変える
【出力イメージ】
【shスクリプト】
basic_find_v2.sh
#!/bin/sh
_find(){
top_dir=$1
find ${top_dir} -print
}
_make_input(){
while read path dummy
do
printf "%s %s %s" ${path} `dirname ${path}` `basename ${path}`
if [ -d "${path}" ]
then
printf "\tdirectory\n"
else
printf "\tfile\n"
fi
done
}
_find $1 | _make_input | gawk '
BEGIN{
printf("digraph %s {\n", "xxx" );
}
{
id = $1;
parent = $2;
label = $3;
kind = $4;
if( kind == "file"){
printf("\"%s\" [label=\"%s\" shape = box]\n", id, label );
}
if( kind == "directory"){
printf("\"%s\" [label=\"%s\" shape = folder]\n", id, label );
}
else {
printf("\"%s\" [label=\"%s\"]\n", id, label );
}
if( parent != "." )printf("\"%s\" -> \"%s\"\n", parent, id );
}
END{
printf("}\n");
}
' | tee /dev/tty | dot -T png -o q.png
【実行】
スクリプトの起動
sh basic_find_v2.sh d1
実行結果
digraph xxx {
"d1" [label="d1" shape = folder]
"d1/d11" [label="d11" shape = folder]
"d1" -> "d1/d11"
"d1/d11/d111" [label="d111" shape = folder]
"d1/d11" -> "d1/d11/d111"
"d1/d11/d112" [label="d112" shape = folder]
"d1/d11" -> "d1/d11/d112"
"d1/d11/d112/f1121" [label="f1121" shape = box]
"d1/d11/d112/f1121" [label="f1121"]
"d1/d11/d112" -> "d1/d11/d112/f1121"
"d1/d12" [label="d12" shape = folder]
"d1" -> "d1/d12"
"d1/d12/d121" [label="d121" shape = folder]
"d1/d12" -> "d1/d12/d121"
}
shスクリプトの解説
1 #!/bin/sh
2 _find(){
3 top_dir=$1
4 find ${top_dir} -print
5 }
6 _make_input(){
7 while read path dummy
8 do
9 printf "%s %s %s" ${path} `dirname ${path}` `basename ${path}`
10 if [ -d "${path}" ]
11 then
12 printf "\tdirectory\n"
13 else
14 printf "\tfile\n"
15 fi
★10-15行★ ファイルがディレクトリかどうか調べ、結果(※)を出力しています。
※結果: ディレクトリの場合 directory
ファイルの場合 file
16 done
17 }
18 _find $1 | _make_input | gawk '
19 BEGIN{
20 printf("digraph %s {\n", "xxx" );
21 }
22 {
23 id = $1;
24 parent = $2;
25 label = $3;
26 kind = $4;
27 if( kind == "file"){
28 printf("\"%s\" [label=\"%s\" shape = box]\n", id, label );
★28行★ ファイルの場合はshapeをboxにしています。
29 }
30 if( kind == "directory"){
31 printf("\"%s\" [label=\"%s\" shape = folder]\n", id, label );
★31行★ ディレクトリの場合はshapeをfolderにしています。
32 }
33 else {
34 printf("\"%s\" [label=\"%s\"]\n", id, label );
35 }
36 if( parent != "." )printf("\"%s\" -> \"%s\"\n", parent, id );
37 }
38 END{
39 printf("}\n");
40 }
41 ' | tee /dev/tty | dot -T png -o q.png
シンボリックリンクを図示する
【ファイル・ディレクトリ構成】
find d1 | xargs ls -ld
drwxrwxr-x. xxxxxxx d1
drwxrwxr-x. xxxxxxx d1/d11
drwxrwxr-x. xxxxxxx d1/d11/d111
drwxrwxr-x. xxxxxxx d1/d11/d112
-rw-rw-r--. xxxxxxx d1/d11/d112/f1121
lrwxrwxrwx. xxxxxxx d1/d11/sd113 -> /etc
lrwxrwxrwx. xxxxxxx d1/d11/sd114 -> ../d12
lrwxrwxrwx. xxxxxxx d1/d11/sd115 -> /xxxx
drwxrwxr-x. xxxxxxx d1/d12
drwxrwxr-x. xxxxxxx d1/d12/d121
lrwxrwxrwx. xxxxxxx d1/d12/f122 -> ../d11/d112/f1121
※ ユーザ名、グループ名、タイムスタンプはxxxxxxxで伏せています。
【出力イメージ】
【shスクリプト】
basic_find_v3.sh
#!/bin/sh
_find(){
local top_dir=$1
find ${top_dir} -print
}
_make_input(){
local path dummy link_to top_char dir_path dir_link_to file_link_to
local w w2
while read path dummy
do
printf "%s %s %s" ${path} `dirname ${path}` `basename ${path}`
if [ -h "${path}" ]
then
link_to=`file ${path} | sed -n 's/.*\`\(.*\)'\''/\1/p'`
top_char=`sed -n 's/^\(.\).*/\1/p' <<< "${link_to}"`
if [ "${top_char}" != "/" ]
then
dir_path=`dirname ${path}`
dir_link_to=`dirname ${link_to}`
file_link_to=`basename ${link_to}`
w=`cd "${dir_path}";cd "${dir_link_to}";pwd`
w2=`cd "${top_dir/..}";pwd`
link_to=`expr "${w}/${file_link_to}" : "${w2}/\(.*\)"`
link_to="${top_dir}"/"${link_to}"
fi
printf "\tsymbolic_link %s\n" "${link_to}"
continue
fi
if [ -d "${path}" ]
then
printf "\tdirectory\n"
else
printf "\tfile\n"
fi
done
}
main(){
local top_dir=$1
local type=$2
local output=$3
_find ${top_dir} | _make_input | gawk '
BEGIN{
printf("digraph %s {\n", "xxx" );
}
{
id = $1;
parent = $2;
label = $3;
kind = $4;
link_to = $5;
if( kind == "file"){
printf("\"%s\" [label=\"%s\" shape = box]\n", id, label );
}
if( kind == "directory"){
printf("\"%s\" [label=\"%s\" shape = folder]\n", id, label );
}
else if( kind == "symbolic_link"){
printf("\"%s\" [label=\"%s\" shape =doubleoctagon ]\n", id, label );
printf("\"%s\" -> \"%s\" [label = \"ln -s\"]\n", id, link_to );
}
else {
printf("\"%s\" [label=\"%s\"]\n", id, label );
}
if( parent != "." )printf("\"%s\" -> \"%s\"\n", parent, id );
}
END{
printf("}\n");
}
' | tee /dev/tty | dot -T ${type} -o ${output}
}
top_dir=$1
type=$2
output=$3
main ${top_dir} ${type} ${output}
【実行】
スクリプトの起動
sh basic_find_v3.sh d1 png q.png
実行結果
digraph xxx {
"d1" [label="d1" shape = folder]
"d1/d11" [label="d11" shape = folder]
"d1" -> "d1/d11"
"d1/d11/d111" [label="d111" shape = folder]
"d1/d11" -> "d1/d11/d111"
"d1/d11/d112" [label="d112" shape = folder]
"d1/d11" -> "d1/d11/d112"
"d1/d11/d112/f1121" [label="f1121" shape = box]
"d1/d11/d112/f1121" [label="f1121"]
"d1/d11/d112" -> "d1/d11/d112/f1121"
"d1/d11/sd113" [label="sd113" shape =doubleoctagon ]
"d1/d11/sd113" -> "/etc" [label = "ln -s"]
"d1/d11" -> "d1/d11/sd113"
"d1/d11/sd114" [label="sd114" shape =doubleoctagon ]
"d1/d11/sd114" -> "d1/d12" [label = "ln -s"]
"d1/d11" -> "d1/d11/sd114"
"d1/d11/sd115" [label="sd115" shape =doubleoctagon ]
"d1/d11/sd115" -> "/xxxx" [label = "ln -s"]
"d1/d11" -> "d1/d11/sd115"
"d1/d12" [label="d12" shape = folder]
"d1" -> "d1/d12"
"d1/d12/d121" [label="d121" shape = folder]
"d1/d12" -> "d1/d12/d121"
"d1/d12/f122" [label="f122" shape =doubleoctagon ]
"d1/d12/f122" -> "d1/d11/d112/f1121" [label = "ln -s"]
"d1/d12" -> "d1/d12/f122"
}
shスクリプトの解説
1 #!/bin/sh
2 _find(){
3 local top_dir=$1
4 find ${top_dir} -print
5 }
6 _make_input(){
7 local path dummy link_to top_char dir_path dir_link_to file_link_to
8 local w w2
9
10 while read path dummy
11 do
12 printf "%s %s %s" ${path} `dirname ${path}` `basename ${path}`
13 if [ -h "${path}" ]
14 then
15 link_to=`file ${path} | sed -n 's/.*\`\(.*\)'\''/\1/p'`
16 top_char=`sed -n 's/^\(.\).*/\1/p' <<< "${link_to}"`
17 if [ "${top_char}" != "/" ]
18 then
19 dir_path=`dirname ${path}`
20 dir_link_to=`dirname ${link_to}`
21 file_link_to=`basename ${link_to}`
22 w=`cd "${dir_path}";cd "${dir_link_to}";pwd`
23 w2=`cd "${top_dir/..}";pwd`
24 link_to=`expr "${w}/${file_link_to}" : "${w2}/\(.*\)"`
25 link_to="${top_dir}"/"${link_to}"
26 fi
27
28 printf "\tsymbolic_link %s\n" "${link_to}"
29 continue
30 fi
31 if [ -d "${path}" ]
32 then
33 printf "\tdirectory\n"
34 else
35 printf "\tfile\n"
36 fi
37 done
38 }
39 main(){
40 local top_dir=$1
41 local type=$2
42 local output=$3
43 _find ${top_dir} | _make_input | gawk '
44 BEGIN{
45 printf("digraph %s {\n", "xxx" );
46 }
47 {
48 id = $1;
49 parent = $2;
50 label = $3;
51 kind = $4;
52 link_to = $5;
53 if( kind == "file"){
54 printf("\"%s\" [label=\"%s\" shape = box]\n", id, label );
55 }
56 if( kind == "directory"){
57 printf("\"%s\" [label=\"%s\" shape = folder]\n", id, label );
58 }
59 else if( kind == "symbolic_link"){
60 printf("\"%s\" [label=\"%s\" shape =doubleoctagon ]\n", id, labe
l );
★60行★ シンボリックリンクの場合、shapeをdoubleoctagonにしています。
61 printf("\"%s\" -> \"%s\" [label = \"ln -s\"]\n", id, link_to );
★61行★ シンボリックリンクのエッジのlabelを”ln -s”にしています。
62 }
63 else {
64 printf("\"%s\" [label=\"%s\"]\n", id, label );
65 }
66 if( parent != "." )printf("\"%s\" -> \"%s\"\n", parent, id );
67 }
68 END{
69 printf("}\n");
70 }
71 ' | tee /dev/tty | dot -T ${type} -o ${output}
72 }
73
74 top_dir=$1
75 type=$2
76 output=$3
77 main ${top_dir} ${type} ${output}
【shスクリプト(フィルタ版)】
basic_find_v4.sh
#!/bin/sh
_find(){
local top_dir=$1
find ${top_dir} -print
}
_make_input(){
local path dummy link_to top_char dir_path dir_link_to file_link_to
local w w2
while read path dummy
do
printf "%s %s %s" ${path} `dirname ${path}` `basename ${path}`
if [ -h "${path}" ]
then
link_to=`file ${path} | sed -n 's/.*\`\(.*\)'\''/\1/p'`
top_char=`sed -n 's/^\(.\).*/\1/p' <<< "${link_to}"`
if [ "${top_char}" != "/" ]
then
dir_path=`dirname ${path}`
dir_link_to=`dirname ${link_to}`
file_link_to=`basename ${link_to}`
w=`cd "${dir_path}";cd "${dir_link_to}";pwd`
w2=`cd "${top_dir/..}";pwd`
link_to=`expr "${w}/${file_link_to}" : "${w2}/\(.*\)"`
link_to="${top_dir}"/"${link_to}"
fi
printf "\tsymbolic_link %s\n" "${link_to}"
continue
fi
if [ -d "${path}" ]
then
printf "\tdirectory\n"
else
printf "\tfile\n"
fi
done
}
mainx(){
_make_input | gawk '
BEGIN{
printf("digraph %s {\n", "xxx" );
}
{
id = $1;
parent = $2;
label = $3;
kind = $4;
link_to = $5;
if( kind == "file"){
printf("\"%s\" [label=\"%s\" shape = box]\n", id, label );
}
if( kind == "directory"){
printf("\"%s\" [label=\"%s\" shape = folder]\n", id, label );
}
else if( kind == "symbolic_link"){
printf("\"%s\" [label=\"%s\" shape =doubleoctagon ]\n", id, label );
printf("\"%s\" -> \"%s\" [label = \"ln -s\"]\n", id, link_to );
}
else {
printf("\"%s\" [label=\"%s\"]\n", id, label );
}
if( parent != "." )printf("\"%s\" -> \"%s\"\n", parent, id );
}
END{
printf("}\n");
}
'
}
mainx
shスクリプト(フィルタ版)の使い方例
find d1 | sh basic_find_v4.sh | tee /dev/tty | dot -T svg -o q.svg
環境
Graphviz 2.38.0
ホスト Windows10 COREi7
VM VirtualBox バージョン 5.2.6 r120293 (Qt5.6.2)
CentOS Linux release 7.4.1708 (Core)
参考
おわりに
- ここまで書いてから気付いたのですが、basic_find_v3.shの43行目の"_find \${top_dir} |"と71行目の"| tee /dev/tty | dot -T ${type} -o ${output}"を外せばフィルタになりますね。
ということで、フィルタ版のbasic_find_v4.sh
を追加しました。