I'm trying to clean up a set of directories after some tests, and do so in a way that's independent of the machine (and direct). I was using File::Directory::Tree but that wasn't working, throwing out weird errors. Instead, I tried:
method rm-cache() {
if $*SPEC ~~ IO::Spec::Win32 {
my $win-path = "$*CWD/$!path".trans( ["/"] => ["\\"] );
shell "del $win-path\.lock";
shell "rmdir /S /Q $win-path" ;
shell "rmdir $win-path";
} else {
shell "rm -rf $!path";
}
}
The Linux version works. The windows version, to which I came after 3 hours of tests, initially gave some error about an existing lock, which I'm deleting in the first shell sentence, without success, since it can't be removed.
Where's the lock created and how can we get rid of it programmatically.
There's better, and more Perl6y way, of doing that.
Related
I had a hard time with Windows 7 OS to where it would think that certain folders are in usage (not allowing folder delete), when in fact they aren't.
After alot of research and trial and error, I was able to use a command that worked well on Windows 7:
rmdir /S /Q "S:\Allied MTRS\Not Scanned\FITTINGS AND FLANGES\RG AR 2686 MOVED FOR AUTO INDEXING"
When I try to run this programmatically via shell command (see code below), it gives me: "File Not Found" message.
So if you try to run it programmatically first, it won't work. Then try to run the same thing, via command line, it works fine. Of course, if you try to run it grammatically again, after that, it will give you "File Not Found" (naturally, since the folder is already deleted). If you want to retry the experiment, you have to try on another folder....
Any ideas?
Sub tryitz()
Dim s As String
Dim ReturnCode As Integer
s = "S:\Allied MTRS\Not Scanned\FITTINGS AND FLANGES\RG AR 2686 MOVED FOR AUTO INDEXING"
s = "rmdir /S /Q " + Chr(34) + Trim(s) + Chr(34)
ReturnCode = Shell(s)
End Sub
Here is a possible answer, but I am looking for something better.
But hey, if not, at least this works, just a little bit more labor in terms of writing a bit of code...
Create a batch file
DelFile.Bat, say.
The DelFile.Bat is edited by my program (programmatically).
I edit it so that it has my desired "rmdir /S /Q? statement.
Then, I run it through shell.
I am looking to set up a script to do the following:
1st: SCP a directory on the first day of month to another server
2nd: Delete the directory after successful transfer
The directory I need to move will always have a different name, and the lowest numbered one is always the one that needs to move:
2018/files/02/
2018/files/03/
So what im looking to write up is something like:
scp /2018/files/% user#host:/backups/2018/files/
{where % = lowest num} &&
rm -rf /2018/files/%
{where % = lowest num} &&
exit
Thanks for any advice
If you are open to using Ruby, you could accomplish it with something like this:
def file_number(filespec)
filespect.split('/').last.to_i
end
directories = Dir['/2018/files'].select { |f| File.directory?(f) }
sorted_dirs = directories.sort_by do |dir1, dir2|
file_number(dir1) <=> file_number(dir1)
end
dir_to_copy = sorted_dirs.first
destination_dir = File.join('/', 'backups', dir_to_copy)
`scp #{dir_to_copy} user#host:#{destination_dir}`
`rm -rf #{dir_to_copy}`
I have not tested this, but if you have any problems, let me know what they are and I can work through it with you.
While using shell scripting eliminates the need for the Ruby interpreter, to me the code is not nearly as straightforward.
In very large directory lists (maybe 10,000's?) the sort might be intolerably slow, and another method would be needed to optimize for speed.
I would caution you against doing an unconditional rm -rf after the backup -- that seems really risky to me.
The big challenge here is to actually find the right files to copy, and shudder, delete. So let us call that step 0.
Let's start with some boiler plate
sourceD=/2018/files/
targetD=/backups/2018/files/
And a little assertion, which bails out from the script if $1 does not equate to a directory.
assert_directory() { (cd ${1:?directory name}) || exit; }
step 0: Identify directory:
assert_directory $sourceD
to_be_archived=$(
# source must be two characters, hence "??"
# source must a directory, hence trailing "/"
# set -- sorts its arguments
# First match must be our source
set -- $sourceD/??/ &&
assert_directory "$1"
echo ${1:?nothing found}
) || exit
This is only a couple of lines of condensed code. Note that this may
cause trouble if you (accidentally) run this multiple times in a row.
Step 1, Copy files now appears to be the easy part.
scp -r ${to_be_archived:?} user#host:${targetD:?}
This is a simple method for copying files, but also slow and risky.
Lookup rsync over ssh for alternatives.
Step 2, Remove
The rm -fr line will do the job, but I won't include that here.
We are missing an essential step, as we need to make sure that our
files have arrived safely. Again, rsync has options for that.
In summary:
assert_directory() { (cd ${1:?directory name}) || exit; }
assert_directory $sourceD
to_be_archived=$(
set -- $sourceD/??/ &&
assert_directory "$1"
echo ${1:?nothing found}
) || exit
This will give you the first two-character name directory (if one exists) in sourceD or abort the running script. It will break if $sourceD contains spaces.
I'm not sure why this isn't working, but perhaps I've oversimplified/overcomplicated things
I'm writing a Perl script that ultimately needs to call an external program. The catch is, this program needs a modified version of the LD_LIBRARY_PATH environment variable, in order to find a couple of libraries which the vendor does not install in standard places.
OK, the environment is in %ENV, which can be rewritten, yes?
I thought if I changed LD_LIBRARY_PATH in the parent, it would affect the dynamic linking of the child.
So I have:
use Env qw(#LD_LIBRARY_PATH);
use IPC::System::Simple qw(capturex $EXITVAL);
# We need these to establish the call to rsq later
my ($rsqexe, $rsqhome, $suffix) = fileparse($config->rsq());
push #LD_LIBRARY_PATH, $rsqhome;
eval {
$output = capturex(
$config->rsq(),
qq/"$source"/
);
};
But the child process dies with an error indicating the shared libraries can't be found.
How can I improve this?
I do need to examine the contents of $output after successful execution.
eval {
$output = capturex(
$config->rsq(),
qq/"$source"/
);
};
Here's the problem: I wasn't examining what was in $# (or $EVAL_ERR if you use ENGLISH;)
If I had, I would have seen that the problem was with the quoting qq/"$source"/ - because capturex() doesn't call the shell (which was the desired behaviour) the quotes break the file name (i.e. test.pdf exists, but ""test.pdf"" does not).
EDIT: So ultimately what I ended up doing here is running it straight in a mysql.expect script. Any variables that need to be updated would be replaced via sed in standard bash script used to launch mysql.expect.
I have a bash script that runs expect, and automates the MySQL installation process, as you can see here. The reason expect needs to be called in this script is to source local bash variables, so I can't just run it via expect, but rather it needs to be called it as follows:
if [ catch "spawn /bin/bash ./mysql.sh" error ] {
puts "Could not spawn mysql.sh $error"
}
I know this works, because there's another script I have called "test.sh" that does the following:
#!/bin/bash
source ./myvars.rc
echo "CONNECTED" >> "./out.html"
echo "$MYVARIABLE" >> "./out.html"
This works fine, the variable is correctly added to out.html. The mysql.sh script works when called directly, but not through expect, and there are no errors. Any ideas? Thanks.
I'm not an expect expert, but you may have a syntax error in the spawn command. This seems to work:
#!/usr/bin/expect
if { [ catch {spawn /bin/bash ./mysql.sh} error ] } {
puts "Could not spawn mysql.sh $error"
}
# This is the important part
interact
catch returns 0 (OK) if your command succeeds. You were seeing "success" because it really errored out, but you were testing the other condition.
So, I did some more testing and updated that a bit. You don't want the ! there, but you do want the interact. Once you spawn something with expect, you want to either use expect to process the output, or if there is none to process, just let the script run with interact.
The interesting thing is that if you use /bin/bash ./mysql.sh without interact, this will just do nothing but not actually run the script. If you use just ./mysql.sh, it will hang. I assume there is something with standard in/out that happens differently between the two.
Anyway, I hope this helps and actually solve your problem. Extra added stuff because I'm geeking out here -- you probably want exec instead of spawn if you don't want to interact with your script:
#!/usr/bin/expect
if { [ catch {puts [exec /bin/bash ./mysql.sh]} error ] } {
puts "Could not spawn mysql.sh $error"
}
The puts is there because otherwise you will lose the output of the script. If you don't care about the output, you can use:
#!/usr/bin/expect
if { [ catch {exec /bin/bash ./mysql.sh} error ] } {
puts "Could not spawn mysql.sh $error"
}
As part of a project, the Mono program has to write a series of images to a movie. Therefore the images are first cached in the /tmp/ folder/ since their is a possibility that their are still images of a previous session. I want te remove these images. Therefore I use the following commands:
Process proc = new Process();
proc.EnableRaisingEvents = false;
proc.StartInfo.FileName = "rm";
proc.StartInfo.Arguments = "/tmp/output*";
proc.Start();
proc.WaitForExit();
However when the program is executed I get the following warning: /bin/rm: cannot remove '/tmp/output*': No such file or directory..
However when I executed /bin/rm /tmp/output* in the terminal (in user mode), the command doesn't seem to have a problem recognizing the files.
Why does this command doesn't work?
Spawning an external process for this is terrible. Just use the standard System.IO APIs, for instance:
foreach (var file in Directory.EnumerateFiles ("/tmp", "output*")) {
try {
File.Delete (file);
} catch {
; // optionally report error
}
}
You may also use the overload that takes a SearchOption argument to recursively search in subdirectories. See http://msdn.microsoft.com/en-us/library/dd383571.aspx.
Because you should run the shell to expand the glob pattern /tmp/output* into a an ordered array of file paths.
You could run sh -c "rm /tmp/output*" as a Mono process, but that is ugly
But you don't need a shell. You could for instance use mono readdir to build an array (or a list) of file paths to remove, then remove them by calling a function doing the unlink(2) syscall (I leave you to find how to do that in Mono).