Bash, test for presence of two files - bash

I found this answer which works fine, but I wanted to understand why the following code won't detect the presence of two files?
if [[ $(test -e ./file1 && test -e ./file2) ]]; then
echo "yep"
else
echo "nope"
fi
Running this directly from the shell works as expected:
test -e ./file1 && test -e ./file2 && echo yes

The output of test -e ./file1 && test -e ./file2 is an empty string, which causes [[ ]] to produce a non-zero exit code. You want
if [[ -e ./file1 && -e ./file2 ]]; then
echo "yep"
else
echo "nope"
fi
[[ ... ]] is a replacement for [ ... ] or test ..., not a wrapper around it.

if executes a program (or builtin, in the case of [[) and branches based on its return value. You need to omit either the [[ ]] or the tests:
if [[ -e ./file1 && -e ./file2 ]]; then
Or
if test -e ./file1 && test -e ./file2; then

Related

Bash script ends without doing echo

I am running a script from command line that runs a bunch of Jmeter tests if 4 variables are set.
The script works but I have added parts so the script will end if the server is unknown.
if [ echo "$2" != | grep -iq "^hibagon" ] || [ echo "$2" != | grep -iq "^kameosa" ] ;then
echo "Unkown server stopping tests"
else
echo "Continueing to tests"
when this section of the script runs it will end the script if hibagon or kameosa is not found (not case sensitive).
I want the command line to echo Unknown server stopping tests then end but at the moment it just ends with no echo
That's a strange syntax. Try this:
if echo "$2" | grep -iq "^hibagon\|^kameosa";
then
echo "Continuing to tests"
else
echo "Unkown server, stopping tests"
fi
Or if you're using bash:
if [[ "$2" =~ ^hibagon ]] || [[ "$2" =~ ^kameosa ]]
then
echo "Continuing to tests"
else
echo "Unkown server, stopping tests"
fi
First the test [ echo "$2" != | grep -iq "^hibagon" ] is wrong, then you can use only one (extended)grep with the negation flag -v putting together the two words in one regex ^(hibagon|kameosa). Also the fi was missing. but I suppose it's only a typo here.
if echo "$2" | egrep -ivq "^(hibagon|kameosa)"; then
echo "Unknown server stopping tests"
else
echo "Continuing to tests"
fi
If you like it, even:
if egrep -ivq "^(hibagon|kameosa)" <<< "$2"; then

getops still performs default actions when arguments are provided

I've recently started working with the getopts command in bash. I am confused as to why my script runs the dafult action "cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl" when arguments have been provided. I only want that to run if no arguments were passed to the shell script. I've used getopts:Std in perl where I was able to code somthing like:
unless ($opts{d}) {
do something...}
How would I code something like that in a shell script? Also, how would I code logic such as this:
if ($opts{c}) {
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -c
}
elsif ($opts{d} {
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -d
My code:
#!/bin/sh
while getopts cd name
do
case $name in
c)copt=1;;
d)dopt=1;;
*)echo "Invalid arg";;
esac
done
if [[ ! -z $copt ]] #Specifies what happens if the -c argument was provided
then
echo "CSV file created!"
cat "~/bin/Temp/log.txt" | ~/bin/Scripts/vpnreport/report.pl -c
fi
if [[ ! -z $dopt ]] #Specifies what happens if the -d argument was provided
then
echo "Debug report and files created"
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -d
fi
if [[ ! -z $name ]] #Specifies what happens if no argument was provided
then
echo "Running standard VPN report"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl
fi
shift $(($OPTIND -1))
My Output:
[~/bin/Scripts/report]$ sh getoptstest.sh
Running standard report
[~/bin/Scripts/report]$ sh getoptstest.sh -d
Debug report and files created
Running standard report
[~/bin/Scripts/report]$
The two getopts commands are vasty different from bash to perl and I just can't seem to get the hang of the bash varient even after reading several tutorials. Any help would be greatly appreciated!
On the final run of getopts, your variable (name) will be set to "?".
#!/bin/bash
while getopts abc foo; do :; done
echo "<$foo>"
Output of the above:
$ ./mytest.sh
<?>
$ ./mytest.sh -a
<?>
Insead, use elif, which is like Perl's elsif:
if [[ ! -z $copt ]]
then
# ...
elif [[ ! -z $dopt ]]
then
# ...
else
# ...
fi
Or test if [[ -z $copt && -z $dopt ]], or so forth. Other notes:
See the official if and case documentation in the Bash manual under "Conditional Constructs".
[[ ! -z $name ]] means the same as the more-direct [[ -n $name ]].
Use #!/bin/bash instead of #!/bin/sh, or switch off of [[ in favor of [. The double square bracket (and your use thereof) is specific to bash, and rarely works with sh.
I took Jeff's answer and rewrote my script so it works now:
#!/bin/bash
while getopts cd name
do
case $name in
c)carg=1;;
d)darg=1;;
*)echo "Invalid arg";;
esac
done
#Specifies what happens if the -c argument was provided:
if [[ ! -z $carg ]]
then
if [[ -z $darg ]]
then
echo "CSV created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -c
else
echo "Debug CSV created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -cd
fi
fi
#Specifies what happens if the -d argurment was provided:
if [[ ! -z $darg ]]
then
echo "Debug report created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -d
#Specifies what happens if no argument was provided:
else
echo "Standard report created"
cat ~bin/Temp/logs.txt | ~bin/Scripts/report.pl
fi
Thank you again for your assistance!

