Preventing escaping at shell in script with multiple levels of quotes - linux

I'd like to append the following lines to the end of my .zshrc file in an install script that is run:
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
The .zshrc file has write only access by root, and I am another user (but with sudo access).
One way to do it is similar to the following:
sudo bash -c "echo 'export PATH=\"$HOME/.rbenv/bin:$PATH\"' >> ~/.zshrc"
The main problem is that the $HOME and $PATH fields, as well as the $() section are then inserted after being replaced with the interpreted values. I could put a single quote on the outside, but I need to use a double quote where the first single quote is currently, which then interprets the inside.
I'd appreciate any help about how to do this without interpreting the variables/commands before insertion. Is a heredoc an easier way to do this?

Escape variable expansion by placing \ before $:
sudo bash -c "echo 'export PATH=\"\$HOME/.rbenv/bin:\$PATH\"' >> ~/.zshrc"

Just use the correct quotes. And don't be afraid to switch between them.
sudo bash -c "echo 'export "'PATH="$HOME/.rbenv/bin:$PATH"'"'" >> ~/.zshrc"

Related

How to run .profile inside a script

I have to SU first and run a profile inside a script, when I ran below script..the profile is giving log to the terminal and the parameters resolved in profiel or not resolving for the next line.thanks in advance
if [ $owner = "user1" ]
then
su -c " . ~/.profile; cd $LOG_DOR; cat $job.log" - user1
else
echo "$owner not found"
fi
To read in a file to the current shell, use source
source ~/.profile
When the shell parses a double-quoted string, it does variable expansion on the contents (replacing $varname with the variable's value) before executing the command. In this case, since the LOG_DOR and job variables are defined in ~/.profile (and not in the script) and that means that
su -c " . ~/.profile; cd $LOG_DOR; cat $job.log" - user1
will be expanded to:
su -c " . ~/.profile; cd ; cat .log" - user1
Which isn't what you want at all. To defer expansion of the variables, you can use single-quotes instead of double:
su -c ' . ~/.profile; cd $LOG_DOR; cat $job.log' - user1
But I'll actually recommend a couple of other changes as well: Put double-quotes around the variable references, to avoid possible problems when they do get expanded, and join the commands with && (instead of ;) so that if one fails it skips the rest (rather than blindly trying to continue):
su -c '. ~/.profile && cd "$LOG_DOR" && cat "$job.log"' - user1
EDIT: apparently in this case job is defined in the script, and LOG_DOR in ~/.profile; therefore the expansion of $job should be done immediately, and $JOB_DOR deferred. Fortunately, it's possible to use double-quotes and escape (\) the $ for expansions you want deferred. You'll also need to escape some other special characters, like included double-quotes (which would end the string if not escaped). Here, I've also switched to single-quotes around the $job reference, in case it contains any special characters that'd otherwise cause trouble (although this will fail if it contains any single-quotes itself):
su -c ". ~/.profile && cd \"\$LOG_DOR\" && cat '$job.log'" - user1

How can I feed input within bash [Executed through the Network]

As the title says, within linux how can I feed input to the bash when I do sudo bash
Lets say I have a bash script that reads the name.
The way I execute the script is through sudo using:
cat read-my-name-script.sh | sudo bash
Lets just say this is how I execute the script throught the network.
Now I want to fill the name automatically, is there a way to feed the input. I tried doing this: cat read-my-name-script.sh < name-input-file | sudo bash where the name-input-file is a file for the input that the user will be using to feed the script.
I am new to linux and learning to automate the input and wanted to create a file for input where the user can fill it and feed it to my script.
This is convoluted, but might do what you want.
sudo bash -c "$(cat read-my-name.sh)" <name-input-file
The -c says the next quoted argument are the commands to run (so, read the script as a string on the command line, instead of from a file), and the calling shell interpolates the contents of the file inside the double quotes before the sudo command gets evaluated. So if read-my-name.sh contains
#!/bin/bash
read -p "I want your name please"
then the command gets expanded into
sudo bash -c '#!/bin/bash
read -p "I want your name please"' <name-input-file
(where of course at this time the shell has actually removed the outer double quotes altogether; I put in single quotes in their place instead to show how this would look as actually executable, syntactically valid code).
I think you need that:
while read -r arg; do sudo bash read-my-name-script.sh "$arg";done <name-input-file
So each line of name-input-file will be passed as argument to sudo bash read-my-name-script.sh
If your argslist located on http server, you can do that:
while read -r arg; do sudo bash read-my-name-script.sh "$arg";done < <(wget -q -O- http://some/address/in/internet/name-input-file)
UPD
add [[ -f name-input-file ]] && readarray -t args <name-input-file
to read-my-name-script.sh
and use "${args[#]}" as arguments of command in the script.
For example echo "${args[#]}" or cmd "${args[0]}" "${args[1]}" ... "${args[100]}" in any order.
In this case you can use
wget -q -O- http://some/address/in/internet/read-my-name-script.sh | bash
for run your script with arguments from name-input-file whitout saving script to the local machine

Setting Path variable using shell scripting - Using a shell variable

I have variable difine as SCRPT_PATH="/home/dasitha" I need to add this path to .bashrc file using shell scirpt.
What I tired was something like this.
echo 'export PATH=$PATH:$SCRPT_PATH")' >> /root/.bashrc
After opening my .bashrc file it looks like this
export PATH=$PATH:$SCRPT_PATH")
What I actually need is export PATH=$PATH:/home/dasitha. How should I do this by changing the shell script?
You've got the wrong quotes (in addition to a spurious looking parenthesis and quote mark). You're looking for something more like
echo "export PATH=$PATH:$SCRPT_PATH" >> /root/.bashrc
Here's a quick example that demonstrates quoting:
krismatth#earth ~$ echo $HOME
/Users/krismatth
krismatth#earth ~$ echo '$HOME'
$HOME
krismatth#earth ~$ echo "$HOME"
/Users/krismatth

Customize SHELL in Makefile (set SHELL=my_shell.sh)

I would like to customize the shell for a Makefile, but I am running into trouble. Here is a MWE. I have a Makefile,
SHELL=./my_shell.sh
all: abc def
abc:
touch abc
def:
touch def
clean:
rm -f abc def
and a simple custom script, my_shell.sh.
#!/bin/bash
eval $*
I made sure to run chmod +x my_shell.sh beforehand. Then, when I typed make, I got an error.
make[3]: Entering directory `<CONFIDENTIAL>'
touch abc
./my_shell.sh: line 2: eval: -c: invalid option
eval: usage: eval [arg ...]
make[3]: *** [abc] Error 2
make[3]: Leaving directory `<CONFIDENTIAL>`
I have been struggling to get rid of the -c option. Setting .SHELLFLAGS='' doesn't seem to work.
First, if you want to set a make variable to empty use
.SHELLFLAGS =
By adding the quotes you've actually set the variable to the literal string '' (make is not the shell and does not do shell quote stripping).
Second, the .SHELLFLAGS variable was added in GNU make 3.82 so if your version is older than that, it won't work. If you have 3.82 or newer then it does work (I just tried it).
Lastly, as Jean-François points out you will problems if your command has escaped spaces. However, his solution of using "$#" cannot work as-is, because then the entire command is seen as a single string.
A more reliable implementation of the script would be:
#!/bin/bash
exec /bin/bash "$#"
And then do not modify .SHELLFLAGS. Or else, do set .SHELLFLAGS to empty and make your script:
#!/bin/bash
exec /bin/bash -c "$#"
SHELL expects a shell command like bash or csh. make invokes the shell command using -c as first argument (command option).
Since you pass all arguments directly to the eval commands you get that error.
Use shift to skip the first -c argument that you are not using.
#!/bin/bash
shift
eval $*
And maybe you'll need "$#" instead of $* if the arguments have spaces.

bash - how to pipe result from the which command to cd

How could I pipe the result from a which command to cd?
This is what I am trying to do:
which oracle | cd
cd < which oracle
But none of them works.
Is there a way to achieve this (rather than copy/paste of course)?
Edit : on second thought, this command would fail, because the destination file is NOT a folder/directory.
So I am thinking and working out a better way to get rid of the trailing "/oracle" part now (sed or awk, or even Perl) :)
Edit :
Okay that's what I've got in the end:
cd `which oracle | sed 's/\/oracle//g'`
You use pipe in cases where the command expects parameters from the standard input. ( More on this ).
With cd command that is not the case. The directory is the command argument. In such case, you can use command substitution. Use backticks or $(...) to evaluate the command, store it into variable..
path=`which oracle`
echo $path # just for debug
cd $path
although it can be done in a much simpler way:
cd `which oracle`
or if your path has special characters
cd "`which oracle`"
or
cd $(which oracle)
which is equivalent to backtick notation, but is recommended (backticks can be confused with apostrophes)
.. but it looks like you want:
cd $(dirname $(which oracle))
(which shows you that you can use nesting easily)
$(...) (as well as backticks) work also in double-quoted strings, which helps when the result may eventually contain spaces..
cd "$(dirname "$(which oracle)")"
(Note that both outputs require a set of double quotes.)
With dirname to get the directory:
cd $(which oracle | xargs dirname)
EDIT: beware of paths containing spaces, see #anishpatel comment below
cd `which oracle`
Note those are backticks (generally the key to the left of 1 on a US keyboard)
OK, here a solution that uses correct quoting:
cd "$(dirname "$(which oracle)")"
Avoid backticks, they are less readable, and always quote process substitutions.
You don't need a pipe, you can do what you want using Bash parameter expansion!
Further tip: use "type -P" instead of the external "which" command if you are using Bash.
# test
touch /ls
chmod +x /ls
cmd='ls'
PATH=/:$PATH
if cmdpath="$(type -P "$cmd")" && cmdpath="${cmdpath%/*}" ; then
cd "${cmdpath:-/}" || { echo "Could not cd to: ${cmdpath:-/}"; exit 1; }
else
echo "No such program in PATH search directories: ${cmd}"
exit 1
fi
besides good answer above, one thing needs to mention is that cd is a shell builtin, which run in the same process other than new process like ls which is a command.
https://unix.stackexchange.com/questions/50022/why-cant-i-redirect-a-path-name-output-from-one-command-to-cd
http://en.wikipedia.org/wiki/Shell_builtin
In response to your edited question, you can strip off the name of the command using dirname:
cd $(dirname `which oracle`)

Resources