Rename executable for single script in bash - linux

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.

Related

What is nim's equivalent of Python's `sys.executable`?

Following is the content of foo.py
import sys
print(sys.executable)
When I execute this, I can get the full path of the the Python interpreter that called this script.
$ /mingw64/bin/python3.9.exe foo.py
/mingw64/bin/python3.9.exe
How to do this in nim (nimscript)?
If you want to do that in Nim (not NimScript), you can take compiler executable path using https://nim-lang.org/docs/os.html#getCurrentCompilerExe
import os
echo getCurrentCompilerExe()
The question mentions NimScript, which has other uses in the Nim ecosystem, but can also be used to write executable scripts instead of using, e.g., Bash or Python. You can use the selfExe proc to get the path to the Nim executable which is running a NimScript script:
#!/usr/bin/env -S nim --hints:off
mode = ScriptMode.Silent
echo selfExe()
After saving the above as test.nims and using chmod +x to make the file executable, the script can be invoked to show the path to the current Nim executable:
$ ./test.nims
/home/.choosenim/toolchains/nim-1.4.8/bin/nim
Nim is compiled, so I assume you want to get the path of the application's own binary? If so, you can do that with:
import std/os
echo getAppFilename()

Create a spawn shell in Makefile

I want to create a makefile target in Ubuntun to spawn a poetry shell. Here are the things I want to do if I were in the command shell:
type poetry shell, which is going to spawn a shell within the virtual environment.
do something in the poetry shell, such as executing a python script using command python ...
To facilitate the process, I want to create a makefile looking something like below
# if I can set SHELL in a specific way
# SHELL = ?????
foo:
poetry shell
echo "Success"
# many lines to be executed in the poetry shell, here is an example
python <a_python_file>
The problem as I found is that the execution will be hanging after poetry shell and will not execute echo "Success"
I know this could be a general question on spawning a shell from command shell, and it is not limited to poetry. Any comments/suggestions would be appreciated.
As a comment pointed out, what I really want is python ... instead of poetry run python .... I edited it.
As a comment pointed out, I added some pseudo code in the makefile.
I think there's some misunderstanding. I'd never heard of poetry before but a quick look at its manual makes clear how it works.
If you run poetry shell then you get an interactive shell which you are expected to type commands into from your keyboard. The reason it "hangs" is that it started a poetry shell and is now waiting for you to enter commands. It's not hung, it's waiting for some input.
You don't want to run an interactive set of poetry commands, you have a predefined set of poetry commands you want to run. For that, you would use poetry run as mentioned in the comments above:
foo:
poetry run python <first command>
poetry run python <second command>
...
echo "Success"
If you want to run all the commands within a single instance of poetry, you have to combine them all into a single invocation, maybe something like this (I didn't try this so the quoting might be wrong):
foo:
poetry run 'python <first command> && python <second command> ...'
echo "Success"
You could do this:
foo:
poetry run $(MAKE) in-poetry
echo "Success"
in-poetry:
python <command1>
python <command2>
Now if you run make foo all the commands in the in-poetry target are run within the poetry environment, because poetry run runs a make program in its environment, and that make program runs a bunch of python.
But if someone ran make in-poetry directory (not via the foo target) then those python operations would not be run inside a poetry environment (unless the user set it up before they ran make).

shebang under linux does not split arguments

I have kotlin script (but it can be any Linux command with arguments) for example:
#!/usr/bin/env kotlinc -script
println("hello world")
When I run it in Ubuntu I get:
/usr/bin/env: ‘kotlinc -script’: No such file or directory
but when I run in command line:
/usr/bin/env kotlinc -script
It works. It is no problem with finding path because script:
#!/usr/bin/env kotlinc
println("hello world")
works
For some reason under Ubuntu "#!/usr/bin/env kotlinc -script" treats "kotlinc -script" as single argument. But only in shell script header.
I need explicitly to run my script "#!/usr/bin/env kotlinc -script" because I want it to run properly on other distributions end environments where "kotlin" is in $PATH.
Is there a bug in Ubuntu coreutils or sth? Is there a way to fix it?
On Linux, you can't pass more than one argument via the shebang line. All arguments will be passed as a single string to the executable:
#!/bin/foo -a -b -c
will pass one option "-a -b -c" to /bin/foo, plus the contents of the file. Like if you would call:
/bin/foo '-a -b -c' contents-of-file.txt
The behaviour should be the same on most unix derivates nowadays, but it can differ, I haven't tested them all :)
It's hard to find proper documentation for this, the best I could quickly find was this: https://www.in-ulm.de/~mascheck/various/shebang/#splitting
As a workaround you would normally create a shell wrapper:
#!/bin/bash
exec kotlin --arg1 --arg2 ... /path/to/kotlin-script
Check your coreutils version:
apt-cache policy coreutils
Starting with coreutils 8.30 you will be able to use:
#!/usr/bin/env -S command arg1 arg2 ...
You may want to upgrade your coreutils
For me the solution was to install kotlin, since I did not yet have installed it and just downloaded https://github.com/bernaferrari/GradleKotlinConverter
and thought it should work.
sudo snap install kotlin --classic

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

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

In Linux how do I get my Perl script to run like a binary?

I have a Perl Script that i'd like to be able to run from the command line. I can run this perfectly on it's own. Can anyone see what I'm doing wrong here?
Here are the things I've tried
$ ln -s slice.pl /bin
$ ln -s slice.pl /usr/bin
$ ln -s slice.pl /bin/slice
$ ln -s slice.pl /usr/bin/slice
$ slice
$ slice: command not found
Does the symlink have to be chmod +x? The original file is.
you need to invoke it like this:
$ ln -s "`pwd`"/slice.pl /bin/slice
$ slice
It fails because the link has to correctly point to the script.
The clue is the command not found error. If your shell were finding slice.pl, it would have complained about a bad interpreter or a permission error.
Clear your shell’s cache of PATH lookups. With bash, run
$ hash -r
or with C shell derivatives (e.g., tcsh), run
% rehash
After this, you will be able to run your slice command.
Associations with file extensions is a DOS-ish convention. With Unix, call your program what it is, i.e., slice, and give the program a shebang line (#!) to tell the operating system how to execute it.
Make sure that /bin or /usr/bin/ is in the users $PATH
Do a chmod a+rx slice.pl
Make sure this is the first like in your script
#!/usr/bin/perl
Provided that, it is indeed path to perl. which perl will give you the path to a perl.
Make sure the file uses unix line endings (0A) rather than Windows line endings (0D 0A), especially on the shebang (#!) line.

Resources