I use a Makefile to compile latex. In the end of the compile process I have the following rule
$(PDFREADER) $(SOURCE).pdf &
where it will open in evince the newly created pdf.
What I want is that next time I compile the pdf, the Makefile closes the current evince process, opened in during previous the compilation.
I read this and this posts, but I did not understand how to do it.
Any help?
Thanks
EDIT this is the makefile I use
SOURCE=PEI
PDFREADER=evince
EXTENSIONS=ps dvi log out toc aux bbl blg idx glg glo ist lof acn ilg ind ist lot xdy synctex.gz glsdefs
$(SOURCE).pdf: $(SOURCE).tex bibliografia.bib
if [ -a evince.PID ]; then \
kill -TERM $$(cat evince.PID) || true; \
fi;
pdflatex -shell-escape $(SOURCE).tex
bibtex $(SOURCE)
pdflatex -shell-escape $(SOURCE).tex
pdflatex -shell-escape $(SOURCE).tex
vis: $(SOURCE).pdf
$(PDFREADER) $(SOURCE).pdf & echo $$! > evince.PID
clean: organized
#rm -f $(SOURCE).pdf
organized:
#rm -f $(foreach postfix, $(EXTENSIONS), $(SOURCE).$(postfix))
#rm -f ./*~
#cd ./tex && rm -rf $(foreach postfix, $(EXTENSIONS), *.$(postfix)) && rm -f ./*~
Once you start your process you have to store the process id (pid) to a file, let me call it evince.PID. So change your line in:
$(PDFREADER) $(SOURCE).pdf & echo $$! > evince.PID
This saves the pid in the evince.PID file. To stop the previous process just read the pid from that file (if exists) and kill the corresponding process. To do this add to you makefile:
if [ -a evince.PID ]; then \
kill -TERM $$(cat evince.PID) || true; \
fi;
For future makefile/bash writing notice that:
$$! corresponds to the bash variable $!, as the makefile syntax also indicates variables with $ to disambiguate a bash variable we need a double $$
$$(cat evince.PID) reads the file into a bash variable, again marked with $$
kill -TERM returns a non 0 exit code if the process does not exist (e.g. you already killed it by hand). With || true a 0 exit code is returned even if kill failed, so a failed kill does not cause the makefile to fail
Edit:
My bad, I figured out that -a to check if a file exists is now deprecated and should be avoided in favor of -e. Debian Almquist shell (dash) seems to be a bit picky about this. To kill the process then use:
if [ -e evince.PID ]; then \
kill -TERM $$(cat evince.PID) || true; \
fi;
Related
I'm trying to find a good way to add some RAII-like actions into a makefile I'm maintaining. Currently, I have something similar to this:
out: in
lockfile in.lock
echo in // Some action which can fail
rm -f in.lock
This code works fine when using multiple jobs, as it is mainly meant sanity instead of performance. At least, if my action is not failing. So if I like to add a fallback to this. So in short, it'll look something like:
out: in
lockfile in.lock
(echo in) || (rm -f in.lock; false)
rm -f in.lock
Yet again looking good, though I don't like having to write twice rm -f in.lock, nor does the (echo in) looks elegant if the actual content is several lines of bash-script.
This would look similar to:
out: in
lockfile in.lock
trap "rm -f in.lock" EXIT; \
(echo in)
However, this would make the actual rules look more complex if you have different rules which are really separate.
out: in
lockfile in.lock
trap "rm -f in.lock" EXIT; \
$(SHOW_DEPENDENCY_ON_DEBUG) && \
(echo in)
Where SHOW_DEPENDENCY_ON_DEBUG can be defined as echo $# <=== $^ in certain circumstances and # in others. So I'm not that sure if I can nicely chain all commands. Therefore I hope any of you know of some tricks that I've missed.
In short, I like to transform:
out: in
lockfile in.lock
echo in // Some action(s) which can fail
rm -f in.lock
In a way that always executes rm -f in.lock, without having to chain bash-commands or duplicating the action(s) that have to be executed to finalize the actions in the rules.
For the problem of ensuring the your lockfile (or any file that make makes) is deleted come what may,
make has a stock solution: make it an .INTERMEDIATE target.
Then, if make creates the file, it will auto-delete it at the end, come what may, e.g.
Makefile
.PHONY: all clean
all: out
in:
touch $# # Whatever
.INTERMEDIATE: in.lock
%.lock: %
touch $# # Whatever
out: in.lock
if [ "`shuf -i 0-1 -n 1`" = "0" ]; then echo Fail; false ; else echo Succeed; touch $#; fi
rm -f $<
clean:
rm -f in out
Here the command:
if [ "`shuf -i 0-1 -n 1`" = "0" ]; then echo Fail; false ; else echo Succeed; touch $#; fi
will fail or succeed on a pseudo-random coin-toss.
Some runs:
$ make
touch in # Whatever
touch in.lock # Whatever
if [ "`shuf -i 0-1 -n 1`" = "0" ]; then echo Fail; false ; else echo Succeed; touch out; fi
Succeed
rm -f in.lock
$ make clean
rm -f in out
$ make
touch in # Whatever
touch in.lock # Whatever
if [ "`shuf -i 0-1 -n 1`" = "0" ]; then echo Fail; false ; else echo Succeed; touch out; fi
Fail
Makefile:14: recipe for target 'out' failed
make: *** [out] Error 1
rm in.lock
But don't push this feature so far as removing the
rm -f $<
from the recipe. make will delete the intermediates at exit, which is fine if the recipe fails.
But if the recipe succeeds you presumably want the lockfile deleted right away rather than when
make finishes, which might be arbitrarily later.
Later
Any chance the .INTERMEDIATE can refer to wildcard, like %.lock?
No. You'd have to mean:
.INTERMEDIATE: %.lock
and there is no "wildcard" there. With no % in lefthand side,
it's not a pattern-rule and % in the righthand side only just means %.
But you don't need this. You must know the names of the prerequisites you
want to lock or at least be able to compute them with makefunctions.
Otherwise you can't write the makefile at all. So say they are ina inb inc.
Then you make all the locks intermediate like:
inputs := ina inb inc
locks := $(patsubst %,%.lock,$(inputs))
.INTERMEDIATE: $(locks)
I have a Makefile with multiple jobs.
Each job does a mkdir -p $(OBJDIR), to ensure the object directory exists before running gcc.
However, the mkdir -p sometimes does not ensure the directory exists before returning. I assume there is some sort of race condition going on. And then the compiler fails, as it can't write to the output file.
Is there a platform agnostic way of doing the mkdir -p so that this race condition doesn't occur?
This failure seems to only happen under OS X.
Question got sent to Stack Overflow, but really a sysadmin question.
# script named 'test'
if [ "$1" == "run" ] ; then
rm -rf XXX
NOW=`date +%s`
echo Now: $NOW
THEN=`echo "$NOW + 10" | bc`
RUNAT=`date -r $THEN +"%m%d%H%M.%S"`
echo $RUNAT
for i in {1..75}; do
./test $THEN &
sleep 0.05
done
wait
exit 0
fi
THEN=$1
# make sure things are cached
NOW=`python -c'import time; print repr(time.time())'`
NOW=`python -c'import time; print repr(time.time())'`
AMOUNT=`echo "$THEN - $NOW" | bc`
echo sleeping $AMOUNT
sleep $AMOUNT
mkdir -p XXX/YYY/ZZZ
touch XXX/YYY/ZZZ/hi
I wrote the above bash script, but it doesn't produce the predicted error. I will put the touch in the makefile, perhaps it will elucidate what is going on.
The makefile's CC statement looks like so:
$(OBJPATH)/%.o : %.cpp
mkdir -p $(dir $#)
#sleep 0.1
$(CPP) $(INCPATH) $(_FLAGS) $(CPPFLAGS) $< -o $#
The #sleep statement is there to possibly wait for the race condition to fix itself, but perhaps there is no race condition after all.
Say I have the following files:
buggy_program:
#!/bin/sh
echo "wops, some bug made me exit with failure"
exit 1
Makefile:
file.gz:
buggy_program | gzip -9 -c >$#
Now if I type make, GNU make will happily build file.gz even though buggy_program exited with non-zero status.
In bash I could do set -o pipefail to make a pipeline exit with failure if at least one program in the pipeline exits with failure. Is there a similar method in GNU make? Or some workaround that doesn't involve temporary files? (The reason for gzipping here is precisely to avoid a huge temporary file.)
Try this
SHELL=/bin/bash -o pipefail
file.gz:
buggy_program | gzip -9 -c >$#
You could do:
SHELL=/bin/bash
.DELETE_ON_ERROR:
file.gz:
set -o pipefail; buggy_program | gzip -9 -c >$#
but this only work with bash.
Here's a possible solution that doesn't require bash. Imagine you have two programs thisworks and thisfails that fail or work fine, respectively. Then the following will only leave you with work.gz, deleting fail.gz, ie. create the gzipped make target if and only if the program executed correctly:
all: fail.gz work.gz
work.gz:
( thisworks && touch $#.ok ) | gzip -c -9 >$#
rm $#.ok || rm $#
fail.gz:
( thisfails && touch $#.ok ) | gzip -c -9 >$#
rm $#.ok || rm $#
Explanation:
In the first line of the work.gz rule, thisworks will exit with success, and a file work.gz.ok will be created, and all stdout goes through gzip into work.gz. Then in the second line, because work.gz.ok exists, the first rm command also exits with success – and since || is short-circuiting, the second rm does not get run and so work.gz is not deleted.
OTOH, in the first line of the fail.gz rule, thisfails will exit with failure, and fail.gz.ok will not be created. All stdout still goes through gzip into fail.gz. Then in the second line, because fail.gz.ok does not exist, the first rm command exits with failure, so || tries the second rm command which deletes the fail.gz file.
To easily check that this works as it should, simply replace thisworks and thisfails with the commands true and false, respectively, put it in a Makefile and type make.
(Thanks to the kind people in #autotools for helping me with this.)
I've looked around for an answer to this one but couldn't find one.
I have written a simple script that does initial server settings and I'd like it to remove/unlink itself from the root directory on completion. I've tried a number of solutions i googled ( for example /bin/rm $test.sh) but the script always seems to remain in place. Is this possible? Below is my script so far.
#! /bin/bash
cd /root/
wget -r -nH -np --cut-dirs=1 http://myhost.com/install/scripts/
rm -f index.html* *.gif */index.html* */*.gif robots.txt
ls -al /root/
if [ -d /usr/local/psa ]
then
echo plesk > /root/bin/INST_SERVER_TYPE.txt
chmod 775 /root/bin/*
/root/bin/setting_server_ve.sh
rm -rf /root/etc | rm -rf /root/bin | rm -rf /root/log | rm -rf /root/old
sed -i "75s/false/true/" /etc/permissions/jail.conf
exit 1;
elif [ -d /var/webmin ]
then
echo webmin > /root/bin/INST_SERVER_TYPE.txt
chmod 775 /root/bin/*
/root/bin/setting_server_ve.sh
rm -rf /root/etc | rm -rf /root/bin | rm -rf /root/log | rm -rf /root/old
sed -i "67s/false/true/" /etc/permissions/jail.conf
break
exit 1;
else
echo no-gui > /root/bin/INST_SERVER_TYPE.txt
chmod 775 /root/bin/*
/root/bin/setting_server_ve.sh
rm -rf /root/etc | rm -rf /root/bin | rm -rf /root/log | rm -rf /root/old
sed -i "67s/false/true/" /etc/permissions/jail.conf
break
exit 1;
fi
rm -- "$0"
Ought to do the trick. $0 is a magic variable for the full path of the executed script.
This works for me:
#!/bin/sh
rm test.sh
Maybe you didn't really mean to have the '$' in '$test.sh'?
The script can delete itself via the shred command (as a secure deletion) when it exits.
#!/bin/bash
currentscript="$0"
# Function that is called when the script exits:
function finish {
echo "Securely shredding ${currentscript}"; shred -u ${currentscript};
}
# Do your bashing here...
# When your script is finished, exit with a call to the function, "finish":
trap finish EXIT
The simplest one:
#!/path/to/rm
Usage: ./path/to/the/script/above
Note: /path/to/rm must not have blank characters at all.
I wrote a small script that adds a grace period to a self deleting script based on
user742030's answer https://stackoverflow.com/a/34303677/10772577.
function selfShred {
SHREDDING_GRACE_SECONDS=${SHREDDING_GRACE_SECONDS:-5}
if (( $SHREDDING_GRACE_SECONDS > 0 )); then
echo -e "Shreding ${0} in $SHREDDING_GRACE_SECONDS seconds \e[1;31mCTRL-C TO KEEP FILE\e[0m"
BOMB="●"
FUZE='~'
SPARK="\e[1;31m*\e[0m"
SLEEP_LEFT=$SHREDDING_GRACE_SECONDS
while (( $SLEEP_LEFT > 0 )); do
LINE="$BOMB"
for (( j=0; j < $SLEEP_LEFT - 1; j++ )); do
LINE+="$FUZE"
done
LINE+="$SPARK"
echo -en $LINE "\r"
sleep 1
(( SLEEP_LEFT-- ))
done
fi
shred -u "${0}"
}
trap selfShred EXIT
See the repo here: https://github.com/reedHam/self-shred
$0 may not contain the script's name/path in certain circumstances. Please check the following: https://stackoverflow.com/a/35006505/5113030 (Choosing between $0 and BASH_SOURCE...)
The following script should work as expected in these cases:
source script.sh - the script is sourced;
./script.sh - executed interactively;
/bin/bash -- script.sh - passed as an argument to a shell program.
#!/usr/bin/env bash
# ...
rm -- "$( readlink -f -- "${BASH_SOURCE[0]:-$0}" 2> '/dev/null'; )";
Please check the following regarding shell script source reading and execution since it may affect the behavior when a script is deleted while running: https://unix.stackexchange.com/a/121025/133353 (How Does Linux deal with shell scripts?...)
Related: https://stackoverflow.com/a/246128/5113030 (How can I get the source directory of a Bash script from...)
Just add to the end:
rm -- "$0"
Why remove the script at all? As other have mentioned it means you have to keep a copy elsewhere.
A suggestion is to use a "firstboot" like approach. Simply create an empty file in e.g. /etc/sysconfig that triggers the execution of this script if it is present. Then remove that file at the end of the script.
Modify the script so it has the necessary chkconfig headers and place it in /etc/init.d/ so it is run at every boot.
That way you can rerun the script at a later time simply by recreating the trigger script.
Hope this helps.
I need a fresh temporary directory to do some work in a shell script. When the work is done (or if I kill the job midway), I want the script to change back to the old working directory and wipe out the temporary one. In Ruby, it might look like this:
require 'tmpdir'
Dir.mktmpdir 'my_build' do |temp_dir|
puts "Temporary workspace is #{temp_dir}"
do_some_stuff(temp_dir)
end
puts "Temporary directory already deleted"
What would be the best bang for the buck to do that in a Bash script?
Here is my current implementation. Any thoughts or suggestions?
here=$( pwd )
tdir=$( mktemp -d )
trap 'return_here' INT TERM EXIT
return_here () {
cd "$here"
[ -d "$tdir" ] && rm -rf "$tdir"
}
do_stuff # This may succeed, fail, change dir, or I may ^C it.
return_here
Here you go:
#!/bin/bash
TDIR=`mktemp -d`
trap "{ cd - ; rm -rf $TDIR; exit 255; }" SIGINT
cd $TDIR
# do important stuff here
cd -
rm -rf $TDIR
exit 0
The usual utility to get yourself a temporary directory is mktemp -d (and the -p option lets you specify a prefix directory).
I'm not sure if "I want to trap" was a question too, but bash does let you trap signals with (surprise!) trap - see the documentation.
Assuming mktemp -d returns a relative pathname, I would forget about $here and do this instead:
tdir=
cleanup() {
test -n "$tdir" && test -d "$tdir" && rm -rf "$tdir"
}
tdir="$(pwd)/$(mktemp -d)"
trap cleanup EXIT
trap 'cleanup; exit 127' INT TERM
# no need to call cleanup explicitly, unless the shell itself crashes, the EXIT trap will run it for you