Why does my menu with select fail the first time? - bash

I tried to answer another SO question with a simple menu using the builtin select statement. The code displays names from /etc/passwd, and let you select a name by giving a number:
PS3="Enter a number: "
select name in $(cut -d: -f1 /etc/passwd) ; do
if [ -n "${name}" ]; then
break
fi
echo "Sorry, please enter a number as shown."
done
echo "Entry from passwd is: ${name}"
The works fine except for the very first time. When you give a correct answer the first time it will ask you to try again.
I tried to get a more detailed explanation of the first time, but I couldn't get a reproducable cook-book. When you copy-paste this code on your server, and give a correct answer you will probably have the same problem. When you repeat the command (from history or a new paste), the code shows now problem. I tried to get the problem again by logging out and logging in (sometimes it works) or rebooting.
I tried different ways to reproduce the problem in other situations (using different variable names, unsetting variables, using a slow list of values with select name in $(echo One; sleep 1; echo Two; sleep 2; echo Three; sleep 1); and opening a new shell.
I searched for other examples with select, but I can't find clues in other posts like https://stackoverflow.com/a/16750755/3220113 and https://askubuntu.com/a/1716.
I tried to fix my code with a sync and that seems to be a work-around:
PS3="Enter a number: "
select name in $(cut -d: -f1 /etc/passwd) ; do
# is sync needed here?
sync
if [ -n "${name}" ]; then
break
fi
echo "Sorry, please enter a number as shown."
done
echo "Entry from passwd is: ${name}"
I couldn't reproduce the error when I include the sync command. Is sync really a working patch and why do I need this here?
I do not need other ways to write a menu. I already found the graphical dialog Dialog from bash script and was looking for a simple replacement of my own over-complicated https://unix.stackexchange.com/a/115371/57293.

This problem only occurs when you type the commands interactively, not in a script. The reason is that the line you type after the select line is being used as the response to the prompt. Since if isn't in the menu, it reports an error. Then it doesn't execute the if command, because it was read as the response to the prompt.
It's not a problem in a script because the commands in the script are not used as standard input.

Related

How to parse through outputted strings of a custom CLI command in bash script?

I have a custom CLI command that executes and requires user to enter a password. If the password fails authentication, it will echo some message like "Authentication failed" and I would like to see if the outputted string contains some substring like "fail". However, I noticed that capturing the "output" of a command is not giving me the text it echos on the terminal screen. If I would like to read through all the text the command echoed which doesn't technically come into the "output" bucket, how would I go about doing that?
Example:
> custauth
Enter your password:
Checking if password is correct...
Authentication Failed. Please try again
In above example you can see that it doesn't have an "output" but generates text that is displayed to the user. I would like to capture those lines and check for a substring in them rather than its official output
Using $? only gives me the exit code but oftentimes the CLI command can fail gracefully and output 0, so I'll need to read through all the statements it echoed.
The process can be passed initially by checking the status correctly, which is through
#!/bin/bash
# We put here any code that we check for verification
#like list files to fast exapmle
ls
cpstatus=$?
if [ "$cpstatus" == 0 ]; then
echo "complete"
else
echo "not complete"
fi
wait # Until the upper commands is finish
Or if you know what you are achieving, you can check the way out through it, and compare if there is a part of the speech that shows us a specific sentence, for example, if we return a failure, you will add that to the condition
I will attach an example code I wrote to infer the name of the system, for example:
System=$(uname -a) #str=$(command)
Substring ='Linux'
if [[ "$System" == *"$Substring"* ]]; then
echo "Yes is contain $Substring"
fi
I hope the solutions helped you, good luck :)

Pass user inputs from one script to another during runtime

