Bash script that allows one word as user input - linux

Made a script that the user gives a "parameter" and it prints out if it is a file, directory or non of them. This is it :
#!/bin/bash
read parametros
for filename in *
do
if [ -f "$parametros" ];
then
echo "$parametros is a file"
elif [ -d "$parametros" ];
then
echo "$parametros is a directory"
else
echo " There is not such file or directory"
fi
exit
done
Altough i want the user to be allowed to give only one word as a parameter. How do i make this happen ? (For example if user press space after first word there would be an error message showing "wrong input")

#!/bin/bash
read parametros
if [[ "$parametros" = *[[:space:]]* ]]
then
echo "wrong input"
elif [[ -f "$parametros" ]]
then
echo "$parametros is a file"
elif [[ -d "$parametros" ]]
then
echo "$parametros is a directory"
else
echo " There is not such file or directory"
fi
See http://mywiki.wooledge.org/BashFAQ/031 for the difference between [...] and [[...]].

You have to use the $#. It gives the number of the parameters.
The code will be something like:
if [ "$#" -ne 1 ]; then
printf 'ERROR!\n'
exit 1
fi

First, I'm curious why you want to restrict to one word - a file or directory could have spaces in it, but maybe you are preventing that somehow in your context.
Here are a few ways you could approach it:
Validate the input after they enter it - check if it has any spaces, eg: if [[ "parametros" == *" " ]]; then...
Get one character at a time in a while loop, eg with: read -n1 char
Show an error if it's a space
Break the loop if it's 'enter'
Build up the overall string from the entered characters
1 is obviously much simpler, but maybe 2 is worth the effort for the instant feedback that you are hoping for?

Related

Yes or No in Bash Script

