问题
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.
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
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 ;;
* )
cmd="printf \"%s\0\" ${y[@]} | parallel -0 wc"
read -e -i "$cmd"; eval "$REPLY" ;; #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
So something has obviously gone wrong with the nuls.
How do I fix the code?
回答1:
It is still unclear to me what you want to do. Comment your code and make sure every variable name has a name that says what it is used for.
Do you want the user to be able to enter a command and have that command run on the files in the array?
# What command does the user want to run?
# The command is a GNU Parallel command template
# So {} will be replaced with the argument
IFS= read -r command_to_run
# Run $command_to_run for every @y.
# -0 is needed if an element in @y contains \n
parallel -0 "$command_to_run" ::: "${y[@]}"
Or maybe:
# Set default command template
cmd="printf \"%s\0\" \${y[@]} | parallel -0 wc"
# Let the user edit the template
IFS= read -r -e -i "$cmd"
# Run the input
eval "$REPLY"
回答2:
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"
read -e -i "$cmd" cmd_edited; eval "$cmd_edited"
来源:https://stackoverflow.com/questions/60368951/allow-user-to-complete-parallel-xargs-command-function-after-selecting-files