echo new line to terminal only when current terminal line is not empty

In bash, I would like to conditionally echo a new line depending on whether the current line in the terminal is empty or not.
Say, for example, first.sh runs first but I don't control it and I don't know what it will print each time. Can I get second.sh to start printing always on a brand new line and don't leave any blank lines above it?
first.sh
#!/bin/bash
let "n = 1"
max=$((RANDOM%3))
while [ "$n" -le "$max" ]
do
printf "%s" x
let "n += 1"
done
second.sh
#!/bin/bash
#if [ not_in_first_terminal_column ]
# echo
#fi
echo "Hola"
I want one of the following outputs
$ ./first.sh && ./second.sh
Hola
$ ./first.sh && ./second.sh
x
Hola
$ ./first.sh && ./second.sh
xx
Hola
but not
$ ./first.sh && ./second.sh
Hola
$ ./first.sh && ./second.sh
xHola
$ ./first.sh && ./second.sh
xxHola
Is it possible to do what I want? I figured using ANSI escape codes, something like in here, but I haven't found a way.
Test your variable's value if it's not empty with -n:
[[ -n $VAR ]] && echo "$VAR"
# POSIX or Original sh compatible:
[ -n "$VAR" ] && echo "$VAR"
# With if:
if [[ -n $VAR ]]; then
echo "$VAR"
fi
if [ -n "$VAR" ]; then
echo "$VAR"
fi
It's actually equivalent to [[ $VAR != "" ]] or ! [ "$VAR" = "" ].
Furthermore in Bash you can test it if it's only filled with whitespaces:
shopt -s extglob ## Place this somewhere at the start of the script
[[ $VAR == +([[:space:]]) ]] && echo "$VAR"
if [[ $VAR == +([[:space:]]) ]]; then
echo "$VAR"
fi
Use [[:blank:]] to only match spaces and tabs and not newlines and the likes if it's more helpful.
If you want to remove empty lines from input file or pipe you can use other tools like sed:
sed -ne '/^$/!p' file or ... | sed -ne '/^[[:space:]]*$/!p'

use multiple conditions in 'test' command

