0

I'm trying to compare sizes of files inside two directories. My problem is that when I store the sizes of the files inside of a "for" loop, my variable takes all the sizes at once instead of taking them one by one.

Here is the part of my code that is problematic :

for dir1Files in dir1/*
do
    sizeFile1=`stat -c%s $dir1Files`
    for dir2Files in dir2/*
    do
        sizeFile2=`stat -c%s $dir2Files`
        diffSize=$((sizeFile1-sizeFile2))
        echo "$diffSize"
    done
done

I realised, thanks to set -x, that my variables sizeFile1 and sizeFile2 are not integers. Instead, they are a few lines long and contain the sizes of my files in directories, with "one line = one integer", if that makes sense. For example, with three files in dir1, my variable sizeFile1 is :

12500
14534
23000

What I would like is for my variable to vary from 12500 to 14534 to 23000. How should I do that ? I'm guessing I need to change my "for" into something else ?

Thanks in advance.

  • I'm confused, isn't bash non-typed ? And isn't the problem there that the "for" loop gives the 3 values of sizeFile1 at the same time instead of one by one ? – Sratard May 29 '20 at 13:48
  • @Sratard, no, the loop assigns the values one at a time, and all arithmetic in bash coerces values to integers; built-in math fails for numbers that can't be parsed as ints. – Charles Duffy May 29 '20 at 13:48
  • 1
    (BTW, it'd be much better to name the variables `dir1File` and `dir2File`, singular, since they only contain one filename at a time) – Charles Duffy May 29 '20 at 13:49
  • I can't reproduce this behavior on bash 5.0.11, which version do you have? – oguz ismail May 29 '20 at 13:50
  • I'm not sure I understand, so the problem is how I used the "stat" function ? – Sratard May 29 '20 at 13:51
  • Are you saying that the output of `stat -c%s $dir1Files` for a single file contains multiple lines? – Benjamin W. May 29 '20 at 13:52
  • 2
    The code as-given doesn't reproduce any problem at all, at least, not with any version of `stat` I'm familiar with. Please show your `set -x` output. – Charles Duffy May 29 '20 at 13:52
  • 1
    Well, it could produce problems if you had filenames with glob characters or whitespace. To fix that, fix your quoting; `stat -c%s "$dir1Files"`, not just bare `stat -c%s $dir1Files` – Charles Duffy May 29 '20 at 13:52
  • I'm using version 4.4.20 @oguzismail – Sratard May 29 '20 at 13:52
  • See Charles' comment above. Do you have files whose name contain glob-active characters like `*`, `?`, etc? – oguz ismail May 29 '20 at 13:56
  • 1
    Thank you @CharlesDuffy you were right, the problem was because of whitespace, using `stat -c%s "$dir1Files"` fixed it ! – Sratard May 29 '20 at 13:57
  • BTW, consider making a habit of running your scripts through http://shellcheck.net/ before asking questions here; among other things, it'll detect unquoted expansions. – Charles Duffy May 29 '20 at 13:59

1 Answers1

1

Nothing in this is broken 100% of the time, but it certainly can be broken if run with unusual filenames present. To make this code more robust:

  • Use quotes whenever you expand a variable. This prevents a file named dir1/ * (with the space in its name) from being replaced with a list of all files in the current directory when generating a stat command line.
  • Use shopt -s nullglob to make the loops not run at all when no glob matches exist, instead of running with the glob expression as a filename itself.
shopt -s nullglob  # prevent dir1/* from ever evaluating to itself
for dir1File in dir1/*; do
    sizeFile1=$(stat -c%s "$dir1File")
    for dir2File in dir2/*; do
        sizeFile2=$(stat -c%s "$dir2File")
        diffSize=$((sizeFile1-sizeFile2))
        echo "$diffSize"
    done
done
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441