Delimiter “, white spaces and bash script in Linux - 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" == "" ]

Related

Bash script that allows one word as user input

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?

Bash scripting using an exit status in an if statement

May be a novice question but anyways in my intro to linux/unix class were touching on bash scripting and in one of the problems I got the it tasked me with making a script so if the user searched to a name in a file that wasn't there it would output a messaged saying 'your_input is not in the directory'
It says to use if statements and the exit status $?.
So far I got the input portion but I'm not sure how to properly use the $? in a if statement if its possible.
#!/bin/bash
name=$1
if [ "$name" = "" ]
then echo -n "Enter a name to search for: "
read name
fi
grep -i $name ~uli101/2014c/phonebook
if [ "$?" < "0" ]
then echo "error"
fi
I get the error:
./phone4: line 14: 0: No such file or directory
My question is: How can I use the $? with and if statement, and If I can't, can you explain me how to use the $? in this problem?
Note: I did use echo $? to see how $? gave a 0 if grep worked and a 1 if it didn't.
There's two bugs in it. The one you already see is that in the [] expression, the < is interpreted not as "less than" but as stream redirection operator. The reason is that [ is just another program (an alias for test), so [ "$?" < "0" ] is similar to cat < filename. The other error is that you don't want to check for "less than" but for "not equal". In sum:
if [ "$?" < "0" ]
should be
if [ "$?" -ne "0" ]
Or you could write
if ! grep "$name" ~uli101/2014c/phonebook
...because if interprets a return code of zero as true and everything else as false.
never mind one of my friends pushed me in the right direction:
all I had to do is:
if [ "$?" = "1" ]
then echo "error"
fi
pretty much I was over thinking it, I just needed to say if $? = 1 then error, because in the readings it said $? can be greater then 1 I was trying to compensate for that.
It should be
if [ "$?" -gt 0 ]
The symbol '<' is a redirection operator, and it's not a Python or C - everything in a shell script is a command, including the text after 'if', and you are executing a command named '[' here, you may find it at the location /usr/bin/[, and this command uses -gt and -lt parameters to compare numbers, instead of '>' and '<', which are special shell operators.
You can rewrite this code like this:
if grep -i "$name" ~uli101/2014c/phonebook
then true # 'true' is also a command, which does nothing and returns success
else echo "Error"
fi
or even like this, using '||' operator, which will execute following command only if previous command returned an error:
grep -i "$name" ~uli101/2014c/phonebook || echo "Error"
The "$?" doesn't need quotes, as it is a number really. If you want better script, check on existance of the Phonebook file, and exit before asking the Name input if the file is missing. Also, if you reply nothing (enter only) on the READ command, you may need to do something.
#!/bin/bash
name=$1
phonebook=~/phonebook
if [ "$name" = "" ]
then
echo -n "Enter a name to search for: "
read name
fi
grep -i "$name" $phonebook
if [ $? -gt 0 ]
then
echo "error, no \"$name\" in $phonebook"
fi

Bash, how to check for control character(non-printable character) in a variable?

I have a Bash statement to get user input(a single character) into tmpchar :
read -n 1 -t 1 tmpchar
and I can check for printable character input like this:
if [ "$tmpchar" = "n" ] || [ "$tmpchar" = "N" ]; then
# do something...
fi
Now my question is: If user input just a Return, or ESC, or Ctrl+a, Ctrl+b etc, how do I check for them?
ENV: openSUSE 12.3 , Bash 4.2.42(1)-release
Maybe you're looking for ANSI-C quoting. E.g., Ctrl-a is represented as $'\ca'.
Use the regex match operator =~ inside of [[ ... ]]:
if [[ $tmpchar =~ [[:cntrl:]] ]]; then
# It's a control character
else
# It's not a control character
fi
Note that read -n1 won't do what you expect for a variety of special characters. At a minimum, you should use:
IFS= read -r -n1
Even with that, you'll never see a newline character: if you type a newline, read will set the reply variable to an empty string.
If you want to know if a character isn't a member of the set of printable characters, use a complementary set expression. This seems to work fine with case:
for c in $'\x20' $'\x19'; do
case "$c" in
[[:print:]]) echo printable;;
[^[:print:]]) echo 'not printable';;
*) echo 'more than one character?';;
esac
done
(outputs printable and then non printable)
for c in $'\x20' $'\x19'; do
if [[ $c = [[:print:]] ]]; then
echo printable
fi
if [[ $c = [^[:print:]] ]]; then
echo not printable
fi
done
works as well. If you want to know what characters sets your system supports, look at man 7 regex on linux or man 7 re_format on OS X.
You can filter the input with tr:
read -n 1 -t 1 tmpchar
clean=$(tr -cd '[:print:]' <<< $tmpchar)
if [ -z "$clean"]; then
echo "No printable"
else
echo "$clean"
fi
I find a trick to check for a sole Return input.
if [ "$tmpchar" = "$(echo -e '')" ]; then
echo "You just pressed Return."
fi
In other word, the highly expected way by #ooga,
if [ "$tmpchar" = $'\x0a' ]; then
echo "You just pressed Return." # Oops!
fi
does not work for Return anyhow, hard to explain.

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

shell script to compare files and print formatted output

I'm trying to write a shell script which will compare two files, and if there are no differences between then, it will indicate that there was a success, and if there are differences, it will indicate that there was a failure, and print the results. Here's what I have so far:
result = $(diff -u file1 file2)
if [ $result = "" ]; then
echo It works!
else
echo It does not work
echo $result
fi
Anybody know what I'm doing wrong???
result=$(diff -u file1 file2)
if [ $? -eq 0 ]; then
echo "It works!"
else
echo "It does not work"
echo "$result"
fi
Suggestions:
No spaces around "=" in the variable assignment for results
Use $? status variable after running diff instead of the string length of $result.
I'm in the habit of using backticks for command substitution instead of $(), but #Dennis Williamson cites some good reasons to use the latter after all. Thanks Dennis!
Applied quotes per suggestions in comments.
Changed "=" to "-eq" for numeric test.
First, you should wrap strings being compared with quotes.
Second, "!" cannot be use it has another meaning. You can wrap it with single quotes.
So your program will be.
result=$(diff -u file1 file2)
if [ "$result" == "" ]; then
echo 'It works!'
else
echo It does not work
echo "$result"
fi
Enjoy.
Since you need results when you fail, why not simply use 'diff -u file1 file2' in your script? You may not even need a script then. If diff succeeds, nothing will happen, else the diff will be printed.
bash string equivalence is "==".
-n is non-zero string, -z is zero length string, wrapping in quotes because the command will complain if the output of diff is longer than a single string with "too many arguments".
so
if [ -n "$(diff $1 $2)" ]; then
echo "Different"
fi
or
if [ -z "$(diff $1 $2)" ]; then
echo "Same"
fi

Resources