允许用户在选择文件到数组后完成 parallel / xargs 命令(函数);在 printf 脚本中正确引用 nuls
allow user to complete parallel / xargs command (function) after selecting files into array; quoting nuls correctly in printf script
这是
的后续问题
在那个问题中,我可以将 selected 文件放入数组并将它们传递给命令/函数(已导出)。这个问题的不同之处在于我希望用户在 selecting 文件后完成命令。
主要目标:我看到了一个文件名列表 (FZF)。我手动 select 其中一些。 FZF 然后将这个子集放入一个数组中。然后我想编写一个未完成的命令,希望用户完成命令并按 Enter
。
文件名可以有空格;因此选择 Null-separated.
我正在使用 FZF
来 select 文件。我认为它会生成一个包含以 nul 结尾的文件名的数组。但是 FZF
产生的第一项是按键的名称。这就是脚本以不同方式处理 FZF
输出的第一项的原因。
目前我有
#!/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[=10=]' ${array_str} | parallel -0 wc"
read -e -i "$cmd" cmd_edited; eval "$cmd_edited" ;; #not working
esac
我已经接近了:该命令看起来应该如此,但 NUL 值不起作用。
最后一行不起作用。它旨在在带有空分隔符的行上打印文件数组,并且仍然允许用户在点击 Enter
之前指定一个函数(已经导出)。 parallel
命令会将函数应用于数组中的每个文件。
$ls
file 1
file 2
...
...
file 100
目前,如果我选择 file 3
和 file 2
,我的脚本输出如下所示:
printf "%s[=22=]" file 3 file 2 | parallel -0
例如,我可能会附加 wc
但是在我输入 wc
并按下 Enter
之后,我得到以下结果:
printf "%s[=12=]" file 3 file 2 | parallel -0 wc
wc: file030file020: No such file or directory
编辑:我现在加入了行declare -p out
以明确FZF正在生产什么。
现在出现的结果,使用 Charles 的修改如下:
declare -a out=([0]="" [1]="file 3" [2]="file 2" [3]="file 1")
printf '%s[=13=]' file\ 3\ file\ 2\ file\ 1 | parallel -0 wc
wc: file030file020file010: No such file or directory
所以 nuls 显然出了问题。
如何修复代码?
我还不清楚你想做什么。注释您的代码并确保每个变量名称都有一个说明其用途的名称。
您是否希望用户能够输入命令并对数组中的文件执行该命令运行?
# Set y
y=("file 1" "file \"two\"")
# 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[@]}"
或者也许:
# Set default command template
cmd='printf "%s[=11=]" "${y[@]}" | parallel -0 wc'
# Let the user edit the template
IFS= read -r -e -i "$cmd"
# Run the input
eval "$REPLY"
忽略 fzf
和 parallel
是否做你想做的,以下肯定不会:
cmd="printf \"%s[=10=]\" ${y[@]} | parallel -0 wc"
为什么?因为 ${y[@]}
没有插入必要的引号和转义以使 y
数组的内容被表示为有效的 shell 语法(通过 [= 反馈时引用数据的原始内容) 16=]).
如果要将数据插入将被解析为代码的字符串中,需要先对其进行转义。 shell 可以使用 printf %q
:
为您做到这一点
printf -v array_str '%q ' "${y[@]}"
cmd="printf '%s[=11=]' ${array_str} | parallel -0 wc"
IFS= read -r -e -i "$cmd" cmd_edited; eval "$cmd_edited"
这是
在那个问题中,我可以将 selected 文件放入数组并将它们传递给命令/函数(已导出)。这个问题的不同之处在于我希望用户在 selecting 文件后完成命令。
主要目标:我看到了一个文件名列表 (FZF)。我手动 select 其中一些。 FZF 然后将这个子集放入一个数组中。然后我想编写一个未完成的命令,希望用户完成命令并按 Enter
。
文件名可以有空格;因此选择 Null-separated.
我正在使用 FZF
来 select 文件。我认为它会生成一个包含以 nul 结尾的文件名的数组。但是 FZF
产生的第一项是按键的名称。这就是脚本以不同方式处理 FZF
输出的第一项的原因。
目前我有
#!/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[=10=]' ${array_str} | parallel -0 wc"
read -e -i "$cmd" cmd_edited; eval "$cmd_edited" ;; #not working
esac
我已经接近了:该命令看起来应该如此,但 NUL 值不起作用。
最后一行不起作用。它旨在在带有空分隔符的行上打印文件数组,并且仍然允许用户在点击 Enter
之前指定一个函数(已经导出)。 parallel
命令会将函数应用于数组中的每个文件。
$ls
file 1
file 2
...
...
file 100
目前,如果我选择 file 3
和 file 2
,我的脚本输出如下所示:
printf "%s[=22=]" file 3 file 2 | parallel -0
例如,我可能会附加 wc
但是在我输入 wc
并按下 Enter
之后,我得到以下结果:
printf "%s[=12=]" file 3 file 2 | parallel -0 wc
wc: file030file020: No such file or directory
编辑:我现在加入了行declare -p out
以明确FZF正在生产什么。
现在出现的结果,使用 Charles 的修改如下:
declare -a out=([0]="" [1]="file 3" [2]="file 2" [3]="file 1")
printf '%s[=13=]' file\ 3\ file\ 2\ file\ 1 | parallel -0 wc
wc: file030file020file010: No such file or directory
所以 nuls 显然出了问题。
如何修复代码?
我还不清楚你想做什么。注释您的代码并确保每个变量名称都有一个说明其用途的名称。
您是否希望用户能够输入命令并对数组中的文件执行该命令运行?
# Set y
y=("file 1" "file \"two\"")
# 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[@]}"
或者也许:
# Set default command template
cmd='printf "%s[=11=]" "${y[@]}" | parallel -0 wc'
# Let the user edit the template
IFS= read -r -e -i "$cmd"
# Run the input
eval "$REPLY"
忽略 fzf
和 parallel
是否做你想做的,以下肯定不会:
cmd="printf \"%s[=10=]\" ${y[@]} | parallel -0 wc"
为什么?因为 ${y[@]}
没有插入必要的引号和转义以使 y
数组的内容被表示为有效的 shell 语法(通过 [= 反馈时引用数据的原始内容) 16=]).
如果要将数据插入将被解析为代码的字符串中,需要先对其进行转义。 shell 可以使用 printf %q
:
printf -v array_str '%q ' "${y[@]}"
cmd="printf '%s[=11=]' ${array_str} | parallel -0 wc"
IFS= read -r -e -i "$cmd" cmd_edited; eval "$cmd_edited"