How to make a bash script fail based on grep regex? - bash

I have the following bash script.
The problem I try to solve is really easy. When a commit in Git no starts with gh-1234 ... it should fail.
What is wrong with this bash script?
commit_regex='(gh-[0-9]+|merge)'
error_msg="Aborting commit. Your commit message is missing either a Github Issue ('gh-1111') or 'Merge'."
if ! grep -q "$commit_regex" <<< "$1"; then
echo "$error_msg" >&2
exit 1
fi

As mentioned in the comments, you need to do grep -E "$commit_regex"
From the man grep page-
-E, --extended-
Interpret PATTERN as an extended regular expression (ERE, see below). (-E is specified by POSIX.)
That should solve your problem since it forces grep to expand the variable.

Also another way to achieve the same without grep :
if [[ $git_message=~ $commit_regex ]]
then
echo "$error_msg"
exit 1;
fi
Also : If the commit it "gh-1234" is mandated to be at the start of the message , you should add ^ in your regex.

Related

How to use grep in if statement in shell

I have this shall script, basically I need to print all <sst> result only when <pr> is found.
Probably I have some syntax error so when I run the script I receive a message "Display all possibilities" basically the grep does not work.
Could you please help me out to understand what is the problem here?
declare -a arr=(
"123"
"345"
)
for i in "${arr[#]}"
do
echo "$i"
if [grep -q "<pr>$i</pr>" ./archiv]
then
grep -r "<sst>" ./archiv
fi
done
There is very likely no command named [grep. Drop the [
if grep -q "<pr>$i</pr>" ./archiv; then ...
[ is not and has never been a part of the shell grammar. It is a command, just like echo or test or grep. The value returned by that command is used to determine whether or not to execute the clause of the if statement.

Cannot figure out how to fix shellcheck complaint that I should not use a glob as a command when starting one script from another

Example script:
#!/bin/bash
printf '1\n1\n1\n1\n' | ./script2*.sh >/dev/null 2>/dev/null
Shellcheck returns the following:
In script1.sh line 3:
printf '1\n1\n1\n1\n' | ./script2*.sh >/dev/null 2>/dev/null
^-- SC2211: This is a glob used as a command name. Was it supposed to be in ${..}, array, or is it missing quoting?
According to https://github.com/koalaman/shellcheck/wiki/SC2211, there should be no exceptions to this rule.
Specifically, it suggests "If you want to specify a command name via glob, e.g. to not hard code version in ./myprogram-*/foo, expand to array or parameters first to allow handling the cases of 0 or 2+ matches."
The reason I'm using the glob in the first place is that I append or change the date to any script that I have just created or changed. Interestingly enough, when I use "bash script2*.sh" instead of "./script2*.sh" the complaint goes away.
Have I fixed the problem or I am tricking shellcheck into ignoring a problem that should not be ignored? If I am using bad bash syntax, how might I execute another script that needs to be referenced to using a glob the proper way?
The problem with this is that ./script2*.sh may end up running
./script2-20171225.sh ./script2-20180226.sh ./script2-copy.sh
which is a strange and probably unintentional thing to do, especially if the script is confused by such arguments, or if you wanted your most up-to-date file to be used. Your "fix" has the same fundamental problem.
The suggestion you mention would take the form:
array=(./script2*.sh)
[ "${#array[#]}" -ne 1 ] && { echo "Multiple matches" >&2; exit 1; }
"${array[0]}"
and guard against this problem.
Since you appear to assume that you'll only ever have exactly one matching file to be invoked without parameters, you can turn this into a function:
runByGlob() {
if (( $# != 1 ))
then
echo "Expected exactly 1 match but found $#: $*" >&2
exit 1
elif command -v "$1" > /dev/null 2>&1
then
"$1"
else
echo "Glob is not a valid command: $*" >&2
exit 1
fi
}
whatever | runByGlob ./script2*.sh
Now if you ever have zero or multiple matching files, it will abort with an error instead of potentially running the wrong file with strange arguments.

Unix Shell Script: Display custom message if given extension file not exists in directory

I have the following unix shell script, which is used to list the files in the given directory. Only we need to pass the extension of the file and script should list the file or files or display custom message.
My try:
Script:
#!/bin/sh
FileNameWithPath=`ls home\docs\customers\*.$1 | wc -w`
if [ $FileNameWithPath -gt 0 ]
then
ls home\docs\customes\*.$1
else
echo "Custom Message about failure(File not found)"
fi
Run:
$ ./Test.sh txt
Note: The above script works fine if i give file extension which is exists but if i give some non exists file extension it will through error plus custom error message. I just want to print custom message that's it.
You can do it with a single command:
ls home/docs/customers/*.$1 2> /dev/null || echo "Custom message about failure (File not found)"
The first command (the 'ls') try to list the files. If it fails, it will print an error message (suppressed by '2> /dev/null') and returns an error code. Since the exit code is different by 0, the second part (the 'echo') will be executed.
If you want to keep your code, you can drop the ls error redirecting stderr to /dev/null in this way:
FileNameWithPath=`ls home\docs\customers\*.$1 2>/dev/null | wc -w`
This doesn't require use of ls.
You can do this with globbing itself:
# turn on glob failure for no matches
shopt -s failglob
# list files or a custom error message
(echo home/docs/customers/*."$1") 2>/dev/null ||
echo "Custom Message about failure"
The error message you get happens in the line where you are assigning to FileNameWithPath. You can suppress it by redirecting it to /dev/null. i.e. 2>/dev/null.
It is much better (and Posix compliant) to use $() instead of the backtick operator, given that you started your script with #!/bin/sh rather than #!/bin/bash. You will then be portable across the modern bourne shells.
Another big win for using $() is that they can be nested easily, whereas you have to escape the backtick when you nest it.
As Andrea Carron points out in their answer, you can do the whole thing on one line using the || logical-or operator. This is a very common idiom.
On the off-chance that your MVCE refers to something more complex, I fixed it for you below.
#!/bin/sh
FileNameWithPath=$(ls home\docs\customers\*.$1 2>/dev/null | wc -w)
if [ $FileNameWithPath -gt 0 ]
then
ls home\docs\customes\*.$1
else
echo "Custom Message about failure(File not found)"
fi
Just add error redirection to null device file in second line of your script:-
FileNameWithPath=`ls home\docs\customers\*.$1 2>/dev/null | wc -w`

Escaping Shebang in grep

in a shell script, i'm trying to find out if another file is a shell script. i'm doing that by grepping the shebang line. but my grep statement doesn't work:
if [[ $($(cat $file) | grep '^#! /bin' | wc -l) -gt 0 ]]
then
echo 'file is a script!'
else
echo "no script"
fi
i always get the error "bash: #!: command not found". i tried several things to escape the shebang but that didn't work.
maybe you can help me with that? :)
cheers,
narf
I would suggest that you change your condition to this:
if grep -q '^#! */bin' "$file"
The -q option to grep is useful in this case as it tells grep to produce no output, exiting successfully if the pattern is matched. This can be used with if directly; there's no need to wrap everything in a test [[ (and especially no need for a useless use of cat).
I also modified your pattern slightly so that the space between #! and /bin is optional.
It's worth noting that this will produce false positives in cases where the match is on a different line of the file, or when another shebang is used. You could work around the first issue by piping head -n 1 to grep, so that only the first line would be checked:
if head -n 1 "$file" | grep -q '^#! */bin'
If you are searching for a known list of shebangs, e.g. /bin/sh and /bin/bash, you could change the pattern to something like ^#! */bin/\(sh\|bash\).

shell script working fine on one server but not on another

the following script is working fine on one server but on the other it gives an error
#!/bin/bash
processLine(){
line="$#" # get the complete first line which is the complete script path
name_of_file=$(basename "$line" ".php") # seperate from the path the name of file excluding extension
ps aux | grep -v grep | grep -q "$line" || ( nohup php -f "$line" > /var/log/iphorex/$name_of_file.log & )
}
FILE=""
if [ "$1" == "" ]; then
FILE="/var/www/iphorex/live/infi_script.txt"
else
FILE="$1"
# make sure file exist and readable
if [ ! -f $FILE ]; then
echo "$FILE : does not exists. Script will terminate now."
exit 1
elif [ ! -r $FILE ]; then
echo "$FILE: can not be read. Script will terminate now."
exit 2
fi
fi
# read $FILE using the file descriptors
# $ifs is a shell variable. Varies from version to version. known as internal file seperator.
# Set loop separator to end of line
BACKUPIFS=$IFS
#use a temp. variable such that $ifs can be restored later.
IFS=$(echo -en "\n")
exec 3<&0
exec 0<"$FILE"
while read -r line
do
# use $line variable to process line in processLine() function
processLine $line
done
exec 0<&3
# restore $IFS which was used to determine what the field separators are
IFS=$BAKCUPIFS
exit 0
i am just trying to read a file containing path of various scripts and then checking whether those scripts are already running and if not running them. The file /var/www/iphorex/live/infi_script.txt is definitely present. I get the following error on my amazon server-
[: 24: unexpected operator
infinity.sh: 32: cannot open : No such file
Thanks for your helps in advance.
You should just initialize file with
FILE=${1:-/var/www/iphorex/live/infi_script.txt}
and then skip the existence check. If the file
does not exist or is not readable, the exec 0< will
fail with a reasonable error message (there's no point
in you trying to guess what the error message will be,
just let the shell report the error.)
I think the problem is that the shell on the failing server
does not like "==" in the equality test. (Many implementations
of test only accept one '=', but I thought even older bash
had a builtin that accepted two '==' so I might be way off base.)
I would simply eliminate your lines from FILE="" down to
the end of the existence check and replace them with the
assignment above, letting the shell's standard default
mechanism work for you.
Note that if you do eliminate the existence check, you'll want
to either add
set -e
near the top of the script, or add a check on the exec:
exec 0<"$FILE" || exit 1
so that the script does not continue if the file is not usable.
For bash (and ksh and others), you want [[ "$x" == "$y" ]] with double brackets. That uses the built-in expression handling. A single bracket calls out to the test executable which is probably barfing on the ==.
Also, you can use [[ -z "$x" ]] to test for zero-length strings, instead of comparing to the empty string. See "CONDITIONAL EXPRESSIONS" in your bash manual.

Resources