How to catch ctrl+c and close file descriptor in Perl - linux

I have Perl script - simple server. Wait for connect in the loop, and write it to the new file on disc. The server should be stopped when ctrl + c are pressed. Before exit, I want to close all files descriptor. It's not so easy, because terminal shows error.
Yes, I read this thread:
How can I check if a filehandle is open in Perl?
But it's not working for me.
There is my main loop:
my $my_socket = new IO::Socket::INET(
LocalHost => $local_host,
LocalPort => $local_port,
Proto => 'tcp',
Listen => 5,
Reuse => 1
);
die "Couldn't create: my_socket $!n " unless $my_socket;
print "Send files.. \n";
while(1) {
my $accepter = $my_socket->accept();
my $count = 0;
#print "$directory.$save_dir/$my_data";
$datetime = localtime();
$datetime =~ s/(?<=\w)\s(?=\w)/_/g;
open my $fh, '>', "$direc/$save_dir/$datetime"
or die "Couldn't open the file";
while(<$accepter>){
chomp;
#last if $count++ ==10;
#print($accepter);
say $fh $_;
}
$SIG{'INT'} = sub {
print "Caught One!\n";
close $fh;
}; #It gives me error. Close fd which is not opened.
}
#print "Received. End \n";
#close $fh;
close $my_socket;

The code in the signal handler closes that filehandle -- and lets the loop continue. So the next time round that filehandle is indeed closed so you get warnings. (I'd expect issues first with printing to it on the next pass, if the problem is described well, but there may be reasons that that's avoided.)
The fix, in short -- set a flag in the signal handler, nothing else. Check for it at a suitable place in the code and if it is set then close the file and exit the loop by last.† (Or perform some other action, as suitable for your code.)
There's more I'd like to comment about the signal handler though. The %SIG is a very global creature. By setting $SIG{INT} you've changed it for all of the code in the interpreter.
Why not use local $SIG{INT} instead? Then it is changed only inside the scope in which it is defined; see local. And I'd pull that definition outside of all loops possible. (If you actually want it global place it right at the beginning so it's loud and clear and not hidden away.)
So something like
SOME_SCOPE:
{
my $got_signal;
local $SIG{INT} = sub {
#say "Caught: #_";
$got_signal = 1;
};
while (1) {
...
open my $fh, '>', ... or die $!;
while (<$accepter>) {
...
if ($got_signal) {
close $fh;
# $got_signal = 0; # if used again (reset it)
last;
}
say $fh $_;
}
...
}
};
This is still a sketch, even as it may work as it stands in a simpler case. I have to assume some things, in the first place that there is a lot more going on in your code. If a contained and complete runnable example can help let me know and I can add it.
† Can't do last in a signal handler's sub (it's not inside of a loop, even if it's defined there in the code). And in general, using last in a sub is not a good idea, to say the least, as it would lead to confusing and opaque code. It also sports very specific behavior
last cannot return a value from a block that typically returns a value, such as eval {}, sub {}, or do {}. It will perform its flow control behavior, which precludes any return value.
(it comes with a warning, too)   In this case you don't need to return a value but (even if it worked) you absolutely would not want to do that out of a signal handler.
To note, even as it is linked in the question, that one can check whether a filehandle is open using Scalar::Util::openhandle. That can come handy in this code.

If you want the server to truly exit when Ctrl-C is pressed, simply put an exit statement in the interrupt handler. That will automatically close all the file handles as Perl exits.
If you want the read loop to terminate when Ctrl-C is pressed but the program will otherwise carry on, do this. However might I suggest Ctrl-\ as a better alternative which sends a SIGQUIT signal. Most people don't expect graceful exits from Ctrl-C; they expect crash stop now.
our $doloop = 1;
$SIG{QUIT} = sub { $doloop = 0; };
while ($doloop) { ... }
$SIG{QUIT} = "IGNORE";
cleanup;
exit;
If you need to check if the filehandle is open before trying to close it, just use -e. For other kinds of open tests see perlfunc -X.
close $fh if -e $fh;
HTH

Related

Append text to the end of a line

I'm working with AutoIt, and I was wondering in there is a method I can use to append a string of text to the end of a line inside a text file. I've been browsing all over autoit forums and there are lots of answers that are really close, but I have not found a solution that has actually worked for me.
The function:
FileWriteLine($LOG, "FText")
just adds a whole new line at the bottom, while the function:
_FileWriteToLine($LOG, 1, "FText", 0)
adds the letters "FText" to the beginning of the first line in the log file.
Is there any way I can add this text to the end of the first line, instead of the beginning?
I have never come up with this problem but just thinking of it, how about reading the whole line, storing it in a variable, add the extra test you want in the end of the line and then write the new line as it is modified replacing the old line???
You can use the FileWrite function: FileWrite documentation
You can create your own file writing function to include the opening, writing, and closing of the file like this:
Func WriteToLog($FileName, $Value)
$FileHandle = FileOpen($FileName, 1) ; 1 = append mode
If $FileHandle <> -1 Then
FileWrite($FileHandle, $Value)
EndIf
FileClose($FileHandle)
EndFunc
Then using your example and assuming $LOG is the file name for your log file, you can simply call your function whenever you need to log something:
WriteToLog($LOG, "FText")

