Is it possible to have consecutive or operators in bash? - bash

Currently I have
if [[ $INPUT_FILE == *".aac" ]] || [[ $INPUT_FILE == *".aiff" ]] || [[ $INPUT_FILE == *".pcm" ]]
I have tried the following, which does not work. It does not match anything.
if [[ $INPUT_FILE == *".aac" || *".aiff" || *".pcm" ]]
Is there any way to factor this expression?

With [[ you could use a regex (specifically, an Extended Regular Expression)
if [[ $input_file =~ \.(aac|aiff|pcm)$ ]]; then
: something
fi
or an extglob
if [[ $input_file = *.#(aac|aiff|pcm) ]]; then
: something
fi
Or you may use a case statement
case $input_file in
*.aac | *.aiff | *.pcm )
# anything
;;
esac

Related

How to grep for multiple strings in the same line inside a file with similar pattern?

I have a file named my.txt:
abc_default_flow
#abc_default_flow -p sam
abc_default_flow -p sam
# abc_default_flow -p david
abc_default_flow -p david -z what_is_it
I want to match a particular line which has multiple strings and want to match the exact line which contains all the strings.
I tried the below piece of code, but as soon as it matches the partial string it comes out of the loop, rather than actual line which contains all the strings.
#!/bin/bash -f
f_name=abc_default_flow
p_name=sam
file_content=./my.txt
#echo "file_content: $file_content"
while IFS= read -r file_line
do
echo $file_line
if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && echo $file_line | grep -E "${f_name}|${p_name}"; then
#if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && echo $file_line | grep -v "${f_name}\|${p_name}"; then
#if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && [[ $file_line =~ $f_name ]] && [[ $file_line =~ $p_name ]]; then
if [[ -z "$p_name" ]]; then
f_name=${f_name}_${p_name}
fi
echo "f_name: ${f_name}"
break
fi
done < $file_content
What would be the right way to grep or use any other process to find the right line within the file?
Update: With the below code I am able to get the output, but is there any simple way with grep or sed or awk to find the result in single line instead of nested if loops.
#!/bin/bash -f
f_name=abc_default_flow
p_name=david
file_content=./my.txt
echo "f_name: $f_name, p_name: $p_name"
while IFS= read -r file_line
do
if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]]; then
echo "l1"
if [[ ! -z "$f_name" ]] && [[ $file_line =~ "$f_name" ]]; then
echo "l2, $file_line, $f_name, $p_name"
if [[ ! -z "$p_name" ]] && [[ $file_line =~ "$p_name" ]]; then
f_name=${f_name}_${p_name}
echo "f_name: ${f_name}"
break
elif [[ ! -z "$p_name" ]] && [[ ! $file_line =~ "$p_name" ]]; then
continue
else
break
fi
fi
fi
done < $file_content
You could use the same [[ ]] style checking for the two strings you're looking for:
if [[ $file_line != *"#"* ]] && [[ $file_line == *"$f_name"* ]] && [[ $file_line == *"$p_name"* ]]; then
...
fi
I removed the empty string check since an empty line won't contain $f_name and $p_name anyways.
If you expect sam will always come after abc_default_flow then you could combine the two checks into a single test:
if [[ $file_line != *"#"* ]] && [[ $file_line == *"$f_name"*"$p_name"* ]]; then
...
fi
If we look at the script as a whole, it'd be nice to get away from the explicit line-by-line loop. Scripts are more idiomatic when they chain together tools that process entire files. Something like:
sed -r '/^\s*#/d' my.txt | grep "$f_name" | grep "$p_name"

How can I call an external bash function with parameters in an if condition

I am trying to call an external bash script in an if condition in my main script.
The code of the external script IsArchive:
#!/bin/bash
STR="$1"
if [[ "$STR" == *".zip"* ]] || [[ "$STR" == *".iso"* ]] || [[ "$STR" == *".tar.gxz"* ]] || [[ "$STR" == *".tar.gx"* ]] || [[ "$STR" == *".tar.bz2"* ]] || \
[[ "$STR" == *".tar.gz"* ]] || [[ "$STR" == *".tar.xz"* ]] || [[ "$STR" == *".tgz"* ]] || [[ "$STR" == *".tbz2"* ]]
then
return 0
else
return 1
fi
and I try calling it in my main script as:
elif [[ $Option = "2" ]]
then
if IsArchive "$SourcePath";
then
less -1Ras "$SourcePath" | tee "$OutputFilePath"
#if file is not an archive
else
ls -1Rasl "$SourcePath" | tee "$OutputFilePath"
fi
when I execute the main script I receive the Error: ./script: line 61: IsArchive: command not found
You just need to make sure that the script is in your PATH. Either that, or reference it with either a full path or a relative path. Perhaps you just need to write:
if ./IsArchive "$SourcePath"; then ...
But there are several issues with IsArchive. You cannot return except from a function, so you probably want to use exit 0 and exit 1 instead of return. You probably don't want to consider a name like foo.zipadeedoodah to be an archive, but *".zip"* will match that, so you should probably remove the trailing *. It would be simpler to write it with a case statement:
#!/bin/bash
case "$1" in
*.zip|*.iso|*.tar.gxz|*.tar.gx|*.tar.bz2| \
*.tar.gz|*.tar.xz|*.tgz|*.tbz2) exit 0;;
*) exit 1;;
esac

