|
NAMEfish-for-bash-users - A quick fish primer for those coming from bashThis is to give you a quick overview if you come from bash (or to a lesser extent other shells like zsh or ksh) and want to know how fish differs. Fish is intentionally not POSIX-compatible and as such some of the things you are used to work differently. Many things are similar - they both fundamentally expand commandlines to execute commands, have pipes, redirections, variables, globs, use command output in various ways. This document is there to quickly show you the differences. COMMAND SUBSTITUTIONSFish spells command substitutions as (command) instead of $(command) (or `command`).In addition, it only splits them on newlines instead of $IFS. If you want to split on something else, use string split, string split0 or string collect. If those are used as the last command in a command substitution the splits they create are carried over. So: for i in (find . -print0 | string split0) will correctly handle all possible filenames. VARIABLESFish sets and erases variables with set instead of VAR=VAL and declare and unset and export. set takes options to determine the scope and exportedness of a variable:# 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 or to erase variables: set -e PAGER VAR=VAL statements are available as environment overrides: PAGER=cat git log Fish does not perform word splitting. Once a variable has been set to a value, that value stays as it is, so double-quoting variable expansions isn't the necessity it is in bash. [1] For instance, here's bash > foo="bar baz" > printf '"%s"\n' $foo # will print two lines, because we didn't double-quote, so the variable is split "bar" "baz" And here is 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" All variables are "arrays" (we use the term "lists"), and expanding a variable expands to all its elements, with each element as its own argument (like bash's "${var[@]}": > set var "foo bar" banana > printf %s\n $var foo bar banana Specific elements of a list can be selected: echo $list[5..7]
WILDCARDS (GLOBS)Fish only supports the * and ** glob (and the deprecated ? glob). If a glob doesn't match it fails the command (like with bash's failglob) unless the command is for, set or count or the glob is used with an environment override (VAR=* command), in which case it expands to nothing (like with bash's nullglob option).Globbing doesn't happen on expanded variables, so: set foo "*" echo $foo will not match any files. There are no options to control globbing so it always behaves like that. QUOTINGFish has two quoting styles: "" and ''. Variables are expanded in double-quotes, nothing is expanded in single-quotes.There is no $'', instead the sequences that would transform are transformed when unquoted: > echo a\nb a b STRING MANIPULATIONFish does not have ${foo%bar}, ${foo#bar} and ${foo/bar/baz}. Instead string manipulation is done by the string builtin.SPECIAL VARIABLESSome bash variables and their closest fish equivalent:
PROCESS SUBSTITUTIONInstead of <(command) fish uses (command | psub). There is no equivalent to >(command).Note that both of these are bashisms, and most things can easily be expressed without. E.g. instead of: source (command | psub) just use: command | source as fish's source can read from stdin. HEREDOCSFish does not have <<EOF "heredocs". Instead of:cat <<EOF some string some more string EOF use: printf %s\n "some string" "some more string" or: echo "some string some more string" Quotes are followed across newlines. TEST (TEST, [, [[)Fish has a POSIX-compatible test or [ builtin. There is no [[ and test does not accept == as a synonym for =. It can compare floating point numbers, however.set -q can be used to determine if a variable exists or has a certain number of elements (set -q foo[2]). ARITHMETIC EXPANSIONFish does not have $((i+1)) arithmetic expansion, computation is handled by math:math $i + 1 It can handle floating point numbers: > math 5 / 2 2.5 PROMPTSFish does not use the $PS1, $PS2 and so on variables. Instead the prompt is the output of the fish_prompt function, plus the fish_mode_prompt function if vi-mode is enabled and the fish_right_prompt function for the right prompt.As an example, here's a relatively simple bash prompt: # <$HOSTNAME> <$PWD in blue> <Prompt Sign in Yellow> <Rest in default light white> export PS1='\h\[\e[1;34m\]\w\[\e[m\] \[\e[1;32m\]\$\[\e[m\] ' and a rough fish equivalent: function fish_prompt set -l prompt_symbol '$' fish_is_root_user; and set prompt_symbol '#' echo -s $hostname (set_color blue) (prompt_pwd) \ (set_color yellow) $prompt_symbol (set_color normal) end This shows a few differences:
The default prompt is reasonably full-featured and its code can be read via type fish_prompt. Fish does not have $PS2 for continuation lines, instead it leaves the lines indented to show that the commandline isn't complete yet. BLOCKS AND LOOPSFish's blocking constructs look a little different. They all start with a word, end in end and don't have a second starting word: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 # (note that bash specifically allows the word "function" as an extension, but POSIX only specifies the form without, so it's more compatible to just use the form without) Fish does not have an until. Use while not or while !. SUBSHELLSBash has a feature called "subshells", where it will start another shell process for certain things. That shell will then be independent and e.g. any changes it makes to variables won't be visible in the main shell.This includes things like: # A list of commands in `()` parentheses (foo; bar) | baz # Both sides of a pipe foo | while read -r bar; do # This variable will not be visible outside of the while loop. VAR=VAL # This background process will not be, either baz & done () subshells are often confused with {} grouping, which does not use a subshell. When you just need to group, you can use begin; end in fish: (foo; bar) | baz # when it should really have been: { foo; bar } | baz # becomes begin; foo; bar; end | baz The pipe will simply be run in the same process, so while read loops can set variables outside: foo | while read bar set -g VAR VAL baz & end echo $VAR # will print VAL jobs # will show "baz" Subshells are also frequently confused with command substitutions, which bash writes as `command` or $(command) and fish writes as (command). Bash also uses subshells to implement them. The isolation can usually be achieved by just scoping variables (with set -l), but if you really do need to run your code in a new shell environment you can always use fish -c 'your code here' to do so explicitly. BUILTINS AND OTHER COMMANDSBy now it has become apparent that fish puts much more of a focus on its builtins and external commands rather than its syntax. So here are some helpful builtins and their rough equivalent in bash:
AUTHORfish-shell developersCOPYRIGHT2021, fish-shell developers
Visit the GSP FreeBSD Man Page Interface. |