Bash script : how to interpret variables in sub-bash command? [duplicate] - bash

This question already has answers here:
Difference between single and double quotes in Bash
(7 answers)
Closed 6 years ago.
I try to have a script to add ips to /etc/hosts, but if it does add a line to /etc/hosts, the line is empty.
I guess there is an issue with the variable name exchanged by value into the ["] :
machines=("dell" "pb")
ips=( "192.168.0.70" "192.168.0.60")
n=-1
for nom_machine in "${machines[#]}"
do
n=$(( $n + 1 ))
ip_machine=${ips[$n]}
link=" $ip_machine $nom_machine"
$(sudo /bin/bash -c 'echo -e $link >> /etc/hosts')
done
Any idea why this add empty lines to /etc/hosts ?

Variables aren't expanded in single quotes, only double quotes. You also don't need the $(...) around sudo.
sudo bash -c "echo -e $link >> /etc/hosts"
As a script style issue, I would suggest removing the sudo call altogether. Instead, expect the person running the script run it with sudo if they don't have sufficient permissions. Your script would just have:
echo -e "$link" >> /etc/hosts

Related

Running for loop using some other user

I am trying to execute a command using some other user. Here is my code
sudo -i -u someuser bash -c 'for i in 1 2 3; do echo $i; done'
I am expecting output as 1 2 3 but executed with someuser. Above code printing blank lines. I tried to add some other commands
sudo -i -u someuser bash -c 'for i in 1 2 3; do ls; done'
somefile1.txt somefile2.txt
somefile1.txt somefile2.txt
somefile1.txt somefile2.txt
If I try loop with the current user it gives expected output
for i in 1 2 3; do echo $i; done
1
2
3
Looks like bash is unable to resolve variable $i inside for loop. I tried escape character \ but not helping.
TL;DR: Don't use sudo -i with bash -c
The usual way to use sudo -i is without any arguments, in which case it simply starts an interactive login shell.
If you really must have a login shell for some reason (which isn't good practice for running scripts), it's much saner to simply add the extra arguments needed to make your shell a login shell to the bash command itself, and keep sudo out of the business of changing the arguments you pass it:
sudo -u someuser bash -lic 'for i in 1 2 3; do echo "$i"; done'
...or...
sudo -u someuser -i <<'EOF'
for i in 1 2 3; do echo "$i"; done
EOF
The Gory Details
When you use sudo -i with arguments, it rewrites the argument list given to concatenate the arguments together into a single command that can be put into the argument after -c, so you get something like {"sh", "-c", "bash -c ..."}. In concatenating arguments together, sudo uses the logic from parse_args handling for MODE_LOGIN_SHELL, adding an escape character before all characters that are not alphanumeric, _, - or $; keeping $ out of this list was introduced in commitish 6484574f, tagged as a fix for bug #564 (which was introduced by the fix to bug #413 -- personally, I think we would all be better off if bug 413 had been left in place rather than making any attempt to fix it).
See also sh -c does not expand positional parameters if I run it from sudo --login over at Unix & Linux Stack Exchange.
Since this behavior was deliberately put in place in 2013, I doubt there's any fixing it at this point -- any change to sudo's escaping behavior has the potential to modify the security properties of existing scripts.

Bash Script SSH Commands on Remote Server Not Executing as Expected [duplicate]

This question already has answers here:
Shell script: Run function from script over ssh
(3 answers)
Nested grep with SSH
(1 answer)
Closed 4 years ago.
So I've got a bash script in which I want to SSH onto one of my remote servers and run some commands. This is my code:
MYFUNCTION="
function my_function
{
VAR=$(readlink -f current | sed 's/[^)
}
my_function
"
ssh -l ${USERNAME} ${HOSTNAME} "${MYFUNCTION}"
The problem is that the VAR variable is not being populated with the command output as it should. I've run the exact same command myself, and I get the desired output, but when doing it through SSH in the bash script, it doesn't work as expected. What am I doing wrong here?
You are putting the code in double quotes, so the variables and commands are being executed on your local machine. Do echo "$MYFUNCTION" and you'll probably be surprised.
Try using a quoted here document:
# Note the single quotes in the next line
ssh -l "$USERNAME" "$HOSTNAME" <<'END_CODE'
function my_function
{
cd www
VAR=$(readlink -f current | sed 's/[^0-9]*//g')
VAR2=$(find . -maxdepth 1 ! -newer "$VAR" ! -name "$VAR"| sort | sed '$!d')
}
my_function
END_CODE
Note also all the quoted variables.

BASH - getting UID on shell script does not work [duplicate]

This question already has an answer here:
Blank first line of shell script: explain behavior of UID variable
(1 answer)
Closed 6 years ago.
Hi I have a question about bash.
and I'm new to it.
I made a file named "test.sh" and its contents is
#!/bin/bash
set -x
echo $UID
echo "$UID"
echo "$(id -u)"
and the result is blank!!
nothing shows up
However, when i just type "echo $UID" on terminal
it shows "1011"
is there anything i missed for bash?
Please help
UPDATED
bash version is 4.3.11 and I typed "sh test.sh" to execute.
and the result is
+ echo
+ echo
+ id -u
+ echo 1011
1011
thanks!
$UID is a Bash variable that is not set under sh, that may be why it outputs blank lines.
Try bash test.sh or make your script executable with chmod u+x test.sh, the program defined in shebang will then be used (/bin/bash)

How can a Bash variable be used in a string passed to Bash?

Let's say I've got the following here-document (which is used to enable hibernation in Ubuntu):
IFS= read -d '' text << "EOF"
[Re-enable hibernate by default in upower]
Identity=unix-user:*
Action=org.freedesktop.upower.hibernate
ResultActive=yes
[Re-enable hibernate by default in logind]
Identity=unix-user:*
Action=org.freedesktop.login1.hibernate
ResultActive=yes
EOF
I could store this text in a protected file in a way like the following:
echo "${text}" | sudo tee /etc/polkit-1/localauthority/50-local.d/com.ubuntu.enable-hibernate.pkla
How could I do a similar operation using the Bash variable in a command string (that's using the redirect operator) passed to Bash running under root?
sudo bash -c 'echo "${text}" > /etc/polkit-1/localauthority/50-local.d/com.ubuntu.enable-hibernate.pkla'
In the new instance of Bash, the variable obviously doesn't exist.
One option would be to pass the variable as an argument, like this:
sudo bash -c 'printf "%s\n" "$0" > /path/to/output' "$text"
text variable is no more defined in the new bash environment, that is why it is failing.
This below command has been tested ok, give it a try:
sudo bash -c 'cat > /etc/polkit-1/localauthority/50-local.d/com.ubuntu.enable-hibernate.pkla' <<<"${text}"

Trouble escaping quotes in a variable held string during a Sub-shell execution call [duplicate]

This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 6 years ago.
I'm trying to write a database call from within a bash script and I'm having problems with a sub-shell stripping my quotes away.
This is the bones of what I am doing.
#---------------------------------------------
#! /bin/bash
export COMMAND='psql ${DB_NAME} -F , -t --no-align -c "${SQL}" -o ${EXPORT_FILE} 2>&1'
PSQL_RETURN=`${COMMAND}`
#---------------------------------------------
If I use an 'echo' to print out the ${COMMAND} variable the output looks fine:
echo ${COMMAND}
screen output:-
#---------------
psql drupal7 -F , -t --no-align -c "SELECT DISTINCT hostname FROM accesslog;" -o /DRUPAL/INTERFACES/EXPORTS/ip_list.dat 2>&1
#---------------
Also if I cut and paste this screen output it executes just fine.
However, when I try to execute the command as a variable within a sub-shell call, it gives an error message.
The error is from the psql client to the effect that the quotes have been removed from around the ${SQL} string.
The error suggests psql is trying to interpret the terms in the sql string as parameters.
So it seems the string and quotes are composed correctly but the quotes around the ${SQL} variable/string are being interpreted by the sub-shell during the execution call from the main script.
I've tried to escape them using various methods: \", \\", \\\", "", \"" '"', \'"\', ... ...
As you can see from my 'try it all' approach I am no expert and it's driving me mad.
Any help would be greatly appreciated.
Charlie101
Instead of storing command in a string var better to use BASH array here:
cmd=(psql ${DB_NAME} -F , -t --no-align -c "${SQL}" -o "${EXPORT_FILE}")
PSQL_RETURN=$( "${cmd[#]}" 2>&1 )
Rather than evaluating the contents of a string, why not use a function?
call_psql() {
# optional, if variables are already defined in global scope
DB_NAME="$1"
SQL="$2"
EXPORT_FILE="$3"
psql "$DB_NAME" -F , -t --no-align -c "$SQL" -o "$EXPORT_FILE" 2>&1
}
then you can just call your function like:
PSQL_RETURN=$(call_psql "$DB_NAME" "$SQL" "$EXPORT_FILE")
It's entirely up to you how elaborate you make the function. You might like to check for the correct number of arguments (using something like (( $# == 3 ))) before calling the psql command.
Alternatively, perhaps you'd prefer just to make it as short as possible:
call_psql() { psql "$1" -F , -t --no-align -c "$2" -o "$3" 2>&1; }
In order to capture the command that is being executed for debugging purposes, you can use set -x in your script. This will the contents of the function including the expanded variables when the function (or any other command) is called. You can switch this behaviour off using set +x, or if you want it on for the whole duration of the script you can change the shebang to #!/bin/bash -x. This saves you explicitly echoing throughout your script to find out what commands are being run; you can just turn on set -x for a section.
A very simple example script using the shebang method:
#!/bin/bash -x
ec() {
echo "$1"
}
var=$(ec 2)
Running this script, either directly after making it executable or calling it with bash -x, gives:
++ ec 2
++ echo 2
+ var=2
Removing the -x from the shebang or the invocation results in the script running silently.

Resources