FISH-FOR-BASH-USERS(1) fish-shell FISH-FOR-BASH-USERS(1)

fish-for-bash-users - راهنمای مهاجرت و تفاوت‌های پوسته فیش برای کاربران بش

این راهنما برای ارائه یک دید کلی و سریع به کاربرانی است که از پوسته bash (یا با شدتی کمتر از سایر پوسته‌ها مانند zsh یا ksh) به فیش مهاجرت کرده‌اند و می‌خواهند تفاوت‌های آن را بشناسند. پوسته fish عمداً با استاندارد POSIX سازگار نیست و به همین دلیل برخی از مواردی که به آن‌ها عادت دارید، در اینجا به شکلی متفاوت کار می‌کنند.

بسیاری از امکانات مشابه هستند - هر دو در اصل خطوط فرمان را برای اجرای دستورات بسط می‌دهند، دارای پایپ‌ها (لوله‌ها)، تغییرمسیرها، متغیرها، الگوهای عام (globs) بوده و از خروجی دستورات به روش‌های گوناگون بهره می‌برند. این سند برای نمایش سریع تفاوت‌ها نگاشته شده است.

پوسته fish جایگزینی دستورات را به صورت $(command) یا (command) می‌نویسد، و نه به صورت بک‌تیک `command`.

علاوه بر این، فیش خروجی را به جای استفاده از متغیر $IFS فقط بر اساس خطوط جدید (newline) تفکیک می‌کند. چنانچه مایل به تفکیک بر پایه موارد دیگری هستید، از دستورات توکار string split, string split0 یا string collect استفاده کنید. اگر این موارد به عنوان آخرین دستور در یک جایگزینی دستور استفاده شوند، تفکیک‌های ایجادشده توسط آن‌ها به مرحله بعد منتقل می‌گردد. بنابراین:

for i in (find . -print0 | string split0)

تمام نام‌های ممکن فایل‌ها را به درستی پردازش خواهد کرد.

پوسته fish تعریف و حذف متغیرها را به جای عبارت VAR=VAL و مجموعه‌ای از دستورات توکار مجزا نظیر declare، unset و export، با استفاده از دستور توکار set انجام می‌دهد. دستور set گزینه‌هایی را برای تعیین محدوده دید (scope) و وضعیت صدور به محیط (export) یک متغیر می‌پذیرد:

# 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 تفکیک کلمات (word splitting) را انجام نمی‌دهد. هنگامی که متغیری روی مقداری تنظیم شد، آن مقدار به همان شکل باقی می‌ماند؛ بنابراین قرار دادن بسط متغیر درون نقل‌قول دوتایی (گیومه مضاعف) الزامی که در 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"

همه متغیرها «آرایه» هستند (در فیش از واژه «لیست» استفاده می‌شود)، و بسط یک متغیر به تمام عناصر آن بسط می‌یابد، به طوری که هر عنصر به عنوان یک آرگومان جداگانه عمل می‌کند (مشابه "${var[@]}" در bash):

> 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 خطایی چاپ خواهد کرد.

برای اطلاعات بیشتر، مستندات متغیرهای پوسته (Shell variables) را ببینید.

[1]
پوسته zsh نیز به‌طور پیش‌فرض تفکیک کلمات را انجام نمی‌دهد (گزینه SH_WORD_SPLIT این رفتار را کنترل می‌کند).

پوسته fish از ساختار ${my_variable:-fallback} برای فراهم کردن مقادیر پیش‌فرض برای متغیرهای تعریف‌نشده پشتیبانی نمی‌کند. در عوض، می‌توانید با بررسی اینکه آیا متغیر تاکنون تنظیم شده است یا خیر، مقادیر پیش‌فرض را مشخص کنید:

# Ensure XDG_CONFIG_HOME is set or use a default value
set -q XDG_CONFIG_HOME || set XDG_CONFIG_HOME $HOME/.config
# now use XDG_CONFIG_HOME as normal

پوسته fish از نظر نحوی فقط از الگوهای عام * و ** (و الگوی منسوخ‌شده ?) پشتیبانی می‌کند. اگر یک الگوی عام تطابقی نیابد، دستور با شکست مواجه می‌شود (مشابه failglob در bash)، مگر اینکه دستور یکی از موارد for، set یا count باشد، یا اینکه الگو با بازنویسی متغیر محیطی (VAR=* command) به کار رود که در این صورت به هیچ مقداری بسط نمی‌یابد (مانند گزینه nullglob در bash).

الگویابی روی متغیرهای بسط‌یافته صورت نمی‌گیرد، بنابراین:

