below you will find a simple code.
i am calling function input if the read input is Y
however read command in input function never executes.
#!/bin/bash
input() {
read -p "Press any key to continue..." # read input (this part never executed)
}
mg() # prompt user for y=true, n=false
{
[ $FORCE ] && echo>&2 "$* (forced)" && return
while read<&2 -p "$* (Y/N)? [Y] " YN
do
[ -z "$YN" -o "$YN" = Y ] && return
[ "$YN" = N ] && return 1
done
err "failed to read from YN $*"
}
if mg "Enter Input?"
then in="yes" | input # call function input if mg return Y
else in="no"
fi
The call to input is getting its standard output from the parameter assignment. As soon as the assignment completes, its (nonexistant) standard output is closed, which read interprets as EOF which causes the read to return.
Use a semi-colon instead.
if mg "Enter Input?"
then in="yes"; input
else in="no"
fi
(Removing the pipeline also allows in to be assigned in the current shell, not in the pipeline-induced subshell.)
Your line
then in="yes" | input
is the issue. You don't want to pipe the assignment of in into input. Separate the commands with a semicolon or a newline:
then in="yes"; input
Related
I want to backup mikrotiks via scp. This script loops through the hosts from the hosts.txt. One by one, connects to each device from the list. Does backup and all manipulations. If at some stage it was not possible to connect to the device, then an empty backup is formed, which is then sent to the cloud.
I want to check. If it was not possible to connect to the host, then write this host into a variable, line by line, and go to the next device. Next, I will notify about all failed connections.
The problem is that only the first error is written to the variable, all subsequent ones are ignored.
Tell me who knows what.
#!/bin/bash
readarray -t hosts < hosts.txt
DATE=$(date +'%Y-%m-%d_%H-%M-%S')
ROS='<br>'
ERR=( )
#Get values from main list
for host in ${hosts[*]}
do
#Get values from sub list
hostname=($(echo ${host} | tr "_" " "))
echo ${hostname[0]} - ${hostname[1]}
#connect & backup & transfer & archive & rm old files & moove to cloud
if ssh backup#${hostname[0]} -C "/system backup save name=${hostname[1]}_$DATE"; then
scp backup#${hostname[0]}:"${hostname[1]}_$DATE.backup" ./
ssh backup#${hostname[0]} -C "rm ${hostname[1]}_$DATE.backup"
tar -czvf ./${hostname[1]}_$DATE.tar.gz ${hostname[1]}_$DATE.backup
scp ./${hostname[1]}_$DATE.tar.gz my#cloud.com:/var/www/my.cloud.com/backups/mikrotik/
rm ${hostname[1]}_$DATE.backup ${hostname[1]}_$DATE.tar.gz
ROS=$ROS${hostname[1]}"<br>"
else
ERR+=(${hosts[*]} "is not ready")
fi
done
hosts.txt
10.10.8.11_CAP-1
10.10.9.12_CAP-2
10.10.10.13_CAP-3
As I noted in the comments, you're misusing the array notation. Your line ERR=(${hosts[*]} "is not ready") should be ERR+=(${hosts[*]} "is not ready") and you should define ERR as an array, not a scalar: ERR=( ) for example, or declare -a ERR. Similarly with ROS.
Here's a test script that avoids all the ssh and scp work to demonstrate that lists of passing and failing hosts work — that the arrays hosts, ROS and ERR are handled correctly.
Note the use of "${ERR[#]}" with double quotes and # instead of no quotes and *. The difference matters because the values in the array contain spaces. Try the alternatives. Note, too, that printf always prints, even when there is no argument corresponding to the %s in the format string. Hence the check on the number of elements in the array before invoking printf.
#!/bin/bash
# Needs Bash 4.x - Bash 3.2 as found on Macs does not support readarray
# readarray -t hosts < hosts.txt
hosts=( passed-1 failed-2 passed-3 failed-4 passed-5 )
declare -a ERR
declare -a ROS
status=passed
for host in "${hosts[#]}"
do
if [ "$status" = "passed" ]
then ROS+=( "$host $status" ); status="failed"
else ERR+=( "$host $status" ); status="passed"
fi
done
# Brute force but handles empty lists
for passed in "${ROS[#]}"
do printf "== PASS == [%s]\n" "$passed"
done
for failed in "${ERR[#]}"
do printf "!! FAIL !! [%s]\n" "$failed"
done
# Alternative - better spread over multiple lines each
if [ "${#ROS}" -gt 0 ]; then printf "== PASS == [%s]\n" "${ROS[#]}"; fi
if [ "${#ERR}" -gt 0 ]; then printf "!! FAIL !! [%s]\n" "${ERR[#]}"; fi
Output:
== PASS == [passed-1 passed]
== PASS == [passed-3 passed]
== PASS == [passed-5 passed]
!! FAIL !! [failed-2 failed]
!! FAIL !! [failed-4 failed]
== PASS == [passed-1 passed]
== PASS == [passed-3 passed]
== PASS == [passed-5 passed]
!! FAIL !! [failed-2 failed]
!! FAIL !! [failed-4 failed]
I'm sorry there are so many failures to backup your data!
I wanted to assign variable name separated by comma based on user input using function.
I will get the user input using below script and it will call function for variable assignment
while [ "$ans" != "q" ]
do
clear
echo "Choose your subject"
echo "Press q once done"
echo " 1.Science"
echo " 2.Maths"
echo " 3.English"
...
read ans
case $ans in
1) clear
Science;;
2) clear
Maths;;
3) clear
English;;
....
esac
done
clear
subjects=""
Science()
{
subjects+="$subjects Science"
}
Maths()
{
subjects+="$subjects Maths"
}
English()
{
subjects+="$subjects English"
}
At the end I wanted to have variable subjects to have option choose by the user:
Etc:
Science,Maths
Maths,English
Science,English
English
In bash, the function definition must be placed before any calls to the function.
The line subjects="" must be placed before the while loop. Otherwise its value will get lost (will be set to empty string) on exit from the loop.
The += operator causes double concatenation in the line subjects+="$subjects Science", since the right hand side contains already the expansion of the subjects variable. Either subjects="$subjects Science", or subjects+=" Science" must have been used (the same is also true for other lines in which the += operator is used). Besides, since a comma separated list is desired, a , character must be used while concatenating strings instead of space character. For example: subjects="$subjects,Science"
So a corrected script could be like this:
#!/bin/bash
subjects=""
Science() {
subjects="$subjects,Science"
}
Maths() {
subjects="$subjects,Maths"
}
English() {
subjects="$subjects,English"
}
while [ "$ans" != "q" ]; do
clear
echo "Choose your subject"
echo "Press q once done"
echo " 1.Science"
echo " 2.Maths"
echo " 3.English"
read -r ans
case $ans in
1) Science;;
2) Maths;;
3) English;;
esac
done
subjects=${subjects:1} # to remove the leading ',' character
echo "Your selections are $subjects"
Note: I wouldn't normally use a function just to append a simple string to a variable.
I’m trying to build a script that asks for a time clock number and a DC number from the user running the script, which I’m intending to fill in the Xs for
/u/applic/tna/shell/tc_software_update.sh tmcxx.s0xxxx.us REFURBISHED
However, I am stumped as to how to have the user’s input fill in those Xs on that command within the script. This script is in its earliest stages so it’s very rough right now lol. Thank you for responding. Here’s the scripts skeleton I’m working on:
#!/bin/bash
#This server is intended to speed up the process to setup timeclocks from DC tickets
#Defines time clock numbers
timeclocks="01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35"
#Defines DC number
echo “What is the DC number?”
read dc
#Defines TMC number
echo "What is the Time Clock number?"
read number
if $number == $timeclocks && $dc == ???; then
/u/applic/tna/shell/tc_software_update.sh tmcxx.s0xxxx.us REFURBISHED
Do you mean invoking $ /u/applic/tna/shell/tc_software_update.sh tmc${number}.s0${dc}.us REFURBISHED?
Consider the following snippet:
[test.sh]
read x
read y
echo "x=${x}, y=${y}"
$ sh test.sh
5
4
x=5, y=4
Further on, you can use command line arguments ($1, $2 etc.) instead of the user input.
Modelling this on your script:
timeclocks=( {1..35} )
printf '%s' "DC number: "; read dc
printf '%s' "Time Clock number: "; read tmc
tmc=$( printf '%02d' "$tmc" )
dc=$( printf '%04d' "$dc" )
tmc_valid=$( for t in ${timeclocks[#]}; do \
[[ $tmc -eq $t ]] && echo true && break; \
done )
[[ "$tmc_valid" = "true" && "$dc" = "???" ]] && \
/u/applic/tna/shell/tc_software_update.sh tmc${tmc}.s0${dc}.us REFURBISHED
Hi I am new to bash so please excuse me if I have a really silly/easy question. I am writing a script which allows the user to change their region (for wireless). What I am wanting to do is put a check in place, so if they type in an incorrect value, it brings up the prompt again to input the region. I want to do this by checking if the output of the command sudo iw reg set $reg, if it is a correct input, there is no output. But if it is a wrong input, it gives an error message. I tried to do this but im getting an error:
#!/bin/bash
echo "Please set a region: "
read reg
if [(sudo iw reg set $reg) -ne 0]; then
echo "Please set a valid region: "
read reg
else
echo "Setting reg as $reg"
sudo iw reg set $reg
fi
Thanks in advance
You can use the -z test, type help test in Bash to learn more (test is the same as the [ command).
You should only call iw reg set once, unless it fails.
echo "Please set a region: "
while true # infinite loop
do
# read in the region:
read reg
# try the command, and catch its output:
output=$( sudo iw reg set "$reg" 2>&1 )
if [ -z "$output" ]
then
# output is empty - success - leave the loop:
break
else
# output is non-empty - continue:
echo "Please set a valid region. "
fi
done
This snippet checks the success condition you gave in your question (empty output), but it should be noted that usually exit codes should be used if possible.
Note the 2>&1 operator redirecting stderr to stdout so any output on either file descriptor will be considered a failure.
You can use read in a while loop:
while read -r -p "Please set a valid region: " reg; do
[[ -z "$(sudo iw reg set $reg)" ]] && break
done
help read gives this:
-r do not allow backslashes to escape any characters
-p prompt output the string PROMPT without a trailing newline before
attempting to read
$(...) is command substitution to execute a command and return output
-z returns true when given string argument (output of iw command) is empty
In the following very simple ksh script example, I need to ask if func1 results equal to 4 ,
This is what I did in the example but this script does not print the "function result = 4" as I expected it to.
What do I need to change in the [[......]] in order to print the "function result = 4"
Remark - func1 must be in the [[.....]]
#!/bin/ksh
func1()
{
return 4
}
[[ ` func1 ` = ` echo $? ` ]] && print "function result = 4"
You need
#!/bin/ksh
func1()
{
print -- 4
}
[[ $(func1) = 4 ]] && print "function result = 4"
OR
#!/bin/ksh
func1()
{
return 4
}
func1 ; [[ $? == 4 ]] && print "function result = 4"
There are several issues in the code that you present, so let me try to explain (You're making it more complicated than it need be).
No. 1 is your use of back-ticks for command substitution, these have been deprecated in the ksh language since ~ 1995! Use $( ... cmd ) for modern cmd-substitution. We often see backticks listed as a nod to portability, but only scripts written for systems where the Bourne shell is the only shell available require the use of backticks. (well, I don't know about dash or ash, so maybe those too).
No 2. is that $? gets set after ever function or command or pipeline is executed and is the return code of that last command. It is a value between 0-255. When you have code like cmd ; rc=$? ; echo $? ; you're now echoing the status of the assignment of rc=$? (which will almost always be 0), AND that is why you will see experienced scriptors save the value of $? before doing anything else with it.
Recall that command-substitution uses what ever is the output of the $( ... cmd ...) or backtics enclosed command while return sets the value of $? (until the very next command execution resets that value).
I hope this helps.
Function does return 4. The operator `` (backticks) ignores the result value, and returns the function's stdout instead (in your case an empty string, since func1 did not print anything to stdout).
And
`echo $?`
is just over-complicated way of saying
$?