разрешить пользователю выполнять команду (функцию) parallel / xargs после выбора файлов в массиве; правильно указывать nuls в сценарии printf - PullRequest
2 голосов
/ 24 февраля 2020

Это дополнительный вопрос к этому вопросу

В этом вопросе я могу получить выбранные файлы в массив и передать их команде / функции (уже экспортированной). Этот вопрос отличается тем, что я хотел бы, чтобы пользователь выполнил команду после выбора файлов.

Основная цель: мне представлен список имен файлов (FZF). Я вручную выбираю некоторые из них. FZF затем помещает это подмножество в массив. Затем я хочу составить незаконченную команду, которая ожидает, что пользователь выполнит команду и нажмет Enter.

В именах файлов могут быть пробелы; отсюда и выбор разделенных нулями.

Я использую FZF для выбора файлов. Я думаю, что он создает массив, содержащий имена файлов с нулевым окончанием. Но первым элементом, который производит 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\0' ${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\0" file 3 file 2 | parallel -0

, к которому я мог бы, например, добавить wc

Но потом, после того как я наберу wc и нажал Enter, я получил следующий результат:

printf "%s\0" file 3 file 2 | parallel -0 wc
wc: file030file020: No such file or directory

Редактировать : Теперь я включил строку declare -p out, чтобы прояснить, что производит FZF. Результаты в том виде, в каком они сейчас отображаются, с использованием приведенной ниже модификации Чарльза:

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

Итак, что-то явно пошло не так с nuls.

Как мне исправить код?

Ответы [ 2 ]

2 голосов
/ 24 февраля 2020

Мне до сих пор неясно, что вы хотите сделать. Прокомментируйте свой код и убедитесь, что каждое имя переменной имеет имя, которое говорит, для чего оно используется.

Хотите ли вы, чтобы пользователь мог вводить команду и запускать эту команду для файлов в массиве?

# 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\0" "${y[@]}" | parallel -0 wc'
# Let the user edit the template
IFS= read -r -e -i "$cmd"
# Run the input
eval "$REPLY"
1 голос
/ 24 февраля 2020

Не обращая внимания на то, что fzf и parallel делают то, что вы хотите, следующее совершенно точно не делает:

cmd="printf \"%s\0\" ${y[@]} | parallel -0 wc"

Почему? Потому что ${y[@]} не вставляет кавычки и экранирование, необходимые для того, чтобы содержимое массива y было выражено в виде допустимого синтаксиса оболочки (для ссылки на исходное содержимое данных при обратной передаче через eval).


Если вы хотите вставить данные в строку, которая будет анализироваться как код, сначала ее необходимо экранировать. Оболочка может сделать это для вас, используя 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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...