Check occurence of a string with a variable number - bash

Check occurence of a string with a variable number
I have to check a certain condition, and that condition is that whenever somewhere in a certain url there is a line that states '[X] wrong' then return 'TestNotSuccesfull'. The X here could be '1','2,' etcetera.
I don't know how to make a statement that can check this, other then check for '1 wrong' OR '2 wrong' or '3 wrong', etc. There must be a better solution. Also because theoretically the X in 'X wrong' can lead up to multiple thousands...
Beneath the command line I made in bash that checks whether '1 wrong' occurs.
export result=`curl http://websiteABC.com | grep '1 wrong' | wc -l`; if [ $result -ge 1 ]; then echo "TestNotSuccesfull"; else echo "TestSuccesfull"; fi
Can anyone help me out?
PS, the string that has to be checked actually is part of '(...) right, X wrong, (...)'.

Regexes are your friend.
if curl http://websiteABC.com | grep -qE '[0-9]*[1-9][0-9]* wrong'
then echo "TestNotSuccesfull"
else echo "TestSuccesfull"
fi
if can take a complex command as it's argument, and any nonzero return evaluates as false.
grep can take Extended regular expressions if you use -E, and -q tells it not to send the matching lines to stdout, but just return n exit code to indicate whether they were found. [0-9]+ means one or more numerals.
I edited the pattern. This one will not falsely report a fail on 0 wrong but will hit on 01 wrong or 10 wrong, etc.
Again, c.f. the link at the beginning of my answer, but -
[...] is a "character class", which means it matches ONE character like any of those between the brackets. It can be elaborated with additional metacharacters:
* means "zero or more" of the preceding pattern, so [0-9]* will match any number of consecutive digits. This means it will match 123454321, or an empty string... so if tested against "ab" it would match at the beginning, the end, and between the letters.
[0-9]*[1-9][0-9]* means "any or no digits" ([0-9]*) followed by any nonzero digit ([1-9]) followed by "any or no digits" ([0-9]*). This allows leading zeroes, ending zeros, but not ONLY zeros. If there isn't at least one nonzero digit it fails.

Related

In bash how can I get the last part of a string after the last hyphen [duplicate]

