grep -c in Bash doesn't recognize parameter - bash

I have an output file, which looks something like this:
value1!value2#value3
value1!value2#value3
value1!value2#value3
I want to find out, with grep, how many times the value2 appears, so I'm doing this:
n = grep -c 'value2' outputfile.tmp
if[$n!=6] ; then
but when I run it, the console says this:
`"-c: command not found"`
like it doesn't recognize the parameter.

This should already choke on the assignment n = with a space: Bash parameter assignments can't have spaces. Secondly, to assign the output of a command to a parameter, you have to use command substitution:
n=$(grep -c 'value2' outputfile.tmp)
Notice that this will tell you how many lines contain at least one occurrence of value2, and not the actual number of value2. Consider:
$ grep -c 'value2' <<< 'value2value2'
1
If you know that there will only be one value2 per line (or you want to count lines), we're good. If you want to count occurrences, you have to use something like
n=$(grep -o 'value2' outputfile.tmp | wc -l)
grep -o prints each match on a separate line, and wc -l counts the lines.
Now, to check if the value of $n is unequal to six, you use a conditional. Unlike the assignments, this must have spaces.
The != comparison is for string comparison; for integers, you should use -ne ("not equal"):
if [ "$n" -ne 6 ]; then
Instead of the test command [ ], Bash has the more flexible [[ ]] conditional epxression:
if [[ "$n" -ne 6 ]]; then
where quoting isn't strictly necessary (but doesn't hurt!).
Or we can use an arithmetic expression:
if (( n != 6 )); then
where any parameter is evaluated without prepending $.

You need to enclose the command with $() to set it to a variable:
n=$(grep -c 'value2' outputfile.tmp)
This is a type of command substitution, see near the bottom of this page: http://www.tldp.org/LDP/abs/html/commandsub.html

Related

Bash parameter expansion from both sides

