This is a follow-on question to this question
In that question, I could get selected files into an array and pass them to a command / function (already exported). This question differs in that I would like the user to complete the command after selecting the files.
Main Aim: I am presented with a list of filenames (FZF). I manually select some of these. FZF then puts this subset into an array. I then want to compose an unfinished command which expects the user to complete the command and press Enter
.
The filenames can have spaces in them; hence the choice of Null-separated.
I'm using FZF
to select the files. It produces an array containing nul-ending filenames, I think. But the first item that FZF
produces is the name of a key-press. That's why the script treats the first item of FZF
's output differently.
Currently I have
#!/bin/bash
readarray -d '' out < <(fd .|fzf --print0 -e -m --expect=ctrl-e,ctrl-l)
if [ ${#out[@]} -eq 0 ]; then return 0
fi
declare -p out
key="$out"
y=${out[@]:1}
if [ ${#y[@]} -eq 0 ]; then return 0
fi
case "$key" in
ctrl-e ) echo do something ;;
ctrl-l ) echo do something else ;;
* )
printf -v array_str '%q ' "${y[@]}"
cmd="printf '%s\0' ${array_str} | parallel -0 wc"
read -e -i "$cmd" cmd_edited; eval "$cmd_edited" ;; #not working
esac
I have gotten close: the command looks like it should, but the NUL values are not behaving.
The last line doesn't work. It is intended to print the array of files on a line with null separator and still allow the user to specify a function (already exported) before hitting Enter
. The parallel
command would apply the function to each file in the array.
$ls
file 1
file 2
...
...
file 100
Currently, if I choose file 3
and file 2
, the output of my script looks like this:
printf "%s\0" file 3 file 2 | parallel -0
to which I might for example, append wc
But then after I type wc
and press Enter
I get the following result:
printf "%s\0" file 3 file 2 | parallel -0 wc
wc: file030file020: No such file or directory
Edit: I have now included the line declare -p out
to make clear what FZF is producing.
The results as they now appear, using Charles' modification below is:
declare -a out=([0]="" [1]="file 3" [2]="file 2" [3]="file 1")
printf '%s\0' file\ 3\ file\ 2\ file\ 1 | parallel -0 wc
wc: file030file020file010: No such file or directory
So something has obviously gone wrong with the nuls.
How do I fix the code?
Ignoring whether fzf
and parallel
do what you want, the following quite certainly doesn't:
cmd="printf \"%s\0\" ${y[@]} | parallel -0 wc"
Why? Because ${y[@]}
doesn't insert quoting and escaping necessary to make the contents of the y
array be expressed as valid shell syntax (to refer to the data's original contents when fed back through eval
).
If you want to insert data into a string that's going to be parsed as code, it needs to be escaped first. The shell can do that for you using printf %q
:
printf -v array_str '%q ' "${y[@]}"
cmd="printf '%s\0' ${array_str} | parallel -0 wc"
IFS= read -r -e -i "$cmd" cmd_edited; eval "$cmd_edited"