Escape special characters in a bash script - linux

I have a command that I want to put in a script but I'm having issues. The command is very picky and it looks like this:
command_to_run -H 'ip_address' -u 'user' -p 'passwd' --register naming_data "/location/of/backup/folder/" "[destination-location]"
I want to create an interactive bash script that asks the user for the ip_address, user, password, location of backup folder and a destination location. The problem I'm having is that I'm storing the user's input on variables (IP_ADD, USERNAME, etc) but when I pass it to the command like
$COMMAND -H '$IPADD' -u '$USERNAME' .....
but again my issues are in the single quotes, the "/" the "[]" ad passing the variables. Can anyone give me a hint and what I need to fix in order to have it working??

Like Etan said, use double quotes:
command_to_run -H "$ip_address" -u "$user" -p "$passwd" ....

Use double quotes:
$COMMAND -H "$IPADD" -u "$USERNAME" ...
If you use single quotes in the shell, variables will not getting expanded meaning the literal $USERNAME will being used for the username.

Related

SSH remote execution - How to declare a variable inside EOF block (Bash script)

I have the following code in a bash script:
remote_home=/home/folder
dump_file=$remote_home/my_database_`date +%F_%X`.sql
aws_pem=$HOME/my_key.pem
aws_host=user#host
local_folder=$HOME/db_bk
pwd_stg=xxxxxxxxxxxxxxxx
pwd_prod=xxxxxxxxxxxxxxx
ssh -i $aws_pem $aws_host << EOF
mysqldump --column-statistics=0 --result-file=$dump_file -u user -p$pwd_prod -h $db_to_bk my_database
mysql -u user -p$pwd_prod -h $db_to_bk -N -e 'SHOW TABLES from my_database' > $remote_home/test.txt
sh -c 'cat test.txt | while read i ; do mysql -u user -p$pwd_prod -h $db_to_bk -D my_database --tee=$remote_home/rows.txt -e "SELECT COUNT(*) as $i FROM $i" ; done'
EOF
My loop while is not working because "i" variable is becoming empty. May anyone give me a hand, please? I would like to understand how to handle data in such cases.
The local shell is "expanding" all of the $variable references in the here-document, but AIUI you want $i to be passed through to the remote shell and expanded there. To do this, escape (with a backslash) the $ characters you don't want the local shell to expand. I think it'll look like this:
ssh -i $aws_pem $aws_host << EOF
mysqldump --column-statistics=0 --result-file=$dump_file -u user -p$pwd_prod -h $db_to_bk my_database
mysql -u user -p$pwd_prod -h $db_to_bk -N -e 'SHOW TABLES from my_database' > $remote_home/test.txt
sh -c 'cat test.txt | while read i ; do mysql -u user -p$pwd_prod -h $db_to_bk -D my_database --tee=$remote_home/rows.txt -e "SELECT COUNT(*) as \$i FROM \$i" ; done'
EOF
You can test this by replacing the ssh -i $aws_pem $aws_host command with just cat, so it prints the here-document as it'll be passed to the ssh command (i.e. after the local shell has done its parsing and expansions, but before the remote shell has done its). You should see most of the variables replaced by their values (because those have to happen locally, where those variables are defined) but $i passed literally so the remote shell can expand it.
BTW, you should double-quote almost all of your variable references (e.g. ssh -i "$aws_pem" "$aws_host") to prevent weird parsing problems; shellcheck.net will point this out for the local commands (along with some other potential problems), but you should fix it for the remote commands as well (except $i, since that's already double-quoted as part of the SELECT command).

Useradd script in linux without using passwd

I have to write a useradd script which adds a new user and sets for him a home directory.
#!/bin/bash
echo "Name:"
read name
echo "Password:"
read password
echo "Group:"
read group
useradd -m -G $group -s /bin/bash -p $password $name
Everything works as intended but I have problems with the password in the following line -
useradd -m -G $group -s /bin/bash -p $password $name
It does not work so I need to use later in terminal passwd command.
How can I rebuild my script so I won't need to use passwd to setup password correctly? I have read that you can use stdin but I was not able to do this correctly.
If you prefer to pipe the user's password from STDIN, use chpasswd utility which is quick and simple.
as suggested by #Ardit.
This script should work for your purpose, assuming you meet the following conditions-
You are interacting as the root user
You have an existing group created for the purpose of your new user
#!/bin/bash
echo "Name:"
read name
echo "Password:"
read password
echo "Group:" # group must exist
read group
# add new user, set group, create new home directory
useradd -G $group -m $name
# update new user password by piping from STDIN
echo ""$name":"$password"" | chpasswd
# change the default user shell to bash
chsh -s /bin/bash $name
First we execute useradd command to create the new user and assign it to an existing group.
Then we pipe the name and password into chpasswd. If you're wondering why wrap those variable expansions with double quotes, check this answer out .
Finally chsh utility is used to update the user shell.
Why not execute everything in a single statement?
I prefer subdividing a problem into smaller tasks for easier understanding.

Open new gnome-terminal from scripts and input vars from present script.

#!/bin/bash
Dpath=/home/$USER/Docker/
IP=`sed -n 1p /home/medma/.medmadoc`
DockerMachine=`sed -n 2p /home/$USER/.medmadoc`
DockerPort=`sed -n 5p /home/$USER/.medmadoc`
DockerUser=`sed -n 3p /home/$USER/.medmadoc`
DockerPass=`sed -n 4p /home/$USER/.medmadoc`
if [ ! -d $Dpath ] ; then
mkdir -p $Dpath
else
stat=`wget -O ".dockerid" http://$IP/DOCKER-STAT.txt`
for ids in `cat .dockerid`
do
if [ "$ids" == "$DockerMachine" ] ; then
gnome-terminal -x sh -c 'sshfs -p$DockerPort $DockerUser#$IP:/var/www/html $Dpath ; bash '
nautilus $Dpath
zenity --info --text "Mounted $DockerMachine"
exit
else
:
fi
done
zenity --info --text "No Such ID:$DockerMachine"
fi
gnome-terminal -x sh -c 'sshfs -p$DockerPort $DockerUser#$IP:/var/www/html $Dpath ; bash '
this command opens up a new terminal but the problem is that it does not load vars like $DockerPort $DockerUser $IP $Dpath from this script.
How do I input the values in these vars from this script to the newly opened terminal ?
Thanks !
As indicated before, you could try to use double quotes instead of single quotes around the sshfs invocation.
Single quotes in Bash are used to delimit verbatim text, in which variables are not expanded. Double quotes, in contrast, allow for variables expansion and command substitution ($(...)) to take place.
If you do use double quotes, beware of unintended side-effects (your username may contain a space, a dollar, a semicolon, or any other shell-special character). A cleaner approach would be to export the variables to the environment before calling gnome-terminal (and not forgetting to add double quotes around your variables inside the single-quotes), so that your code looks like :
export Docker{Port,User} IP Dpath
gnome-terminal -x sh -c 'sshfs -p"$DockerPort" "$DockerUser#$IP":/var/www/html "$Dpath" ; bash'
You may not want to pollute the environment with variables that will only be used once. If that is the case, instead of exporting them, you can use Bash's declare -p feature to serialize variables before loading them into a new environment (in my opinion, this is the cleanest approach). Here is what it looks like :
set_vars="$(declare -p Docker{Port,User} IP Dpath)"
gnome-terminal -x bash -c "$set_vars;"'sshfs ....'
Using this latest method, the variables are only visible to the shell process that runs the sshfs command, not gnome-terminal itself nor any sub-process run thereafter.
PS: you could read all your variables at once from the ~/.medmadoc file by using the following code instead of repeated sed invocations :
for var in IP Docker{Machine,User,Pass,Port}; do
read $var
done < ~/.medmadoc
This code makes use of the read builtin, that reads a line of input into a variable (in its simplest form).
PPS: That stat variable probably won't contain any useful information, since the output of wget was redirected by the -O flag. Perhaps you meant to store the result code of wget into stat, in which case what you meant was :
wget -O .dockerid ...
stat=$?

Why bash converting double quotes to single quotes?

I wrote a simple test script having the below code
#!/bin/bash
CHM=test
NAME="user#test"
if ! grep -q test /etc/passwd
then
useradd -s /bin/bash -m -d /home/test -c "${CHM} : ${NAME}" test
fi
but I'm having problem with bash converting my " to ' example output
+ CHM=test
+ NAME=user#test
+ grep -q test /etc/passwd
+ useradd -s /bin/bash -m -d /home/test -c 'test : user#test' test
useradd: invalid comment 'test : user#test'
I can't figure how to fix this any input is welcome, try to search but does not yield any preferred result
OS: RHEL6
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu
Thanks
/etc/passwd is delimited by : character and hence useradd is rejecting the comment. Try removing : in your comment.
Bash doesn't convert any quotes, this is purely an output artifact of set -x. As you can see, the ${VARIABLES} have been expanded in the set -x output. After that the quote type no longer matters and the bash maintainer chose to display all words containing blanks in single quotes to make the word boundaries clear.
The actual problem, as codegrep_admin correctly identified, is the impossibility to use a colon : for any field in /etc/passwd. E.g. you can't have a user named foo:bar and you can't have a comment saying test:user#test and you can't have a shell named ba:sh.

Passing variable into sshpass command inside a script for loop

I am trying to create a script (test.sh) that logs on to another server and checks the disk usage of some different folders:
test.sh:
DIRS="dir_A dir_B dir_C"
for DIR in $DIRS
do
sshpass -p user_password ssh -o StrictHostKeyChecking=no user_name#host 'cd /opt/app/$DIR;SIZE=$(du -s);echo "YVALUE="$SIZE > ../size_$DIR.txt'
done
However, the variable DIR never gets passed to the script. It is empty when I run the script. I have tried using {} around $DIR but still no success. What am I missing? Thanks for your help!
Basically, use double-quotes instead of single-quotes. You can still concatenate with single quotes if necessary:
sshpass -p user_password ssh -o StrictHostKeyChecking=no user_name#host 'cd /opt/app/'"$DIR"';SIZE=$(du -s);echo "YVALUE="$SIZE > ../size_'"$DIR".txt
I just noticed something: du -s produces an output of two columns so probably it's not being used the proper way yet. Perhaps something like SIZE=${SIZE%$'\t'*} is still needed.
Another way is to send the directory by input and let the other end read it:
sshpass -p user_password ssh -o StrictHostKeyChecking=no user_name#host 'read -r DIR; cd "/opt/app/$DIR"; SIZE=$(du -s); echo "YVALUE=$SIZE" > "../size_$DIR.txt"' <<< "$DIR"
This would be helpful if directories contain spaces or characters that may cause syntax errors. Using an array is also recommended for it:
DIRS=('DIR 1' 'DIR 2' 'DIR 3')
for DIR in "${DIRS[#]}"; do
sshpass ...
done
Wrapping a string in single quotes (') will stop any bash expansion taking place, you will need to use double quotes for $DIR to be evaluated, escaping any double quotes within the string that you want to send over SSH:
sshpass -p user_password ssh -o StrictHostKeyChecking=no user_name#host "cd /opt/app/$DIR;SIZE=\$(du -s);echo \"YVALUE\"=$SIZE > ../size_$DIR.txt"

Resources