Execute a bash script the first time shell opens - bash

Is there a way to execute a bash script automatically on a daily basis, that is I want the bash script to be executed every day the first time I open a shell terminal?
Thanks!

Bash has two files, from the user perspective, that perform "setup" when it is launched:
.bash_profile - This file is executed whenever you open an interactive login shell. This file may also be named .profile in certain distributions or configurations. .profile is usually used for non-Bash specific configuration items. Also be aware that if you have the little used .bash_login, .bash_profile will prevent that file from being used, though it is otherwise equivalent. .bash_profile is standard.
.bashrc - This file is executed for all other bash instances. Note that it is common for people to call .bashrc from .bash_profile to create consistency.
A login shell is spawned when you login; via ssh, telnet, at a console, etc. You can also force the launch of a login shell (forcing .bash_profile) to be processed by starting a shell under su like so:
su - username
Here, the dash indicates that this should be processed as a login shell.
Neither of these seem to be the correct answer for your question, however, unless you are certain to login once each day and only once each day.
A better approach in your case would be to use the cron. Crontab allows you to schedule jobs to run at any desired interval. For daily execution, you would likely want a line configured like so:
0 5 * * * /home/user/script
This would cause the user's script to execute at 5am every day. The columns are:
0 5 * * *
^ ^ ^ ^ ^------ Day of week
^ ^ ^ ^-------- Month of year
^ ^ ^---------- Day of month
^ ^------------ Hour of day
^-------------- Minute of hour
Each of those fields can also represent a comma separated list or even an arithmetic expression. For example, the following will execute the script four times during the 5 AM hour:
*/4 5 * * *

If you want the script to run when you open the shell terminal only, add it to your ~/.bashrc, /etc/bash.bashrc or /etc/bashrc file. This will execute anytime an interactive non login shell is started.
If you want it to execute daily, create a cron for it in /etc/crontab or crontab -e

While informative, the provided answers don't actually solve for the original requirement.
The request is for a script to be run once a day, at the first login, and not again the rest of the day, but then again upon the first login the next day, and the next, etc...
To achieve this you can place the script you want to execute in ~/bin or whatever location you want it in.
At the bottom of your script.sh add these three lines which will remove the execution of the script upon subsequent logins.
cat ~/.bash_profile | grep -v script.sh > bash_profile.tmp
rm -f ~/.bash_profile
mv bash_profile.tmp ~/.bash_profile
What these three lines do:
Reads in your .bash_profile and writes everything EXCEPT the line that contains script.sh to a tmp file
Deletes the existing bash profile that contains the execution of your script
Renames the tmp file, without the script.sh line, to be your new .bash_profile on subsequent logins.
THEN, use 'crontab -e' to add a line to the crontab, that will put back that line to your .bash_profile every morning at a time you would deem to be after your last login of the day but before your first login of the day.
This example is set for zero minutes + four hours, or 4:00am.
0 4 * * * echo "~/bin/script.sh" >> ~/.bash_profile
A problem exists with this, however.
If, for example, the user only logs into the system M-F and not on Sat or Sun. The crontab will still add the line to the profile Sat and Sun morning, meaning that come Monday morning there will be three identical lines. This will cause the script to run three times.
To mitigate this, an IF statement is wrapped around it to check if the command already exists in the file before adding it.
0 4 * * * if [ "$(grep -c '~/bin/script.sh' ~/.bash_profile)" -eq 0 ]; then echo "~/bin/script.sh" >> ~/.bash_profile ; fi
The end result is:
Your script will execute upon first login of the day
At the end, your script will remove the trigger for it to execute on login
Your script will not execute on subsequent logins
Every morning, crontab will add the trigger back to your .bash_profile for it to be executed on the next login, which would be the first login that day.
But crontab will only add the trigger if it doesn't already exist.
note: This is likely not the most efficient or eloquent solution, but it does work, and is pretty simple to understand.

Related

