how to do if statements with "and" in sh - linux

I try to solve this problem with the sh and not the bash.
All i want is a if statement that check some regex AND something else. Normally in bash this would be an easy job but with the sh i only find solutions online that wont work for me
First thing i want to check:
if echo "$1"| grep -E -q '^(\-t|\-\-test)$';
Than i want to check:
if echo "$#"| grep -E -q '^(1|2)$';
Combined:
if [ \(echo "$1"| grep -E -q '^(\-h|\-\-help)$'\) -a \(echo "$#"| grep -E -q '^(1|\2)$'\) ];
ERROR:
grep: grep: ./analysehtml.sh: 41: [: missing ]
Invalid back reference
(echo: No such file or directory
grep: 1: No such file or directory
I also try many diffrent combinations with this brackets but non of them worked for me. Maybe someone can help me here :)

logical and between commands is &&
if
echo "$1"| grep -E -q '^(\-h|\-\-help)$' &&
echo "$#"| grep -E -q '^(1|\2)$';
By default the exit status of a pipe is the exit status of last command.
set -o pipefail the exit status is fail if if any command of pipe has a fail exit status.
when only the exit status of the last command of a sequence must be checked
if { command11; command12;} && { command21; command22;};
However to check parameters there is no need to launch another process grep with a pipe there's an overhead.
Consider using following constructs work with any POSIX sh.
if { [ "$1" = -h ] || [ "$1" = --help ];} &&
{ [ $# -eq 1 ] || [ $# -eq 2 ];};
EDIT: Following are not POSIX but may work with many shell
if [[ $1 = -h || $1 = --help ]] && [[ $# = 1 || $# = 2 ]];
Works also with bash with set -o posix

Perhaps for your particular case, pattern matching might be better:
if [[ $1 =~ ^(\-h|\-\-help)$ && $# =~ ^(1|\2)$ ]]; then

The problem with your command is that the part within test or [ command is expression, not commands list.
So when you run [ echo 'hello' ] or [ \( echo 'hello' \) ] complains error in spite of sh or Bash. Refer to the classic test usage: The classic test command
And the syntax of if is:
if list; then list; fi
So you can just combine command with && operator in if statements:
if echo "$1"| grep -E -q '^(\-h|\-\-help)$' && echo "$#"| grep -E -q '^(1|\2)$';

Related

LINUX script bash [duplicate]

I want to check if a file contains a specific string or not in bash. I used this script, but it doesn't work:
if [[ 'grep 'SomeString' $File' ]];then
# Some Actions
fi
What's wrong in my code?
if grep -q SomeString "$File"; then
Some Actions # SomeString was found
fi
You don't need [[ ]] here. Just run the command directly. Add -q option when you don't need the string displayed when it was found.
The grep command returns 0 or 1 in the exit code depending on
the result of search. 0 if something was found; 1 otherwise.
$ echo hello | grep hi ; echo $?
1
$ echo hello | grep he ; echo $?
hello
0
$ echo hello | grep -q he ; echo $?
0
You can specify commands as an condition of if. If the command returns 0 in its exitcode that means that the condition is true; otherwise false.
$ if /bin/true; then echo that is true; fi
that is true
$ if /bin/false; then echo that is true; fi
$
As you can see you run here the programs directly. No additional [] or [[]].
In case if you want to check whether file does not contain a specific string, you can do it as follows.
if ! grep -q SomeString "$File"; then
Some Actions # SomeString was not found
fi
In addition to other answers, which told you how to do what you wanted, I try to explain what was wrong (which is what you wanted.
In Bash, if is to be followed with a command. If the exit code of this command is equal to 0, then the then part is executed, else the else part if any is executed.
You can do that with any command as explained in other answers: if /bin/true; then ...; fi
[[ is an internal bash command dedicated to some tests, like file existence, variable comparisons. Similarly [ is an external command (it is located typically in /usr/bin/[) that performs roughly the same tests but needs ] as a final argument, which is why ] must be padded with a space on the left, which is not the case with ]].
Here you needn't [[ nor [.
Another thing is the way you quote things. In bash, there is only one case where pairs of quotes do nest, it is "$(command "argument")". But in 'grep 'SomeString' $File' you have only one word, because 'grep ' is a quoted unit, which is concatenated with SomeString and then again concatenated with ' $File'. The variable $File is not even replaced with its value because of the use of single quotes. The proper way to do that is grep 'SomeString' "$File".
Shortest (correct) version:
grep -q "something" file; [ $? -eq 0 ] && echo "yes" || echo "no"
can be also written as
grep -q "something" file; test $? -eq 0 && echo "yes" || echo "no"
but you dont need to explicitly test it in this case, so the same with:
grep -q "something" file && echo "yes" || echo "no"
##To check for a particular string in a file
cd PATH_TO_YOUR_DIRECTORY #Changing directory to your working directory
File=YOUR_FILENAME
if grep -q STRING_YOU_ARE_CHECKING_FOR "$File"; ##note the space after the string you are searching for
then
echo "Hooray!!It's available"
else
echo "Oops!!Not available"
fi
grep -q [PATTERN] [FILE] && echo $?
The exit status is 0 (true) if the pattern was found; otherwise blankstring.
if grep -q [string] [filename]
then
[whatever action]
fi
Example
if grep -q 'my cat is in a tree' /tmp/cat.txt
then
mkdir cat
fi
In case you want to checkif the string matches the whole line and if it is a fixed string, You can do it this way
grep -Fxq [String] [filePath]
example
searchString="Hello World"
file="./test.log"
if grep -Fxq "$searchString" $file
then
echo "String found in $file"
else
echo "String not found in $file"
fi
From the man file:
-F, --fixed-strings
Interpret PATTERN as a list of fixed strings, separated by newlines, any of
which is to be matched.
(-F is specified by POSIX.)
-x, --line-regexp
Select only those matches that exactly match the whole line. (-x is specified by
POSIX.)
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately with zero
status if any match is
found, even if an error was detected. Also see the -s or --no-messages
option. (-q is specified by
POSIX.)
Try this:
if [[ $(grep "SomeString" $File) ]] ; then
echo "Found"
else
echo "Not Found"
fi
I done this, seems to work fine
if grep $SearchTerm $FileToSearch; then
echo "$SearchTerm found OK"
else
echo "$SearchTerm not found"
fi
grep -q "something" file
[[ !? -eq 0 ]] && echo "yes" || echo "no"

How to check if a file with a certain string in its filename exists in bash?

I want to print out an error message if a file with a "RDR_Config_Summary" in its filename does not exist in the directory. Some example filenames could be er2_FCLS_RDR_Config_Summary.txt or er1_CDMA_RDR_Config_Summary.txt. I am getting a syntax error for the following code:
cd $inputDir/$reviewDir
[[ ! grep "RDR_Config_Summary" ]] && echo -e "\nError: RDR Config Summary file was not found\n" && exit 1
cd $inputDir/$reviewDir
ls | grep "RDR_Config_Summary" > /dev/null 2>&1
[ $? -ne 0 ] && echo -e "\nError: RDR Config Summary file was not found\n" && exit 1
[[ is a command itself; you don't need it to run grep. However, you have nothing to run grep on. Use pathname expansion instead.
cd "$inputDir/$reviewDir"
shopt -s failglob
if ! { : *RDR_ConfigSummary*; } 2> /dev/null; then
echo -e "\nError: RDR Config Summary file was not found\n";
exit 1
fi
shopt -u failglob

"unary operator expected" in condition in Bash [duplicate]

In my script I am trying to error check if the first and only argument is equal to -v, but it is an optional argument. I use an if statement, but I keep getting the unary operator expected error.
This is the code:
if [ $1 != -v ]; then
echo "usage: $0 [-v]"
exit
fi
To be more specific:
This part of the script above is checking an optional argument and then after, if the argument is not entered, it should run the rest of the program.
#!/bin/bash
if [ "$#" -gt "1" ]; then
echo "usage: $0 [-v]"
exit
fi
if [ "$1" != -v ]; then
echo "usage: $0 [-v]"
exit
fi
if [ "$1" = -v ]; then
echo "`ps -ef | grep -v '\['`"
else
echo "`ps -ef | grep '\[' | grep root`"
fi
Quotes!
if [ "$1" != -v ]; then
Otherwise, when $1 is completely empty, your test becomes:
[ != -v ]
instead of
[ "" != -v ]
...and != is not a unary operator (that is, one capable of taking only a single argument).
Or for what seems like rampant overkill, but is actually simplistic ... Pretty much covers all of your cases, and no empty string or unary concerns.
In the case the first arg is '-v', then do your conditional ps -ef, else in all other cases throw the usage.
#!/bin/sh
case $1 in
'-v') if [ "$1" = -v ]; then
echo "`ps -ef | grep -v '\['`"
else
echo "`ps -ef | grep '\[' | grep root`"
fi;;
*) echo "usage: $0 [-v]"
exit 1;; #It is good practice to throw a code, hence allowing $? check
esac
If one cares not where the '-v' arg is, then simply drop the case inside a loop. The would allow walking all the args and finding '-v' anywhere (provided it exists). This means command line argument order is not important. Be forewarned, as presented, the variable arg_match is set, thus it is merely a flag. It allows for multiple occurrences of the '-v' arg. One could ignore all other occurrences of '-v' easy enough.
#!/bin/sh
usage ()
{
echo "usage: $0 [-v]"
exit 1
}
unset arg_match
for arg in $*
do
case $arg in
'-v') if [ "$arg" = -v ]; then
echo "`ps -ef | grep -v '\['`"
else
echo "`ps -ef | grep '\[' | grep root`"
fi
arg_match=1;; # this is set, but could increment.
*) ;;
esac
done
if [ ! $arg_match ]
then
usage
fi
But, allow multiple occurrences of an argument is convenient to use in situations such as:
$ adduser -u:sam -s -f -u:bob -trace -verbose
We care not about the order of the arguments, and even allow multiple -u arguments. Yes, it is a simple matter to also allow:
$ adduser -u sam -s -f -u bob -trace -verbose

How to test for if two files exist?

I would like to check if both files exist, but I am getting
test.sh: line 3: [: missing `]'
Can anyone see what's wrong?
#!/bin/sh
if [ -f .ssh/id_rsa && -f .ssh/id_rsa.pub ]; then
echo "both exist"
else
echo "one or more is missing"
fi
Try adding an additional square bracket.
if [[ -f .ssh/id_rsa && -f .ssh/id_rsa.pub ]]; then
[ -f .ssh/id_rsa -a -f .ssh/id_rsa.pub ] && echo both || echo not
or
[[ -f .ssh/id_rsa && -f .ssh/id_rsa.pub ]] && echo both || echo not
also, if you for the [[ ]] solution, you'll probably want to change #!/bin/sh to #!/bin/bash in compliance with your question's tag.
[[ is bash-specific syntax. For POSIX-compatible shells, you need:
[ -f file1 ] && [ -f file2 ]
if [ -e .ssh/id_rsa -a -e .ssh/id_rsa.pub ]; then
echo "both exist"
else
echo "one or more is missing"
fi
Here,
-e check only the file is exits or not.If exits,it return true.else,it return false.
-f also do the same thing but,it check whether the given file is regular file or not.based on that it return the true/false.
Then you are using &&.So that,It need two [[ .. ]] brackets to execute.
instead you can use the -a [same as && operator] -o [same as || operator].
If you need more information go through this link
http://linux.die.net/man/1/bash.

Check whether a certain file type/extension exists in directory [duplicate]

This question already has answers here:
Test whether a glob has any matches in Bash
(22 answers)
Closed 1 year ago.
How would you go about telling whether files of a specific extension are present in a directory, with bash?
Something like
if [ -e *.flac ]; then
echo true;
fi
#!/bin/bash
count=`ls -1 *.flac 2>/dev/null | wc -l`
if [ $count != 0 ]
then
echo true
fi
#/bin/bash
myarray=(`find ./ -maxdepth 1 -name "*.py"`)
if [ ${#myarray[#]} -gt 0 ]; then
echo true
else
echo false
fi
This uses ls(1), if no flac files exist, ls reports error and the script exits; othewise the script continues and the files may be be processed
#! /bin/sh
ls *.flac >/dev/null || exit
## Do something with flac files here
shopt -s nullglob
if [[ -n $(echo *.flac) ]] # or [ -n "$(echo *.flac)" ]
then
echo true
fi
#!/bin/bash
files=$(ls /home/somedir/*.flac 2> /dev/null | wc -l)
if [ "$files" != "0" ]
then
echo "Some files exists."
else
echo "No files with that extension."
fi
You need to be carful which flag you throw into your if statement, and how it relates to the outcome you want.
If you want to check for only regular files and not other types of file system entries then you'll want to change your code skeleton to:
if [ -f file ]; then
echo true;
fi
The use of the -f restricts the if to regular files, whereas -e is more expansive and will match all types of filesystem entries. There are of course other options like -d for directories, etc. See http://tldp.org/LDP/abs/html/fto.html for a good listing.
As pointed out by #msw, test (i.e. [) will choke if you try and feed it more than one argument. This might happen in your case if the glob for *.flac returned more than one file. In that case try wrapping your if test in a loop like:
for file in ./*.pdf
do
if [ -f "${file}" ]; then
echo 'true';
break
fi
done
This way you break on the first instance of the file extension you want and can keep on going with the rest of the script.
The top solution (if [ -e *.flac ];) did not work for me, giving: [: too many arguments
if ls *.flac >/dev/null 2>&1; then it will work.
You can use -f to check whether files of a specific type exist:
#!/bin/bash
if [ -f *.flac ] ; then
echo true
fi
bash only:
any_with_ext () (
ext="$1"
any=false
shopt -s nullglob
for f in *."$ext"; do
any=true
break
done
echo $any
)
if $( any_with_ext flac ); then
echo "have some flac"
else
echo "dir is flac-free"
fi
I use parentheses instead of braces to ensure a subshell is used (don't want to clobber your current nullglob setting).
shopt -s nullglob
set -- $(echo *.ext)
if [ "${#}" -gt 0 ];then
echo "got file"
fi
For completion, with zsh:
if [[ -n *.flac(#qN) ]]; then
echo true
fi
This is listed at the end of the Conditional Expressions section in the zsh manual. Since [[ disables filename globbing, we need to force filename generation using (#q) at the end of the globbing string, then the N flag (NULL_GLOB option) to force the generated string to be empty in case there’s no match.
Here is a solution using no external commands (i.e. no ls), but a shell function instead. Tested in bash:
shopt -s nullglob
function have_any() {
[ $# -gt 0 ]
}
if have_any ./*.flac; then
echo true
fi
The function have_any uses $# to count its arguments, and [ $# -gt 0 ] then tests whether there is at least one argument. The use of ./*.flac instead of just *.flac in the call to have_any is to avoid problems caused by files with names like --help.
Here's a fairly simple solution:
if [ "$(ls -A | grep -i \\.flac\$)" ]; then echo true; fi
As you can see, this is only one line of code, but it works well enough. It should work with both bash, and a posix-compliant shell like dash. It's also case-insensitive, and doesn't care what type of files (regular, symlink, directory, etc.) are present, which could be useful if you have some symlinks, or something.
I tried this:
if [ -f *.html ]; then
echo "html files exist"
else
echo "html files dont exist"
fi
I used this piece of code without any problem for other files, but for html files I received an error:
[: too many arguments
I then tried #JeremyWeir's count solution, which worked for me:
count=`ls -1 *.flac 2>/dev/null | wc -l`
if [ $count != 0 ]
then
echo true
fi
Keep in mind you'll have to reset the count if you're doing this in a loop:
count=$((0))
This should work in any borne-like shell out there:
if [ "$(find . -maxdepth 1 -type f | grep -i '.*\.flac$')" ]; then
echo true
fi
This also works with the GNU find, but IDK if this is compatible with other implementations of find:
if [ "$(find . -maxdepth 1 -type f -iname \*.flac)" ]; then
echo true
fi

Resources