Perl OLE document visible

I try to open and modify an Excel document with a Perl script, and I want this document to be still open after. I use the following code :
unless (defined $ex) {
$ex = Win32::OLE->new('Excel.Application', sub{$_[0]->Quit;})
or die "Cannot start Excel";
}
$ex->{Visible} = 1;
The Excel document briefly appears, then vanishes. The work is correctly done, but Excel closes down after. Is there a way to keep Excel open?
Just remove your destructor construct from the new method:
$ex = Win32::OLE->new('Excel.Application')
or die "Cannot start Excel";

The process cannot access the file 'd:\1.doc' because it is being used by another process

my code :
object c = "d:\\1.doc";
if(File.Exists(c.ToString()))
{
File.Delete(c.ToString());
}
error :
The process cannot access the file 'd:\1.doc' because it is being used
by another process.
How close ? with code
first of all use string instead of object, so:
string c = "d:\\1.doc";
now as the message indicated the file being used by another process. either by windows process, or you are opening the file stream and forget to close it. check in your code where you are interacting with the file.
Edit: Since you are using Microsoft.Office.Interop.Word make sure you close the file(s) open first like:
Word.ApplicationClass word = new Word.ApplicationClass();
//after using it:
if (word.Documents.Count > 0)
{
word.Documents.Close(...);
}
((Word._Application)word.Application).Quit(..);
word.Quit(..);
I had the same type of issue when I wanted to Delete File after Open/Read it using Microsoft.Office.Interop.Word and I needed to close my document and the application like that :
private void parseFile(string filePath)
{
// Open a doc file.
Microsoft.Office.Interop.Word.Application application = new Microsoft.Office.Interop.Word.Application();
Document document = application.Documents.Open(filePath);
// Loop through all words in the document.
int count = document.Words.Count;
for (int i = 1; i <= count; i++)
{
// Write the word.
string text = document.Words[i].Text;
Console.WriteLine("Word {0} = {1}", i, text);
}
// Close document correctly
((_Document)document).Close();
((_Application)application).Quit();
}
You have that file actively open in this or another program, and Windows prevents you from deleting it in that case.
Check if the file still running (opened) by another application
1- Microsoft Word
2- WordPad

Error on Excel importing

