ssh instruction interrupt a while cycle? - bash

I'm trying to deploy a cluster with a script which uses a yaml file. Except for an entry called "RaftFS" each yaml entry represents a machine to deploy. I don't understand why the script does only one while cycle if the ssh command is executed (even if the command is a simple ls !) but if I delete it then everything is fine and it does a number of cycle equals to the number of machines defined in the yaml file!
cat RaftFS/servers.yaml | shyaml keys-0 |
while read -r -d $'\0' value; do
if [ ! $value == "RaftArgs" ]; then
address=$(cat RaftFS/servers.yaml | shyaml get-value $value.machineIP | xargs -0 -n 1 echo)
username=$(cat RaftFS/servers.yaml | shyaml get-value $value.username | xargs -0 -n 1 echo)
password=$(cat RaftFS/servers.yaml | shyaml get-value $value.password | xargs -0 -n 1 echo)
#uploading my fingerprint (in order to use pssh)
echo $address $username $password
echo "uploading my fingerprint on $username#$address $password"
sshpass -p $password ssh-copy-id -oStrictHostKeyChecking=no $username#$address
echo "creating RaftFS"
ssh $username#$address echo "MACHINE=$value vagrant up">>vagrantscript.sh
fi
echo $address $username $password
done

I think there is no issue with the ssh command but it is a delimiter's issue
I've played a little with read -r -d $'\0' and these are the results
echo "a\0b\0c" | while read -r -d $'\0' var; do echo $var; done
prints
a
b
and
echo "a\0b\0c\0" | while read -r -d $'\0' var; do echo $var; done
prints
a
b
c
I assume there is some difference in the end line when the $value == "RaftArgs"

The standard input to the while loop is also the standard input to every command within the while loop. ssh reads from standard input in order to pipe the data to the remote command. It's probably consuming the data intended for the read statement.
You can redirect the ssh command's input:
ssh $username#$address ... >>vagrantscript.sh < /dev/null
Or you can run ssh with the "-n" flag to prevent reading from stdin:
ssh -n $username#$address ... >>vagrantscript.sh

Related

ssh when invoked with variables form while loop not working

I am running into an issue where I am comparing two files (alert.txt and env.txt) and based on common value, I am pulling complete line data from env.txt based on matching entry. I am reading these values into while loop and then invoking a function as follows. the ssh call is not working and also the while loop inside start_admin not working
#!/bin/bash
start_admin()
{
ssh -n -f $user#$host "sh -c 'cd $domain; ./script.sh > /dev/null 2>&1'"
while !(netstat -na | grep -E $host:$port|grep -E LISTEN) 2>/dev/null
sleep 30
do
echo "waiting"
done
echo "started"
}
grep -Ff alert.txt env.txt | (while IFS=" " read -r r1 r2 r3 r4 r5
do
user=$r2
host=$r3
domain=$r4
port=$r5
done
start_admin $user $host $domain $port
)
and contents of alert.txt is:
env2
env3
and that of env.txt is :
env1 user1 host1 /app/domain1/ port1
env2 user2 host2 /app/domain2/ port2
env3 user3 host3 /app/domain3/ port3
I could solve this with multiple if else loops, but that is not a desired solution, please guide me in right direction as to what is missing ?
Use join instead of grep here to avoid false positives
Because your while read loop completes before you run start_admin, you only launch it once (done should be AFTER start_admin)
In start_admin, don't use $user, $host and so on, use $1, $2 (or use them but don't pass them as parameters when calling the function)
I'm not sure exactly what you try to achieve, but here is a revised version already.
#!/bin/bash
start_admin()
{
sanitized_domain=${domain//'"'/'\"'}
ssh -n -f "$user#$host" "sh -c 'cd \"$sanitized_domain\"; ./script.sh >/dev/null 2>&1'"
while ! netstat -na | grep -q " $host:$port .*LISTEN"; do
echo waiting
sleep 30
done
echo started
}
join alert.txt env.txt | while IFS=' ' read -r env user host domain port; do
start_admin
done
)

How to run commands off of a pipe

I would like to run commands such as "history" or "!23" off of a pipe.
How might I achieve this?
Why does the following command not work?
echo "history" | xargs eval $1
To answer (2) first:
history and eval are both bash builtins. So xargs cannot run either of them.
xargs does not use $1 arguments. man xargs for the correct syntax.
For (1), it doesn't really make much sense to do what you are attempting because shell history is not likely to be synchronised between invocations, but you could try something like:
{ echo 'history'; echo '!23'; } | bash -i
or:
{ echo 'history'; echo '!23'; } | while read -r cmd; do eval "$cmd"; done
Note that pipelines run inside subshells. Environment changes are not retained:
x=1; echo "x=2" | while read -r cmd; do eval "$cmd"; done; echo "$x"
You can try like this
First redirect the history commands to a file (cut out the line numbers)
history | cut -c 8- > cmd.txt
Now Create this script hcmd.sh(Referred to this Read a file line by line assigning the value to a variable)
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "Text read from file: $line"
$line
done < "cmd.txt"
Run it like this
./hcmd.sh

reading from serial using shellscript

