forked from szabgab/perlmaven.com
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththe-most-important-file-system-tools.tt
234 lines (157 loc) · 6.06 KB
/
the-most-important-file-system-tools.tt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
=title The 19 most important file-system tools in Perl 5
=timestamp 2012-07-19T12:45:56
=indexes cwd, tempdir, catfile, catdir, dirname, FindBin, $Bin, File::Spec, File::Basename, File::Temp, Cwd
=status show
=author szabgab
=index 1
=archive 1
=feed 1
=comments 1
=social 1
=abstract start
When writing Perl scripts that need to deal with the file-system, I often need to load a lot of modules.
Many of the functions I need are scattered around in separate modules. Some are built-in functions of perl,
others are in standard modules coming with perl, yet others need to be installed from CPAN.
Let's go over the 15 most used tools in the kit.
=abstract end
<h2>Current path</h2>
Often I need to know what is the current directory I am in. The <b>Cwd</b> module has a
function with the same name, but with all lowercase letters <b>cwd</b> that will return
the <b>current working directory</b>.
<img src="/img/Hdd_icon.svg" style="float: right" />
<code lang="perl">
use strict;
use warnings;
use Cwd qw(cwd);
print cwd, "\n";
</code>
<h2>Temporary directory</h2>
Often I need to create a bunch of temporary files and I'd like to make
sure they are automatically removed when the script finishes. The easiest way is to create
a temporary directory using the <b>tempdir</b> function from <b>File::Temp</b> with the CLEANUP
option being turned on.
<code lang="perl">
use strict;
use warnings;
use autodie;
use File::Temp qw(tempdir);
my $dir = tempdir( CLEANUP => 1 );
print "$dir\n";
open my $fh, '>', "$dir/some_file.txt";
print $fh "text";
close $fh;
</code>
<h2>Operating System independent path</h2>
While the above code will work on both Linux and Windows, people are used to see
back-slashes separating parts of a path on Windows. Besides, this won't work on VMS.
I think. That's where the <b>catfile</b> function of <b>File::Spec::Functions</b>
comes into play:
<code lang="perl">
use strict;
use warnings;
use File::Spec::Functions qw(catfile);
use File::Temp qw(tempdir);
my $dir = tempdir( CLEANUP => 1 );
print "$dir\n";
print catfile($dir, 'some_file.txt'), "\n";
</code>
Try this code. You'll see the name of the temporary directory printed
and the file attached to the end.
<h2>Changing directory</h2>
There often cases when it is easier to first change the working directory to
the temporary directory and work there. It can happen a lot
when writing tests but in other cases too. For this we can use the built in
<b>chdir</b> function.
<code lang="perl">
use strict;
use warnings;
use autodie;
use File::Temp qw(tempdir);
use Cwd;
my $dir = tempdir( CLEANUP => 1 );
print cwd, "\n";
chdir $dir;
print cwd, "\n";
open my $fh, '>', 'temp.txt';
print $fh, 'text';
close $fh;
</code>
That could work well but when File::Temp will try to remove the directory,
we are still "in it" as we have changed the working directory to it.
For example I got the following error message:
<code>
cannot remove path when cwd is /tmp/P3DZP_rmqg for /tmp/P3DZP_rmqg:
</code>
In order to avoid that I usually save the path returned by <b>cwd</b> before
I change the directory and at the end I call <b>chdir</b> again:
<code lang="perl">
my $original = cwd;
...
chdir $original;
</code>
There is still a slight problem with this though. What happens if I have to call
<b>exit()</b> in the middle of the script or if something throws an exception
that terminates the script before it reaches the <b>chdir $original</b>.
Perl has a solution for us, wrapping the last chdir in an <b>END</b> block.
This will ensure, that the code is executed no matter when and how we exit
the script.
<code lang="perl">
my $original = cwd;
...
END {
chdir $original;
}
</code>
<h2>Relative path</h2>
When writing a project that has multiple files (e.g. one ore more scripts, some modules,
maybe some templates) and I don't want to "install" them, the best directory layout
is to make sure everything is in a fixed place <b>relative</b> to the scripts.
So usually I have a project directory in which there is a subdirectory for scripts,
one for modules (lib) , one for templates etc.:
<code>
project/
scripts/
lib/
templates/
</code>
How can I make sure the scripts will find the templates? For this I have several solutions:
<code lang="perl">
use strict;
use warnings;
use autodie;
use FindBin qw($Bin);
use File::Basename qw(dirname);
use File::Spec::Functions qw(catdir);
print $Bin, "\n"; # /home/foobar/Rocket-Launcher/scripts
print dirname($Bin), "\n"; # /home/foobar/Rocket-Launcher
print catdir(dirname($Bin), 'templates'), "\n"; # /home/foobar/Rocket-Launcher/templates
</code>
The <b>$bin</b> variable exported by the <b>FindBin</b> module will contain the path
to the directory of the current script. In our case that will be a path to the
project/scripts/ directory.
The <b>dirname</b> function of <b>File::Basename</b> takes a path and returns the same path
removing the last part.
The last line is just the <b>catdir</b> function from <b>File::Spec::Functions</b>, which
is basically the same as the catfile we saw earlier.
Instead of printing to the screen you would of course use the return value of catdir to specify
the templates.
<h2>Loading modules from a relative path</h2>
Almost the same applies to finding and loading the modules that are located
in the lib/ directory of the project. For this we will combine the previous code
with the <b>lib</b> pragma. That will change the content of the <b>@INC</b> variable
adding the relative path to the beginning of the array.
<code lang="perl">
use strict;
use warnings;
use autodie;
use FindBin qw($Bin);
use File::Basename qw(dirname);
use File::Spec::Functions qw(catdir);
use lib catdir(dirname($Bin), 'lib');
use Rocket::Launcher;
</code>
Of course I assume we already have a file called lib/Rocket/Launcher.pm
<h2>Where is the rest?</h2>
There is more to this, but I think it is enough as the first part of this series.
If you want to make sure you don't miss the others, <a href="/register">register to the newsletter</a>. It's free.
<a href="http://commons.wikimedia.org/wiki/File:Hdd_icon.svg">Image source</a>