Yves
Yves

Reputation: 12371

bash script: calculate sum size of files

I'm working on Linux and need to calculate the sum size of some files in a directory.

I've written a bash script named cal.sh as below:

#!/bin/bash

while IFS='' read -r line || [[ -n "$line" ]]; do
    echo $line
done<`ls -l | grep opencv | awk '{print $5}'`

However, when I executed this script ./cal.sh, I got an error:

./cal.sh: line 6: `ls -l | grep opencv | awk  '{print $5}'`: ambiguous redirect

And if I execute it with sh cal.sh, it seems to work but I will get some weird message at the end of output:

25
31
385758: File name too long

Why does sh cal.sh seem to work? Where does File name too long come from?

Upvotes: 2

Views: 3217

Answers (3)

kmiles
kmiles

Reputation: 302

Ultimately, as other answers will point out, it's not a good idea to parse the output of ls because it may vary between systems. But it's worth knowing why the script doesn't work.

The ambiguous redirect error is because you need quotes around your ls command i.e.:

while IFS='' read -r line || [[ -n "$line" ]]; do
  echo $line
done < "`ls -l | grep opencv | awk '{print $5}'`"

But this still doesn't do what you want. The "<" operator is expecting a filename, which is being defined here as the output of the ls command. But you don't want to read a file, you want to read the output of ls. For that you can use the "<<<" operator, also known as a "here string" i.e.:

while IFS='' read -r line || [[ -n "$line" ]]; do
    echo $line
done <<< "`ls -l | grep opencv | awk '{print $5}'`"

This works as expected, but has some drawbacks. When using a "here string" the command must first execute in full, then store the output of said command in a temporary variable. This can be a problem if the command takes long to execute or has a large output.

IMHO the best and most standard method of iterating a commands output line by line is the following:

ls -l | grep opencv | awk '{print $5} '| while read -r line ; do
    echo "line: $line"
done

Upvotes: 2

builder-7000
builder-7000

Reputation: 7627

Alternatively, you can do:

du -cb *opencv* | awk 'END{print $1}'

option -b will display each file in bytes and -c will print the total size.

Upvotes: 4

Tom Fenech
Tom Fenech

Reputation: 74645

I would recommend against using that pipeline to get the sizes of the files you want - in general parsing ls is something that you should avoid. Instead, you can just use *opencv* to get the files and stat to print the size:

stat -c %s *opencv*

The format specifier %s prints the size of each file in bytes.

You can pipe this to awk to get the sum:

stat -c %s *opencv* | awk '{ sum += $0 } END { if (sum) print sum }'

The if is there to ensure that no input => no output.

Upvotes: 3

Related Questions