> and < difference Bash - linux

I have to test if pathname is a regular file and if it's length is greater 50 bytes , for this reason I do like this:
if [[ -f $path && `wc -c < $path` -gt 50 ]]; then ......
and it works , but , for curiosity , I tried to do also like this:
if [[ -f $path && `$path > wc -c` -gt 50 ]]; then ......
but it doesn't work and I don't understand why.
For this reason I ask you the difference between < and > operator in Bash.

< is "read from" -- redirecting input, while > is "write to" -- redirecting output. Both are followed by the name of the file to use. So
wc -c < $path
runs the wc command, reading from the file $path
$path > wc -c
runs the $path command, writing to the file wc

These operators are not commutative (position aren't swappable).
wc -c < $path means launch wc and use the file at $path as the input.
$path > wc -c means launch the executable at $path (which in your case $path isn't an executable) and send it's output to the file at wc.
As you can see the second one doesn't really make sense. Always make the executable the first operand (argument), and the file you are reading from or writing to the second operand.

< instructs the shell to take the contents of the file on the right side of the operator and provide them as input to the command on the left side.
> instructs the shell to take the output of the command on the left side and store it in the file named on the right side.
Accordingly, the command wc -c < $path is equivalent to cat $path | wc -c. $path > wc -c would mean "run the command $path and store the output in a file named wc (the -c would be discarded)."

Related

Pipe Bash command output to stdout and to a variable

I have to find files with selected permissions and list them as well as their number. Therefore I would like to pipe result of find command to shell and to the next command, which output I want to store in a variable so I could display it nicely later. I would like to have something like
for i in "$#"
do
find $filename -perm $i | tee /dev/tty | var=${wc -l}
echo "number of files with $i permission: $var"
done
but var=${wc -l} part doesn't work. Please help.
EDIT
I'm aware that I can put entire output of the command to a variable like
var=$(find $filename -perm $i | tee /dev/tty | wc -l)
but then I need only the result of wc -l. How would I get this number from that variable? Would it be possible to display it in reversed order, number first and then the list?
Copying To A TTY (Not Stdout!)
Pipeline components run in subshells, so even if they do assign shell variables (and the syntax for that was wrong), those shell variables are unset as soon as the pipeline exits (since the subshells only live as long as the pipeline does).
Thus, you need to capture the output of the entire pipeline into your variable:
var=$(find "$filename" -perm "$i" | tee /dev/tty | wc -l)
Personally, btw, I'd be teeing to /dev/stderr or /dev/fd/2 to avoid making behavior dependent on whether a TTY is available.
Actually Piping To Stdout
With bash 4.1, automatic file descriptor allocation lets you do the following:
exec {stdout_copy}>&1 # make the FD named in "$stdout_copy" a copy of FD 1
# tee over to "/dev/fd/$stdout_copy"
var=$(find "$filename" -perm "$i" | tee /dev/fd/"$stdout_copy" | wc -l)
exec {stdout_copy}>&- # close that copy previously created
echo "Captured value of var: $var"
With an older version of bash, you'd need to allocate a FD yourself -- in the below example, I'm choosing file descriptor number 3 (as 0, 1 and 2 are reserved for stdin, stdout and stderr, respectively):
exec 3>&1 # make copy of stdout
# tee to that copy with FD 1 going to wc in the pipe
var=$(find "$filename" -perm "$i" | tee /dev/fd/3 | wc -l)
exec 3>&- # close copy of stdout

How read paths in text file and get the file count under that paths

I have a text file which contains multiple paths like below
$ cat directory.txt
/aaaa/bbbbb/ccccc/
/aaaa/bbbbb/eeeee/
/aaaa/bbbbb/ddddd/
I need to change directory to each path in text file and need to get count of files under that paths.Below is the code i used, But it is not working.
i=cat /aaaa/bbbbb/directory.txt
while read $i ;do
cd $i
ls |wc -l
done < /aaaa/bbbbb/count.txt
Actually you're almost there. The line i=... is not needed, read $i should be read i, and you simply need to call ls with the path instead of cd it first.
#!/bin/bash
while read i; do
ls "$i" | wc -l
done < "/xxx/yyy/count.txt"
Thanks every one i tried this code it is working fine
!/bin/bash
for i in cat /nrt/home/directory.txt;
do
cd $i
ls | wc -l
done > /nrt/home/count.txt

List files greater than 100K in bash

I want to list the files recursively in the HOME directory. I'm trying to write my own script , so I should not use the command find or ls. My script is:
#!/bin/bash
minSize=102400;
printFiles() {
for x in "$1/"*; do
if [ -d "$x" ]; then
printFiles "$x";
else
size=$(wc -c "$x");
if [[ "$size" -gt "$minSize" ]]; then
echo "$size";
fi
fi
done
}
printFiles "/~";
So, the problem here is that when I run this script, the terminal throws Line 11: division by 0 and /home/gandalf/Videos/*: No such file or directory. I have not divided by any number, why I'm getting this error?. And the second one?
Alternatively, I can't use find or ls because I have to display the files one by one asking to the user if he want to see the next file or not. This is possible using the command find or ls or only can be done writing my own function?
Thanks.
size=$(wc -c "$x");
That's the line that is failing. When you run that wc command manually you should be able to see why:
$ wc -c /tmp/out
5 /tmp/out
The output contains not only the file size but also the file name. So you can't use $size with the -gt comparator on the next line. One way to fix that is to change the wc line to use cut (or awk, or sed, etc) to keep just the file size.
size=$(wc -c "$x" | cut -f1 -d " ")
A simpler alternative suggested by #mklement0:
size=$(wc -c < "$x")

Bash scripting wanting to find a size of a directory and if size is greater than x then do a task

I have put the following together with a couple of other articles but it does not seem to be working. What I am trying to do eventually do is for it to check the directory size and then if the directory has new content above a certain total size it will then let me know.
#!/bin/bash
file=private/videos/tv
minimumsize=2
actualsize=$(du -m "$file" | cut -f 1)
if [ $actualsize -ge $minimumsize ]; then
echo "nothing here to see"
else
echo "time to sync"
fi
this is the output:
./sync.sh: line 5: [: too many arguments
time to sync
I am new to bash scripting so thank you in advance.
The error:
[: too many arguments
seems to indicate that either $actualsize or $minimumsize is expanding to more than one argument.
Change your script as follows:
#!/bin/bash
set -x # Add this line.
file=private/videos/tv
minimumsize=2
actualsize=$(du -m "$file" | cut -f 1)
echo "[$actualsize] [$minimumsize]" # Add this line.
if [ $actualsize -ge $minimumsize ]; then
echo "nothing here to see"
else
echo "time to sync"
fi
The set -x will echo commands before attempting to execute them, something which assists greatly with debugging.
The echo "[$actualsize] [$minimumsize]" will assist in trying to establish whether these variables are badly formatted or not, before the attempted comparison.
If you do that, you'll no doubt find that some arguments will result in a lot of output from the du -m command since it descends into subdirectories and gives you multiple lines of output.
If you want a single line of output for all the subdirectories aggregated, you have to use the -s flag as well:
actualsize=$(du -ms "$file" | cut -f 1)
If instead you don't want any of the subdirectories taken into account, you can take a slightly different approach, limiting the depth to one and tallying up all the sizes:
actualsize=$(find . -maxdepth 1 -type f -print0 | xargs -0 ls -al | awk '{s += $6} END {print int(s/1024/1024)}')

creating Unix script to check for directories and subdirectories in

I'm completing the following for one of my assignments using Korn shell.
For each argument in the argument list (which becomes the current pathname):
Check whether the current pathname is a directory, and if so:
Initialize a variable maxsubdir with the null (empty) string, and
a maxentries variable to 0;
For each entry in the directory check if that entry represents a
directory and if so, find the numbers of entries in that
subdirectory with a pipe consisting of ls -l and wc, and save the
result in a variable named curentries.
Compare curentries with maxentries, and if curentries is greater,
update maxsubdir and maxentries. (--10 points)
When the for cycle for a directory is completed, display (with
echo) the directory name, maxsubdir and maxentries (with appropriate
explanatory text.)
If the pathname in a) is not a directory, display the pathname
and an explanatory text saying that the pathname does not represent
a directory.
Go to the next command line argument (pathname) and repeat 1-7
The execution of the script ends when all pathnames are processed (the while is completed )
This is the code I have for it so far (EDITED):
#!/bin/ksh
directoy=$1
while [ $# -ne 0 ]; do
if [ -d $1 ]; then
maxsubdir=
maxentries=0
for x in $1; do
echo "Checking if $1 represents a directory..\n"
curentries="ls -l | wc"
if [ $curentries > $maxentries ]; then
maxentries=$curentries
maxsubdir=$curentries
fi;
done
echo "The directory structure of $1 is … \n"
echo "Maximum sub directories: \n"
echo "$maxsubdir\n"
echo "Maximum directory entries: \n"
echo "$maxentries"
fi
done
Where do I need to insert the "shift" command since I Unix can only handle a limited number of arguments?
Is my syntax appropriate? Or do I have syntax errors on sort lines?
Script seems to run but does not produce output to screen? Perhaps it's endless?
Have a look here and see if this helps out. Explanations are in the code.
#!/bin/ksh
directory=$1
# check whether the entered path is a directory
if [ -d $1 ];then # yes, it's a directory
maxsubdir=null
maxentries=0
echo "$1 is a directory"
# you are only counting lines, add -l to wc
# also you have to not count the first line. it's returns the size
curentries=`ls -l $1 | wc -l`
echo ${curentries}
fi
You don't.
You do have some errors.
Or perhaps, it never reaches that code?
Your assignment says specifically to use a for loop, and you've implemented a while loop.
I'll get you started:
for directory in $*; do
cd "$directory"
curentries=$(ls -1 | wc -l)
for entry in $(ls -1); do
...
done
done

Resources