I have this variable:
A="Some variable has value abc.123"
I need to extract this value i.e abc.123. Is this possible in bash?
Simplest is
echo "$A" | awk '{print $NF}'
Edit: explanation of how this works...
awk breaks the input into different fields, using whitespace as the separator by default. Hardcoding 5 in place of NF prints out the 5th field in the input:
echo "$A" | awk '{print $5}'
NF is a built-in awk variable that gives the total number of fields in the current record. The following returns the number 5 because there are 5 fields in the string "Some variable has value abc.123":
echo "$A" | awk '{print NF}'
Combining $ with NF outputs the last field in the string, no matter how many fields your string contains.
Yes; this:
A="Some variable has value abc.123"
echo "${A##* }"
will print this:
abc.123
(The ${parameter##word} notation is explained in ยง3.5.3 "Shell Parameter Expansion" of the Bash Reference Manual.)
Some examples using parameter expansion
A="Some variable has value abc.123"
echo "${A##* }"
abc.123
Longest match on " " space
echo "${A% *}"
Some variable has value
Longest match on . dot
echo "${A%.*}"
Some variable has value abc
Shortest match on " " space
echo "${A%% *}"
some
Read more Shell-Parameter-Expansion
The documentation is a bit painful to read, so I've summarised it in a simpler way.
Note that the '*' needs to swap places with the ' ' depending on whether you use # or %. (The * is just a wildcard, so you may need to take off your "regex hat" while reading.)
${A% *} - remove shortest trailing * (strip the last word)
${A%% *} - remove longest trailing * (strip the last words)
${A#* } - remove shortest leading * (strip the first word)
${A##* } - remove longest leading * (strip the first words)
Of course a "word" here may contain any character that isn't a literal space.
You might commonly use this syntax to trim filenames:
${A##*/} removes all containing folders, if any, from the start of the path, e.g.
/usr/bin/git -> git
/usr/bin/ -> (empty string)
${A%/*} removes the last file/folder/trailing slash, if any, from the end:
/usr/bin/git -> /usr/bin
/usr/bin/ -> /usr/bin
${A%.*} removes the last extension, if any (just be wary of things like my.path/noext):
archive.tar.gz -> archive.tar
How do you know where the value begins? If it's always the 5th and 6th words, you could use e.g.:
B=$(echo "$A" | cut -d ' ' -f 5-)
This uses the cut command to slice out part of the line, using a simple space as the word delimiter.
As pointed out by Zedfoxus here. A very clean method that works on all Unix-based systems. Besides, you don't need to know the exact position of the substring.
A="Some variable has value abc.123"
echo "$A" | rev | cut -d ' ' -f 1 | rev
# abc.123
More ways to do this:
(Run each of these commands in your terminal to test this live.)
For all answers below, start by typing this in your terminal:
A="Some variable has value abc.123"
The array example (#3 below) is a really useful pattern, and depending on what you are trying to do, sometimes the best.
1. with awk, as the main answer shows
echo "$A" | awk '{print $NF}'
2. with grep:
echo "$A" | grep -o '[^ ]*$'
the -o says to only retain the matching portion of the string
the [^ ] part says "don't match spaces"; ie: "not the space char"
the * means: "match 0 or more instances of the preceding match pattern (which is [^ ]), and the $ means "match the end of the line." So, this matches the last word after the last space through to the end of the line; ie: abc.123 in this case.
3. via regular bash "indexed" arrays and array indexing
Convert A to an array, with elements being separated by the default IFS (Internal Field Separator) char, which is space:
Option 1 (will "break in mysterious ways", as #tripleee put it in a comment here, if the string stored in the A variable contains certain special shell characters, so Option 2 below is recommended instead!):
# Capture space-separated words as separate elements in array A_array
A_array=($A)
Option 2 [RECOMMENDED!]. Use the read command, as I explain in my answer here, and as is recommended by the bash shellcheck static code analyzer tool for shell scripts, in ShellCheck rule SC2206, here.
# Capture space-separated words as separate elements in array A_array, using
# a "herestring".
# See my answer here: https://stackoverflow.com/a/71575442/4561887
IFS=" " read -r -d '' -a A_array <<< "$A"
Then, print only the last elment in the array:
# Print only the last element via bash array right-hand-side indexing syntax
echo "${A_array[-1]}" # last element only
Output:
abc.123
Going further:
What makes this pattern so useful too is that it allows you to easily do the opposite too!: obtain all words except the last one, like this:
array_len="${#A_array[#]}"
array_len_minus_one=$((array_len - 1))
echo "${A_array[#]:0:$array_len_minus_one}"
Output:
Some variable has value
For more on the ${array[#]:start:length} array slicing syntax above, see my answer here: Unix & Linux: Bash: slice of positional parameters, and for more info. on the bash "Arithmetic Expansion" syntax, see here:
https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Arithmetic-Expansion
https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Arithmetic
You can use a Bash regex:
A="Some variable has value abc.123"
[[ $A =~ [[:blank:]]([^[:blank:]]+)$ ]] && echo "${BASH_REMATCH[1]}" || echo "no match"
Prints:
abc.123
That works with any [:blank:] delimiter in the current local (Usually [ \t]). If you want to be more specific:
A="Some variable has value abc.123"
pat='[ ]([^ ]+)$'
[[ $A =~ $pat ]] && echo "${BASH_REMATCH[1]}" || echo "no match"
echo "Some variable has value abc.123"| perl -nE'say $1 if /(\S+)$/'

How to check for inequality in bash for loop?

I have the following for loop in a bash script:
for (( j = i; ${1:j:3} != " "; j=j + 1 ))
do
sleep 0.1
done
printf '%s' "${letter[${1:i:j}]}"
i=$j
When run, it leads to an infinite loop of the following errors:
/home/com/morsecoder.sh: line 188: letter: bad array subscript
/home/com/morsecoder.sh: line 184: ((: ... != : syntax error: operand expected (error token is "... != ")
The problem is on the first line; the bad array subscript error is almost certainly a byproduct of that.
I can see the error is caused by my ${1:j:3} != " ". Basically, what I need is for the loop to run through the characters in a string until it finds three consecutive spaces. The string contains Morse code, and each letter is separated by 3 characters (because in American Morse, letters can contain 0, 1, or 2 spaces, so 3 is the minimum letter delimiter).
Afterward, I convert what I have detected to be a complete Morse letter to English and print it out, and then move on to the next Morse characters.
The printf part seems to be working fine, but the error here has me puzzled. I checked and I am using (( and )) properly as well as != to check for inequality. I also tried enclosing ${1:j:3} in quotes, but that did nothing. How can I rephrase the for loop so that I don't get an error about invalid syntax?
This form of the for loop is only for arithmetic operations. You need to use a while loop instead:
j=$i
while [[ ${1:j:3} != " " ]]; do
sleep 0.1
j=$((j+1))
done
Basically, what I need is for the loop to run through the characters
in a string until it finds three consecutive spaces. The string
contains Morse code, and each letter is separated by 3 characters
(because in American Morse, letters can contain 0, 1, or 2 spaces, so
3 is the minimum letter delimiter).
Well that seems an odd way to go about it. The shell has better mechanisms for this task than scanning the string by iterating over an index. For example,
# the value of $1, with the longest suffix matching glob pattern " *" removed.
letter=${1%% *}
The purpose of the sleep 0.1 in your example code is not clear to me, but if you're simply simulating the timing of receiving a signal via Morse code (complete with different timings for letters of different Morse length) then it can be addressed separately.
Afterward, I convert what I have detected to be a complete Morse
letter to English and print it out, and then move on to the next Morse
characters.
So I would approach it more like this:
morse=$1
while [[ -n "$morse" ]]; do
# extract the next letter
next_let=${morse%% *}
# sleep for a duration based on the (Morse) length of the letter
sleep "0.${#next_let}"
# print the corresponding decoded Latin letter
printf '%s' "${letter[${next_let}]}"
# remove the Morse letter and its delimiter, if any
morse=${morse:$((${#next_let}+3))}
done
That covers your loop over all the Morse letters, by the way, not just one.
Iterating over a string by index is not necessarily wrong in shell code, but it has bad code smell.

Shell Bash Replace or remove part of a number or string

Good day.
Everyday i receive a list of numbers like the example below:
11986542586
34988745236
2274563215
4532146587
11987455478
3652147859
As you can see some of them have a 9(11 digits total) in as the third digit and some dont(10 digits total, that`s because the ones with an extra 9 are the new Brazilian mobile number format and the ones without it are in the old format.
The thing is that i have to use the numbers in both formats as a parameter for another script and i usually have do this by hand.
I am trying to create a script that reads the length of a mobile number and check it`s and add or remove the "9" of a number or string if the digits condition is met and save it in a separate file condition is met.
So far i am only able to check its length but i don`t know how to add or remove the "9" in the third digit.
#!/bin/bash
Numbers_file="/FILES/dir/dir2/Numbers_File.txt"
while read Numbers
do
LEN=${#Numbers}
if [ $LEN -eq "11" ]; then
echo "lenght = "$LEN
elif [ $LEN -eq "10" ];then
echo "lenght = "$LEN
else
echo "error"
fi
done < $Numbers_file
You can delete the third character of any string with sed as follows:
sed 's/.//3'
Example:
echo "11986542586" | sed 's/.//3'
1186542586
To add a 9 in the third character:
echo "2274563215" | sed 's/./&9/3'
22794563215
If you are absolutely sure about the occurrence happening only at the third position, you can use an awk statement as below,
awk 'substr($0,3,1)=="9"{$0=substr($0,1,2)substr($0,4,length($0))}1' file
1186542586
3488745236
2274563215
4532146587
1187455478
3652147859
Using the POSIX compliant substr() function, process only the lines having 9 at the 3rd position and move around the record not considering that digit alone.
substr(s, m[, n ])
Return the at most n-character substring of s that begins at position m, numbering from 1. If n is omitted, or if n specifies more characters than are left in the string, the length of the substring shall be limited by the length of the string s
There are lots of text manipulation tools that will do this, but the lightest weight is probably cut because this is all it does.
cut only supports a single range but does have an invert function so cut -c4 would give you just the 4th character, but add in --complement and you get everything but character 4.
echo 1234567890 | cut -c4 --complement
12356789

How to extend string to certain length

Hey basically right now my program gives me this output:
BLABLABLA
TEXTEXOUAIGJIOAJGOAJFKJAFKLAJKLFJKL
TEXT
MORE TEXT OF RANDOM CHARACTER OVER LIMIT
which is a result of for loop. Now here's what i want:
if the string raches over 10 characters, cut the rest and add two dots & colon to the end "..:"
otherwise (if the string has less than 10 characters) fill the gap with spaces so they're alligned
so on the example i provided i'd want something like this as output:
BLABLABLA :
TEXTEXOUA..:
TEXT :
MORE TEXT..:
I also solved the first part of the problem (when its over 10 characters), only the second one gives me trouble.
AMOUNT=definition here, just simplyfying so not including it
for (( i=1; i<="$AMOUNT"; i++ )); do
STRING=definition here, just simplyfying so not including it
DOTS="..:"
STRING_LENGTH=`echo -n "$STRING" | wc -c`
if [ "$STRING_LENGTH" -gt 10 ]
then
#Takes
STRING=`echo -n "${STRING:0:10}"$DOTS`
else
#now i dont know what to do here, how can i take my current $STRING
#and add spaces " " until we reach 10 characters. Any ideas?
fi
Bash provides a simple way to get the length of a string stored in a variable: ${#STRING}
STRING="definition here, just simplyfying so not including it"
if [ ${#STRING} -gt 10 ]; then
STR12="${STRING:0:10}.."
else
STR12="$STRING " # 12 spaces here
STR12="${STR12:0:12}"
fi
echo "$STR12:"
The expected output you posted doesn't match the requirements in the question. I tried to follow the requirements and ignored the sample expected output and the code you posted.
Use printf:
PADDED_STRING=$(printf %-10s $STRING)

Bash: Using '\b' makes strings not equal?

I'm a bit perplexed as to why this doesn't return true...
if [ "$(echo -e 'b\bg')" == "g" ]; then
echo "true"
else
echo "false"
fi
Even without ' (instead a \") it doesn't work.
Running it in a console does:
~$ echo -e 'b\bg'
g
So, does g not equal g in this senario or something?
\b does not remove the preceding character from the string; when displayed, it causes the cursor to move backwards one position, resulting in the preceding character being overwritten by the following character. b\bg is still a 3-character string that will not match any one-character string, even if they look identical when displayed. (For that matter, it is not guaranteed that a terminal will treat \b as moving the cursor back; it might simply display an unprintable character glyph in its place, e.g. b?g.)

Resources