Can I avoid eval in the below example or is there no other way? The list of files on variable is dynamic, and I want to cksum all of them. There are many other files in the directory, but they should be ignored.

$ Z="file1 file4"
$ echo ${Z//\ /,}
$ echo {${Z//\ /,}}
$ cksum {file1,file4}
927007485 136284 file1
2748059092 136286 file4
$ cksum {${Z//\ /,}}
cksum: {file1,file4}: No such file or directory
$ eval cksum {${Z//\ /,}}
927007485 136284 file1
2748059092 136286 file4
  • Are the filenames hard-coded into the Z variable? Consider hard-coding them into an array, instead, or showing a glob pattern that would pick up all & only the files you're interested in. – Jeff Schaller Apr 16 '18 at 13:31
  • i'm curious, why don't you use directly cksum $Z ? it works for me. – D'Arcy Nader Apr 16 '18 at 13:32
  • 1
    ... until you have a file named space here or f* or file? in which case shell globbing get involved. – Jeff Schaller Apr 16 '18 at 13:41
  • 1
    Relating: unix.stackexchange.com/q/131766/117549 – Jeff Schaller Apr 16 '18 at 13:41

Using an array instead of a string:

files=( file1 file2 "filename with spaces" file50 "*my* file" )

cksum "${files[@]}"

Note that every double quote above is important, especially if you have filenames with spaces or filename globbing characters in them.

  • 1
    For consideration, if the filenames are hard-coded into Z, you could: set -f; files=($Z); set +f, then use the array. – Jeff Schaller Apr 16 '18 at 13:32
  • @JeffSchaller Yes, that would probably work unless the files have spaces in their names. – Kusalananda Apr 16 '18 at 13:39
  • Indeed; I almost wrote up a separate answer, starting with *If* you have a *space-separated* list of filenames in $Z, but realized it was an assumption, thus my comment on the Q. – Jeff Schaller Apr 16 '18 at 13:54

To explain why cksum {${Z//\ /,}} did not work as you expected:

  1. according the the bash order of substitutions/expansions, brace expansion occurs before parameter substitions
  2. however, it is documented that "To avoid conflicts with parameter expansion, the string ‘${’ is not considered eligible for brace expansion." -- so bash does not perform brace expansion here.
  3. then the shell eventually gets to parameter substitution, and the command is expanded to cksum {file1,file4}
  4. the shell does not go back and re-do the list of expansions, so we don't get brace expansion again: we are left with the single word "{file1,file4}"
  5. and there is no such file.

When you introduce eval into the mix, you are explicitly asking the shell to go through the list of expansions once more. Now we can get brace expansion on the command cksum {file1,file4} and you get the checksums for those 2 files.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.