I have a set of 100 questions. My requirement is when a user enter "yes", then question 1 should appear. If not, directly it go to question 2. Like that it should go on till 100 questions. Any lead would be appreciated.
This is what I tried, but it is failing.
#!/bin/bash
echo "Execute question1 "
select result in Yes No
do
echo "How to see apache config file"
exit
done
echo "execute question2"
select result in Yes No Cancel
do
echo "Command for listing processes"
exit
done
Thanks in advance
Here is a way to do this with an array.
#!/bin/bash
questions=(
"How to see apache config file"
"Command for listing processes"
"My hovercraft is full of eels"
)
for((q=0; q<${#questions[#]}; q++)); do
echo "Execute question $q?"
select result in Yes No; do
case $result in
Yes)
echo "${questions[q]}";;
esac
break
done
done
Using select for this seems rather clumsy, though. Perhaps just replace it with
read -p "Execute question $q? " -r result
case $result in
[Yy]*) echo "${questions[q]}";;
esac
Having just a list of questions still seems weird. With Bash 5+ you could have an associative array, or you could have a parallel array with the same indices with answers to the questions. But keeping each question and answer pair together in the source would make the most sense. Maybe loop over questions and answers and assign every other one to an answers array, and only increment the index when you have read a pair?
pairs=(
"How to see Apache config file"
"cat /etc/httpd.conf"
"Command for listing processes"
"ps"
"My hovercraft is full of what?"
"eels"
)
questions=()
answers=()
for((i=0; i<=${#pairs[#]}/2; ++i)); do
questions+=("${pairs[i*2]}")
answers+=("${pairs[1+i*2]}")
done
This ends up with two copies of everything, so if you are really strapped for memory, maybe refactor to just a for loop over the strings and get rid of the pairs array which is only useful during initialization.
Use an array of questions and loop over it, like this:
#!/bin/bash
n=1
questions=(
'How to see apache config file'
'Command for listing processes'
)
check_user_input(){
read -p "y/n " input
case $input in
[Yy]*) return 0;;
[Nn]*) return 1;;
*) check_user_input;;
esac
}
for question in "${questions[#]}"; {
echo "Execute question $n"
check_user_input && echo "$question"
((n++))
}
Here is a straight forward example. Play with it.
#!/bin/bash
echo "Type 'y' for yes, 'n' to skip or 'q' to quit and press Enter!"
for((i=1; i < 101; ++i)); do
echo 'Execute question '$i
while read user_input; do
if [[ "$user_input" = 'q' ]]; then
break 2
elif [[ "$user_input" = 'n' ]]; then
break
elif [[ $i -eq 1 ]]; then
echo 'How to see apache config file?'
break 2 # Change from "break 2" to "break" for the next question.
elif [[ $i -eq 2 ]]; then
echo 'Command for listing processes.'
break 2 # Change from "break 2" to "break" for the next question.
else
echo "Wrong input: $user_input"
echo "Type 'y' for yes, 'n' to skip or 'q' to quit and press Enter!"
fi
done
done
echo 'Finished'

Why doesn't string comparison with wildcards work properly?

I wrote this shell code, but it doesn't get the good output.
Even though the $csoport gets the "...: No such user" output, id doesn't echoes the following line I wrote there.
read felhasznalo
while [ "$felhasznalo" != "exit" ]
do
csoport=`groups $felhasznalo`
echo "$csoport"
if [[ "$csoport" == *": No such user"* ]] ; then
echo -n "Nincs ilyen felhasznalo a rendszerben"
else
echo "$csoport"
fi
echo -n "Felhasznalo: "
read felhasznalo
done
You shouldn't try to match the error messsage since you only care if groups fails. You ought to do:
if ! csoport=$(groups "$felhasznalo"); then
printf "Nincs ilyen felhasznalo a rendszerben"
else
echo "$csoport"
fi

How to read two input values in bash?

I am writing a bash script which takes multiple user inputs. Before the script will take any action, I want to ensure that all values have been added by a user.
#/bin/bash
read -p "Please enter the domain Name: " domain
read -p "Please Enter path where you want to save your result: " path
if [[ -z "$domain" && "$path"]]; then
echo "You have not entered the Domain Name"
exit 1
else
echo "Do Something Here"
fi
I have checked with 1 user input, working fine, but when trying with 2 user inputs, I am getting an error.
./test.sh: line 5: unexpected token `;', conditional binary operator expected
./test.sh: line 5: syntax error near `;'
./test.sh: line 5: `if [[ -z "$domain" && "$path"]]; then'
Thanks!
Because you're using the [[ double brackets, you can use || to test if either of your conditions is true. In this case, your code would look like this:
#!/usr/bin/env bash
read -p "Please enter the domain Name: " domain
read -p "Please Enter path where you want to save your result: " path
if [[ -z "$domain" || -z "$path" ]]; then
echo "Either you have not entered the Domain Name, or you have not entered the path."
exit 1
else
echo "Do Something Here"
fi
Note that spaces around the brackets are necessary.
As others have pointed out, errors should be specific, so you should consider something like this:
if [[ -z "$domain" ]]; then
echo "You have not entered the Domain Name"
exit 1
elif [[ -z "$path" ]]; then
echo "You have not entered the path"
exit 1
fi
echo "Do something here"
It's a bit more verbose but gives the user more specific feedback.
You are getting syntax error because you forgot to put a space between "$path" and ] (bash uses spaces as delimiters).
If you want to fail when at least one condition is wrong, you should use the || (OR) operator.
#/bin/bash
read -p "Please enter the domain name: " domain
read -p "Please enter the path where you want to save your result: " path
if [[ -z "$domain" ]] || [[ -z "$path" ]] ; then
echo "You didn't enter the domain name or the save path"
exit 1
else
echo "Do something here"
fi

Delimiter “, white spaces and bash script in Linux

I want in a bash script (Linux) to check, if two files are identical.
I use the following code:
#!/bin/bash
…
…
differ=$(diff $FILENAME.out_ok $FILENAME.out)
echo "******************"
echo $differ
echo "******************"
if [ $differ=="" ]
then
echo "pass"
else
echo "Error ! different output"
echo $differ
fi
The problem:
the diff command return white space and break the if command
output
******************
82c82 < ---------------------- --- > ---------------------
******************
./test.sh: line 32: [: too many arguments
Error ! different output
The correct tool for checking whether two files are identical is cmp.
if cmp -s $FILENAME.out_ok $FILENAME.out
then : They are the same
else : They are different
fi
Or, in this context:
if cmp -s $FILENAME.out_ok $FILENAME.out
then
echo "pass"
else
echo "Error ! different output"
diff $FILENAME.out_ok $FILENAME.out
fi
If you want to use the diff program, then double quote your variable (and use spaces around the arguments to the [ command):
if [ -z "$differ" ]
then
echo "pass"
else
echo "Error ! different output"
echo "$differ"
fi
Note that you need to double quote the variable when you echo it to ensure that newlines etc are preserved in the output; if you don't, everything is mushed onto a single line.
Or use the [[ test:
if [[ "$differ" == "" ]]
then
echo "pass"
else
echo "Error ! different output"
echo "$differ"
fi
Here, the quotes are not strictly necessary around the variable in the condition, but old school shell scripters like me would put them there automatically and harmlessly. Roughly, if the variable might contain spaces and the spaces matter, it should be double quoted. I don't see a need to learn a special case for the [[ command when it works fine with double quotes too.
Instead of:
if [ $differ=="" ]
Use:
if [[ $differ == "" ]]
Better to use modern [[ and ]] instead of an external program /bin/[
Also use diff -b to compare 2 files while ignoring white spaces
#anubhava answer is correct,
you can also use
if [ "$differ" == "" ]

If then elif then else statement in bash

then, elif, else statement that I have programmed in a bash script. I know that it works because I can run the same command in the terminal interface and see that it is doing what I want it to do. However when I run it in a script it seems to always jump to the else statement and not detect anything. Can anybody help explain why this is so? Here is my script code:
if [ -e "$1" ]
then
for line in `samtools view -H $1`
do
if [[ "$line" == *NCBI-Build-36\.1* ]]
then
echo "hg18"
break
elif [[ "$line" == *hg19* ]]
then
echo "hg19"
break
else
echo "Reference was not found, manual entry required: "
read ans
echo "$ans"
break
fi
done
else
echo -e "Usage: \e[1;32mreadRef.sh \e[1;36mbamfile.bam"
fi
No matter what file I plug in it always skips to the else and asks me for manual entry.
Here is the command I ran on terminal:
for line in `samtools view -H $bignormal`; do if [[ "$line" == *NCBI-Build-36\.1* ]]; then echo "YES - $line"; else echo "NO - $line"; fi; done
And the output is like this:
NO - #HD
NO - VN:1.0
NO - GO:none
NO - SO:coordinate
NO - #SQ
NO - SN:1
NO - LN:247249719
YES - AS:NCBI-Build-36.1
YES - UR:http://www.bcgsc.ca/downloads/genomes/9606/NCBI-Build-36.1/bwa_ind/genome/
NO - SP:Homo
NO - sapiens
.
.
.
Why is the script not detecting the string I am looking for, but it is in terminal?
EDIT:
I tried what Charles said, this is the output:
:+'[' -e /projects/rcorbettprj2/DLBCL/CNV/RG065/normal/A01440_8_lanes_dupsFlagged.bam ']'
::+samtools view -H /projects/rcorbettprj2/DLBCL/CNV/RG065/normal/A01440_8_lanes_dupsFlagged.bam
:+for line in '`samtools view -H $1`'
:+case "$line" in
:+echo 'Reference was not found, manual entry required: '
Reference was not found, manual entry required:
:+read ans
I think your code has a logic error nobody's spotted yet. I'm not sure, since you haven't told us what the script's supposed to be doing, but it looks to me like what you want is to ask for manual entry only if you don't find a match to either of your patterns anywhere in the output, but what you're actually doing is examining only the first word of output for a match. And from your sample output, the first word is "#HD", which doesn't match either pattern, so the script is doing exactly what I'd expect.
Now, assuming I'm right and that the point is to look for either pattern anywhere in the output, you can actually simplify things a bit. Mainly, you don't need the loop, you can just do a single comparison to look for the pattern in the entire output at once:
#!/bin/bash
if [ -e "$1" ]
then
output="$(samtools view -H "$1")"
if [[ "$output" == *NCBI-Build-36.1* ]]
then
echo "hg18"
elif [[ "$output" == *hg19* ]]
then
echo "hg19"
else
read -p "Reference was not found, manual entry required: " ans
echo "$ans"
fi
done
else
echo -e "Usage: \e[1;32mreadRef.sh \e[1;36mbamfile.bam"
fi

Resources