رفتن به محتوا
LoopX

آزمون بخش ۲: ساختارمند نوشتن

این آزمون چهار درس بخش «ساختارمند نوشتن» را می‌سنجد. جواب هر سؤال همان لحظه با توضیح نشان داده می‌شود و نتیجه‌ات در همین مرورگر ذخیره می‌شود (پیشرفت من).

؟ آزمون بخش ۲
  1. تابعی با echo یک عدد چاپ می‌کند و با return ۰ تمام می‌شود. نتیجه‌ی تابع را چطور در متغیر می‌گیری؟

  2. متغیری که داخل تابع (بدون local) مقدار می‌گیرد، بعد از پایان تابع چه می‌شود؟

  3. چرا local out=$(false) کد خروج false را پنهان می‌کند؟

  4. داخل تابعی exit 1 نوشته‌ای. اثرش چیست؟

  5. تابع inc شمارنده‌ی سراسری cnt را زیاد می‌کند. بعد از v=$(inc)، cnt چه می‌شود؟

  6. تابع hi را در اسکریپت تعریف کرده‌ای و bash -c hi (فرزند) آن را نمی‌شناسد. راه‌حل؟

  7. arr=(a b c) و echo $arr چه چاپ می‌کند؟

  8. files=("my report.txt" "a.txt") و دستور cp "${files[@]}" backup/ چند آرگومان مبدأ می‌دهد؟

  9. روی آرایه‌ی سه‌عضوی unset 'arr[1]' می‌زنی. ${!arr[@]} چه می‌دهد؟

  10. اگر declare -A را فراموش کنی و بنویسی m[apple]=1; m[banana]=2 چه می‌شود؟

  11. کدام روش برای ساختن آرایه از خط‌های یک فایل (حتی خط‌های دارای فاصله) درست است؟

  12. p=/var/log/app.log.gz و echo ${p##*/} چه چاپ می‌کند؟

  13. برای سه کاراکتر آخر s از ${s:-3} استفاده کرده‌ای و کل رشته برمی‌گردد. چرا و راه‌حل؟

  14. تفاوت ${v:-d} و ${v-d}؟

  15. for f in *.txt; do mv $f ${f%.txt}.md; done با فایل «my notes.txt» چه می‌شود؟

  16. چرا ${s/[0-9]+/N} «یک یا چند رقم» را عوض نمی‌کند؟

  17. کدام یک $uri را در متن here-doc عیناً نگه می‌دارد؟

  18. چرا sudo cat > /etc/app.conf <<EOF با Permission denied شکست می‌خورد؟

  19. <<-EOF دقیقاً چه چیزی را از ابتدای خط‌ها برمی‌دارد؟

  20. چرا متغیر شل را به awk با -v می‌دهیم و نه داخل متن برنامه‌ی awk؟

  21. برای جست‌وجوی متن دقیق example.ir (نقطه واقعی) در فایل با متغیر name، کدام درست‌تر است؟

  22. کدام دستور ویرایش درجای فایل را هم روی GNU sed (لینوکس) و هم BSD sed (macOS) یکسان انجام می‌دهد؟

🏁 چالش: سایت‌سازسخت

با همه‌ی آنچه در این بخش آموختی، اسکریپت sites.sh <فایل-دامنه‌ها> <پوشه‌ی-خروجی> را بنویس که از فهرست دامنه‌ها، فایل تنظیمات nginx بسازد:

۱. تابع‌ها (با local برای هر متغیر داخلشان): log سطح پیام (روی صفحه و با tee -a در build.log)، die پیام (ثبت خطا روی stderr و exit 1)، valid_domain (فقط کد خروج؛ با regex)، trim (فقط با گسترش پارامتر). ۲. فایل دامنه‌ها را خط‌به‌خط بخوان (while IFS= read -r)؛ هر خط را trim و حروف کوچک کن؛ کامنت‌ها (#) و خط‌های خالی را رد کن. دامنه‌های معتبر را در یک آرایه و نامعتبرها را در آرایه‌ی دیگر بریز (برای نامعتبر، یک WARN ثبت کن). ۳. برای هر دامنه‌ی معتبر با here-doc فایل پوشه/دامنه.conf را بساز (server_name با و بدون www.، root، و try_files $uri $uri/ =404 سالم)؛ روی فایل موجود رونویسی نکن. ۴. با یک آرایه‌ی انجمنی تعداد دامنه‌های معتبر را به تفکیک پسوند (ir، com، …) بشمار و خلاصه (تعداد معتبر و نامعتبر، شمارش پسوندها مرتب) را چاپ کن. ۵. کد خروج: ۰ همه‌چیز درست؛ ۱ استفاده‌ی غلط یا نبودن فایل؛ ۲ اگر دامنه‌ی نامعتبر بود (ولی دامنه‌های معتبر بسازد). ۶. ثابت کن فایل‌های ساخته‌شده را خود nginx می‌پذیرد (nginx -t با یک پوشش حداقلی) و در پایان همه‌چیز را پاک کن.

راهنمایی

ساختار: چهار تابع بالای فایل؛ بررسی آرگومان‌ها با [[ $# -eq 2 ]] || die ...؛ حلقه‌ی while IFS= read -r line; do line="$(trim "${line,,}")"; [[ -z $line || $line == \#* ]] && continue; ...; done < "$domains_file". برای پسوند: ${domain##*.} و ((per_tld[$tld]++)) بعد از declare -A per_tld. برای here-doc بدون کوتیشن، \$uri را escape کن. برای مرتب‌کردن کلیدها: printf '%s\n' "${!per_tld[@]}" | sort. برای nginx -t یک فایل پوششی با include پوشه/*.conf و مسیرهای موقت داخل پوشه‌ی خودت بساز و -c و -p بده.

راه‌حل
mkdir -p ~/bashlab/quiz2 && cd ~/bashlab/quiz2
cat > sites.sh <<'EOF'
#!/usr/bin/env bash
# sites.sh: build nginx server blocks from a list of domains
# Usage: ./sites.sh <domains-file> <output-dir>
# Exit codes: 0 ok, 1 usage/missing file, 2 some domains were invalid
LOG_FILE="${LOG_FILE:-build.log}"
log() {
local level="$1"
shift
echo "$(date '+%F %T') [$level] $*" | tee -a "$LOG_FILE"
}
die() {
log ERROR "$*" >&2
exit 1
}
valid_domain() {
[[ $1 =~ ^([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$ ]]
}
trim() {
local s="$1"
s="${s#"${s%%[![:space:]]*}"}"
s="${s%"${s##*[![:space:]]}"}"
printf '%s' "$s"
}
[[ $# -eq 2 ]] || die "usage: $0 <domains-file> <output-dir>"
domains_file="$1"
out_dir="$2"
[[ -f $domains_file ]] || die "'$domains_file' not found"
mkdir -p "$out_dir"
valid=()
invalid=()
declare -A per_tld
while IFS= read -r line; do
line="$(trim "${line,,}")"
[[ -z $line || $line == \#* ]] && continue
if valid_domain "$line"; then
valid+=("$line")
((per_tld[${line##*.}]++))
else
invalid+=("$line")
log WARN "skipping invalid domain: '$line'"
fi
done < "$domains_file"
for d in "${valid[@]}"; do
conf="$out_dir/$d.conf"
if [[ -e $conf ]]; then
log WARN "$conf already exists, skipped"
continue
fi
cat > "$conf" <<NGINX
server {
listen 80;
server_name $d www.$d;
root /var/www/$d;
location / {
try_files \$uri \$uri/ =404;
}
}
NGINX
log INFO "created $conf"
done
echo "--- Summary"
echo "valid: ${#valid[@]}"
echo "invalid: ${#invalid[@]}"
for tld in $(printf '%s\n' "${!per_tld[@]}" | sort); do
echo " .$tld ${per_tld[$tld]}"
done
if (( ${#invalid[@]} > 0 )); then
exit 2
fi
EOF
chmod +x sites.sh
cat > domains.txt <<'EOF'
# customers
Shop.Example.IR
blog.example.ir
example.com
bad;domain
-leading.com
mail.example.org
EOF
echo "== اجرای اول"
./sites.sh domains.txt out; echo "کد خروج: $?"
echo "== فایل‌های ساخته‌شده"
ls out
echo "== اجرای دوم (رونویسی نکند)"
./sites.sh domains.txt out 2>&1 | grep -c "already exists"
echo "== خطاهای استفاده"
./sites.sh; echo "کد خروج: $?"
./sites.sh nofile.txt out; echo "کد خروج: $?"
echo "== یکی از فایل‌ها"
cat out/shop.example.ir.conf
echo "== nginx -t روی همه‌ی فایل‌ها"
mkdir -p tmp prefix
cat > nginx-test.conf <<EOF
pid $PWD/nginx.pid;
error_log $PWD/error.log;
events {}
http {
access_log off;
client_body_temp_path $PWD/tmp/body;
proxy_temp_path $PWD/tmp/proxy;
fastcgi_temp_path $PWD/tmp/fastcgi;
uwsgi_temp_path $PWD/tmp/uwsgi;
scgi_temp_path $PWD/tmp/scgi;
include $PWD/out/*.conf;
}
EOF
nginx -t -c "$PWD/nginx-test.conf" -p "$PWD/prefix"; echo "کد خروج: $?"
echo "== log"
grep -c INFO build.log
grep WARN build.log | cut -c21-80 | head -n 2
echo "== پاک‌سازی"
cd ~ && rm -rf ~/bashlab
خروجی
== اجرای اول
2026-10-04 10:50:40 [WARN] skipping invalid domain: 'bad;domain'
2026-10-04 10:50:40 [WARN] skipping invalid domain: '-leading.com'
2026-10-04 10:50:40 [INFO] created out/shop.example.ir.conf
2026-10-04 10:50:40 [INFO] created out/blog.example.ir.conf
2026-10-04 10:50:40 [INFO] created out/example.com.conf
2026-10-04 10:50:40 [INFO] created out/mail.example.org.conf
--- Summary
valid: 4
invalid: 2
.com 1
.ir 2
.org 1
کد خروج: 2
== فایل‌های ساخته‌شده
blog.example.ir.conf
example.com.conf
mail.example.org.conf
shop.example.ir.conf
== اجرای دوم (رونویسی نکند)
4
== خطاهای استفاده
2026-10-04 10:50:41 [ERROR] usage: ./sites.sh <domains-file> <output-dir>
کد خروج: 1
2026-10-04 10:50:41 [ERROR] 'nofile.txt' not found
کد خروج: 1
== یکی از فایل‌ها
server {
listen 80;
server_name shop.example.ir www.shop.example.ir;
root /var/www/shop.example.ir;
location / {
try_files $uri $uri/ =404;
}
}
== nginx -t روی همه‌ی فایل‌ها
nginx: the configuration file /home/ali/bashlab/quiz2/nginx-test.conf syntax is ok
nginx: configuration file /home/ali/bashlab/quiz2/nginx-test.conf test is successful
کد خروج: 0
== log
4
[WARN] skipping invalid domain: 'bad;domain'
[WARN] skipping invalid domain: '-leading.com'
== پاک‌سازی