Running emacs from the command line and handling locked files - emacs

I'm using org-mode and am looking to export my agenda (example of an agenda) to a text file so that I can display it with conky. The org-mode manual features this command line example to do this:
emacs -batch -l ~/.emacs -eval '(org-batch-agenda "t")' | lpr
I've modified this like so:
emacs -batch -l ~/.emacs -eval '(org-batch-agenda "e")' \
> ~/org/aux/agenda-export.txt
I have this set as a cron job to run every 5 minutes. It works great unless I actually have emacs open. Then I noticed that the file agenda-export.txt was empty. In running this manually from the coammand line vs. through cron, I get this error (or similar depending on the files I have open):
...~/org/file.org locked by jwhendy (pid 10935): (s, q, p, ?)?
I was going to write a script to perhaps export to agenda-export-test.txt, then perhaps check for an empty file or no lines (wc -l == 0 maybe?). If true, leave the existing file alone and delete agenda-export-test.txt. If not, then move agenda-export-test.txt to agenda-export.txt.
But... when I try to run such a script, I'm met with the same emacs inquiry about whether to steal the lock, proceed, or quit. I'm fine with proceeding, as I don't think org-agenda does anything to the files and thus it wouldn't harm anything... but I don't know how to tell emacs to "force" or choose "proceed" if problems are encountered. I need something non-interactive.
So, to summarize, my thoughts were to try:
passing a --force option or similar to emacs to make it proceed at the pause
see if the exported tmp file has any lines and deal with it accordingly
tell emacs to run in "read only mode" (but I don't think it exists)
The main issue is that with cron, I'm not there to tell the process what to do, and so it just just make an empty file as the exported results. How can I handle this locked file business with a "blind" process like cron that can't respond?
I've tried asking the mailing list as well without a working outcome. [1] I wondered if someone here might have ideas.
[1] http://www.mail-archive.com/emacs-orgmode#gnu.org/msg45056.html

Have you tried copying file without using emacs?
put in your crontab:
cp ~/org/file.org /tmp/export.org && emacs -batch .... /tmp/export.org ..
A regular "cp" command should not copy emacs locks. Of course, once in a while you might get a damaged file if you save agenda just during cp command, but this should not be too bad.

Related

Editing WSL2 instance of Ubuntu Crontab using Windows VSCode

My question is whether it is possible to edit the crontab of a WSL2-based instance of Ubuntu with my Windows VSCode that is connected via WSL remote SSH.
If I type export EDITOR=code inside my WSL instance and then crontab -e, I am able to see a /tmp/crontab.sygFAU file load inside my VSCode instance.
The problem is that once I make edits to this file, it will save the file to /tmp/crontab.sysFAU but it doesn't actually take the next step of replacing the the real crontab file in /var/spool/cron/crontabs.
So once I re-open the crontab, it will just show what I had previously, and not my saved edits.
It would be nice to know if this is not possible or if there are any alternative ways to run a GUI editor because using nano is a pain!
An interesting question that I haven't considered before, myself. Here's what's happening:
You set your editor to code
You crontab -e, which properly loads VSCode with the temporary crontab.
However, because this is a Windows GUI application, it returns control to the parent Linux application (crontab) immediately after starting VSCode. You can see the same result if you just start notepad.exe from your shell in WSL -- Once Notepad starts (rather than exits) control is returned to the shell.
If you switch back to your terminal at this point, you'll see that crontab detected that the editor that it launched exited (returned), and so it has already tried to copy the temporary file to the permanent location.
However, since the temporary files doesn't yet have any changes, crontab decides there's nothing to do.
Editing the file in VSCode and saving it has no effect, other than to leave a dangling /tmp/... file hanging around (since crontab isn't there to clean up).
So what's the solution? We need a way to launch a Windows GUI application and prevent it from returning control to crontab until you are done editing.
I originally thought something from this question might work, but the problem is that the actual command that launches the Windows process is embedded in a shell script, which you can see with less "$(which code)" (or code "$(which code)"), but it's probably not a great idea to edit this.
So the next-best thing I came up with is a simple "wrapper" script around the (already-a-wrapper) code command. Create ~/.local/bin/code_no_fork.sh (could be anywhere) with:
#!/usr/bin/env bash
code $* > /dev/null
echo Press Spacebar to continue
read -r -s -d ' '
Credit: This answer for the Spacebar approach
Then:
EDITOR=~/.local/bin/code_no_fork crontab -e
After you make your edits in VSCode, simply press Space to allow the script to continue/exit, at which point crontab will (assuming no errors were detected) install the new Crontab.
Alternatives
This is should typically only be a problem with Windows GUI applications, so the other possible avenue is to simply use any Linux editor that doesn't fork. If you want a GUI editor, that's entirely possible as long as you are running a WSL release that includes WSLg support (now available for Windows 10 and 11).
I won't offer any individual editor suggestions since that would get into "opinion" and "software recommendation" territory, which is off-topic here.

emacs to automatically rsync?

This was discussed in a emacs StackExchange Discussion where Howard Abrams and gavinb mentioned something that could work. However, I am such a newbie to emacs, and not a programmer at all, that I have no idea of how to get his code to work in my emacs editor. I have started to use org-mode and have used the "easy customization" mode to change things in "[-]-\ Group Org", but I don't know enough to know what to do with it to implement a complete solution.
I assume that the gist code of Mr. Abrams works to run one script named ".on-save" that lives in the same directory as the file that is being saved. And that this code runs the .on-save script and passes the saved file's name to that script to process. At that point I get lost in how to get a rsync script to process the saved file's name.
Would I put the code that Mr. Abrams mentions in my ~.emacs file?
Alternatively do I use "easy customization" to run Mr. Abrams code?
If I managed to run/install the code, how would my rsync script look to make use of his code to pass the file name of the file being saved to the rsync script for processing.
Thanks.
Aaron
So I bound Alt-F11 to trigger a shell script ".rsync-org-mode.sh".
In my .emacs file I put the following:
(defvar script-name "~/.rsync-org-mode.sh")
(defun call-my-script-with-word ()
(interactive)
(shell-command
(concat script-name
" "
(thing-at-point 'word))))
(global-set-key (kbd "M-<f11>") 'call-my-script-with-word)
The shell script:
#!/bin/bash
rsync -avh --delete ~/My-Org-Directory/ /My-Backup-Org-Directory/
I saved the script as ~/.rsync-org-mode.sh and made it executable with chmod+ It rsync's my entire org-mode directory.
To use it I periodically save to backup using Alt-F11. I did not want it to be automatic for fear that it would automatically sync a bad copy.
Very very low tech, but it is adequate for my needs, and it will sync the directories. Lawlist's solution in the post he referenced is great, and more comprehensive, but since I fuse mount my only sftp directory prior to each emacs session, it was more than I really needed.

emacs --daemon with --batch and input file

I would like to create a script that simply cleans up the whitespace and tabs on several files in a folder for me. I have created a bash file with among other things:
emacsclient -t -e '(progn (prelude-cleanup-buffer-or-region) (save-buffer-kill-terminal))' $FILE
Now this doesn't seem to work as it interprets ALL the file arguments as functions to be run (so $FILE is executed as a function). (P.S. prelude-cleanup-buffer-or-region is from here)
Now what I really want appears to be --batch described here (since I don't actually want to display anything on the screen) but this isn't one of the options of emacsclient. The reason I want to use emacsclient rather than just using emacs --batch is that I have a lot of startup files so want all of this to stay loaded otherwise my script would take too long.
Does anyone have any advice on how to go about this?
Thanks in advance.
emacsclient -e means evaluate lisp forms, do not edit files
from the man page
-e, --eval
do not visit files but instead evaluate the arguments as Emacs
Lisp expressions.
I guess you could add a (find-file "file") to your list of forms to execute
I just tried this snippet -
/opt/local/bin/emacsclient -e '(progn (find-file "./tmpfoo")
(end-of-buffer) (insert "ffff") (save-buffer))'
and it edits the file silently like you'd expect.
you could use shell globbing and a script to expand an argument filename into the list of forms.
do not run with the -t switch either, -e doesn't expect to have a persistent editor window, and you don't need the kill-terminal. The client will just run your elisp and exit.
I think I would probably write a lisp function that took a filename argument, that I loaded into emacs at startup time, and then just call that with a filename via emacsclient,
e.g. FILENAME="somefile"; emacsclient -e "(now-do-my-thing $FILENAME)"

start emacsclient within emacs (like magit does)

I am looking for ways to start emacs within shell-mode, and I want the client to connect to the enclosing emacs instance so I don't need to switch to the instance running the server to edit the file.
I sense it is doable because magit is already doing something like this: the commit message is edited by emacsclient, and always opened in the enclosing instance. I don't even need to start the server explicitly!
My primary usage is to do git commit from shell-mode. I can't rely on magit because I have to use some customized git version with special command. Anyway I think it would be cool thing to do.
UPDATE:
I found the magit function magit-with-emacsclient that does the trick here:
https://github.com/magit/magit/blob/master/magit.el#L1979
Any help to turn the function into a shell script that can act as the default editor of git will be greatly appreciated! I will also try and post the code if I can make it work.
UPDATE 2:
Here is my solution. It requires start the server beforehand with pid in the socket name and then set your $EDITOR to be emacsclient connecting to that server.
Put this in your init.el
; start a server with a pid
(require 'server)
(defun server-start-pid ()
(interactive)
(when (server-running-p server-name)
(setq server-name (format "server%s" (emacs-pid)))
(when (server-running-p server-name)
(server-force-delete server-name)))
(server-start))
and run server-start-pid before you launch the editor in your shell-mode.
put this script emacsclient-pid in your PATH. It recursively find the parent process until it finds the emacs instance and invoke emacsclient on the right socket:
#! /usr/bin/python
import os, sys
import psutil
def get_pid(name):
pid = os.getppid()
while True:
proc = psutil.Process(pid)
if name in proc.name():
break
pid = proc.ppid()
return pid
if __name__ == '__main__':
pid = get_pid("emacs")
# pass the argument to emacsclient
args = ' '.join(sys.argv[1:])
cmd = "emacsclient -s server{pid} {args}".format(**globals())
print cmd
os.system(cmd)
And put this in your .bashrc
if [[ $EMACS"x" == tx || $TERM"x" == dumbx ]]; then
export PAGER=/bin/cat
export EDITOR='emacsclient-pid'
else
export PAGER=/usr/bin/less
export EDITOR='emacs -q -nw'
fi
I have split the code responsible for client-server interaction into a separate library with-editor. It's part of git-modes' next branch. Magit will start using that once I merge its next branch into master. You should study that instead of the old implementation in magit itself, because it puts all things related to this in one place and because it is also improved in many ways. E.g. it also works when using tramp.
with-editors can be used by other packages besides magit. That's one of the reasons why I have split it into a separate library/package. But I have not yet implemented the parts not actually needed by magit. E.g. the functionality it provides could also be used with shell-command, see https://github.com/magit/git-modes/pull/96.
Something similar would probably work for shell-mode too. From a very quick look I think the function that would need to be advices in this case is comint-exec. I will probably not work on this soon, but eventually I will get back to this. Meanwhile have a look at that library yourself. Understanding what it does won't be easy without good knowledge of elisp and how emacs handles child processes, though. The only advice I can give you here is to also read the issues that concern with-editor in the magit/git-modes repository on github.
Ps. Translating this to a shell script won't work, because certain things have to happen in Emacs before the child process is started, like starting the server.

Emacsclient called by applescript can't find emacs server socket

The shell command
emacsclient -n -e '(make-remember-frame)'
works.
But the applescript
do shell script "emacsclient -n -e '(make-remember-frame)'"
just returns
emacsclient: can't find socket; have you started the server?
To start the server in Emacs, type \"M-x server-start\".
emacsclient: No socket or alternate editor. Please use:
--socket-name
--server-file (or environment variable EMACS_SERVER_FILE)
--alternate-editor (or environment variable ALTERNATE_EDITOR)
I rarely use this, but it has worked successfully in the past for various purposes. Perhaps you can modify it to suit your needs. The init.el or .emacs file must have (server-start) inside in order to make everything work. I have lots of stuff that loads when Emacs is activated for the first time, so I need a 5 second delay before emacsclient is called -- you can adjust the delay downward if your Emacs loads faster. If Emacs is already running, there is no need for a delay. You can comment out the verbal messages generated by say -- I used them this morning to test the conditions and make a minor adjustment to the script. The script contains a command-line example on line 4, which calls two Emacs functions. Of course, the path to your Emacs and emacsclient will need to be adjusted to wherever you have installed them on your computer.
# `(server-start)` must be inside `init.el` or `.emacs` file.
# This script can be used in the terimal: osascript path-to-script arguments
# Terminal Example:
# osascript /Users/HOME/.0.data/.0.emacs/.emacsclient.applescript "-e '(progn (dired \"/Applications\") (message \"Hello-World\!\"))'"
on run argv
set arg to item 1 of argv
set emacs to application "Emacs"
set appIsRunning to emacs is running
if appIsRunning then
say "Emacs is already running."
do shell script "/Users/HOME/.0.data/.0.emacs/Emacs_06_01_2014.app/Contents/MacOS/bin/emacsclient " & arg
else
tell application "/Users/HOME/.0.data/.0.emacs/Emacs_06_01_2014.app/Contents/MacOS/Emacs" to activate
say "Please wait five seconds for Emacs to load."
delay 5
do shell script "/Users/HOME/.0.data/.0.emacs/Emacs_06_01_2014.app/Contents/MacOS/bin/emacsclient " & arg
end if
end run