How to check if a file is open in editor?

Hi there! I'm developing a program that allows the user to open and edit files using both an editor and the terminal. Once the user has finished editing the file an update is sent to the logbook that compares the file before and after it was edited - this can only be done if the file is closed (I think) to guarantee the user is finished editing it. Currently I am checking if the file is closed like this:

while : 
            do
                if pgrep -x "gedit" > /dev/null; then ###**NOT RELIABLE NEED TO CHANGE**###
                        tput cup 2 5 ; echo -e "${RED}           Can not update log until editor is closed!           ${NC}"
                    tput cup 3 5 ; echo -e "${boldon}Please finish editing your file and close gedit/text editor.${boldoff}"
                    sleep 1
                else
                    clear
                    updateLog
                fi
            done

This method does work but it isn't very effective as the if statement checks if ANY file is open in gedit - not the particular one the user has opened which is what I want to do. I played around with PID's and was unable to find a solution.

Is there a better way of doing this?

Thanks in advance,
-cherryTango

Hi cherryTango,

Maybe you could try to looking for "inotifywait"

Bash script to check if a file is modified or not - Unix & Linux Stack Exchange

Hoping help you

If you run the program in foreground instead of background, your program can simply wait for it instead of polling.

If you run it in background, you can save its PID with PID="$!" and poll for that specific PID.

Did you consider using the -f option to pgrep and supplying the full command line includig the file name (within double quotes)?

You can try using fuser and see if that helps:-

fuser -v ./filename ./.filename.swp 

This might work if the editor in question is indeed holding open the file. Most times this is perhaps the case but if i remember correctly some editors create a temporary file, work on this tempfile and only finally overwrite the original with the changed copy destroying the temp. In this (admittedly - probaby quite rare -) case one wouldn't see that in fuser .

I suppose there is no 100% sure method for this and using fuser is IMHO as close as it gets. But if you already know which editor(s) you want/need to deal with you could put a PID-file or some other marker somewhere and hold other processing as long as the marker remains.

I hope this helps.

bakunin

Hi guys! Thanks for replying, I tried both:

xdg-open "$1"
PID="$!"
while : 
            do
                if ps -p $PID >/dev/null;
# Rest of code..

I also tried:

xdg-open "$1"
            while : 
            do
                if pgrep -f $currentPath/"$localFile" > /dev/null;
# Rest of code..

$currentPath=/tmp/Assignment1/Repository/Repository_Alpha
$localFile=example_file1.txt

Both solutions just ended the loop and called the updateLog function while the editor was open.

For the fuser solution not sure I understand what is meant by the

Any further information would be great! Thank you!

When you open a file using vim it creates a temporary .swp file, hence I included it.

But as Bakunin mentioned, fuser will not cover all file editors since some editors works differently.

Not quite clear what exactly you are doing there; please give more info. Using leafpad to open / edit a file:

pgrep -xf "leafpad file1"; echo $?
6200
0
pgrep -xf "leafpad file1"; echo $?
1

First is with open file, second after closing it.

So I've got a program that allows the user to edit a file either in gedit or using vim. I want a log to update only once the user has finished editing the file. Figured easiest way to do this would be only to update once the user had closed the editor. Been playing around with it for a while now - think I have found the best solution for what I want to do however I can't seem to get it to work.

My code:

gedit "$1" & PID="$!"
            # Do stuff while file is open
            wait $PID
            clear
            updateLog

(For the vim option the code is identical except 'vim' replaces 'gedit')

My logic is that this will guarantee that the process is terminated before updating the log.. however in practice it just immediately updates the log. Have absolutely no idea what I'm doing wrong! The $PID variable is being filled correctly as I've tested by printing it. Does the PID for a process change and this is why it's not working? Or is my wait command incorrect? When I put an

exit

command after the wait there are no errors in the terminal.

Have you tried just wait rather than wait $PID? Or how about not putting it into the background at all:

gedit "$1"
echo "Editing now finished"

A process ID does not change during the life of a process. But with a clear command in your code, any diagnostics messages that might be printed to give you an indication of what went wrong will likely be erased before you get a chance to see them.

How is it that you expect to edit a file when your editor is running in the background and can't read editing instructions from the terminal?

1 Like

Hi again, still struggling with this. All the solutions I see online seem to be working for everyone else but not me! They always return that the process is active initially and then return that it is not - even if the editor is still open. For example:

