405 lines
No EOL
18 KiB
Bash
Executable file
405 lines
No EOL
18 KiB
Bash
Executable file
#!/bin/bash
|
||
# Name: ipf
|
||
# Version: 1.7.0
|
||
# Date: 2026-03-05
|
||
# Description: Fully featured port forwarder with rich help, multi-protocol
|
||
# detection (SSH/HTTP/HTTPS/etc.), and JP/EN localization.
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# 1. Locale & Messaging Setup
|
||
# -----------------------------------------------------------------------------
|
||
SYSTEM_LANG=$([[ "$LANG" =~ ^ja ]] && echo "ja" || echo "en")
|
||
|
||
declare -A MSGS
|
||
if [[ "$SYSTEM_LANG" == "ja" ]]; then
|
||
MSGS=(
|
||
[system]="\e[34m[システム]\e[0m"
|
||
[enabling_fwd]="IP フォワーディングを有効化しています..."
|
||
[err_fwd]="致命的:net.ipv4.ip_forward を有効にできませんでした。"
|
||
[rules_header]="転送ルール一覧 (テーブル:%s):"
|
||
[overwrite]="\e[33mポート :%s (ハンドル %s) の既存ルールを上書きします...\e[0m"
|
||
[reset_warn]="\e[33m警告:テーブル '%s' 内の全ルールが削除されます。\e[0m"
|
||
[confirm]="削除を確認しますか?(y/N): "
|
||
[reset_done]="テーブル '%s' をリセットしました。"
|
||
[testing]="ハンドル %s をテスト中 (Local :%s -> Target %s:%s) [%s]... "
|
||
[passed]=" \e[32m成功 (パケット増分: +%s)\e[0m"
|
||
[skipped]=" \e[33mスキップ (疎通なし、またはシャドウイングの可能性)\e[0m"
|
||
[err_not_found]="ハンドル %s が見つかりません。"
|
||
[unknown_opt]="不明なオプション '%s' です。-h でヘルプを表示してください。"
|
||
[nat_mode]="\e[32mSNAT モード (fail2ban 対応)\e[0m"
|
||
[masq_mode]="\e[33mMASQUERADE モード (シンプル)\e[0m"
|
||
)
|
||
else
|
||
MSGS=(
|
||
[system]="\e[34m[System]\e[0m"
|
||
[enabling_fwd]="Enabling IP forwarding..."
|
||
[err_fwd]="Critical: Could not enable net.ipv4.ip_forward."
|
||
[rules_header]="Forwarding Rules (Table: %s):"
|
||
[overwrite]="\e[33mOverwriting existing rule on port :%s (Handle %s)...\e[0m"
|
||
[reset_warn]="\e[33mWarning: This will delete ALL rules in table '%s'.\e[0m"
|
||
[confirm]="Confirm removal? (y/N): "
|
||
[reset_done]="Table '%s' has been reset."
|
||
[testing]="Testing handle %s (Local :%s -> Target %s:%s) [%s]... "
|
||
[passed]=" \e[32mPASSED (Counter: +%s)\e[0m"
|
||
[skipped]=" \e[33mSKIPPED (No traffic or shadowed)\e[0m"
|
||
[err_not_found]="Handle %s not found."
|
||
[unknown_opt]="Unknown option '%s'. Use -h for help."
|
||
[nat_mode]="\e[32mSNAT mode (fail2ban compatible)\e[0m"
|
||
[masq_mode]="\e[33mMASQUERADE mode (simple)\e[0m"
|
||
)
|
||
fi
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# 2. Root Check & Init
|
||
# -----------------------------------------------------------------------------
|
||
if [ "$(id -u)" -ne 0 ]; then
|
||
exec sudo "$0" "$@"
|
||
fi
|
||
|
||
TABLE_NAME="ipf"
|
||
VERSION="1.7.0"
|
||
PROTO="tcp"
|
||
FORCE_FLAG=false
|
||
SKIP_TEST=false
|
||
QUIET_MODE=false
|
||
RESET_MODE=false
|
||
SNAT_FLAG=false
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# 3. Core Functions
|
||
# -----------------------------------------------------------------------------
|
||
|
||
msg() { [[ "$QUIET_MODE" == false ]] && echo -e "$@"; }
|
||
err() { [[ "$QUIET_MODE" == false ]] && echo -e "\e[31m$@\e[0m" >&2 || echo "$@" >&2; }
|
||
|
||
enable_forwarding() {
|
||
msg "${MSGS[system]} ${MSGS[enabling_fwd]}"
|
||
sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1
|
||
for dev in /proc/sys/net/ipv4/conf/*/route_localnet; do
|
||
[[ -f "$dev" ]] && echo 1 > "$dev" 2>/dev/null
|
||
done
|
||
}
|
||
|
||
init_nft() {
|
||
nft add table inet "${TABLE_NAME}" 2>/dev/null
|
||
nft add chain inet "${TABLE_NAME}" prerouting "{ type nat hook prerouting priority -100 ; policy accept ; }" 2>/dev/null
|
||
nft add chain inet "${TABLE_NAME}" output "{ type nat hook output priority -100 ; policy accept ; }" 2>/dev/null
|
||
nft add chain inet "${TABLE_NAME}" postrouting "{ type nat hook postrouting priority 100 ; policy accept ; }" 2>/dev/null
|
||
nft add chain inet "${TABLE_NAME}" forward "{ type filter hook forward priority 0 ; policy accept ; }" 2>/dev/null
|
||
}
|
||
|
||
test_connection() {
|
||
local host=$1; local port=$2; local proto=${3:-$PROTO}; local info=""
|
||
|
||
# UDP mode: use nc -zu for connectivity check
|
||
if [[ "$proto" == "udp" ]]; then
|
||
if nc -zu -w 1 "$host" "$port" >/dev/null 2>&1; then
|
||
echo -e "\e[32mUP\e[0m (UDP)"
|
||
return 0
|
||
else
|
||
echo -e "\e[31mDOWN\e[0m (UDP)"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
# A. Protocol Banner Check (SSH, FTP, etc.)
|
||
local banner=$(timeout 0.5s nc -w 1 "$host" "$port" 2>/dev/null | head -n 1 | tr -d '\000-\031')
|
||
if [[ -n "$banner" ]]; then
|
||
case "$banner" in
|
||
SSH*) info=" (SSH: ${banner:0:15})" ;;
|
||
"220"*) info=" (FTP/SMTP)" ;;
|
||
"RFB"*) info=" (VNC)" ;;
|
||
"mysql"*) info=" (MySQL)" ;;
|
||
*) info=" (Banner: ${banner:0:15})" ;;
|
||
esac
|
||
echo -e "\e[32mUP\e[0m${info}"; return 0
|
||
fi
|
||
|
||
# B. HTTP/HTTPS & Ollama Check
|
||
if nc -z -w 1 "$host" "$port" >/dev/null 2>&1; then
|
||
local schemas=("http" "https")
|
||
[[ "$port" == "443" ]] && schemas=("https" "http")
|
||
|
||
for sch in "${schemas[@]}"; do
|
||
# Ollama Check (Port 11434 or general HTTP)
|
||
local url="${sch}://${host}:${port}"
|
||
local res=$(curl -sLk -m 3 -A "Mozilla/5.0 ipf-tester" "${url}/api/tags" 2>/dev/null)
|
||
|
||
# Ollama API 判定 (JSON に "models" が含まれているか)
|
||
if [[ "$res" == *"models"* ]]; then
|
||
info=" (Ollama API)"
|
||
echo -e "\e[32mUP\e[0m${info}"
|
||
|
||
# "__" を含むモデル名の抽出
|
||
local hidden_models=$(echo "$res" | grep -oP '"name":"[^"]*__[^"]*"' | cut -d'"' -f4)
|
||
if [[ -n "$hidden_models" ]]; then
|
||
echo -e " \e[35m[!] Hidden Models Found:\e[0m"
|
||
while read -r m; do
|
||
echo -e " - $m"
|
||
done <<< "$hidden_models"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
# 通常の HTTP/HTTPS ステータス判定
|
||
local code=$(curl -IsLk -m 2 -o /dev/null -w "%{http_code}" "${url}/" 2>/dev/null)
|
||
[[ "$code" == "000" ]] && code=$(curl -sLk -m 2 -o /dev/null -w "%{http_code}" "${url}/" 2>/dev/null)
|
||
|
||
if [[ "$code" == "400" ]]; then
|
||
info=" (HTTPS? 400 Bad Request)"; break
|
||
elif [[ "$code" =~ ^[0-9]+$ && "$code" -ne 000 ]]; then
|
||
info=" (${sch^^} $code)"; break
|
||
fi
|
||
done
|
||
echo -e "\e[32mUP\e[0m${info}"
|
||
return 0
|
||
else
|
||
echo -e "\e[31mDOWN\e[0m"; return 1
|
||
fi
|
||
}
|
||
|
||
|
||
show_help() {
|
||
if [[ "$SYSTEM_LANG" == "ja" ]]; then
|
||
cat << EOF
|
||
ipf (IP Forwarder) - nftables ベースのポート転送管理ツール
|
||
|
||
使用法:
|
||
ipf [オプション] [ルール]
|
||
|
||
ルール形式:
|
||
<ローカルポート>:<ターゲット IP>:<ターゲットポート>
|
||
例:ipf 8080:10.10.100.1:80 (外部アクセス 8080 を内部 80 へ)
|
||
|
||
<ローカルポート>:<ターゲットポート>
|
||
例:ipf 80:8080 (外部 80 をローカル 8080 へ)
|
||
|
||
<ポート番号>
|
||
例:ipf 22 (外部 22 をローカル 22 へ)
|
||
|
||
オプション:
|
||
-l, -L 現在の転送ルールを一覧表示 (引数なしのデフォルト)
|
||
-d <ID/PORT> 指定したハンドル ID または : ポート でルールを削除
|
||
例:ipf -d 12 / ipf -d :80
|
||
-d all すべてのルールを削除してテーブルを初期化
|
||
-t <IP:PORT> 指定したターゲットの接続性とプロトコルをテスト
|
||
-t <HANDLE> 既存ルールのハンドル ID を指定して疎通テストを実行
|
||
-u UDP プロトコルを使用 (デフォルトは TCP)
|
||
ルール追加時:ipf -u 5353:10.0.0.1:53
|
||
テスト時: ipf -u -t 10.0.0.1:53
|
||
-R 設定リセット (テーブルを再作成)
|
||
-f, -y 確認なしで実行し、IP フォワーディングを強制有効化
|
||
-q クワイエットモード (メッセージ出力を抑制)
|
||
-F SNAT モード (fail2ban と整合): snat to type nat orig
|
||
例:ipf -F 8080:10.10.100.1:80
|
||
-h, --help この詳細ヘルプを表示
|
||
|
||
セキュリティポリシー:
|
||
デフォルトは MASQUERADE モード (シンプル、無難) です。
|
||
fail2ban と整合させるには -F オプションを使用します。
|
||
|
||
SNAT モードの特徴:
|
||
- クライアント IP がログされる
|
||
- fail2ban が正しく動作する
|
||
- パフォーマンスは MASQUERADE よりわずかに低速
|
||
|
||
EOF
|
||
else
|
||
cat << EOF
|
||
ipf (IP Forwarder) - Simple nftables-based port forwarding manager
|
||
|
||
Usage:
|
||
ipf [OPTIONS] [RULE]
|
||
|
||
Rule Formats:
|
||
<LPORT>:<TIP>:<TPORT>
|
||
Ex: ipf 8080:10.10.100.1:80
|
||
|
||
<LPORT>:<TPORT>
|
||
Ex: ipf 80:8080
|
||
|
||
<PORT>
|
||
Ex: ipf 22
|
||
|
||
Options:
|
||
-l, -L List current rules
|
||
-d <ID/PORT> Delete rule by handle or :port
|
||
-d all Flush all rules
|
||
-t <IP:PORT> Test connectivity/protocol
|
||
-u Use UDP protocol (default: TCP)
|
||
Add rule: ipf -u 5353:10.0.0.1:53
|
||
Test: ipf -u -t 10.0.0.1:53
|
||
-R Reset table
|
||
-f, -y Force enable forwarding
|
||
-q Quiet mode (suppress output)
|
||
-F SNAT mode (fail2ban compatible): snat to type nat orig
|
||
Ex: ipf -F 8080:10.10.100.1:80
|
||
-h, --help Show this help
|
||
|
||
Security Policy:
|
||
Default is MASQUERADE mode (simple, safe).
|
||
Use -F option for fail2ban compatibility.
|
||
|
||
SNAT Mode Features:
|
||
- Client IP is logged
|
||
- fail2ban works correctly
|
||
- Slightly slower than MASQUERADE
|
||
|
||
EOF
|
||
fi
|
||
}
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# 4. Implementation Logic (List, Add, Delete)
|
||
# -----------------------------------------------------------------------------
|
||
|
||
delete_by_handle() {
|
||
local h=$1; local uuid=""
|
||
local chains=("prerouting" "output" "forward" "postrouting")
|
||
for c in "${chains[@]}"; do
|
||
uuid=$(nft -a list chain inet "${TABLE_NAME}" "$c" 2>/dev/null | grep "handle $h" | grep -o 'ipf-id:[a-z0-9-]*' | head -n 1)
|
||
[[ -n "$uuid" ]] && break
|
||
done
|
||
[[ -z "$uuid" ]] && return 1
|
||
for c in "${chains[@]}"; do
|
||
nft -a list chain inet "${TABLE_NAME}" "$c" 2>/dev/null | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r rh; do
|
||
nft delete rule inet "${TABLE_NAME}" "$c" handle "$rh"
|
||
done
|
||
done
|
||
return 0
|
||
}
|
||
|
||
test_strict_handle() {
|
||
local h=$1
|
||
local info=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "handle $h")
|
||
[[ -z "$info" ]] && { err "$(printf "${MSGS[err_not_found]}" "$h")"; return; }
|
||
local lp=$(echo "$info" | grep -o 'dport [0-9]*' | awk '{print $2}')
|
||
local target=$(echo "$info" | grep -oE 'to ([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | awk '{print $2}')
|
||
local tip=$(echo "$target" | cut -d':' -f1 | tr -d '[]')
|
||
local tp=$(echo "$target" | cut -d':' -f2)
|
||
local uuid=$(echo "$info" | grep -o 'ipf-id:[a-z0-9-]*')
|
||
local short_id=$(echo "$uuid" | cut -d':' -f2 | cut -c1-8)
|
||
# ルールからプロトコルを検出 (明示的に -u 指定がなければルール自体から判定)
|
||
local rule_proto=$PROTO
|
||
echo "$info" | grep -q "udp" && rule_proto="udp"
|
||
printf "${MSGS[testing]}" "$h" "$lp" "$tip" "$tp" "$short_id"
|
||
local count_before=$(nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{sum+=$2} END {print sum+0}')
|
||
test_connection "$tip" "$tp" "$rule_proto"
|
||
local nc_opt="-z"; [[ "$rule_proto" == "udp" ]] && nc_opt="-zu"
|
||
nc $nc_opt -w 1 127.0.0.1 "$lp" >/dev/null 2>&1; sleep 0.1
|
||
local count_after=$(nft list table inet "${TABLE_NAME}" 2>/dev/null | grep "$uuid" | grep -o 'packets [0-9]*' | awk '{sum+=$2} END {print sum+0}')
|
||
if (( count_after > count_before )); then printf "${MSGS[passed]}\n" "$((count_after - count_before))"
|
||
else printf "${MSGS[skipped]}\n"; fi
|
||
}
|
||
|
||
list_rules() {
|
||
[[ "$QUIET_MODE" == true ]] && return
|
||
local highlight_uuid=$1; init_nft
|
||
msg "$(printf "${MSGS[rules_header]}" "${TABLE_NAME}")"
|
||
printf "%-10s %-6s %-20s %-20s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID"
|
||
echo "-----------------------------------------------------------------------------------"
|
||
nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dnat" | while read -r line; do
|
||
local handle=$(echo "$line" | grep -o 'handle [0-9]*' | awk '{print $2}')
|
||
local proto=$(echo "$line" | grep -q "udp" && echo "udp" || echo "tcp")
|
||
local target=$(echo "$line" | grep -oE '([0-9.]+|\[[0-9a-fA-F:]+\]):[0-9]+' | head -n 1)
|
||
local lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}')
|
||
local full_uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2)
|
||
if [[ -n "$highlight_uuid" && "$full_uuid" == "$highlight_uuid" ]]; then
|
||
printf "\e[1;36m*%-9s %-6s %-20s %-20s %-10s\e[0m\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}"
|
||
else printf " %-9s %-6s %-20s %-20s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${full_uuid:0:8}"; fi
|
||
done
|
||
}
|
||
|
||
add_rule() {
|
||
local raw=$1; init_nft
|
||
[[ "$FORCE_FLAG" == true ]] && enable_forwarding
|
||
|
||
if [[ "$raw" =~ ^([0-9]+):([0-9\.]+):([0-9]+)$ ]]; then
|
||
lp=${BASH_REMATCH[1]}; tip=${BASH_REMATCH[2]}; tp=${BASH_REMATCH[3]}
|
||
elif [[ "$raw" =~ ^([0-9]+):([0-9]+)$ ]]; then
|
||
lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[2]}
|
||
elif [[ "$raw" =~ ^([0-9]+)$ ]]; then
|
||
lp=${BASH_REMATCH[1]}; tip="127.0.0.1"; tp=${BASH_REMATCH[1]}
|
||
else err "Invalid format: $raw"; return 1; fi
|
||
|
||
local conflicts=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport $lp" | grep -o 'handle [0-9]*' | awk '{print $2}')
|
||
for ch in $conflicts; do msg "$(printf "${MSGS[overwrite]}" "$lp" "$ch")"; delete_by_handle "$ch"; done
|
||
|
||
local u_raw=$(cat /proc/sys/kernel/random/uuid); local u="ipf-id:$u_raw"; local fam="ip"; [[ "$tip" =~ : ]] && fam="ip6"
|
||
|
||
nft insert rule inet "${TABLE_NAME}" prerouting "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\""
|
||
nft insert rule inet "${TABLE_NAME}" output "$PROTO" dport "$lp" counter dnat $fam to "$tip:$tp" comment "\"$u\""
|
||
nft insert rule inet "${TABLE_NAME}" forward $fam daddr "$tip" "$PROTO" dport "$tp" ct state new,established,related accept comment "\"$u\""
|
||
nft insert rule inet "${TABLE_NAME}" forward $fam saddr "$tip" "$PROTO" sport "$tp" ct state established,related accept comment "\"$u\""
|
||
nft insert rule inet "${TABLE_NAME}" forward iifname "lo" accept comment "\"$u\""
|
||
|
||
# ポストルートイング:SNAT モードか MASQUERADE モードかで分岐
|
||
if [[ "$SNAT_FLAG" == true ]]; then
|
||
msg "$(printf "${MSGS[nat_mode]}")"
|
||
nft insert rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" \
|
||
counter snat to type nat orig comment "\"$u\""
|
||
else
|
||
msg "$(printf "${MSGS[masq_mode]}")"
|
||
nft insert rule inet "${TABLE_NAME}" postrouting $fam daddr "$tip" "$PROTO" dport "$tp" \
|
||
masquerade comment "\"$u\""
|
||
fi
|
||
|
||
list_rules "$u_raw"
|
||
[[ "$SKIP_TEST" == false ]] && { local nh=$(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "$u_raw" | grep -o 'handle [0-9]*' | awk '{print $2}'); echo ""; test_strict_handle "$nh"; }
|
||
}
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# 5. Main Loop
|
||
# -----------------------------------------------------------------------------
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
-q*) QUIET_MODE=true; FORCE_FLAG=true; SKIP_TEST=true ;;
|
||
-f*|-y*) FORCE_FLAG=true; SKIP_TEST=true ;;
|
||
-F*) SNAT_FLAG=true; SKIP_TEST=true ;;
|
||
-u) PROTO="udp" ;;
|
||
-R) RESET_MODE=true ;;
|
||
esac
|
||
done
|
||
|
||
if [[ "$RESET_MODE" == true ]]; then
|
||
if [[ "$FORCE_FLAG" == false ]]; then
|
||
msg "$(printf "${MSGS[reset_warn]}" "${TABLE_NAME}")"
|
||
read -p "${MSGS[confirm]}" confirm
|
||
[[ ! "$confirm" =~ ^[yY]$ ]] && exit 0
|
||
fi
|
||
nft delete table inet "${TABLE_NAME}" 2>/dev/null; init_nft; msg "$(printf "${MSGS[reset_done]}" "${TABLE_NAME}")"; list_rules; exit 0
|
||
fi
|
||
|
||
[[ $# -eq 0 ]] && { list_rules; exit 0; }
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
-h|--help) show_help; exit 0 ;;
|
||
-v|--version) echo "ipf version ${VERSION}"; exit 0 ;;
|
||
-l|-L) list_rules; exit 0 ;;
|
||
-u) shift; [[ $# -eq 0 ]] && exit 0; continue ;;
|
||
-f|-y|-q) [[ "$1" == "-f" ]] && enable_forwarding; shift; [[ $# -eq 0 ]] && exit 0; continue ;;
|
||
-F*) enable_forwarding; shift; [[ $# -eq 0 ]] && exit 0; continue ;;
|
||
-*[d]*)
|
||
target="${1#-d}"; [[ -z "$target" ]] && { target="$2"; shift; }
|
||
[[ "$target" == "all" ]] && { nft delete table inet "${TABLE_NAME}" 2>/dev/null; init_nft; list_rules; exit 0; }
|
||
IFS=',' read -r -a parts <<< "$target"
|
||
for p in "${parts[@]}"; do
|
||
if [[ "$p" =~ ^:([0-9]+)$ ]]; then
|
||
for h in $(nft -a list chain inet "${TABLE_NAME}" prerouting 2>/dev/null | grep "dport ${BASH_REMATCH[1]}" | grep -o 'handle [0-9]*' | awk '{print $2}'); do delete_by_handle "$h"; done
|
||
else delete_by_handle "$p"; fi
|
||
done
|
||
list_rules; exit 0 ;;
|
||
-t*)
|
||
target="${1#-t}"; [[ -z "$target" ]] && { target="$2"; shift; }
|
||
if [[ "$target" =~ ^([0-9\.]+):([0-9]+)$ ]]; then
|
||
echo -n "Target $target [$PROTO]... "; test_connection "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "$PROTO"
|
||
elif [[ "$target" =~ ^:([0-9]+)$ ]]; then
|
||
echo -n "Local $target [$PROTO]... "; test_connection "127.0.0.1" "${BASH_REMATCH[1]}" "$PROTO"
|
||
else test_strict_handle "$target"; fi
|
||
exit 0 ;;
|
||
[0-9]*) add_rule "$1"; exit 0 ;;
|
||
*) err "$(printf "${MSGS[unknown_opt]}" "$1")"; exit 1 ;;
|
||
esac
|
||
shift
|
||
done |