bash string comparison not working as expected - how to fix - linux

I am trying to check if a program is installed on a linux system by comparing command output with an expected output. I expect the program not to be present and so if program is abc then I should see:
bash: abc: command not found
But instead I am getting this output:
Shell for this session: bash
./checkprogs.sh: line 17: [: too many arguments
addgroup feature enabled test 1
./checkprogs.sh: line 15: tftp: command not found
./checkprogs.sh: line 17: [: too many arguments
tftp feature enabled test 2
2 tests performed
Here is the bash code:
ourshell="/bin/bash"
ourshell=${ourshell#*bin/}
echo "Shell for this session: $ourshell"
counter=1
# list of programs we want to test if available
progs='addgroup tftp'
for prog in $progs
do
expected="$ourshell: $prog: command not found"
#echo "$expected"
progoutput=$($prog -h)
#echo "prog output is: $progoutput"
if [ $expected != $progoutput ]
then
echo "test failed"
fi
echo "$prog feature enabled test $counter"
((counter++))
done
((counter--))
echo "$counter tests performed"
How can I fix this?
On the system I am working on, the shell is bash and addgroup is present but tftp is not.
Please note that I am on an embedded platform and "expect" is not installed. Assume minimal linux installation.
EDIT:
had to add space after [ and before ] - but even after fixing that still doesn't work as expected.
EDIT:
sorry I am new to bash scripting...
If I quote the strings inside [ ] and add 2>&1 to progoutput then code looks like this:
progoutput=$($prog -h 2>&1) # line 15
if [ "$expected" != "$progoutput" ] # line 17
and just so anyone can try this:
progs='ls invalidxxx' # line 9
then output is:
Shell for this session: bash
test failed
ls feature enabled test 1
test failed
invalidxxx feature enabled test 2
2 tests performed

actual method of string comparison
if [ "$expected" != "$progoutput" ]
or
if [[ $expected != $progoutput ]]

Related

Can't run ansible commands inside a shell script

I can't seem to run ansible commands inside shell scripts.
Whenever I run ansible or ansible-playbook commands, it fails with the below error:
./check_best_host_for_vm_creation.sh: line 9: syntax error near unexpected token `ansible-playbook'
I am sure that the ansible-playbook command is correct and there is nothing wrong with it, as I am able to execute it successfully from outside the script.
The full script is:
#!/bin/bash
hostname_selected=''
for host in 10.28.187.153 10.28.143.10 do
ansible-playbook /etc/ansible/gather_vcenter_facts.yml --extra-vars "esxi_hostname=$host"
host_memory=`cat /etc/ansible/files/tmp_host_memory`
if [ "$host_memory" -eq 4000]; then
ansible-playbook /etc/ansible/create_vms_on_host.yml --extra-vars "esxi_hostname='$host'"
$hostname_selected=$host
break
fi
done
if ["$hostname_selected = '']; then
echo "No host available with free memory"
else
echo "Script done and the VM is created on host $hostname_selected "
fi
~
File names are correct, as well as paths.
There were several indentation, spacing and syntax errors. I corrected to this. Please try and let me know if it works now.
#!/bin/bash
hostname_selected=''
for host in '10.28.187.153' '10.28.143.10'
do
ansible-playbook /etc/ansible/gather_vcenter_facts.yml --extra-vars "esxi_hostname=$host"
host_memory=$( cat /etc/ansible/files/tmp_host_memory )
if [ "$host_memory" -eq 4000 ]
then
ansible-playbook /etc/ansible/create_vms_on_host.yml --extra-vars "esxi_hostname='$host'"
hostname_selected=$host
break
fi
done
if [ "$hostname_selected" = '' ]
then
echo "No host available with free memory"
else
echo "Script done and the VM is created on host $hostname_selected"
fi
Regards!

Execute Bash script remotely via cURL

I have a simple Bash script that takes in inputs and prints a few lines out with that inputs
fortinetTest.sh
read -p "Enter SSC IP: $ip " ip && ip=${ip:-1.1.1.1}
printf "\n"
#check IP validation
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "SSC IP: $ip"
printf "\n"
else
echo "Enter a valid SSC IP address. Ex. 1.1.1.1"
exit
fi
I tried to upload them into my server, then try to run it via curl
I am not sure why the input prompt never kick in when I use cURL/wget.
Am I missing anything?
With the curl ... | bash form, bash's stdin is reading the script, so stdin is not available for the read command.
Try using a Process Substitution to invoke the remote script like a local file:
bash <( curl -s ... )
Your issue can be simply be reproduced by run the script like below
$ cat test.sh | bash
Enter a valid SSC IP address. Ex. 1.1.1.1
This is because the bash you launch with a pipe is not getting a TTY, when you do a read -p it is read from stdin which is content of the test.sh in this case. So the issue is not with curl. The issue is not reading from the tty
So the fix is to make sure you ready it from tty
read < /dev/tty -p "Enter SSC IP: $ip " ip && ip=${ip:-1.1.1.1}
printf "\n"
#check IP validation
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "SSC IP: $ip"
printf "\n"
else
echo "Enter a valid SSC IP address. Ex. 1.1.1.1"
exit
fi
Once you do that even curl will start working
vagrant#vagrant:/var/www/html$ curl -s localhost/test.sh | bash
Enter SSC IP: 2.2.2.2
SSC IP: 2.2.2.2
I personally prefer source <(curl -s localhost/test.sh) option. While it is similar to bash ..., the one significant difference is how processes handled.
bash will result in a new process being spun up, and that process will evoke commands from the script.
source on the other hand will use current process to evoke commands from the script.
In some cases that can play a key role. I admit that is not very often though.
To demonstrate do the following:
### Open Two Terminals
# In the first terminal run:
echo "sleep 5" > ./myTest.sh
bash ./myTest.sh
# Switch to the second terminal and run:
ps -efjh
## Repeat the same with _source_ command
# In the first terminal run:
source ./myTest.sh
# Switch to the second terminal and run:
ps -efjh
Results should look similar to this:
Before execution:
Running bash (main + two subprocesses):
Running source (main + one subprocess):
UPDATE:
Difference in use variable usage by bash and source:
source command will use your current environment. Meaning that upon execution all changes and variable declarations, made by the script, will be available in your prompt.
bash on the other hand will be running in as a different process; therefore, all variables will be discarded when process exits.
I think everyone will agree that there are benefits and drawbacks to each method. You just have to decide which one is better for your use case.
## Test for variables declared by the script:
echo "test_var3='Some Other Value'" > ./myTest3.sh
bash ./myTest3.sh
echo $test_var3
source ./myTest3.sh
echo $test_var3
## Test for usability of current environment variables:
test_var="Some Value" # Setting a variable
echo "echo $test_var" > myTest2.sh # Creating a test script
chmod +x ./myTest2.sh # Adding execute permission
## Executing:
. myTest2.sh
bash ./myTest2.sh
source ./myTest2.sh
./myTest2.sh
## All of the above results should print the variable.
I hope this helps.

Not being able to run bash script on windows

I am trying hard to run a bash script on windows but the message I am getting is "Sorry, only Linux and MacOS are supported."
I have installed Cygwin and Clink in order to be able to run sh scripts on windows platform but still it is of no avail.
Here is my bash script,
#!/bin/bash
for ((j=0;j<10;j++)); do
rtg map -i sample_NA19240/SRR003988 -t hg19 -o map_sample_NA19240/SRR003988- $j --start-read=$[j*1000000] --end-read=$[(j+1)*1000000]
done
It's in the program you are using, "rtg" :
# Pre-flight safety-belts
if [ "$(uname -s)" != "Linux" ] && [ "$(uname -s)" != "Darwin" ]; then
# If you comment this check out you are on your own :-)
echo "Sorry, only Linux and MacOS are supported."
exit 1
You can try the "suggestion", that is, remove the check in the file installer/rtg. If it works, you are lucky. Else use a vm or ask the rtg author.

snmpd on Beaglebone/Debian, reading file with source

I have installed snmpd on a Beaglebone Black with Debian and everything works perfect so far, except one thing.
I have configured snmpd.conf for a pass-through
pass .1.3.6.1.4.1.45919 /bin/sh /usr/local/bin/snmp-20
Then snmp-20 is a batch script that looks like this
#!/bin/bash
if [ "$1" = "-n" ]
then
exit 0
fi
. /root/snmp.cfg
#sSerialNumber
if [ "$2" = ".1.3.6.1.4.1.45919.1.120.5" ]
then
echo 1.3.6.1.4.1.45919.1.120.5
echo string
echo $serial
exit 0
fi
In snmp.cfg it looks like this
serial=12345
I feel this is all pretty straight forward. Now when I run /bin/sh /usr/local/bin/snmp-20 or just /usr/local/bin/snmp-20 I get the expected output.
When I do the snmpget -c public -v2c localhost 1.3.6.1.4.1.45919.1.120.5 it returns with "No such instance currently exists ..."
However when I comment the ./root/snmp.cfg the snmpget call is successful, so the calling parameters all work correctly.
It seems the script exits when the source /root/snmp.cfg command is called, but only when called by snmpget, not when called from the prompt.
Any idea would be appreciated.

Gsettings with cron

I wrote a bash script that changes the wallpaper (for GNOME3).
#!/bin/bash
# Wallpaper's directory.
dir="${HOME}/images/wallpapers/"
# Random wallpaper.
wallpaper=`find "${dir}" -type f | shuf -n1`
# Change wallpaper.
# http://bit.ly/HYEU9H
gsettings set org.gnome.desktop.background picture-options "spanned"
gsettings set org.gnome.desktop.background picture-uri "file://${wallpaper}"
Script executed in a terminal emulator (eg gnome-terminal) works great. During the execution by cron, or ttyX terminal getting the error:
** (process:26717): WARNING **: Command line `dbus-launch --autolaunch=d64a757758b286540cc0858400000603 --binary-syntax --close-stderr' exited with non-zero exit status 1: Autolaunch error: X11 initialization failed.\n
** (process:26717): WARNING **: Command line `dbus-launch --autolaunch=d64a757758b286540cc0858400000603 --binary-syntax --close-stderr' exited with non-zero exit status 1: Autolaunch error: X11 initialization failed.\n
** (process:26721): WARNING **: Command line `dbus-launch --autolaunch=d64a757758b286540cc0858400000603 --binary-syntax --close-stderr' exited with non-zero exit status 1: Autolaunch error: X11 initialization failed.\n
** (process:26721): WARNING **: Command line `dbus-launch --autolaunch=d64a757758b286540cc0858400000603 --binary-syntax --close-stderr' exited with non-zero exit status 1: Autolaunch error: X11 initialization failed.\n
Finally I managed how to solve this issue after many, many attempts.
Indeed, the problem occur because cron uses only a very restricted set of environment variables. And the only one environment variable that is responsible for running in the right way the script from the question when this is set as a cron job is DBUS_SESSION_BUS_ADDRESS, not DISPLAY or XAUTHORITY or GSETTINGS_BACKEND or something else. This fact was also pointed well in this answer.
But the problem in this answer is that there's no guarantee that the DBUS_SESSION_BUS_ADDRESS variable from that file from ~/.dbus/session-bus/ directory is updated to the current value from the current gnome session. To go over this problem a method would be to find the PID of a process in the current gnome session, and obtain the dbus address from its environment. We can do this as follow:
PID=$(pgrep gnome-session) # instead of 'gnome-session' it can be also used 'noutilus' or 'compiz' or the name of a process of a graphical program about that you are sure that is running after you log in the X session
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-)
That being said, the script should look like:
#!/bin/bash
# TODO: At night only dark wallpapers.
# Wallpaper's directory.
dir="${HOME}/images/wallpapers/"
# export DBUS_SESSION_BUS_ADDRESS environment variable
PID=$(pgrep gnome-session)
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-)
# Random wallpaper.
wallpaper=`find "${dir}" -type f | shuf -n1`
# Change wallpaper.
# http://bit.ly/HYEU9H
gsettings set org.gnome.desktop.background picture-options "spanned"
gsettings set org.gnome.desktop.background picture-uri "file://${wallpaper}"
I found some solutions. When you export a variable DBUS_SESSION_BUS_ADDRESS contained in the file ~/.dbus/session-bus/*, dbus-launch does not tell more about the error. However, instead of wallpaper there are artefacts.
Added code:
sessionfile=`find "${HOME}/.dbus/session-bus/" -type f`
export `grep "DBUS_SESSION_BUS_ADDRESS" "${sessionfile}" | sed '/^#/d'`
Now the script looks like this:
#!/bin/bash
# TODO: At night only dark wallpapers.
# Wallpaper's directory.
dir="${HOME}/images/wallpapers/"
# Weird, but necessary thing to run with cron.
sessionfile=`find "${HOME}/.dbus/session-bus/" -type f`
export `grep "DBUS_SESSION_BUS_ADDRESS" "${sessionfile}" | sed '/^#/d'`
# Random wallpaper.
wallpaper=`find "${dir}" -type f | shuf -n1`
# Change wallpaper.
# https://superuser.com/questions/298050/periodically-changing-wallpaper-under-gnome-3/298182#298182
gsettings set org.gnome.desktop.background picture-options "spanned"
gsettings set org.gnome.desktop.background picture-uri "file://${wallpaper}"
Tried this and it worked great for me:
dbus-launch --exit-with-session gsettings set schema key value
Or from root cron:
sudo -u user dbus-launch --exit-with-session gsettings set schema key value
Credit: http://php.mandelson.org/wp2/?p=565
add export DISPLAY=:0 && export XAUTHORITY=/home/username/.Xauthority , where username is your ubuntu username. It should fix the X11 authorisation error.
To change your wallpaper through cron, just do this directly in your crontab :
Execute crontab -e
Add lines like this :
30 09 * * * DISPLAY=:0 GSETTINGS_BACKEND=dconf /usr/bin/gsettings set org.gnome.desktop.background picture-uri file:////home/elison/Pictures/morning.jpg
00 12 * * * DISPLAY=:0 GSETTINGS_BACKEND=dconf /usr/bin/gsettings set org.gnome.desktop.background picture-uri file:////home/elison/Pictures/noon.jpg
Also see this solution that work for me:
https://unix.stackexchange.com/questions/111188/using-notify-send-with-cron#answer-111190
:
You need to set the DBUS_SESSION_BUS_ADDRESS variable. By default cron does not have access to the variable. To remedy this put the following script somewhere and call it when the user logs in, for example using awesome and the run_once function mentioned on the wiki. Any method will do, since it does not harm if the function is called more often than required.
#!/bin/sh
touch $HOME/.dbus/Xdbus
chmod 600 $HOME/.dbus/Xdbus
env | grep DBUS_SESSION_BUS_ADDRESS > $HOME/.dbus/Xdbus
echo 'export DBUS_SESSION_BUS_ADDRESS' >> $HOME/.dbus/Xdbus
exit 0
This creates a file containing the required Dbus evironment variable. Then in the script called by cron you import the variable by sourcing the script:
if [ -r "$HOME/.dbus/Xdbus" ]; then
. "$HOME/.dbus/Xdbus"
fi
And finally the ugly, no cron at all (darn it)! Using other methods the changes get set in gconf, but the image does not change. Maybe it is because I am running Deepin's DDE (dde makes use of the same path, different key). Ugly for the rescue: A final attempt to make this saga work.
With this script the wallpaper is changed every 420 seconds (7 minutes) in an endless loop picking a random wallpaper from one of 4 sets (or directories), acordingly to the time of the day or night.
I've created a .desktop file and added this .desktop file to the "~/.config/autostart". I've also created another pair script/desktop without the loop to be on my dock, so I can click on it and change it on fly too.
Set the ugly up: save the script as wallpaperd somewhere and make it executable:
chmod +x wallpaperd
Now create a folder called Wallpaper inside the Pictures directory. Inside this Wallpaper folder create 4 more folders with the names afternoon, duskdawn, morning and night. Put the image files you wish in those 4 directories.
mkdir -p ~/Pictures/Wallpaper/morning
mkdir ~/Pictures/Wallpaper/afternoon
mkdir ~/Pictures/Wallpaper/night
mkdir ~/Pictures/Wallpaper/duskdawn
wallpaperd
#!/bin/bash
for (( ; ; ))
do
me="MyUser" # Change me!
morning="morning"
afternoon="afternoon"
dawn="duskdawn"
night="night"
dusk="duskdawn"
now="morning"
hour=$(date +%R | sed 's/\:.*//')
if [ "$hour" -ge 7 ] && [ "$hour" -lt 12 ]
then
now="morning"
elif [ "$hour" -ge 12 ] && [ "$hour" -lt 17 ]
then
now="afternoon"
elif [ "$hour" -ge 17 ] && [ "$hour" -lt 18 ]
then
now="duskdawn"
elif [ "$hour" -ge 18 ] && [ "$hour" -le 23 ]
then
now="night"
elif [ "$hour" -ge 0 ] && [ "$hour" -lt 6 ]
then
now="night"
elif [ "$hour" -ge 6 ] && [ "$hour" -lt 7 ]
then
now="duskdawn"
fi
imgPath="/home/$me/Pictures/Wallpaper/$now/"
imgFile=$(ls -1 $imgPath | shuf -n 1 | awk '{print $NF}')
export bgNow=""$imgPath$imgFile""
# Deepin desktop
/usr/bin/gsettings set com.deepin.wrap.gnome.desktop.background picture-uri "$bgNow"
# Gnome desktop
#/usr/bin/gsettings set org.gnome.desktop.background picture-uri "$bgNow"
sleep 420
done
Set the proper gsettings command for your desktop in script!
wallyd.desktop
** Autostart Path: /home/YOUR_USER/.config/autostart/wallyd.desktop**
[Desktop Entry]
Categories=System;
Comment=Change Wallpapers Agent
Exec=/home/$USER/Bin/wallpaperd
Icon=computer
Name=Wally Daemon
NoDisplay=false
Terminal=false
Type=Application
Edit the script's path to match the path where you saved the script.
No loop
To create a desktop icon without the loop, only to change the wally and quit do this:
Save the script as wallpaper (without the d at the end) and remove these lines:
for (( ; ; ))
do
done
Use the template above to create another .desktop file for this non looped wallpaper script. Change the Name and the Exec path for the non looped script.
Save this .desktop here:
/usr/share/applications/wally.desktop
Drag it to your taskbar or dock. Click it and it will change the wallpaper on the fly.
When setting the DBUS_SESSION_BUS_ADDRESS environment variable, the PID=$(pgrep gnome-session) command may return multiple values in certain cases. This can cause issues when setting the environment variable using the grep command.
To address this issue, you can use the pgrep command with the -t option to limit the search to a specific terminal. In this case, you can use PID=$(pgrep -t tty2 gnome-session) to limit the search to the second terminal (tty2) and ensure that only one PID is returned.
Therefore, for Ubuntu 22.04, you can modify the command to:
PID=$(pgrep -t tty2 gnome-session)
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-)
This should allow you to set the DBUS_SESSION_BUS_ADDRESS environment variable correctly, even when multiple gnome-session processes are running.
Thanks to Ray Foss for providing this solution in the comments to the original answer.

Resources