gedit "$1" & PID="$!"
            echo "$PID"
            
                me="$(basename $0)($$):"
                if [ -z "$PID" ]
                then
                    echo "$me a PID is required as an argument" >&2
                    exit 2
                fi

                name=$(ps -p $PID -o comm=)
                if [ $? -eq 0 ]
                then
                    echo "$me waiting for PID $PID to finish ($name)"
                    while ps -p $PID > /dev/null; do echo "Running"; sleep 1; done;
                else
                    echo "$me failed to find process with PID $PID" >&2
                    exit 1
                fi
            echo "Finished."

Will return:

20946
ex_03.sh(20854): waiting for PID 20946 to finish (gedit)
Running
Finished.

Another example is:

gedit "$1" & PID="$!"
            echo "$PID"
            (while kill -0 $PID; do echo "Running"; sleep 1; done) && echo "Finished."

Will return:

21078
Running
./ex_03.sh: line 446: kill: (21078) - No such process
Finished.

Finally:

gedit "$1" & PID="$!"
            echo "$PID"
            echo "Running"
            wait $PID
            echo "Finished."

Will return:

21190
Running
Finished.

In all cases I have not closed the editor (I don't even have time to before the script stops running). In the final example I attempted to give it some random PID that I just typed in to see if it returned an error message - which it did in several cases and it returned various different error messages - but not from the variable I give it. I am totally running out of ideas here. Is it possible that the PID changes?

---------- Post updated at 04:29 PM ---------- Previous update was at 04:10 PM ----------

Update: Also, when trying this:

gedit "$1" 
            PID=`ps -ef | grep ${USER} | grep "gedit $1" | grep -v grep | awk '{ print $2 }'`
            echo "$PID"
            while : 
            do
            sleep 1
                if [ -x /proc/$PID ]; then
                    echo "Running"
                else
                    echo "Finished."
                fi
            done

It returns:

Running
Running
Running
###Keeps running...

No PID and continues to run. When I try this:

gedit "$1" & PID="$!"
            echo "$PID"
            while : 
            do
            sleep 1
                if [ -x /proc/$PID ]; then
                    echo "Running"
                else
                    echo "Finished."
                fi
            done

It returns:

22729
Finished.
Finished.
Finished.
###Keeps saying 'finished'..

However this time it does get the PID. I've seen examples for the first scenario identical to mine where it does get the PID. Any further help would be so greatly appreciated!

Have you tried my suggestion of not putting gedit in the background at all?

Forget putting your code in a script!

What happens when you run the following commands directly in your shell:

gedit somefile& PID=$!
wait $PID

where somefile is the name of some file that you can edit? If this didn't work for you when run outside of a script, what makes you think it should work when you run it inside a script?

As I said in post #12 in this thread:

Jobs run in the background are blocked if they try to read from or write to their controlling terminal. The wait command that you are executing is not reporting that gedit terminated; it is reporting that gedit was stopped (probably by a SIGTTOU or SIGTTIN signal).

Maybe issuing the following command line in a shell would be instructive:

gedit somefile& PID=$!; sleep 10; jobs $PID; sleep 10; fg $PID
1 Like

I can't speak for gedit , but leafpad , be it sent to background or not, opens a new window with an editing session.

Sorry, I didn't really understand what you meant. When running those commands in the terminal this was the result:

adam@adam-VirtualBox:~/Systems$ gedit ex_03.sh & PID=$!
[1] 23022
adam@adam-VirtualBox:~/Systems$ wait $PID
[1]+  Done                    gedit ex_03.sh

So it works properly there? I'm totally new to this though so I don't understand why something like that wouldn't work in a .sh script?

---------- Post updated at 01:20 PM ---------- Previous update was at 01:17 PM ----------

I'm sorry the reason I've not done this is I don't know how? Any further help would be greatly appreciated.

The code I suggested?

gedit filename
echo "Editor has now quit"
1 Like
adam@adam-VirtualBox:~/Systems$ gedit Repository.sh 
adam@adam-VirtualBox:~/Systems$ echo "File closed."
File closed.

I didn't need to close the file or anything - whereas when I use a different system I normally have to CTRL+C or something to use the terminal once I've used it to open a file. Just noticed this now.

No one's mentioned lock files and race conditions yet. Go us!