Why using explicit interpreter in bash doesn't use path variable - linux

Say I have a script /tmp/printy.py containing only:
#! /usr/bin/python2.7
print "hello world"
Why does this work:
chmod +x /tmp/printy.py
export PATH=$PATH:/tmp/
printy.py
But this doesn't:
chmod +x /tmp/printy.py
export PATH=$PATH:/tmp/
python printy.py
And what can be added to, say, a cron job which is supposed to run printy.py with a specific interpreter, to make it work?
Quite possibly duplicate but I can't find anything, maybe I'm using the wrong search terms?

Path lookups in the shell only apply to commands, not arbitrary files. In your first example, printy.py is the command name. In the second, it is just an argument to Python, and Python doesn't use PATH to find the script to run; it expects printy.py to be in the current working directory.
You can use a combination of PYTHONPATH and -m to simulate this:
PYTHONPATH=$PATH python -m printy

Related

Rename executable for single script in bash

I want to run shell script, where exe1 instead being looked up as /path/to/exe1 instead references what I see as exe2 in /path/to/exe2. Ideally, I want to do this in the most compact way with minimal side effects.
To make this example concrete, and somewhat similar to the problem I actually care about, I have a shell script script.sh where
$ cat script.sh
#!/usr/bin/env bash
python --version
I have two executables on my current PATH:
$ python --version
Python 2.7.x
$ python3 --version
Python 3.5.x
I want to call script.sh in such a way that
$ <magic> ./script.sh
Python 3.5.x
$ python --version
Python 2.7.x
The best solution I can find so far is
$ mkdir /tmp/python3 && ln -s $(which python3) /tmp/python3/python && env PATH="/tmp/python3:$PATH" ./script.sh
This could be made a little better by using mktemp -d and cleaning up, but it still involves writing mostly unnecessary files, for something it seems like I should just be able to tell bash, treat python as python3. Something like aliases would be ideal, but they don't get passed onto subshells. Is there an obvious tool I'm missing, or am I stuck with a variant of this method?
I want to call script.sh in such a way that
$ <magic> ./script.sh
Python 3.5.x
$ python --version
Python 2.7.x
You can do it like this using a function:
(python() { python3 "$#"; }; declare -xf python; ./script.sh)
We create a function called python inside a subshell that just invokes python3 executable.
Doing it in a sub-shell so that current environment is not messed up.

Is it possible to use "py" instead of "python" at the command line in Linux?

Do you think it's possible in Ubuntu instead of typing python3 test.py every time I want to run a script, to use a shorthand equivalent such like this: py test.py? In other words, what I want is to make a shorthand command for python3 that should look like this: py. Can I do that?
Sure. You can use the bash alias command to do something like this:
alias py=python3
Put it somewhere like .bashrc and IIRC it will be set up for each (interactive) session.
Why not ensure the top line of your python code has
#!/bin/python3
Then make sure the file is executable
chmod my_python_code.py +x
Now just run it
./my_python_code.py
The previous alias command will also work - even if you do the above steps.
./bye.py

Anyway to change the default exec call length?

I have a bash script and will have the first line start with # and followed by the command to execute the script, and it seems the limitation is 80 characters due to the exec call has such limitation, is there anyway to change that ? because sometimes my path will be very long.
Update.
My case is that I use virtualenv to generate a clean python environment. And in this environment, there's one executable file called pip, the shebang line is python executable path and sometimes this path will be very long, e.g.
#!/Users/myname/github/myproject/virtualenv_python3.4/bin/python3.4
If you don't want to modify your path to include the directory in which the executable, you can create a simple wrapper:
#!/bin/bash
/Users/myname/github/myproject/virtualenv_python3.4/bin/python3.4 <(cat <<"EOF"
# Python script goes here
EOF) "$#"

How to change Example.bat to Example.pl?

I have read other threads enter link description herethat discuss .bat to L/unix conversions, but none has been satisfactory. I have also tried a lot of hack type approach in writing my own scripts.
I have the following example.bat script that is representative of the kind of script I want to run on unix.
Code:
echo "Example.bat"
perl script1 param.in newParam.in
perl script2 newParam.in stuff.D2D stuff.D2C
program.exe stuff.D2C
perl script3 stuff.DIS results.out
My problem is I don't know how to handle the perl and program.exe in the unix bash shell. I have tried putting them in a system(), but that did not work. Can someone please help me?
Thank you!
Provided that you have an executable file named program.exe somewhere in your $PATH (which you well might — Unix executables don't have to end in .exe, but nothing says they can't), the code you've pasted is a valid shell script. If you save it in a file named, say, example.bat, you can run it by typing
sh example.bat
into the shell prompt.
Of course, Unix shell scripts are usually given the suffix .sh — or no suffix at all — rather than .bat. Also, if you want your script to be executable directly, by typing just
example.sh
rather than sh example.sh, you need to do three things:
Start the script with a "shebang" line: a line that begins with #! and the full path to the shell interpreter you want to use to run it (e.g. /bin/sh for the basic Bourne shell), like this:
#!/bin/sh
echo "This is a shell script."
# ... more commands here ...
Mark your script as executable using the chmod command, e.g.
chmod a+rx example.sh
Put your script somewhere along your $PATH. On Unix, the default path will not normally contain the current directory ., so you can't execute programs from the current directory just by typing their name. You can, however, run them by specifying an explicit path, e.g.
./example.sh # runs example.sh from the current directory
To find out what your $PATH is, just type echo $PATH into the shell.

