Общая проблема
Я хочу написать сценарий, который взаимодействует с пользователем, даже если он находится в середине цепочки каналов.
Конкретный пример
Конкретно, требуется file
или stdin
, отображает строки (с номерами строк), просит пользователя ввести выбор или номера строк, а затем печатает соответствующие строки stdout
. Давайте назовем этот скрипт selector
. Тогда в принципе, я хочу быть в состоянии сделать
grep abc foo | selector > myfile.tmp
Если foo
содержит
blabcbla
foo abc bar
quux
xyzzy abc
тогда selector
дарит мне (на терминале, а не в myfile.tmp
!) варианты
1) blabcbla
2) foo abc bar
3) xyzzy abc
Select options:
после чего я набираю
2-3
и в конечном итоге
foo abc bar
xyzzy abc
как содержание myfile.tmp
.
У меня есть скрипт выбора и работает, и в основном он работает отлично, если я не перенаправить ввод и вывод. Так
selector foo
ведет себя как я хочу. Однако при объединении вещей, как в приведенном выше примере, selector
печатает представленные параметры myfile.tmp
и пытается прочитать выборку из сброшенного ввода.
Мой подход
Я пытался использовать -u
флаг read
, как в
exec 4< /proc/$PPID/fd/0
exec 4> /proc/$PPID/fd/1
nl $INPUT >4
read -u4 -p"Select options: "
но это не то, что я надеялся.
Q: Как я могу получить реальное взаимодействие с пользователем?
cmd | { some processing; read var </dev/tty; } | cmd
alias selector='{ TMPFILE=$(mktemp); cat > $TMPFILE; nl -s") " $TMPFILE | column -c $(tput cols); read -e -p"Select options: " < /dev/tty; rangeselect -v range="$REPLY" $TMPFILE; rm $TMPFILE; }'
который работает довольно хорошо. Однакоgrep b foo | selector | wc -l
перерывы здесь. Есть идеи как это исправить? Кстати, то,rangeselect
что я использовал, можно найти на pastebin.com/VAxTSSHs . Это простой скрипт AWK, который печатает строки файла, соответствующие заданному диапазону номеров белья. (Диапазоны могут быть такими, как "3-10, 12,14,16-20".)alias
, скорееselector() { all of that stuff...; }
в функцию.alias
es переименовывают простые команды, тогда как функции упаковывают составную команду в одну простую команду .Ответы:
Использование
/proc/$PPID/fd/0
ненадежно: родительскийselector
процесс может не иметь терминала в качестве входных данных.Существует стандартный путь , который всегда относится к терминалу текущего процесса в:
/dev/tty
.или
источник
Я написал небольшую функцию: она не будет отвечать о том, о чем вы просили связать трубы, но решит вашу проблему.
Функция переворачивает все аргументы, которые вы ей передаете
grep
. Если вы используете глобус оболочки для указания файлов, которые он должен прочитать, он вернет все совпадения во всех файлах, начиная с первого в порядке глобинга и заканчивая последним совпадением.grep
передает свои выходные данные, наnl
которые нумерует каждую строку, и которые передают свои выходные данные, наtee
которые дублирует свои выходные данные и вstdout
и в/dev/tty
. Это означает, что выходные данные из конвейера одновременно выводятся как в массив аргументов функции, где он разделяется на электронные\n
линии, так и в терминал, как он работает.Затем
_in()
функция пытается выполнитьread
выборку, если хотя бы один результат предыдущего действия был максимум пять раз. Выбор может состоять только из чисел, разделенных пробелами, или диапазонов номеров, разделенных-
. Если что-то ещеread
(включая пустую строку), он попытается снова - но только, как и прежде, максимум пять раз.Последняя
_out()
функция анализирует выбор пользователя и расширяет любые диапазоны в нем. Он печатает свои результаты в форме"${[num]}"
для каждого - тем самым сопоставляя значение строк, хранящихся вinf()
массиве arg. Этот выводeval
обрабатывается как аргументы,printf
поэтому печатает только те строки, которые выбрал пользователь.Он явно
read
из терминала и печатает толькоSelect:
менюstderr
и поэтому достаточно дружественен к конвейеру. Например, следующие работы:Но вы можете использовать любые опции, которые вы дадите,
grep
и любое количество имен файлов, которые вы можете передать. То есть вы можете использовать любой, кроме одного вида - поскольку побочный эффект его синтаксического ввода с$IFS
ним не будет работать, если вы ищете пустые строки. Но кто хотел бы выбрать из нумерованного списка пустых строк?В заключение отметим, что поскольку это работает путем прямого преобразования числового пользовательского ввода в числовые позиционные параметры, хранящиеся в массиве аргументов функции, то результатом будет то, что выберет пользователь, столько раз, сколько выберет пользователь, и в любом порядке, выбранном пользователем. Это.
Например:
источник