I have a requirement where ScriptA.sh has commands to ask for User's inputs and perform a set of actions. I want to automate this by creating another script which will read the questions asked from output of ScriptA.sh and provide the necessary values in runtime.
ScriptA.sh as follows :-
echo "Enter the CR Number"
read varnamecr
echo "CR Number is" $varnamecr
echo "Loading the config set. Choose Option From Below set
1.JAN
2.FEB
3.MAR"
read optionchoosen
echo "Option Choosen is :" $optionchoosen
echo "Will run the script/load configuration is this Ok ?[y/N]"
read userinput
echo "Proceed further, User has pressed ->"$userinput"<--Key"
How to write the second script to achieve this. Tried spawn and few other commands in the second script, but no luck. Please help me with this.
Since you're not specifying any shell in your tag, this is a possible, albeit crude, solution in ksh. It's using the coprocess capability of that shell (pretty sure it's not supported in bash although please don't quote me on that one)
#!/bin/ksh
./ScriptA.sh |&
while read -p Dummy; do
print $Dummy
case $Dummy in
"Enter the CR Number")print -p "CR123456"
;;
"3.MAR")print -p "3"
;;
"Will run the script"*)print -p "y"
;;
esac
done
The output gives :
Enter the CR Number
CR Number is CR123456
Loading the config set. Choose Option From Below set
1.JAN
2.FEB
3.MAR
Option Choosen is : 3
Will run the script/load configuration is this Ok ?[y/N]
Proceed further, User has pressed ->y<--Key
Will input remain same everytime? If so you can create wrapper of this script to provide required input.
cat wrapper
./ScriptA.sh <<!
123
2
y
!

Correct script construct to monitor RaspPi sound card output and execute CEC command to activate AV receiver?

Apologies in advance - I'm a complete beginner so the code below is probably a car crash but I'd really appreciate any help if anyone can spare a minute?
Aim - I have my RaspPi as a music source to my AV receiver. I've installed libcec onto RPi and the receiver is cec enabled so I am trying to write a script that sends the 'active source' command to the AVR whenever the sound card is active.
The active source command:
echo 'as' | cec-client -d 1 -s
The script to return sound card status:
grep RUNNING /proc/asound/card*/pcm*/sub*/status
I've tried to represent the following logic:
1. If music is playing - send active command (turns on AVR with correct channel) and create the empty file 'yamaha-yes'
2. If yamaha-yes file exists check that music is playing - if not then remove 'yamaha-yes' file.
The idea with the yamaha-yes file is to prevent the script from continually sending the active source command whilst music is playing - it just needs sending once, so I've tried to write it so that the presence of the file whilst music playing leads to no further action.
I was hoping to use the 'watch' command from boot to have this running continually.
#!/bin/bash
musicon="$( grep RUNNING /proc/asound/card*/pcm*/sub*/status )"
file="/etc/yamaha-yes"
if [ -e $file ] ; then
if [ "$musicon" = "" ] ; then
sudo rm /etc/yamaha-yes
fi
else
if [ "$musicon" ] ; then
echo 'as' | cec-client -d 1 -s
sudo touch /etc/yamaha-yes
fi
fi
The current error returned is 'line 8: [: too many arguments'. But I suspect there is a lot more wrong with it than that and was hoping to check I was on the right track before flogging it any further!
Thanks in advance! Tom
EDIT
Some changes made in line with Marc's advice and the code now seems to work - though I realise it still isn't the most elegant read! Perhaps there is a better way of scripting it?
The short answer is that you are missing quotes [ "$musicon" = 'RUNNING' ]. However, there are problems with how your grepping is going to work. When audio is running you could end up with multiple RUNNING values (giving the string RUNNING\nRUNNING or longer) returned which will not equal RUNNING. Also, $musicoff is broken because the ! command doesnt change a commands output, only its return value. "$musicoff" will equal RUNNING only if audio is running, which is the exact opposite of what you want. Fortunately, the fix also simplifies the script.
The behavior of grep is that it returns 0 (true) if it found the search text anywhere, otherwise it returns 1 (false). So, instead of comparing the output of grep against a specific value (which might fail in certain cases) use the return value of grep directly:
musicon="grep RUNNING /proc/asound/card*/pcm*/sub*/status"
if $musicon ; then
echo Music is on;
fi
if ! $musicon; then
echo Music is off;
fi

