Vidir: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Raku}}: Bug fix, rename some variables minor tweaks)
Line 116: Line 116:


</lang>
</lang>

=={{header|Phix}}==
Should work on Windows and Linux. I have commented out all the actualy destructive statements.
<lang Phix>-- demo/rosetta/vidir.exw
string directory = ".",
editor = iff(platform()=WINDOWS?"notepad":"gedit"),
workfile = "vidir.txt"
-- filter = ""
--bool recursive = false,
-- overwrite = false,
-- verbose = false -- ,... etc

procedure process_command_line()
sequence cl = command_line()[3..$]
if length(cl) then
-- I assume you can figure out how to deal with eg
-- {`-d`,`C:\Users\Pete\Documents`,`-e`,`notepad++`}
?{"command line arguments (ignored)",cl}
end if
end procedure

process_command_line()
sequence d = dir(directory)
if d=-1 then
crash("no such directory")
end if
d = d[3..$] -- (drop "." and "..")
integer fn = open(workfile,"w")
for i=1 to length(d) do
printf(fn,"%d: %s\n",{i,d[i][D_NAME]})
end for
close(fn)
{} = system_exec(editor&" "&workfile)
object lines = get_text(workfile,GT_LF_STRIPPED)
integer last = 0
if lines=-1 then
crash("error reading edited file")
end if
for i=1 to length(lines) do
sequence r = scanf(lines[i],"%d: %s")
if r={} then
crash("error parsing line")
end if
{{integer line, string name}} = r
for last=last+1 to line-1 do
printf(1,"delete_file(%s)\n",{d[last][D_NAME]})
if not file_exists(d[last][D_NAME]) then ?9/0 end if
-- if not delete_file(d[last][D_NAME]) then
-- crash("error deleting file")
-- end if
end for
string prev = d[line][D_NAME]
if prev!=name then
printf(1,"rename_file(%s,%s)\n",{prev,name})
if not file_exists(prev) then ?9/0 end if
if file_exists(name) then ?9/0 end if
-- if name[2]=':' and name[1]!=prev[1] then
-- if not move_file(prev,name) then
-- crash(error moving file")
-- end if
-- elsif not rename_file(prev,name) then
-- crash("error renaming file")
-- end if
end if
last = line
end for
?"done"
{} = wait_key()</lang>


=={{header|Raku}}==
=={{header|Raku}}==

Revision as of 01:44, 14 June 2020

Vidir is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Recreate the vidir utility in your favorite programming language.

Vidir is a bulk file renaming utility included in the moreutils package available for Linux and Linux-like operating systems.


Basic operation involves:

  • Reading the file names from a directory into a text file.
  • Editing the text file with a third party editor to make bulk changes to file names (modify,delete).
  • Using the edited text file to then apply the desired changes to the file system.


In general, the filenames are typically written to the text file, one to a line with an identifying label (a unique integer in the originals case). The text file is edited as desired; file names changed, files moved, files removed, files added. Then the edited text file is used to apply the changes to the file system.

It is not required to support multiple operating systems. Make a note of which one(s) are supported.


There are many possible ways to enhance the utility but that is the basic functionality.

Some possible enhancements:

  • Operate on directories other than the current directory.
  • Recursive directory search.
  • File input filtering.
  • File/Directory permissions detection.
  • Allow creation of files / directories.
  • Verbose operation.
  • Text editor selectable from the command line.
  • First party text editing.
  • Support multiple operating systems


At a minimum support file name modification and file removal in the current directory. Add as many enhancements as desired.


See also


Furor

<lang Furor>

      1. sysinclude dir.uh
      2. define SEPARATOR 9
  1. g argc 3 < { "." }{ 2 argv } sto mypath

@mypath 'd !istrue { ."The given directory doesn't exist! Exited.\n" end } 6 '- pidstr sto pidstring @mypath getdir { ."Cannot load the dir! Aborted.\n" end } sto mydir mypath per // If the end is not a '/' char, then add to... 10 mem sto aktfilename 10 mem sto neworiginalname tömb @mydir dirlist filtered tömb SEPARATOR [ #s 0 [-] "R" != 0 [-] "D" != #g & { dropline }{ 2 dropfield 2 dropfield } getline ] "/tmp/furordiredit_tempfile-" sto myfilename

  1. s

@pidstring sum myfilename ".txt" sum myfilename filtered @myfilename listtofile // print the list into a temporary file "EDITOR" env sto mycommand " " sum mycommand @myfilename sum mycommand @mycommand shell // execute the command editedlist @myfilename filetolist filtered SEPARATOR externalloop: ![ // Loop for the original list [-?] !{ dropline } // If empty lines occured...

  1. s

zero foundflag editedlist SEPARATOR ![ // Loop for the edited list // searching for the originaltype and originalnumber: 0 [-] 0 [-]§externalloop != { dropline } 1 [-] 1 [-]§externalloop != { dropline } 2 [-] 2 [-]§externalloop != { 2 [-] sbr §aktfilenamecreate neworiginalname @mypath = 2 [-]§externalloop sum neworiginalname @neworiginalname @aktfilename rename ."Renamed  : " @neworiginalname print ." ==> " @aktfilename printnl } one foundflag [>] ] // end for the editedlist-loop @foundflag !{ // No file or dir. In this case it must be deleted: 0 [-]§externalloop "D" == { // directory 2 [-] sbr §aktfilenamecreate @aktfilename rmdir ."Deleted  : " @aktfilename printnl dropline } 0 [-]§externalloop "R" == { // regular file 2 [-] sbr §aktfilenamecreate @aktfilename removefile ."Deleted  : " @aktfilename printnl dropline } }

] // End of the loop for the original list

@myfilename removefile end aktfilenamecreate: aktfilename @mypath = sum aktfilename rts

{ „mydir” } { „tömb” } { „mypath” } { „filtered” } { „pidstring” } { „myfilename” } { „mycommand” } { „editedlist” } { „foundflag” } { „aktfilename” } { „neworiginalname” }

</lang>

Phix

Should work on Windows and Linux. I have commented out all the actualy destructive statements. <lang Phix>-- demo/rosetta/vidir.exw string directory = ".",

      editor = iff(platform()=WINDOWS?"notepad":"gedit"),
      workfile = "vidir.txt"

-- filter = "" --bool recursive = false, -- overwrite = false, -- verbose = false -- ,... etc

procedure process_command_line()

   sequence cl = command_line()[3..$]
   if length(cl) then
       -- I assume you can figure out how to deal with eg
       -- {`-d`,`C:\Users\Pete\Documents`,`-e`,`notepad++`}
       ?{"command line arguments (ignored)",cl}
   end if

end procedure

process_command_line() sequence d = dir(directory) if d=-1 then

   crash("no such directory")

end if d = d[3..$] -- (drop "." and "..") integer fn = open(workfile,"w") for i=1 to length(d) do

   printf(fn,"%d: %s\n",{i,d[i][D_NAME]})

end for close(fn) {} = system_exec(editor&" "&workfile) object lines = get_text(workfile,GT_LF_STRIPPED) integer last = 0 if lines=-1 then

   crash("error reading edited file")

end if for i=1 to length(lines) do

   sequence r = scanf(lines[i],"%d: %s")
   if r={} then
       crash("error parsing line")
   end if
   Template:Integer line, string name = r
   for last=last+1 to line-1 do
       printf(1,"delete_file(%s)\n",{d[last][D_NAME]})
       if not file_exists(d[last][D_NAME]) then ?9/0 end if

-- if not delete_file(d[last][D_NAME]) then -- crash("error deleting file") -- end if

   end for
   string prev = d[line][D_NAME]
   if prev!=name then
       printf(1,"rename_file(%s,%s)\n",{prev,name})
       if not file_exists(prev) then ?9/0 end if
       if file_exists(name) then ?9/0 end if

-- if name[2]=':' and name[1]!=prev[1] then -- if not move_file(prev,name) then -- crash(error moving file") -- end if -- elsif not rename_file(prev,name) then -- crash("error renaming file") -- end if

   end if
   last = line

end for ?"done" {} = wait_key()</lang>

Raku

Works with: Rakudo version 2020.05

This is a fairly faithful recreation of the vidir utility with some extra "enhancements". It is set up to work seamlessly with Linux, OSX and most unix-like operating systems. Probably could be adapted for Windows too but won't work there as is.

Takes several positional and/or named command line parameters:


Positionals:

  • path, string, optional, defaults to the present directory (relative './').
  • filter, string, optional, defaults to none. A regex assertion you can use to filter the returned file names. E.G. \.txt$ (filenames ending with .txt extension)


Named:

  • -r or --recurse, flag, optional, default False. Recurse into nested directories and process those files as well.
  • -v or --verbose, flag, optional, default False. Be chatty about what is going on when making changes.
  • --e=whatever or --editor=whatever, string, optional, defaults the default text editor. Pass in a command name to specify a specific editor: (E.G. --editor=vim)


Will get a list of all file names from the specified director(y|ies), put them in a temporary file and open the text file with the default (or specified) text editor.

Edit the file names or directory names in the text editor: add, remove, modify file names, then save the text file; all of the modifications made to the text file will be applied to the file system.

To edit a file name: Just edit the file name, extension, whatever. Warning! There is nothing preventing you from using characters in the file name which will make it difficult to open, modify or delete the file. You can screw yourself quite easily if you make an effort. With great power comes great responsibility. The foot cannon is primed and loaded.

If you add a forward solidus (directory separator: /) to a filename, it will treat anything between separators as a directory name and WILL CREATE non-existent directories.

To move a file: Edit the directory path. The file will be moved to the new path. Non-existing directories will be created.

To delete a file: Delete the entire line (file name and identifier) in the text file.

To add a file: Add a new line with a unique integer (need not be in any order), one or more tabs, then the new file path/name. May be a relative or absolute path.

Notice that all of the above operations will fail to apply if you lack sufficient permissions for the affected files or directories.

<lang perl6>use Sort::Naturally; use File::Temp;

my %*SUB-MAIN-OPTS = :named-anywhere;

unit sub MAIN (

   Str $path         = '.',    #= default $path
   Str $filter       = ,     #= default file filter
   Bool :r(:$recurse)= False,  #= recursion flag
   Bool :v(:$verbose)= False,  #= verbose mode
   Str :e(:$editor)  = $*DISTRO ~~ /'Darwin'/ ?? "open" !! "xdg-open"; #= default editor

);

my $dir = $path;

  1. fix up path if necessary

$dir ~= '/' unless $dir.substr(*-1) eq '/';

  1. check that path is reachable

die "Can not find directory $dir" unless $dir.IO.d;


my @files;

  1. get files from that path

getdir( $dir, $filter );

@files.= sort( &naturally );

  1. set up a temp file and file handle

my ($filename, $filehandle) = tempfile :suffix('.vidir');

  1. write the filenames to the tempfile

@files.kv.map: { $filehandle.printf("%s\t%s\n", $^k, $^v) };

  1. flush the buffer to make sure all of the filenames have been written

$filehandle.flush;

  1. editor command

my $command = "$editor $filename";

  1. start text editor; suppress STDERR, some editors complain about open files being deleted

shell("$command 2> /dev/null");

react {

   # watch for file changes
   whenever IO::Notification.watch-path($filename) {
       # allow a short interval for the file to finish writing
       sleep .1;
       # read in changed file
       my %changes = $filename.IO.lines.map( { my ($k, $v) = .split(/\t+/); "{$k.trim}" => $v} );
       # walk the filenames and make the desired changes
       for ^@files -> $k {
           if %changes{"$k"}:exists {
               # name has changed, rename the file on disk
               if (%changes{"$k"}) ne @files[$k] {
                   # check to see that the desired directory exists
                   checkdir %changes{"$k"};
                   # notify and do it
                   say "Renaming: {@files[$k]} to " ~ %changes{"$k"} if $verbose;
                   rename @files[$k], %changes{"$k"} orelse .die;
               }
               %changes{"$k"}:delete;
           }
           else {
               # name is gone, delete the file
               # notify and do it
               say "Deleting: {@files[$k]}" if $verbose;
               @files[$k].unlink orelse .die;
           }
       }
       for %changes.kv -> $k, $v {
           # a new name is added, add an empty file with that name
           # check to see that the desired directory exists
           checkdir $v;
           # notify and do it
           say "Adding: $v" if $verbose;
           shell("touch $v") orelse .die;
       }
       # clean up when done
       done;
       exit;
   };
   # watch for CTRL-C, cleanup and exit
   whenever signal(SIGINT) {
       print "\b\b";
       done;
       exit;
   }

}

  1. get the files in a specified directory matching the filter parameter

sub files ( $dir, $filter = ) {

   if $filter.chars {
       $dir.IO.dir.grep( *.f ).grep( *.basename.contains(/<$filter>/) );
   } else {
       $dir.IO.dir.grep( *.f );
   }

}

  1. get the files in the present directory and recurse if desired

sub getdir ($dir, $filter) {

   if $recurse {
       @files.append: files($dir, $filter);
       getdir( $_, $filter ) for $dir.IO.dir.grep( *.d );
   } else {
       @files = files($dir, $filter);
   }

}

  1. check for existence of a directory and create it if not found

sub checkdir ($dir) {

   unless $dir.IO.dirname.IO.e {
       # if not, create it
       my @path = $dir.IO.dirname.split('/');
       for 1 .. @path {
           my $thispath = @path[^$_].join('/');
           unless $thispath.IO.e {
               say "Creating new directory $thispath" if $verbose;
               mkdir($thispath);
           }
       }
   }

}</lang>