I am constructing a script to count the occurrences of two letters in a given string. I cannot figure out how to make the variable a testable number.
#!/bin/bash
touch ~/trfindlog.txt ~/trfindt ~/trfindr
echo $1 > ~/trfindlog.txt
cat ~/trfindlog.txt | grep -oi r | wc -l > ~/trfindr
cat ~/trfindlog.txt | grep -oi t | wc -l > ~/trfindt
varR='/trfindr'
varT='/trfindt'
if [[ "${varR}" -eq 0 && "${varT}" -eq 0 ]]
then
echo "This phrase contains no Rs or Ts."
elif [[ "${varR}" -eq 1 && "${varT}" -eq 1 ]]
then
echo "This phrase contains 1 R and 1 T."
elif [[ "${varR}" -gt 1 && "${varT}" -eq 1 ]]
then
echo "This phrase contains ${varR} Rs and 1 T."
elif [[ "${varR}" -eq 1 && "${varT}" -gt 1 ]]
then
echo "This phrase contains 1 R and ${varT} Ts."
elif [[ "${varR}" -gt 1 && "${varT}" -gt 1 ]]
then
echo "This phrase contains ${varR} Rs and ${varT} Ts."
fi
rm ~/trfindlog.txt ~/trfindt ~/trfindr
exit
This script is giving me the following errors.
/automount/home/jcampbell/tools/itc/trfind.sh: line 12: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 16: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 20: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 24: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
/automount/home/jcampbell/tools/itc/trfind.sh: line 28: [[: /trfindr: syntax error: operand expected (error token is "/trfindr")
Here is the working script. This is just for kicks & educating myself. I am glad to receive a variety of answers.
#!/bin/bash
touch ~/trfindlog.txt
echo $1 > ~/trfindlog.txt
varR=$(echo $1 | tr -cd r)
varT=$(echo $1 | tr -cd t)
if [[ "${#varR}" -eq 0 && "${#varT}" -eq 0 ]]
then
echo "This phrase contains no Rs or Ts."
elif [[ "${#varR}" -eq 1 && "${#varT}" -eq 1 ]]
then
echo "This phrase contains 1 R and 1 T."
elif [[ "${#varR}" -gt 1 && "${#varT}" -eq 1 ]]
then
echo "This phrase contains ${#varR} Rs and 1 T."
elif [[ "${#varR}" -eq 1 && "${#varT}" -gt 1 ]]
then
echo "This phrase contains 1 R and ${#varT} Ts."
elif [[ "${#varR}" -gt 1 && "${#varT}" -gt 1 ]]
then
echo "This phrase contains ${#varR} Rs and ${#varT} Ts."
fi
rm ~/trfindlog.txt
exit
Putting the variable in a file is cumbersome, inelegant, and, with a static filename, will break if you run two instances of the script at the same time. All of this would be a lot more succinct with the variable in memory.
With Bash, you can make a copy of the variable and perform a simple substitution.
Rs=${1//[!R]/}
Ts=${1//[!T]/}
Now, the length of each of these strings is the number of occurrences of the characters you were looking for.
echo "We have ${#Rs} R characters and ${#Ts} T characters."
Deciding whether to print a plural s should be a simple addition. Hint: If the first string is exactly R you want to suppress the s. But if you want flexible wording, it may be simpler to use a case statement over the possibilities.
case $Rs:$Ts in
:) echo "We have none of either";;
R:) echo "We have one R and no Ts.";;
:T) echo "We have no Rs and one T.";;
R:T) echo "We have one of each.";;
*:) echo "We have ${#Rs} Rs and no Ts.";;
*:T) echo "We have ${#Rs} Rs and one T.";;
:*) echo "We have no Rs and ${#Ts} Ts.";;
R:*) echo "We have one R and ${#Ts} Ts.";;
*:*) echo "We have ${#Rs} and ${#Ts} Ts.";;
esac
I would still be tempted to handle the cornermost cases : and R:T as above, and then generate a string from smaller pieces in the remaining cases.
You need to use something like
var=$(grep -c r ~/trfindlog.txt)
Note that grep will count the number of matching lines, and not the number of matches. So, what you really need is more something like:
var=$(echo $1 | tr -cd r)
echo ${#var}
Related
Basically im trying to add up all the numbers in a file called numbers.txt . It contains non-number strings as well .
Here is my shell script
#!/bin/bash
sum=0
x=$(cat numbers.txt)
re='^[0-9]+$'
for i in $x
do
echo $i
if [ $i = re ]
then
sum=`expr $sum + $i`
fi
done
echo $sum
Here is the Output
abc
hellow
123
1
2
3
hello67
39
0
Below is txt file
abc hellow 123
1 2 3
hello67 39
The output instead of zero should have been 168 .
Corrected your script a little, for comparing with regex i use =~:
#!/bin/bash
sum=0
x=$(cat numbers.txt)
re='^[0-9]+$'
for i in $x
do
echo $i
if [[ $i =~ $re ]]
then
sum=$((sum + i))
fi
done
echo $sum
You are literally comparing the strings, not matching a regex.
You can use grep for regex matching, for example:
#!/bin/bash
sum=0
x=$(cat numbers.txt)
re='^[0-9]+$'
for i in $x
do
echo $i
echo $i | grep -oP "${re}" &> /dev/null
if [ $? == "0" ]
then
sum=`expr $sum + $i`
fi
done
echo $sum
echo $i | grep -oP "${re}" will pipe the text into grep. If it matches the regex, grep returns 0 which will be written into the special variable $?. So if that is 0, you know you have a number and can sum it up. That is the reason for if [ $? == "0" ].
Btw: = will assign a value to a variable, to compare, you need to use ==.
When using [ it actually does, my bad.
Trying to fetch a webpage as a lowercase string, then search the string output for a substring
My attempt:
1 #!/usr/bin/env bash
2
3 URL="https://somesite.com"
4 MOVIES_SOURCE="movies.txt"
5 PAGE=`curl "$URL"` | tr '[:upper:]' '[:lower:]'
6
7 while IFS= read -r movie
8 do
9 FOUND="$($PAGE =~ "$movie")"
10 echo $FOUND
11 if [[ $FOUND ]]; then
12 echo "$movie found"
13 fi
14 done < $MOVIES_SOURCE
15
When I run this, I'm receiving line 9: =~: command not found
The $movie variable is valid and contains each line from movies.txt, but I'm struggling to figure out this one!
If you want to use regex matching in bash:
if [[ $PAGE =~ $movie ]]; then
echo "$movie found"
fi
example:
PAGE="text blah Avengers more text"
movie="Avengers"
if [[ $PAGE =~ $movie ]]; then
echo "$movie found"
fi
gives:
Avengers found
Also: to capture the output of the whole curl command:
PAGE=$(curl "$URL" | tr '[:upper:]' '[:lower:]')
always prefer $() over backticks
you had to wrap your whole command in order for $PAGE to contain the output where you converted to lowercase.
I have some files with content like this:
file1:
AAA
BBB
CCC
123
file2:
AAA
BBB
123
I want to echo the filename only if the first 3 lines are letters, or "file1" in the samples above.
Im merging the 3 lines into one and comparing it to my regex [A-Z], but could not get it to match for some reason
my script:
file=file1
if [[ $(head -3 $file|tr -d '\n'|sed 's/\r//g') == [A-Z] ]]; then
echo "$file"
fi
I ran it with bash -x, this is the output
+ file=file1
++ head -3 file1
++ tr -d '\n'
++ sed 's/\r//g'
+ [[ ASMUTCEDD == [A-Z] ]]
+exit
What you missed:
You can use grep to check that the input matches only [A-Z] characters (or indeed Bash's built-in regex matching, as #Barmar pointed out)
You can use the pipeline directly in the if statement, without [[ ... ]]
Like this:
file=file1
if head -n 3 "$file" | tr -d '\n\r' | grep -qE '^[A-Z]+$'; then
echo "$file"
fi
To do regular expression matching you have to use =~, not ==. And the regular expression should be ^[A-Z]*$. Your regular expression matches if there's a letter anywhere in the string, not just if the string is entirely letters.
if [[ $(head -3 $file|tr -d '\n\r') =~ ^[A-Z]*$ ]]; then
echo "$file"
fi
You can use built-ins and character classes for this problem:-
#!/bin/bash
file="file1"
C=0
flag=0
while read line
do
(( ++C ))
[ $C -eq 4 ] && break;
[[ "$line" =~ '[^[:alpha:]]' ]] && flag=1
done < "$file"
[ $flag -eq 0 ] && echo "$file"
Using ONLY the commands: echo, grep, sed
Argument $1 of the script is a code that has to be translated according to the table below and the translation sent to STDOUT. The code is a one to four digit code that starts in "A" and increments alphabetically to "ZZZZ". For example: A, B, .... Z, AA, AB,.....
Code - Translation
- A -> 1
- B to AA -> 2
- AB to AF -> 3
- AG to ZZZZ -> 4
For example if the script is called script.sh A the output would be 1. If the script is called script.sh ABC the output would be 4.
#!/bin/bash
echo $1 | grep -e '^A$'>/dev/null && echo 1 && exit
echo $1 | grep -e '^[^A]$' -e '^[A][A]$' >/dev/null && echo 2 && exit
echo $1 | grep -e '^[A][B-F]$' >/dev/null && echo 3 && exit
echo $1 | grep -e '^[A-Z]\{2,4\}$' >/dev/null && echo 4 && exit
echo 0
exit
#!/bin/bash
str=$1
if [[ -z "${str#A}" ]]
then
echo 1
elif [[ -z "${str#?}" || -z "${str#AA}" ]]
then
echo 2
elif [[ -z "${str/A[B-F]/}" ]]
then
echo 3
else
echo 4
fi
# just in case, so you can say you used them:
grep . < /dev/null | sed > /dev/null
EDIT: silly ., you should be ?.
EDIT2: Command [ is gone, non-command [[ to the rescue!
How do I check if a variable contains characters (regex) other than 0-9a-z and - in pure bash?
I need a conditional check. If the string contains characters other than the accepted characters above simply exit 1.
One way of doing it is using the grep command, like this:
grep -qv "[^0-9a-z-]" <<< $STRING
Then you ask for the grep returned value with the following:
if [ ! $? -eq 0 ]; then
echo "Wrong string"
exit 1
fi
As #mpapis pointed out, you can simplify the above expression it to:
grep -qv "[^0-9a-z-]" <<< $STRING || exit 1
Also you can use the bash =~ operator, like this:
if [[ ! "$STRING" =~ [^0-9a-z-] ]] ; then
echo "Valid";
else
echo "Not valid";
fi
case has support for matching:
case "$string" in
(+(-[[:alnum:]-])) true ;;
(*) exit 1 ;;
esac
the format is not pure regexp, but it works faster then separate process with grep - which is important if you would have multiple checks.
Using Bash's substitution engine to test if $foo contains $bar
bar='[^0-9a-z-]'
if [ -n "$foo" -a -z "${foo/*$bar*}" ] ; then
echo exit 1
fi