توی این درس یاد میگیری وقتی یک کانتینر کار نمیکند، بهجای حدس زدن، یک روش منظم را دنبال کنی: اول docker ps -a برای دیدن وضعیت، بعد docker logs برای پیام اپ، بعد docker inspect برای کد خروج و دلیل، و در صورت نیاز بازتولید مشکل. کدهای خروج (۰، ۱، ۱۲۵، ۱۲۶، ۱۲۷، ۱۳۷، ۱۴۳) را میخوانی، سه خرابی رایج را میشناسی: کانتینری که بلافاصله خارج میشود، مشکل شبکه (مثل bind روی 127.0.0.1) و مشکل دسترسی فایل، و با docker events رویدادهای زنده را میبینی. در تمرین اصلی سه کانتینر خراب را عیبیابی میکنی.
مسئله: «کار نمیکند» چیزی نمیگوید
Section titled “مسئله: «کار نمیکند» چیزی نمیگوید”اغلب خراب بودن کانتینر یک جمله است: «بالا نمیآید»، «صفحه باز نمیشود»، «خطا میدهد». اگر هر بار شروع کنی به تغییر تصادفی چیزی، ساعتها میگذرد. اما داکر تقریباً همیشه سرنخ میدهد: وضعیت کانتینر، لاگها و کد خروج. کار تو خواندن به ترتیب درست است.
تشبیه: پزشک در اورژانس
Section titled “تشبیه: پزشک در اورژانس”پزشک اورژانس اول علائم حیاتی را میگیرد (وضعیت: زنده است؟)، بعد از بیمار میپرسد (لاگها: چه میگوید؟)، بعد پروندهی پزشکی را باز میکند (inspect: کد خروج، تنظیمات)، و فقط اگر لازم بود آزمایش میکند (اجرای مجدد با شل). ترتیب مهم است: از ارزانترین و سریعترین شروع کن.
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: کانتینری که بلافاصله خارج میشود
Section titled “مثال ۱: کانتینری که بلافاصله خارج میشود”یک کانتینر فقط تا وقتی زنده است که پروسهی اصلی (PID 1) زنده باشد. اگر آن پروسه کارش را تمام کند، کانتینر هم تمام است، و این خطا نیست:
docker run -d --name lx-ts1 ubuntu:24.04 >/dev/nulldocker run -d --name lx-ts2 alpine echo "سلام و خداحافظ" >/dev/nullsleep 2docker ps -a --filter name=lx-ts --format '{{.Names}}\t{{.Status}}\t{{.Command}}' | sortdocker logs lx-ts2lx-ts1 Exited (0) 2 seconds ago "/bin/bash"lx-ts2 Exited (0) 2 seconds ago "echo 'سلام و خداحاف…"سلام و خداحافظubuntu بهطور پیشفرض bash را اجرا میکند، بدون ترمینال ورودی بلافاصله تمام میشود، با کد 0. کانتینر echo هم کارش را کرد و رفت. برای نگه داشتن کانتینر، پروسهی اصلی باید ادامهدار باشد (مثل یک سرور). قاعده: «Exited (0)» یعنی درست تمام شد، نه خراب.
docker rm -f lx-ts1 lx-ts2 >/dev/nullمثال ۲: کدهای خروج، کد خروج چه میگوید؟
Section titled “مثال ۲: کدهای خروج، کد خروج چه میگوید؟”run() { docker run --name lx-tsx "$@" >/dev/null 2>&1; echo "$(docker inspect lx-tsx --format '{{.State.ExitCode}}' 2>/dev/null || echo -) ← $*"; docker rm -f lx-tsx >/dev/null 2>&1; }run alpine sh -c 'exit 0'run alpine sh -c 'exit 3'run alpine nosuchcommandrun alpine /etc/passwddocker run --no-such-flag alpine >/dev/null 2>&1; echo "$? ← docker run --no-such-flag"0 ← alpine sh -c exit 03 ← alpine sh -c exit 3127 ← alpine nosuchcommand126 ← alpine /etc/passwd125 ← docker run --no-such-flagآخرین عدد از هر اجرا: ۰ موفق، ۳ (کدی که خود برنامه برگردانده)، ۱۲۷ «فرمان پیدا نشد»، ۱۲۶ «فایل هست ولی اجرایی نیست»، و ۱۲۵ خطای خود داکر (مثل flag اشتباه، نه برنامه). مرجع کاملتر:
| کد | معنی | علت رایج |
|---|---|---|
0 |
موفق | پروسهی اصلی بهدرستی تمام شد |
1 |
خطای عمومی برنامه | باگ، تنظیم اشتباه (لاگ را بخوان) |
125 |
خطای خود docker run |
flag اشتباه، daemon خطا داد |
126 |
فرمان قابل اجرا نیست | دسترسی اجرا ندارد، یا فایل اجرایی نیست |
127 |
فرمان پیدا نشد | اشتباه تایپی، ابزار در image نیست |
137 |
128+9 (SIGKILL) |
docker kill، یا OOM killer، یا docker stop که مهلت گذشت |
139 |
128+11 (SIGSEGV) |
کرش برنامه (segfault) |
143 |
128+15 (SIGTERM) |
برنامه با درخواست توقف تمیز تمام شد |
قاعدهی حفظ: کد بالای ۱۲۸ یعنی پروسه با سیگنال مرده؛ سیگنال = کد منهای ۱۲۸.
کد خروج ۱۳۷ یعنی چه؟
۱۳۷ = ۱۲۸ + ۹. در درس منابع OOMKilled را میبینی.
مثال ۳: لاگ و inspect برای کانتینر خراب
Section titled “مثال ۳: لاگ و inspect برای کانتینر خراب”یک nginx با تنظیم اشتباه (یک ; جا افتاده):
server { listen 80 location / { return 200 "ok\n"; }}docker run -d --name lx-ts3 -v "$PWD/ts/bad.conf:/etc/nginx/conf.d/default.conf:ro" nginx:alpine >/dev/nullsleep 3docker ps -a --filter name=lx-ts3 --format '{{.Names}}: {{.Status}}'echo "--- docker logs:"docker logs lx-ts3 2>&1 | grep -E 'emerg'echo "--- docker inspect:"docker inspect lx-ts3 --format 'ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}} Status={{.State.Status}} Error="{{.State.Error}}"'lx-ts3: Exited (1) 3 seconds ago--- docker logs:2026/10/03 13:47:23 [emerg] 1#1: directive "listen" is not terminated by ";" in /etc/nginx/conf.d/default.conf:3nginx: [emerg] directive "listen" is not terminated by ";" in /etc/nginx/conf.d/default.conf:3--- docker inspect:ExitCode=1 OOMKilled=false Status=exited Error=""docker ps -a میگوید Exited (1)، docker logs دلیل را به زبان خود اپ میدهد (directive "listen" is not terminated by ";"، یعنی نقطهویرگول جا افتاده)، و inspect جزئیات را: کد ۱، بدون OOM، بدون خطای داکر. با اصلاح فایل:
docker rm -f lx-ts3 >/dev/nullsed -i.bak 's/listen 80$/listen 80;/' ts/bad.conf && rm ts/bad.conf.bakdocker run -d --name lx-ts3 -p 8350:80 -v "$PWD/ts/bad.conf:/etc/nginx/conf.d/default.conf:ro" nginx:alpine >/dev/nullsleep 2echo "بعد از اصلاح: $(curl -s localhost:8350)"docker rm -f lx-ts3 >/dev/nullبعد از اصلاح: okمثال ۴: مشکل شبکه، برنامه روی 127.0.0.1 گوش میکند
Section titled “مثال ۴: مشکل شبکه، برنامه روی 127.0.0.1 گوش میکند”شایعترین دلیل «پورت را publish کردم ولی کار نمیکند»: برنامه داخل کانتینر روی 127.0.0.1 گوش میکند، یعنی فقط برای همان کانتینر قابلدیدن است، نه برای بقیه یا میزبان:
docker run -d --name lx-ts4 -p 8351:8000 python:3.12-alpine python -m http.server 8000 --bind 127.0.0.1 >/dev/nullsleep 2curl -s -o /dev/null -m 5 localhost:8351; echo "از بیرون (bind روی 127.0.0.1): curl exit=$?"echo "داخل خود کانتینر: HTTP $(docker exec lx-ts4 python -c "import urllib.request; print(urllib.request.urlopen('http://127.0.0.1:8000').status)")"docker rm -f lx-ts4 >/dev/nulldocker run -d --name lx-ts4 -p 8351:8000 python:3.12-alpine python -m http.server 8000 --bind 0.0.0.0 >/dev/nullsleep 2echo "bind روی 0.0.0.0: curl HTTP $(curl -s -o /dev/null -m 5 -w '%{http_code}' localhost:8351)"docker rm -f lx-ts4 >/dev/nullاز بیرون (bind روی 127.0.0.1): curl exit=56داخل خود کانتینر: HTTP 200bind روی 0.0.0.0: curl HTTP 200از بیرون پاسخی نیامد (اتصال بلافاصله قطع شد؛ curl exit=56)، اما داخل کانتینر سرور سالم بود. بعد از --bind 0.0.0.0 از بیرون هم 200. راهحل: برنامه باید روی 0.0.0.0 گوش کند (یا IP کانتینر)، نه 127.0.0.1/localhost. اغلب با یک flag یا متغیر محیطی (HOST=0.0.0.0) تنظیم میشود.
مثال ۵: تفاوت «پورت publish نشده» و «اپ گوش نمیکند»
Section titled “مثال ۵: تفاوت «پورت publish نشده» و «اپ گوش نمیکند»”docker run -d --name lx-ts5 python:3.12-alpine python -m http.server 8000 >/dev/nullsleep 2curl -s -o /dev/null -m 5 -w 'بدون -p: ' localhost:8352; echo "exit=$?"IP=$(docker inspect lx-ts5 --format '{{(index .NetworkSettings.Networks "bridge").IPAddress}}')echo "از داخل شبکهی داکر: $(docker run --rm alpine wget -qO- -T 3 http://$IP:8000 >/dev/null 2>&1 && echo 'کار میکند' || echo 'در دسترس نیست')"docker rm -f lx-ts5 >/dev/nullبدون -p: exit=7از داخل شبکهی داکر: کار میکنددو حالت را از هم جدا کن: اگر curl localhost:PORT از بیرون «connection refused» (exit 7) میدهد، یعنی پورتی publish نشده (یا اپ نیست). اگر از کانتینر دیگر با IP/نام کار میکند ولی از میزبان نه، مشکل -p است. برای بررسی: docker port NAME، docker inspect, ss -tulpn روی میزبان.
مثال ۶: مشکل دسترسی فایل، volume و کاربر غیر root
Section titled “مثال ۶: مشکل دسترسی فایل، volume و کاربر غیر root”volume تازه معمولاً مالک root است. اپی که با کاربر غیر root اجرا میشود نمیتواند در آن بنویسد:
docker volume create lx-ts-vol >/dev/nulldocker run --rm -v lx-ts-vol:/data --user 1000:1000 alpine sh -c 'touch /data/f' 2>&1echo "--- مالک پوشهی volume:"docker run --rm -v lx-ts-vol:/data alpine ls -ld /data | awk '{print $1, $3, $4, $9}'echo "--- راهحل: مالکیت را با یک کانتینر کمکی درست کن"docker run --rm -v lx-ts-vol:/data alpine chown 1000:1000 /datadocker run --rm -v lx-ts-vol:/data --user 1000:1000 alpine sh -c 'touch /data/f && echo "نوشتن موفق"'docker volume rm lx-ts-vol >/dev/nulltouch: /data/f: Permission denied--- مالک پوشهی volume:drwxr-xr-x root root /data--- راهحل: مالکیت را با یک کانتینر کمکی درست کننوشتن موفقخطای Permission denied و مالک root. راهحلها: chown روی volume (مثل بالا یا در entrypoint)، یا ساخت پوشه در Dockerfile با مالک درست قبل از VOLUME (volume جدید مالکیت پوشهی image را به ارث میبرد)، یا --user با uid مناسب. (روی bind mount در Docker Desktop مک، لایهی اشتراک فایل uid ها را نگاشت میکند و این مشکل کمتر دیده میشود، ولی روی سرور لینوکس مهم است.)
مثال ۷: docker events، رویدادهای زنده
Section titled “مثال ۷: docker events، رویدادهای زنده”docker events هر اتفاق daemon را (ساخت، شروع، مرگ، حذف) همزمان نشان میدهد، برای وقتی که میخواهی ببینی «کانتینر مدام ریستارت میشود؟»:
docker events --filter name=lx-ev --format '{{.Action}}' > ev.txt 2>&1 &EVP=$!sleep 1docker run --name lx-ev --rm alpine sh -c 'exit 2' >/dev/null 2>&1sleep 2kill $EVP 2>/dev/null; wait $EVP 2>/dev/nullcat ev.txt | grep -v '^exec' | tr '\n' ' '; echocreate attach connect start disconnect die destroyدنبالهی رویداد یک کانتینر (create ← attach ← start ← die ← destroy …) دیده میشود. die را همراه exitCode میتوان دید (--format '{{.Actor.Attributes.exitCode}}'). برای یک بازهی گذشته: docker events --since 10m --until 0s.
مثال ۸: داخل کانتینر مرده را ببین
Section titled “مثال ۸: داخل کانتینر مرده را ببین”کانتینر خراب اجرا نمیشود که exec بزنی. چند راه:
docker run --name lx-ts6 alpine sh -c 'echo "دادهی مهم" > /report.txt; exit 1' >/dev/null 2>&1echo "--- docker diff (تغییرهای فایلسیستم):"docker diff lx-ts6 | head -3echo "--- docker cp از کانتینر متوقف:"docker cp lx-ts6:/report.txt - | tar -xOecho "--- یک شل در همان image (entrypoint عوض شده):"docker run --rm --entrypoint sh alpine -c 'ls / | head -3 | tr "\n" " "'; echodocker rm lx-ts6 >/dev/null--- docker diff (تغییرهای فایلسیستم):A /report.txt--- docker cp از کانتینر متوقف:دادهی مهم--- یک شل در همان image (entrypoint عوض شده):bin dev etcdocker diff تغییرها را میدهد (A اضافه، C تغییر، D حذف)، docker cp از کانتینر متوقف هم کار میکند، و --entrypoint sh یک محیط یکسان با image برای آزمایش میدهد. (در ترمینال واقعی از -it استفاده میکنی تا شل تعاملی بشود.)
پشت پرده
Section titled “پشت پرده”داکر وضعیت هر کانتینر را در State ذخیره میکند: Status، ExitCode، OOMKilled، Error (خطای خود داکر، مثلاً نتوانست فرمان را شروع کند)، StartedAt و FinishedAt. لاگها همان stdout و stderr پروسهی اصلیاند که توسط logging driver (پیشفرض json-file) روی دیسک ذخیره میشوند؛ برای همین docker logs برای کانتینر مرده هم کار میکند ولی با docker rm لاگها هم میروند.
دو نکته که مشکلساز میشوند:
- اپی که لاگ را در فایل مینویسد (نه stdout) در
docker logsدیده نمیشود. داکر فقط خروجی استاندارد را جمع میکند. - اگر کانتینر با
--restartمدام ریستارت میشود،docker psفقط وضعیت فعلی را میدهد.docker inspect --format '{{.RestartCount}}'وdocker eventsتصویر درست را میدهند.
جدولهای مرجع
Section titled “جدولهای مرجع”| بخش | دستور | چه میپرسد؟ |
|---|---|---|
| وضعیت | docker ps -a |
چه کانتینرهایی هستند و چرا خارج شدند؟ |
| لاگ | docker logs --tail 50 -f NAME |
خود اپ چه گفت؟ |
| پرونده | docker inspect NAME |
ExitCode، Env، Mounts، شبکه |
| بازرسی زنده | docker exec NAME sh |
داخل کانتینر در حال اجرا چه خبر است؟ |
| بازتولید | docker run --rm --entrypoint sh IMG |
image بدون فرمان اصلی چه دارد؟ |
| فایلها | docker diff NAME، docker cp |
چه چیزی تغییر کرد؟ |
| رویداد | docker events |
چه زمانی چه شد؟ |
| پورت | docker port NAME |
کدام پورت publish شده؟ |
| علامت | محتملترین علت | اولین کار |
|---|---|---|
Exited (0) بلافاصله |
پروسهی اصلی ادامهدار نیست | CMD/ENTRYPOINT را بررسی کن |
Exited (1) |
خطای اپ | docker logs |
Exited (127) |
فرمان پیدا نشد | مسیر/ابزار در image |
Exited (137) |
کشته شد (OOM یا kill) | inspect → OOMKilled |
Restarting مدام |
کرش و restart policy | logs + RestartCount |
| پورت publish شده ولی قطع | bind روی 127.0.0.1 | گوشدادن روی 0.0.0.0 |
Permission denied |
مالک فایل/volume | ls -ld، chown، --user |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) فقط docker ps (بدون -a)
Section titled “۱) فقط docker ps (بدون -a)”docker ps فقط کانتینرهای در حال اجرا را نشان میدهد؛ خرابها دیده نمیشوند. راهحل: docker ps -a.
۲) docker rm قبل از دیدن لاگ
Section titled “۲) docker rm قبل از دیدن لاگ”با حذف کانتینر لاگهایش هم میرود. راهحل: اول docker logs، بعد حذف.
۳) خطای نام کانتینر
Section titled “۳) خطای نام کانتینر”docker logs lx-no-such-container 2>&1 | head -1Error response from daemon: No such container: lx-no-such-containerنام اشتباه یا کانتینر حذفشده. راهحل: docker ps -a را ببین؛ میتوانی بهجای نام، چند حرف اول ID را بدهی.
۴) عوض کردن چند چیز همزمان
Section titled “۴) عوض کردن چند چیز همزمان”اگر هم image، هم تنظیم، هم پورت را با هم عوض کنی نمیفهمی کدام مشکل را حل کرد. راهحل: در هر مرحله یک تغییر.
۵) اعتماد به latest موقع عیبیابی
Section titled “۵) اعتماد به latest موقع عیبیابی”اگر دیروز کار میکرد و امروز نه، شاید tag latest به نسخهی دیگری اشاره میکند. راهحل: docker image inspect --format '{{.Id}}' را دو بار مقایسه کن و tag دقیق بگذار.
کانتینر alpine sh -c 'exit 5' را اجرا کن و کد خروج را با docker inspect بخوان.
دیدن جواب
docker run --name lx-e1 alpine sh -c 'exit 5' >/dev/null 2>&1docker inspect lx-e1 --format 'کد خروج: {{.State.ExitCode}}'docker rm lx-e1 >/dev/nullکد خروج: 5تمرین اصلی: سه کانتینر خراب را عیبیابی کن. (۱) nginx با تنظیمی که ; ندارد (۲) اپ پایتونی که روی 127.0.0.1 گوش میکند و با -p publish شده (۳) کانتینری که با uid=1000 میخواهد در volume تازه بنویسد. برای هر کدام علت را با یک دستور نشان بده و درست کن.
دیدن جواب
echo "=== ۱) nginx"printf 'server {\n listen 80\n}\n' > e1.confdocker run -d --name lx-e2a -v "$PWD/e1.conf:/etc/nginx/conf.d/default.conf:ro" nginx:alpine >/dev/null; sleep 2docker logs lx-e2a 2>&1 | grep -m1 emerg | cut -c1-120printf 'server {\n listen 80;\n}\n' > e1.confdocker rm -f lx-e2a >/dev/null; docker run -d --name lx-e2a -v "$PWD/e1.conf:/etc/nginx/conf.d/default.conf:ro" nginx:alpine >/dev/null; sleep 2docker inspect lx-e2a --format 'بعد از اصلاح: {{.State.Status}}'docker rm -f lx-e2a >/dev/nullecho "=== ۲) bind روی 127.0.0.1"docker run -d --name lx-e2b -p 8353:8000 python:3.12-alpine python -m http.server 8000 --bind 127.0.0.1 >/dev/null; sleep 2curl -s -o /dev/null -m 4 localhost:8353; echo "قبل: curl exit=$?"docker rm -f lx-e2b >/dev/nulldocker run -d --name lx-e2b -p 8353:8000 python:3.12-alpine python -m http.server 8000 --bind 0.0.0.0 >/dev/null; sleep 2echo "بعد: HTTP $(curl -s -o /dev/null -m 4 -w '%{http_code}' localhost:8353)"docker rm -f lx-e2b >/dev/nullecho "=== ۳) volume"docker volume create lx-e2v >/dev/nulldocker run --rm -v lx-e2v:/data --user 1000 alpine touch /data/x 2>&1 | head -1docker run --rm -v lx-e2v:/data alpine chown 1000 /datadocker run --rm -v lx-e2v:/data --user 1000 alpine sh -c 'touch /data/x && echo "بعد از chown: موفق"'docker volume rm lx-e2v >/dev/nullrm -f e1.conf=== ۱) nginx2026/10/03 13:47:57 [emerg] 1#1: unexpected "}" in /etc/nginx/conf.d/default.conf:3بعد از اصلاح: running=== ۲) bind روی 127.0.0.1قبل: curl exit=56بعد: HTTP 200=== ۳) volumetouch: /data/x: Permission deniedبعد از chown: موفقیک اسکریپت diagnose NAME بنویس که برای یک کانتینر خلاصهی عیبیابی چاپ کند: وضعیت، کد خروج، OOMKilled، تعداد restart، و آخرین سه خط لاگ. روی کانتینری که با کد ۳ خارج شده و لاگ دارد تست کن.
دیدن جواب
diagnose() { docker inspect "$1" --format 'وضعیت={{.State.Status}} کد-خروج={{.State.ExitCode}} OOM={{.State.OOMKilled}} restart={{.RestartCount}}' echo "آخرین لاگ:"; docker logs --tail 3 "$1" 2>&1 | sed 's/^/ /'}docker run --name lx-e3 alpine sh -c 'echo یک; echo دو; echo "خطا: چیزی خراب شد" >&2; exit 3' >/dev/null 2>&1diagnose lx-e3docker rm lx-e3 >/dev/nullوضعیت=exited کد-خروج=3 OOM=false restart=0آخرین لاگ: خطا: چیزی خراب شد یک دوآزمونک
Section titled “آزمونک”کانتینر ubuntu بلافاصله بعد از docker run -d با Exited (0) تمام شد. علتش؟
کانتینر تا وقتی زنده است که PID 1 زنده باشد.
کد خروج ۱۲۷ یعنی…
اشتباه تایپی یا ابزار در image نیست.
اولین دستور عیبیابی کانتینری که دیده نمیشود؟
docker ps فقط کانتینرهای در حال اجرا را نشان میدهد.
اپ داخل کانتینر روی 127.0.0.1 گوش میکند و -p هم زدهای. نتیجه؟
127.0.0.1 فقط برای خود کانتینر است.
docker logs برای چه چیزی کار نمیکند؟
داکر فقط stdout و stderr را جمع میکند.
Permission denied روی volume برای کاربر غیر root معمولاً به چه علت است؟
مالکیت را با یک کانتینر کمکی یا در Dockerfile درست کن.
جمعبندی
Section titled “جمعبندی”- ترتیب:
docker ps -a←docker logs←docker inspect← بازتولید (exec/run --entrypoint sh). - «Exited (0)» یعنی پروسهی اصلی درست تمام شد (نه خراب)؛ کانتینر عمرش به PID 1 است.
- کدها: ۱ خطای اپ، ۱۲۵ خطای داکر، ۱۲۶/۱۲۷ مشکل فرمان، ۱۳۷ = SIGKILL، ۱۴۳ = SIGTERM (کد > ۱۲۸ = ۱۲۸ + سیگنال).
- شبکه: اپ باید روی
0.0.0.0گوش کند؛ «refused» یعنی پورت/اپ نیست، و از داخل شبکهی داکر هم باید تست شود. - فایل: مالکیت volume را (
chown/--user/Dockerfile) درست کن. - هر بار فقط یک تغییر؛ قبل از
docker rmلاگ را ببین.
| دستور | کاری که میکند |
|---|---|
docker ps -a | همهی کانتینرها با وضعیت |
docker logs --tail 50 -f NAME | لاگ (آخر و زنده) |
docker inspect NAME --format '{{.State.ExitCode}}' | کد خروج |
docker inspect NAME --format '{{.State.OOMKilled}}' | آیا OOM کشته؟ |
docker exec NAME sh | شل داخل کانتینر در حال اجرا |
docker run --rm --entrypoint sh IMG -c 'ls /' | نگاه به image بدون فرمان اصلی |
docker diff NAME | تغییرهای فایلسیستم |
docker cp NAME:/path ./ | کپی فایل از کانتینر (حتی متوقف) |
docker events --since 10m | رویدادهای اخیر |
docker port NAME | پورتهای publishشده |