File Coverage

blib/lib/App/pathed.pm
Criterion Covered Total %
statement 45 61 73.7
branch 16 32 50.0
condition 2 12 16.6
subroutine 7 9 77.7
pod 0 3 0.0
total 70 117 59.8


line stmt bran cond sub pod time code
1             package App::pathed;
2 1     1   765 use strict;
  1         3  
  1         35  
3 1     1   6 use warnings;
  1         2  
  1         27  
4 1     1   5 use Config;
  1         13  
  1         57  
5 1     1   1274 use Getopt::Long;
  1         40240  
  1         10  
6 1     1   3557 use Pod::Usage;
  1         62859  
  1         135  
7 1     1   13 use Pod::Find qw(pod_where);
  1         2  
  1         573  
8             our $VERSION = '0.07';
9              
10             sub usage {
11 0     0 0 0 pod2usage(-input => pod_where({ -inc => 1 }, __PACKAGE__), @_);
12             }
13              
14             sub run {
15 0     0 0 0 my %opt;
16 0 0       0 GetOptions(
17             \%opt, qw(
18             delete|d=s@ append|a=s@ prepend|p=s@
19             unique|u split|s check|c
20             var|v=s sep|e=s help|h man
21             )
22             ) or usage(-exitval => 2);
23 0 0       0 usage(-exitval => 1) if $opt{help};
24 0 0       0 usage(-exitval => 0, -verbose => 2, -output => \*STDERR) if $opt{man};
25 0 0 0     0 usage(-exitval => 2, -msg => '--split and --check are mutually exclusive')
26             if $opt{split} && $opt{check};
27 0         0 my $path = shift @ARGV;
28 0 0 0     0 usage(
29             -exitval => 2,
30             -msg => 'using a path argument and --var and are mutually exclusive'
31             ) if defined $path && $opt{var};
32 0 0       0 unless (defined $path) {
33 0 0       0 if ($opt{var}) {
34 0         0 $path = $ENV{ $opt{var} };
35 0 0 0     0 unless (defined $path && length $path) {
36 0         0 die "The $opt{var} environment variable is empty\n";
37             }
38             } else {
39 0         0 $path = $ENV{PATH};
40             }
41             }
42 0         0 my @result = process($path, \%opt);
43 0         0 print "$_\n" for @result;
44             }
45              
46             # separate methods so it's easily testable
47             sub process {
48 12     12 0 7084 my ($path, $opt) = @_;
49 12   66     94 my $separator = $opt->{sep} // $Config::Config{path_sep};
50 12         79 my @parts = split $separator => $path;
51 12 100       36 if ($opt->{append}) {
52 2         2 push @parts, @{ $opt->{append} };
  2         5  
53             }
54 12 100       27 if ($opt->{prepend}) {
55 2         2 unshift @parts, reverse @{ $opt->{prepend} };
  2         6  
56             }
57 12 100       27 if ($opt->{delete}) {
58 6         9 for my $delete (@{ $opt->{delete} }) {
  6         13  
59 8         12 @parts = grep { index($_, $delete) == -1 } @parts;
  33         88  
60             }
61             }
62 12 100       24 if ($opt->{unique}) {
63 1         2 my %seen;
64 1         3 @parts = grep { !$seen{$_}++ } @parts;
  3         12  
65             }
66 12 100       38 if ($opt->{check}) {
    100          
67 1         2 my (%seen, @result);
68 1         2 for my $part (@parts) {
69 5 100       16 next if $seen{$part}++;
70 4 100       83 next if -r $part;
71 2         5 push @result => "$part is not readable";
72             }
73 1         6 return @result;
74             } elsif ($opt->{split}) {
75 3         13 return @parts;
76             } else {
77 8         37 return (join $separator => @parts);
78             }
79             }
80             1;
81              
82             =pod
83              
84             =head1 NAME
85              
86             App::pathed - munge the Bash PATH environment variable
87              
88             =head1 SYNOPSIS
89              
90             $ PATH=$(pathed --unique --delete rbenv)
91             $ PATH=$(pathed --append /home/my/bin -a /some/other/bin)
92             $ PATH=$(pathed --prepend /home/my/bin -p /some/other/bin)
93             $ for i in $(pathed --split); do ...; done
94             $ pathed --check
95             $ pathed -u --var PERL5LIB
96             $ pathed -u $PERL5LIB
97             $ pathed -d two --sep ';' '/foo/one;foo/two'
98             $ pathed --man
99              
100             =head1 DESCRIPTION
101              
102             The Bash C environment variable contains a colon-separated list of paths.
103             Platforms other than UNIX might use a different separator; C uses the
104             default separator for the current OS. C - "path editor" - can split
105             the path, append, prepend or remove elements, remove duplicates and reassemble
106             it.
107              
108             The result is then printed so you can assign it to the C variable. If
109             C<--split> is used, each path element is printed on a separate line, so you can
110             iterate over them, for example.
111              
112             The path elements can also be checked with C<--check> to make sure that the
113             indicated paths are readable.
114              
115             But C isn't just for the C variable. You can specify an
116             environment variable to use with the C<--var> option, or just pass a value to
117             be used directly after the options.
118              
119             The following command-line options are supported:
120              
121             =over 4
122              
123             =item C<--append>, C<-a> C<< >>
124              
125             Appends the given path to the list of path elements. This option can be
126             specified several times; the paths are appended in the given order.
127              
128             =item C<--prepend>, C<-p> C<< >>
129              
130             Prepends the given path to the list of path elements. This option can be
131             specified several times; the paths are prepended in the given order. For
132             example:
133              
134             $ pathed -p first -p second -p third
135              
136             will result in C.
137              
138             =item C<--delete>, C<-d> C<< >>
139              
140             Deletes those path elements which contain the given substring. This option can
141             be specified several times; the path elements are deleted in the given order.
142              
143             When options are mixed, C<--append> is processed first, then C<--prepend>, then
144             C<--delete>.
145              
146             =item C<--unique>, C<-u>
147              
148             Removes duplicate path elements.
149              
150             =item C<--split>, C<-s>
151              
152             Prints each path element on its own line. If this option is not specified, the
153             path elements are printed on one line, joined by the default path separator as
154             reported by L - usually a colon -, like you would normally specify the
155             C variable.
156              
157             =item C<--check>, C<-c>
158              
159             Checks whether each path element is readable and prints warnings if necessary.
160             Does not check whether the path element is a directory because C can
161             also be used for specifying multiple files such as configuration files.
162             Warnings are printed only once per path element, even if that element occurs
163             several times in C.
164              
165             When C<--check> is used, the path is not printed. C<--check> and C<--split> are
166             mutually exclusive.
167              
168             =item C<--var>, C<-v> C<< >>
169              
170             Use the indicated environment variable.
171              
172             =item C<--sep>, C<-e> C<< >>
173              
174             The default path separator is what L reports - usually a colon - but
175             with this option you can specify a different separator. It is used to split the
176             input path and to join the output path.
177              
178             =item C<--help>, C<-h>
179              
180             Prints the synopsis.
181              
182             =item C<--man>
183              
184             Prints the whole documentation.
185              
186             =back
187              
188             =head1 WHY pathed?
189              
190             The initial motivation for writing C came when I tried to install
191             C with C while C was active. C wanted to be compiled
192             with the system ruby, so I was looking for a quick way to remove C from
193             the C:
194              
195             $ PATH=$(pathed -d rbenv) brew install vim
196              
197             =head1 AUTHORS
198              
199             The following person is the author of all the files provided in this
200             distribution unless explicitly noted otherwise.
201              
202             Marcel Gruenauer , L
203              
204             =head1 COPYRIGHT AND LICENSE
205              
206             The following copyright notice applies to all the files provided in this
207             distribution, including binary files, unless explicitly noted otherwise.
208              
209             This software is copyright (c) 2013 by Marcel Gruenauer.
210              
211             This is free software; you can redistribute it and/or modify it under the same
212             terms as the Perl 5 programming language system itself.