cron: run a script that sources a function - bash

I have script that does a bunch of stuff. It sources a bunch of functions that are in the directory the script is being run from. i.e.
/home/me/script.sh
/home/me/function1
/home/me/function2
If I cd into /home/me and run ./script.sh everything works fine. The functions are sourced and do what needs to be done.
However, if I try to run this as a cron job, it will run up until the point I am trying to source the functions, and then it just stops and the process is terminated (if I run it directly from the directory, at least I get some errors).
Like wise, if I try to run this from another directory, I get a bunch of errors. e.g.
cd /opt/
/home/me/script.sh
function1: command not found
function2: command not found
I'm sure this has something to do with environmental variables, but I have no idea which ones. I have tried setting (in crontab):
PATH=/home/me
SHELL=/bin/bash
But that doesn't work either. Any help is appreciated. I don't want to hard code in the paths to the functions, and instead make them relative to the path the script is in (preferably the same dir).
Please let me know if you need any more information.

You are most probably aware of this, but just to be clear: A shell function does not have a path. They just need to be loaded into the current shell by sourcing the script that contains them:
source /path/to/functions
or
cd /path/to/functions
source functions
If you are talking about shell programs (scripts) instead, then you need to account for the fact that on Unix-like OS, the current directory is never in the PATH by default:
/path/to/functions/function1
or
cd /path/to/functions
./function1
You tagged your question Bash, but note that to be POSIX-compatible (e.g. if using sh), you have to use the . keyword (instead of either . or source on Bash) and the same restrictions regarding the PATH as for command execution apply, see dot:
. ./function1

Related

How can I invoke my code in bash like most compilers/interpreters are invoked? i.e. when someone types "python <file>", "java <file>" or "g++ <file>"

I am on Mac, but it would be best if there was a way to do this on any platform.
I want my program to behave as if it is an "HTML Compiler" - namely, to behave like the compilers/interpreters for Python, Java, C, C++, Node, etc. I don't understand how to install the program in bash so that I can invoke it like those other ones are invoked.
Namely, I want to run my program with
a global command (meaning accessible from any directory),
an optional specified argument for the HTML file, and also
context of which directory the command came from (so that the program can make later changes to that directory).
This is what it should look like:
parchment index.html or parchment .
I know how to make aliases and custom scripts in bash, but there is some piece missing where I can't figure out how to get all of these functionalities.
If it matters, the program itself is written in Java, and currently not an executable. I run it in the directory it is in by typing "java Parchment".
Thanks!
Create parchment like this
#! /usr/bin/env python3
import os
import sys
print('arg:', sys.argv)
print('cwd:', os.getcwd())
then
chmod +x parchment
You can run it from anywhere on your filesystem, like
/path/to/your/parchment
If you don't want to specify the absolute (or relative) path, parchment should be in a directory in your PATH. Check
echo $PATH
and move it to a directory in there
mv parchment /Users/myuser/bin/
edit
One more clarification. Once you have done all of these steps do:
parchment myfile.html
and see the output.

in which order will ubuntu search bin-folders for executables?

So I learned that in order to be able to execute a program from everywhere in the shell I have to put a reference file looking something like
#!/bin/bash
path/to/my/original/executable
in my bin-directory and make it executable.
On my current linux system (provided by my workplace) there are multiple bin-directories like
/usr/local/bin
/home/MyUsername/bin/
/home/otherUsername/bin/
For my work I have to alter a c++ program which is supposed to be usable for every user on the computer (hence I used /usr/local/bin/ sofar). But I realized that my changes to the original program do not come through... So my questions are:
Where could other bin-folders be, that I need to check for old executables?
How does my operating system (ubuntu 16.04) choose between executables in different bin-locations but with the same name?
Thanks in advance!
The PATH shell variable contains a colon separated list of paths to look for executables in. The list is processed left to right, the shell executes the first executable binary it finds (make sure to chmod +x the binary you are providing). If you want an easier printout you can use: echo $PATH | tr ":" "\n". Also keep in mind that a program might have been started with a different PATH than your shell and that users can customize their PATH variable. Systemwide PATH settings can usually be found in /etc/profile or /etc/profile.d/. You can use which file to display the full path expansion of file.

