I have this program for tagging directories and switching between the folders via tags. The front end is written in Bash (version 4.4.23) and the back end in Perl (5.26.1).
Critique request
Since this is my very first Perl program, I would like to hear any comments regarding how to improve it.
Code
ds_script.sh
out_file=$(mktemp);
perl ~/.ds/ds_engine.pl "$@" > $out_file
command_type=$(head -n 1 $out_file)
if [ $# -eq 0 ]; then
next_path=$(tail -n 1 $out_file)
perl ~/.ds/ds_engine.pl --update-previous $(pwd)
cd "$next_path"
elif [ "$command_type" == "switch_directory" ]; then
next_path=$(tail -n 1 $out_file)
if [ ! -d "$next_path" ]; then
echo 2> "Directory \"$next_path\" does not exist."
else
~/.ds/ds_engine.pl --update-previous $(pwd)
cd "$next_path"
fi
elif [ "$command_type" == "list" ] || [ "$command_type" == "msg" ]; then
tail -n +2 $out_file
fi
rm $out_file
DSConstants.pm
package DSConstants;
use File::HomeDir;
use constant {
SORT_BY_TAGS => "tags",
SORT_BY_DIRS => "dirs",
TAG_FILE_NAME => File::HomeDir->my_home . "/.ds/tags",
TMP_TAG_FILE_NAME_TEMPLATE => File::HomeDir->my_home . "/.ds/tags.XXXXX",
OPERATION_SWITCH => "switch_directory",
OPERATION_MSG => "msg",
OPERATION_LIST => "list",
PREVIOUS_DIRECTORY_TAG => "zzzPREV",
COMMAND_LIST_TAGS => "-l",
COMMAND_LIST_TAGS_DIRS => "-L",
COMMAND_LIST_SORTED_TAGS => "-s",
COMMAND_LIST_SORTED_TAGS_DIRS => "-S",
COMMAND_LIST_SORTED_DIRS => "-d",
COMMAND_HELP_SHORT => "-h",
COMMAND_HELP_LONG => "--help",
COMMAND_HELP_WOORD => "help",
COMMAND_VERSION_SHORT => "-v",
COMMAND_VERSION_LONG => "--version",
COMMAND_VERSION_WORD => "version",
COMMAND_ADD_SHORT => "-a",
COMMAND_ADD_LONG => "--add-tag",
COMMAND_ADD_WORD => "add",
COMMAND_REMOVE_SHORT => "-r",
COMMAND_REMOVE_LONG => "--remove-tag",
COMMAND_REMOVE_WORD => "remove",
COMMAND_VERSION_SHORT => "-v",
COMMAND_VERSION_LONG => "--version",
COMMAND_HELP_SHORT => "-h",
COMMAND_HELP_LONG => "--help",
COMMAND_UPDATE_PREVIOUS => "--update-previous",
EXIT_STATUS_TOO_MANY_ARGS => 1,
EXIT_STATUS_BAD_COMMAND => 2
};
1;
DirectoryTagEntry.pm
package DirectoryTagEntry;
use warnings;
use strict;
sub minimum {
my @sorted = sort { $a <=> $b } @_;
return $sorted[0];
}
sub edit_distance_impl (\@\@);
sub edit_distance ($$);
sub edit_distance ($$) {
my ($ref_str1, $ref_str2) = @_;
my @chars1 = split "", $ref_str1;
my @chars2 = split "", $ref_str2;
return edit_distance_impl(@chars1, @chars2);
}
sub edit_distance_impl (\@\@) {
my ($ref_chars1, $ref_chars2) = @_;
my $matrix_height = scalar @$ref_chars1 + 1;
my $matrix_width = scalar @$ref_chars2 + 1;
my $matrix = [];
# Initialize the matrix:
for my $y (0 .. $matrix_height - 1) {
my $row = [];
# Make $row $matrix_width elements long:
$row->[$matrix_width - 1] = 0;
$matrix->[$y] = $row;
for my $i (0 .. $matrix_width - 1) {
$row->[$i] = 0;
}
}
# Initialize the leftmost column:
for my $y (0 .. $matrix_height - 1) {
$matrix->[$y][0] = $y;
}
# Initialize the topmost row:
for my $x (1 .. $matrix_width - 1) {
$matrix->[0][$x] = $x;
}
my $substitution_cost;
# Compute the distance:
for my $x (1 .. $matrix_width - 1) {
for my $y (1 .. $matrix_height - 1) {
if ($$ref_chars1[$y - 1] eq $$ref_chars2[$x - 1]) {
$substitution_cost = 0;
} else {
$substitution_cost = 1;
}
$matrix->[$y][$x] =
minimum($matrix->[$y - 1][$x] + 1,
$matrix->[$y][$x - 1] + 1,
$matrix->[$y - 1][$x - 1] +
$substitution_cost);
}
}
return $matrix->[$matrix_height - 1][$matrix_width - 1];
}
sub new {
my $class = shift;
my $self = {@_};
bless ($self, $class);
return $self;
}
sub tag {
my $self = shift;
my $val = shift;
if (defined $val) {
$self->{tag} = $val;
}
return $self->{tag};
}
sub dir {
my $self = shift;
my $val = shift;
if (defined $val) {
$self->{dir} = $val;
}
return $self->{dir};
}
sub get_edit_distance_to {
my $self = shift;
my $str = shift;
return edit_distance($self->{tag}, $str);
}
1;
DirectoryTagEntryList.pm
package DirectoryTagEntryList;
use warnings;
use strict;
use Cwd;
use File::HomeDir;
use lib glob("~/.ds");
use DSConstants;
use DirectoryTagEntry;
sub get_previous_directory;
sub minimum {
my @sorted = sort { $a <=> $b } @_;
return $sorted[0];
}
sub maximum {
my @sorted = sort { $a <=> $b } @_;
return $sorted[$#sorted];
}
sub read_file($);
sub new {
my $class = shift;
my $self = [];
bless ($self, $class);
return $self;
}
sub get_size {
my $self = shift;
return scalar(@{$self});
}
sub read_file($) {
my $self = shift;
my $file_name = shift;
open(TAG_FILE, "<", $file_name) or die "Cannot open $file_name: $!\n";
while (<TAG_FILE>) {
my $line = $_;
chomp $line;
if ($line =~ /^\s*(\w+)\s+(.*)$/g) {
my $tag = $1;
my $dir = $2;
$dir =~ s/^\s+|\s+$//g;
my $tag_entry = DirectoryTagEntry->new( tag => $tag,
dir => $dir );
push @$self, $tag_entry;
}
}
close TAG_FILE;
}
sub write_file {
my $self = shift;
my $file_name = shift;
open(TAG_FILE, ">", $file_name) or die "Cannot open $file_name: $!\n";
for my $tag_entry (@$self) {
print TAG_FILE $tag_entry->tag() . " " . $tag_entry->dir() . "\n";
}
close TAG_FILE;
}
sub add_tag_entry {
my $self = shift;
my $tag = shift;
my $dir = shift;
for my $tag_entry (@$self) {
if ($tag_entry->tag() eq $tag) {
# Tag already present. Update:
$tag_entry->dir($dir);
return $tag_entry;
}
}
my $tag_entry = DirectoryTagEntry->new(tag => $tag,
dir => $dir);
push @$self, $tag_entry;
return $tag_entry;
}
sub remove_tag_entry {
my $self = shift;
my $tag = shift;
my $i = 0;
for my $tag_entry (@$self) {
if ($tag_entry->tag() eq $tag) {
splice @$self, $i, 1;
return $tag_entry;
}
$i++;
}
return undef;
}
sub update_previous_directory {
my $self = shift;
my $prevous_directory_name = shift;
for my $tag_entry (@$self) {
if ($tag_entry->tag() eq DSConstants::PREVIOUS_DIRECTORY_TAG) {
$tag_entry->dir($prevous_directory_name);
return;
}
}
my $prev_tag = DirectoryTagEntry->new(
tag => DSConstants::PREVIOUS_DIRECTORY_TAG,
dir => $prevous_directory_name );
push @$self, $prev_tag;
}
sub get_previous_directory {
my $self = shift;
for my $tag_entry (@$self) {
if ($tag_entry->tag() eq DSConstants::PREVIOUS_DIRECTORY_TAG) {
return $tag_entry->dir();
}
}
return undef;
}
sub sort {
my $self = shift;
my $flag = shift;
if ($flag eq DSConstants::SORT_BY_TAGS) {
@$self = sort { $a->tag() cmp $b->tag() } @$self;
} elsif ($flag eq DSConstants::SORT_BY_DIRS) {
@$self = sort { $a->dir() cmp $b->dir() } @$self;
} else {
die "Unknown sort flag: $flag\n";
}
}
sub print_tags {
my $self = shift;
print DSConstants::OPERATION_LIST, "\n";
for my $tag_entry (@$self) {
print $tag_entry->tag(), "\n";
}
}
sub print_tags_and_dirs {
my $self = shift;
my $max_tag_width = -1;
for my $tag_entry (@$self) {
$max_tag_width = maximum($max_tag_width,
length($tag_entry->tag()));
}
my $tag_column_width = "" . $max_tag_width;
print DSConstants::OPERATION_LIST, "\n";
for my $tag_entry (@$self) {
printf("%-" . $tag_column_width . "s %s\n",
$tag_entry->tag(),
$tag_entry->dir());
}
}
sub print_dirs_and_tags {
my $self = shift;
my $max_dir_width = -1;
for my $tag_entry (@$self) {
$max_dir_width = maximum($max_dir_width,
length($tag_entry->dir()));
}
my $dir_column_width = "" . $max_dir_width;
print DSConstants::OPERATION_LIST, "\n";
for my $tag_entry (@$self) {
printf("%-" . $dir_column_width . "s %s\n",
$tag_entry->dir(),
$tag_entry->tag());
}
}
sub change_tilde_prefix_to_path {
my $dir = shift;
if ($dir =~ /^\s*\~/) {
return File::HomeDir->my_home . substr($dir, 1);
}
return $dir;
}
sub match {
my $self = shift;
my $tag = shift;
my $current_best_edit_distance = 1000_000_000;
my $best_match = undef;
for my $tag_entry (@$self) {
my $tmp_edit_distance = $tag_entry->get_edit_distance_to($tag);
if ($current_best_edit_distance > $tmp_edit_distance) {
$best_match = $tag_entry;
if ($tmp_edit_distance == 0) {
last;
}
$current_best_edit_distance = $tmp_edit_distance;
}
}
if (!$best_match) {
return undef;
}
my $best_match_copy =
DirectoryTagEntry->new(tag => $best_match->tag(),
dir => $best_match->dir());
$best_match_copy->dir(
change_tilde_prefix_to_path(
$best_match_copy->dir()));
return $best_match_copy;
}
sub get_tag_entry {
my $self = shift;
my $tag = shift;
for my $tag_entry (@$self) {
if ($tag eq $tag_entry->tag()) {
return $tag_entry;
}
}
return undef;
}
1;
ds_engine.pl
#!/usr/bin/perl
use warnings;
use strict;
use Cwd;
use File::HomeDir;
use lib glob("~/.ds");
use DSConstants;
use DirectoryTagEntry;
use DirectoryTagEntryList;
sub show_tag_list {
my $list = shift;
my ($show_dirs, $sorted) = @_;
if ($sorted) {
$list->sort(DSConstants::SORT_BY_TAGS);
}
if ($show_dirs) {
$list->print_tags_and_dirs();
} else {
$list->print_tags();
}
}
sub show_tag_list_sorted_by_dirs {
my $list = shift;
$list->sort(DSConstants::SORT_BY_DIRS);
$list->print_dirs_and_tags();
}
sub process_jump_to_previous {
my $list = shift;
my $previous_dir = $list->get_previous_directory();
print DSConstants::OPERATION_SWITCH, "\n";
if (defined $previous_dir) {
print $previous_dir;
} else {
print getcwd;
}
print "\n";
}
sub jump_to_tagged_directory {
my $list = shift;
my $tag = shift;
my $best_tag_entry = $list->match($tag);
print DSConstants::OPERATION_SWITCH, "\n";
if (not defined $best_tag_entry) {
print getcwd();
} else {
print $best_tag_entry->dir();
}
print "\n";
}
sub show_version_info {
print DSConstants::OPERATION_MSG, "\n";
print <<"END"
ds (Directory Switcher) 1.6
Copyright (C) 2022 Rodion "rodde" Efremov.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Rodion Efremov.
END
}
sub show_help_info {
print DSConstants::OPERATION_MSG, "\n";
print <<"END"
Usage: ds
ds TAG
ds -a | --add-tag | add TAG [DIR]
ds -r | --remove-tag | remove TAG [TAG...]
ds -l | -L | -s | -S | -d
-a | --add-tag | add TAG [DIR]
Add tag called TAG and point it to DIR.
If DIR is not specified, point to the current working directory.
-r | --remove-tag | remove TAG [TAG...]
Remove all the specified tags from the user\'s tag file.
-l
List all tags in the tag file.
-L
List all tags and directories in the tag file.
-s
List all tags in the tag file sorted by tag names.
-S
List all tags and directories in the tag file sorted by tag names.
-d
List all directories and tags in the tag file sorted by directories.
TAG
Switches to the directory tagged with TAG. If there is not TAG in
the tag file, the closest tag (by Levenshtein distance) is assumed.
[NO ARGS]
Switch to the previous directory. Issuing this command repeatedly
allows the user to switch back and forth between two directories.
END
}
sub process_single_arg {
my $list = $_[0];
my $flag = $_[1];
if ($flag =~ /^-[lLsSdvh]|--help|--version$/) {
for ($flag) {
$_ eq DSConstants::COMMAND_LIST_TAGS && show_tag_list($list, 0, 0);
$_ eq DSConstants::COMMAND_LIST_TAGS_DIRS && show_tag_list($list, 1, 0);
$_ eq DSConstants::COMMAND_LIST_SORTED_TAGS && show_tag_list($list, 0, 1);
$_ eq DSConstants::COMMAND_LIST_SORTED_TAGS_DIRS && show_tag_list($list, 1, 1);
$_ eq DSConstants::COMMAND_LIST_SORTED_DIRS && show_tag_list_sorted_by_dirs($list);
$_ eq DSConstants::COMMAND_VERSION_SHORT && show_version_info;
$_ eq DSConstants::COMMAND_VERSION_LONG && show_version_info;
$_ eq DSConstants::COMMAND_HELP_SHORT && show_help_info;
$_ eq DSConstants::COMMAND_HELP_LONG && show_help_info;
}
} else {
jump_to_tagged_directory($list, $flag);
}
}
sub add_tag {
my ($list, $tag, $dir) = @_;
print DSConstants::OPERATION_MSG, "\n";
if ($tag =~ /\s+/) {
print "Error: a requested tag contains space characters.\n";
return;
}
my $tag_entry = $list->get_tag_entry($tag);
if (defined $tag_entry) {
if ($tag_entry->dir() ne $dir) {
print "Updating the directory <" . $tag_entry->dir() . "> to <$dir>.";
$tag_entry->dir($dir);
} else {
print "Redirecting the tag \"$tag\" to itself. Nothing changed.";
}
} else {
$list->add_tag_entry($tag, $dir);
print "Added the tag \"$tag\" to point to\n";
print "<$dir>.";
}
save_list($list);
print "\n";
}
sub remove_tag {
my ($list, $tag) = @_;
my $remove_tag_entry = $list->remove_tag_entry($tag);
save_list($list);
print DSConstants::OPERATION_MSG, "\n";
if (defined $remove_tag_entry) {
print "Removed tag \"" . $remove_tag_entry->tag() . "\"" .
" pointing to <" . $remove_tag_entry->dir() . ">.\n";
} else {
print "$tag: no such tag.\n";
}
}
sub update_previous {
my $list = shift;
my $new_dir = shift;
$list->update_previous_directory($new_dir);
save_list($list);
}
sub process_double_args {
my ($list, $cmd, $tag) = @_;
my $cmd_regex = "^" .
DSConstants::COMMAND_ADD_SHORT . "|" .
DSConstants::COMMAND_ADD_LONG . "|" .
DSConstants::COMMAND_ADD_WORD . "|" .
DSConstants::COMMAND_REMOVE_SHORT . "|" .
DSConstants::COMMAND_REMOVE_LONG . "|" .
DSConstants::COMMAND_REMOVE_WORD . "|" .
DSConstants::COMMAND_UPDATE_PREVIOUS . "\$";
if ($cmd !~ /$cmd_regex/) {
die "$cmd: command not recognized.";
}
for ($cmd) {
$_ eq DSConstants::COMMAND_ADD_SHORT && add_tag($list, $tag, getcwd());
$_ eq DSConstants::COMMAND_ADD_LONG && add_tag($list, $tag, getcwd());
$_ eq DSConstants::COMMAND_ADD_WORD && add_tag($list, $tag, getcwd());
$_ eq DSConstants::COMMAND_REMOVE_SHORT && remove_tag($list, $tag);
$_ eq DSConstants::COMMAND_REMOVE_LONG && remove_tag($list, $tag);
$_ eq DSConstants::COMMAND_REMOVE_WORD && remove_tag($list, $tag);
my $update_dir = $tag;
$_ eq DSConstants::COMMAND_UPDATE_PREVIOUS && update_previous($list, $update_dir);
}
}
sub process_triple_args {
my $list = shift;
my $cmd = shift;
my $tag = shift;
my $dir = shift;
my $cmd_regex = "^" .
DSConstants::COMMAND_ADD_SHORT . "|" .
DSConstants::COMMAND_ADD_LONG . "|" .
DSConstants::COMMAND_ADD_WORD . "|" .
DSConstants::COMMAND_REMOVE_SHORT . "|" .
DSConstants::COMMAND_REMOVE_LONG . "|" .
DSConstants::COMMAND_REMOVE_WORD . "\$";
if ($cmd !~ /$cmd_regex/) {
print DSConstants::OPERATION_MSG . "\n";
print "$cmd: command not recognized.";
print DSConstants::COMMAND_ADD_SHORT, ", ";
print DSConstants::COMMAND_ADD_LONG, " or ";
print DSConstants::COMMAND_ADD_WORD, " expected.";
exit DSConstants::EXIT_STATUS_BAD_COMMAND;
}
if ($cmd eq DSConstants::COMMAND_REMOVE_SHORT ||
$cmd eq DSConstants::COMMAND_REMOVE_LONG ||
$cmd eq DSConstants::COMMAND_REMOVE_WORD) {
# $tag contains the first out of two tags to remove:
print DSConstants::OPERATION_MSG, "\n";
my $removed_tag_entry = $list->remove_tag_entry($tag);
if ($removed_tag_entry) {
print "Removed tag \"" . $tag . "\".\n";
} else {
print "No tag \"" . $tag . "\". Omitting.\n";
}
$removed_tag_entry = $list->remove_tag_entry($dir);
if ($removed_tag_entry) {
print "Removed tag \"" . $dir . "\".\n";
} else {
print "No tag \"" . $dir . "\". Omitting.\n";
}
save_list($list);
} else {
add_tag($list, $tag, $dir);
}
}
sub process_multiple_args {
my $list = shift;
my $cmd = shift;
my $cmd_regex = "^(" .
DSConstants::COMMAND_ADD_SHORT . "|" .
DSConstants::COMMAND_ADD_LONG . "|" .
DSConstants::COMMAND_ADD_WORD . "|" .
DSConstants::COMMAND_REMOVE_SHORT . "|" .
DSConstants::COMMAND_REMOVE_LONG . "|" .
DSConstants::COMMAND_REMOVE_WORD . "|" .
DSConstants::COMMAND_UPDATE_PREVIOUS . "\$)";
if ($cmd !~ /$cmd_regex/) {
die "Command \"$cmd\" not recognized.";
}
if ($cmd eq DSConstants::COMMAND_REMOVE_SHORT ||
$cmd eq DSConstants::COMMAND_REMOVE_LONG ||
$cmd eq DSConstants::COMMAND_REMOVE_WORD) {
print DSConstants::OPERATION_MSG . "\n";
my @all_arguments = @_;
for my $tag_name (@all_arguments) {
my $tag_entry = $list->remove_tag_entry($tag_name);
if ($tag_entry) {
print "Removed \"" . $tag_entry->tag() . "\".\n";
} else {
print "No tag \"" . $tag_name . "\ in the tags file. Omitting.\n";
}
}
save_list($list);
return;
}
my @all_arguments = @_;
my $tag = shift;
my @dir_components = @_;
my $dir = join " ", @dir_components;
if ($1 eq DSConstants::COMMAND_UPDATE_PREVIOUS) {
my $path = join " ", @all_arguments;
update_previous($list, $path);
} else {
add_tag($list, $tag, $dir);
}
}
sub save_list {
my $list = shift;
$list->write_file(DSConstants::TAG_FILE_NAME);
}
my $directory_tag_list = DirectoryTagEntryList->new();
$directory_tag_list->read_file(DSConstants::TAG_FILE_NAME);
for (scalar @ARGV) {
$_ == 0 && process_jump_to_previous($directory_tag_list);
$_ == 1 && process_single_arg ($directory_tag_list, @ARGV);
$_ == 2 && process_double_args ($directory_tag_list, @ARGV);
$_ == 3 && process_triple_args ($directory_tag_list, @ARGV);
$_ > 3 && process_multiple_args ($directory_tag_list, @ARGV);
}
ds
(man page source)
.\" Manpage for ds.
.\" Contact [email protected] to correct errors or typos.
.TH man 8 "22 Feb 2022" "1.6" "ds man page"
.SH NAME
ds \- manage directory tags
.SH SYNOPSIS
.B ds
.br
.B ds
[-l|-L|-s|-S|-d-|-h|-v|--help|--version]
.br
.B ds
TAG
.br
.B ds
.RI [-a|--add-tag|add]
TAG
[DIR]
.br
.B ds
.RI [-r|--remove-tag|remove]
TAG
.RI [ TAG .\|.\|.]
.SH DESCRIPTION
.B ds
is a (bash) shell utility for managing directory tags providing a simple facility for tagging directories and switching between them via tags. Also, ds provides a front-end for managing the user's tag file.
.SH OPTIONS
.SS "Generic Program Information"
.TP
.B \-l
List all the tags in the order they appear in the tag file.
.TP
.BR \-L
List all the tags and their respective directories in the order they appear in the tag file.
.TP
.BR \-s
List all the tags in sorted order.
.TP
.BR \-S
List all the tags and their respective directories in the order sorted by tags.
.TP
.BR \-d
List all the directories and their respective tags in the order sorted by directories.
.TP
.BR \-h ", " \-\^\-help
Shows the help message and exits.
.TP
.BR \-v ", " \-\^\-version
Shows the version info and exits.
.SS "Tag Management Commands"
.TP
.BR \-a "," \-\^\-add-tag ", "add
Attempts to add the tag
.B TAG
and point it to the directory
.B DIR.
If
.B TAG
is not yet present in the user's tag file, it is created and pointed to
.B DIR
. Otherwise, the directory of
.B TAG
is redirected to
.B DIR
.
.TP
.BR \-r "," \-\^\-remove-tag ", "remove
Removes all the listed tags from the user's tag file.
.SH DESCRIPTION
ds maintains the tag file
.IB tags
located in the user's directory
.IB \[ti]/.ds
.
The file has two columns: the left one for the tags and the right one for the respective directories. In addition to all the tags, the tag file (usually) has a tag called
.IB zzzPREV
which holds the previously visited directory. This allows ds to "jump" back and forth between two directories simply by invoking the ds without any arguments.
A tag is a string containing no space characters, while directories with spaces are allowed as long as they are surrounded with single or double quotes.
In addition, if the directory starts with a tilde (~), it will be expanded to the name of the user's home directory. In order to keep the tilde character intact, surround the path with single or double quotes.
.SH EXAMPLES
.TP
1. To list all the directory/tag pairs sorted by directory:
.EX
.B ds -d
.EE
.TP
2. To add a tag "docs" and point it to the directory "~/Documents", type:
.EX
.B ds --add-tag docs "~/Documents"
.EE
.TP
3. To remove tags "home", "root", "docs", type:
.EX
.B ds --remove-tag home root docs
.EE
.SH SEE ALSO
alias (1), cd (1)
.SH BUGS
No known bugs. Yet.
.SH AUTHOR
Rodion "rodde" Efremov ([email protected])
installer.sh
#!/usr/bin/env bash
script_magic="alias ds='source ~/.ds/ds_script.sh'"
echo "Installing ds..."
grep "$script_magic" > /dev/null 2> /dev/null ~/.bashrc
if [ $? != 0 ]; then
echo "$script_magic" >> ~/.bashrc
echo "~/.bashrc updated!"
else
echo "~/.bashrc is already updated."
fi
# Create files:
echo "Creating files..."
mkdir -p ~/.ds
echo "Created the ~/.ds directory."
cp ds_engine.pl *.pm ds_script.sh ~/.ds
echo "Copied the code."
tag_file=$HOME"/.ds/tags"
touch "$tag_file"
add_tag_to_file () {
if grep -q "^$1" $tag_file; then
echo "Tag \"$1\" is already in the tag file."
else
echo "$1 $2" >> $tag_file
echo "Added tag \"$1\" to \"$2\""
fi
}
# Populate the default
echo "Populating the tag file with default tags..."
add_tag_to_file "docs" "~/Documents"
add_tag_to_file "down" "~/Downloads"
add_tag_to_file "root" "/"
add_tag_to_file "home" "~"
add_tag_to_file "ds" "~/.ds"
echo "Done populating the tag file with default tags."
echo "Done! ds will be available for use in your next shell session. :-]"
echo ""
echo ""
install-manpages.sh
#!/bin/bash
sudo cp ds /usr/local/man/man1/ds.1
gzip /usr/local/man/man1/ds.1
Outro
I apologize beforehand for posting a lot of code written in different technologies, but I really need your help for making ds more mature.