How to use a list of filenames with spaces in a variable to pass the contents of the variable as an argument to du?

125 Views Asked by At

I have a list of filenames in variable in my bash script. And I need to summarize disk usage of listed files. But I can't figure out how to escape spaces in file names. I tried various options, none of them worked.

It is a my test script:

#!/bin/bash

FILE_LIST="/tmp/filename_without_spaces
           /tmp/filename\ with\ spaces"

du -shc $FILE_LIST

FILE_LIST="/tmp/filename_without_spaces
           /tmp/filename\\ with\\ spaces"

du -shc $FILE_LIST

FILE_LIST="/tmp/filename_without_spaces
           \"/tmp/filename with spaces\""

du -shc $FILE_LIST

and the script output I got:

1,0G    /tmp/filename_without_spaces
du: cannot access '/tmp/filename\': No such file or directory
du: cannot access 'with\': No such file or directory
du: cannot access 'spaces': No such file or directory
1,0G    total
1,0G    /tmp/filename_without_spaces
du: cannot access '/tmp/filename\': No such file or directory
du: cannot access 'with\': No such file or directory
du: cannot access 'spaces': No such file or directory
1,0G    total
1,0G    /tmp/filename_without_spaces
du: cannot access '"/tmp/filename': No such file or directory
du: cannot access 'with': No such file or directory
du: cannot access 'spaces"': No such file or directory
1,0G    total

The file with spaces in name could not be processed by du.

3

There are 3 best solutions below

0
Gilles Quénot On BEST ANSWER

Use bash arrays:

#!/bin/bash

file_list=( /tmp/filename_without_spaces '/tmp/filename with spaces' )    
du -shc "${file_list[@]}"

An array maps numbers to strings. Bash >= 4 also has associative arrays (maps strings to strings).
http://mywiki.wooledge.org/BashSheet#Arrays
http://mywiki.wooledge.org/BashFAQ/005
http://wiki.bash-hackers.org/syntax/arrays

0
ktc On

Simpler Way

Use find and forget about spaces.

find . -type d -mindepth 1 -maxdepth 1 -exec du -sh {} \;

These means:

  • find only folders.
  • only 1 depth folders. (no deeper sub folders; no current folder .)
  • check their du -sh respectively.


Additional Notes:

# man du

du <options ... skipped> [file ...]
#                        ^^^^^^^^^^
#                        this means that you can pass several files as input. 

And those inputs are separated by (space)


So, given a FILE_LIST variable:

FILE_LIST="/tmp/filename_without_spaces
           /tmp/filename\ with\ spaces"

would be like pass input with

# case 1: bash
# 4 inputs

/tmp/filename_without_spaces  /tmp/filename\  with\  spaces
#                           ^^              ^^     ^^
#                     4 inputs seperated by these 3 spaces


# case 2: zsh
# 1 inputs, literally this

"/tmp/filename_without_spaces
           /tmp/filename\ with\ spaces"

None of them is what you want.


You need to give those inputs with their own double quotes (").

Like an Array mentioned above.

0
Paul Hodges On

Don't do this.
Spend some time reading over the BashFAQs, such as 20 and 50.

If you have your filenames listed in a file, load them to an array.

$: cat file
file_one
file two
$: mapfile -t lst < file
$: du "${lst[@]}"
0       file_one
0       file two

If you are generating them dynamically and storing that output in FILE_LIST, how are you generating them? You might be able to just skip that step.

FILE_LIST="$(printf "%s\n" file????)" # don't do this
lst=( file???? )                      # use an array
$: printf "[%s]\n" "${lst[@]}"
[file two]
[file_one]
$: du "${lst[@]}"
0       file two
0       file_one

or just skip storing it and pass the globs straight to du

$: du file????
0       file two
0       file_one

find can use globs in the -name option too.

$: find /tmp/ -mindepth 1 -maxdepth 1 -name 'file????' -exec du {} +
0       /tmp/file two
0       /tmp/file_one