incrementing a number in bash with leading 0

2019-06-12 14:36发布

问题:

I am a very newbie to bash scripting and am trying to write some code to parse and manipulate a file that I am working on.

I need to increment and decrement the minute of a time for a bunch of different times in a file. My problem happens when the time is for example 2:04 or 14:00.

File Example:

2:43
2:05
15:00

My current excerpt from my bash script is like this

for x in `cat $1`;
  do minute_var=$(echo $x | cut -d: -f2);
  incr_min=$(($minute_var + 1 | bc));
  echo $incr_min;
done

Current Result:

44
6
1

Required Result:

44
06
01

Any suggestions

回答1:

Use printf:

incr_min=$(printf %02d $(($minute_var + 1 )) )

No that bc is not needed if only integers are involved.



回答2:

is this ok for your requirement?

kent$  echo "2:43
2:05
15:00"|awk -F: '{$2++;printf "%02d\n", $2}'
44
06
01


回答3:

while IFS=: read hour min; do 
    printf "%02d\n" $((10#$min + 1))
done <<END
2:43
2:05
15:00
8:08
0:59
END
44
06
01
09
60

For the minute wrapping to the next hour, use a language with time functions, like gawk

awk -F: '{
    time = mktime("1970 01 01 " $1 " " $2 " 00")
    time += 60
    print strftime("%M", time)
}' 
perl -MTime::Piece -MTime::Seconds -nle '
    $t = Time::Piece->strptime($_, "%H:%M");
    print +($t + ONE_MINUTE)->strftime("%M");
'


回答4:

UPDATED #2

There are some problems with your script. At first instead of `cat file` you should use `<file` or rather $(<file). One fork and exec call is spared as bash simply opens the file. On the other hand calling cut and bc (and printf) also not needed as bash has internally the proper features. So you can spare some forks and execs again.

If the input file is large (greater then cca 32 KiB) then the for-loop line can be too large to be processed by bash so I suggest to use while-loop instead and read the file line-by-line.

I could suggest something like this in pure bash (applied Atle's substr solution):

while IFS=: read hr min; do
  incr_min=$((1$min+1)); #Octal problem solved
  echo ${incr_min: -2}; #Mind the space before -2!
  #or with glennjackman's suggestion to use decimal base
  #incr_min=0$((10#$min+1))
  #echo ${incr_min: -2};
  #or choroba's solution improved to set variable directly
  #printf -v incr_min %02d $((10#$min+1))
  #echo $incr_min
done <file

Input file

$ cat file
2:43
2:05
15:00
12:07
12:08
12:09

Output:

44
06
01
08
09
10

Maybe the printf -v is the simplest as it puts the result to the variable in a single step.

Good question from tripleee what should happen if the result is 60.



回答5:

Use printf to reformat the output to be zero-padded, 2-wide:

incr_min=$(printf %02d $incr_min)


回答6:

Here's a solution that

  • wraps the seconds from 59 to 0
  • is fully POSIX compliant--no bashisms!
  • doesn't need a single fork thus is extremely fast
$ cat x
2:43
2:05
2:08
2:09
15:00
15:59
$ while IFS=: read hr min; do
   printf '%02d\n' $(((${min#0}+1)%60))
done < x
44
06
09
10
01
00


回答7:

Try this:

for x in $(<$1); do
    printf "%02d\n" $(((${x#*:}+1)%60));
done


回答8:

Padding with 0, and getting two last characters:

for x in `cat $1`;
  do minute_var=$(echo $x | cut -d: -f2);
  incr_min=0$(($minute_var + 1 | bc));
  echo ${incr_min: -2:2};
done