I'm getting dollar values from a file into the variable in the form
p=$1234.56, and would like remove $ and decimal places to get integer value in my conditional like
if [[ ${p%%.*} < 1000]]; then p=${p}0; fi
But this doesn't remove the $ sign, and I don't want to do it in 2 steps and modify actual variable, as I need $ for later use.
How to get integer value regardless of number of digits ie ($2.2, $123456.1234...)?
Unfortunately, there is no way to avoid performing multiple parameter expansions if you need to remove multiple patterns, in the general case.
In simple cases like this, you can avoid a temporary variable just by assigning back to the same variable.
p=${p#\$}
p=${p%.??}
In your specific scenario, of course, you can just replace any nonnumeric characters globally, and accept that the number will be multiplied by 100. You will obviously then need to multiply the number you compare against correspondingly.
if [[ ${p//[!0-9]/} < 100000 ]]
Of course, for this to work, you need to be sure that your variable's value conforms to your expectations. If the value could have different numbers of decimal places depending on what a user passes in or where you read the input from, you need to perform additional normalizations, or just use a different approach entirely (frequently you'd pass your input to Awk or bc which support floating point math, unlike the shell).
However, the string substitution parameter expansion ${variable//pattern/replacement} is a Bash extension, and not portable to Bourne/POSIX sh.
It's not possible without modifying the var. But you can use a subshell process with something like sed
if [[ $(sed 's/\$\([0-9]*\)\..*/\1/' <<< $p) < 1000 ]]; then p=${p}0; fi
Another option will be to use cut command to extract the substring
before the dot (if any). Then you can say something like:
p='$1234.56'
[[ $(cut -d. -f1 <<< "${p#\$}") < 1000 ]] && p=${p}0
echo "$p"
BTW the expression [[ str1 < str2 ]] performs lexicographical comparison,
meaning [[ 20 < 1000 ]] returns false because 20 sorts after
1000 in dictionary order.
If what you want to do is arithmetic comparison, you'll need to say
[[ val1 -le val2 ]] or (( val1 < val2 )) such as:
p='$1234.56'
[[ $(cut -d. -f1 <<< "${p#\$}") -le 1000 ]] && p=${p}0
echo "$p"

script in bash two if condition and print in for loop

Hello im learning to script in bash, i trying to solve a little exercise.
The Exercise is this
If the variable named "basenew" contains the contents of the variable
named "valuebase". "basenew" must contain more than 113,469
characters. If both conditions are met, the script must then print
the last 20 characters of the variable "basenew".
My code is
#!/bin/bash
basenew="8dm7KsjU28B7v621Jls"
valuebase="ERmFRMVZ0U2paTlJYTkxDZz09Cg"
for i in {1..40}
do
basenew=$(echo $basenew | base64)
if [[ $basenew =~ $valuebase && ${#basenew} -ge 113469 ]] ; then
echo $i
echo $basenew | wc -c
StrLen=`echo ${basenew} | wc -c`
From=`expr $StrLen - 20`
echo $basenew | cut -c ${From}-${StrLen}
else
echo "error"
fi
done
But im stuck, because prints in the loop 28, and are the 20 last but isn't the correct answer.
Any advice to print the last 20 characters using tail -c 20?
Thanks
Why do you think the value of basenew will change during the execution of your loop? You never assign it. But you assign var that you never use, could it be the issue? Yes it is, you need to update basenew, not the unused var.
There is another problem with your script: cut applies your character selection on each line of the input. base64, by default, splits the output in lines of 76 characters. So, after a while, you have many short lines. And as they all are much shorter than your specification cut would only print a lot of newlines, not the last 20 characters of the string. You could change the behaviour of base64 with base64 -w0 but it would also change the results of each iteration and you would probably never match the reference string.
Instead of piping echo in cut follow joop's suggestion: "${basenew: -20: 20}" expands as the 20 last characters of the value of basenew. Or, a bit simpler but equivalent: "${basenew: -20}". Do not forget the space between : and -20, it is needed.
Finally, you could use more built-in bash constructs to avoid external commands (wc, echo, cut), use the modern $(...) instead of the old backticks, and use the arithmetic evaluation (((...))) instead of the obsolete expr. Try, maybe:
#!/usr/bin/env bash
# file: foo.sh
basenew="8dm7KsjU28B7v621Jls"
valuebase="ERmFRMVZ0U2paTlJYTkxDZz09Cg"
for i in {1..40}; do
basenew=$(echo "$basenew" | base64)
if [[ $basenew =~ $valuebase ]] && (( ${#basenew} >= 113469 )) ; then
printf '%d\n%d\n%s\n' "$i" "${#basenew}" "${basenew: -20}"
else
printf 'error\n'
fi
done
Demo:
$ ./foo.sh
error
...
error
28
113469
U2paTlJYTkxDZz09Cg==
error
...
error
Any advice to print the last 20 characters using tail
From the tail man-page:
-c, --bytes=[+]NUM
output the last NUM bytes; or use -c +NUM to output starting with
byte NUM of each file
Hence, if you have a variable x, you get the last 20 characters of it printed to stdout using
tail --bytes=20 <<<$x

Using sed to get only the digits from all parameters to a bash script

I need a help, please
I have script
#!/bin/bash
NUMBER=$(echo "$1" | sed 's/[^0-9]*//g')
echo $NUMBER
./test.sh tas1 tefst2 thgst3 ynft4 jhuf5 hjh6 jhd7
1
returns only 1 but I need it 1234567
The reason you only get "1" is because $1 is only the first parameter. Use "$*" to get a single string containing all the parameters, separated by spaces (by default).
You can also do this with bash variable substitution:
params="$*"
echo "${params//[^[:digit:]]/}"

Grep a specific string from bash script and compare

i have a large log file A.log, i want to grep ONE CERTAIN STRING from the last 10 lines and compare to a variable (FTP_SUCCESS_MSG), how can i do that?
something like:
logs='/tmp/A.log'
FTP_SUCCESS_MSG="226 Transfer complete"
if [tail -10 $logs == $FTP_SUCCESS_MSG] ;
then
echo "Success"
else
echo "Failed"
exit 1
fi
if tail -10 "$logs" | grep -Fq "$FTP_SUCCESS_MSG" ; then ...
Notice how [ is not present in the condition (and if it were, it would require non-optional spaces on both sides).
Notice also how variable interpolations are in double quotes unless you require the shell to tokenize the value and perform wildcard expansion on the tokens.

test command in KornShell (ksh)

I have a question on the test command in the KornShell (ksh). I know -ne is for comparing integers and != is for comparing strings. How will the test command behave if one argument is a string and the other is an integer? I have below conditions in my code and both are working properly.
Code:
myCount=1
myCount=`expr $myCount+ 0`
temp=`ps -aef | grep damn | wc -l`
if [ $temp -ne $myCount]; then
echo ERROR Number
fi
if [ $temp != $myCount ]; then
echo ERROR Strings
fi
Output:
ERROR Number
ERROR Strings
The type is not relevant because it's a simple text substitution. In other words, the value of the variable $temp will be substituted in place of $temp (for example).
At least for the version of ksh I'm running, for the numeric comparison, if the value starts with a non-numeric, it will equate to 0. If it starts with a numeric but contains non-numerics, you will get an error.
For example:
$ export s1=xyz
$ export s2=7xyz
$ export i1=0
$ if [ $i1 -eq $s1 ]
> then
> echo equal
> fi
equal
$ if [ $i1 -eq $s2 ]
> then
> echo equal
> fi
ksh: 7xyz: bad number `7xyz'
However, based on your comments, that may not be the case for all versions of ksh.
Based on that, I would try to ensure that you use string comparisons for strings and numeric comparisons for numbers. Anything else may be non-portable.
But your code is flawed anyway.
temp=ps -aef | grep damn | wc -l
will always return at least 1, since it will find the grep command as well as being a string padded with leading spaces, which is why both of your tests are true.
Piping to wc is also unnecessary since the -c switch of grep will count for you.
better code would be:
temp=ps -aef |grep damn |grep -cv grep
which will return the number of running instances of processes containing the damn string and it will be a number.
Using ksh93 and GNU coreutils expr 7.4 your command:
myCount=`expr $myCount+ 0`
gives me a syntax error and sets myCount to null which causes both if statements to output "ksh: [: argument expected" errors. Try putting a space before the plus sign. Also, there needs to be a space before ].
You shouldn't need to convert myCount or temp to integers. The coercion of myCount using expr is completely unnecessary.
I prefer this form for comparing integers since it allows you to use symbolic comparison operators such as != and > instead of -ne and -gt:
if (( $temp != $myCount ))

Resources