How can I define a bash alias as a sequence of multiple commands? [duplicate]

This question already has answers here:
Multiple commands in an alias for bash
(10 answers)
Closed 4 years ago.
I know how to configure aliases in bash, but is there a way to configure an alias for a sequence of commands?
I.e say I want one command to change to a particular directory, then run another command.
In addition, is there a way to setup a command that runs "sudo mycommand", then enters the password? In the MS-DOS days I'd be looking for a .bat file but I'm unsure of the linux (or in this case Mac OSX) equivalent.
For chaining a sequence of commands, try this:
alias x='command1;command2;command3;'
Or you can do this:
alias x='command1 && command2 && command3'
The && makes it only execute subsequent commands if the previous returns successful.
Also for entering passwords interactively, or interfacing with other programs like that, check out expect. (http://expect.nist.gov/)
You mention BAT files so perhaps what you want is to write a shell script. If so then just enter the commands you want line-by-line into a file like so:
command1
command2
and ask bash to execute the file:
bash myscript.sh
If you want to be able to invoke the script directly without typing "bash" then add the following line as the first line of the file:
#! /bin/bash
command1
command2
Then mark the file as executable:
chmod 755 myscript.sh
Now you can run it just like any other executable:
./myscript.sh
Note that unix doesn't really care about file extensions. You can simply name the file "myscript" without the ".sh" extension if you like. It's that special first line that is important. For example, if you want to write your script in the Perl programming language instead of bash the first line would be:
#! /usr/bin/perl
That first line tells your shell what interpreter to invoke to execute your script.
Also, if you now copy your script into one of the directories listed in the $PATH environment variable then you can call it from anywhere by simply typing its file name:
myscript.sh
Even tab-completion works. Which is why I usually include a ~/bin directory in my $PATH so that I can easily install personal scripts. And best of all, once you have a bunch of personal scripts that you are used to having you can easily port them to any new unix machine by copying your personal ~/bin directory.
it's probably easier to define functions for these types of things than aliases, keeps things more readable if you want to do more than a command or two:
In your .bashrc
perform_my_command() {
pushd /some_dir
my_command "$#"
popd
}
Then on the command line you can simply do:
perform_my_command my_parameter my_other_parameter "my quoted parameter"
You could do anything you like in a function, call other functions, etc.
You may want to have a look at the Advanced Bash Scripting Guide for in depth knowledge.
For the alias you can use this:
alias sequence='command1 -args; command2 -args;'
or if the second command must be executed only if the first one succeeds use:
alias sequence='command1 -args && command2 -args'
Your best bet is probably a shell function instead of an alias if the logic becomes more complex or if you need to add parameters (though bash supports aliases parameters).
This function can be defined in your .profile or .bashrc. The subshell is to avoid changing your working directory.
function myfunc {
( cd /tmp; command )
}
then from your command prompt
$ myfunc
For your second question you can just add your command to /etc/sudoers (if you are completely sure of what you are doing)
myuser ALL = NOPASSWD: \
/bin/mycommand
Apropos multiple commands in a single alias, you can use one of the logical operators to combine them. Here's one to switch to a directory and do an ls on it
alias x="cd /tmp && ls -al"
Another option is to use a shell function. These are sh/zsh/bash commands. I don't know enough of other shells to be sure if they work.
As for the sudo thing, if you want that (although I don't think it's a good idea), the right way to go is to alter the /etc/sudoers file to get what you want.
You can embed the function declaration followed by the function in the alias itself, like so:
alias my_alias='f() { do_stuff_with "$#" (arguments)" ...; }; f'
The benefit of this approach over just declaring the function by itself is that you can have a peace of mind that your function is not going to be overriden by some other script you're sourcing (or using .), which might use its own helper under the same name.
E.g., Suppose you have a script init-my-workspace.sh that you're calling like . init-my-workspace.sh or source init-my-workspace.sh whose purpose is to set or export a bunch of environment variables (e.g., JAVA_HOME, PYTHON_PATH etc.). If you happen to have a function my_alias inside there, as well, then you're out of luck as the latest function declaration withing the same shell instance wins.
Conversely, aliases have separate namespace and even in case of name clash, they are looked up first. Therefore, for customization relevant to interactive usage, you should only ever use aliases.
Finally, note that the practice of putting all the aliases in the same place (e.g., ~/.bash_aliases) enables you to easily spot any name clashes.
you can also write a shell function; example for " cd " and "ls " combo here

Resources