how to do arithmetic operations with the size of the files and directory in shell scripting - linux

how to do arithmetic operations with the size of the files and directory when they are in different unites like free space is in MB and the file size is in GB
With one preparatory command I am able to fetch the size of the "/home/abc/def" directory in MB. Its 30GB so getting in KB is not a good idea.
mount fssizeMB
===== =======
/home/abc/def 30002
root#abc:/home/abc/def> ls -lrth
total 7.0G
drwxrwxrwx 3 root root 114 Oct 29 2012 file1
drwxr-xr-x 3 root root 103 Nov 22 2012 file2
-rw-r--r-- 1 root root 114 Jan 25 2013 file3
-rw-r--r-- 1 mtc users 3.8G Jul 22 03:02 file4 <------------------- concerned file
-rw-r--r-- 1 mtc users 3.2G Jul 24 22:26 file5
-rw-r--r-- 1 root root 0 Jan 5 20:30 file6
How to turn below logic in script:
If twice the file size of file4 is < free space of "/home/abc/def " then echo success or else failure.

You could use stat or du -Sh to get the size of file (don't use ls for that in a script).
And to browse the files of a folder :
for i in <direcory>/*; do ...; done
Then, you could use test or [ commands (or [[ if you use Bash) to make a comparison (with -ge, -gt, -lt, -le options as arithmetic operators).
See the manpages of each command to get more information.

this would work with percentages, just to give you an idea, you could modify it to deal with MB or GB and so on.
my advice: doing arithmetic operations in bash is not such a good idea, you should work with programming languages that deal with special variable data type, like float or str and so on. bash is simpler and doesn't work so well with arithmetic operations. sure it does your + and -, but when it comes to percentages and floats... not so well.
try python or perl, or try researching something else. and definitely use, as suggested above, du -sh
#!/usr/bin/env bash
#take df -h and iterate trough percentages
#check to see if file system is full more than 80 %
perc="$(df -h | awk '{print $5}'| sed -e 's/[%/ a-z/A-Z].*//g' )"
#echo $perc
for p in $perc
do
if [ $p -gt 30 ] #change 30 to whatever
then
df -h | grep $p
echo -e "$p Exceeded on `hostname`"
fi
done

Most commands have options to show the size using a specific unit.
The -h flag of ls and df are to produce "human readable" format, which is not suitable for arithmetic calculations, as they can be in inconsistent units.
To get the size of a file, use stat, or even wc -c. It's a bad practice to parse the output of ls -l, so don't use that.
If you can get the size of a file consistently in kilobytes, and the size of free space consistently in bytes, not a problem, you can just multiply the size in bytes with 1024 to be able to make comparisons in consistent units.
The specific commands and flags to use will depend on your operating system and the software installed.

Related

Program is about space utilisation. i am getting error : 72G value too great for base (error token is "72") [duplicate]

Is there a shell command that simply converts back and forth between a number string in bytes and the "human-readable" number string offered by some commands via the -h option?
To clarify the question: ls -l without the -h option (some output supressed)
> ls -l
163564736 file1.bin
13209 file2.bin
gives the size in bytes, while with the -hoption (some output supressed)
> ls -lh
156M file1.bin
13K file2.bin
the size is human readable in kilobytes and megabytes.
Is there a shell command that simply turns 163564736into 156M and 13209 into 13K and also does the reverse?
numfmt
To:
echo "163564736" | numfmt --to=iec
From:
echo "156M" | numfmt --from=iec
There is no standard (cross-platform) tool to do it. But solution using awk is described here

Copying files with wildcard * why isn't it working?

There are 3 txt files called
1.txt 2.txt 3.txt
I want to batch copy with the name
1.txt.cp 2.txt.cp 3.txt.cp
using the wildcard *
I entered the command cp *.txt *.txt.cp
but it wasn't working...
cp : target *.txt.cp : is not a directory
what was the problem???
Use: for i in *.txt; do cp "$i" "$i.cp"; done
Example:
$ ls -l *.txt
-rw-r--r-- 1 halley halley 20 out 27 08:14 1.txt
-rw-r--r-- 1 halley halley 25 out 27 08:14 2.txt
-rw-r--r-- 1 halley halley 33 out 27 08:15 3.txt
$ ls -l *.cp
ls: could not access '*.cp': File or directory does not exist
$ for i in *.txt; do cp "$i" "$i.cp"; done
$ ls -l *.cp
-rw-r--r-- 1 halley halley 20 out 27 08:32 1.txt.cp
-rw-r--r-- 1 halley halley 25 out 27 08:32 2.txt.cp
-rw-r--r-- 1 halley halley 33 out 27 08:32 3.txt.cp
$ for i in *.txt; do diff "$i" "$i.cp"; done
$
If you are used to MS/Windown CMD shell, it is important to note that Unix system handle very differently the wild cards. MS/Windows has kept the MS/DOS rule that said that wild cards were not interpreted but were passed to the command. The command sees the wildcard characters and can handle the second * in the command as noting where the match from the first should go, making copy ab.* cd.* sensible.
In Unix (and derivatives like Linux) the shell is in charge of handling the wildcards and it replaces any word containing one with all the possible matches. The good news is that the command has not to care about that. But the downside is that if the current folder contains ab.txt ab.md5 cd.jpg, a command copy ab.* cd.* will be translated into copy ab.txt ab.md5 cd.jpg which is probably not want you would expect...
The underlying reason is Unix shells are much more versatile than the good old MS/DOS inherited CMD.EXE and do have simple to use for and if compound commands. Just look at #Halley Oliveira's answer for the syntax for your use case.

Linux Command to display filename and its datetime [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 3 years ago.
Improve this question
I am trying to list the files in a directory along with date and time.
I tried using the below two commands:
ls
getting output as below:
abc.txt testFile.txt
Then
ls -ltr
getting output as below:
-rw-r--r-- 1 xxxxxxx domain users 23 Aug 22 09:00 RCS
-rw-r--r-- 1 xxxxxxx domain users 0 Sep 12 06:09 testFile.txt
I expect an output as:
Aug 22 09:00 RCS
Sep 12 06:09 testFile.txt
You can use below command to get the output as you needed :
ls -l|awk '{print $6,$7,$8,$9}'
We are using -l as long listing which will give you most of the information and then we will use awk command to fetch what we want. You can tweak the print statement as per your requirement.
As pointed out by F Hauri, it would be better to use a process substitution with stat and read from there, e.g.
while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(stat -c '%Z %n' *)
Example Use/Output
$ while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(stat -c '%Z %n' *)
Apr 12 15:36:38 comboboxfocus.c
Apr 12 15:36:38 comboboxfocus2.c
Nov 3 17:43:51 createonclick.c
Feb 28 19:01:54 cw-drawinput.c
Nov 28 07:08:19 debug.c
Apr 12 15:36:38 debugsig.c
Jun 20 16:35:32 evboxstruct.c
Jun 20 16:35:32 evboxstruct2.c
Aug 23 08:50:38 ex00-window.c
Aug 23 08:50:38 ex01-hello.c
Aug 23 08:50:38 ex02-packing.c
Aug 23 08:50:38 ex03-builder.c
Feb 28 19:01:54 exampleappmain.c
Feb 28 19:01:54 exampleappwin_final.c
Feb 28 19:01:54 exampleappwin_wsrch.c
Dec 11 03:56:00 examplewindow.c
Dec 16 10:11:15 file_dialog_new.c
Dec 11 03:56:00 infobarex.c
Dec 7 14:03:20 poppler_page.c
Either will work, but awk will likely be quite a bit faster.
Why not use stat command ?
stat -c "%z %n" *
or you can use find command with its "-printf" switch to get this :
find -maxdepth 1 -type -f -printf '%t %f\n'
Similar output can be fetched using the find command.
Intro
After reading all this thread, I think rprakash's answer is the more accurate, as they point to two standard indicated command: stat and find -maxdepth....
Another way, using ls | sed
You could write:
ls -og|sed -re 's/\S*\s+\S+\s+\S+\s+//'
But as bash could be very efficient and because I prefer to be able to choose time ouput format (strftime) as I want:
Clean bash way to list date and files:
based on rprakash's answer stat command, with pretty formatting, as David C. Rankin' purpose, but without useless forks:
Short oneliner:
while read tm fl;do printf "%(%b %e %T)T %s\n" $tm "$fl";done< <(stat -c %Z\ %n *)
More readable:
while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(
stat -c '%Z %n' *
)
And if you want sorted list, you could add sort in this way:
while read time file;do
printf "%(%b %e %T)T %s\n" $time "$file"
done < <(
stat -c '%Z %n' * |
sort -n
)
Some explanations:
From man stat:
%x time of last access, human-readable
%X time of last access, seconds since Epoch
%y time of last data modification, human-readable
%Y time of last data modification, seconds since Epoch
%z time of last status change, human-readable
%Z time of last status change, seconds since Epoch
From man bash
while read ... do; ... ; done < <(stat ... | sort ..)
Process Substitution
Process substitution allows a process's input or output to be referred to
using a filename. It takes the form of <(list) or >(list). The process list
is run asynchronously, and its input or output appears as a filename. This
filename is passed as an argument to the current command as the result of the
expansion. If the >(list) form is used, writing to the file will provide
input for list. If the <(list) form is used, the file passed as an argument
should be read to obtain the output of list. Process substitution is sup‐
ported on systems that support named pipes (FIFOs) or the /dev/fd method of
naming open files.
When available, process substitution is performed simultaneously with parame‐
ter and variable expansion, command substitution, and arithmetic expansion.
printf "%(...)T" $UNIXTIME
printf [-v var] format [arguments]
...
%(datefmt)T
causes printf to output the date-time string resulting from
using datefmt as a format string for strftime(3). The corre‐
sponding argument is an integer representing the number of sec‐
onds since the epoch. Two special argument values may be used:
-1 represents the current time, and -2 represents the time the
shell was invoked. If no argument is specified, conversion
behaves as if -1 had been given. This is an exception to the
usual printf behavior.

Assignment asking to use grep command, how would I write a command when it is fine with the find command?

My Intro to OS teacher wants me to enter a command that long lists the /etc directory and finds all files modified or created with the size of 4096 bytes.
My answer is simply find /etc -size 4k -ls.
How am I supposed to use the grep command to do this? It seems kind of pointless...
It sure makes no sense but in IT you kind of get used to insane seeming requirements so here is one:
$ ls -l /etc | grep "^-\([^ ]* *\)\{4\}4096 "
-rw-r--r-- 1 root root 4096 Oct 27 2014 wgetrc
^- it's a file (^d for dir etc.)
[^ ]* * field and delimiting space(s)
\(...\)\{4\} four times previous, ie. filesize is in the fifth field
4096 filesize. Notice the trailing space to limit the size exactly to 4096.

created a 100 Bytes file containing zeros .but i cant see zeros

I want to create a 100 Byte file of zeroes.
I used the script:
dd if=/dev/zero of=zero_file_100B bs=50 count=2
It works fine, but when I do cat zero_file_100B it does not print anything. What might be the reason for that?
The reason for that is the character with ASCII code 0 has no graphical representation in your terminal emulator, which is the expected behavior. You should't confuse it with the character '0' which has ASCII code 48.
To view binary data a portable way, you might use od:
od -v -t u1 zero
but when I do cat zero_file_100B it does not print anything
perhaps you were looking for this
$ printf "%0*d" 100 0 > 38650594
$ ls -l 38650594
-rw-rw-r-- 1 me me 100 Jul 29 10:32 38650594 # Size is 100 bytes.
What do you want with zero?
You already succeeded in \0, you can print '0' with
for i in {1..100}; do printf 0; done`
Slower than other solutions, but you can change this for other requirements:
# create file with size 100 only containing strings "zero"
for i in {1..25}; do printf "zero"; done
# create file with size 100 only containing strings "zeros"
for i in {1..20}; do printf "zeros"; done

Resources