set foo "*"
echo $foo

هیچ فایلی را مطابقت نخواهد داد.

هیچ گزینه‌ای برای کنترل رفتار الگویابی وجود ندارد، بنابراین رفتار آن همواره یکسان است.

الگوی ** فایل‌ها را در زیرپوشه‌ها نیز مطابقت می‌دهد. در سایر پوسته‌ها این قابلیت معمولاً نیازمند فعال‌سازی از طریق گزینه‌ای خاص مانند setopt globstar در bash است.

برخلاف bash، فیش پیوندهای نمادین (symlinks) را نیز دنبال می‌کند و نتایج را با ترتیب طبیعی (natural sort) مرتب می‌سازد، به گونه‌ای که اعداد موجود به عنوان عدد با هم مقایسه می‌شوند. این به این معناست که برای نمونه، قطعات موسیقی را حتی اگر شماره‌هایی مثل 1 به جای 01 داشته باشند، به درستی مرتب می‌کند.

برای اطلاعات بیشتر، مستندات الگوهای عام (Wildcards) را ببینید.

پوسته fish دو سبک نقل‌قول دارد: "" (گیومه مضاعف) و '' (گیومه تکی). متغیرها درون گیومه مضاعف بسط می‌یابند، اما هیچ چیزی درون گیومه تکی بسط پیدا نمی‌کند.

ساختار $'' وجود ندارد؛ در عوض توالی‌هایی که باید تبدیل شوند، هنگامی که درون گیومه نباشند تبدیل می‌گردند:

> echo a\nb
a
b

برای اطلاعات بیشتر، مستندات نقل‌قول (Quotes) را ببینید.

پوسته 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

تطبیق با عبارات باقاعده (Regex) به عنوان جایگزینی برای grep:

> echo bababa | string match -r 'aba$'
aba

پر کردن رشته‌ها تا پهنای مشخص با کاراکترهای دلخواه (padding):

> string pad -c x -w 20 "foo"
xxxxxxxxxxxxxxxxxfoo

تبدیل حروف رشته به کوچک یا بزرگ:

> string lower Foo
foo
> string upper Foo
FOO

تکرار رشته‌ها، پیرایش فاصله‌ها (trim)، اسکیپ کردن رشته‌ها، یا چاپ طول و عرض رشته (بر حسب سلول‌های ترمینال).

برخی از متغیرهای bash و نزدیک‌ترین معادل آن‌ها در fish:

  • متغیرهای $*، $@، $1 و موارد بعدی: $argv
  • متغیر $?: معادل $status
  • متغیر $$: معادل $fish_pid
  • متغیر $#: متغیری وجود ندارد، به جای آن از count $argv استفاده کنید
  • متغیر $!: معادل $last_pid
  • متغیر $0: معادل status filename
  • متغیر $-: عمدتاً معادل دستورات status is-interactive و status is-login

پوسته fish به جای ساختار <(command) از (command | psub) استفاده می‌کند. هیچ معادلی برای >(command) وجود ندارد.

توجه داشته باشید که هر دوی این‌ها قابلیت‌های مختص بش (bashisms) هستند، و بیشتر اهداف را می‌توان به راحتی بدون آن‌ها محقق ساخت. برای مثال، به جای:

source (command | psub)

استفاده کنید از:

command | source

زیرا دستور توکار source در فیش می‌تواند مستقیماً از ورودی استاندارد (stdin) بخواند.

پوسته fish از ساختار اسناد درجا یا همان «heredoc» به شکل <<EOF بهره نمی‌برد. به جای:

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» انجام می‌دهند به شرح زیر است:

1.
خواندن و تفسیر رشته متنی، با قواعدی ویژه، تا رسیدن به نشانگر پایانی. [2]
2.
نوشتن رشته حاصل در یک فایل موقت.
3.
آغاز به کار دستوری که سند درجا به آن متصل شده، به طوری که آن فایل موقت به عنوان ورودی استاندارد (stdin) به آن داده می‌شود.

این بدان معناست که ماهیت آن اساساً همانند خواندن از یک پایپ (لوله) است؛ بنابراین:

echo "foo" | cat

تا حد زیادی مشابه این دستور است:

cat <<EOF
foo
EOF

همانند اسناد درجا، دستور مورد استفاده باید آماده خواندن از ورودی استاندارد (stdin) باشد. گاهی این امر مستلزم کاربرد گزینه‌های ویژه است، و اغلب دادن نام فایل به صورت - این قابلیت را فعال می‌سازد.