creating a cronjob for a script - fcron not cron -

I wrote a very simple script to copy files from one dir into another one, I want to create a cronjob - by the way, I don't have "crontab" here but "fcrontab" instead since scheduler isn't cron but fcron - so it runs once a week - on Sundays - but I'm not sure whether if it's correct or not. Can you take a look and tell me so?
this is the cronjob:
0 1 * 1-12 SUN /home/myusername/dir/cp.sh
or
0 1 * 1-12 SUN /bin/bash /home/myusername/dir/cp.sh
I've read quite a few posts from here as well as over the web but I'm still confused. Thanks in advance for your answers.
The weekday field contains a number where 0 represents Sunday. There is no support for human-readable weekdays in any crontab variant I have come across.
If you want this to execute every month, just put * for the month.
0 1 * * 0 /home/myusername/dir/cp.sh
As long as the script is executable (chmod +x cp.sh) and has a valid shebang (#!/bin/bash as the very first line of the file) you don't need to explicitly tell the OS to run it with bash, just like on the command line.
crontab runs your jobs from your home directory so you could replace /home/myusername with . if this is running from myusername's account. If $HOME/dir is in your PATH you only need cp.sh (but take care to set the PATH where cron, too, can see it!)
Any output or error messages will be sent by email if your server is set up to handle that. This is slightly obscure and sometimes bewildering, especially if you don't actually have email properly configured, so many users add a redirection to a log file for every cron job.
0 1 * * 0 cp.sh >>cp.sh.log 2>&1
(Some beginners like to redirect everything to /dev/null and then come here to ask us what's wrong when there's an error. Of course, we don't know, either.)
The Stack Overflow crontab tag info page has syntax advice, troubleshooting tips, and a link to a site where you can generate a valid crontab from a newbie-friendly form where you just click buttons and move sliders to say when your job should run.

Bash script runs in shell, gives "not found" error in crontab

I am using an EC2 instance, crontab, and slack-cleaner to delete all Slack messages older than 48 hours. To do this, I created delete_slack.sh (I've deleted my slack api token):
for CHANNEL in random general
do
slack-cleaner --token <MY TOKEN> --message --channel $CHANNEL --user "*" --before $(date -d '48 hour ago' "+%Y%m%d") --perform
done
Then I created a crontab line to run it every minute (once it works I'll change the timing to once a day) and had cron spit out the results to a log file:
* * * * * /home/ubuntu/delete_slack/delete_slack.sh >> /var/log/delete_slack.log 2>&1
To test, I ran sh /home/ubuntu/delete_slack/delete_slack.sh >> /var/log/delete_slack.log 2>&1 in the shell and it works fine. However, when I let the crontab run I get an error in the log file:
/home/ubuntu/delete_slack/delete_slack.sh: 3: /home/ubuntu/delete_slack/delete_slack.sh: slack-cleaner: not found
Any ideas? I've been banging my head against this all afternoon.
Sounds like the PATH you get via cron and the PATH you get through your login are different.
Either set the PATH in your script or use the absolute path to slack-cleaner
The PATH tells the shell which directories to search for executables (including scripts). You can echo $PATH to compare your path to the one cron gives and confirm that this is the issue.
If using the absolute path works, that is simplest, but if slack-cleaner uses other exes itself, setting the path may be better.
If you want to go the "modify PATH" method then you want to append the correct path to existing PATH and not completely overwrite it. i.e. export PATH=$PATH:/path/to/slack-cleaner-dir. You can always use which slack-cleaner to find out the correct path. NOTE: you want the directory without "slack-cleaner" appended to the end.
ALWAYS use full path in crons and you'll save a lot of time.
If you don't like export PATH=... then just use /path/to/slack-cleaner-dir instead.
Just load your profile before running the command to be in the exact same situation as when you launch it from your shell :
* * * * * . ~/.profile;/home/ubuntu/delete_slack/delete_slack.sh >> /var/log/delete_slack.log 2>&1
As I read that you're a bit new to this, here are just some more explanations about the profile :
The profile is a file loaded automatically when you connect in shell with your user.
The file is hidden in your home directory, to see it, you can launch :
ls -la ~
If you're in bash, the file will be named .bash_profile, if you're in shell or ksh, it will be named .profile
Hope it helped !

Define a Increment variable in shell script that increments on every cronjob

I have searched the forum couldn't find one.can we define a variable that only increments on every cronjob run?
for example:
i have a script that runs every 5minutes so i need a variable that increments based on the cron run
Say if the job ran 5minutes for minutes. so 6 times the script got executed so my counter variable should be 6 now
Im expecting in bash/shell
Apologies if a duplicate question
tried:
((count+1))
You can do it this way:
create two scripts: counter.sh and increment_counter.sh
add execution of increment_counter.sh in your cron job
add . /path/to/counter.sh into /etc/profile or /etc/bash.bashrc or wherever you need
counter.sh
declare -i COUNTER
COUNTER=1
export COUNTER
increment_counter.sh
#!/bin/bash
echo "COUNTER=\$COUNTER+1" >> /path/to/counter.sh
The shell that you've run the command in has exited; any variables it has set have gone away. You can't use variables for this purpose.
What you need is some sort of permanent data store. This could be a database, or a remote network service, or a variety of things, but by far the simplest solution is to store the value in a file somewhere on disk. Read the file in when the script starts and write out the incremented value afterwards.
You should think about what to do if the file is missing and what happens if multiple copies of the script are run at the same time, and decide whether those are situations you care about at all. If they are, you'll need to add appropriate error handling and locking, respectively, in your script.
Wouldn't this be a better solution?
...to define a file under /tmp, such that a command like:
echo -n "." > $MyCounterFilename
Tracks the number of times something is invoked, in my particular case of app.:
#!/bin/bash
xterm [ Options ] -T "$(cat $MyCounterFilename | wc -c )" &
echo -n "." > $MyCounterFilename
Because i had to modify the way xterm is invoked for my purposes and i found already that having opened many of these concurrently one waste less time if knowing exactly what is running on each one by its number (without having to cycle alt+tab and eye inspect through everything).
NOTE: /etc/profile, or better either ~/.profile or ~/.bash_profile needs only a env. variable name defined containing the full path to your counter file.
Anyway, if you dont like the idea above, experiments might be performed to determine a) 1st time out of all that /etc/profile is executed since machine is powered on and system boots. 2) Wether /etc/profile is executed or not, and how many times (Each time we open an xterm?, for instance). ... thereafter the same sort of testing for the other dudes lesser general than /etc one.

Can a cronjob be written in a shell?

I need to write a cronjob in .sh file. Is it possible to write a cronjob in .sh file?
My cronjob is
*/1 * * * * bash /abc/def/ghi/sample.sh
This cronjob will be executed once every minute.
Update According to comments by Jonathan Leffler, the variable SHELL may not be recognized by cron (depending on the version) and is not POSIX mandated. This would make the method in the question a better option, on that account alone.
The cron will (attempt to) execute whatever is there. The way you have it, it will run bash with following arguments, thus bash will run your script. (Thanks to Jonathan Leffler in the comment.)
Or, you can tell cron to run all commands via the shell of your choice by setting the SHELL variable
SHELL=/bin/bash
* * * * * /abc/def/ghi/sample.sh
See man 5 crontab on a Linux system for a lot of detail and examples.
Given the questions posed in comments this seems appropriate to add.
To set up a cron job we need to make a "crontab" file. This is done by invoking
crontab -e
The option -e is for "edit." The editor comes up and then you can enter the line from your question, then "save" and "quit". Which editor comes up depends on the settings, but if you didn't do anything it will most likely be vi. The 'root' may need to 'allow' this capability for a user. You can view the contents of your crontab by crontab -l.
When the time comes the command will get executed and the script "/path/sample.sh" will run. In that script your first line should be #!/bin/bash. The line you show does not go in the script, it belongs to the crontab file. The rest of the script "sample.sh" should consist of code that bash shell can execute.
NOTE Please see (additional) comments by Jonathan Leffler for more detail and expertise.

Insert output of schemer to Terminator config

My problem consists mainly of ignorance towards the different linux commands. I've run into a wall as I'm at a loss of words to correctly look up the commands I need.
However, I'd like to take the string that schemer (github.com/thefryscorer/schemer" outputs and insert it into my Terminator config at the line which starts with "palette=", replacing the existing info.
The purpose is to set this to run at intervals to keep my cycling wallpaper list updated with my bash colors.
If you could point me towards a place to learn of such automation and usage of commands, I'd be grateful.
Running Cinnamon 2.6.13 on Arch Linux, I wrote this code that, from a directory defined, takes a random file and applies it as the wallpaper. Afterwards it runs schemer and copies the newly generated config into Terminators config directory.
#!/bin/bash
#Todo;
currentmonth=August2015
#directory of wallpaper
currentfilename="$(ls ~/Pictures/wallpapers/"$currentmonth" | shuf -n 1)"
#set $currentfilename to random filename from $currentmonth
gsettings set org.cinnamon.desktop.background picture-uri file:///home/cogitantium/Pictures/wallpapers/"$currentmonth"/"$currentfilename"
#set wallpaper as current filename from default directory.
~/Go/bin/schemer -term="terminator" ~/Pictures/wallpapers/"$currentmonth"/"$currentfilename" > ~/Scripts/temp/currentpalette
#generate palette and redirect output to temporary palette file
echo "$currentmonth - $currentfilename - $currentpalette"
currentpalette="$(cat temp/currentpalette)"
#set $currentpalette to currentpalette
touch "temp/config"
sed -i "s/^.*\bpalette\b.*$/$currentpalette/g" "temp/config"
#insert generated palette into terminator config
cp "temp/config" "~/.config/terminator/config"
It does contain some errors and behaves irregularly at times. Furthermore, Terminator doesn't seem to react to the changes, even after a killall. I'll update my answer, should I find a solution.
The two things you'll need are sed and cron.
sed is a stream editor. In particular, it uses regular expressions to allow you to search through text files and replace parts of your text.
In particular, if you have
#conf.config
palette=REPLACE_ME
other.var=numbers
You can say sed s/REPLACE_ME/replaced_text/g conf.config
And sed will replace the text in conf.config with that second argument ("replace text").
So that'll be in a bash script you write.
What you'll want to do then in regularly execute your script, which you do by setting up a cron job. This will regularly execute a file.
You can either put your shell script in any one of the /etc/cron folders etc/cron.hourly, etc/cron.daily, etc, or
enter crontab -e at the terminal to open your personal cron configuration file, for more fine-grained control over when your commands execute.
The format of a cron command is as follows:
minute hour day-of-month month day-of-week command
So you could execute a command (as is explained in the sources below) once a week on Monday afternoon with
30 17 * * 1 /path/to/command (30=min, 17=5pm, and 1 is Monday as Sunday is 0-indexed).
or every 15 minutes with
*/15 * * * * /path/to/command
And your command would be ~/scripts/myscript.sh or whatever bash script has your sed command.
So you could have your cron job run schemer, then in the same script, put that string in your config file. You'll probably need to reload your config file, and the command for that (to stick at the end of your script) is source [path_to_config]
And there you go. Run schemer regularly, grab the string, stick it in your config file, and reload the file. cron, some basic bash (as I am unfamiliar with schemer, or the nature of it's output beyond "it's a string"), sed, and source.
Sources for cron jobs, as I am less familiar with them than with sed:
https://help.ubuntu.com/community/CronHowto
https://askubuntu.com/questions/2368/how-do-i-set-up-a-cron-job

Resources