I have been trying to convert the following code to use 'test' instead of 'if'
if [ -e ./blah ] && [ ! -L ./blah ];
then
exit 1
fi
My intention is to use test instead, so that I dont have to exit 1 explicitly. I am trying something like this:
test -e ./blah && ! -L ./blah;
instead of &&, I have tried -a, with different bracket combinations, but I am not successful. I am sure there should be a simple way to do this. Can anyone help me ?
test does not understand logical operators && and ||. You can use test -e ./blah -a ! -L ./blah, but if you are using bash, you can also switch to its more powerful [[ .. ]] construct:
[[ -e ./blah && ! -L ./blah ]]
To make if [ -e ./blah ] && [ ! -L ./blah ]; work, use the following
if [ -e ./blah -a ! -L ./blah ]; (-a stands for and) http://tldp.org/LDP/abs/html/comparison-ops.html
Demo -- http://ideone.com/GR8UiK
But, as others have pointed out, [[ .. ]] construct is more powerful than [...].
You can combine them all (including if then fi):
[[ -e ./blah && ! -L ./blah ]] && exit 1
Use [[ keyword as it is more powerful.
if [[ -e ./blah && ! -L ./blah ]]
then
...
fi
However, to ensure portability, you can do something like this too
if [ -e ./blah ] && [ ! -L ./blah ]
then
...do something
fi
As you ask to use test, you can do like so:
test -e ./blah && test -L ./blah || ( echo 'First action' ; echo 'Second action )
The different operators (&&, ||, etc...) are first parsed by the shell, so you can't use it in command parameter(s).
if [ -e ./blah ] && [ ! -L ./blah ];
is equivalent to
if test -e ./blah && test ! -L ./blah;
Therefore you can simply write
test -e ./blah && test ! -L ./blah
To wit:
$ help [\[] | tail -n +3
[: [ arg... ]
Evaluate conditional expression.
This is a synonym for the "test" builtin, but the last argument must
be a literal `]', to match the opening `['.
Do the following:
$ ls -i /bin/test
54008404 /bin/test
$ ls -i /bin/[
54008404 /bin/test
That 54008404 is the inode number. This is the real name of a file. The /bin/test simply points to the inode and the inode contains all file file information.
The thing to note is that /bin/[ and /bin/test are the same inode. That means, they're the same command.
Thus:
if [ -f "$foo" ]
is the same as:
if test -f "$foo"
The if command executes the command given, and then will execute the if clause if the command returns true and doesn't execute the clause if the command it false.
For example:
if grep -q "foo" $foo
then
echo "File $foo contains the regular expression /foo/"
fi
Is completely valid. The grep -q command (in many variants of grep means search for the regular expression, and if that regular expression is in the file, return an exit code of 0 (which means the command succeeded and is true).
Note there is no square brackets.
The test command (or [...]) merely runs a test as specified, and returns with an exit code of 0 (thus the command is a success) if the test is true. That's all it does.
You may also see this construct:
[ "$foo" = "$bar" ] && echo "$foo is equal to $bar"
The && means execute the next command (and return the exit code) if the first command returns an exit code of zero. Otherwise, simply return the first command's exit code.
Thus:
if [ -e ./blah ] && [ ! -L ./blah ];
is saying run test -e ./blah and if that is true (that is, the file exists) execute test ! -L ./blah and if that is also true, run the if clause of the statement.
Note that [ -e ./blah] and [ ! -L ./blah ] are two separate commands. The && strings together the two commands:
[ "$foo" = "$bar" ] && some_command;
This says, run test "$foo" = "$bar" and if that is true, run the command some_command. Note that this is equivalent to:
if [ "$foo" = "$bar" ]
then
some_command
fi
The other list structure is the ||. This means that if the first command succeeds, return an exit code of 0 and don't run the second command. Thus:
[ "$foo" = "$bar" ] || some_command;
Is the same as:
if [ "$foo" = "$bar" ]
then
:
else
some_command
fi
Let's get back to your _original question:
if [ -e ./blah ] && [ ! -L ./blah ];
then
exit 1
fi
Is the same as:
if test -e ./blah && test ! -L ./blah
then
exit 1
fi
Which is the same as
test -e ./blah && test ! -L ./blah && exit 1
This means: If test -e ./blah is true (./blah is a file), then execute the command after the && list operator. This is test -! -L ./blah. If this test also is true, run the command after the && list operator again.
This could also be rewritten as:
test -e ./blah && test -L ./blah || exit 1
This says that if test -e ./blah is true, run the command after the && list operator. If test -L ./blah is false, run the command after the || operator.

2 grep in if statement

Anyone can tell me what will be the problem?i know that this is a half sec problem but pls help:) egrep "first" a.sh && egrep "second" a.sh works, a.sh contains first,second,third etc.. Thx!
if [[ egrep "first" a.sh && egrep "second" a.sh ]]; then
echo "success"
fi
Your problem is that you're using the [[ command. Use just the greps.
if egrep ... && egrep ... ; then
I think this may be what you are looking to do:
found_1=$(egrep "first" a.sh)
found_2=$(egrep "second" a.sh)
if [[ -n "$found_1" ]] && [[ -n "$found_2" ]]; then
echo "success"
fi

Resources