Yearly Archives: 2016


The disk is offline because it has a signature collision with another disk that is online 1

Recently, we cloned a hard disk using dd.
When we booted into Windows, the new drive was not visible.
After checking with the disk utilities, we got the following informative message:

The disk is offline because it has a signature collision with another disk that is online

To resolve the issue, we used diskpart.
To start diskpart, press the key combination Win + R which will pop up the Run prompt.
Type diskpart in the input box and hit the Enter key.

A new terminal window will appear.
Using that we identified the two disks and changed the label for the second one.

First step:

We issued list disk to get the list of disks.

DISKPART

Microsoft DiskPart version 10.0.14393.0
Copyright (C) 1999-2013 Microsoft Corporation.
On computer: BYTEFREAKS-NET

DISKPART> list disk

  Disk ###  Status         Size     Free     Dyn  Gpt
  --------  -------------  -------  -------  ---  ---
  Disk 0    Online          931 GB      0 B
  Disk 1    Offline         465 GB      0 B

Second step:

We issued select disk 1 so that we could process the disk 1 that was offline and using uniqueid disk we got the signature of the disk.

DISKPART> select disk 1
Disk 1 is now the selected disk.

DISKPART> uniqueid disk
Disk ID: 09FC13CB

Third step:

Set the signature of the disk to a random value other than the one that it already had using the command uniqueid disk ID=FFAABBCCDD

DISKPART> uniqueid disk ID=FFAABBCCDD

DISKPART> uniqueid disk
Disk ID: FFAABBCCDD

The random value must be 8 characters long and each character must be a value between 0-9 or A-F.

Finally:

Restart the machine to get both disks running.


Compiling GobiNet on Ubuntu 16.04 64bit

Recently, we tried to compile the GobiNet drivers for Linux on an Ubuntu 16.04 64bit machine.

The version of GobiNet was GobiNet_2016-10-20.tar.gz ([download id=”2179″]).

To our surprise, we got a errors while compiling.

Solution:

Get the full project after it was patched from here: [download id=”3342″]

or

Download the following patched version of QMIDevice.c and replace it on your machine [download id=”2183″].

Download the following patched version of GobiUSBNet.c and replace it on your machine [download id=”3352″].

Errors/Warnings:

First warning: return from incompatible pointer type [-Wincompatible-pointer-types] (GobiUSBNet.c:1692:13)

We got the following warning:

 module_exit(GobiUSBNetModExit);
             ^
include/linux/module.h:135:11: note: in definition of macro ‘module_exit’
  { return exitfn; }     \

module_exit is defined as follows:

typedef void (*exitcall_t)(void);
#define module_exit(exitfn)                                     \
         static inline exitcall_t __exittest(void)               \
         { return exitfn; }                                      \
         void cleanup_module(void) __attribute__((alias(#exitfn)));

So we see that we need to update static int GobiUSBNetModExit(void) to become static void GobiUSBNetModExit(void) and have it return nothing.

GobiUSBNet.c:1669 
@@ -1666,11 +1666,11 @@ RETURN VALUE: 
    void 
 ===========================================================================*/ 
 // static int GobiUSBNetModExit(struct platform_device *pdev) 
-static int GobiUSBNetModExit(void) 
+static void GobiUSBNetModExit(void) 
 { 
    usb_deregister( &GobiNet ); 
    class_destroy( gpClass ); 
-   return 0; 
+   return; 
 }

Second warning: passing argument 1 of ‘atomic_read’ from incompatible pointer type [-Wincompatible-pointer-types] (QMIDevice.c:2393:21)

On this 64bit architecture the f_count of the struct file  is defined as long (64bit) so we should not use atomic_read but atomic_long_read instead.

@@ -2390,7 +2390,7 @@ int UserspaceClose(
    }
 
    // Fallthough.  If f_count == 1 no need to do more checks
-   if (atomic_read( &pFilp->f_count ) != 1)
+   if (atomic_long_read( &pFilp->f_count ) != 1)^M
    {
       rcu_read_lock();
       for_each_process( pEachTask )

First error: ‘struct file’ has no member named ‘f_dentry’ (in 9 placed of QMIDevice.c)

The error was error: struct file has no member named f_dentry
Apparently, there was a change in the struct that contained f_dentry in the kernel 3.19 source code which created this bug.

@@ -2269,7 +2269,7 @@ int UserspaceIOCTL(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
 
@@ -2427,7 +2427,7 @@ int UserspaceClose(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
    
@@ -2486,7 +2486,7 @@ ssize_t UserspaceRead(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
    
@@ -2567,7 +2567,7 @@ ssize_t UserspaceWrite(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
 
@@ -2646,7 +2646,7 @@ unsigned int UserspacePoll(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return POLLERR;
    }
 
@@ -3003,9 +3003,9 @@ void DeregisterQMIDevice( sGobiUSBNet * pDev )
             for (count = 0; count < pFDT->max_fds; count++)
             {
                pFilp = pFDT->fd[count];
-               if (pFilp != NULL &&  pFilp->f_dentry != NULL)
+               if (pFilp != NULL &&  pFilp->f_path.dentry != NULL)^M
                {
-                  if (pFilp->f_dentry->d_inode == pOpenInode)
+                  if (pFilp->f_path.dentry->d_inode == pOpenInode)^M
                   {
                      // Close this file handle
                      rcu_assign_pointer( pFDT->fd[count], NULL );                     
@@ -3050,9 +3050,9 @@ void DeregisterQMIDevice( sGobiUSBNet * pDev )
                for (count = 0; count < pFDT->max_fds; count++)
                {
                   pFilp = pFDT->fd[count];
-                  if (pFilp != NULL &&  pFilp->f_dentry != NULL)
+                  if (pFilp != NULL &&  pFilp->f_path.dentry != NULL)^M
                   {
-                     if (pFilp->f_dentry->d_inode == pOpenInode)
+                     if (pFilp->f_path.dentry->d_inode == pOpenInode)^M
                      {
                         // Close this file handle
                         rcu_assign_pointer( pFDT->fd[count], NULL );

Following is the original output of the make commands before and while we were patching the source files.

george@ubuntu-1604:~/Downloads$ lsb_release -a
No LSB modules are available.
Distributor ID:    Ubuntu
Description:    Ubuntu 16.04.1 LTS
Release:    16.04
Codename:    xenial

george@ubuntu-1604:~/Downloads$ wget https://portland.source.codeaurora.org/patches/quic/gobi/Gobi_Linux/GobiNet_2016-10-20.tar.gz
--2017-04-24 10:05:54--  https://portland.source.codeaurora.org/patches/quic/gobi/Gobi_Linux/GobiNet_2016-10-20.tar.gz
Resolving portland.source.codeaurora.org (portland.source.codeaurora.org)... 198.145.29.80
Connecting to portland.source.codeaurora.org (portland.source.codeaurora.org)|198.145.29.80|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 43818 (43K) [application/x-gzip]
Saving to: ‘GobiNet_2016-10-20.tar.gz’

GobiNet_2016-10-20.tar 100%[===========================>]  42,79K   111KB/s    in 0,4s    

2017-04-24 10:05:56 (111 KB/s) - ‘GobiNet_2016-10-20.tar.gz’ saved [43818/43818]

george@ubuntu-1604:~/Downloads$ tar -xzf GobiNet_2016-10-20.tar.gz 
george@ubuntu-1604:~/Downloads$ cd gobiusbnet/
george@ubuntu-1604:~/Downloads/gobiusbnet$ make
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module.* modules.order
make -C /lib/modules/4.4.0-57-generic/build M=/home/george/Downloads/gobiusbnet modules
make[1]: Entering directory '/usr/src/linux-headers-4.4.0-57-generic'
  CC [M]  /home/george/Downloads/gobiusbnet/qmap.o
  CC [M]  /home/george/Downloads/gobiusbnet/GobiUSBNet.o
In file included from include/linux/phy.h:22:0,
                 from include/net/dsa.h:19,
                 from include/linux/netdevice.h:44,
                 from include/linux/etherdevice.h:26,
                 from /home/george/Downloads/gobiusbnet/Structs.h:47,
                 from /home/george/Downloads/gobiusbnet/GobiUSBNet.c:57:
/home/george/Downloads/gobiusbnet/GobiUSBNet.c: In function ‘__exittest’:
/home/george/Downloads/gobiusbnet/GobiUSBNet.c:1692:13: warning: return from incompatible pointer type [-Wincompatible-pointer-types]
 module_exit(GobiUSBNetModExit);
             ^
include/linux/module.h:135:11: note: in definition of macro ‘module_exit’
  { return exitfn; }     \
           ^
  CC [M]  /home/george/Downloads/gobiusbnet/QMIDevice.o
/home/george/Downloads/gobiusbnet/QMIDevice.c: In function ‘UserspaceIOCTL’:
/home/george/Downloads/gobiusbnet/QMIDevice.c:2272:26: error: ‘struct file’ has no member named ‘f_dentry’
       pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
                          ^
/home/george/Downloads/gobiusbnet/QMIDevice.c: In function ‘UserspaceClose’:
/home/george/Downloads/gobiusbnet/QMIDevice.c:2393:21: warning: passing argument 1 of ‘atomic_read’ from incompatible pointer type [-Wincompatible-pointer-types]
    if (atomic_read( &pFilp->f_count ) != 1)
                     ^
In file included from include/linux/atomic.h:4:0,
                 from ./arch/x86/include/asm/thread_info.h:53,
                 from include/linux/thread_info.h:54,
                 from ./arch/x86/include/asm/preempt.h:6,
                 from include/linux/preempt.h:59,
                 from include/linux/spinlock.h:50,
                 from include/linux/mm_types.h:8,
                 from include/linux/kmemcheck.h:4,
                 from include/linux/skbuff.h:18,
                 from include/linux/if_ether.h:23,
                 from include/linux/etherdevice.h:25,
                 from /home/george/Downloads/gobiusbnet/Structs.h:47,
                 from /home/george/Downloads/gobiusbnet/QMIDevice.h:95,
                 from /home/george/Downloads/gobiusbnet/QMIDevice.c:91:
./arch/x86/include/asm/atomic.h:25:28: note: expected ‘const atomic_t * {aka const struct <anonymous> *}’ but argument is of type ‘atomic_long_t * {aka struct <anonymous> *}’
 static __always_inline int atomic_read(const atomic_t *v)
                            ^
/home/george/Downloads/gobiusbnet/QMIDevice.c:2430:26: error: ‘struct file’ has no member named ‘f_dentry’
       pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
                          ^
/home/george/Downloads/gobiusbnet/QMIDevice.c: In function ‘UserspaceRead’:
/home/george/Downloads/gobiusbnet/QMIDevice.c:2489:26: error: ‘struct file’ has no member named ‘f_dentry’
       pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
                          ^
/home/george/Downloads/gobiusbnet/QMIDevice.c: In function ‘UserspaceWrite’:
/home/george/Downloads/gobiusbnet/QMIDevice.c:2570:26: error: ‘struct file’ has no member named ‘f_dentry’
       pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
                          ^
/home/george/Downloads/gobiusbnet/QMIDevice.c: In function ‘UserspacePoll’:
/home/george/Downloads/gobiusbnet/QMIDevice.c:2649:26: error: ‘struct file’ has no member named ‘f_dentry’
       pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
                          ^
/home/george/Downloads/gobiusbnet/QMIDevice.c: In function ‘DeregisterQMIDevice’:
/home/george/Downloads/gobiusbnet/QMIDevice.c:3006:43: error: ‘struct file’ has no member named ‘f_dentry’
                if (pFilp != NULL &&  pFilp->f_dentry != NULL)
                                           ^
/home/george/Downloads/gobiusbnet/QMIDevice.c:3008:28: error: ‘struct file’ has no member named ‘f_dentry’
                   if (pFilp->f_dentry->d_inode == pOpenInode)
                            ^
/home/george/Downloads/gobiusbnet/QMIDevice.c:3053:46: error: ‘struct file’ has no member named ‘f_dentry’
                   if (pFilp != NULL &&  pFilp->f_dentry != NULL)
                                              ^
/home/george/Downloads/gobiusbnet/QMIDevice.c:3055:31: error: ‘struct file’ has no member named ‘f_dentry’
                      if (pFilp->f_dentry->d_inode == pOpenInode)
                               ^
scripts/Makefile.build:258: recipe for target '/home/george/Downloads/gobiusbnet/QMIDevice.o' failed
make[2]: *** [/home/george/Downloads/gobiusbnet/QMIDevice.o] Error 1
Makefile:1420: recipe for target '_module_/home/george/Downloads/gobiusbnet' failed
make[1]: *** [_module_/home/george/Downloads/gobiusbnet] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-4.4.0-57-generic'
Makefile:8: recipe for target 'all' failed
make: *** [all] Error 2


george@ubuntu-1604:~/Downloads/gobiusbnet$ git diff
diff --git a/GobiUSBNet.c b/GobiUSBNet.c
index b8151e2..3b88372 100644
--- a/GobiUSBNet.c
+++ b/GobiUSBNet.c
@@ -1666,11 +1666,11 @@ RETURN VALUE:
    void
 ===========================================================================*/
 // static int GobiUSBNetModExit(struct platform_device *pdev)
-static int GobiUSBNetModExit(void)
+static void GobiUSBNetModExit(void)
 {
    usb_deregister( &GobiNet );
    class_destroy( gpClass );
-   return 0;
+   return;
 }
 
 #if 0
diff --git a/QMIDevice.c b/QMIDevice.c
index e7b3f72..6be39a6 100644
--- a/QMIDevice.c
+++ b/QMIDevice.c
@@ -2269,7 +2269,7 @@ int UserspaceIOCTL(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
 
@@ -2390,7 +2390,7 @@ int UserspaceClose(
    }
 
    // Fallthough.  If f_count == 1 no need to do more checks
-   if (atomic_read( &pFilp->f_count ) != 1)
+   if (atomic_long_read( &pFilp->f_count ) != 1)^M
    {
       rcu_read_lock();
       for_each_process( pEachTask )
@@ -2427,7 +2427,7 @@ int UserspaceClose(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
    
@@ -2486,7 +2486,7 @@ ssize_t UserspaceRead(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
    
@@ -2567,7 +2567,7 @@ ssize_t UserspaceWrite(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return -ENXIO;
    }
 
@@ -2646,7 +2646,7 @@ unsigned int UserspacePoll(
    if (IsDeviceValid( pFilpData->mpDev ) == false)
    {
       DBG( "Invalid device! Updating f_ops\n" );
-      pFilp->f_op = pFilp->f_dentry->d_inode->i_fop;
+      pFilp->f_op = pFilp->f_path.dentry->d_inode->i_fop;^M
       return POLLERR;
    }
 
@@ -3003,9 +3003,9 @@ void DeregisterQMIDevice( sGobiUSBNet * pDev )
             for (count = 0; count < pFDT->max_fds; count++)
             {
                pFilp = pFDT->fd[count];
-               if (pFilp != NULL &&  pFilp->f_dentry != NULL)
+               if (pFilp != NULL &&  pFilp->f_path.dentry != NULL)^M
                {
-                  if (pFilp->f_dentry->d_inode == pOpenInode)
+                  if (pFilp->f_path.dentry->d_inode == pOpenInode)^M
                   {
                      // Close this file handle
                      rcu_assign_pointer( pFDT->fd[count], NULL );                     
@@ -3050,9 +3050,9 @@ void DeregisterQMIDevice( sGobiUSBNet * pDev )
                for (count = 0; count < pFDT->max_fds; count++)
                {
                   pFilp = pFDT->fd[count];
-                  if (pFilp != NULL &&  pFilp->f_dentry != NULL)
+                  if (pFilp != NULL &&  pFilp->f_path.dentry != NULL)^M
                   {
-                     if (pFilp->f_dentry->d_inode == pOpenInode)
+                     if (pFilp->f_path.dentry->d_inode == pOpenInode)^M
                      {
                         // Close this file handle
                         rcu_assign_pointer( pFDT->fd[count], NULL );     

george@ubuntu-1604:~/Downloads/gobiusbnet$ make
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module.* modules.order
make -C /lib/modules/4.4.0-57-generic/build M=/home/george/Downloads/gobiusbnet modules
make[1]: Entering directory '/usr/src/linux-headers-4.4.0-57-generic'
  CC [M]  /home/george/Downloads/gobiusbnet/qmap.o
  CC [M]  /home/george/Downloads/gobiusbnet/GobiUSBNet.o
  CC [M]  /home/george/Downloads/gobiusbnet/QMIDevice.o
  CC [M]  /home/george/Downloads/gobiusbnet/QMI.o
  LD [M]  /home/george/Downloads/gobiusbnet/GobiNet.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/george/Downloads/gobiusbnet/GobiNet.mod.o
  LD [M]  /home/george/Downloads/gobiusbnet/GobiNet.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.4.0-57-generic'

Get execution time in seconds

The following methods demonstrate different methods on how to compute the time a potion of code or script take to complete their execution.

[download id=”2158″]

 

Method 1 – Using date

The following example will calculate the execution time in seconds by subtracting the system date and time in seconds since 1970-01-01 00:00:00 UTC once right before the script goes to the computation part and once right after.

In order to get the system date and time in seconds since 1970-01-01 00:00:00 UTC we use the command date +%s.

[download id=”2158″]


#!/bin/bash

#Print the system date and time in seconds since 1970-01-01 00:00:00 UTC
startTime=$(date +%s);

#We pick a random number between 1 and 10.
#Then we delay the execution for that amount of seconds.
sleep $(( (RANDOM % 10) + 1 ));

endTime=$(date +%s);

#Subtract endTime from startTime to get the total execution time
totalTime=$(($endTime-$startTime));

echo "Process finished after $totalTime seconds";

exit 0;

Method 2 – Using bash internal SECONDS variable

The following example will calculate the execution time in seconds by reseting the bash internal variable SECONDS to 0, forcing the shell to continue counting from there.

[download id=”2158″]


#!/bin/bash

#This variable expands to the number of seconds since the shell was started.
#We set it to 0, forcing the shell to continue counting from there.
SECONDS=0;

#We pick a random number between 1 and 10.
#Then we delay the execution for that amount of seconds.
sleep $(( (RANDOM % 10) + 1 ));

echo "Process finished after $SECONDS seconds";

exit 0;

Method 3 – Using bash time

The following example uses the bash time command, which reports the time consumed by a pipeline’s execution.
When time command is executed without its complete path, then the bash built-in time command is executed, instead of the GNU time command. We will use the bash time command in this example and we will use it to run a whole block of commands.
Please note that time command will return the time in seconds as a float (i.e. there will be decimal places. e.g. 1 will be printed as 1.00).

[download id=”2158″]


#!/bin/bash

#The bash time command reports the time consumed by pipeline's execution
#When time command is executed without its complete path, then the bash built-in time command is executed, instead of the GNU time command.
#We will use the bash time command in this example and we will use it to run a whole block of commands.

#We change the output format of time to print elapsed real time in seconds.
TIMEFORMAT="%E";
#We pick a random number between 1 and 10.
#Then we delay the execution for that amount of seconds.
totalTime=`time ( sleep $(( (RANDOM % 10) + 1 )) ) 2>&1`;

#Please note that time command will return the time in seconds as a float (i.e. there will be decimal places. e.g. 1 will be printed as 1.00).
#This will happen as time has build-in more precision than the first two methods presented here.
echo "Process finished after $totalTime seconds";

totalTimeBlock=`time (
	sleep $(( (RANDOM % 10) + 1 ));
	sleep $(( (RANDOM % 10) + 1 ));
) 2>&1`;
echo "Block finished after $totalTimeBlock seconds";

exit 0;

Method 4 – Using GNU time

The GNU time command runs the specified program command with the given arguments.
When time command is executed without its complete path (in our case it was /usr/bin/time), then the bash built-in time command is executed, instead of the GNU time command. To make sure we use the GNU time command, we use which to get the full path of the time command.
Please note that time command will return the time in seconds as a float (i.e. there will be decimal places. e.g. 1 will be printed as 1.00).

[download id=”2158″]


#!/bin/bash
#The time command runs the specified program command with the given arguments.
#When time command is executed without its complete path (in our case it was /usr/bin/time), then the bash built-in time command is executed, instead of the GNU time command.
#To make sure we use the GNU time command, we use which to get the full path of the time command.
time=`which time`;

#We pick a random number between 1 and 10.
#Then we delay the execution for that amount of seconds.
#We change the output format of time to print elapsed real time in seconds.
totalTime="$( $time -f '%e' sleep $(( (RANDOM % 10) + 1 )) 2>&1 1>/dev/null )";

#Please note that time command will return the time in seconds as a float (i.e. there will be decimal places. e.g. 1 will be printed as 1.00).
#This will happen as time has build-in more precision than the first two methods presented here.
echo "Process finished after $totalTime seconds";

exit 0;

Notes

RANDOM internal variable

Each time RANDOM internal variable is referenced, a random integer between 0 and 32767 is generated.

By using the RANDOM variable in this command $(( (RANDOM % 10) + 1 )); we perform a modulo on the random value with the static value 10. This way we force the range of valid values to be between 0 and 9.
Later, we add 1 to that value to shift the range to be between 1 and 10.


convertAndPrintSeconds – Convert seconds to minutes, hours and days in bash 1

The following code can be used to convert some time in seconds to minutes, hours and days in bash.
It will print on screen the converted values only if they are not 0. i.e If the resulting days is 0, it will not print the text for days at all.
You can use it in any script without copy pasting everything in it by executing the following command source ./convertAndPrintSeconds.sh.
Doing so, it will load to your script the function that is defined in convertAndPrintSeconds.sh, making it available for you to use (something like including code in C, with some caveats).

[download id=”2118″]


#!/bin/bash

convertAndPrintSeconds() {

    local totalSeconds=$1;
    local seconds=$((totalSeconds%60));
    local minutes=$((totalSeconds/60%60));
    local hours=$((totalSeconds/60/60%24));
    local days=$((totalSeconds/60/60/24));
    (( $days > 0 )) && printf '%d days ' $days;
    (( $hours > 0 )) && printf '%d hours ' $hours;
    (( $minutes > 0 )) && printf '%d minutes ' $minutes;
    (( $days > 0 || $hours > 0 || $minutes > 0 )) && printf 'and ';
    printf '%d seconds\n' $seconds;
}

Usage

Function convertAndPrintSeconds accepts one parameter, the positive integer number that represents the seconds count we want to convert.

Example


$ source ./time.sh 
$ convertAndPrintSeconds 10
10 seconds
$ convertAndPrintSeconds 100
1 minutes and 40 seconds
$ convertAndPrintSeconds 1000
16 minutes and 40 seconds
$ convertAndPrintSeconds 10000
2 hours 46 minutes and 40 seconds
$ convertAndPrintSeconds 100000
1 days 3 hours 46 minutes and 40 seconds
$ convertAndPrintSeconds 1000000
11 days 13 hours 46 minutes and 40 seconds