Рыба для пользователей bash
Данный документ предоставляет краткий обзор отличий оболочки Fish от bash (и в меньшей степени других оболочек, таких как zsh или ksh). Fish намеренно не совместима с POSIX, поэтому некоторые привычные вам вещи работают по-другому.
Многие вещи похожи — обе оболочки в основе своей расширяют командные строки для выполнения команд, поддерживают пайпы, перенаправления, переменные, шаблоны подстановки, использование выходных данных команд различными способами. Этот документ призван быстро показать вам различия.
Подстановки команд
В Fish подстановки команд записываются как $(command) или (command), но не как `command`.
Кроме того, они разбиваются только по символам новой строки вместо $IFS. Если вам нужно разбить по чему-то другому, используйте string split, string split0 или string collect. Если эти команды используются в последней части подстановки команд, то созданные ими разделители сохраняются. Таким образом:
for i in (find . -print0 | string split0)
будет правильно обрабатывать все возможные имена файлов.
Переменные
Fish устанавливает и удаляет переменные с помощью команды set вместо VAR=VAL и различных отдельных встроенных команд, таких как declare и unset и export. Команда set принимает опции для определения области действия и экспортируемости переменной:
# Define $PAGER *g*lobal and e*x*ported, # so this is like ``export PAGER=less`` set -gx PAGER less # Define $alocalvariable only locally, # like ``local alocalvariable=foo`` set -l alocalvariable foo
или для удаления переменных:
set -e PAGER
VAR=VAL заявления доступны в качестве переопределений среды:
PAGER=cat git log
Fish не выполняет разделение слов. После установки значения переменной, это значение остается неизменным, поэтому двойные кавычки при расширении переменных не являются такой же необходимостью, как в bash. [1]
Например, вот bash:
> foo="bar baz" > printf '"%s"\n' $foo # will print two lines, because we didn't double-quote # this is word splitting "bar" "baz"
А вот fish:
> set foo "bar baz" > printf '"%s"\n' $foo # foo was set as one element, so it will be passed as one element, so this is one line "bar baz"
Все переменные являются «массивами» (мы используем термин «списки»), и расширение переменной расширяется до всех ее элементов, с каждым элементом в качестве собственного аргумента (как в bash’s "${var[@]}"):
> set var "foo bar" banana > printf %s\n $var foo bar banana
Можно выбрать определённые элементы списка:
echo $list[5..7]
Аргументы для set являются обычными, поэтому вы также можете установить переменную в значение, полученное от команды:
# Set lines to all the lines in file, one element per line set lines (cat file)
или смесью литеральных значений и выходных данных:
> set numbers 1 2 3 (seq 5 8) 9 > printf '%s\n' $numbers 1 2 3 5 6 7 8 9
Символ = не нужен и бесполезен в контексте set - set foo = bar установит переменную «foo» в два значения: «=» и «bar». set foo=bar выведет ошибку.
См. Переменные оболочки для получения дополнительной информации.
Шаблоны подстановки (глобы)
Fish поддерживает только * и ** шаблоны подстановки (и устаревший ? шаблон) в качестве синтаксиса. Если шаблон подстановки не совпадает, команда завершается с ошибкой (как в bash’s failglob) за исключением команд for, set или count или когда шаблон подстановки используется с переопределением среды (VAR=* command), в этом случае он расширяется до пустой строки (как в bash’s nullglob вариант).
Глоббинг не происходит на расширенных переменных, поэтому:
set foo "*" echo $foo
не будет соответствовать никаким файлам.
Нет опций для управления глоббингом, поэтому он всегда ведёт себя так.
См. Шаблоны подстановки для получения дополнительной информации.
Кавычки
Fish имеет два стиля кавычек: "" и ''. Переменные расширяются в двойных кавычках, ничего не расширяется в одинарных кавычках.
Нет $'', вместо этого последовательности, которые бы преобразовались, преобразуются при отсутствии кавычек:
> echo a\nb a b
См. Кавычки для получения дополнительной информации.
Обработка строк
Fish не имеет ${foo%bar}, ${foo#bar} и ${foo/bar/baz}. Вместо этого обработка строк выполняется встроенной командой string.
Например, чтобы заменить «bar» на «baz»:
> string replace bar baz "bar luhrmann" baz luhrmann
Он также может разбивать строки:
> string split "," "foo,bar" foo bar
Совпадение с регулярными выражениями как замена grep:
> echo bababa | string match -r 'aba$' aba
Выравнивание строк до заданной ширины с произвольными символами:
> string pad -c x -w 20 "foo" xxxxxxxxxxxxxxxxxfoo
Преобразование строк в нижний/верхний регистр:
> string lower Foo foo > string upper Foo FOO
повторение строк, обрезка строк, экранирование строк или вывод длины или ширины строки (в ячейках терминала).
Специальные переменные
Некоторые переменные bash и их ближайшие аналоги в fish:
-
$*,$@,$1и так далее:$argv -
$?:$status -
$$:$fish_pid -
$#: Нет переменной, вместо этого используйтеcount $argv -
$!:$last_pid -
$0:status filename -
$-: В основномstatus is-interactiveиstatus is-login
Подстановка процессов
Вместо <(command) fish использует (command | psub). Нет эквивалента >(command).
Обратите внимание, что оба эти элемента являются bash-измами, и большинство вещей легко выразить без них. Например, вместо:
source (command | psub)
просто используйте:
command | source
так как fish’s source может считывать данные из стандартного ввода.
Heredocs
Fish не имеет <<EOF «heredocs». Вместо
cat <<EOF some string some more string EOF
используйте:
printf %s\n "some string" "some more string"
или:
echo "some string some more string" # or if you want the quotes on separate lines: echo "\ some string some more string\ "
Кавычки следуют через новые строки.
Что делают «heredocs»:
- Считывают/интерпретируют строку со специальными правилами до терминатора. [2]
- Записывают результирующую строку во временный файл.
- Запускают команду, к которой прикреплён heredoc, с этим файлом в качестве стандартного ввода.
Это означает, что это по существу то же самое, что просто читать из пайпа, поэтому:
echo "foo" | cat
почти то же самое, что
cat <<EOF foo EOF
Точно так же, как и с heredocs, команда должна быть подготовлена к чтению из стандартного ввода. Иногда это требует использования специальных опций, часто предоставляя имя файла - для его включения.
Например:
echo "xterm rxvt-unicode" | pacman --remove - # is the same as (the `-` makes pacman read arguments from stdin) pacman --remove xterm rxvt-unicode
и может быть записано в других оболочках как
# This "-" is still necessary - the heredoc is *also* passed over stdin! pacman --remove - << EOF xterm rxvt-unicode EOF
Итак, heredocs на самом деле являются лишь незначительным синтаксическим сахаром, который вводит множество специальных правил, поэтому в fish их нет. Пайпы являются базовым понятием и проще в использовании.
Например, «EOF» — это просто соглашение, терминатор может быть произвольной строкой, что-то вроде «THISISTHEEND» также работает. И использование <<- обрезает ведущие символы табуляции (но не другие пробелы), поэтому вы можете отступать строки, но только с помощью табуляции. Подстановки (переменные, команды) выполняются в heredoc по умолчанию, но не если терминатор находится в кавычках: cat << "EOF".
Тест (test, [, [[)
Fish имеет совместимую с POSIX встроенную команду test или [ . Нет [[ , а test не принимает == как синоним для = . Однако она может сравнивать числа с плавающей точкой.
set -q можно использовать для определения, существует ли переменная или имеет ли она определенное количество элементов (set -q foo[2]).
Арифметическое расширение
Fish не имеет $((i+1)) арифметического расширения, вычисления выполняются встроенной командой math:
math $i + 1
В отличие от арифметики bash, она может обрабатывать числа с плавающей точкой:
> math 5 / 2 2.5
А также имеет некоторые функции, такие как для тригонометрии:
> math cos 2 x pi 1
Вы можете передавать аргументы math отдельно, как выше, или в кавычках. Так как fish использует () скобки для подстановок команд, кавычки необходимы, если вы хотите использовать их в своём выражении:
> math '(5 + 2) * 4'
Как * , так и x являются допустимыми способами написания умножения, но * нуждается в кавычках, потому что он похож на шаблон подстановки.
Подсказки
Fish не использует переменные $PS1, $PS2 и так далее. Вместо этого подсказка является результатом работы функции fish_prompt, а также функции fish_mode_prompt, если включен режим vi, и функции fish_right_prompt для правой подсказки.
В качестве примера, вот относительно простая подсказка bash:
# <$HOSTNAME> <$PWD in blue> <Prompt Sign in Yellow> <Rest in default light white> PS1='\h\[\e[1;34m\]\w\[\e[m\] \[\e[1;32m\]\$\[\e[m\] '
и приблизительный эквивалент в fish:
function fish_prompt
set -l prompt_symbol '$'
fish_is_root_user; and set prompt_symbol '#'
echo -s (prompt_hostname) \
(set_color blue) (prompt_pwd) \
(set_color yellow) $prompt_symbol (set_color normal)
end
Это демонстрирует некоторые различия:
- Fish предоставляет set_color для форматирования текста цветом. Она может использовать 16 именованных цветов и последовательности RGB (поэтому вы также можете использовать
set_color 5555FF) - Вместо введения специфических эскейпов, таких как
\hдля имени хоста, подсказка просто функция. Для достижения эффекта\h, fish предоставляет вспомогательные функции, такие как prompt_hostname, которая выводит сокращенную версию имени хоста. - Fish предлагает другие вспомогательные функции для добавления элементов в подсказку, такие как fish_vcs_prompt для добавления отображения для распространённых систем управления версиями (git, mercurial, svn) и prompt_pwd для отображения сокращённого
$PWD(домашняя директория пользователя становится~, а любые компоненты пути сокращаются).
По умолчанию подсказка достаточно функциональна, и её код можно прочитать с помощью type fish_prompt.
Fish не имеет $PS2 для продолжения строк, вместо этого он отступает строки, чтобы показать, что командная строка не завершена.
Блоки и циклы
Конструкции блоков в Fish выглядят немного иначе. Все они начинаются со слова, заканчиваются end и не имеют второго начального слова:
for i in 1 2 3; do
echo $i
done
# becomes
for i in 1 2 3
echo $i
end
while true; do
echo Weeee
done
# becomes
while true
echo Weeeeeee
end
{
echo Hello
}
# becomes
begin
echo Hello
end
if true; then
echo Yes I am true
else
echo "How is true not true?"
fi
# becomes
if true
echo Yes I am true
else
echo "How is true not true?"
end
foo() {
echo foo
}
# becomes
function foo
echo foo
end
# (bash allows the word "function",
# but this is an extension)
Fish не имеет until. Используйте while not или while !.
Подпроцессы
Bash имеет функцию подпроцессы, где он запускает другой процесс оболочки для определённых задач. Эта оболочка будет независимой, и, например, любые изменения, которые она внесёт в переменные, не будут видны в основной оболочке.
Это включает в себя такие вещи, как:
# A list of commands in `()` parentheses
(foo; bar) | baz
# Both sides of a pipe
foo | while read -r bar; do
# This will not be visible outside of the loop.
VAR=VAL
# This background process will not be, either
baz &
done
Подпроцессы часто путают с {} группированием, которое не использует подпроцесс. Когда вам просто нужно сгруппировать, вы можете использовать begin; end в fish:
(foo; bar) | baz
# when it should really have been:
{ foo; bar; } | baz
# becomes
begin; foo; bar; end | baz
Вывод будет выполняться в том же процессе, поэтому циклы while read могут устанавливать переменные вне цикла:
foo | while read bar
set -g VAR VAL
baz &
end
echo $VAR # will print VAL
jobs # will show "baz"
Подпроцессы также часто путают с подстановками команд, которые bash записывает как `command` или $(command), а fish записывает как $(command) или (command). Bash также использует подпроцессы для их реализации.
Изоляцию, как правило, можно достичь, просто ограничив переменные (с помощью set -l). Но если вам действительно нужно выполнить код в новой среде оболочки, вы всегда можете использовать fish -c 'your code here' для явного выполнения.
Встроенные команды и другие команды
К настоящему моменту становится очевидным, что fish уделяет гораздо больше внимания своим встроенным и внешним командам, а не синтаксису. Поэтому вот некоторые полезные встроенные команды и их примерные эквиваленты в bash:
- string - заменяет большинство преобразований строк (
${i%foo}и т. д.) и может также использоваться вместоgrepиsedи т. п. - math - заменяет арифметику
$((i + 1))и также может выполнять операции с числами с плавающей точкой и некоторые простые функции (синус и аналогичные). - argparse - может обрабатывать разбор опций скрипта, для чего bash, вероятно, использовал бы
getopt(zsh предоставляетzparseopts). - count может использоваться для подсчёта и, следовательно, заменяет
$#и может использоваться вместоwc. - status предоставляет информацию о состоянии оболочки, например, является ли она интерактивной или каков текущий номер строки. Это заменяет
$-и$BASH_LINENOи другие переменные. seq(1)может использоваться в качестве замены для{1..10}диапазонного расширения. Если ваша ОС не поставляется сseq, fish включает в себя функцию-замену.
Другие возможности
Bash имеет set -x или set -o xtrace для вывода всех выполняемых команд. В fish это можно включить, установив fish_trace.
Или, если вашей целью является профилирование того, сколько времени занимает каждая строка скрипта, вы можете использовать fish --profile — см. страницу команды fish.
© 2005-2009 Axel Liljencrantz, 2009-2023 fish-shell contributors
Licensed under the GNU General Public License, version 2.
https://fishshell.com/docs/3.6/fish_for_bash_users.html