Рыба для пользователей 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 global and exported, # 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 с "${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 выведет ошибку.
Дополнительную информацию смотрите в разделе Переменные оболочки.
-
1 -
zsh также по умолчанию не выполняет разделение слов (опция SH_WORD_SPLIT управляет этим)
Шаблоны подстановки (globs)
Fish поддерживает только шаблоны подстановки * и ** (и устаревший шаблон ?) в качестве синтаксиса. Если шаблон подстановки не соответствует, то команда завершается ошибкой (как в bash с failglob) , если команда не является for, set или count, или шаблон подстановки используется с переопределением среды (VAR=* command), в этом случае он расширяется до пустой строки (как в bash с опцией 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).
Обратите внимание, что оба этих — bashisms, и большинство вещей можно легко выразить без них. Например, вместо:
source (command | psub)
используйте просто:
command | source
так как в fish команда 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 их не имеет. Конвейеры — это основное понятие и проще, и лучше составляются.
-
2 -
Например, «EOF» — это всего лишь соглашение, терминатор может быть произвольной строкой, работает и что-то вроде «THISISTHEEND». Использование
<<-обрезает ведущие символы табуляции (но не другие пробелы), так что вы можете отступать строки, но только с табуляцией. Подстановки (переменные, команды) выполняется по умолчанию, но не если терминатор находится в кавычках: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 являются допустимыми способами записи умножения, но * нужно заключать в кавычки, так как оно выглядит как шаблон подстановки glob.
Подсказки
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 включает в себя функцию-замену.
© 2022 fish-shell developers
Licensed under the GNU General Public License, version 2.
https://fishshell.com/docs/3.5/fish_for_bash_users.html