I am trying to import around 1500 Excel files to my system.
The code is working in a loop and I am able open and import around 600 Excel files. After that I am getting an error message like: Error calling external object function open at line 55.....
I really stuck with this issue, if anyone can help that will be grateful.
Code posted in reply comments:
For ll_LoopCnt = 1 To Dw_1.rowcount( )
Ls_File_Name = Dw_1.getitemstring( ll_LoopCnt, "file_name")
Ls_Path =Dw_1.getitemstring( ll_LoopCnt, "file_path")
ll_Sr_No= Dw_1.getitemNumber( ll_LoopCnt, "sr_no")
ldt_File_Date= Dw_1.getitemDateTime( ll_LoopCnt, "file_date")
Excel.Application.DisplayAlerts = "False"
Excel.WorkBooks.Open( Ls_Path )
Excel.Application.Visible = False
Excel.windowstate = 2 // 1 : Normal, 2 : Minimize, 3 : Maximize
Excel.Application.CutCopyMode = False
Lb_sheet_rtn = excel.worksheets(7).Activate
Ls_ClipBoard = clipboard()
Excel.Application.ActiveWorkbook.Save()
Excel.Worksheets(7).UsedRange.Copy
ll_cnt = ds_1.importclipboard()
IF ll_cnt <= 1 THEN
Messagebox("Error", "Could not find.")
Else
Dw_1.Scrolltorow( ll_LoopCnt )
Dw_1.SetItem( ll_LoopCnt, "status", 'Success')
For ll_Inner_LoopCnt = 1 To Ds_1.RowCount( )
Ds_1.Object.file_path[ll_Inner_LoopCnt] = Ls_Path
Ds_1.Object.file_name[ll_Inner_LoopCnt] = Ls_File_Name
Ds_1.Object.file_sr_no[ll_Inner_LoopCnt] = ll_Sr_No
Ds_1.Object.file_date[ll_Inner_LoopCnt] = ldt_File_Date
Next
END IF
Clipboard(ls_ClipBoard)
Ds_1.Reset( ) //Reset the data store
Excel.Application.ActiveWorkbook.Save()
Excel.Application.ActiveWorkbook.Close(False);
Excel.Application.Quit
Excel.Application.CutCopyMode = False
IF ll_LoopCnt = ll_Excel_Cnt Then //--->> After 100 files reset the memmory
ll_Excel_Cnt = ll_LoopCnt + 100
Excel.DisConnectObject()
DESTROY excel
DESTROY TEst_Excel
GarbageCollect ( )
Excel = Create OLEObject
Test_Excel = Create OLEObject
Li_rtn = excel.ConnectToNewObject("excel.application")
IF li_rtn <> 0 THEN
MessageBox('Excel error','can not run Excel Program')
DESTROY Excel
RETURN 0
END IF
End IF
Next
Excel.displayalerts = False
Excel.Application.Quit
Excel.displayalerts = True
Excel.DisConnectObject()
DESTROY Excel
DESTROY Test_Excel /* This is the code i written i dont think the OLE is crashing i think the connnectto the OLE is getting lost after some time, but stile its going fine for almost 600 records.. */
Seeing the line of code would help, but this error message typically (in the context of OLE, which I'm guessing is the case here) comes from PowerBuilder making an OLE call which the OLE host rejects. From the information you've supplied, it's impossible to tell if the OLE host has crashed and isn't responding anymore, or if you've got the OLE host into a state where these functions are no longer applicable, or if the OLE object has become invalid, or what.
If it were me, and it was happening consistently, I'd run the app in the debugger to get to the state where the error is about to happen (you can set advanced attributes in breakpoints to not have a breakpoint activate every time it is passed) and try interrogating the OLE objects. I'd expect you'd also have to throw in some test code, since I'm not confident everything you'd want to test would be available to the debugger.
New Feb 21
I'd also change the set of files being processed, so that I could tell if the key to the crash is a specific file, or the quantity of files processed. For example, if you get rid of the first 100 files, does it still crash on the 600th file (same quantity) or the 500th file (same file)?
One possibility is that you're running out of memory. Each "dot" in an OLE reference (attribute access, method call) creates an object in memory that isn't destroyed until the garbage collect. The code clip you've posted will never enter the block where the GarbageCollect() is called (ll_Excel_Cnt is never initialized), so you might want to make sure that part is working. I'd also get rid of unnecessary calls. For example, you've got several calls that maintain the state of Excel within the loop (e.g. Excel.Application.Visible), when they only need to be called once. I'm also not clear from the code clip why you'd need to call a Save(), either time; this could be expendable as well. This clean up should also make your code run faster.
Good luck,
Terry
Around line 30 you have
Excel.Application.ActiveWorkbook.Save()
Excel.Application.ActiveWorkbook.Close(False);
Excel.Application.Quit
You shouldn't call Excel.Application.Quit there. Also, I always recommend to put anything that uses OLE inside a Try..Catch block and catch OleRuntimeError and RuntimeError.

HOW TO close Excel instance started by mail merge

HOW TO close Excel instance started by mail merge
this code running inside launcher does not have access to Excel running via DDE ??
'For i = 1 To Workbooks.Count
' MsgBox ("here" + Workbooks(i).Name)
'If (Workbooks(i).Name <> ActiveWorkbook.Name) Then
'Workbooks(i).Close
'End If
'Next i
You can kill the excel process like so: (from http://www.dreamincode.net/code/snippet1543.htm)
//Namespaces needed
using System.Diagnostics;
public bool FindAndKillProcess(string name)
{
//here we're going to get a list of all running processes on
//the computer
foreach (Process clsProcess in Process.GetProcesses()) {
//now we're going to see if any of the running processes
//match the currently running processes by using the StartsWith Method,
//this prevents us from incluing the .EXE for the process we're looking for.
//. Be sure to not
//add the .exe to the name you provide, i.e: NOTEPAD,
//not NOTEPAD.EXE or false is always returned even if
//notepad is running
if (clsProcess.ProcessName.StartsWith(name))
{
//since we found the proccess we now need to use the
//Kill Method to kill the process. Remember, if you have
//the process running more than once, say IE open 4
//times the loop thr way it is now will close all 4,
//if you want it to just close the first one it finds
//then add a return; after the Kill
clsProcess.Kill();
//process killed, return true
return true;
}
}
//process not found, return false
return false;
}
Excel can be closed via VBA, if called from within Excel
Application.Quit
If called from outside Excel, you will need to set a reference to Excel and then close it.
Set appExcel = GetObject(, "Excel.Application")
appExcel.Quit
You need to ensure that all Workbooks are closed or saved, otherwise Excel will prompt the user to save.

Resources