Why is bash swallowing characters - bash

I have the following code in a bash script:
ssh_management
if [[ "$PROMPT_SSH" == "true" ]]; then
read -p "Generate and Install SSH keys? [y/n]" -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
generate_container_ssh
install_ssh_keys
check_ssh_state
else
echo "Skipping SSH key install"
fi
fi
install_docker
But the script errors with:
Skipping container SSH key install
bash: line 102: nstall_docker: command not found
It appears that the read command is somehow swalling the first character on install_docker
Not sure why this is happening or how it fix it.

Seems something was messing up my STDIN.
read -p "Generate and Install SSH keys? [y/n]" -n 1 -r < /dev/tty
Fixes the problem.

Related

While loop in bash script execute only once [duplicate]

This question already has answers here:
bash cycle breaks when calling ssh command in the loop
(2 answers)
Closed 3 months ago.
I have a given shell script:
#!/bin/bash
USERNAME=xenobot
SSH_KEY_LOCATION="~/.ssh/id_rsa"
FILE="server.info"
while read -r server_ip package_type; do
if [[ $package_type == "deb" ]]; then
echo "For $server_ip package type is $package_type"
DPKG_FILE_PATH=$(ls ./src/dpkg/dpkg-*)
DPKG_FILE=$(basename $DPKG_FILE_PATH)
echo "$DPKG_FILE located at $DPKG_FILE_PATH will be transfered via SSH to server"
scp ./src/dpkg/${DPKG_FILE} $USERNAME#$server_ip:/tmp
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo dpkg -i *.deb"
elif [[ $package_type == "rpm" ]]; then
echo "For $server_ip package type is $package_type"
RPM_FILE_PATH=$(ls ./src/rpm/rpm-*)
RPM_FILE=$(basename $RPM_FILE_PATH)
echo "$RPM_FILE located at $RPM_FILE_PATH will be transfered via SSH to server"
scp -o 'StrictHostKeyChecking no' ./src/rpm/${RPM_FILE} $USERNAME#$server_ip:/tmp
echo "$RPM_FILE has been successfuly transfered to server!"
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo rpm -ivh --force $RPM_FILE"
fi
done <"$FILE"
And I have following file server.info, from which previous shell script reads:
213.136.80.123 rpm 213.136.91.102 rpm
The problem with it, as it is execute only once from first line, and just stops there without executing the second line
I have tried just to print output from this server.info using the same while read -r and it seems that outputs just fine like this
#!/bin/bash
USERNAME=xenobot
SSH_KEY_LOCATION="~/.ssh/id_rsa"
FILE="server.info"
while read -r server_ip package_type; do
if [[ $package_type == "deb" ]]; then
echo "For $server_ip package type is $package_type"
elif [[ $package_type == "rpm" ]]; then
echo "For $server_ip package type is $package_type"
fi
done <"$FILE"
Output:
213.136.80.123 rpm 213.136.91.102 rpm
So it seems like it outputs two separate lines, however I do not know why it runs script only once in bigger example
Try using a different file descriptor for your list other than 0, 1, or 2 (in this case it's set to 3).
while read -r server_ip package_type <&3; do
...
done 3<"$FILE"
If that works, then what's going on here is that ssh -- being an interactive program -- is sucking up your STDIN input.

How to check connection to a list of servers in bash?

Im trying to check connections for a list of servers. I want to loop through the list, check if a connection works and if yes, do some stuff, if not, echo out a problem message.
My problem is:
the script stops at the first node without echoing the $?.
So, whats wrong with my for-loop?
These vars are included from a config file:
$nodes is a list of server IPs like 1.1.1.1,2.2.2.2,10.10.10.10
$user is one string
for node in $(echo $nodes | sed "s/,/ /g")
do
echo "Checking Node: $node"
ssh -q -o ConnectTimeout=3 $user#$node echo ok
echo $?
if [[ $? != 0 ]]
then
echo "Problem in logging into $node"
else
# do some stuff here
fi
done
EDIT #1:
for node in $(echo $nodes | sed "s/,/ /g")
do
echo "Checking Node: $node"
ssh -q -t -o ConnectTimeout=3 $user#$node "echo ok"
retcode=$?
echo $retcode
if [[ "$retcode" -ne 0 ]]
then
echo "Problem in logging into $node"
else
echo "OK"
fi
done
It is because ssh first asks you to validate The authority of the host and If you accept the authority it will ask for password. That is why your command does not return to shell and waits for input.
If your intention is just validating ssh connection, then you may consider to use
telnet <your_host> <port> < /dev/null
But if your intend is to run some commands you need a trust relationship between hosts. In that case you can use:
Execute this commands:
ssh-keygen -t rsa
then
ssh-copy-id -i root#ip_address
Now you can connect with
ssh <user>#<host>
Furher information
You can add -tto make virtual terminal and add quotes on command:
ssh -q -t -o ConnectTimeout=3 ${user}#${node} "echo ok"
Also use -ne instead of != which is for compare strings
if [[ "$?" -ne 0 ]]
Also echo $? mess the return code. You should use something like:
ssh -q -t -o ConnectTimeout=3 ${user}#${node} "echo ok"
retcode=$?
echo $retcode
if [[ "$retcode" -ne 0 ]]
You can rewrite ssh command like this to avoid problems with ssh host keys
ssh -q -t -o StrictHostKeyChecking=no -o ConnectTimeout=3 ${user}#${node} "echo ok"

How can I prompt the user inside a while read loop? [duplicate]

This question already has answers here:
Read user input inside a loop
(6 answers)
How to read from user within while-loop read line?
(3 answers)
Closed 5 years ago.
For my purpose, I need to execute a shell command, achieve the output, and for each line ask user for prompt.
The problem is that on the read prompt, stdin buffer isn't empty
this is my code:
#!/bin/sh
git branch -a | sed 's/remotes\/origin\///g'
echo "############################"
git branch -a | sed 's/remotes\/origin\///g' | while read line
do
if [[ "$line" != *develop* ]] \
&& [[ "$line" != *master ]] \
&& [[ "$line" != *release/* ]] \
&& [[ "$line" != *hotfix* ]]
then
read -r -p "Do you want to delete branch $line <y/N>?" prompt
echo $prompt
fi
done
The line:
read -r -p "Do you want to delete branch $line <y/N>?" prompt
does not even display to video, and prompt variable show the result of line variable above.
How can I solve this problem?
Use a FD other than 0 (stdin), to leave the original stdin free for input from the user:
#!/usr/bin/env bash
# ^^^^- NOT /bin/sh; also, do not run with "sh scriptname"
while read -r line <&3; do
line=${line#remotes/origin/} # trim remotes/origin/ w/o needing sed
case $line in
*develop*|*master|*release/*|*hotfix*) continue ;;
*) read -r -p "Do you want to delete branch $line <y/N>?" prompt
echo "$prompt" ;;
esac
done 3< <(git branch -a)
Here, we're using FD 3 for output from git, such that FD 0 is still stdin, available to read from the user; and then redirecting <&3 on the explicit read where we want content from git.

Pseudo-input for Bash's "read" variable

I have a script listening for a user input like that.
read -p "Run? (y/[n]) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
[..]
fi
Is there a way (upon executing the script) to already send the value which
read is going to read and handle?
your_script.sh <<< "Y"
This also supports multiple read's
your_script.sh <<< "YNYYNNY"

Get return code of ssh command in bash

I need to get the return/error code of an ssh command in my bash script. The command uses applescript to move a file on the remote machine to the trash. This is part of a larger script:
ssh $login "bash -s" <<-EOF
error=(osascript -e "tell application \"Finder\" to move POSIX file \"$remote_filepath\" to trash");
[[ -n "$error" ]] && { echo -e "\nCannot move file to trash on remote mac"; exit 1; };
EOF
# echo $?; exit
[[ $? -ne 0 ]] && exit 1
# more code ...
My aim is to have the ssh command exit with code 1 if the osascript fails, so I can catch the error and abort the rest of the script.
The ssh runs successfully, the file is indeed moved to the trash. Apparently osascript runs fine, because the error message is not shown. Still, the ssh return code is 1 (I checked it with the echo $? statement. This is where I'm stuck. I'd appreciate any insight on what's wrong here.
The problem is that the [[ -n "$error" ]] command sets the error code to 1. You need to use the negation of that test. Try:
[[ -z "$error" ]] || { echo -e ...
After all the problems with osascript not returning a real error code I decided to just check if the file was moved at all, like this:
# move file to trash on remote mac
ssh $login "bash -s" <<-EOF
osascript -e "tell application \"Finder\" to move POSIX file \"$remote_filepath\" to trash" > /dev/null;
EOF
[[ ! -e "$remote_filepath" ]] || { printf "\nCannot move file to trash on remote mac" && exit 1; }
Just seems simpler and easier to maintain for me. Thanks everyone for the input!

Resources