How to implicitly run a shell script sourced?

I have made a shell script to run as terminal command, but the cd commands inside it is not effective and hence I want to run it with source so that the cd commands take effect.
script name : "project.sh"
I added this file to /usr/local/bin, made it executable by chmod +x project.sh and it runs fine, but the cd command is not working.
I know it runs in a child process and hence at the end terminal returns back to the starting directory, rendering no effect of cd commands inside project.sh.
The solutions presented at Sol:1 do not work for me, because they asks me to run source <file>, which is not possible if I want to use it as Bash command.
You use the source command:
source /usr/local/bin/project.sh
There's no way to make this happen automatically by typing the script name, that always runs the script in a subprocess. If you don't want to have to type this all out, you could create an alias in your .bashrc to simplify it:
alias project='source /usr/local/bin/project.sh'
Then typing project will be translated to that full command.
Of course, source <file> is a Bash command - it uses the source builtin to run script <file> in the context of the current shell rather than in a child process, thus allowing commands in <file> to change the current shell's environment, such as in terms of the working directory (using cd).
Using source, or its alias ., is (ultimately) the only way to achieve that.
If your intent is not to have to invoke <script> explicitly with source, you have two options, both of which are best defined in your Bash profile, ~/.bash_profile (since you're on OS X; on Linux, use ~/.bashrc[1]):
I'll assume that your script is /path/to/foo, and that you want to invoke it sourced as just foo:
Option 1: Define an alias: alias foo='source "/path/to/foo"'
Option 2: Define a function: foo() { source "/path/to/foo"; }
Both aliases and functions execute in the current shell, allowing you to effectively hide the source call behind a single command; aliases are generally a little easier to define, but functions offer more flexibility.
By virtue of the alias / function being defined in your Bash profile, which itself is implicitly sourced, the commands in /path/to/foo will affect your interactive shells' environment.
Note: Either definition of foo will only be available in interactive shells (those that (automatically) source ~/.bash_profile).
Additional steps would be needed to make foo work inside non-sourced scripts as well, but at that point you should ask yourself whether you're obscuring things by not making the fact that /path/to/foo is getting sourced explicit.
If you're writing a script that must be sourced for distribution to others:
Install the sourcing command in the user's shell profile / initialization file (as described above) on installation of your script.
If there is no installation process (and also to enable on-demand installation in general), implement a command-line option for your script such as i (--install) that performs this installation on demand.
Preferably, also implement an uninstallation option.
Either way, build logic into the script so that it refuses to run when run without sourcing, and have the error message contain instructions on how to install sourcing.
See this answer for how to detect sourcing.
A real-world implementation of the above - although more elaborate due to being multi-shell - is my typex utility; source code here.
[1] On OS X, Bash instances started by Terminal.app are login shells, which means that the only (user-specific) file that is automatically sourced on startup is ~/.bash_profile.
By contrast, on most Linux systems Bash instances are non-login shells, where only ~/.bashrc is automatically sourced.
While it is common practice to source ~/.bashrc from one's ~/.bash_profile, this has to be configured manually and therefore cannot be relied upon blindly.

Putting links to scripts in my cygwin bin

I have made a few python scripts, but is there an easier way to run them? I am using cygwin.
python "C:\Users\Desk\Dropbox\scripts\wsort.py" > data11414_unsorted.txt < data11414_sorted.txt
I want something like this (not typing the path name or "python"):
wsort > data11414_unsorted.txt < data11414_sorted.txt
where wsort is a link to my real wsort.py
Add a
Shebang
to the script
#!/bin/python
then invoke like this
wsort.py > data11414_unsorted.txt < data11414_sorted.txt
First, your question has a Windows-style path (backslashes, beginning with C:) rather than a Cygwin path (/cygdrive/c/Users/Desk/Dropbox/scripts/wsort.py). That implies you're not actually using Cygwin, or if you are, you're ignoring a bunch of warnings.
The below assumes you're using Cygwin Bash (which should be what you get if you start Cygwin Terminal from the Start Menu) and Cygwin Python (which you've installed using Cygwin's setup.exe, not a Windows Python installer). If your not, you're making life more difficult for yourself than you need to.
That out the way, there's a bunch of steps you need to take:
First, make the script executable. Use the chmod command for that, from a Cygwin Bash shell:
chmod +x /cygdrive/c/Users/Desk/Dropbox/scripts/wsort.py
Second, tell the system how to execute it. Add the following line to the top of the script:
#!/bin/python
(That's a "shebang". Python sees it as a comment, so doesn't do anything with it, but Cygwin and other Linux-like systems will use that line to see which program to run the script with. In this case, Python.)
Third, make sure your line endings are correct. Cygwin expects Linux line endings and will fail without them. This may not be a problem, but there's no harm in doing this. Run the following command:
dos2unix /cygdrive/c/Users/Desk/Dropbox/scripts/wsort.py
At this point, you'll be able to call the script by specifying the full path to it in Cygwin. You can't yet run it without specifying where the script is explicitly.
The fourth step is making sure the script is "in your path", ie in one of the folders where Cygwin looks for scripts to run. There are lots of ways to do this, but the most sensible is probably to just add your scripts directory to your path. The following command will add your scripts directory to your path whenever you start a new Cygwin session:
echo 'PATH="/cygdrive/c/Users/Desk/Dropbox/scripts:$PATH"' >>~/.bashrc
You will need to restart your Cygwin terminal for that to take effect, however.
At that point, you'll be able to run the script in Cygwin just by typing wsort.py (and thus use it with redirections and so forth as in your question).
Finally, to be able to call it simply as wsort, there's a number of options. The obvious one is just renaming the file. More usefully (and without copying the file or doing anything liable to break with Dropbox syncing things), try creating an alias:
echo 'alias wsort=wsort.py' >>~/.bashrc
Again, you'll need to restart your Cygwin terminal for that to take effect.
Maybe use an alias ?
alias wsort = "Command_Used"

Can I use cygwin to script a hudson build step?

I've tried executing the following:
#!C:\cygwin\bin\bash.exe
ls ${WORKSPACE}
But that doesn't find ls (even if it's on the windows path). Is there any way to set this up?
UPDATE: In other words, I want to be able to set up a build step that uses cygwin bash instead of windows cmd like this page shows you how to do with Python.
So put your cygwin's bin directory in your PATH.
In case you don't know how to do it (Control Panel -> System -> Advanced -> Environment Variables), see: http://support.microsoft.com/kb/310519
That shell-script has two errors: the hash-bang line should be "#!/bin/bash", and ${WORKSPACE} is not a shell-variable. Hudson has a bunch of variables of its own which are expanded in the commands you specify to be run (i.e. when you add commands in the web gui).
If you want to run Hudson build step on the Cygwin command line, you need to figure out what command Hudson runs and in which directory.
To give a more specific answer, you need to show us how your project is configured and what steps you want to run separately.
Provided cygwin's bin folder is in your path, the following works for me:
#!/bin/sh
ls ${WORKSPACE}
I find Hudson does not pick up environment variable changes unless you restart the server.
you might want to try to give a full path to ls
/cygdrive/c/cygwin/bin/ls
One other thing that seems to work is to use this:
#!C:\cygwin\bin\bash.exe
export PATH=$PATH:/usr/bin
ls
But it would be nice not to have to modify the path for every script.
Have you thought about power shell? as much as I like cygwin, it's always been a little flaky, powershell is a solid fully functional shell on windows, another option is Windows Services for UNIX it gives you korn shell or c shell not quite as nice as bash but it gets the job done
You will need to pass the --login (aka -l) option to bash so that it will source Cygwin's /etc/profile and set up the PATH variable correctly. This will cause the current directory to get changed to the default "home" but you can set the environment variable CHERE_INVOKING to 1 before running bash -l and it will stay in the current directory if you need to preserve that.

Resources