I wrote a shell script called gola to install golang, and put it on folder /usr/local/bin
#!/usr/bin/env bash
curl -LO https://golang.org/dl/go1.16.3.linux-amd64.tar.gz
sudo tar -C /usr/local -zxf go1.16.3.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
echo -e "Installed" && exit 1
I run sudo gola, and installed golang successfully, but when I run go version, the command go can't be found, export PATH=$PATH:/usr/local/go/bin doesn't work. I think it might be because the script is running in a subshell and it doesn’t work on its parent. I can add export PATH=$PATH:/usr/local/go/bin to bash profile, it will works. Besides, is there any other way if I just want it to take effect by running sudo gola?
There's no way to do this when running the script with sudo, because the sudo program itself runs as a subprocess of your shell (and the shell running the script runs as a subprocess of sudo), and subprocesses cannot affect parent processes' environments. (Note: export makes a variable export down to subprocesses, not up to parent processes.)
But since you're already running the tar command itself with sudo inside the script, why do you need to run the entire script with sudo? If you run it with source (or . if your shell doesn't support that), it'll run in the current shell, and the change to PATH will apply to the current shell.
But even that might not be what you really want, because it'll affect only the current shell. Next time you open a new one, you won't be able to use go until you change the PATH for the new shell instance. If you want future shells to be able to use go, you must add it to someplace like ~/.bash_profile.
Related
I have a script foo.sh located in /home/pi/Documents/Python directory. Purpose of this shell script is to run python script which needs root priviledges as it must reset usb device.
The script is as follows:
#!/bin/sh
export PATH="$PATH:/home/pi/.local/lib/python3.7"
python3 /home/pi/Documents/Python/foo.py
When I run the foo.py from Midnight Commander (setting a cursor on the file and pressing enter) it works, it exports the path correctly and the python script fails as it does not have enough priviledges to reset usb device.
I have actually made this script to run python script under root, but the root needs set a path to used module first.
However when I run
sudo foo.sh
I receive an answer:
sudo: foo.sh: command not found
I have checked the permissions and the foo.sh file has -rwxr-xr-x
sudo python3
typed in terminal also works correctly and opens python interpreter.
What is the problem that causes wrong behaviour under sudo?
I might be mistaken (I don't have a Linux Machine at hand atm, so I cannot verify), but if I recall correctly the user_home is part of the PATH variable exported for that user.
When you use the command sudo you are acting on the behalf of root which has got a different user_home than yours (== the current user), therefore your script is not found in any of the directories listed in the active PATH (the one of root because you are using the sudo command).
However, it should be possible to run successfully the following command:
$ sudo ./foo.sh
I hope this might shed some light.
Unless foo.sh is in a directory shown referenced by the PATH environmental variable, the environment will not recognise the command and hence the error
If you are in the directory with the foo.sh script, execute it with:
sudo ./foo.sh
If you are in a different directory, execute with:
sudo /pathtosh/foo.sh
I am building project source code in a SUSE server.
The project build.sh called "lzma" command to compress kernel.
The project build.sh need "sudo" to get access to some system command.
But I has tried to execute "sudo ./build.sh", and the shell always report error: "lzma: command not found."
I could execute "lzma" in shell with my user account. It works fine.
I also write a test shell script named "test.sh" which calls "lzma" command.
I found that it fails with same error message if I excute "test.sh" with "sudo" .
But if I execute "test.sh" without "sudo", it works fine.
Why ?
"Command not found" within sudo is almost invariably the result of an environment variable such as PATH, LD_LIBRARY_PATH (if what's missing is not the executable but a shared library it requires) or the like being altered.
You can pass working values through your environment variables through explicitly:
sudo PATH="$PATH" ./test.sh
Sudo uses a different Path then your user account.
EDIT (see comments)
Try and execute:
type lzma
Say the output reads something like '/usr/bin/lzma', then just copy that output into your sudo command like (for example):
sudo /usr/bin/lzma
That should do the trick. You should also write the full path of lzma into your shell script if you are to run it as root.
EDIT 2:
Or, as Charles Duffy mentioned in his answer, you could leave all things as is and simply use PATH="$PATH" in your command if you are trying to execute your file as SUDO or as a different user.
I am attempting to write a bash command line tool that is usable immediately after installation, i.e. in the same shell as its installation script was called. Lets say install-script.sh (designed for Ubuntu) looks like:
# Get the script's absolute path:
pushd `dirname $0` > /dev/null
SCRIPTPATH=`pwd`
popd > /dev/null
# Add lines to bash.bashrc to export the environment variable:
echo "SCRIPT_HOME=${SCRIPTPATH}" >> /etc/bash.bashrc
echo "export SCRIPT_HOME" >> /etc/bash.bashrc
# Create a new command:
cp ${SCRIPTPATH}/newcomm /usr/bin
chmod a+x /usr/bin/newcomm
The idea is that the new command newcomm uses the SCRIPT_HOME environment variable to reference the main script - which is also in SCRIPTPATH:
exec "${SCRIPT_HOME}/main-script.sh"
Now, the updated bash.bashrc hasn't been loaded into the parent shell yet. Worse, I cannot source it from within the script - which is running in a child shell. Using export to change SCRIPT_HOME in the parent shell would at best be duct-taping the issue, but even this is impossible. Also note that the installation script needs to be run using sudo so it cannot be called from the parent shell using source.
It should be possible since package managers like apt do it. Is there a robust way to patch up my approach? How is this usually done, and is there a good guide to writing bash installers?
You can't. Neither can apt.
A package manager will instead just write required data/variables to a file, which are read either by the program itself, by a patch to the program, or by a wrapper.
Good examples can be found in /etc/default/*. These are files with variable definitions, and some even helpfully describe where they're sourced from:
$ cat /etc/default/ssh
# Default settings for openssh-server. This file is sourced by /bin/sh from
# /etc/init.d/ssh.
# Options to pass to sshd
SSHD_OPTS=
You'll notice that none of the options are set in your current shell after installing a package, since programs get them straight from the files in one way or another.
The only way to modify the current shell is to source a script. That's unavoidable, so start there. Write a script that is sourced. That script will in turn call your current script.
Your current script will need to communicate with the sourced one to tell it what to change. A common way is to echo variable assignments that can be directly executed by the caller. For instance:
printf 'export SCRIPT_HOME=%q\n' "$SCRIPTPATH"
Using printf with %q ensures any special characters will be escaped properly.
Then have the sourced script eval the inner script.
eval "$(sudo install-script.sh)"
If you want to hide the sourceing of the top script you could hide it behind an alias or shell function.
I put a custom command (shell script) in /usr/local/scripts/.
In order to see commands from /usr/local/scripts, I set the PATH using the following methods:
sudo visudo
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/scripts"
sudo nano /etc/profile
PATH="$PATH:/usr/local/scripts"
export PATH
sudo nano /etc/login.defs
ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/scripts
ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/usr/local/scripts
sudo nano /root/.bashrc
PATH="$PATH:/usr/local/scripts"
export PATH
sudo nano /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/scripts"
And this works in most cases, but...
I have two script which, in their code, calls a script from /usr/local/scripts/, and it can't find my script!
The first is /etc/network/if-up.d/sendip, which is run as root when the networking stack is initialized.
And the second is /usr/local/scripts/notif-login which is run as root from pam by /etc/pam.d/sshd:
session optional pam_exec.so /usr/local/scripts/notif-login
If I run both script from my terminal shell, another user, with sudo, without sudo, after su, or login with root, it works properly. But when it is runner by the system (first when networking initialized, and the second via SSH) both failed to run scripts from /usr/local/scripts.
Is there another place where I have to set the path?
Bash/sh will not read /etc/profile for "non-interactive shells", such as the shells from which the scripts you mention run. I'm not sure which distribution you're using, but you should just be able to add it to /etc/environment's PATH definition. Ob Ubuntu, for example:
Change:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
To:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/scripts"
Edit
This should work under Raspbian; it does under Debian. If it doesn't, you might need to just modify the path at the start of one of your scripts. None of the normal startup files will execute for a non-interactive session under Bash. Also try outputting $SHELL and make sure the script is running under the shell you think it is.
I'm trying to create a script that launches another script and have it as a child directly. My goals are:
the child program should see a different $HOME
the child script should run in a different directory than the current working directory (i.e. different pwd)
no extra shells
I achieved the first two goals via subshells and exec, but I've yet to manage the third one. Could someone help me?
Here are the details. For ease of description, I will call the first script run, and the other program sleepcmd. Here's the content of sleepcmd script
echo $HOME && exec sleep 1000
Here's the content of run script
(HOME=~/foo/bar && cd $HOME/bin && ./sleepcmd)
Adding an exec before ./sleepcmd invocation, i.e.
(HOME=~/foo/bar && cd $HOME/bin && exec ./sleepcmd)
gets this to just one extra shell, compared with running sleepcmd (or sleep) directly.
How can I do better than that, and get rid of the () subshell, while still invoking sleep 1000 with a different $HOME and working directory?
try
pushd
VAR=value command [args...]
popd
e.g.
pushd ~/foo/bar/bin
HOME=~/foo/bar ./sleepcmd
popd
You could try using the "source" command on a file (e.g. "myscript") containing the new environment and commands, e.g.
source myscript
or
. myscript
and set up the new environment at the top of myscript. However, you will have to set the environment back to what it was before at the end of myscript, or it will continue in your main script.