0

I have some command foo that produces a list of files (one per line).

I'd like to safely use that in command substitution; e.g. git checkout ... -- $(foo). Since filenames can have special characters and spaces, I'd like to avoid any word-splitting or other issues.

What's a good way to go about this? Keep in mind I anticipate needing to do this quite a bit, so it'd be nice to have a solution that's not janky (interpret however you like.)

Scoobie
  • 1,097
  • 2
  • 12
  • 22
  • 2
    read about `xargs` – KamilCuk Jan 28 '21 at 13:34
  • 1
    [This](https://stackoverflow.com/a/32589977/10248678) is probably what you're looking for. – oguz ismail Jan 28 '21 at 13:50
  • 1
    @rowboat : Would you mind proposing your solution as an answer, and perhaps explain the `(f)` flag? I think this is something many users could find useful. – user1934428 Jan 29 '21 at 10:18
  • unfortunately, `bash` is a requirement – Scoobie Feb 03 '21 at 15:09
  • Your approach is flawed. How are you going to handle a name that contains a newline? You cannot even reliably store the names "one per line". If you are not going to worry about newlines embedded in the names, then there's really no point worrying about "special characters" and other whitespace. – William Pursell Feb 03 '21 at 15:18

3 Answers3

1

IFS can be set to respect spaces when making arrays:

IFS=$'\r\n' eval 'FILES=($(foo))'

Finally, this can be used in the script as needed by doing:

git checkout ... -- "${FILES[@]}"
Scoobie
  • 1,097
  • 2
  • 12
  • 22
0
foo | xargs -I % git checkout ... "%"
useless
  • 1,876
  • 17
  • 18
  • 2
    While this code may provide a solution to the question, it's better to add context as to why/how it works. This can help future users learn, and apply that knowledge to their own code. You are also likely to have positive feedback from users in the form of upvotes, when the code is explained. – Amit Verma Jan 28 '21 at 17:23
0

Solution for zsh

You could store them into an array

files=( ${(f)"$(foo)"} )

and use it as

git checkout ... -- $files

The (f) splits the output from the command at newlines.

user1934428
  • 19,864
  • 7
  • 42
  • 87
  • 2
    did you mean `git checkout ... -- "$files[@]"`? – Scoobie Feb 03 '21 at 08:42
  • this didn't work for me. `$files` is the first file only. – Scoobie Feb 03 '21 at 10:19
  • Maybe `foo` returned only one file. Did you verify the content of the variable `files`? Did you run the two lines with `set -x` so that you can see at what point it fails? BTW, since you are on zsh, the quotes are unnecessary. I update my answer. – user1934428 Feb 03 '21 at 10:23
  • @Scoobie : Ah, I just realize **that my solution fails** in a different way: If `foo` outputs a line which contains a space, this will create two different elements in the array `files`. This is not what we want! – user1934428 Feb 03 '21 at 10:33
  • @Scoobie : See my updated answer. This works for me at least. – user1934428 Feb 03 '21 at 10:41