I have a serial port device that I would like to test using Linux command line.
And if I run the following command from terminal, it gives output
cat < /dev/ttyS0 &
This command opens the serial port and relays what it reads from it to its stdout.So, I tried it from shell script file but it is not working
fName="test.txt";
awk '
BEGIN { RS = "" ; FS = "\n" }
{
address = '/dev/ttyS0';
system("cat < " address );
}
END {
}' "$fName";
But it is not working and giving output.How can I listen to communication between a process and a serial port? Thanks
Using awk timeouts
I've successfully read something under dash, be using GAWK_READ_TIMEOUT environment variable:
out=`GAWK_READ_TIMEOUT=3000 awk '{print}' </dev/ttyS0 & sleep 1 ; echo foo >/dev/ttyS0`
On my terminal, this output:
echo "$out"
foo
Password:
or
echo "$out"
Login incorrect
testhost login:
Using bash timeouts
You could use FD under bash as:
exec 5>/dev/ttyS0
exec 6</dev/ttyS0
while read -t .1 -u 6 line;do
echo $line
done
or, to read unfinished lines:
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
echo
So you could:
echo 'root' >&5
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
echo 'password is 1234' >&5
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
... Once done, you could close FD by running:
exec 6<&-
exec 5>&-
Sample bash poor terminal script
I've logged and test some commands with:
#!/bin/bash
exec 5>/dev/ttyS0
exec 6</dev/ttyS0
readbuf() {
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
};
while [ "$cmd" != "tquit" ] ;do
readbuf
read cmd
echo >&5 "$cmd"
done

A script to find all the users who are executing a specific program

I've written the bash script (searchuser) which should display all the users who are executing a specific program or a script (at least a bash script). But when searching for scripts fails because the command the SO is executing is something like bash scriptname.
This script acts parsing the ps command output, it search for all the occurrences of the specified program name, extracts the user and the program name, verifies if the program name is that we're searching for and if it's it displays the relevant information (in this case the user name and the program name, might be better to output also the PID, but that is quite simple). The verification is accomplished to reject all lines containing program names which contain the name of the program but they're not the program we are searching for; if we're searching gedit we don't desire to find sgedit or gedits.
Other issues I've are:
I would like to avoid the use of a tmp file.
I would like to be not tied to GNU extensions.
The script has to be executed as:
root# searchuser programname <invio>
The script searchuser is the following:
#!/bin/bash
i=0
search=$1
tmp=`mktemp`
ps -aux | tr -s ' ' | grep "$search" > $tmp
while read fileline
do
user=`echo "$fileline" | cut -f1 -d' '`
prg=`echo "$fileline" | cut -f11 -d' '`
prg=`basename "$prg"`
if [ "$prg" = "$search" ]; then
echo "$user - $prg"
i=`expr $i + 1`
fi
done < $tmp
if [ $i = 0 ]; then
echo "No users are executing $search"
fi
rm $tmp
exit $i
Have you suggestion about to solve these issues?
One approach might looks like such:
IFS=$'\n' read -r -d '' -a pids < <(pgrep -x -- "$1"; printf '\0')
if (( ! ${#pids[#]} )); then
echo "No users are executing $1"
fi
for pid in "${pids[#]}"; do
# build a more accurate command line than the one ps emits
args=( )
while IFS= read -r -d '' arg; do
args+=( "$arg" )
done </proc/"$pid"/cmdline
(( ${#args[#]} )) || continue # exited while we were running
printf -v cmdline_str '%q ' "${args[#]}"
user=$(stat --format=%U /proc/"$pid") || continue # exited while we were running
printf '%q - %s\n' "$user" "${cmdline_str% }"
done
Unlike the output from ps, which doesn't distinguish between ./command "some argument" and ./command "some" "argument", this will emit output which correctly shows the arguments run by each user, with quoting which will re-run the given command correctly.
What about:
ps -e -o user,comm | egrep "^[^ ]+ +$1$" | cut -d' ' -f1 | sort -u
* Addendum *
This statement:
ps -e -o user,pid,comm | egrep "^\s*\S+\s+\S+\s*$1$" | while read a b; do echo $a; done | sort | uniq -c
or this one:
ps -e -o user,pid,comm | egrep "^\s*\S+\s+\S+\s*sleep$" | xargs -L1 echo | cut -d ' ' -f1 | sort | uniq -c
shows the number of process instances by user.

How to read from 2 files

I try to make a script to connect with MySQL.
Reading hosts from one file and the MySQL password from another file, but I have a problem.
When I try to execute the script it's returning me this error:
./do: line 15: syntax error: unexpected end of file
The code is like this:
#!/bin/bash
FILE=$1
INFO=$2
cat $FILE | while read HOST;
cat $INFO | while read INFO;do
DBS=`mysql -u root -p $INFO -h $HOST --connect_timeout=4 -Bse'show databases' | wc -l`
if [ "$DBS" -gt "0" ]; then
echo $HOST - mysql - $DBS >> log.sql
fi
sleep 0.1
done
Where is my mistake ?
Salut DragoČ™,
You can't use two while loops (even after you fix the syntax error) to read from two different files at the same time.
Instead, you can use paste to combine the two files first, then execute your loop:
#!/bin/bash
hostnames="$1"
passwords="$2"
while IFS=$'\t' read host password; do
dbs=$(mysql -u root -p "$password" -h "$host" --connect_timeout=4 -Bse'show databases' | wc -l)
[ $dbs -gt 0 ] && echo "$host - mysql - $dbs" >> log.sql
done <<<"$(paste "$hostnames" "$passwords")"
This script will correctly handle filenames with spaces, as well as hostnames and passwords containing spaces.

Resources