برای نمونه:

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

بنابراین اسناد درجا صرفاً یک ویژگی نحوی کم‌اهمیت (syntactic sugar) هستند که قوانین ویژه بسیاری را ایجاد می‌کنند؛ به همین سبب فیش فاقد آن‌ها است. پایپ‌ها مفهومی کلیدی، ساده‌تر و با ترکیب‌پذیری مناسب‌تر به شمار می‌روند.

شما همچنین می‌توانید چند ورودی را به یک دستور پایپ کنید، مانند این مورد:

{ cat packages1; echo fish; } | pacman --sync -
[2]
به عنوان مثال، عبارت "EOF" فقط یک توافق و سنت است؛ نشانگر پایانی می‌تواند هر رشته دلخواهی باشد و عبارتی مثل "THISISTHEEND" نیز کار می‌کند. همچنین استفاده از <<- نویسه‌های تب ابتدای خطوط را حذف می‌کند (اما سایر فاصله‌های خالی را دست‌نخورده می‌گذارد)، بنابراین می‌توانید خطوط را به داخل ببرید اما فقط با نویسه تب. جایگزینی‌ها (متغیرها، دستورات) به‌طور پیش‌فرض روی سند درجا انجام می‌شوند، مگر اینکه نشانگر پایان درون گیومه قرار داده شده باشد: cat << "EOF".

پوسته fish دارای دستور توکار test یا [ سازگار با استاندارد POSIX است. ساختار [[ در آن وجود ندارد و دستور 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 بفرستید. از آنجا که فیش از پرانتزهای () برای جایگزینی دستورات بهره می‌برد، اگر بخواهید از آن‌ها در عبارت خود استفاده کنید نقل‌قول کردن (قرار دادن درون گیومه) الزامی است:

> 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 --reset)
end

این چند تفاوت را نشان می‌دهد:

  • پوسته fish دستور set_color را برای رنگی کردن متن فراهم می‌کند. این دستور می‌تواند ۱۶ رنگ نام‌گذاری‌شده و همچنین توالی‌های RGB را به کار گیرد (بنابراین می‌توانید از set_color 5555FF نیز استفاده کنید).
  • به جای معرفی کدهای اسکیپ اختصاصی مانند \h برای نام میزبان، اعلان یک تابع است. برای دستیابی به اثری مانند \h، فیش توابع کمکی نظیر 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 قابلیتی به نام «زیرپوسته» (subshell) دارد که طی آن، برای کارهایی مشخص یک فرآیند پوسته دیگر را راه‌اندازی می‌کند. آن پوسته مستقل خواهد بود و برای مثال هر تغییری که در متغیرها بدهد، در پوسته اصلی دیده نخواهد شد.

این موضوع شامل مواردی مانند این‌ها است:

# 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

پوسته fish در حال حاضر دارای زیرپوسته نیست. شما باید راهکار دیگری پیدا کنید. ایزوله‌سازی معمولاً با تعیین محدوده متغیرها (با دستور set -l) امکان‌پذیر است، اما اگر واقعاً نیاز به اجرای کد در محیط پوسته‌ای نو دارید، می‌توانید برای انجام صریح آن از دستور fish -c 'your code here' استفاده فرمایید.

زیرپوسته‌های دارای پرانتز () معمولاً با گروه‌بندی براکتی {} که از زیرپوسته استفاده نمی‌کند اشتباه گرفته می‌شوند. وقتی که فقط نیاز به گروه‌بندی دارید، می‌توانید در fish از begin; end بهره ببرید:

(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 برای پیاده‌سازی آن‌ها نیز از زیرپوسته‌ها استفاده می‌کند.

تاکنون آشکار گردیده که پوسته 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 نباشد، فیش یک تابع جایگزین درون خود گنجانده است.

پوسته Bash دارای گزینه‌های set -x یا set -o xtrace برای چاپ همه دستوراتی است که اجرا می‌شوند. در fish، این ویژگی با مقداردهی متغیر محیطی fish_trace فعال می‌گردد.

یا اگر هدف شما پروفایل (profile) کردن این است که هر خط از اسکریپت چه مدت زمان صرف می‌کند، می‌توانید از دستور fish --profile استفاده کنید - به صفحه راهنمای دستور fish(1) مراجعه فرمایید.

توسعه‌دهندگان پوسته فیش (fish-shell developers)

توسعه‌دهندگان پوسته فیش (fish-shell developers)

fish(1), bash(1), fish-doc(1)

پایگاه وب رسمی پوسته فیش: https://fishshell.com

2026-09-07 4.9