[{"data":1,"prerenderedAt":4202},["ShallowReactive",2],{"page-\u002Fbash\u002F11-command-line-arguments":3},{"id":4,"title":5,"body":6,"description":4195,"extension":4196,"meta":4197,"navigation":156,"path":4198,"seo":4199,"stem":4200,"__hash__":4201},"content\u002Fbash\u002F11-command-line-arguments.md","Bash 11 — Argument Parsing Internals: getopts, Subcommands & CLI Design",{"type":7,"value":8,"toc":4178},"minimark",[9,14,19,372,379,795,801,988,997,1218,1222,1808,1812,2967,2971,3353,3361,3438,3442,3814,3818,3964,3968,3975,4049,4174],[10,11,13],"h1",{"id":12},"_11-argument-parsing-internals-getopts-subcommands-cli-design","11 — Argument Parsing Internals: getopts, Subcommands & CLI Design",[15,16,18],"h2",{"id":17},"positional-arguments-internal-mechanics","Positional Arguments: Internal Mechanics",[20,21,23],"code-wrapper",{"language":22},"bash",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-bash shiki shiki-themes github-light github-dark","# ── Positional parameters: $1, $2, ..., $9, ${10}, ${10}+ ──\necho \"$0\"          # script name (or the shell if sourced — use BASH_SOURCE[0] instead)\necho \"$1\"          # first arg\necho \"$2\"          # second arg\necho \"${10}\"       # 10th arg — MUST use braces! ($10 is $1 followed by literal \"0\")\necho \"$@\"          # all args, each separate (USE QUOTED: \"$@\")\necho \"$*\"          # all args joined by IFS into one string\necho \"$#\"          # count of args\n\n# ── shift: remove args from the front ──\nset -- a b c d e   # set positional args (for demo)\necho \"$@\"          # a b c d e\nshift              # remove $1 (a), shift rest left\necho \"$@\"          # b c d e\nshift 2            # remove $1 and $2 (b, c)\necho \"$@\"          # d e\n\n# ── Iterating all args ──\nfor arg in \"$@\"; do    # ALWAYS quote \"$@\" — preserves args with spaces\n    echo \"arg: $arg\"\ndone\n\n# ── Passing args to a function ──\nprocess() {\n    echo \"function sees $# args: $@\"\n}\nprocess \"$@\"   # pass the script's args to the function (quoted — safe)\n","",[30,31,32,41,61,76,91,106,121,136,151,158,164,191,205,214,228,239,253,258,264,293,308,314,319,325,335,352,358],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","# ── Positional parameters: $1, $2, ..., $9, ${10}, ${10}+ ──\n",[33,42,44,48,52,55,58],{"class":35,"line":43},2,[33,45,47],{"class":46},"snvgF","echo",[33,49,51],{"class":50},"sJ6F3"," \"",[33,53,54],{"class":46},"$0",[33,56,57],{"class":50},"\"",[33,59,60],{"class":39},"          # script name (or the shell if sourced — use BASH_SOURCE[0] instead)\n",[33,62,64,66,68,71,73],{"class":35,"line":63},3,[33,65,47],{"class":46},[33,67,51],{"class":50},[33,69,70],{"class":46},"$1",[33,72,57],{"class":50},[33,74,75],{"class":39},"          # first arg\n",[33,77,79,81,83,86,88],{"class":35,"line":78},4,[33,80,47],{"class":46},[33,82,51],{"class":50},[33,84,85],{"class":46},"$2",[33,87,57],{"class":50},[33,89,90],{"class":39},"          # second arg\n",[33,92,94,96,98,101,103],{"class":35,"line":93},5,[33,95,47],{"class":46},[33,97,51],{"class":50},[33,99,100],{"class":46},"${10}",[33,102,57],{"class":50},[33,104,105],{"class":39},"       # 10th arg — MUST use braces! ($10 is $1 followed by literal \"0\")\n",[33,107,109,111,113,116,118],{"class":35,"line":108},6,[33,110,47],{"class":46},[33,112,51],{"class":50},[33,114,115],{"class":46},"$@",[33,117,57],{"class":50},[33,119,120],{"class":39},"          # all args, each separate (USE QUOTED: \"$@\")\n",[33,122,124,126,128,131,133],{"class":35,"line":123},7,[33,125,47],{"class":46},[33,127,51],{"class":50},[33,129,130],{"class":46},"$*",[33,132,57],{"class":50},[33,134,135],{"class":39},"          # all args joined by IFS into one string\n",[33,137,139,141,143,146,148],{"class":35,"line":138},8,[33,140,47],{"class":46},[33,142,51],{"class":50},[33,144,145],{"class":46},"$#",[33,147,57],{"class":50},[33,149,150],{"class":39},"          # count of args\n",[33,152,154],{"class":35,"line":153},9,[33,155,157],{"emptyLinePlaceholder":156},true,"\n",[33,159,161],{"class":35,"line":160},10,[33,162,163],{"class":39},"# ── shift: remove args from the front ──\n",[33,165,167,170,173,176,179,182,185,188],{"class":35,"line":166},11,[33,168,169],{"class":46},"set",[33,171,172],{"class":46}," --",[33,174,175],{"class":50}," a",[33,177,178],{"class":50}," b",[33,180,181],{"class":50}," c",[33,183,184],{"class":50}," d",[33,186,187],{"class":50}," e",[33,189,190],{"class":39},"   # set positional args (for demo)\n",[33,192,194,196,198,200,202],{"class":35,"line":193},12,[33,195,47],{"class":46},[33,197,51],{"class":50},[33,199,115],{"class":46},[33,201,57],{"class":50},[33,203,204],{"class":39},"          # a b c d e\n",[33,206,208,211],{"class":35,"line":207},13,[33,209,210],{"class":46},"shift",[33,212,213],{"class":39},"              # remove $1 (a), shift rest left\n",[33,215,217,219,221,223,225],{"class":35,"line":216},14,[33,218,47],{"class":46},[33,220,51],{"class":50},[33,222,115],{"class":46},[33,224,57],{"class":50},[33,226,227],{"class":39},"          # b c d e\n",[33,229,231,233,236],{"class":35,"line":230},15,[33,232,210],{"class":46},[33,234,235],{"class":46}," 2",[33,237,238],{"class":39},"            # remove $1 and $2 (b, c)\n",[33,240,242,244,246,248,250],{"class":35,"line":241},16,[33,243,47],{"class":46},[33,245,51],{"class":50},[33,247,115],{"class":46},[33,249,57],{"class":50},[33,251,252],{"class":39},"          # d e\n",[33,254,256],{"class":35,"line":255},17,[33,257,157],{"emptyLinePlaceholder":156},[33,259,261],{"class":35,"line":260},18,[33,262,263],{"class":39},"# ── Iterating all args ──\n",[33,265,267,271,275,278,280,282,284,287,290],{"class":35,"line":266},19,[33,268,270],{"class":269},"svdQ7","for",[33,272,274],{"class":273},"ssxIu"," arg ",[33,276,277],{"class":269},"in",[33,279,51],{"class":50},[33,281,115],{"class":46},[33,283,57],{"class":50},[33,285,286],{"class":273},"; ",[33,288,289],{"class":269},"do",[33,291,292],{"class":39},"    # ALWAYS quote \"$@\" — preserves args with spaces\n",[33,294,296,299,302,305],{"class":35,"line":295},20,[33,297,298],{"class":46},"    echo",[33,300,301],{"class":50}," \"arg: ",[33,303,304],{"class":273},"$arg",[33,306,307],{"class":50},"\"\n",[33,309,311],{"class":35,"line":310},21,[33,312,313],{"class":269},"done\n",[33,315,317],{"class":35,"line":316},22,[33,318,157],{"emptyLinePlaceholder":156},[33,320,322],{"class":35,"line":321},23,[33,323,324],{"class":39},"# ── Passing args to a function ──\n",[33,326,328,332],{"class":35,"line":327},24,[33,329,331],{"class":330},"sIsaT","process",[33,333,334],{"class":273},"() {\n",[33,336,338,340,343,345,348,350],{"class":35,"line":337},25,[33,339,298],{"class":46},[33,341,342],{"class":50}," \"function sees ",[33,344,145],{"class":46},[33,346,347],{"class":50}," args: ",[33,349,115],{"class":46},[33,351,307],{"class":50},[33,353,355],{"class":35,"line":354},26,[33,356,357],{"class":273},"}\n",[33,359,361,363,365,367,369],{"class":35,"line":360},27,[33,362,331],{"class":330},[33,364,51],{"class":50},[33,366,115],{"class":46},[33,368,57],{"class":50},[33,370,371],{"class":39},"   # pass the script's args to the function (quoted — safe)\n",[15,373,375,378],{"id":374},"getopts-the-built-in-option-parser",[30,376,377],{},"getopts",": The Built-In Option Parser",[20,380,381],{"language":22},[24,382,384],{"className":26,"code":383,"language":22,"meta":28,"style":28},"#!\u002Fusr\u002Fbin\u002Fenv bash\n# ── getopts parses short options: -v, -o VALUE, -h ──\n# It's a built-in (no subprocess), POSIX, and reliable (unlike external `getopt`).\n\nverbose=false\noutput=\"\"\ncount=0\n\n# ── Syntax: getopts \"OPTIONS\" var ──\n# Options string: each letter is an option. A letter followed by `:` takes an argument.\n# Leading `:` enables silent error reporting (you handle errors with ? and :).\nwhile getopts \":hvo:c:\" opt; do\n    # :hvo:c: →\n    #   h    → flag (no argument)\n    #   v    → flag (no argument)\n    #   o:   → option with argument (OPTARG)\n    #   c:   → option with argument\n    # Leading : → silent mode (don't print error, set opt to ? or :)\n\n    case \"$opt\" in\n        h)\n            echo \"Usage: $0 [-h] [-v] [-o FILE] [-c N]\"; exit 0\n            ;;\n        v) verbose=true ;;\n        o) output=\"$OPTARG\" ;;      # OPTARG: the argument value for -o\n        c) count=\"$OPTARG\" ;;\n        \\?)                          # ? — invalid option\n            echo \"Invalid option: -$OPTARG\" >&2\n            exit 1\n            ;;\n        :)                           # : — missing argument\n            echo \"Option -$OPTARG requires an argument.\" >&2\n            exit 1\n            ;;\n    esac\ndone\nshift $((OPTIND - 1))   # OPTIND: index of next arg. Shift to remove parsed options.\n\n# After shift, \"$@\" contains only positional args (not options)\necho \"verbose: $verbose\"\necho \"output: $output\"\necho \"count: $count\"\necho \"positional: $@\"\n",[30,385,386,391,396,401,405,416,426,436,440,445,450,455,474,479,484,489,494,499,504,508,523,532,553,558,577,602,622,632,647,656,661,672,687,694,699,705,710,733,738,744,757,770,783],{"__ignoreMap":28},[33,387,388],{"class":35,"line":36},[33,389,390],{"class":39},"#!\u002Fusr\u002Fbin\u002Fenv bash\n",[33,392,393],{"class":35,"line":43},[33,394,395],{"class":39},"# ── getopts parses short options: -v, -o VALUE, -h ──\n",[33,397,398],{"class":35,"line":63},[33,399,400],{"class":39},"# It's a built-in (no subprocess), POSIX, and reliable (unlike external `getopt`).\n",[33,402,403],{"class":35,"line":78},[33,404,157],{"emptyLinePlaceholder":156},[33,406,407,410,413],{"class":35,"line":93},[33,408,409],{"class":273},"verbose",[33,411,412],{"class":269},"=",[33,414,415],{"class":50},"false\n",[33,417,418,421,423],{"class":35,"line":108},[33,419,420],{"class":273},"output",[33,422,412],{"class":269},[33,424,425],{"class":50},"\"\"\n",[33,427,428,431,433],{"class":35,"line":123},[33,429,430],{"class":273},"count",[33,432,412],{"class":269},[33,434,435],{"class":50},"0\n",[33,437,438],{"class":35,"line":138},[33,439,157],{"emptyLinePlaceholder":156},[33,441,442],{"class":35,"line":153},[33,443,444],{"class":39},"# ── Syntax: getopts \"OPTIONS\" var ──\n",[33,446,447],{"class":35,"line":160},[33,448,449],{"class":39},"# Options string: each letter is an option. A letter followed by `:` takes an argument.\n",[33,451,452],{"class":35,"line":166},[33,453,454],{"class":39},"# Leading `:` enables silent error reporting (you handle errors with ? and :).\n",[33,456,457,460,463,466,469,471],{"class":35,"line":193},[33,458,459],{"class":269},"while",[33,461,462],{"class":46}," getopts",[33,464,465],{"class":50}," \":hvo:c:\"",[33,467,468],{"class":50}," opt",[33,470,286],{"class":273},[33,472,473],{"class":269},"do\n",[33,475,476],{"class":35,"line":207},[33,477,478],{"class":39},"    # :hvo:c: →\n",[33,480,481],{"class":35,"line":216},[33,482,483],{"class":39},"    #   h    → flag (no argument)\n",[33,485,486],{"class":35,"line":230},[33,487,488],{"class":39},"    #   v    → flag (no argument)\n",[33,490,491],{"class":35,"line":241},[33,492,493],{"class":39},"    #   o:   → option with argument (OPTARG)\n",[33,495,496],{"class":35,"line":255},[33,497,498],{"class":39},"    #   c:   → option with argument\n",[33,500,501],{"class":35,"line":260},[33,502,503],{"class":39},"    # Leading : → silent mode (don't print error, set opt to ? or :)\n",[33,505,506],{"class":35,"line":266},[33,507,157],{"emptyLinePlaceholder":156},[33,509,510,513,515,518,520],{"class":35,"line":295},[33,511,512],{"class":269},"    case",[33,514,51],{"class":50},[33,516,517],{"class":273},"$opt",[33,519,57],{"class":50},[33,521,522],{"class":269}," in\n",[33,524,525,529],{"class":35,"line":310},[33,526,528],{"class":527},"svAP2","        h",[33,530,531],{"class":269},")\n",[33,533,534,537,540,542,545,547,550],{"class":35,"line":316},[33,535,536],{"class":46},"            echo",[33,538,539],{"class":50}," \"Usage: ",[33,541,54],{"class":46},[33,543,544],{"class":50}," [-h] [-v] [-o FILE] [-c N]\"",[33,546,286],{"class":273},[33,548,549],{"class":46},"exit",[33,551,552],{"class":46}," 0\n",[33,554,555],{"class":35,"line":321},[33,556,557],{"class":273},"            ;;\n",[33,559,560,563,566,569,571,574],{"class":35,"line":327},[33,561,562],{"class":527},"        v",[33,564,565],{"class":269},")",[33,567,568],{"class":273}," verbose",[33,570,412],{"class":269},[33,572,573],{"class":50},"true",[33,575,576],{"class":273}," ;;\n",[33,578,579,582,584,587,589,591,594,596,599],{"class":35,"line":337},[33,580,581],{"class":527},"        o",[33,583,565],{"class":269},[33,585,586],{"class":273}," output",[33,588,412],{"class":269},[33,590,57],{"class":50},[33,592,593],{"class":273},"$OPTARG",[33,595,57],{"class":50},[33,597,598],{"class":273}," ;;      ",[33,600,601],{"class":39},"# OPTARG: the argument value for -o\n",[33,603,604,607,609,612,614,616,618,620],{"class":35,"line":354},[33,605,606],{"class":527},"        c",[33,608,565],{"class":269},[33,610,611],{"class":273}," count",[33,613,412],{"class":269},[33,615,57],{"class":50},[33,617,593],{"class":273},[33,619,57],{"class":50},[33,621,576],{"class":273},[33,623,624,627,629],{"class":35,"line":360},[33,625,626],{"class":46},"        \\?",[33,628,565],{"class":269},[33,630,631],{"class":39},"                          # ? — invalid option\n",[33,633,635,637,640,642,644],{"class":35,"line":634},28,[33,636,536],{"class":527},[33,638,639],{"class":50}," \"Invalid option: -",[33,641,593],{"class":273},[33,643,57],{"class":50},[33,645,646],{"class":527}," >&2\n",[33,648,650,653],{"class":35,"line":649},29,[33,651,652],{"class":527},"            exit",[33,654,655],{"class":527}," 1\n",[33,657,659],{"class":35,"line":658},30,[33,660,557],{"class":527},[33,662,664,667,669],{"class":35,"line":663},31,[33,665,666],{"class":527},"        :",[33,668,565],{"class":269},[33,670,671],{"class":39},"                           # : — missing argument\n",[33,673,675,677,680,682,685],{"class":35,"line":674},32,[33,676,536],{"class":527},[33,678,679],{"class":50}," \"Option -",[33,681,593],{"class":273},[33,683,684],{"class":50}," requires an argument.\"",[33,686,646],{"class":527},[33,688,690,692],{"class":35,"line":689},33,[33,691,652],{"class":527},[33,693,655],{"class":527},[33,695,697],{"class":35,"line":696},34,[33,698,557],{"class":527},[33,700,702],{"class":35,"line":701},35,[33,703,704],{"class":269},"    esac\n",[33,706,708],{"class":35,"line":707},36,[33,709,313],{"class":269},[33,711,713,715,718,721,724,727,730],{"class":35,"line":712},37,[33,714,210],{"class":46},[33,716,717],{"class":273}," $((",[33,719,720],{"class":330},"OPTIND",[33,722,723],{"class":50}," -",[33,725,726],{"class":46}," 1",[33,728,729],{"class":273},"))   ",[33,731,732],{"class":39},"# OPTIND: index of next arg. Shift to remove parsed options.\n",[33,734,736],{"class":35,"line":735},38,[33,737,157],{"emptyLinePlaceholder":156},[33,739,741],{"class":35,"line":740},39,[33,742,743],{"class":39},"# After shift, \"$@\" contains only positional args (not options)\n",[33,745,747,749,752,755],{"class":35,"line":746},40,[33,748,47],{"class":46},[33,750,751],{"class":50}," \"verbose: ",[33,753,754],{"class":273},"$verbose",[33,756,307],{"class":50},[33,758,760,762,765,768],{"class":35,"line":759},41,[33,761,47],{"class":46},[33,763,764],{"class":50}," \"output: ",[33,766,767],{"class":273},"$output",[33,769,307],{"class":50},[33,771,773,775,778,781],{"class":35,"line":772},42,[33,774,47],{"class":46},[33,776,777],{"class":50}," \"count: ",[33,779,780],{"class":273},"$count",[33,782,307],{"class":50},[33,784,786,788,791,793],{"class":35,"line":785},43,[33,787,47],{"class":46},[33,789,790],{"class":50}," \"positional: ",[33,792,115],{"class":46},[33,794,307],{"class":50},[15,796,798,800],{"id":797},"getopts-internals-state-machine",[30,799,377],{}," Internals: State Machine",[20,802,803],{"language":22},[24,804,806],{"className":26,"code":805,"language":22,"meta":28,"style":28},"# ── How getopts works internally ──\n# getopts maintains two global variables:\n# OPTIND — index of the next arg to process (starts at 1)\n# OPTARG — the argument value for options that take one (-o value → OPTARG=\"value\")\n\n# ── Each call to getopts processes ONE option ──\n# It looks at $OPTIND in \"$@\", finds the next -flag, and:\n#   - Sets $opt to the flag letter\n#   - If the flag takes an arg (followed by :), sets $OPTARG to the value\n#   - Increments $OPTIND\n# Returns 0 (success) if an option was found, 1 (failure) at end of options.\n\n# ── -o value vs -ovalue (both work) ──\n.\u002Fscript.sh -o output.txt    # OPTARG=\"output.txt\"\n.\u002Fscript.sh -ooutput.txt     # OPTARG=\"output.txt\" (attached — also works)\n\n# ── Combined short flags ──\n.\u002Fscript.sh -vo output.txt   # -v and -o output.txt (combined: -v is a flag, -o takes arg)\n# getopts processes: v (flag), then o (with arg \"output.txt\")\n\n# ── -- ends option parsing ──\n.\u002Fscript.sh -v -- -o output.txt\n# -v is parsed as a flag. -- stops option parsing.\n# After shift: \"$@\" = \"-o output.txt\" (the -o is a POSITIONAL arg, not an option!)\n\n# ── Options after positional args STOP parsing ──\n.\u002Fscript.sh file.txt -v\n# getopts stops at \"file.txt\" (first non-option). -v is NOT parsed — it's positional!\n# This is a getopts limitation: options must come BEFORE positional args.\n# Fix: use manual parsing (below) for intermixed args, or document the order.\n",[30,807,808,813,818,823,828,832,837,842,847,852,857,862,866,871,885,895,899,904,916,921,925,930,944,949,954,958,963,973,978,983],{"__ignoreMap":28},[33,809,810],{"class":35,"line":36},[33,811,812],{"class":39},"# ── How getopts works internally ──\n",[33,814,815],{"class":35,"line":43},[33,816,817],{"class":39},"# getopts maintains two global variables:\n",[33,819,820],{"class":35,"line":63},[33,821,822],{"class":39},"# OPTIND — index of the next arg to process (starts at 1)\n",[33,824,825],{"class":35,"line":78},[33,826,827],{"class":39},"# OPTARG — the argument value for options that take one (-o value → OPTARG=\"value\")\n",[33,829,830],{"class":35,"line":93},[33,831,157],{"emptyLinePlaceholder":156},[33,833,834],{"class":35,"line":108},[33,835,836],{"class":39},"# ── Each call to getopts processes ONE option ──\n",[33,838,839],{"class":35,"line":123},[33,840,841],{"class":39},"# It looks at $OPTIND in \"$@\", finds the next -flag, and:\n",[33,843,844],{"class":35,"line":138},[33,845,846],{"class":39},"#   - Sets $opt to the flag letter\n",[33,848,849],{"class":35,"line":153},[33,850,851],{"class":39},"#   - If the flag takes an arg (followed by :), sets $OPTARG to the value\n",[33,853,854],{"class":35,"line":160},[33,855,856],{"class":39},"#   - Increments $OPTIND\n",[33,858,859],{"class":35,"line":166},[33,860,861],{"class":39},"# Returns 0 (success) if an option was found, 1 (failure) at end of options.\n",[33,863,864],{"class":35,"line":193},[33,865,157],{"emptyLinePlaceholder":156},[33,867,868],{"class":35,"line":207},[33,869,870],{"class":39},"# ── -o value vs -ovalue (both work) ──\n",[33,872,873,876,879,882],{"class":35,"line":216},[33,874,875],{"class":330},".\u002Fscript.sh",[33,877,878],{"class":46}," -o",[33,880,881],{"class":50}," output.txt",[33,883,884],{"class":39},"    # OPTARG=\"output.txt\"\n",[33,886,887,889,892],{"class":35,"line":230},[33,888,875],{"class":330},[33,890,891],{"class":46}," -ooutput.txt",[33,893,894],{"class":39},"     # OPTARG=\"output.txt\" (attached — also works)\n",[33,896,897],{"class":35,"line":241},[33,898,157],{"emptyLinePlaceholder":156},[33,900,901],{"class":35,"line":255},[33,902,903],{"class":39},"# ── Combined short flags ──\n",[33,905,906,908,911,913],{"class":35,"line":260},[33,907,875],{"class":330},[33,909,910],{"class":46}," -vo",[33,912,881],{"class":50},[33,914,915],{"class":39},"   # -v and -o output.txt (combined: -v is a flag, -o takes arg)\n",[33,917,918],{"class":35,"line":266},[33,919,920],{"class":39},"# getopts processes: v (flag), then o (with arg \"output.txt\")\n",[33,922,923],{"class":35,"line":295},[33,924,157],{"emptyLinePlaceholder":156},[33,926,927],{"class":35,"line":310},[33,928,929],{"class":39},"# ── -- ends option parsing ──\n",[33,931,932,934,937,939,941],{"class":35,"line":316},[33,933,875],{"class":330},[33,935,936],{"class":46}," -v",[33,938,172],{"class":46},[33,940,878],{"class":46},[33,942,943],{"class":50}," output.txt\n",[33,945,946],{"class":35,"line":321},[33,947,948],{"class":39},"# -v is parsed as a flag. -- stops option parsing.\n",[33,950,951],{"class":35,"line":327},[33,952,953],{"class":39},"# After shift: \"$@\" = \"-o output.txt\" (the -o is a POSITIONAL arg, not an option!)\n",[33,955,956],{"class":35,"line":337},[33,957,157],{"emptyLinePlaceholder":156},[33,959,960],{"class":35,"line":354},[33,961,962],{"class":39},"# ── Options after positional args STOP parsing ──\n",[33,964,965,967,970],{"class":35,"line":360},[33,966,875],{"class":330},[33,968,969],{"class":50}," file.txt",[33,971,972],{"class":46}," -v\n",[33,974,975],{"class":35,"line":634},[33,976,977],{"class":39},"# getopts stops at \"file.txt\" (first non-option). -v is NOT parsed — it's positional!\n",[33,979,980],{"class":35,"line":649},[33,981,982],{"class":39},"# This is a getopts limitation: options must come BEFORE positional args.\n",[33,984,985],{"class":35,"line":658},[33,986,987],{"class":39},"# Fix: use manual parsing (below) for intermixed args, or document the order.\n",[15,989,991,992,994,995],{"id":990},"anti-pattern-forgetting-shift-after-getopts","Anti-Pattern: Forgetting ",[30,993,210],{}," After ",[30,996,377],{},[20,998,999],{"language":22},[24,1000,1002],{"className":26,"code":1001,"language":22,"meta":28,"style":28},"# ❌ NAIVE — no shift, positional args include the options\nwhile getopts \"vo:\" opt; do\n    case \"$opt\" in\n        v) verbose=true ;;\n        o) output=\"$OPTARG\" ;;\n    esac\ndone\necho \"positional: $@\"    # -v -o output.txt file1 file2 — still includes options!\n\n# ✅ CORRECT — shift after the loop\nwhile getopts \"vo:\" opt; do\n    case \"$opt\" in\n        v) verbose=true ;;\n        o) output=\"$OPTARG\" ;;\n    esac\ndone\nshift $((OPTIND - 1))   # remove parsed options, leave positional args\necho \"positional: $@\"    # file1 file2 — options stripped\n\n# ── Why OPTIND - 1 ──\n# OPTIND is the index of the NEXT arg to process (after getopts stops).\n# If 3 options were parsed (-v -o out.txt), OPTIND=4.\n# shift $((4-1)) = shift 3 → removes -v, -o, out.txt, leaving positional args.\n",[30,1003,1004,1009,1024,1036,1050,1068,1072,1076,1089,1093,1098,1112,1124,1138,1156,1160,1164,1181,1194,1198,1203,1208,1213],{"__ignoreMap":28},[33,1005,1006],{"class":35,"line":36},[33,1007,1008],{"class":39},"# ❌ NAIVE — no shift, positional args include the options\n",[33,1010,1011,1013,1015,1018,1020,1022],{"class":35,"line":43},[33,1012,459],{"class":269},[33,1014,462],{"class":46},[33,1016,1017],{"class":50}," \"vo:\"",[33,1019,468],{"class":50},[33,1021,286],{"class":273},[33,1023,473],{"class":269},[33,1025,1026,1028,1030,1032,1034],{"class":35,"line":63},[33,1027,512],{"class":269},[33,1029,51],{"class":50},[33,1031,517],{"class":273},[33,1033,57],{"class":50},[33,1035,522],{"class":269},[33,1037,1038,1040,1042,1044,1046,1048],{"class":35,"line":78},[33,1039,562],{"class":527},[33,1041,565],{"class":269},[33,1043,568],{"class":273},[33,1045,412],{"class":269},[33,1047,573],{"class":50},[33,1049,576],{"class":273},[33,1051,1052,1054,1056,1058,1060,1062,1064,1066],{"class":35,"line":93},[33,1053,581],{"class":527},[33,1055,565],{"class":269},[33,1057,586],{"class":273},[33,1059,412],{"class":269},[33,1061,57],{"class":50},[33,1063,593],{"class":273},[33,1065,57],{"class":50},[33,1067,576],{"class":273},[33,1069,1070],{"class":35,"line":108},[33,1071,704],{"class":269},[33,1073,1074],{"class":35,"line":123},[33,1075,313],{"class":269},[33,1077,1078,1080,1082,1084,1086],{"class":35,"line":138},[33,1079,47],{"class":46},[33,1081,790],{"class":50},[33,1083,115],{"class":46},[33,1085,57],{"class":50},[33,1087,1088],{"class":39},"    # -v -o output.txt file1 file2 — still includes options!\n",[33,1090,1091],{"class":35,"line":153},[33,1092,157],{"emptyLinePlaceholder":156},[33,1094,1095],{"class":35,"line":160},[33,1096,1097],{"class":39},"# ✅ CORRECT — shift after the loop\n",[33,1099,1100,1102,1104,1106,1108,1110],{"class":35,"line":166},[33,1101,459],{"class":269},[33,1103,462],{"class":46},[33,1105,1017],{"class":50},[33,1107,468],{"class":50},[33,1109,286],{"class":273},[33,1111,473],{"class":269},[33,1113,1114,1116,1118,1120,1122],{"class":35,"line":193},[33,1115,512],{"class":269},[33,1117,51],{"class":50},[33,1119,517],{"class":273},[33,1121,57],{"class":50},[33,1123,522],{"class":269},[33,1125,1126,1128,1130,1132,1134,1136],{"class":35,"line":207},[33,1127,562],{"class":527},[33,1129,565],{"class":269},[33,1131,568],{"class":273},[33,1133,412],{"class":269},[33,1135,573],{"class":50},[33,1137,576],{"class":273},[33,1139,1140,1142,1144,1146,1148,1150,1152,1154],{"class":35,"line":216},[33,1141,581],{"class":527},[33,1143,565],{"class":269},[33,1145,586],{"class":273},[33,1147,412],{"class":269},[33,1149,57],{"class":50},[33,1151,593],{"class":273},[33,1153,57],{"class":50},[33,1155,576],{"class":273},[33,1157,1158],{"class":35,"line":230},[33,1159,704],{"class":269},[33,1161,1162],{"class":35,"line":241},[33,1163,313],{"class":269},[33,1165,1166,1168,1170,1172,1174,1176,1178],{"class":35,"line":255},[33,1167,210],{"class":46},[33,1169,717],{"class":273},[33,1171,720],{"class":330},[33,1173,723],{"class":50},[33,1175,726],{"class":46},[33,1177,729],{"class":273},[33,1179,1180],{"class":39},"# remove parsed options, leave positional args\n",[33,1182,1183,1185,1187,1189,1191],{"class":35,"line":260},[33,1184,47],{"class":46},[33,1186,790],{"class":50},[33,1188,115],{"class":46},[33,1190,57],{"class":50},[33,1192,1193],{"class":39},"    # file1 file2 — options stripped\n",[33,1195,1196],{"class":35,"line":266},[33,1197,157],{"emptyLinePlaceholder":156},[33,1199,1200],{"class":35,"line":295},[33,1201,1202],{"class":39},"# ── Why OPTIND - 1 ──\n",[33,1204,1205],{"class":35,"line":310},[33,1206,1207],{"class":39},"# OPTIND is the index of the NEXT arg to process (after getopts stops).\n",[33,1209,1210],{"class":35,"line":316},[33,1211,1212],{"class":39},"# If 3 options were parsed (-v -o out.txt), OPTIND=4.\n",[33,1214,1215],{"class":35,"line":321},[33,1216,1217],{"class":39},"# shift $((4-1)) = shift 3 → removes -v, -o, out.txt, leaving positional args.\n",[15,1219,1221],{"id":1220},"long-options-manual-parsing","Long Options: Manual Parsing",[20,1223,1224],{"language":22},[24,1225,1227],{"className":26,"code":1226,"language":22,"meta":28,"style":28},"#!\u002Fusr\u002Fbin\u002Fenv bash\n# ── Manual parsing for long options (--verbose, --output FILE) ──\n# More verbose than getopts, but supports --long-option.\n\nverbose=false\noutput=\"\"\ninput=\"\"\ncount=0\n\nwhile [[ $# -gt 0 ]]; do\n    case \"$1\" in\n        # ── Flags (no argument) ──\n        -v|--verbose)\n            verbose=true\n            shift\n            ;;\n        -h|--help)\n            usage; exit 0\n            ;;\n\n        # ── Options with argument: --output VALUE ──\n        -o|--output)\n            output=\"$2\"\n            shift 2    # consume --output AND the value\n            ;;\n        -i|--input)\n            input=\"$2\"\n            shift 2\n            ;;\n        -c|--count)\n            count=\"$2\"\n            shift 2\n            ;;\n\n        # ── Options with = syntax: --output=VALUE ──\n        --output=*)\n            output=\"${1#--output=}\"   # strip the --output= prefix\n            shift\n            ;;\n        --input=*)\n            input=\"${1#--input=}\"\n            shift\n            ;;\n\n        # ── End of options ──\n        --)\n            shift      # remove --, stop parsing\n            break\n            ;;\n\n        # ── Unknown option ──\n        -*)\n            echo \"Unknown option: $1\" >&2\n            exit 1\n            ;;\n\n        # ── Positional argument ──\n        *)\n            positionals+=(\"$1\")\n            shift\n            ;;\n    esac\ndone\n\necho \"verbose: $verbose\"\necho \"output: $output\"\necho \"input: $input\"\necho \"count: $count\"\necho \"positionals: ${positionals[@]:-}\"\n",[30,1228,1229,1233,1238,1243,1247,1255,1263,1272,1280,1284,1304,1316,1321,1334,1344,1349,1353,1365,1376,1380,1384,1389,1401,1414,1424,1428,1440,1453,1460,1464,1476,1489,1495,1499,1503,1508,1518,1544,1548,1552,1561,1581,1585,1589,1594,1600,1608,1616,1622,1627,1632,1638,1648,1662,1669,1674,1679,1685,1691,1711,1716,1721,1726,1731,1736,1747,1758,1771,1782],{"__ignoreMap":28},[33,1230,1231],{"class":35,"line":36},[33,1232,390],{"class":39},[33,1234,1235],{"class":35,"line":43},[33,1236,1237],{"class":39},"# ── Manual parsing for long options (--verbose, --output FILE) ──\n",[33,1239,1240],{"class":35,"line":63},[33,1241,1242],{"class":39},"# More verbose than getopts, but supports --long-option.\n",[33,1244,1245],{"class":35,"line":78},[33,1246,157],{"emptyLinePlaceholder":156},[33,1248,1249,1251,1253],{"class":35,"line":93},[33,1250,409],{"class":273},[33,1252,412],{"class":269},[33,1254,415],{"class":50},[33,1256,1257,1259,1261],{"class":35,"line":108},[33,1258,420],{"class":273},[33,1260,412],{"class":269},[33,1262,425],{"class":50},[33,1264,1265,1268,1270],{"class":35,"line":123},[33,1266,1267],{"class":273},"input",[33,1269,412],{"class":269},[33,1271,425],{"class":50},[33,1273,1274,1276,1278],{"class":35,"line":138},[33,1275,430],{"class":273},[33,1277,412],{"class":269},[33,1279,435],{"class":50},[33,1281,1282],{"class":35,"line":153},[33,1283,157],{"emptyLinePlaceholder":156},[33,1285,1286,1288,1291,1293,1296,1299,1302],{"class":35,"line":160},[33,1287,459],{"class":269},[33,1289,1290],{"class":273}," [[ ",[33,1292,145],{"class":46},[33,1294,1295],{"class":269}," -gt",[33,1297,1298],{"class":46}," 0",[33,1300,1301],{"class":273}," ]]; ",[33,1303,473],{"class":269},[33,1305,1306,1308,1310,1312,1314],{"class":35,"line":166},[33,1307,512],{"class":269},[33,1309,51],{"class":50},[33,1311,70],{"class":46},[33,1313,57],{"class":50},[33,1315,522],{"class":269},[33,1317,1318],{"class":35,"line":193},[33,1319,1320],{"class":39},"        # ── Flags (no argument) ──\n",[33,1322,1323,1326,1329,1332],{"class":35,"line":207},[33,1324,1325],{"class":527},"        -v",[33,1327,1328],{"class":269},"|",[33,1330,1331],{"class":527},"--verbose",[33,1333,531],{"class":269},[33,1335,1336,1339,1341],{"class":35,"line":216},[33,1337,1338],{"class":273},"            verbose",[33,1340,412],{"class":269},[33,1342,1343],{"class":50},"true\n",[33,1345,1346],{"class":35,"line":230},[33,1347,1348],{"class":46},"            shift\n",[33,1350,1351],{"class":35,"line":241},[33,1352,557],{"class":273},[33,1354,1355,1358,1360,1363],{"class":35,"line":255},[33,1356,1357],{"class":527},"        -h",[33,1359,1328],{"class":269},[33,1361,1362],{"class":527},"--help",[33,1364,531],{"class":269},[33,1366,1367,1370,1372,1374],{"class":35,"line":260},[33,1368,1369],{"class":330},"            usage",[33,1371,286],{"class":273},[33,1373,549],{"class":46},[33,1375,552],{"class":46},[33,1377,1378],{"class":35,"line":266},[33,1379,557],{"class":273},[33,1381,1382],{"class":35,"line":295},[33,1383,157],{"emptyLinePlaceholder":156},[33,1385,1386],{"class":35,"line":310},[33,1387,1388],{"class":39},"        # ── Options with argument: --output VALUE ──\n",[33,1390,1391,1394,1396,1399],{"class":35,"line":316},[33,1392,1393],{"class":527},"        -o",[33,1395,1328],{"class":269},[33,1397,1398],{"class":527},"--output",[33,1400,531],{"class":269},[33,1402,1403,1406,1408,1410,1412],{"class":35,"line":321},[33,1404,1405],{"class":273},"            output",[33,1407,412],{"class":269},[33,1409,57],{"class":50},[33,1411,85],{"class":46},[33,1413,307],{"class":50},[33,1415,1416,1419,1421],{"class":35,"line":327},[33,1417,1418],{"class":46},"            shift",[33,1420,235],{"class":46},[33,1422,1423],{"class":39},"    # consume --output AND the value\n",[33,1425,1426],{"class":35,"line":337},[33,1427,557],{"class":273},[33,1429,1430,1433,1435,1438],{"class":35,"line":354},[33,1431,1432],{"class":527},"        -i",[33,1434,1328],{"class":269},[33,1436,1437],{"class":527},"--input",[33,1439,531],{"class":269},[33,1441,1442,1445,1447,1449,1451],{"class":35,"line":360},[33,1443,1444],{"class":273},"            input",[33,1446,412],{"class":269},[33,1448,57],{"class":50},[33,1450,85],{"class":46},[33,1452,307],{"class":50},[33,1454,1455,1457],{"class":35,"line":634},[33,1456,1418],{"class":46},[33,1458,1459],{"class":46}," 2\n",[33,1461,1462],{"class":35,"line":649},[33,1463,557],{"class":273},[33,1465,1466,1469,1471,1474],{"class":35,"line":658},[33,1467,1468],{"class":527},"        -c",[33,1470,1328],{"class":269},[33,1472,1473],{"class":527},"--count",[33,1475,531],{"class":269},[33,1477,1478,1481,1483,1485,1487],{"class":35,"line":663},[33,1479,1480],{"class":273},"            count",[33,1482,412],{"class":269},[33,1484,57],{"class":50},[33,1486,85],{"class":46},[33,1488,307],{"class":50},[33,1490,1491,1493],{"class":35,"line":674},[33,1492,1418],{"class":46},[33,1494,1459],{"class":46},[33,1496,1497],{"class":35,"line":689},[33,1498,557],{"class":273},[33,1500,1501],{"class":35,"line":696},[33,1502,157],{"emptyLinePlaceholder":156},[33,1504,1505],{"class":35,"line":701},[33,1506,1507],{"class":39},"        # ── Options with = syntax: --output=VALUE ──\n",[33,1509,1510,1513,1516],{"class":35,"line":707},[33,1511,1512],{"class":527},"        --output=",[33,1514,1515],{"class":269},"*",[33,1517,531],{"class":269},[33,1519,1520,1522,1524,1526,1529,1532,1534,1536,1539,1541],{"class":35,"line":712},[33,1521,1405],{"class":273},[33,1523,412],{"class":269},[33,1525,57],{"class":50},[33,1527,1528],{"class":46},"${1",[33,1530,1531],{"class":269},"#",[33,1533,1398],{"class":273},[33,1535,412],{"class":50},[33,1537,1538],{"class":46},"}",[33,1540,57],{"class":50},[33,1542,1543],{"class":39},"   # strip the --output= prefix\n",[33,1545,1546],{"class":35,"line":735},[33,1547,1348],{"class":46},[33,1549,1550],{"class":35,"line":740},[33,1551,557],{"class":273},[33,1553,1554,1557,1559],{"class":35,"line":746},[33,1555,1556],{"class":527},"        --input=",[33,1558,1515],{"class":269},[33,1560,531],{"class":269},[33,1562,1563,1565,1567,1569,1571,1573,1575,1577,1579],{"class":35,"line":759},[33,1564,1444],{"class":273},[33,1566,412],{"class":269},[33,1568,57],{"class":50},[33,1570,1528],{"class":46},[33,1572,1531],{"class":269},[33,1574,1437],{"class":273},[33,1576,412],{"class":50},[33,1578,1538],{"class":46},[33,1580,307],{"class":50},[33,1582,1583],{"class":35,"line":772},[33,1584,1348],{"class":46},[33,1586,1587],{"class":35,"line":785},[33,1588,557],{"class":273},[33,1590,1592],{"class":35,"line":1591},44,[33,1593,157],{"emptyLinePlaceholder":156},[33,1595,1597],{"class":35,"line":1596},45,[33,1598,1599],{"class":39},"        # ── End of options ──\n",[33,1601,1603,1606],{"class":35,"line":1602},46,[33,1604,1605],{"class":527},"        --",[33,1607,531],{"class":269},[33,1609,1611,1613],{"class":35,"line":1610},47,[33,1612,1418],{"class":46},[33,1614,1615],{"class":39},"      # remove --, stop parsing\n",[33,1617,1619],{"class":35,"line":1618},48,[33,1620,1621],{"class":269},"            break\n",[33,1623,1625],{"class":35,"line":1624},49,[33,1626,557],{"class":273},[33,1628,1630],{"class":35,"line":1629},50,[33,1631,157],{"emptyLinePlaceholder":156},[33,1633,1635],{"class":35,"line":1634},51,[33,1636,1637],{"class":39},"        # ── Unknown option ──\n",[33,1639,1641,1644,1646],{"class":35,"line":1640},52,[33,1642,1643],{"class":527},"        -",[33,1645,1515],{"class":269},[33,1647,531],{"class":269},[33,1649,1651,1653,1656,1658,1660],{"class":35,"line":1650},53,[33,1652,536],{"class":46},[33,1654,1655],{"class":50}," \"Unknown option: ",[33,1657,70],{"class":46},[33,1659,57],{"class":50},[33,1661,646],{"class":269},[33,1663,1665,1667],{"class":35,"line":1664},54,[33,1666,652],{"class":46},[33,1668,655],{"class":46},[33,1670,1672],{"class":35,"line":1671},55,[33,1673,557],{"class":273},[33,1675,1677],{"class":35,"line":1676},56,[33,1678,157],{"emptyLinePlaceholder":156},[33,1680,1682],{"class":35,"line":1681},57,[33,1683,1684],{"class":39},"        # ── Positional argument ──\n",[33,1686,1688],{"class":35,"line":1687},58,[33,1689,1690],{"class":269},"        *)\n",[33,1692,1694,1697,1700,1703,1705,1707,1709],{"class":35,"line":1693},59,[33,1695,1696],{"class":273},"            positionals",[33,1698,1699],{"class":269},"+=",[33,1701,1702],{"class":273},"(",[33,1704,57],{"class":50},[33,1706,70],{"class":46},[33,1708,57],{"class":50},[33,1710,531],{"class":273},[33,1712,1714],{"class":35,"line":1713},60,[33,1715,1348],{"class":46},[33,1717,1719],{"class":35,"line":1718},61,[33,1720,557],{"class":273},[33,1722,1724],{"class":35,"line":1723},62,[33,1725,704],{"class":269},[33,1727,1729],{"class":35,"line":1728},63,[33,1730,313],{"class":269},[33,1732,1734],{"class":35,"line":1733},64,[33,1735,157],{"emptyLinePlaceholder":156},[33,1737,1739,1741,1743,1745],{"class":35,"line":1738},65,[33,1740,47],{"class":46},[33,1742,751],{"class":50},[33,1744,754],{"class":273},[33,1746,307],{"class":50},[33,1748,1750,1752,1754,1756],{"class":35,"line":1749},66,[33,1751,47],{"class":46},[33,1753,764],{"class":50},[33,1755,767],{"class":273},[33,1757,307],{"class":50},[33,1759,1761,1763,1766,1769],{"class":35,"line":1760},67,[33,1762,47],{"class":46},[33,1764,1765],{"class":50}," \"input: ",[33,1767,1768],{"class":273},"$input",[33,1770,307],{"class":50},[33,1772,1774,1776,1778,1780],{"class":35,"line":1773},68,[33,1775,47],{"class":46},[33,1777,777],{"class":50},[33,1779,780],{"class":273},[33,1781,307],{"class":50},[33,1783,1785,1787,1790,1793,1796,1799,1802,1805],{"class":35,"line":1784},69,[33,1786,47],{"class":46},[33,1788,1789],{"class":50}," \"positionals: ${",[33,1791,1792],{"class":273},"positionals",[33,1794,1795],{"class":50},"[",[33,1797,1798],{"class":269},"@",[33,1800,1801],{"class":50},"]",[33,1803,1804],{"class":269},":-",[33,1806,1807],{"class":50},"}\"\n",[15,1809,1811],{"id":1810},"production-pattern-full-cli-with-subcommands","Production Pattern: Full CLI with Subcommands",[20,1813,1814],{"language":22},[24,1815,1817],{"className":26,"code":1816,"language":22,"meta":28,"style":28},"#!\u002Fusr\u002Fbin\u002Fenv bash\nset -euo pipefail\n\nreadonly VERSION=\"1.0.0\"\nreadonly SCRIPT_NAME=$(basename \"$0\")\n\n# ── Usage ──\nusage() {\n    cat \u003C\u003CEOF\n$SCRIPT_NAME v$VERSION\n\nUsage: $SCRIPT_NAME [OPTIONS] \u003Ccommand> [args...]\n\nCommands:\n  build       Build the project\n  test        Run tests\n  deploy      Deploy to environment\n\nOptions:\n  -h, --help     Show this help\n  -v, --verbose  Verbose output\n  --version      Show version\n\nExamples:\n  $SCRIPT_NAME build --output dist\u002F\n  $SCRIPT_NAME test --coverage\n  $SCRIPT_NAME deploy --env prod --dry-run\nEOF\n}\n\n# ── Global options ──\nverbose=false\n\n# ── Parse global options (before subcommand) ──\nwhile [[ $# -gt 0 ]]; do\n    case \"$1\" in\n        -h|--help)   usage; exit 0 ;;\n        -v|--verbose) verbose=true; shift ;;\n        --version)   echo \"$VERSION\"; exit 0 ;;\n        -*)          echo \"Unknown option: $1\" >&2; usage; exit 1 ;;\n        *)           break ;;  # first non-option is the subcommand\n    esac\ndone\n\n# ── Subcommand dispatch ──\nsubcommand=\"${1:-}\"\nshift || true   # remove subcommand, don't fail if no args\n\ncase \"$subcommand\" in\n    build)  cmd_build \"$@\" ;;\n    test)   cmd_test \"$@\" ;;\n    deploy) cmd_deploy \"$@\" ;;\n    \"\"|help) usage ;;\n    *)      echo \"Unknown command: $subcommand\" >&2; usage; exit 1 ;;\nesac\n\n# ── Subcommand implementations ──\ncmd_build() {\n    local output=\"dist\u002F\"\n    while [[ $# -gt 0 ]]; do\n        case \"$1\" in\n            -o|--output) output=\"$2\"; shift 2 ;;\n            --output=*)  output=\"${1#--output=}\"; shift ;;\n            -h|--help)   echo \"Usage: $SCRIPT_NAME build [-o DIR]\"; return 0 ;;\n            *)           echo \"Unknown: $1\" >&2; return 1 ;;\n        esac\n    done\n    $verbose && echo \"Building to $output...\"\n    # ... real build logic ...\n}\n\ncmd_test() {\n    local coverage=false\n    while [[ $# -gt 0 ]]; do\n        case \"$1\" in\n            --coverage) coverage=true; shift ;;\n            -h|--help)  echo \"Usage: $SCRIPT_NAME test [--coverage]\"; return 0 ;;\n            *)          echo \"Unknown: $1\" >&2; return 1 ;;\n        esac\n    done\n    $verbose && echo \"Running tests (coverage: $coverage)...\"\n    # ... real test logic ...\n}\n\ncmd_deploy() {\n    local env=\"staging\" dry_run=false\n    while [[ $# -gt 0 ]]; do\n        case \"$1\" in\n            -e|--env)     env=\"$2\"; shift 2 ;;\n            --env=*)     env=\"${1#--env=}\"; shift ;;\n            --dry-run)   dry_run=true; shift ;;\n            -h|--help)   echo \"Usage: $SCRIPT_NAME deploy [-e ENV] [--dry-run]\"; return 0 ;;\n            *)           echo \"Unknown: $1\" >&2; return 1 ;;\n        esac\n    done\n    $verbose && echo \"Deploying to $env (dry-run: $dry_run)...\"\n    # ... real deploy logic ...\n}\n",[30,1818,1819,1823,1833,1837,1850,1873,1877,1882,1889,1900,1911,1915,1925,1929,1934,1939,1944,1949,1953,1958,1963,1968,1973,1977,1982,1990,1997,2004,2008,2012,2016,2021,2029,2033,2038,2054,2066,2087,2109,2134,2166,2180,2184,2188,2192,2197,2214,2227,2231,2245,2263,2281,2299,2316,2345,2350,2354,2359,2366,2378,2395,2408,2437,2471,2500,2525,2530,2535,2550,2555,2560,2565,2573,2585,2602,2615,2635,2664,2687,2692,2697,2713,2719,2724,2729,2737,2757,2774,2787,2819,2853,2874,2902,2925,2930,2935,2956,2962],{"__ignoreMap":28},[33,1820,1821],{"class":35,"line":36},[33,1822,390],{"class":39},[33,1824,1825,1827,1830],{"class":35,"line":43},[33,1826,169],{"class":46},[33,1828,1829],{"class":46}," -euo",[33,1831,1832],{"class":50}," pipefail\n",[33,1834,1835],{"class":35,"line":63},[33,1836,157],{"emptyLinePlaceholder":156},[33,1838,1839,1842,1845,1847],{"class":35,"line":78},[33,1840,1841],{"class":269},"readonly",[33,1843,1844],{"class":273}," VERSION",[33,1846,412],{"class":269},[33,1848,1849],{"class":50},"\"1.0.0\"\n",[33,1851,1852,1854,1857,1859,1862,1865,1867,1869,1871],{"class":35,"line":93},[33,1853,1841],{"class":269},[33,1855,1856],{"class":273}," SCRIPT_NAME",[33,1858,412],{"class":269},[33,1860,1861],{"class":273},"$(",[33,1863,1864],{"class":330},"basename",[33,1866,51],{"class":50},[33,1868,54],{"class":46},[33,1870,57],{"class":50},[33,1872,531],{"class":273},[33,1874,1875],{"class":35,"line":108},[33,1876,157],{"emptyLinePlaceholder":156},[33,1878,1879],{"class":35,"line":123},[33,1880,1881],{"class":39},"# ── Usage ──\n",[33,1883,1884,1887],{"class":35,"line":138},[33,1885,1886],{"class":330},"usage",[33,1888,334],{"class":273},[33,1890,1891,1894,1897],{"class":35,"line":153},[33,1892,1893],{"class":330},"    cat",[33,1895,1896],{"class":269}," \u003C\u003C",[33,1898,1899],{"class":50},"EOF\n",[33,1901,1902,1905,1908],{"class":35,"line":160},[33,1903,1904],{"class":273},"$SCRIPT_NAME",[33,1906,1907],{"class":50}," v",[33,1909,1910],{"class":273},"$VERSION\n",[33,1912,1913],{"class":35,"line":166},[33,1914,157],{"emptyLinePlaceholder":156},[33,1916,1917,1920,1922],{"class":35,"line":193},[33,1918,1919],{"class":50},"Usage: ",[33,1921,1904],{"class":273},[33,1923,1924],{"class":50}," [OPTIONS] \u003Ccommand> [args...]\n",[33,1926,1927],{"class":35,"line":207},[33,1928,157],{"emptyLinePlaceholder":156},[33,1930,1931],{"class":35,"line":216},[33,1932,1933],{"class":50},"Commands:\n",[33,1935,1936],{"class":35,"line":230},[33,1937,1938],{"class":50},"  build       Build the project\n",[33,1940,1941],{"class":35,"line":241},[33,1942,1943],{"class":50},"  test        Run tests\n",[33,1945,1946],{"class":35,"line":255},[33,1947,1948],{"class":50},"  deploy      Deploy to environment\n",[33,1950,1951],{"class":35,"line":260},[33,1952,157],{"emptyLinePlaceholder":156},[33,1954,1955],{"class":35,"line":266},[33,1956,1957],{"class":50},"Options:\n",[33,1959,1960],{"class":35,"line":295},[33,1961,1962],{"class":50},"  -h, --help     Show this help\n",[33,1964,1965],{"class":35,"line":310},[33,1966,1967],{"class":50},"  -v, --verbose  Verbose output\n",[33,1969,1970],{"class":35,"line":316},[33,1971,1972],{"class":50},"  --version      Show version\n",[33,1974,1975],{"class":35,"line":321},[33,1976,157],{"emptyLinePlaceholder":156},[33,1978,1979],{"class":35,"line":327},[33,1980,1981],{"class":50},"Examples:\n",[33,1983,1984,1987],{"class":35,"line":337},[33,1985,1986],{"class":273},"  $SCRIPT_NAME",[33,1988,1989],{"class":50}," build --output dist\u002F\n",[33,1991,1992,1994],{"class":35,"line":354},[33,1993,1986],{"class":273},[33,1995,1996],{"class":50}," test --coverage\n",[33,1998,1999,2001],{"class":35,"line":360},[33,2000,1986],{"class":273},[33,2002,2003],{"class":50}," deploy --env prod --dry-run\n",[33,2005,2006],{"class":35,"line":634},[33,2007,1899],{"class":50},[33,2009,2010],{"class":35,"line":649},[33,2011,357],{"class":273},[33,2013,2014],{"class":35,"line":658},[33,2015,157],{"emptyLinePlaceholder":156},[33,2017,2018],{"class":35,"line":663},[33,2019,2020],{"class":39},"# ── Global options ──\n",[33,2022,2023,2025,2027],{"class":35,"line":674},[33,2024,409],{"class":273},[33,2026,412],{"class":269},[33,2028,415],{"class":50},[33,2030,2031],{"class":35,"line":689},[33,2032,157],{"emptyLinePlaceholder":156},[33,2034,2035],{"class":35,"line":696},[33,2036,2037],{"class":39},"# ── Parse global options (before subcommand) ──\n",[33,2039,2040,2042,2044,2046,2048,2050,2052],{"class":35,"line":701},[33,2041,459],{"class":269},[33,2043,1290],{"class":273},[33,2045,145],{"class":46},[33,2047,1295],{"class":269},[33,2049,1298],{"class":46},[33,2051,1301],{"class":273},[33,2053,473],{"class":269},[33,2055,2056,2058,2060,2062,2064],{"class":35,"line":707},[33,2057,512],{"class":269},[33,2059,51],{"class":50},[33,2061,70],{"class":46},[33,2063,57],{"class":50},[33,2065,522],{"class":269},[33,2067,2068,2070,2072,2074,2076,2079,2081,2083,2085],{"class":35,"line":712},[33,2069,1357],{"class":527},[33,2071,1328],{"class":269},[33,2073,1362],{"class":527},[33,2075,565],{"class":269},[33,2077,2078],{"class":330},"   usage",[33,2080,286],{"class":273},[33,2082,549],{"class":46},[33,2084,1298],{"class":46},[33,2086,576],{"class":273},[33,2088,2089,2091,2093,2095,2097,2099,2101,2103,2105,2107],{"class":35,"line":735},[33,2090,1325],{"class":527},[33,2092,1328],{"class":269},[33,2094,1331],{"class":527},[33,2096,565],{"class":269},[33,2098,568],{"class":273},[33,2100,412],{"class":269},[33,2102,573],{"class":50},[33,2104,286],{"class":273},[33,2106,210],{"class":46},[33,2108,576],{"class":273},[33,2110,2111,2114,2116,2119,2121,2124,2126,2128,2130,2132],{"class":35,"line":740},[33,2112,2113],{"class":527},"        --version",[33,2115,565],{"class":269},[33,2117,2118],{"class":46},"   echo",[33,2120,51],{"class":50},[33,2122,2123],{"class":273},"$VERSION",[33,2125,57],{"class":50},[33,2127,286],{"class":273},[33,2129,549],{"class":46},[33,2131,1298],{"class":46},[33,2133,576],{"class":273},[33,2135,2136,2138,2140,2142,2145,2147,2149,2151,2154,2156,2158,2160,2162,2164],{"class":35,"line":746},[33,2137,1643],{"class":527},[33,2139,1515],{"class":269},[33,2141,565],{"class":269},[33,2143,2144],{"class":46},"          echo",[33,2146,1655],{"class":50},[33,2148,70],{"class":46},[33,2150,57],{"class":50},[33,2152,2153],{"class":269}," >&2",[33,2155,286],{"class":273},[33,2157,1886],{"class":330},[33,2159,286],{"class":273},[33,2161,549],{"class":46},[33,2163,726],{"class":46},[33,2165,576],{"class":273},[33,2167,2168,2171,2174,2177],{"class":35,"line":759},[33,2169,2170],{"class":269},"        *)",[33,2172,2173],{"class":269},"           break",[33,2175,2176],{"class":273}," ;;  ",[33,2178,2179],{"class":39},"# first non-option is the subcommand\n",[33,2181,2182],{"class":35,"line":772},[33,2183,704],{"class":269},[33,2185,2186],{"class":35,"line":785},[33,2187,313],{"class":269},[33,2189,2190],{"class":35,"line":1591},[33,2191,157],{"emptyLinePlaceholder":156},[33,2193,2194],{"class":35,"line":1596},[33,2195,2196],{"class":39},"# ── Subcommand dispatch ──\n",[33,2198,2199,2202,2204,2206,2208,2210,2212],{"class":35,"line":1602},[33,2200,2201],{"class":273},"subcommand",[33,2203,412],{"class":269},[33,2205,57],{"class":50},[33,2207,1528],{"class":46},[33,2209,1804],{"class":269},[33,2211,1538],{"class":46},[33,2213,307],{"class":50},[33,2215,2216,2218,2221,2224],{"class":35,"line":1610},[33,2217,210],{"class":46},[33,2219,2220],{"class":269}," ||",[33,2222,2223],{"class":46}," true",[33,2225,2226],{"class":39},"   # remove subcommand, don't fail if no args\n",[33,2228,2229],{"class":35,"line":1618},[33,2230,157],{"emptyLinePlaceholder":156},[33,2232,2233,2236,2238,2241,2243],{"class":35,"line":1624},[33,2234,2235],{"class":269},"case",[33,2237,51],{"class":50},[33,2239,2240],{"class":273},"$subcommand",[33,2242,57],{"class":50},[33,2244,522],{"class":269},[33,2246,2247,2250,2252,2255,2257,2259,2261],{"class":35,"line":1629},[33,2248,2249],{"class":527},"    build",[33,2251,565],{"class":269},[33,2253,2254],{"class":330},"  cmd_build",[33,2256,51],{"class":50},[33,2258,115],{"class":46},[33,2260,57],{"class":50},[33,2262,576],{"class":273},[33,2264,2265,2268,2270,2273,2275,2277,2279],{"class":35,"line":1634},[33,2266,2267],{"class":527},"    test",[33,2269,565],{"class":269},[33,2271,2272],{"class":330},"   cmd_test",[33,2274,51],{"class":50},[33,2276,115],{"class":46},[33,2278,57],{"class":50},[33,2280,576],{"class":273},[33,2282,2283,2286,2288,2291,2293,2295,2297],{"class":35,"line":1640},[33,2284,2285],{"class":527},"    deploy",[33,2287,565],{"class":269},[33,2289,2290],{"class":330}," cmd_deploy",[33,2292,51],{"class":50},[33,2294,115],{"class":46},[33,2296,57],{"class":50},[33,2298,576],{"class":273},[33,2300,2301,2304,2306,2309,2311,2314],{"class":35,"line":1650},[33,2302,2303],{"class":50},"    \"\"",[33,2305,1328],{"class":269},[33,2307,2308],{"class":527},"help",[33,2310,565],{"class":269},[33,2312,2313],{"class":330}," usage",[33,2315,576],{"class":273},[33,2317,2318,2321,2324,2327,2329,2331,2333,2335,2337,2339,2341,2343],{"class":35,"line":1664},[33,2319,2320],{"class":269},"    *)",[33,2322,2323],{"class":46},"      echo",[33,2325,2326],{"class":50}," \"Unknown command: ",[33,2328,2240],{"class":273},[33,2330,57],{"class":50},[33,2332,2153],{"class":269},[33,2334,286],{"class":273},[33,2336,1886],{"class":330},[33,2338,286],{"class":273},[33,2340,549],{"class":46},[33,2342,726],{"class":46},[33,2344,576],{"class":273},[33,2346,2347],{"class":35,"line":1671},[33,2348,2349],{"class":269},"esac\n",[33,2351,2352],{"class":35,"line":1676},[33,2353,157],{"emptyLinePlaceholder":156},[33,2355,2356],{"class":35,"line":1681},[33,2357,2358],{"class":39},"# ── Subcommand implementations ──\n",[33,2360,2361,2364],{"class":35,"line":1687},[33,2362,2363],{"class":330},"cmd_build",[33,2365,334],{"class":273},[33,2367,2368,2371,2373,2375],{"class":35,"line":1693},[33,2369,2370],{"class":269},"    local",[33,2372,586],{"class":273},[33,2374,412],{"class":269},[33,2376,2377],{"class":50},"\"dist\u002F\"\n",[33,2379,2380,2383,2385,2387,2389,2391,2393],{"class":35,"line":1713},[33,2381,2382],{"class":269},"    while",[33,2384,1290],{"class":273},[33,2386,145],{"class":46},[33,2388,1295],{"class":269},[33,2390,1298],{"class":46},[33,2392,1301],{"class":273},[33,2394,473],{"class":269},[33,2396,2397,2400,2402,2404,2406],{"class":35,"line":1718},[33,2398,2399],{"class":269},"        case",[33,2401,51],{"class":50},[33,2403,70],{"class":46},[33,2405,57],{"class":50},[33,2407,522],{"class":269},[33,2409,2410,2413,2415,2417,2419,2421,2423,2425,2427,2429,2431,2433,2435],{"class":35,"line":1723},[33,2411,2412],{"class":527},"            -o",[33,2414,1328],{"class":269},[33,2416,1398],{"class":527},[33,2418,565],{"class":269},[33,2420,586],{"class":273},[33,2422,412],{"class":269},[33,2424,57],{"class":50},[33,2426,85],{"class":46},[33,2428,57],{"class":50},[33,2430,286],{"class":273},[33,2432,210],{"class":46},[33,2434,235],{"class":46},[33,2436,576],{"class":273},[33,2438,2439,2442,2444,2446,2449,2451,2453,2455,2457,2459,2461,2463,2465,2467,2469],{"class":35,"line":1728},[33,2440,2441],{"class":527},"            --output=",[33,2443,1515],{"class":269},[33,2445,565],{"class":269},[33,2447,2448],{"class":273},"  output",[33,2450,412],{"class":269},[33,2452,57],{"class":50},[33,2454,1528],{"class":46},[33,2456,1531],{"class":269},[33,2458,1398],{"class":273},[33,2460,412],{"class":50},[33,2462,1538],{"class":46},[33,2464,57],{"class":50},[33,2466,286],{"class":273},[33,2468,210],{"class":46},[33,2470,576],{"class":273},[33,2472,2473,2476,2478,2480,2482,2484,2486,2488,2491,2493,2496,2498],{"class":35,"line":1733},[33,2474,2475],{"class":527},"            -h",[33,2477,1328],{"class":269},[33,2479,1362],{"class":527},[33,2481,565],{"class":269},[33,2483,2118],{"class":46},[33,2485,539],{"class":50},[33,2487,1904],{"class":273},[33,2489,2490],{"class":50}," build [-o DIR]\"",[33,2492,286],{"class":273},[33,2494,2495],{"class":269},"return",[33,2497,1298],{"class":46},[33,2499,576],{"class":273},[33,2501,2502,2505,2508,2511,2513,2515,2517,2519,2521,2523],{"class":35,"line":1738},[33,2503,2504],{"class":269},"            *)",[33,2506,2507],{"class":46},"           echo",[33,2509,2510],{"class":50}," \"Unknown: ",[33,2512,70],{"class":46},[33,2514,57],{"class":50},[33,2516,2153],{"class":269},[33,2518,286],{"class":273},[33,2520,2495],{"class":269},[33,2522,726],{"class":46},[33,2524,576],{"class":273},[33,2526,2527],{"class":35,"line":1749},[33,2528,2529],{"class":269},"        esac\n",[33,2531,2532],{"class":35,"line":1760},[33,2533,2534],{"class":269},"    done\n",[33,2536,2537,2540,2542,2545,2547],{"class":35,"line":1773},[33,2538,2539],{"class":273},"    $verbose && ",[33,2541,47],{"class":46},[33,2543,2544],{"class":50}," \"Building to ",[33,2546,767],{"class":273},[33,2548,2549],{"class":50},"...\"\n",[33,2551,2552],{"class":35,"line":1784},[33,2553,2554],{"class":39},"    # ... real build logic ...\n",[33,2556,2558],{"class":35,"line":2557},70,[33,2559,357],{"class":273},[33,2561,2563],{"class":35,"line":2562},71,[33,2564,157],{"emptyLinePlaceholder":156},[33,2566,2568,2571],{"class":35,"line":2567},72,[33,2569,2570],{"class":330},"cmd_test",[33,2572,334],{"class":273},[33,2574,2576,2578,2581,2583],{"class":35,"line":2575},73,[33,2577,2370],{"class":269},[33,2579,2580],{"class":273}," coverage",[33,2582,412],{"class":269},[33,2584,415],{"class":273},[33,2586,2588,2590,2592,2594,2596,2598,2600],{"class":35,"line":2587},74,[33,2589,2382],{"class":269},[33,2591,1290],{"class":273},[33,2593,145],{"class":46},[33,2595,1295],{"class":269},[33,2597,1298],{"class":46},[33,2599,1301],{"class":273},[33,2601,473],{"class":269},[33,2603,2605,2607,2609,2611,2613],{"class":35,"line":2604},75,[33,2606,2399],{"class":269},[33,2608,51],{"class":50},[33,2610,70],{"class":46},[33,2612,57],{"class":50},[33,2614,522],{"class":269},[33,2616,2618,2621,2623,2625,2627,2629,2631,2633],{"class":35,"line":2617},76,[33,2619,2620],{"class":527},"            --coverage",[33,2622,565],{"class":269},[33,2624,2580],{"class":273},[33,2626,412],{"class":269},[33,2628,573],{"class":50},[33,2630,286],{"class":273},[33,2632,210],{"class":46},[33,2634,576],{"class":273},[33,2636,2638,2640,2642,2644,2646,2649,2651,2653,2656,2658,2660,2662],{"class":35,"line":2637},77,[33,2639,2475],{"class":527},[33,2641,1328],{"class":269},[33,2643,1362],{"class":527},[33,2645,565],{"class":269},[33,2647,2648],{"class":46},"  echo",[33,2650,539],{"class":50},[33,2652,1904],{"class":273},[33,2654,2655],{"class":50}," test [--coverage]\"",[33,2657,286],{"class":273},[33,2659,2495],{"class":269},[33,2661,1298],{"class":46},[33,2663,576],{"class":273},[33,2665,2667,2669,2671,2673,2675,2677,2679,2681,2683,2685],{"class":35,"line":2666},78,[33,2668,2504],{"class":269},[33,2670,2144],{"class":46},[33,2672,2510],{"class":50},[33,2674,70],{"class":46},[33,2676,57],{"class":50},[33,2678,2153],{"class":269},[33,2680,286],{"class":273},[33,2682,2495],{"class":269},[33,2684,726],{"class":46},[33,2686,576],{"class":273},[33,2688,2690],{"class":35,"line":2689},79,[33,2691,2529],{"class":269},[33,2693,2695],{"class":35,"line":2694},80,[33,2696,2534],{"class":269},[33,2698,2700,2702,2704,2707,2710],{"class":35,"line":2699},81,[33,2701,2539],{"class":273},[33,2703,47],{"class":46},[33,2705,2706],{"class":50}," \"Running tests (coverage: ",[33,2708,2709],{"class":273},"$coverage",[33,2711,2712],{"class":50},")...\"\n",[33,2714,2716],{"class":35,"line":2715},82,[33,2717,2718],{"class":39},"    # ... real test logic ...\n",[33,2720,2722],{"class":35,"line":2721},83,[33,2723,357],{"class":273},[33,2725,2727],{"class":35,"line":2726},84,[33,2728,157],{"emptyLinePlaceholder":156},[33,2730,2732,2735],{"class":35,"line":2731},85,[33,2733,2734],{"class":330},"cmd_deploy",[33,2736,334],{"class":273},[33,2738,2740,2742,2745,2747,2750,2753,2755],{"class":35,"line":2739},86,[33,2741,2370],{"class":269},[33,2743,2744],{"class":273}," env",[33,2746,412],{"class":269},[33,2748,2749],{"class":50},"\"staging\"",[33,2751,2752],{"class":273}," dry_run",[33,2754,412],{"class":269},[33,2756,415],{"class":273},[33,2758,2760,2762,2764,2766,2768,2770,2772],{"class":35,"line":2759},87,[33,2761,2382],{"class":269},[33,2763,1290],{"class":273},[33,2765,145],{"class":46},[33,2767,1295],{"class":269},[33,2769,1298],{"class":46},[33,2771,1301],{"class":273},[33,2773,473],{"class":269},[33,2775,2777,2779,2781,2783,2785],{"class":35,"line":2776},88,[33,2778,2399],{"class":269},[33,2780,51],{"class":50},[33,2782,70],{"class":46},[33,2784,57],{"class":50},[33,2786,522],{"class":269},[33,2788,2790,2793,2795,2798,2800,2803,2805,2807,2809,2811,2813,2815,2817],{"class":35,"line":2789},89,[33,2791,2792],{"class":527},"            -e",[33,2794,1328],{"class":269},[33,2796,2797],{"class":527},"--env",[33,2799,565],{"class":269},[33,2801,2802],{"class":273},"     env",[33,2804,412],{"class":269},[33,2806,57],{"class":50},[33,2808,85],{"class":46},[33,2810,57],{"class":50},[33,2812,286],{"class":273},[33,2814,210],{"class":46},[33,2816,235],{"class":46},[33,2818,576],{"class":273},[33,2820,2822,2825,2827,2829,2831,2833,2835,2837,2839,2841,2843,2845,2847,2849,2851],{"class":35,"line":2821},90,[33,2823,2824],{"class":527},"            --env=",[33,2826,1515],{"class":269},[33,2828,565],{"class":269},[33,2830,2802],{"class":273},[33,2832,412],{"class":269},[33,2834,57],{"class":50},[33,2836,1528],{"class":46},[33,2838,1531],{"class":269},[33,2840,2797],{"class":273},[33,2842,412],{"class":50},[33,2844,1538],{"class":46},[33,2846,57],{"class":50},[33,2848,286],{"class":273},[33,2850,210],{"class":46},[33,2852,576],{"class":273},[33,2854,2856,2859,2861,2864,2866,2868,2870,2872],{"class":35,"line":2855},91,[33,2857,2858],{"class":527},"            --dry-run",[33,2860,565],{"class":269},[33,2862,2863],{"class":273},"   dry_run",[33,2865,412],{"class":269},[33,2867,573],{"class":50},[33,2869,286],{"class":273},[33,2871,210],{"class":46},[33,2873,576],{"class":273},[33,2875,2877,2879,2881,2883,2885,2887,2889,2891,2894,2896,2898,2900],{"class":35,"line":2876},92,[33,2878,2475],{"class":527},[33,2880,1328],{"class":269},[33,2882,1362],{"class":527},[33,2884,565],{"class":269},[33,2886,2118],{"class":46},[33,2888,539],{"class":50},[33,2890,1904],{"class":273},[33,2892,2893],{"class":50}," deploy [-e ENV] [--dry-run]\"",[33,2895,286],{"class":273},[33,2897,2495],{"class":269},[33,2899,1298],{"class":46},[33,2901,576],{"class":273},[33,2903,2905,2907,2909,2911,2913,2915,2917,2919,2921,2923],{"class":35,"line":2904},93,[33,2906,2504],{"class":269},[33,2908,2507],{"class":46},[33,2910,2510],{"class":50},[33,2912,70],{"class":46},[33,2914,57],{"class":50},[33,2916,2153],{"class":269},[33,2918,286],{"class":273},[33,2920,2495],{"class":269},[33,2922,726],{"class":46},[33,2924,576],{"class":273},[33,2926,2928],{"class":35,"line":2927},94,[33,2929,2529],{"class":269},[33,2931,2933],{"class":35,"line":2932},95,[33,2934,2534],{"class":269},[33,2936,2938,2940,2942,2945,2948,2951,2954],{"class":35,"line":2937},96,[33,2939,2539],{"class":273},[33,2941,47],{"class":46},[33,2943,2944],{"class":50}," \"Deploying to ",[33,2946,2947],{"class":273},"$env",[33,2949,2950],{"class":50}," (dry-run: ",[33,2952,2953],{"class":273},"$dry_run",[33,2955,2712],{"class":50},[33,2957,2959],{"class":35,"line":2958},97,[33,2960,2961],{"class":39},"    # ... real deploy logic ...\n",[33,2963,2965],{"class":35,"line":2964},98,[33,2966,357],{"class":273},[15,2968,2970],{"id":2969},"reading-from-stdin","Reading from stdin",[20,2972,2973],{"language":22},[24,2974,2976],{"className":26,"code":2975,"language":22,"meta":28,"style":28},"# ── Detect if stdin is a pipe (data) or terminal (interactive) ──\nif [[ -t 0 ]]; then\n    # stdin is a terminal — no piped input\n    echo \"No input on stdin. Use: $0 \u003C file.txt  OR  cat file | $0\"\n    input=\"\"\nelse\n    # stdin is a pipe or file — read it\n    input=$(cat)          # read ALL of stdin\n    # Or line by line:\n    # while IFS= read -r line; do ...; done\nfi\n\n# ── Common pattern: read from file OR stdin ──\n# If a file is given as arg, read from it; otherwise read from stdin.\nif [[ $# -gt 0 ]]; then\n    input_file=\"$1\"\n    [[ -f \"$input_file\" ]] || { echo \"not found: $input_file\" >&2; exit 1; }\n    while IFS= read -r line; do\n        process \"$line\"\n    done \u003C \"$input_file\"\nelse\n    while IFS= read -r line; do\n        process \"$line\"\n    done\nfi\n\n# ── Check if stdout is a terminal ──\nif [[ -t 1 ]]; then\n    # stdout is a terminal — use colors\n    RED=$'\\e[31m'\n    RESET=$'\\e[0m'\nelse\n    # stdout is piped\u002Fredirected — no colors\n    RED=\"\"\n    RESET=\"\"\nfi\necho \"${RED}Error${RESET}: something went wrong\"\n",[30,2977,2978,2983,3000,3005,3021,3030,3035,3040,3057,3062,3067,3072,3076,3081,3086,3102,3115,3159,3181,3193,3207,3211,3229,3239,3243,3247,3251,3256,3270,3275,3291,3305,3309,3314,3322,3330,3334],{"__ignoreMap":28},[33,2979,2980],{"class":35,"line":36},[33,2981,2982],{"class":39},"# ── Detect if stdin is a pipe (data) or terminal (interactive) ──\n",[33,2984,2985,2988,2990,2993,2995,2997],{"class":35,"line":43},[33,2986,2987],{"class":269},"if",[33,2989,1290],{"class":273},[33,2991,2992],{"class":269},"-t",[33,2994,1298],{"class":46},[33,2996,1301],{"class":273},[33,2998,2999],{"class":269},"then\n",[33,3001,3002],{"class":35,"line":63},[33,3003,3004],{"class":39},"    # stdin is a terminal — no piped input\n",[33,3006,3007,3009,3012,3014,3017,3019],{"class":35,"line":78},[33,3008,298],{"class":46},[33,3010,3011],{"class":50}," \"No input on stdin. Use: ",[33,3013,54],{"class":46},[33,3015,3016],{"class":50}," \u003C file.txt  OR  cat file | ",[33,3018,54],{"class":46},[33,3020,307],{"class":50},[33,3022,3023,3026,3028],{"class":35,"line":93},[33,3024,3025],{"class":273},"    input",[33,3027,412],{"class":269},[33,3029,425],{"class":50},[33,3031,3032],{"class":35,"line":108},[33,3033,3034],{"class":269},"else\n",[33,3036,3037],{"class":35,"line":123},[33,3038,3039],{"class":39},"    # stdin is a pipe or file — read it\n",[33,3041,3042,3044,3046,3048,3051,3054],{"class":35,"line":138},[33,3043,3025],{"class":273},[33,3045,412],{"class":269},[33,3047,1861],{"class":273},[33,3049,3050],{"class":330},"cat",[33,3052,3053],{"class":273},")          ",[33,3055,3056],{"class":39},"# read ALL of stdin\n",[33,3058,3059],{"class":35,"line":153},[33,3060,3061],{"class":39},"    # Or line by line:\n",[33,3063,3064],{"class":35,"line":160},[33,3065,3066],{"class":39},"    # while IFS= read -r line; do ...; done\n",[33,3068,3069],{"class":35,"line":166},[33,3070,3071],{"class":269},"fi\n",[33,3073,3074],{"class":35,"line":193},[33,3075,157],{"emptyLinePlaceholder":156},[33,3077,3078],{"class":35,"line":207},[33,3079,3080],{"class":39},"# ── Common pattern: read from file OR stdin ──\n",[33,3082,3083],{"class":35,"line":216},[33,3084,3085],{"class":39},"# If a file is given as arg, read from it; otherwise read from stdin.\n",[33,3087,3088,3090,3092,3094,3096,3098,3100],{"class":35,"line":230},[33,3089,2987],{"class":269},[33,3091,1290],{"class":273},[33,3093,145],{"class":46},[33,3095,1295],{"class":269},[33,3097,1298],{"class":46},[33,3099,1301],{"class":273},[33,3101,2999],{"class":269},[33,3103,3104,3107,3109,3111,3113],{"class":35,"line":241},[33,3105,3106],{"class":273},"    input_file",[33,3108,412],{"class":269},[33,3110,57],{"class":50},[33,3112,70],{"class":46},[33,3114,307],{"class":50},[33,3116,3117,3120,3123,3125,3128,3130,3133,3136,3139,3141,3144,3146,3148,3150,3152,3154,3156],{"class":35,"line":255},[33,3118,3119],{"class":273},"    [[ ",[33,3121,3122],{"class":269},"-f",[33,3124,51],{"class":50},[33,3126,3127],{"class":273},"$input_file",[33,3129,57],{"class":50},[33,3131,3132],{"class":273}," ]] ",[33,3134,3135],{"class":269},"||",[33,3137,3138],{"class":273}," { ",[33,3140,47],{"class":46},[33,3142,3143],{"class":50}," \"not found: ",[33,3145,3127],{"class":273},[33,3147,57],{"class":50},[33,3149,2153],{"class":269},[33,3151,286],{"class":273},[33,3153,549],{"class":46},[33,3155,726],{"class":46},[33,3157,3158],{"class":273},"; }\n",[33,3160,3161,3163,3166,3168,3171,3174,3177,3179],{"class":35,"line":260},[33,3162,2382],{"class":269},[33,3164,3165],{"class":273}," IFS",[33,3167,412],{"class":269},[33,3169,3170],{"class":46}," read",[33,3172,3173],{"class":46}," -r",[33,3175,3176],{"class":50}," line",[33,3178,286],{"class":273},[33,3180,473],{"class":269},[33,3182,3183,3186,3188,3191],{"class":35,"line":266},[33,3184,3185],{"class":330},"        process",[33,3187,51],{"class":50},[33,3189,3190],{"class":273},"$line",[33,3192,307],{"class":50},[33,3194,3195,3198,3201,3203,3205],{"class":35,"line":295},[33,3196,3197],{"class":269},"    done",[33,3199,3200],{"class":269}," \u003C",[33,3202,51],{"class":50},[33,3204,3127],{"class":273},[33,3206,307],{"class":50},[33,3208,3209],{"class":35,"line":310},[33,3210,3034],{"class":269},[33,3212,3213,3215,3217,3219,3221,3223,3225,3227],{"class":35,"line":316},[33,3214,2382],{"class":269},[33,3216,3165],{"class":273},[33,3218,412],{"class":269},[33,3220,3170],{"class":46},[33,3222,3173],{"class":46},[33,3224,3176],{"class":50},[33,3226,286],{"class":273},[33,3228,473],{"class":269},[33,3230,3231,3233,3235,3237],{"class":35,"line":321},[33,3232,3185],{"class":330},[33,3234,51],{"class":50},[33,3236,3190],{"class":273},[33,3238,307],{"class":50},[33,3240,3241],{"class":35,"line":327},[33,3242,2534],{"class":269},[33,3244,3245],{"class":35,"line":337},[33,3246,3071],{"class":269},[33,3248,3249],{"class":35,"line":354},[33,3250,157],{"emptyLinePlaceholder":156},[33,3252,3253],{"class":35,"line":360},[33,3254,3255],{"class":39},"# ── Check if stdout is a terminal ──\n",[33,3257,3258,3260,3262,3264,3266,3268],{"class":35,"line":634},[33,3259,2987],{"class":269},[33,3261,1290],{"class":273},[33,3263,2992],{"class":269},[33,3265,726],{"class":46},[33,3267,1301],{"class":273},[33,3269,2999],{"class":269},[33,3271,3272],{"class":35,"line":649},[33,3273,3274],{"class":39},"    # stdout is a terminal — use colors\n",[33,3276,3277,3280,3282,3285,3288],{"class":35,"line":658},[33,3278,3279],{"class":273},"    RED",[33,3281,412],{"class":269},[33,3283,3284],{"class":50},"$'",[33,3286,3287],{"class":46},"\\e",[33,3289,3290],{"class":50},"[31m'\n",[33,3292,3293,3296,3298,3300,3302],{"class":35,"line":663},[33,3294,3295],{"class":273},"    RESET",[33,3297,412],{"class":269},[33,3299,3284],{"class":50},[33,3301,3287],{"class":46},[33,3303,3304],{"class":50},"[0m'\n",[33,3306,3307],{"class":35,"line":674},[33,3308,3034],{"class":269},[33,3310,3311],{"class":35,"line":689},[33,3312,3313],{"class":39},"    # stdout is piped\u002Fredirected — no colors\n",[33,3315,3316,3318,3320],{"class":35,"line":696},[33,3317,3279],{"class":273},[33,3319,412],{"class":269},[33,3321,425],{"class":50},[33,3323,3324,3326,3328],{"class":35,"line":701},[33,3325,3295],{"class":273},[33,3327,412],{"class":269},[33,3329,425],{"class":50},[33,3331,3332],{"class":35,"line":707},[33,3333,3071],{"class":269},[33,3335,3336,3338,3341,3344,3347,3350],{"class":35,"line":712},[33,3337,47],{"class":46},[33,3339,3340],{"class":50}," \"${",[33,3342,3343],{"class":273},"RED",[33,3345,3346],{"class":50},"}Error${",[33,3348,3349],{"class":273},"RESET",[33,3351,3352],{"class":50},"}: something went wrong\"\n",[15,3354,3356,3357,3360],{"id":3355},"anti-pattern-getopt-external-command","Anti-Pattern: ",[30,3358,3359],{},"getopt"," (External Command)",[20,3362,3363],{"language":22},[24,3364,3366],{"className":26,"code":3365,"language":22,"meta":28,"style":28},"# ❌ AVOID — external getopt is unreliable across systems\n# GNU getopt: supports long options, reorders args\n# BSD getopt: different behavior, no long options\n# The two are INCOMPATIBLE — scripts break across platforms.\n\ngetopt -o \"vo:\" -l \"verbose,output:\" -- \"$@\"\n# GNU: works. BSD: might not. Different error handling. Unpredictable.\n\n# ✅ USE getopts (built-in) for short options\n# ✅ USE manual while\u002Fcase parsing for long options\n# ✅ USE argbash (code generator) for complex CLIs: brew install argbash\n",[30,3367,3368,3373,3378,3383,3388,3392,3414,3419,3423,3428,3433],{"__ignoreMap":28},[33,3369,3370],{"class":35,"line":36},[33,3371,3372],{"class":39},"# ❌ AVOID — external getopt is unreliable across systems\n",[33,3374,3375],{"class":35,"line":43},[33,3376,3377],{"class":39},"# GNU getopt: supports long options, reorders args\n",[33,3379,3380],{"class":35,"line":63},[33,3381,3382],{"class":39},"# BSD getopt: different behavior, no long options\n",[33,3384,3385],{"class":35,"line":78},[33,3386,3387],{"class":39},"# The two are INCOMPATIBLE — scripts break across platforms.\n",[33,3389,3390],{"class":35,"line":93},[33,3391,157],{"emptyLinePlaceholder":156},[33,3393,3394,3396,3398,3400,3403,3406,3408,3410,3412],{"class":35,"line":108},[33,3395,3359],{"class":330},[33,3397,878],{"class":46},[33,3399,1017],{"class":50},[33,3401,3402],{"class":46}," -l",[33,3404,3405],{"class":50}," \"verbose,output:\"",[33,3407,172],{"class":46},[33,3409,51],{"class":50},[33,3411,115],{"class":46},[33,3413,307],{"class":50},[33,3415,3416],{"class":35,"line":123},[33,3417,3418],{"class":39},"# GNU: works. BSD: might not. Different error handling. Unpredictable.\n",[33,3420,3421],{"class":35,"line":138},[33,3422,157],{"emptyLinePlaceholder":156},[33,3424,3425],{"class":35,"line":153},[33,3426,3427],{"class":39},"# ✅ USE getopts (built-in) for short options\n",[33,3429,3430],{"class":35,"line":160},[33,3431,3432],{"class":39},"# ✅ USE manual while\u002Fcase parsing for long options\n",[33,3434,3435],{"class":35,"line":166},[33,3436,3437],{"class":39},"# ✅ USE argbash (code generator) for complex CLIs: brew install argbash\n",[15,3439,3441],{"id":3440},"tips-tricks","💡 Tips & Tricks",[20,3443,3444],{"language":22},[24,3445,3447],{"className":26,"code":3446,"language":22,"meta":28,"style":28},"# ── `--flag=value` extraction pattern ──\ncase \"$1\" in\n    --output=*) output=\"${1#--output=}\" ;;  # ${1#--output=} strips the prefix\n    --port=*)   port=\"${1#--port=}\" ;;\nesac\n\n# ── Re-parse with getopts (reset OPTIND) ──\n# If you need to call getopts in a function (after a previous getopts loop):\nparse_options() {\n    OPTIND=1   # MUST reset — getopts uses OPTIND globally\n    while getopts \"vo:\" opt; do\n        # ...\n    done\n}\n\n# ── Validate numeric args ──\nvalidate_int() {\n    local var=$1\n    [[ \"$var\" =~ ^[0-9]+$ ]] || die \"$var is not a positive integer\"\n}\ncount=\"${1:-}\"\nvalidate_int \"$count\"\n\n# ── Accept multiple values for one flag (--include *.py --include *.sh) ──\nincludes=()\nwhile [[ $# -gt 0 ]]; do\n    case \"$1\" in\n        --include) includes+=(\"$2\"); shift 2 ;;\n        *)         break ;;\n    esac\ndone\n\n# ── Tab completion helper: output --help in a parseable format ──\n# For bash completion, add this to ~\u002F.bashrc:\n# complete -F _my_script myscript\n# _my_script() {\n#     local cur=\"${COMP_WORDS[COMP_CWORD]}\"\n#     COMPREPLY=( $(compgen -W \"build test deploy\" -- \"$cur\") )\n# }\n",[30,3448,3449,3454,3466,3498,3529,3533,3537,3542,3547,3554,3567,3581,3586,3590,3594,3598,3603,3610,3623,3652,3656,3672,3682,3686,3691,3701,3717,3729,3758,3767,3771,3775,3779,3784,3789,3794,3799,3804,3809],{"__ignoreMap":28},[33,3450,3451],{"class":35,"line":36},[33,3452,3453],{"class":39},"# ── `--flag=value` extraction pattern ──\n",[33,3455,3456,3458,3460,3462,3464],{"class":35,"line":43},[33,3457,2235],{"class":269},[33,3459,51],{"class":50},[33,3461,70],{"class":46},[33,3463,57],{"class":50},[33,3465,522],{"class":269},[33,3467,3468,3471,3473,3475,3477,3479,3481,3483,3485,3487,3489,3491,3493,3495],{"class":35,"line":63},[33,3469,3470],{"class":527},"    --output=",[33,3472,1515],{"class":269},[33,3474,565],{"class":269},[33,3476,586],{"class":273},[33,3478,412],{"class":269},[33,3480,57],{"class":50},[33,3482,1528],{"class":46},[33,3484,1531],{"class":269},[33,3486,1398],{"class":273},[33,3488,412],{"class":50},[33,3490,1538],{"class":46},[33,3492,57],{"class":50},[33,3494,2176],{"class":273},[33,3496,3497],{"class":39},"# ${1#--output=} strips the prefix\n",[33,3499,3500,3503,3505,3507,3510,3512,3514,3516,3518,3521,3523,3525,3527],{"class":35,"line":78},[33,3501,3502],{"class":527},"    --port=",[33,3504,1515],{"class":269},[33,3506,565],{"class":269},[33,3508,3509],{"class":273},"   port",[33,3511,412],{"class":269},[33,3513,57],{"class":50},[33,3515,1528],{"class":46},[33,3517,1531],{"class":269},[33,3519,3520],{"class":273},"--port",[33,3522,412],{"class":50},[33,3524,1538],{"class":46},[33,3526,57],{"class":50},[33,3528,576],{"class":273},[33,3530,3531],{"class":35,"line":93},[33,3532,2349],{"class":269},[33,3534,3535],{"class":35,"line":108},[33,3536,157],{"emptyLinePlaceholder":156},[33,3538,3539],{"class":35,"line":123},[33,3540,3541],{"class":39},"# ── Re-parse with getopts (reset OPTIND) ──\n",[33,3543,3544],{"class":35,"line":138},[33,3545,3546],{"class":39},"# If you need to call getopts in a function (after a previous getopts loop):\n",[33,3548,3549,3552],{"class":35,"line":153},[33,3550,3551],{"class":330},"parse_options",[33,3553,334],{"class":273},[33,3555,3556,3559,3561,3564],{"class":35,"line":160},[33,3557,3558],{"class":273},"    OPTIND",[33,3560,412],{"class":269},[33,3562,3563],{"class":50},"1",[33,3565,3566],{"class":39},"   # MUST reset — getopts uses OPTIND globally\n",[33,3568,3569,3571,3573,3575,3577,3579],{"class":35,"line":166},[33,3570,2382],{"class":269},[33,3572,462],{"class":46},[33,3574,1017],{"class":50},[33,3576,468],{"class":50},[33,3578,286],{"class":273},[33,3580,473],{"class":269},[33,3582,3583],{"class":35,"line":193},[33,3584,3585],{"class":39},"        # ...\n",[33,3587,3588],{"class":35,"line":207},[33,3589,2534],{"class":269},[33,3591,3592],{"class":35,"line":216},[33,3593,357],{"class":273},[33,3595,3596],{"class":35,"line":230},[33,3597,157],{"emptyLinePlaceholder":156},[33,3599,3600],{"class":35,"line":241},[33,3601,3602],{"class":39},"# ── Validate numeric args ──\n",[33,3604,3605,3608],{"class":35,"line":255},[33,3606,3607],{"class":330},"validate_int",[33,3609,334],{"class":273},[33,3611,3612,3614,3617,3619],{"class":35,"line":260},[33,3613,2370],{"class":269},[33,3615,3616],{"class":273}," var",[33,3618,412],{"class":269},[33,3620,3622],{"class":3621},"sCrzJ","$1\n",[33,3624,3625,3627,3629,3632,3634,3637,3640,3642,3645,3647,3649],{"class":35,"line":266},[33,3626,3119],{"class":273},[33,3628,57],{"class":50},[33,3630,3631],{"class":273},"$var",[33,3633,57],{"class":50},[33,3635,3636],{"class":269}," =~",[33,3638,3639],{"class":273}," ^[0-9]+$ ]] ",[33,3641,3135],{"class":269},[33,3643,3644],{"class":330}," die",[33,3646,51],{"class":50},[33,3648,3631],{"class":273},[33,3650,3651],{"class":50}," is not a positive integer\"\n",[33,3653,3654],{"class":35,"line":295},[33,3655,357],{"class":273},[33,3657,3658,3660,3662,3664,3666,3668,3670],{"class":35,"line":310},[33,3659,430],{"class":273},[33,3661,412],{"class":269},[33,3663,57],{"class":50},[33,3665,1528],{"class":46},[33,3667,1804],{"class":269},[33,3669,1538],{"class":46},[33,3671,307],{"class":50},[33,3673,3674,3676,3678,3680],{"class":35,"line":316},[33,3675,3607],{"class":330},[33,3677,51],{"class":50},[33,3679,780],{"class":273},[33,3681,307],{"class":50},[33,3683,3684],{"class":35,"line":321},[33,3685,157],{"emptyLinePlaceholder":156},[33,3687,3688],{"class":35,"line":327},[33,3689,3690],{"class":39},"# ── Accept multiple values for one flag (--include *.py --include *.sh) ──\n",[33,3692,3693,3696,3698],{"class":35,"line":337},[33,3694,3695],{"class":273},"includes",[33,3697,412],{"class":269},[33,3699,3700],{"class":273},"()\n",[33,3702,3703,3705,3707,3709,3711,3713,3715],{"class":35,"line":354},[33,3704,459],{"class":269},[33,3706,1290],{"class":273},[33,3708,145],{"class":46},[33,3710,1295],{"class":269},[33,3712,1298],{"class":46},[33,3714,1301],{"class":273},[33,3716,473],{"class":269},[33,3718,3719,3721,3723,3725,3727],{"class":35,"line":360},[33,3720,512],{"class":269},[33,3722,51],{"class":50},[33,3724,70],{"class":46},[33,3726,57],{"class":50},[33,3728,522],{"class":269},[33,3730,3731,3734,3736,3739,3741,3743,3745,3747,3749,3752,3754,3756],{"class":35,"line":634},[33,3732,3733],{"class":527},"        --include",[33,3735,565],{"class":269},[33,3737,3738],{"class":273}," includes",[33,3740,1699],{"class":269},[33,3742,1702],{"class":273},[33,3744,57],{"class":50},[33,3746,85],{"class":46},[33,3748,57],{"class":50},[33,3750,3751],{"class":273},"); ",[33,3753,210],{"class":46},[33,3755,235],{"class":46},[33,3757,576],{"class":273},[33,3759,3760,3762,3765],{"class":35,"line":649},[33,3761,2170],{"class":269},[33,3763,3764],{"class":269},"         break",[33,3766,576],{"class":273},[33,3768,3769],{"class":35,"line":658},[33,3770,704],{"class":269},[33,3772,3773],{"class":35,"line":663},[33,3774,313],{"class":269},[33,3776,3777],{"class":35,"line":674},[33,3778,157],{"emptyLinePlaceholder":156},[33,3780,3781],{"class":35,"line":689},[33,3782,3783],{"class":39},"# ── Tab completion helper: output --help in a parseable format ──\n",[33,3785,3786],{"class":35,"line":696},[33,3787,3788],{"class":39},"# For bash completion, add this to ~\u002F.bashrc:\n",[33,3790,3791],{"class":35,"line":701},[33,3792,3793],{"class":39},"# complete -F _my_script myscript\n",[33,3795,3796],{"class":35,"line":707},[33,3797,3798],{"class":39},"# _my_script() {\n",[33,3800,3801],{"class":35,"line":712},[33,3802,3803],{"class":39},"#     local cur=\"${COMP_WORDS[COMP_CWORD]}\"\n",[33,3805,3806],{"class":35,"line":735},[33,3807,3808],{"class":39},"#     COMPREPLY=( $(compgen -W \"build test deploy\" -- \"$cur\") )\n",[33,3810,3811],{"class":35,"line":740},[33,3812,3813],{"class":39},"# }\n",[15,3815,3817],{"id":3816},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,3819,3820],{"language":22},[24,3821,3823],{"className":26,"code":3822,"language":22,"meta":28,"style":28},"# ── getopts stops at first non-option ──\n# .\u002Fscript.sh file.txt -v  →  getopts doesn't see -v (stops at file.txt)\n# Options must come BEFORE positional args (or use manual parsing).\n\n# ── getopts only does short options ──\n# No --verbose. Use manual parsing or argbash for long options.\n\n# ── `shift` beyond $# errors ──\n# shift with no args left: \"shift: shift count out of range\"\n# Guard: [[ $# -gt 0 ]] && shift  OR  shift || true\n\n# ── `$10` is `$1` followed by `0` ──\necho $10     # \"$1\" + \"0\" = \"foo0\" (if $1 is \"foo\")\necho ${10}   # the actual 10th arg\n\n# ── `$0` isn't always the script name ──\n# If called via symlink: $0 is the symlink path (not the real script)\n# If sourced: $0 is the shell (e.g., \"bash\")\n# Use BASH_SOURCE[0] for the script's actual path.\n\n# ── `--` stops option parsing ──\n# .\u002Fscript.sh -v -- -file.txt  →  -v is a flag, -file.txt is positional\n# The -- is consumed by getopts, but in manual parsing you need to handle it.\n\n# ── OPTIND is global ──\n# If you call getopts in a function after a previous getopts, OPTIND is stale.\n# Reset: OPTIND=1  (or OPTIND=0 in some versions — but 1 is standard)\n",[30,3824,3825,3830,3835,3840,3844,3849,3854,3858,3863,3868,3873,3877,3882,3892,3902,3906,3911,3916,3921,3926,3930,3935,3940,3945,3949,3954,3959],{"__ignoreMap":28},[33,3826,3827],{"class":35,"line":36},[33,3828,3829],{"class":39},"# ── getopts stops at first non-option ──\n",[33,3831,3832],{"class":35,"line":43},[33,3833,3834],{"class":39},"# .\u002Fscript.sh file.txt -v  →  getopts doesn't see -v (stops at file.txt)\n",[33,3836,3837],{"class":35,"line":63},[33,3838,3839],{"class":39},"# Options must come BEFORE positional args (or use manual parsing).\n",[33,3841,3842],{"class":35,"line":78},[33,3843,157],{"emptyLinePlaceholder":156},[33,3845,3846],{"class":35,"line":93},[33,3847,3848],{"class":39},"# ── getopts only does short options ──\n",[33,3850,3851],{"class":35,"line":108},[33,3852,3853],{"class":39},"# No --verbose. Use manual parsing or argbash for long options.\n",[33,3855,3856],{"class":35,"line":123},[33,3857,157],{"emptyLinePlaceholder":156},[33,3859,3860],{"class":35,"line":138},[33,3861,3862],{"class":39},"# ── `shift` beyond $# errors ──\n",[33,3864,3865],{"class":35,"line":153},[33,3866,3867],{"class":39},"# shift with no args left: \"shift: shift count out of range\"\n",[33,3869,3870],{"class":35,"line":160},[33,3871,3872],{"class":39},"# Guard: [[ $# -gt 0 ]] && shift  OR  shift || true\n",[33,3874,3875],{"class":35,"line":166},[33,3876,157],{"emptyLinePlaceholder":156},[33,3878,3879],{"class":35,"line":193},[33,3880,3881],{"class":39},"# ── `$10` is `$1` followed by `0` ──\n",[33,3883,3884,3886,3889],{"class":35,"line":207},[33,3885,47],{"class":46},[33,3887,3888],{"class":273}," $10     ",[33,3890,3891],{"class":39},"# \"$1\" + \"0\" = \"foo0\" (if $1 is \"foo\")\n",[33,3893,3894,3896,3899],{"class":35,"line":216},[33,3895,47],{"class":46},[33,3897,3898],{"class":3621}," ${10}",[33,3900,3901],{"class":39},"   # the actual 10th arg\n",[33,3903,3904],{"class":35,"line":230},[33,3905,157],{"emptyLinePlaceholder":156},[33,3907,3908],{"class":35,"line":241},[33,3909,3910],{"class":39},"# ── `$0` isn't always the script name ──\n",[33,3912,3913],{"class":35,"line":255},[33,3914,3915],{"class":39},"# If called via symlink: $0 is the symlink path (not the real script)\n",[33,3917,3918],{"class":35,"line":260},[33,3919,3920],{"class":39},"# If sourced: $0 is the shell (e.g., \"bash\")\n",[33,3922,3923],{"class":35,"line":266},[33,3924,3925],{"class":39},"# Use BASH_SOURCE[0] for the script's actual path.\n",[33,3927,3928],{"class":35,"line":295},[33,3929,157],{"emptyLinePlaceholder":156},[33,3931,3932],{"class":35,"line":310},[33,3933,3934],{"class":39},"# ── `--` stops option parsing ──\n",[33,3936,3937],{"class":35,"line":316},[33,3938,3939],{"class":39},"# .\u002Fscript.sh -v -- -file.txt  →  -v is a flag, -file.txt is positional\n",[33,3941,3942],{"class":35,"line":321},[33,3943,3944],{"class":39},"# The -- is consumed by getopts, but in manual parsing you need to handle it.\n",[33,3946,3947],{"class":35,"line":327},[33,3948,157],{"emptyLinePlaceholder":156},[33,3950,3951],{"class":35,"line":337},[33,3952,3953],{"class":39},"# ── OPTIND is global ──\n",[33,3955,3956],{"class":35,"line":354},[33,3957,3958],{"class":39},"# If you call getopts in a function after a previous getopts, OPTIND is stale.\n",[33,3960,3961],{"class":35,"line":360},[33,3962,3963],{"class":39},"# Reset: OPTIND=1  (or OPTIND=0 in some versions — but 1 is standard)\n",[15,3965,3967],{"id":3966},"quick-quiz","🧠 Quick Quiz",[3969,3970,3971,3972,3974],"p",{},"Why does this fail to parse the ",[30,3973,1331],{}," flag?",[20,3976,3977],{"language":22},[24,3978,3980],{"className":26,"code":3979,"language":22,"meta":28,"style":28},"while getopts \"v-:\" opt; do\n    case \"$opt\" in\n        v) verbose=true ;;\n        -) echo \"long option: $OPTARG\" ;;\n    esac\ndone\n",[30,3981,3982,3997,4009,4023,4041,4045],{"__ignoreMap":28},[33,3983,3984,3986,3988,3991,3993,3995],{"class":35,"line":36},[33,3985,459],{"class":269},[33,3987,462],{"class":46},[33,3989,3990],{"class":50}," \"v-:\"",[33,3992,468],{"class":50},[33,3994,286],{"class":273},[33,3996,473],{"class":269},[33,3998,3999,4001,4003,4005,4007],{"class":35,"line":43},[33,4000,512],{"class":269},[33,4002,51],{"class":50},[33,4004,517],{"class":273},[33,4006,57],{"class":50},[33,4008,522],{"class":269},[33,4010,4011,4013,4015,4017,4019,4021],{"class":35,"line":63},[33,4012,562],{"class":527},[33,4014,565],{"class":269},[33,4016,568],{"class":273},[33,4018,412],{"class":269},[33,4020,573],{"class":50},[33,4022,576],{"class":273},[33,4024,4025,4027,4029,4032,4035,4037,4039],{"class":35,"line":78},[33,4026,1643],{"class":527},[33,4028,565],{"class":269},[33,4030,4031],{"class":46}," echo",[33,4033,4034],{"class":50}," \"long option: ",[33,4036,593],{"class":273},[33,4038,57],{"class":50},[33,4040,576],{"class":273},[33,4042,4043],{"class":35,"line":93},[33,4044,704],{"class":269},[33,4046,4047],{"class":35,"line":108},[33,4048,313],{"class":269},[4050,4051,4052,4056,4080,4104,4110,4150],"details",{},[4053,4054,4055],"summary",{},"Answer",[3969,4057,4058,4060,4061,4065,4066,4069,4070,4073,4074,4076,4077,4079],{},[30,4059,377],{}," does ",[4062,4063,4064],"strong",{},"not"," support long options. The ",[30,4067,4068],{},"-"," in ",[30,4071,4072],{},"\"v-:\""," is treated as a regular short option named ",[30,4075,4068],{},", not as a prefix for long options. ",[30,4078,377],{}," only parses single-character options.",[3969,4081,4082,4083,4086,4087,4089,4090,4093,4094,4096,4097,4100,4101,4103],{},"When you run ",[30,4084,4085],{},".\u002Fscript.sh --verbose",", getopts sees ",[30,4088,4068],{}," as the first character after ",[30,4091,4092],{},"--",", and since ",[30,4095,4068],{}," is in the optstring, it might match it — but ",[30,4098,4099],{},"OPTARG"," won't contain ",[30,4102,409],{}," in a useful way. This is a common misconception.",[3969,4105,4106,4109],{},[4062,4107,4108],{},"The correct approaches",":",[4111,4112,4113,4125,4138],"ol",{},[4114,4115,4116,4117,4124],"li",{},"Use ",[4062,4118,4119,4120,4123],{},"manual ",[30,4121,4122],{},"while\u002Fcase"," parsing"," for long options (as shown above).",[4114,4126,4116,4127,4130,4131,4134,4135,4137],{},[4062,4128,4129],{},"external tools"," like ",[30,4132,4133],{},"argbash"," (code generator) or ",[30,4136,3359],{}," (GNU, not portable).",[4114,4139,4116,4140,4143,4144,4146,4147,4149],{},[4062,4141,4142],{},"short options only"," with ",[30,4145,377],{}," and document ",[30,4148,1331],{}," as not supported.",[3969,4151,4152,4155,4156,4158,4159,4162,4163,4166,4167,4169,4170,4173],{},[4062,4153,4154],{},"The lesson",": ",[30,4157,377],{}," is for short options only (",[30,4160,4161],{},"-v",", ",[30,4164,4165],{},"-o VALUE","). For ",[30,4168,1331],{},", use manual ",[30,4171,4172],{},"while [[ $# -gt 0 ]]; do case \"$1\" in ... esac; done"," parsing.",[4175,4176,4177],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .svAP2, html code.shiki .svAP2{--shiki-default:#032F62;--shiki-github-dark:#DBEDFF}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":28,"searchDepth":43,"depth":43,"links":4179},[4180,4181,4183,4185,4187,4188,4189,4190,4192,4193,4194],{"id":17,"depth":43,"text":18},{"id":374,"depth":43,"text":4182},"getopts: The Built-In Option Parser",{"id":797,"depth":43,"text":4184},"getopts Internals: State Machine",{"id":990,"depth":43,"text":4186},"Anti-Pattern: Forgetting shift After getopts",{"id":1220,"depth":43,"text":1221},{"id":1810,"depth":43,"text":1811},{"id":2969,"depth":43,"text":2970},{"id":3355,"depth":43,"text":4191},"Anti-Pattern: getopt (External Command)",{"id":3440,"depth":43,"text":3441},{"id":3816,"depth":43,"text":3817},{"id":3966,"depth":43,"text":3967},"Deep-dive into Bash argument parsing: positional args, getopts state machine, manual long-option parsing with while\u002Fcase, subcommand dispatch, stdin detection, and production CLI patterns. Code-first reference for senior engineers.","md",{},"\u002Fbash\u002F11-command-line-arguments",{"title":5,"description":4195},"bash\u002F11-command-line-arguments","NZCZUomSYDDtW7GRTW6ynK2fUhMt_KlwM_F0WT8LnbA",1789924644817]