Вопрос или проблема
В kakoune (редакторе, подобном vim) я могу нажать pipe (|), ввести команду, нажать enter, и она выполнит команду, используя текущее выделение в качестве ввода, и заменит это выделение на вывод выполненной команды. Например, |jq<ret>
, где <ret>
– это клавиша Enter, отформатирует выделенный JSON.
Иногда, однако, я предпочел бы использовать выделение как часть команды, а не как ввод. Например, если я хочу использовать cat
, чтобы заменить имя файла на его содержимое. В Kakoune есть удобная комбинация клавиш для вставки текущего выделения в командную строку, но она не работает с множественными выделениями (выполнение одной и той же команды несколько раз с разными вводом/выводом).
Решение, к которому я пришел, — это расширить stdin, используя $(cat)
. Для предыдущего примера я бы нажал |cat $(cat)<ret>
.
Это использование $(cat)
, кажется, полагается на специфичный (и вырожденный?) способ, которым bash обрабатывает расширения и подпроцессы. Например, это не работает в zsh или fish, где расширение происходит немедленно, и вводом cat
является вывод терминала, или, предположительно, того, что запустило команду. Вместо этого в bash вводом cat
является вывод команды перед pipe.
Я использую эту схему и вне Kakoune, например:
pgrep code | ps -p $(cat) -O [options]
чтобы использовать pgrep для поиска процессов и ps для форматирования информации, которую я хочу отобразить (лучше, чем с pgrep -a
), но команда pipe в Kakoune — единственный случай, когда я не могу использовать более надежную и, возможно, более идиоматическую:
ps -p $(pgrep code) -O [option]
Kakoune по умолчанию использует sh
, что чаще всего означает bash или dash, так что меня это устраивает, но так как я в основном использую fish как свою интерактивную оболочку, мне все еще очень интересно достичь чего-то подобного в fish. А именно:
Как расширить stdin из ранее переданной команды, или как сгенерировать и выполнить команду в зависимости от stdin?
Меня также интересуют более чистые способы в bash.
Одно из того, что я люблю в интерактивных языках оболочки (в отличие от большинства языков программирования или даже Nu), это гибкость в потоке информации. Например, поскольку я привык выполнять их отдельно и в этом порядке, pgrep code | ps -p $(cat)
имеет для меня смысл, больше чем начать с ps
, и мне нравится, что есть выбор. Что касается kak, то, кроме записи скриптов или записи во временные файлы, команда pipe накладывает ограничение, которое оказывается достаточно сильным, несмотря на то, что вполне обосновано.
Кажется, xargs
— это ответ на вопрос: “как сгенерировать и выполнить команду в зависимости от stdin?”
Например, xargs cat
, чтобы добавить stdin в начало с cat и выполнить его как команду, и pgrep code | xargs -I% ps -p % -O cmd
с использованием опции -I для выполнения более сложных действий, чем добавление в начало.
Я пока не отмечу этот вопрос как решенный, так как мне все еще интересны детали и альтернативы.
Я тоже использую kakoune.
Сегодня утром я понял, что можно передать ввод в fish и считать его в переменную.
Предположим, у меня есть этот файл
1. a ----------------- 1
2. bbbbbbb ----------- 2
3. cccc -------------- 3
4. ddddd ------------- 4
5. eee --------------- 5
И я хочу заменить тире на повторяющуюся строку, например "- "
Если я сделаю это наивно:
%<a-s>-+<ret>|wc | awk '{print $3}' | fish -c 'read a ; string repeat -n $a -m $a -- "- " '
Я получу:
1. a - - - - - - 1
2. bbbbbbb - - - - 2
3. cccc - - - - - 3
4. ddddd - - - - - 4
5. eee - - - - - 5
что не выровнено для строк, не кратных длине строки замены.
Вместо этого я просто поменяю местами команду fish в конце:
%<a-s>-+<ret>|wc | awk '{print $3}' | fish -c 'read a ; string repeat -n $a -m $a -- "- " ' | rev
и форматирование будет правильным:
1. a - - - - - - 1
2. bbbbbbb - - - - 2
3. cccc - - - - - 3
4. ddddd - - - - - 4
5. eee - - - - - 5
Суть в следующем:
- вы можете вызвать оболочку fish как команду и передать ей stdin через pipe
- вы можете сохранить поток ввода в переменную с помощью
read
- вы можете использовать эту переменную в любой части внутренней команды fish
Я думаю, что ;
, разделяющий read
и остальную часть команды fish, важен. Я читал в каком-то другом ответе, что pipe создает подоболочку, и присваивание переменной существует только для этой подоболочки.
.
Ответ или решение
В рыбном командном интерпретаторе (fish shell) возникают определенные сложности, когда речь заходит об использовании стандартного ввода (stdin) в контексте выполнения команд с подстановками. В отличие от bash, который позволяет гибко манипулировать параметрами командной строки через подстановки и каналы, fish требует других подходов для достижения схожего функционала. Давайте разберем, как можно использовать stdin в fish shell и каким образом можно адаптировать ваш опыт работы с другими шеллами.
Теория
Одной из ключевых особенностей fish shell является отсутствие поддержки традиционного синтаксиса с подстановкой команд в стиле $(...)
, используемого в bash для обработки stdin. Вместо этого fish предлагает свой синтаксис, который рассчитан на повышение читаемости и предотвращение возможных ошибок, свойственных оболочкам с более сложным синтаксисом. Однако, это также означает, что возникают некоторые ограничения в манипуляциях с субпроцессами и потоками данных.
Fish поддерживает ряд инструментов и команд, которые могут быть использованы для передачи данных и подстановок, таких как xargs
, read
и функции, которые можно определять прямо в командной строке. Они позволяют реализовать ряд аналогичных паттернов, но требуют иного подхода к написанию команд.
Пример
Рассмотрим задачу: использование текущего выделения текста в качестве части команды и замена его на результат выполнения. Представленная вами ситуация в рыбе решается через подстановку и применение таких команд как read
, string
и другие утилиты Unix.
Вот один из подходов, применимый в fish, для получения данных из stdin и их подстановки:
-
Применение
xargs
для построчной проработки:echo "some text" | xargs -I{} fish -c 'echo {}'
-
Прямое использование
read
для интерпретации:
Пример будет включать создание переменной и использование ее внутри единой команды.echo "some text" | fish -c 'read myVar; echo $myVar processed'
В этом примере, текст "some text" передается в стандартный ввод, где он с помощью команды read
сохраняется в переменной myVar
, а затем используется в расширенной команде.
Применение
Решение подобных задач требует учитывать, что fish использует иные модели потоков данных, но благодаря гибкости можно добиться аналогичных результатов специфичными для fish способами. Рассмотрим процесс, как использовать команду pgrep
в сочетании с ps
, чтобы наблюдать за процессами.
Если в bash стандартная конструкция выглядела как:
pgrep code | ps -p $(cat) -O [options]
В fish аналог может быть реализован следующим образом:
pgrep code | xargs -I{} ps -p {} -O [options]
В этом случае xargs
управляет подстановкой параметра в команду ps
, что позволяет добиваться желаемого результата.
Заключение
Переход на fish shell требует адаптации исходных сценариев и подходов, часто применяемых в bash. Однако стоит учитывать природу fish, который оптимизирован для повышения ясности и интуитивности. Это способствует разработке более устойчивых скриптов, минимизируя возможность внесения неявных ошибок. Весь процесс требует понимания потоков данных в самом fish и использования наиболее подходящих инструментов, таких как xargs
и read
, для работы с командами и их подстановками. Эти инструменты становятся мощными помощниками в создании сложных сценариев, адаптированных под специфику fish, и открывают новые возможности в управлении рабочей средой и автоматизацией процессов.