If loop with OR

I have following strings in input.txt file.
Running
isn't running
is running
Stopped
stopped
Aborted
aborted
Here I would need to match everything except "Running" and "is running". So far I have done below but it seems to be printing "Running" and "is running" as well. Can some help ?
exec < input.txt
while read line
do
if [[ $line =~ [Aa]borted || [Ss]topped || isn*t ]]; then
echo "$line"
else
echo "FINE"
fi
done
Why don't use grep?
grep -v "unning" < input.txt
gives you desired lines.
grep -vE '\<(R|is r)unning\>' input.txt
The \< and \> markers are word boundaries, so you won't filter out "this running"
In shell
while IFS= read -r line; do
case $line in
*"Running"* | *"is running"*) echo FINE ;;
*) echo "$line" ;;
esac
done < input.txt
Problem in your code: this line
[[ $line =~ [Aa]borted || [Ss]topped || isn*t ]]
is not equivalent to this
[[ $line =~ [Aa]borted ]] || [[ $line =~ [Ss]topped ]] || [[ $line =~ isn*t ]]
it is equivalent to this
[[ $line =~ [Aa]borted ]] || [[ -n "[Ss]topped" ]] || [[ -n "isn*t" ]]
because the "or" operator || has higher precedence than the regex match operator =~
Since the last 2 conditions are always true, each line passes.
Note also that you're using glob patterns with a regular expression match operator, so this [[ $line =~ isn*t ]] will not match isn't, it will match is and t with zero or more n in between. (ist, isnt, isnnt, etc)
You intended to write this
[[ $line == [Aa]borted || $line == [Ss]topped || $line == isn*t ]]
which is mote concisely written with case:
case $line in
[Aa]borted | [Ss]topped | isn*t) echo "$line" ;;
*) echo FINE ;;
esac
which is the inverse of my answer.

A simple bash program that prints the third character of a string, that uses [[ ]] (compare to method)

Unfortunately i tried this and it doesn't work, i must use the [[ ]]
read input
for i in input
do
if [[ i = "$input" ]]
then
echo "i"
fi
done
when I run this nothing happens, it only reads my input
This line:
if [[ i = "$input" ]]
should be:
if [[ "$i" = "$input" ]]
OR:
if [[ "$i" == "$input" ]]
PS: Same thing for input also.
Remember that variables in shell are accessed with $ prefix.
May be you can re-factor your script to this:
read input
for i in $input
do
[[ "$i" == "something" ]] && echo "$i"
done
I think when you use only numbers you can also try:
for i in input
do
if [[ $i -eq "$input" ]]
then
echo "$i"
fi
done

Repetitive Sequence in Bash

I'm stuck in something in my bash script, I have a string that composes of a repetitive sequence of 20s, e.g. 202020, there might be more or less 20s, e.g. 2020 or 2020202020, I want to create an if condition that if finds any strange number inside, e.g. 30 in 20203020, gives an error.
Any ideas?
this should do the test:
[[ "$var" =~ "^(20)+$" ]]
check this:
kent$ [[ "202" =~ "^(20)+$" ]] && echo "y" || echo "n"
n
kent$ [[ "20203" =~ "^(20)+$" ]] && echo "y" || echo "n"
n
kent$ [[ "20202002" =~ "^(20)+$" ]] && echo "y" || echo "n"
n
kent$ [[ "20202020" =~ "^(20)+$" ]] && echo "y" || echo "n"
y
how about this example, can you use it?
if [ "`echo "202020302020" | sed -e 's/20//g'`" ];
then echo "there is something";
fi
Extended patterns are a tiny bit more compact than regular expressions.
shopt -s extglob
if [[ $str = +(20) ]]; then
echo "twenties"
else
echo "mismatch"
fi
At some point, bash changed to treat all patterns in [[...]] expressions as extended patterns, so the shopt command may not be necessary.

Resources