I need to increment the package version in the spec file for automating RPM builds.
FILE=somefile1.spec
OLD=$(grep "packageversion 2.64" "$FILE" | awk {'print $3'})
NEW=$(($OLD | bc -l))
echo $NEW
Returns:
change_spec: line 11: 2.64 | bc -l: syntax error: invalid arithmetic operator (error token is ".64 | bc -l")
I am open to suggestions, as you can see, my script writing abilities are nil.
If $OLD equals .64, then you can do:
NEW=$((${OLD:1} + 1))
Just echo $OLD before to see its content (and report it here!).
${OLD:1} simply do a substring, as described here: Substring Extraction.
Related
I am writing a bash script that has 1) number of lines in a file matching a pattern and 2) total lines in a file.
a) To get the number of lines in a file within a directory that had a specific pattern I used grep -c "pattern" f*
b) For overall line count in each file within the directory I used
wc -l f*
I am trying to divide the output from 2 by 1. I have tried a for loop
for i in $a
do
printf "%f\n" $(($b/$a)
echo i
done
but that returns an error syntax error in expression (error token is "first file in directory")
I also have tried
bc "$b/$a"
which does not work either
I am not sure if this is possible to do -- any advice appreciated. thanks!
Sample: grep -c *f generates a list like this
myfile1 500
myfile2 0
myfile3 14
myfile4 18
and wc -l *f generates a list like this:
myfile1 500
myfile2 500
myfile3 500
myfile4 238
I want my output to be the outcome of output for grep/wc divided so for example
myfile1 1
myfile2 0
myfile3 0.28
myfile4 0.07
bash only supports integer math so the following will print the (silently) truncated integer value:
$ a=3 b=5
$ printf "%f\n" $(($b/$a))
1.000000
bc is one solution and with a tweak of OP's current code:
$ bc <<< "scale=2;$b/$a"
1.66
# or
$ echo "scale=4;$b/$a" | bc
1.6666
If you happen to start with real/float numbers the printf approach will error (more specifically, the $(($b/$a)) will generate an error):
$ a=3.55 b=8.456
$ printf "%f\n" $(($b/$a))
-bash: 8.456/3.55: syntax error: invalid arithmetic operator (error token is ".456/3.55")
bc to the rescue:
$ bc <<< "scale=2;$b/$a"
2.38
# or
$ echo "scale=4;$b/$a" | bc
2.3819
NOTE: in OP's parent code there should be a test for $a=0 and if true then decide how to proceed (eg, set answer to 0; skip the calculation; print a warning message) otherwise the this code will generate a divide by zero error
bash doesn't have builtin floating-point arithmetic, but it can be simulated to some extent. For instance, in order to truncate the value of the fraction a/b to two decimal places (without rounding):
q=$((100*a/b)) # hoping multiplication won't overflow
echo ${q:0:-2}.${q: -2}
The number of decimal places can be made parametric:
n=4
q=$((10**n*a/b))
echo ${q:0:-n}.${q: -n}
This awk will do it all:
awk '/pattern/{a+=1}END{print a/NR}' f*
jot 93765431 |
mawk -v __='[13579]6$' 'BEGIN {
_^=__=_*=FS=__ }{ __+=_<NF } END { if (___=NR) {
printf(" %\47*.f / %\47-*.f ( %.*f %% )\n",
_+=++_*_*_++,__,_,___,_--,_*__/___*_) } }'
4,688,271 / 93,765,431 ( 4.99999941343 % )
filtering pattern = [13579]6$
I have some trouble with such an easy task...
Please find relevant code below:
loewdin_fuk=$(echo $line_fukui|awk '{print $4}')
nbo_fuk=$(echo $line_fukui|awk '{print $5}')
echo "loewdin_fuk $loewdin_fuk nbo_fuk $nbo_fuk"
aver_fuk=$(($loewdin_fuk + $nbo_fuk))
\#aver_fuk=$(echo "scale=4; 0.5*($loewdin_fuk $nbo_fuk)" | bc -l)
The output is:
loewdin_fuk +0.1662 nbo_fuk +0.1865
./collectFukui.sh: line 151: +0.1662 + +0.1865: syntax error: invalid
arithmetic operator (error token is ".1662 + +0.1865")
Using the command line:
aver_fuk=$(echo "scale=4; 0.5*($loewdin_fuk $nbo_fuk)" | bc -l)
leads to following output:
loewdin_fuk +0.1662 nbo_fuk +0.1865
(standard_in) 1: syntax error
I don't get what's wrong... Thank you in advance!
Best,
Me
The problem here is that bc does not consider + to be a unary operator. So +0.1662 +0.1865 is invalid syntax. (It would have worked ok if the first number were negative, because - is a unary operator.)
So if you want to use bc, you need to do something like:
aver_fuk=$(echo "scale=4; 0.5*(0$loewdin_fuk $nbo_fuk)" | bc -l)
Prepending 0 without a space will work whether or not $loewdin_fuk starts with a sign character. If you put a space in between, it would work with values with explicit sign characters, but fail on values without a sign.
I want to write a KornShell (ksh) script to get the latest three versions for a file in the directory having lot of files with various versions (files with prefix as same but time stamp as suffix) and compress it and at the same time have to remove the remaining versions of the file (other than latest three versions of the file).
I'm new to KornShell scripting. Can any one provide me with solution?
The directory structure is like this:
abcd.11122013.txt
abcd.12122013.txt
abcd.10122013.txt
abcd.09122013.txt
xyz.11122013.txt
xyz.12122013.txt
xyz.10122013.txt
......................
In this I want the latest 3 version of files starting with abcd* as prefix. Similarly files starting with xyz*.
Assuming the file modification times are correct, it could be as simple as:
ls -tr abcd* | tail -3
One way using perl :
printf '%s\n' * |
perl -F'\'. -lane '
/^abcd/ and
$h{
substr($F[1], 4, 4),
substr($F[1], 2, 2),
substr($F[1], 0, 2)
} = $_;
END{
$c=0;
for (sort $a <=> $b, keys %h) {
print $h{$_};
$c++ == 3 and last;
}
}
'
There are undoubtedly simpler ones but here is one way:
for base in abcd xyz
do
ls ${base}* | sed 's/\(.*\).\(..\)\(..\)\(....\).txt/\4 \3 \2 \1/' | sort | tail -3 | while read a b c d;do echo $d.$c$b$a.txt; done
done
Update: using an array to store the prefixes and showing one way to call the archive function for each file.
#!/bin/ksh
typeset -a prefix=(abcd xyz foo bar)
for base in "${prefix[#]}"
do
for file in $(ls "${base}"* |
sed 's/\(.*\).\(..\)\(..\)\(....\).txt/\4 \3 \2 \1/' |
sort |
tail -3 |
while read a b c d; do
echo $d.$c$b$a.txt
done); do
echo archive $file
done
done
In UNIX environment, I have a file.txt that contains following details:
Data recording started:
0001100 Matched at 412090
0001101 Mismatched at 414798
0001102 Matched at 420007
0001103 Mismatched at 420015
Job completed
How do I can get the first Matched value by searching "Matched" (line 2) word and also for the first "Mismatched" (line 3)
Find the difference between them and store as a variable, "dif"
The result is Matched minus Mismatched, so it cannot find the data by specify line number, i.e. find line 3 last integers minus line 2 last integers, because the mismatched may come at first like following:
Data recording started:
0001100 Mismatched at 412090
0001101 Matched at 414798
0001102 Mismatched at 420007
0001103 Matched at 420015
Job completed
One way:
echo $((
$(grep Matched input | head -1 | sed 's/.*at //')
- $(grep Mismatched input | head -1 | sed 's/.*at //')
))
or using only sed:
echo $((
$(sed -n 's/.*Matched.*at //p' input | head -1)
- $(sed -n 's/.*Mismatched.*at //p' input | head -1)
))
Output
-2708
We can use grep -m 1 to kick away head.
dif=$((
$(grep -m 1 'Matched' a.txt | sed 's/.*at \([0-9]*\).*/\1/')
- $(grep -m 1 'Mismatched' a.txt | sed 's/.*at \([0-9]*\).*/\1/')
))
echo $dif
In a text file, test.txt, I have the next information:
sl-gs5 desconnected Wed Oct 10 08:00:01 EDT 2012 1001
I want to extract the hour of the event by the next command line:
hour=$(grep -n sl-gs5 test.txt | tail -1 | cut -d' ' -f6 | awk -F ":" '{print $1}')
and I got "08". When I try to add 1,
14 echo $((hour+1))
I receive the next error message:
./test2.sh: line 14: 08: value too great for base (error token is "08")
If variables in Bash are untyped, why?
See ARITHMETIC EVALUATION in man bash:
Constants with a leading 0 are interpreted as octal numbers.
You can remove the leading zero by parameter expansion:
hour=${hour#0}
or force base-10 interpretation:
$((10#$hour + 1))
what I'd call a hack, but given that you're only processing hour values, you can do
hour=08
echo $(( ${hour#0} +1 ))
9
hour=10
echo $(( ${hour#0} +1))
11
with little risk.
IHTH.
You could also use bc
hour=8
result=$(echo "$hour + 1" | bc)
echo $result
9
Here's an easy way, albeit not the prettiest way to get an int value for a string.
hour=`expr $hour + 0`
Example
bash-3.2$ hour="08"
bash-3.2$ hour=`expr $hour + 0`
bash-3.2$ echo $hour
8
In Short: In order to deal with "Leading Zero" numbers (any 0 digit that comes before the first non-zero) in bash
- Use bc An arbitrary precision calculator language
Example:
a="000001"
b=$(echo $a | bc)
echo $b
Output: 1
From Bash manual:
"bc is a language that supports arbitrary precision numbers with interactive execution
of statements. There are some similarities in the syntax to the C programming lan-
guage. A standard math library is available by command line option. If requested, the
math library is defined before processing any files. bc starts by processing code from
all the files listed on the command line in the order listed. After all files have
been processed, bc reads from the standard input. All code is executed as it is read.
(If a file contains a command to halt the processor, bc will never read from the standard input.)"
Since hours are always positive, and always 2 digits, you can set a 1 in front of it and subtract 100:
echo $((1$hour+1-100))
which is equivalent to
echo $((1$hour-99))
Be sure to comment such gymnastics. :)
The leading 0 is leading to bash trying to interpret your number as an octal number, but octal numbers are 0-7, and 8 is thus an invalid token.
If I were you, I would add some logic to remove a leading 0, add one, and re-add the leading 0 if the result is < 10.
How about sed?
hour=`echo $hour|sed -e "s/^0*//g"`