Bash Script Help "if (user input) = then (var) ="

I a writing a script to cherry pick all open changes from Gerrit. I found one that works sort of, though I need to be able to change inputs so that I do not have a script for each repo hardcoded with that specific repo's information.
#! /bin/sh
REMOTE="${1-review}"
ssh -p 29418 user#gerrit.remote.com gerrit query --format=text --patch-sets status:open branch:XXX project:XXX | grep revision: | awk '{print $2;}' | while read ID
do
git fetch "${REMOTE}" && git cherry-pick "${ID}"
done
Now I have been able to pick open changes successfully but I am trying to make it so I can pass input to change username, branch, project and remote. With the current method I need to enter my username, project, branch, and remote manually into the script. Then it is only good for that specific repo.
I have been having trouble with if/then statements. I know as it looks now none of the things I am asking for are coded, I wanted to provide someone with a working model though.
I did change username and the particular details, easy enough for someone to use this script themselves to cherry-pick by inserting the requisite information.
If I do something like this:
PROJECT="$1"
if [ "$1" = "XX" ]; then
"$PROJECT="project:name of project"
Then bash returns XX command not found. I am not trying to make it a command I want it to be input to be inserted into the ssh command later on. Also I am trying to not only use if but also else if so that PROJECT can be whatever is input.
I think I am almost there though completely stumped at this point.
Assume $1 is equal to "XX". Your code:
PROJECT="$1"
will assign PROJECT=XX. Next,
if [ "$1" = "XX" ]; then
is true, "then" clause will be executed. This clause is:
"$PROJECT="project:name of project"
that tries to execute command "XX=...", causing "command not found"
Suggestion, remove $ on this line, as in:
PROJECT="project:name of project"

bash overriding a single line in a text file with another while using variables

Overview: I am trying to make a script that will take a list of machines and manually update their /etc/shadow files with a new root passwd. I know this isn't the best method but my boss wants this process automated. we are using a application called puppet for 90% of the update but some machines failed the update or can't have puppet installed, hence this dodgy fix.
(sorry for any stupid errors its only my 3rd week using any unix product, I have been a windows admin my whole life)
Issue:
I need to ssh into the PC's update the /etc/shadow file but only change the root user (not all systems have the same users and I don't want to remove any of those users in the process) I have gotten as far as being able to extract the current user in line 1 through ssh, then check if that user is indeed the root user but I am stuck on then updating the /etc/shadow file on the new machine as my boss has asked that the following standards happen.
I can't have any real user interaction in the script, so no manually typing the new passwd.
I am not allowed to have the new passwd displayed anywhere in clear text (inside the script or in another file)
Ok hopefully that's enough info onto the code.
root=user
unknown='unknown.txt'
filelines=`cat $unknown`
prod='new-shadow'
ohf='option-one-holding-file'
pel=prod-errorlog
for line in $filelines ; do
echo "Attempting to fix $line please wait"
ssh -oBatchMode=yes -l $user $line "awk '{if (NR==1) print \$0}' /etc/shadow" >> $ohf
if grep -q "root:" $ohf ; then
echo "root user located updating to produtcion password"
# ** This is the line that doesn't work **
ssh -oBatchMode=yes -l $user $line "sed -i '1s/.*/$prod/' /etc/shadow"
else
echo "unable to find root user this will require a manual fix this server will be listed in
the prod-errorlog file"
echo "$line" >> $pel
fi
done
The line in bold the sed line doesn't work I know why it doesn't work but I have no idea how to fix it at all, thank you to anyone who takes the time to look at this, I know the codes a bit of a mess, please forgive me.
To replace only the first line:
"echo '$prod' > /etc/shadow.new; tail -n +1 /etc/shadow >> /etc/shadow.new; mv -f /etc/shadow.new /etc/shadow"
Sorry for my previous wrong argument wrong: The '$prod' part in your script is correct, and is expanded OK. Yet $prod contains many reserved characters for regular expressions. Now this new version just create a new file (replacing the first line) and then move/overwrite on to the target one.

Resources