[{"data":1,"prerenderedAt":4858},["ShallowReactive",2],{"page-\u002Fbash\u002F05-functions-and-scope":3},{"id":4,"title":5,"body":6,"description":4851,"extension":4852,"meta":4853,"navigation":86,"path":4854,"seo":4855,"stem":4856,"__hash__":4857},"content\u002Fbash\u002F05-functions-and-scope.md","Bash 05 — Function Internals, Scope Mechanics & Signal Traps",{"type":7,"value":8,"toc":4834},"minimark",[9,14,19,474,478,937,949,1163,1170,1439,1443,1789,1793,2329,2333,3010,3014,3332,3336,3610,3614,3877,3881,4365,4369,4624,4628,4632,4670,4830],[10,11,13],"h1",{"id":12},"_05-function-internals-scope-mechanics-signal-traps","05 — Function Internals, Scope Mechanics & Signal Traps",[15,16,18],"h2",{"id":17},"function-definition-and-the-call-stack","Function Definition and the Call Stack",[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","# ── Two syntaxes ──\n# POSIX-compatible (preferred — works in sh too):\ngreet() {\n    echo \"Hello, $1!\"\n}\n\n# C-style (Bash-only, `function` keyword):\nfunction greet() {\n    echo \"Hello, $1!\"\n}\n# Note: `function greet` (without ()) is Bash-only and doesn't require ().\n\n# ── The call stack: FUNCNAME, BASH_SOURCE, BASH_LINENO ──\ntrace() {\n    local depth=${#FUNCNAME[@]}    # number of frames in the call stack\n    for ((i = 0; i \u003C depth - 1; i++)); do\n        printf '%*s→ %s() at %s:%d\\n' \\\n            \"$((i * 2))\" \"\" \\\n            \"${FUNCNAME[i]}\" \\\n            \"$(basename \"${BASH_SOURCE[i]}\")\" \\\n            \"${BASH_LINENO[i]}\"\n    done\n}\n\nouter() { inner; }\ninner() { trace; }\nouter\n# Output:\n# → outer() at script.sh:12\n#   → inner() at script.sh:11\n#     → trace() at script.sh:7\n\n# ── Functions get their own $1, $2, $@ (independent of the script's args) ──\nshow_args() {\n    echo \"function args ($#): $*\"     # the function's args, NOT the script's\n    echo \"script args: $SCRIPT_ARGS\"  # script args must be passed explicitly\n}\n\nSCRIPT_ARGS=\"$@\"\nshow_args \"alpha\" \"beta\"   # function sees: alpha beta\n# The script's $@ is NOT visible inside the function as $@ — only the function's $@.\n","",[30,31,32,41,47,58,75,81,88,94,106,117,122,128,133,139,147,177,218,230,253,267,287,298,304,309,314,329,340,346,352,358,364,370,375,381,389,412,428,433,438,454,468],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","# ── Two syntaxes ──\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"# POSIX-compatible (preferred — works in sh too):\n",[33,48,50,54],{"class":35,"line":49},3,[33,51,53],{"class":52},"sIsaT","greet",[33,55,57],{"class":56},"ssxIu","() {\n",[33,59,61,65,69,72],{"class":35,"line":60},4,[33,62,64],{"class":63},"snvgF","    echo",[33,66,68],{"class":67},"sJ6F3"," \"Hello, ",[33,70,71],{"class":63},"$1",[33,73,74],{"class":67},"!\"\n",[33,76,78],{"class":35,"line":77},5,[33,79,80],{"class":56},"}\n",[33,82,84],{"class":35,"line":83},6,[33,85,87],{"emptyLinePlaceholder":86},true,"\n",[33,89,91],{"class":35,"line":90},7,[33,92,93],{"class":39},"# C-style (Bash-only, `function` keyword):\n",[33,95,97,101,104],{"class":35,"line":96},8,[33,98,100],{"class":99},"svdQ7","function",[33,102,103],{"class":52}," greet",[33,105,57],{"class":56},[33,107,109,111,113,115],{"class":35,"line":108},9,[33,110,64],{"class":63},[33,112,68],{"class":67},[33,114,71],{"class":63},[33,116,74],{"class":67},[33,118,120],{"class":35,"line":119},10,[33,121,80],{"class":56},[33,123,125],{"class":35,"line":124},11,[33,126,127],{"class":39},"# Note: `function greet` (without ()) is Bash-only and doesn't require ().\n",[33,129,131],{"class":35,"line":130},12,[33,132,87],{"emptyLinePlaceholder":86},[33,134,136],{"class":35,"line":135},13,[33,137,138],{"class":39},"# ── The call stack: FUNCNAME, BASH_SOURCE, BASH_LINENO ──\n",[33,140,142,145],{"class":35,"line":141},14,[33,143,144],{"class":52},"trace",[33,146,57],{"class":56},[33,148,150,153,156,159,162,165,168,171,174],{"class":35,"line":149},15,[33,151,152],{"class":99},"    local",[33,154,155],{"class":56}," depth",[33,157,158],{"class":99},"=",[33,160,161],{"class":56},"${",[33,163,164],{"class":99},"#",[33,166,167],{"class":56},"FUNCNAME[",[33,169,170],{"class":99},"@",[33,172,173],{"class":56},"]}    ",[33,175,176],{"class":39},"# number of frames in the call stack\n",[33,178,180,183,186,188,191,194,197,200,203,206,209,212,215],{"class":35,"line":179},16,[33,181,182],{"class":99},"    for",[33,184,185],{"class":56}," ((i ",[33,187,158],{"class":99},[33,189,190],{"class":63}," 0",[33,192,193],{"class":56},"; i ",[33,195,196],{"class":99},"\u003C",[33,198,199],{"class":56}," depth ",[33,201,202],{"class":99},"-",[33,204,205],{"class":63}," 1",[33,207,208],{"class":56},"; i",[33,210,211],{"class":99},"++",[33,213,214],{"class":56},")); ",[33,216,217],{"class":99},"do\n",[33,219,221,224,227],{"class":35,"line":220},17,[33,222,223],{"class":63},"        printf",[33,225,226],{"class":67}," '%*s→ %s() at %s:%d\\n'",[33,228,229],{"class":63}," \\\n",[33,231,233,236,239,242,245,248,251],{"class":35,"line":232},18,[33,234,235],{"class":67},"            \"$((",[33,237,238],{"class":52},"i",[33,240,241],{"class":63}," *",[33,243,244],{"class":63}," 2",[33,246,247],{"class":67},"))\"",[33,249,250],{"class":67}," \"\"",[33,252,229],{"class":63},[33,254,256,259,262,265],{"class":35,"line":255},19,[33,257,258],{"class":67},"            \"${",[33,260,261],{"class":56},"FUNCNAME",[33,263,264],{"class":67},"[i]}\"",[33,266,229],{"class":63},[33,268,270,273,276,279,282,285],{"class":35,"line":269},20,[33,271,272],{"class":67},"            \"$(",[33,274,275],{"class":52},"basename",[33,277,278],{"class":67}," \"${",[33,280,281],{"class":56},"BASH_SOURCE",[33,283,284],{"class":67},"[i]}\")\"",[33,286,229],{"class":63},[33,288,290,292,295],{"class":35,"line":289},21,[33,291,258],{"class":67},[33,293,294],{"class":56},"BASH_LINENO",[33,296,297],{"class":67},"[i]}\"\n",[33,299,301],{"class":35,"line":300},22,[33,302,303],{"class":99},"    done\n",[33,305,307],{"class":35,"line":306},23,[33,308,80],{"class":56},[33,310,312],{"class":35,"line":311},24,[33,313,87],{"emptyLinePlaceholder":86},[33,315,317,320,323,326],{"class":35,"line":316},25,[33,318,319],{"class":52},"outer",[33,321,322],{"class":56},"() { ",[33,324,325],{"class":52},"inner",[33,327,328],{"class":56},"; }\n",[33,330,332,334,336,338],{"class":35,"line":331},26,[33,333,325],{"class":52},[33,335,322],{"class":56},[33,337,144],{"class":52},[33,339,328],{"class":56},[33,341,343],{"class":35,"line":342},27,[33,344,345],{"class":52},"outer\n",[33,347,349],{"class":35,"line":348},28,[33,350,351],{"class":39},"# Output:\n",[33,353,355],{"class":35,"line":354},29,[33,356,357],{"class":39},"# → outer() at script.sh:12\n",[33,359,361],{"class":35,"line":360},30,[33,362,363],{"class":39},"#   → inner() at script.sh:11\n",[33,365,367],{"class":35,"line":366},31,[33,368,369],{"class":39},"#     → trace() at script.sh:7\n",[33,371,373],{"class":35,"line":372},32,[33,374,87],{"emptyLinePlaceholder":86},[33,376,378],{"class":35,"line":377},33,[33,379,380],{"class":39},"# ── Functions get their own $1, $2, $@ (independent of the script's args) ──\n",[33,382,384,387],{"class":35,"line":383},34,[33,385,386],{"class":52},"show_args",[33,388,57],{"class":56},[33,390,392,394,397,400,403,406,409],{"class":35,"line":391},35,[33,393,64],{"class":63},[33,395,396],{"class":67}," \"function args (",[33,398,399],{"class":63},"$#",[33,401,402],{"class":67},"): ",[33,404,405],{"class":63},"$*",[33,407,408],{"class":67},"\"",[33,410,411],{"class":39},"     # the function's args, NOT the script's\n",[33,413,415,417,420,423,425],{"class":35,"line":414},36,[33,416,64],{"class":63},[33,418,419],{"class":67}," \"script args: ",[33,421,422],{"class":56},"$SCRIPT_ARGS",[33,424,408],{"class":67},[33,426,427],{"class":39},"  # script args must be passed explicitly\n",[33,429,431],{"class":35,"line":430},37,[33,432,80],{"class":56},[33,434,436],{"class":35,"line":435},38,[33,437,87],{"emptyLinePlaceholder":86},[33,439,441,444,446,448,451],{"class":35,"line":440},39,[33,442,443],{"class":56},"SCRIPT_ARGS",[33,445,158],{"class":99},[33,447,408],{"class":67},[33,449,450],{"class":63},"$@",[33,452,453],{"class":67},"\"\n",[33,455,457,459,462,465],{"class":35,"line":456},40,[33,458,386],{"class":52},[33,460,461],{"class":67}," \"alpha\"",[33,463,464],{"class":67}," \"beta\"",[33,466,467],{"class":39},"   # function sees: alpha beta\n",[33,469,471],{"class":35,"line":470},41,[33,472,473],{"class":39},"# The script's $@ is NOT visible inside the function as $@ — only the function's $@.\n",[15,475,477],{"id":476},"return-values-three-mechanisms","Return Values: Three Mechanisms",[20,479,480],{"language":22},[24,481,483],{"className":26,"code":482,"language":22,"meta":28,"style":28},"# ── 1. Exit status (return) — 0-255, for boolean\u002Fsuccess\u002Ffailure ──\nis_even() {\n    (( $1 % 2 == 0 ))    # arithmetic: returns exit 0 if true, 1 if false\n    # return is implicit — uses the last command's exit status\n}\n\nif is_even 4; then echo \"even\"; fi    # calls is_even, checks $?\n\n# Explicit return:\nis_even() {\n    if (( $1 % 2 == 0 )); then\n        return 0    # success\u002Ftrue\n    else\n        return 1   # failure\u002Ffalse\n    fi\n}\n\n# ⚠️ return wraps to 0-255 (modulo 256):\nreturn 300   # actual exit status: 44 (300 % 256)\n# return is for FUNCTIONS. exit is for the SCRIPT. They're different!\n\n# ── 2. stdout capture ($(...)) — for data ──\nget_date() {\n    printf '%s' \"$(date +%Y-%m-%d)\"   # printf (not echo) — no trailing newline in the capture\n}\n\ntoday=$(get_date)                    # captures stdout\necho \"$today\"                        # 2024-01-15\n\n# ⚠️ echo vs printf in function output:\nget_name() { echo \"Alice\"; }        # echo adds \\n — $(...) strips trailing \\n → \"Alice\"\nget_name() { printf '%s' \"Alice\"; } # no \\n — $(...) → \"Alice\" (same result, but explicit)\n\n# ⚠️ Don't use echo for binary\u002Fspecial data — use printf:\nget_bytes() { printf '\\x01\\x02'; }  # raw bytes — echo can't handle \\x00 or \\x01 safely\n\n# ── 3. Nameref (local -n) — for complex data (arrays, multiple values) ──\nparse_version() {\n    local -n _result=$1   # nameref: _result is an alias for the caller's variable\n    local input=$2\n    IFS='.' read -ra _result \u003C\u003C\u003C \"$input\"   # split into array, write via nameref\n}\n\nparse_version parts \"1.2.3\"\necho \"${parts[@]}\"    # 1 2 3 — the caller's `parts` array was populated by the function\n# The function set a variable in the CALLER's scope by name.\n",[30,484,485,490,497,521,526,530,534,565,569,574,580,604,614,619,628,633,637,641,646,657,662,666,671,678,698,702,706,724,740,744,749,767,786,790,795,813,817,822,829,846,858,889,894,899,910,931],{"__ignoreMap":28},[33,486,487],{"class":35,"line":36},[33,488,489],{"class":39},"# ── 1. Exit status (return) — 0-255, for boolean\u002Fsuccess\u002Ffailure ──\n",[33,491,492,495],{"class":35,"line":43},[33,493,494],{"class":52},"is_even",[33,496,57],{"class":56},[33,498,499,502,505,508,510,513,515,518],{"class":35,"line":49},[33,500,501],{"class":56},"    (( ",[33,503,71],{"class":504},"sCrzJ",[33,506,507],{"class":99}," %",[33,509,244],{"class":63},[33,511,512],{"class":99}," ==",[33,514,190],{"class":63},[33,516,517],{"class":56}," ))    ",[33,519,520],{"class":39},"# arithmetic: returns exit 0 if true, 1 if false\n",[33,522,523],{"class":35,"line":60},[33,524,525],{"class":39},"    # return is implicit — uses the last command's exit status\n",[33,527,528],{"class":35,"line":77},[33,529,80],{"class":56},[33,531,532],{"class":35,"line":83},[33,533,87],{"emptyLinePlaceholder":86},[33,535,536,539,542,545,548,551,554,557,559,562],{"class":35,"line":90},[33,537,538],{"class":99},"if",[33,540,541],{"class":52}," is_even",[33,543,544],{"class":63}," 4",[33,546,547],{"class":56},"; ",[33,549,550],{"class":99},"then",[33,552,553],{"class":63}," echo",[33,555,556],{"class":67}," \"even\"",[33,558,547],{"class":56},[33,560,561],{"class":99},"fi",[33,563,564],{"class":39},"    # calls is_even, checks $?\n",[33,566,567],{"class":35,"line":96},[33,568,87],{"emptyLinePlaceholder":86},[33,570,571],{"class":35,"line":108},[33,572,573],{"class":39},"# Explicit return:\n",[33,575,576,578],{"class":35,"line":119},[33,577,494],{"class":52},[33,579,57],{"class":56},[33,581,582,585,588,590,592,594,596,598,601],{"class":35,"line":124},[33,583,584],{"class":99},"    if",[33,586,587],{"class":56}," (( ",[33,589,71],{"class":504},[33,591,507],{"class":99},[33,593,244],{"class":63},[33,595,512],{"class":99},[33,597,190],{"class":63},[33,599,600],{"class":56}," )); ",[33,602,603],{"class":99},"then\n",[33,605,606,609,611],{"class":35,"line":130},[33,607,608],{"class":99},"        return",[33,610,190],{"class":63},[33,612,613],{"class":39},"    # success\u002Ftrue\n",[33,615,616],{"class":35,"line":135},[33,617,618],{"class":99},"    else\n",[33,620,621,623,625],{"class":35,"line":141},[33,622,608],{"class":99},[33,624,205],{"class":63},[33,626,627],{"class":39},"   # failure\u002Ffalse\n",[33,629,630],{"class":35,"line":149},[33,631,632],{"class":99},"    fi\n",[33,634,635],{"class":35,"line":179},[33,636,80],{"class":56},[33,638,639],{"class":35,"line":220},[33,640,87],{"emptyLinePlaceholder":86},[33,642,643],{"class":35,"line":232},[33,644,645],{"class":39},"# ⚠️ return wraps to 0-255 (modulo 256):\n",[33,647,648,651,654],{"class":35,"line":255},[33,649,650],{"class":99},"return",[33,652,653],{"class":63}," 300",[33,655,656],{"class":39},"   # actual exit status: 44 (300 % 256)\n",[33,658,659],{"class":35,"line":269},[33,660,661],{"class":39},"# return is for FUNCTIONS. exit is for the SCRIPT. They're different!\n",[33,663,664],{"class":35,"line":289},[33,665,87],{"emptyLinePlaceholder":86},[33,667,668],{"class":35,"line":300},[33,669,670],{"class":39},"# ── 2. stdout capture ($(...)) — for data ──\n",[33,672,673,676],{"class":35,"line":306},[33,674,675],{"class":52},"get_date",[33,677,57],{"class":56},[33,679,680,683,686,689,692,695],{"class":35,"line":311},[33,681,682],{"class":63},"    printf",[33,684,685],{"class":67}," '%s'",[33,687,688],{"class":67}," \"$(",[33,690,691],{"class":52},"date",[33,693,694],{"class":67}," +%Y-%m-%d)\"",[33,696,697],{"class":39},"   # printf (not echo) — no trailing newline in the capture\n",[33,699,700],{"class":35,"line":316},[33,701,80],{"class":56},[33,703,704],{"class":35,"line":331},[33,705,87],{"emptyLinePlaceholder":86},[33,707,708,711,713,716,718,721],{"class":35,"line":342},[33,709,710],{"class":56},"today",[33,712,158],{"class":99},[33,714,715],{"class":56},"$(",[33,717,675],{"class":52},[33,719,720],{"class":56},")                    ",[33,722,723],{"class":39},"# captures stdout\n",[33,725,726,729,732,735,737],{"class":35,"line":348},[33,727,728],{"class":63},"echo",[33,730,731],{"class":67}," \"",[33,733,734],{"class":56},"$today",[33,736,408],{"class":67},[33,738,739],{"class":39},"                        # 2024-01-15\n",[33,741,742],{"class":35,"line":354},[33,743,87],{"emptyLinePlaceholder":86},[33,745,746],{"class":35,"line":360},[33,747,748],{"class":39},"# ⚠️ echo vs printf in function output:\n",[33,750,751,754,756,758,761,764],{"class":35,"line":366},[33,752,753],{"class":52},"get_name",[33,755,322],{"class":56},[33,757,728],{"class":63},[33,759,760],{"class":67}," \"Alice\"",[33,762,763],{"class":56},"; }        ",[33,765,766],{"class":39},"# echo adds \\n — $(...) strips trailing \\n → \"Alice\"\n",[33,768,769,771,773,776,778,780,783],{"class":35,"line":372},[33,770,753],{"class":52},[33,772,322],{"class":56},[33,774,775],{"class":63},"printf",[33,777,685],{"class":67},[33,779,760],{"class":67},[33,781,782],{"class":56},"; } ",[33,784,785],{"class":39},"# no \\n — $(...) → \"Alice\" (same result, but explicit)\n",[33,787,788],{"class":35,"line":377},[33,789,87],{"emptyLinePlaceholder":86},[33,791,792],{"class":35,"line":383},[33,793,794],{"class":39},"# ⚠️ Don't use echo for binary\u002Fspecial data — use printf:\n",[33,796,797,800,802,804,807,810],{"class":35,"line":391},[33,798,799],{"class":52},"get_bytes",[33,801,322],{"class":56},[33,803,775],{"class":63},[33,805,806],{"class":67}," '\\x01\\x02'",[33,808,809],{"class":56},"; }  ",[33,811,812],{"class":39},"# raw bytes — echo can't handle \\x00 or \\x01 safely\n",[33,814,815],{"class":35,"line":414},[33,816,87],{"emptyLinePlaceholder":86},[33,818,819],{"class":35,"line":430},[33,820,821],{"class":39},"# ── 3. Nameref (local -n) — for complex data (arrays, multiple values) ──\n",[33,823,824,827],{"class":35,"line":435},[33,825,826],{"class":52},"parse_version",[33,828,57],{"class":56},[33,830,831,833,836,839,841,843],{"class":35,"line":440},[33,832,152],{"class":99},[33,834,835],{"class":63}," -n",[33,837,838],{"class":56}," _result",[33,840,158],{"class":99},[33,842,71],{"class":504},[33,844,845],{"class":39},"   # nameref: _result is an alias for the caller's variable\n",[33,847,848,850,853,855],{"class":35,"line":456},[33,849,152],{"class":99},[33,851,852],{"class":56}," input",[33,854,158],{"class":99},[33,856,857],{"class":504},"$2\n",[33,859,860,863,865,868,871,874,876,879,881,884,886],{"class":35,"line":470},[33,861,862],{"class":56},"    IFS",[33,864,158],{"class":99},[33,866,867],{"class":67},"'.'",[33,869,870],{"class":63}," read",[33,872,873],{"class":63}," -ra",[33,875,838],{"class":67},[33,877,878],{"class":99}," \u003C\u003C\u003C",[33,880,731],{"class":67},[33,882,883],{"class":56},"$input",[33,885,408],{"class":67},[33,887,888],{"class":39},"   # split into array, write via nameref\n",[33,890,892],{"class":35,"line":891},42,[33,893,80],{"class":56},[33,895,897],{"class":35,"line":896},43,[33,898,87],{"emptyLinePlaceholder":86},[33,900,902,904,907],{"class":35,"line":901},44,[33,903,826],{"class":52},[33,905,906],{"class":67}," parts",[33,908,909],{"class":67}," \"1.2.3\"\n",[33,911,913,915,917,920,923,925,928],{"class":35,"line":912},45,[33,914,728],{"class":63},[33,916,278],{"class":67},[33,918,919],{"class":56},"parts",[33,921,922],{"class":67},"[",[33,924,170],{"class":99},[33,926,927],{"class":67},"]}\"",[33,929,930],{"class":39},"    # 1 2 3 — the caller's `parts` array was populated by the function\n",[33,932,934],{"class":35,"line":933},46,[33,935,936],{"class":39},"# The function set a variable in the CALLER's scope by name.\n",[15,938,940,941,944,945,948],{"id":939},"anti-pattern-local-masks-set-e-failures","Anti-Pattern: ",[30,942,943],{},"local"," Masks ",[30,946,947],{},"set -e"," Failures",[20,950,951],{"language":22},[24,952,954],{"className":26,"code":953,"language":22,"meta":28,"style":28},"set -e\n\n# ❌ NAIVE — `local` always returns 0, masking the command's failure\nbad_func() {\n    local x=$(false)    # `local x=$(false)` — local's exit status is 0 (local always succeeds)\n    echo \"reached\"      # \"reached\" — false's failure was MASKED by local!\n}\nbad_func   # prints \"reached\" — set -e didn't catch the false\n\n# ✅ CORRECT — separate declaration and assignment\ngood_func() {\n    local x\n    x=$(false)          # x=$(false) — false's exit status propagates (set -e catches it)\n    echo \"reached\"      # NEVER reached — script exits on the x=$(false) line\n}\ngood_func   # script exits\n\n# ── The mechanism ──\n# `local` is a command that returns exit 0 (it's a variable declaration, always succeeds).\n# When you write `local x=$(false)`, the $(false) runs, but local's exit status overrides it.\n# With `set -e`, the masked failure doesn't trigger the exit.\n# Fix: `local x; x=$(false)` — the assignment's exit status propagates.\n\n# ── This also applies to declare ──\ndeclare -i y=$(false)   # masked! declare returns 0.\ndeclare -i y; y=$(false) # not masked — false propagates.\n",[30,955,956,964,968,973,980,1000,1010,1014,1021,1025,1030,1037,1044,1061,1070,1074,1081,1085,1090,1095,1100,1105,1110,1114,1119,1142],{"__ignoreMap":28},[33,957,958,961],{"class":35,"line":36},[33,959,960],{"class":63},"set",[33,962,963],{"class":63}," -e\n",[33,965,966],{"class":35,"line":43},[33,967,87],{"emptyLinePlaceholder":86},[33,969,970],{"class":35,"line":49},[33,971,972],{"class":39},"# ❌ NAIVE — `local` always returns 0, masking the command's failure\n",[33,974,975,978],{"class":35,"line":60},[33,976,977],{"class":52},"bad_func",[33,979,57],{"class":56},[33,981,982,984,987,989,991,994,997],{"class":35,"line":77},[33,983,152],{"class":99},[33,985,986],{"class":56}," x",[33,988,158],{"class":99},[33,990,715],{"class":56},[33,992,993],{"class":63},"false",[33,995,996],{"class":56},")    ",[33,998,999],{"class":39},"# `local x=$(false)` — local's exit status is 0 (local always succeeds)\n",[33,1001,1002,1004,1007],{"class":35,"line":83},[33,1003,64],{"class":63},[33,1005,1006],{"class":67}," \"reached\"",[33,1008,1009],{"class":39},"      # \"reached\" — false's failure was MASKED by local!\n",[33,1011,1012],{"class":35,"line":90},[33,1013,80],{"class":56},[33,1015,1016,1018],{"class":35,"line":96},[33,1017,977],{"class":52},[33,1019,1020],{"class":39},"   # prints \"reached\" — set -e didn't catch the false\n",[33,1022,1023],{"class":35,"line":108},[33,1024,87],{"emptyLinePlaceholder":86},[33,1026,1027],{"class":35,"line":119},[33,1028,1029],{"class":39},"# ✅ CORRECT — separate declaration and assignment\n",[33,1031,1032,1035],{"class":35,"line":124},[33,1033,1034],{"class":52},"good_func",[33,1036,57],{"class":56},[33,1038,1039,1041],{"class":35,"line":130},[33,1040,152],{"class":99},[33,1042,1043],{"class":56}," x\n",[33,1045,1046,1049,1051,1053,1055,1058],{"class":35,"line":135},[33,1047,1048],{"class":56},"    x",[33,1050,158],{"class":99},[33,1052,715],{"class":56},[33,1054,993],{"class":63},[33,1056,1057],{"class":56},")          ",[33,1059,1060],{"class":39},"# x=$(false) — false's exit status propagates (set -e catches it)\n",[33,1062,1063,1065,1067],{"class":35,"line":141},[33,1064,64],{"class":63},[33,1066,1006],{"class":67},[33,1068,1069],{"class":39},"      # NEVER reached — script exits on the x=$(false) line\n",[33,1071,1072],{"class":35,"line":149},[33,1073,80],{"class":56},[33,1075,1076,1078],{"class":35,"line":179},[33,1077,1034],{"class":52},[33,1079,1080],{"class":39},"   # script exits\n",[33,1082,1083],{"class":35,"line":220},[33,1084,87],{"emptyLinePlaceholder":86},[33,1086,1087],{"class":35,"line":232},[33,1088,1089],{"class":39},"# ── The mechanism ──\n",[33,1091,1092],{"class":35,"line":255},[33,1093,1094],{"class":39},"# `local` is a command that returns exit 0 (it's a variable declaration, always succeeds).\n",[33,1096,1097],{"class":35,"line":269},[33,1098,1099],{"class":39},"# When you write `local x=$(false)`, the $(false) runs, but local's exit status overrides it.\n",[33,1101,1102],{"class":35,"line":289},[33,1103,1104],{"class":39},"# With `set -e`, the masked failure doesn't trigger the exit.\n",[33,1106,1107],{"class":35,"line":300},[33,1108,1109],{"class":39},"# Fix: `local x; x=$(false)` — the assignment's exit status propagates.\n",[33,1111,1112],{"class":35,"line":306},[33,1113,87],{"emptyLinePlaceholder":86},[33,1115,1116],{"class":35,"line":311},[33,1117,1118],{"class":39},"# ── This also applies to declare ──\n",[33,1120,1121,1124,1127,1130,1132,1134,1136,1139],{"class":35,"line":316},[33,1122,1123],{"class":99},"declare",[33,1125,1126],{"class":63}," -i",[33,1128,1129],{"class":56}," y",[33,1131,158],{"class":99},[33,1133,715],{"class":56},[33,1135,993],{"class":63},[33,1137,1138],{"class":56},")   ",[33,1140,1141],{"class":39},"# masked! declare returns 0.\n",[33,1143,1144,1146,1148,1151,1153,1155,1157,1160],{"class":35,"line":331},[33,1145,1123],{"class":99},[33,1147,1126],{"class":63},[33,1149,1150],{"class":56}," y; y",[33,1152,158],{"class":99},[33,1154,715],{"class":56},[33,1156,993],{"class":63},[33,1158,1159],{"class":56},") ",[33,1161,1162],{"class":39},"# not masked — false propagates.\n",[15,1164,1166,1167,1169],{"id":1165},"dynamic-scoping-how-local-actually-works","Dynamic Scoping: How ",[30,1168,943],{}," Actually Works",[20,1171,1172],{"language":22},[24,1173,1175],{"className":26,"code":1174,"language":22,"meta":28,"style":28},"# ── Bash uses DYNAMIC scoping (not lexical) ──\n# A `local` variable is visible to:\n# 1. The function that declares it\n# 2. ALL functions called BY that function (dynamic — based on call stack, not source code)\n\nouter() {\n    local x=\"outer's value\"\n    inner    # inner can SEE outer's x (dynamic scope)\n    echo \"after inner: x=$x\"   # may have been modified by inner!\n}\n\ninner() {\n    echo \"inner sees: x=$x\"    # \"outer's value\" — sees caller's local\n    x=\"inner modified it\"     # modifies OUTER's x (because inner didn't declare its own local x)\n}\n\nouter\n# inner sees: x=outer's value\n# after inner: x=inner modified it  ← outer's x was modified by inner!\n\n# ── `local` shadows: if inner declares its own local, it doesn't see outer's ──\nouter2() {\n    local x=\"outer2\"\n    inner2\n    echo \"outer2 after: x=$x\"   # \"outer2\" — inner2's local x is gone, outer2's is restored\n}\n\ninner2() {\n    local x=\"inner2\"           # declares its OWN x — shadows outer2's x\n    echo \"inner2 sees: x=$x\"   # \"inner2\"\n}\n\nouter2\n# inner2 sees: x=inner2\n# outer2 after: x=outer2  ← outer2's x was NOT modified (inner2's local x shadowed it)\n\n# ── Lexical scoping (Python\u002FJS) vs Dynamic scoping (Bash) ──\n# Python: inner() would NOT see x (x isn't defined in inner's lexical scope).\n# Bash: inner() SEES x (x is on the call stack — outer declared it, inner is called by outer).\n# This is powerful but dangerous — callees can modify caller's locals.\n",[30,1176,1177,1182,1187,1192,1197,1201,1207,1218,1226,1241,1245,1249,1255,1269,1281,1285,1289,1293,1298,1303,1307,1312,1319,1330,1335,1349,1353,1357,1364,1378,1392,1396,1400,1405,1410,1415,1419,1424,1429,1434],{"__ignoreMap":28},[33,1178,1179],{"class":35,"line":36},[33,1180,1181],{"class":39},"# ── Bash uses DYNAMIC scoping (not lexical) ──\n",[33,1183,1184],{"class":35,"line":43},[33,1185,1186],{"class":39},"# A `local` variable is visible to:\n",[33,1188,1189],{"class":35,"line":49},[33,1190,1191],{"class":39},"# 1. The function that declares it\n",[33,1193,1194],{"class":35,"line":60},[33,1195,1196],{"class":39},"# 2. ALL functions called BY that function (dynamic — based on call stack, not source code)\n",[33,1198,1199],{"class":35,"line":77},[33,1200,87],{"emptyLinePlaceholder":86},[33,1202,1203,1205],{"class":35,"line":83},[33,1204,319],{"class":52},[33,1206,57],{"class":56},[33,1208,1209,1211,1213,1215],{"class":35,"line":90},[33,1210,152],{"class":99},[33,1212,986],{"class":56},[33,1214,158],{"class":99},[33,1216,1217],{"class":67},"\"outer's value\"\n",[33,1219,1220,1223],{"class":35,"line":96},[33,1221,1222],{"class":52},"    inner",[33,1224,1225],{"class":39},"    # inner can SEE outer's x (dynamic scope)\n",[33,1227,1228,1230,1233,1236,1238],{"class":35,"line":108},[33,1229,64],{"class":63},[33,1231,1232],{"class":67}," \"after inner: x=",[33,1234,1235],{"class":56},"$x",[33,1237,408],{"class":67},[33,1239,1240],{"class":39},"   # may have been modified by inner!\n",[33,1242,1243],{"class":35,"line":119},[33,1244,80],{"class":56},[33,1246,1247],{"class":35,"line":124},[33,1248,87],{"emptyLinePlaceholder":86},[33,1250,1251,1253],{"class":35,"line":130},[33,1252,325],{"class":52},[33,1254,57],{"class":56},[33,1256,1257,1259,1262,1264,1266],{"class":35,"line":135},[33,1258,64],{"class":63},[33,1260,1261],{"class":67}," \"inner sees: x=",[33,1263,1235],{"class":56},[33,1265,408],{"class":67},[33,1267,1268],{"class":39},"    # \"outer's value\" — sees caller's local\n",[33,1270,1271,1273,1275,1278],{"class":35,"line":141},[33,1272,1048],{"class":56},[33,1274,158],{"class":99},[33,1276,1277],{"class":67},"\"inner modified it\"",[33,1279,1280],{"class":39},"     # modifies OUTER's x (because inner didn't declare its own local x)\n",[33,1282,1283],{"class":35,"line":149},[33,1284,80],{"class":56},[33,1286,1287],{"class":35,"line":179},[33,1288,87],{"emptyLinePlaceholder":86},[33,1290,1291],{"class":35,"line":220},[33,1292,345],{"class":52},[33,1294,1295],{"class":35,"line":232},[33,1296,1297],{"class":39},"# inner sees: x=outer's value\n",[33,1299,1300],{"class":35,"line":255},[33,1301,1302],{"class":39},"# after inner: x=inner modified it  ← outer's x was modified by inner!\n",[33,1304,1305],{"class":35,"line":269},[33,1306,87],{"emptyLinePlaceholder":86},[33,1308,1309],{"class":35,"line":289},[33,1310,1311],{"class":39},"# ── `local` shadows: if inner declares its own local, it doesn't see outer's ──\n",[33,1313,1314,1317],{"class":35,"line":300},[33,1315,1316],{"class":52},"outer2",[33,1318,57],{"class":56},[33,1320,1321,1323,1325,1327],{"class":35,"line":306},[33,1322,152],{"class":99},[33,1324,986],{"class":56},[33,1326,158],{"class":99},[33,1328,1329],{"class":67},"\"outer2\"\n",[33,1331,1332],{"class":35,"line":311},[33,1333,1334],{"class":52},"    inner2\n",[33,1336,1337,1339,1342,1344,1346],{"class":35,"line":316},[33,1338,64],{"class":63},[33,1340,1341],{"class":67}," \"outer2 after: x=",[33,1343,1235],{"class":56},[33,1345,408],{"class":67},[33,1347,1348],{"class":39},"   # \"outer2\" — inner2's local x is gone, outer2's is restored\n",[33,1350,1351],{"class":35,"line":331},[33,1352,80],{"class":56},[33,1354,1355],{"class":35,"line":342},[33,1356,87],{"emptyLinePlaceholder":86},[33,1358,1359,1362],{"class":35,"line":348},[33,1360,1361],{"class":52},"inner2",[33,1363,57],{"class":56},[33,1365,1366,1368,1370,1372,1375],{"class":35,"line":354},[33,1367,152],{"class":99},[33,1369,986],{"class":56},[33,1371,158],{"class":99},[33,1373,1374],{"class":67},"\"inner2\"",[33,1376,1377],{"class":39},"           # declares its OWN x — shadows outer2's x\n",[33,1379,1380,1382,1385,1387,1389],{"class":35,"line":360},[33,1381,64],{"class":63},[33,1383,1384],{"class":67}," \"inner2 sees: x=",[33,1386,1235],{"class":56},[33,1388,408],{"class":67},[33,1390,1391],{"class":39},"   # \"inner2\"\n",[33,1393,1394],{"class":35,"line":366},[33,1395,80],{"class":56},[33,1397,1398],{"class":35,"line":372},[33,1399,87],{"emptyLinePlaceholder":86},[33,1401,1402],{"class":35,"line":377},[33,1403,1404],{"class":52},"outer2\n",[33,1406,1407],{"class":35,"line":383},[33,1408,1409],{"class":39},"# inner2 sees: x=inner2\n",[33,1411,1412],{"class":35,"line":391},[33,1413,1414],{"class":39},"# outer2 after: x=outer2  ← outer2's x was NOT modified (inner2's local x shadowed it)\n",[33,1416,1417],{"class":35,"line":414},[33,1418,87],{"emptyLinePlaceholder":86},[33,1420,1421],{"class":35,"line":430},[33,1422,1423],{"class":39},"# ── Lexical scoping (Python\u002FJS) vs Dynamic scoping (Bash) ──\n",[33,1425,1426],{"class":35,"line":435},[33,1427,1428],{"class":39},"# Python: inner() would NOT see x (x isn't defined in inner's lexical scope).\n",[33,1430,1431],{"class":35,"line":440},[33,1432,1433],{"class":39},"# Bash: inner() SEES x (x is on the call stack — outer declared it, inner is called by outer).\n",[33,1435,1436],{"class":35,"line":456},[33,1437,1438],{"class":39},"# This is powerful but dangerous — callees can modify caller's locals.\n",[15,1440,1442],{"id":1441},"anti-pattern-accidental-global-via-dynamic-scope","Anti-Pattern: Accidental Global via Dynamic Scope",[20,1444,1445],{"language":22},[24,1446,1448],{"className":26,"code":1447,"language":22,"meta":28,"style":28},"# ❌ NAIVE — function modifies a variable without declaring it local\nprocess() {\n    count=$((count + 1))   # if count isn't local, this modifies the GLOBAL count!\n    echo \"processed: $count\"\n}\n\ncount=0\nprocess   # processed: 1\nprocess   # processed: 2\necho \"global: $count\"  # 2 — the function clobbered the global!\n\n# ✅ CORRECT — use local for all function variables\nprocess() {\n    local count=$((count + 1))  # local count — but reads the global? NO!\n    #  ^-- `local count=$((count + 1))` — the RHS reads the GLOBAL count, then local is created.\n    #  This is a common bug: local count copies the global, increments the copy, then discards.\n    echo \"processed: $count\"\n}\n\ncount=0\nprocess   # processed: 1 (local count = global 0 + 1)\nprocess   # processed: 1 (local count = global 0 + 1 — global unchanged!)\necho \"global: $count\"  # 0 — global was never modified\n\n# ── If you WANT to modify a global from a function, don't use local ──\nincrement_global() {\n    count=$((count + 1))   # no local — modifies the global\n}\ncount=0\nincrement_global\nincrement_global\necho \"$count\"   # 2 — global incremented\n\n# ── Or use nameref for explicit pass-by-reference ──\nincrement_ref() {\n    local -n _c=$1   # nameref to the caller's variable\n    ((_c++))\n}\ncount=0\nincrement_ref count   # passes the NAME \"count\"\necho \"$count\"   # 1 — modified via nameref\n",[30,1449,1450,1455,1462,1486,1498,1502,1506,1515,1522,1529,1543,1547,1552,1558,1581,1586,1591,1601,1605,1609,1617,1624,1631,1644,1648,1653,1660,1679,1683,1691,1696,1700,1713,1717,1722,1729,1745,1755,1759,1767,1776],{"__ignoreMap":28},[33,1451,1452],{"class":35,"line":36},[33,1453,1454],{"class":39},"# ❌ NAIVE — function modifies a variable without declaring it local\n",[33,1456,1457,1460],{"class":35,"line":43},[33,1458,1459],{"class":52},"process",[33,1461,57],{"class":56},[33,1463,1464,1467,1469,1472,1475,1478,1480,1483],{"class":35,"line":49},[33,1465,1466],{"class":56},"    count",[33,1468,158],{"class":99},[33,1470,1471],{"class":56},"$((",[33,1473,1474],{"class":52},"count",[33,1476,1477],{"class":67}," +",[33,1479,205],{"class":63},[33,1481,1482],{"class":56},"))   ",[33,1484,1485],{"class":39},"# if count isn't local, this modifies the GLOBAL count!\n",[33,1487,1488,1490,1493,1496],{"class":35,"line":60},[33,1489,64],{"class":63},[33,1491,1492],{"class":67}," \"processed: ",[33,1494,1495],{"class":56},"$count",[33,1497,453],{"class":67},[33,1499,1500],{"class":35,"line":77},[33,1501,80],{"class":56},[33,1503,1504],{"class":35,"line":83},[33,1505,87],{"emptyLinePlaceholder":86},[33,1507,1508,1510,1512],{"class":35,"line":90},[33,1509,1474],{"class":56},[33,1511,158],{"class":99},[33,1513,1514],{"class":67},"0\n",[33,1516,1517,1519],{"class":35,"line":96},[33,1518,1459],{"class":52},[33,1520,1521],{"class":39},"   # processed: 1\n",[33,1523,1524,1526],{"class":35,"line":108},[33,1525,1459],{"class":52},[33,1527,1528],{"class":39},"   # processed: 2\n",[33,1530,1531,1533,1536,1538,1540],{"class":35,"line":119},[33,1532,728],{"class":63},[33,1534,1535],{"class":67}," \"global: ",[33,1537,1495],{"class":56},[33,1539,408],{"class":67},[33,1541,1542],{"class":39},"  # 2 — the function clobbered the global!\n",[33,1544,1545],{"class":35,"line":124},[33,1546,87],{"emptyLinePlaceholder":86},[33,1548,1549],{"class":35,"line":130},[33,1550,1551],{"class":39},"# ✅ CORRECT — use local for all function variables\n",[33,1553,1554,1556],{"class":35,"line":135},[33,1555,1459],{"class":52},[33,1557,57],{"class":56},[33,1559,1560,1562,1565,1567,1569,1571,1573,1575,1578],{"class":35,"line":141},[33,1561,152],{"class":99},[33,1563,1564],{"class":56}," count",[33,1566,158],{"class":99},[33,1568,1471],{"class":56},[33,1570,1474],{"class":52},[33,1572,1477],{"class":67},[33,1574,205],{"class":63},[33,1576,1577],{"class":56},"))  ",[33,1579,1580],{"class":39},"# local count — but reads the global? NO!\n",[33,1582,1583],{"class":35,"line":149},[33,1584,1585],{"class":39},"    #  ^-- `local count=$((count + 1))` — the RHS reads the GLOBAL count, then local is created.\n",[33,1587,1588],{"class":35,"line":179},[33,1589,1590],{"class":39},"    #  This is a common bug: local count copies the global, increments the copy, then discards.\n",[33,1592,1593,1595,1597,1599],{"class":35,"line":220},[33,1594,64],{"class":63},[33,1596,1492],{"class":67},[33,1598,1495],{"class":56},[33,1600,453],{"class":67},[33,1602,1603],{"class":35,"line":232},[33,1604,80],{"class":56},[33,1606,1607],{"class":35,"line":255},[33,1608,87],{"emptyLinePlaceholder":86},[33,1610,1611,1613,1615],{"class":35,"line":269},[33,1612,1474],{"class":56},[33,1614,158],{"class":99},[33,1616,1514],{"class":67},[33,1618,1619,1621],{"class":35,"line":289},[33,1620,1459],{"class":52},[33,1622,1623],{"class":39},"   # processed: 1 (local count = global 0 + 1)\n",[33,1625,1626,1628],{"class":35,"line":300},[33,1627,1459],{"class":52},[33,1629,1630],{"class":39},"   # processed: 1 (local count = global 0 + 1 — global unchanged!)\n",[33,1632,1633,1635,1637,1639,1641],{"class":35,"line":306},[33,1634,728],{"class":63},[33,1636,1535],{"class":67},[33,1638,1495],{"class":56},[33,1640,408],{"class":67},[33,1642,1643],{"class":39},"  # 0 — global was never modified\n",[33,1645,1646],{"class":35,"line":311},[33,1647,87],{"emptyLinePlaceholder":86},[33,1649,1650],{"class":35,"line":316},[33,1651,1652],{"class":39},"# ── If you WANT to modify a global from a function, don't use local ──\n",[33,1654,1655,1658],{"class":35,"line":331},[33,1656,1657],{"class":52},"increment_global",[33,1659,57],{"class":56},[33,1661,1662,1664,1666,1668,1670,1672,1674,1676],{"class":35,"line":342},[33,1663,1466],{"class":56},[33,1665,158],{"class":99},[33,1667,1471],{"class":56},[33,1669,1474],{"class":52},[33,1671,1477],{"class":67},[33,1673,205],{"class":63},[33,1675,1482],{"class":56},[33,1677,1678],{"class":39},"# no local — modifies the global\n",[33,1680,1681],{"class":35,"line":348},[33,1682,80],{"class":56},[33,1684,1685,1687,1689],{"class":35,"line":354},[33,1686,1474],{"class":56},[33,1688,158],{"class":99},[33,1690,1514],{"class":67},[33,1692,1693],{"class":35,"line":360},[33,1694,1695],{"class":52},"increment_global\n",[33,1697,1698],{"class":35,"line":366},[33,1699,1695],{"class":52},[33,1701,1702,1704,1706,1708,1710],{"class":35,"line":372},[33,1703,728],{"class":63},[33,1705,731],{"class":67},[33,1707,1495],{"class":56},[33,1709,408],{"class":67},[33,1711,1712],{"class":39},"   # 2 — global incremented\n",[33,1714,1715],{"class":35,"line":377},[33,1716,87],{"emptyLinePlaceholder":86},[33,1718,1719],{"class":35,"line":383},[33,1720,1721],{"class":39},"# ── Or use nameref for explicit pass-by-reference ──\n",[33,1723,1724,1727],{"class":35,"line":391},[33,1725,1726],{"class":52},"increment_ref",[33,1728,57],{"class":56},[33,1730,1731,1733,1735,1738,1740,1742],{"class":35,"line":414},[33,1732,152],{"class":99},[33,1734,835],{"class":63},[33,1736,1737],{"class":56}," _c",[33,1739,158],{"class":99},[33,1741,71],{"class":504},[33,1743,1744],{"class":39},"   # nameref to the caller's variable\n",[33,1746,1747,1750,1752],{"class":35,"line":430},[33,1748,1749],{"class":56},"    ((_c",[33,1751,211],{"class":99},[33,1753,1754],{"class":56},"))\n",[33,1756,1757],{"class":35,"line":435},[33,1758,80],{"class":56},[33,1760,1761,1763,1765],{"class":35,"line":440},[33,1762,1474],{"class":56},[33,1764,158],{"class":99},[33,1766,1514],{"class":67},[33,1768,1769,1771,1773],{"class":35,"line":456},[33,1770,1726],{"class":52},[33,1772,1564],{"class":67},[33,1774,1775],{"class":39},"   # passes the NAME \"count\"\n",[33,1777,1778,1780,1782,1784,1786],{"class":35,"line":470},[33,1779,728],{"class":63},[33,1781,731],{"class":67},[33,1783,1495],{"class":56},[33,1785,408],{"class":67},[33,1787,1788],{"class":39},"   # 1 — modified via nameref\n",[15,1790,1792],{"id":1791},"namerefs-in-depth","Namerefs in Depth",[20,1794,1795],{"language":22},[24,1796,1798],{"className":26,"code":1797,"language":22,"meta":28,"style":28},"# ── `local -n` creates a nameref (Bash 4.3+) ──\n# A nameref is an alias for another variable. Reads and writes go to the target.\n\ntarget=\"hello\"\ndeclare -n ref=target   # ref is now a nameref to target\necho \"$ref\"              # hello — reads target\nref=\"world\"             # writes to target\necho \"$target\"          # world — target was modified\n\n# ── Returning arrays from functions via nameref ──\nfilter_files() {\n    local -n _out=$1    # nameref to caller's array\n    shift\n    _out=()             # clear the target array\n    local f\n    for f in \"$@\"; do\n        [[ -f \"$f\" ]] && _out+=(\"$f\")   # write to caller's array via nameref\n    done\n}\n\nexisting_files=()\nfilter_files existing_files *.txt *.md *.json\necho \"${existing_files[@]}\"   # only files that actually exist\n\n# ── ⚠️ Nameref circular reference ──\n# If the nameref has the SAME NAME as the target, it's a circular reference:\ncircular() {\n    local -n arr=$1   # if $1 is \"arr\" (same as the nameref name)...\n    echo \"${arr[@]}\"\n}\narr=(1 2 3)\ncircular arr   # ✗ \"circular name reference\" — arr references itself!\n\n# Fix: use a distinct name for the nameref:\nsafe() {\n    local -n _ref=$1   # _ref is different from any reasonable caller variable name\n    echo \"${_ref[@]}\"\n}\narr=(1 2 3)\nsafe arr   # ✓ works — _ref references arr, no collision\n\n# ── nameref to an associative array ──\nset_config() {\n    local -n _cfg=$1\n    _cfg[host]=\"localhost\"\n    _cfg[port]=8080\n    _cfg[debug]=true\n}\ndeclare -A config\nset_config config\necho \"${config[host]}:${config[port]} debug=${config[debug]}\"\n# localhost:8080 debug=true\n",[30,1799,1800,1805,1810,1814,1824,1841,1855,1868,1882,1886,1891,1898,1914,1919,1932,1939,1959,1994,1998,2002,2006,2016,2038,2055,2059,2064,2069,2076,2092,2108,2112,2131,2140,2144,2149,2156,2172,2187,2191,2207,2216,2220,2225,2232,2246,2256,2266,2277,2282,2293,2300,2323],{"__ignoreMap":28},[33,1801,1802],{"class":35,"line":36},[33,1803,1804],{"class":39},"# ── `local -n` creates a nameref (Bash 4.3+) ──\n",[33,1806,1807],{"class":35,"line":43},[33,1808,1809],{"class":39},"# A nameref is an alias for another variable. Reads and writes go to the target.\n",[33,1811,1812],{"class":35,"line":49},[33,1813,87],{"emptyLinePlaceholder":86},[33,1815,1816,1819,1821],{"class":35,"line":60},[33,1817,1818],{"class":56},"target",[33,1820,158],{"class":99},[33,1822,1823],{"class":67},"\"hello\"\n",[33,1825,1826,1828,1830,1833,1835,1838],{"class":35,"line":77},[33,1827,1123],{"class":99},[33,1829,835],{"class":63},[33,1831,1832],{"class":56}," ref",[33,1834,158],{"class":99},[33,1836,1837],{"class":56},"target   ",[33,1839,1840],{"class":39},"# ref is now a nameref to target\n",[33,1842,1843,1845,1847,1850,1852],{"class":35,"line":83},[33,1844,728],{"class":63},[33,1846,731],{"class":67},[33,1848,1849],{"class":56},"$ref",[33,1851,408],{"class":67},[33,1853,1854],{"class":39},"              # hello — reads target\n",[33,1856,1857,1860,1862,1865],{"class":35,"line":90},[33,1858,1859],{"class":56},"ref",[33,1861,158],{"class":99},[33,1863,1864],{"class":67},"\"world\"",[33,1866,1867],{"class":39},"             # writes to target\n",[33,1869,1870,1872,1874,1877,1879],{"class":35,"line":96},[33,1871,728],{"class":63},[33,1873,731],{"class":67},[33,1875,1876],{"class":56},"$target",[33,1878,408],{"class":67},[33,1880,1881],{"class":39},"          # world — target was modified\n",[33,1883,1884],{"class":35,"line":108},[33,1885,87],{"emptyLinePlaceholder":86},[33,1887,1888],{"class":35,"line":119},[33,1889,1890],{"class":39},"# ── Returning arrays from functions via nameref ──\n",[33,1892,1893,1896],{"class":35,"line":124},[33,1894,1895],{"class":52},"filter_files",[33,1897,57],{"class":56},[33,1899,1900,1902,1904,1907,1909,1911],{"class":35,"line":130},[33,1901,152],{"class":99},[33,1903,835],{"class":63},[33,1905,1906],{"class":56}," _out",[33,1908,158],{"class":99},[33,1910,71],{"class":504},[33,1912,1913],{"class":39},"    # nameref to caller's array\n",[33,1915,1916],{"class":35,"line":135},[33,1917,1918],{"class":63},"    shift\n",[33,1920,1921,1924,1926,1929],{"class":35,"line":141},[33,1922,1923],{"class":56},"    _out",[33,1925,158],{"class":99},[33,1927,1928],{"class":56},"()             ",[33,1930,1931],{"class":39},"# clear the target array\n",[33,1933,1934,1936],{"class":35,"line":149},[33,1935,152],{"class":99},[33,1937,1938],{"class":56}," f\n",[33,1940,1941,1943,1946,1949,1951,1953,1955,1957],{"class":35,"line":179},[33,1942,182],{"class":99},[33,1944,1945],{"class":56}," f ",[33,1947,1948],{"class":99},"in",[33,1950,731],{"class":67},[33,1952,450],{"class":63},[33,1954,408],{"class":67},[33,1956,547],{"class":56},[33,1958,217],{"class":99},[33,1960,1961,1964,1967,1969,1972,1974,1977,1980,1983,1985,1987,1989,1991],{"class":35,"line":220},[33,1962,1963],{"class":56},"        [[ ",[33,1965,1966],{"class":99},"-f",[33,1968,731],{"class":67},[33,1970,1971],{"class":56},"$f",[33,1973,408],{"class":67},[33,1975,1976],{"class":56}," ]] && _out",[33,1978,1979],{"class":99},"+=",[33,1981,1982],{"class":56},"(",[33,1984,408],{"class":67},[33,1986,1971],{"class":56},[33,1988,408],{"class":67},[33,1990,1138],{"class":56},[33,1992,1993],{"class":39},"# write to caller's array via nameref\n",[33,1995,1996],{"class":35,"line":232},[33,1997,303],{"class":99},[33,1999,2000],{"class":35,"line":255},[33,2001,80],{"class":56},[33,2003,2004],{"class":35,"line":269},[33,2005,87],{"emptyLinePlaceholder":86},[33,2007,2008,2011,2013],{"class":35,"line":289},[33,2009,2010],{"class":56},"existing_files",[33,2012,158],{"class":99},[33,2014,2015],{"class":56},"()\n",[33,2017,2018,2020,2023,2025,2028,2030,2033,2035],{"class":35,"line":300},[33,2019,1895],{"class":52},[33,2021,2022],{"class":67}," existing_files",[33,2024,241],{"class":63},[33,2026,2027],{"class":67},".txt",[33,2029,241],{"class":63},[33,2031,2032],{"class":67},".md",[33,2034,241],{"class":63},[33,2036,2037],{"class":67},".json\n",[33,2039,2040,2042,2044,2046,2048,2050,2052],{"class":35,"line":306},[33,2041,728],{"class":63},[33,2043,278],{"class":67},[33,2045,2010],{"class":56},[33,2047,922],{"class":67},[33,2049,170],{"class":99},[33,2051,927],{"class":67},[33,2053,2054],{"class":39},"   # only files that actually exist\n",[33,2056,2057],{"class":35,"line":311},[33,2058,87],{"emptyLinePlaceholder":86},[33,2060,2061],{"class":35,"line":316},[33,2062,2063],{"class":39},"# ── ⚠️ Nameref circular reference ──\n",[33,2065,2066],{"class":35,"line":331},[33,2067,2068],{"class":39},"# If the nameref has the SAME NAME as the target, it's a circular reference:\n",[33,2070,2071,2074],{"class":35,"line":342},[33,2072,2073],{"class":52},"circular",[33,2075,57],{"class":56},[33,2077,2078,2080,2082,2085,2087,2089],{"class":35,"line":348},[33,2079,152],{"class":99},[33,2081,835],{"class":63},[33,2083,2084],{"class":56}," arr",[33,2086,158],{"class":99},[33,2088,71],{"class":504},[33,2090,2091],{"class":39},"   # if $1 is \"arr\" (same as the nameref name)...\n",[33,2093,2094,2096,2098,2101,2103,2105],{"class":35,"line":354},[33,2095,64],{"class":63},[33,2097,278],{"class":67},[33,2099,2100],{"class":56},"arr",[33,2102,922],{"class":67},[33,2104,170],{"class":99},[33,2106,2107],{"class":67},"]}\"\n",[33,2109,2110],{"class":35,"line":360},[33,2111,80],{"class":56},[33,2113,2114,2116,2118,2120,2123,2125,2128],{"class":35,"line":366},[33,2115,2100],{"class":56},[33,2117,158],{"class":99},[33,2119,1982],{"class":56},[33,2121,2122],{"class":63},"1",[33,2124,244],{"class":63},[33,2126,2127],{"class":63}," 3",[33,2129,2130],{"class":56},")\n",[33,2132,2133,2135,2137],{"class":35,"line":372},[33,2134,2073],{"class":52},[33,2136,2084],{"class":67},[33,2138,2139],{"class":39},"   # ✗ \"circular name reference\" — arr references itself!\n",[33,2141,2142],{"class":35,"line":377},[33,2143,87],{"emptyLinePlaceholder":86},[33,2145,2146],{"class":35,"line":383},[33,2147,2148],{"class":39},"# Fix: use a distinct name for the nameref:\n",[33,2150,2151,2154],{"class":35,"line":391},[33,2152,2153],{"class":52},"safe",[33,2155,57],{"class":56},[33,2157,2158,2160,2162,2165,2167,2169],{"class":35,"line":414},[33,2159,152],{"class":99},[33,2161,835],{"class":63},[33,2163,2164],{"class":56}," _ref",[33,2166,158],{"class":99},[33,2168,71],{"class":504},[33,2170,2171],{"class":39},"   # _ref is different from any reasonable caller variable name\n",[33,2173,2174,2176,2178,2181,2183,2185],{"class":35,"line":430},[33,2175,64],{"class":63},[33,2177,278],{"class":67},[33,2179,2180],{"class":56},"_ref",[33,2182,922],{"class":67},[33,2184,170],{"class":99},[33,2186,2107],{"class":67},[33,2188,2189],{"class":35,"line":435},[33,2190,80],{"class":56},[33,2192,2193,2195,2197,2199,2201,2203,2205],{"class":35,"line":440},[33,2194,2100],{"class":56},[33,2196,158],{"class":99},[33,2198,1982],{"class":56},[33,2200,2122],{"class":63},[33,2202,244],{"class":63},[33,2204,2127],{"class":63},[33,2206,2130],{"class":56},[33,2208,2209,2211,2213],{"class":35,"line":456},[33,2210,2153],{"class":52},[33,2212,2084],{"class":67},[33,2214,2215],{"class":39},"   # ✓ works — _ref references arr, no collision\n",[33,2217,2218],{"class":35,"line":470},[33,2219,87],{"emptyLinePlaceholder":86},[33,2221,2222],{"class":35,"line":891},[33,2223,2224],{"class":39},"# ── nameref to an associative array ──\n",[33,2226,2227,2230],{"class":35,"line":896},[33,2228,2229],{"class":52},"set_config",[33,2231,57],{"class":56},[33,2233,2234,2236,2238,2241,2243],{"class":35,"line":901},[33,2235,152],{"class":99},[33,2237,835],{"class":63},[33,2239,2240],{"class":56}," _cfg",[33,2242,158],{"class":99},[33,2244,2245],{"class":504},"$1\n",[33,2247,2248,2251,2253],{"class":35,"line":912},[33,2249,2250],{"class":56},"    _cfg[host]",[33,2252,158],{"class":99},[33,2254,2255],{"class":67},"\"localhost\"\n",[33,2257,2258,2261,2263],{"class":35,"line":933},[33,2259,2260],{"class":56},"    _cfg[port]",[33,2262,158],{"class":99},[33,2264,2265],{"class":67},"8080\n",[33,2267,2269,2272,2274],{"class":35,"line":2268},47,[33,2270,2271],{"class":56},"    _cfg[debug]",[33,2273,158],{"class":99},[33,2275,2276],{"class":67},"true\n",[33,2278,2280],{"class":35,"line":2279},48,[33,2281,80],{"class":56},[33,2283,2285,2287,2290],{"class":35,"line":2284},49,[33,2286,1123],{"class":99},[33,2288,2289],{"class":63}," -A",[33,2291,2292],{"class":56}," config\n",[33,2294,2296,2298],{"class":35,"line":2295},50,[33,2297,2229],{"class":52},[33,2299,2292],{"class":67},[33,2301,2303,2305,2307,2310,2313,2315,2318,2320],{"class":35,"line":2302},51,[33,2304,728],{"class":63},[33,2306,278],{"class":67},[33,2308,2309],{"class":56},"config",[33,2311,2312],{"class":67},"[host]}:${",[33,2314,2309],{"class":56},[33,2316,2317],{"class":67},"[port]} debug=${",[33,2319,2309],{"class":56},[33,2321,2322],{"class":67},"[debug]}\"\n",[33,2324,2326],{"class":35,"line":2325},52,[33,2327,2328],{"class":39},"# localhost:8080 debug=true\n",[15,2330,2332],{"id":2331},"signal-traps-cleanup-and-graceful-shutdown","Signal Traps: Cleanup and Graceful Shutdown",[20,2334,2335],{"language":22},[24,2336,2338],{"className":26,"code":2337,"language":22,"meta":28,"style":28},"# ── trap registers a handler for signals or pseudo-signals ──\n# Signals: INT (Ctrl-C), TERM (kill), HUP (terminal closed), etc.\n# Pseudo-signals: EXIT (any exit), ERR (command failure), DEBUG (before each command), RETURN\n\n# ── Clean up temp files on any exit ──\ntmpfile=$(mktemp)\ntmpdir=$(mktemp -d)\ntrap 'rm -f \"$tmpfile\"; rm -rf \"$tmpdir\"' EXIT\n# EXIT fires on: normal end, `exit`, `set -e` abort, and signals (if no specific trap).\n# It fires in the exiting shell — so $tmpfile is still in scope.\n\n# ── Graceful shutdown of a server ──\nserver_pid=\"\"\ncleanup() {\n    if [[ -n \"$server_pid\" ]] && kill -0 \"$server_pid\" 2>\u002Fdev\u002Fnull; then\n        echo \"shutting down server (PID $server_pid)...\" >&2\n        kill \"$server_pid\"           # SIGTERM — let the server clean up\n        wait \"$server_pid\" 2>\u002Fdev\u002Fnull\n    fi\n}\ntrap cleanup EXIT INT TERM\n# EXIT: normal exit or set -e abort\n# INT:  Ctrl-C (user interrupt)\n# TERM: kill (termination request)\n\n# ── ERR trap: log the failing command and line ──\nset -E   # -E makes ERR trap inherit into functions (essential!)\non_error() {\n    local exit_code=$?\n    echo \"ERROR: '$BASH_COMMAND' failed (exit $exit_code) at line $LINENO\" >&2\n    # BASH_COMMAND: the command that triggered the error\n    # LINENO: current line number\n    # $?: the exit status (capture FIRST — any command overwrites $?)\n}\ntrap on_error ERR\n# ERR fires on any command failure (with set -e). Without set -e, ERR doesn't fire.\n\n# ── DEBUG trap: trace every command ──\ntrace() {\n    echo \"DEBUG: line $LINENO: $BASH_COMMAND\" >&2\n}\ntrap trace DEBUG   # fires BEFORE every command — very verbose, but powerful for debugging\n\n# ── Ignoring a signal (critical section) ──\ntrap '' INT    # ignore Ctrl-C during critical section\n# ... critical work that shouldn't be interrupted ...\ntrap - INT    # restore default INT behavior (Ctrl-C kills the script)\n\n# ── HUP for config reload ──\nload_config() {\n    # shellcheck source=\u002Fdev\u002Fnull\n    source \"$CONFIG_FILE\"\n    echo \"config reloaded\"\n}\ntrap load_config HUP   # kill -HUP $pid reloads config without restart\n\n# ── Combining traps ──\n# Multiple trap EXIT overwrite — last one wins. Combine in one function:\ncleanup_all() {\n    local exit_code=$?\n    [[ -n \"$server_pid\" ]] && kill \"$server_pid\" 2>\u002Fdev\u002Fnull\n    [[ -n \"$tmpfile\" ]] && rm -f \"$tmpfile\"\n    [[ -n \"$tmpdir\" ]] && rm -rf \"$tmpdir\"\n    if ((exit_code != 0)); then\n        echo \"script failed (exit $exit_code)\" >&2\n    fi\n    exit \"$exit_code\"   # preserve exit code\n}\ntrap cleanup_all EXIT INT TERM\n# Don't use separate trap cleanup EXIT + trap cleanup INT — the second overwrites the first.\n",[30,2339,2340,2345,2350,2355,2359,2364,2378,2394,2405,2410,2415,2419,2424,2434,2441,2483,2499,2513,2529,2533,2537,2553,2558,2563,2568,2572,2577,2587,2594,2606,2632,2637,2642,2647,2651,2661,2666,2670,2675,2681,2699,2703,2716,2720,2725,2737,2742,2754,2758,2763,2770,2775,2787,2795,2800,2814,2819,2825,2831,2839,2850,2878,2906,2933,2950,2965,2970,2985,2990,3004],{"__ignoreMap":28},[33,2341,2342],{"class":35,"line":36},[33,2343,2344],{"class":39},"# ── trap registers a handler for signals or pseudo-signals ──\n",[33,2346,2347],{"class":35,"line":43},[33,2348,2349],{"class":39},"# Signals: INT (Ctrl-C), TERM (kill), HUP (terminal closed), etc.\n",[33,2351,2352],{"class":35,"line":49},[33,2353,2354],{"class":39},"# Pseudo-signals: EXIT (any exit), ERR (command failure), DEBUG (before each command), RETURN\n",[33,2356,2357],{"class":35,"line":60},[33,2358,87],{"emptyLinePlaceholder":86},[33,2360,2361],{"class":35,"line":77},[33,2362,2363],{"class":39},"# ── Clean up temp files on any exit ──\n",[33,2365,2366,2369,2371,2373,2376],{"class":35,"line":83},[33,2367,2368],{"class":56},"tmpfile",[33,2370,158],{"class":99},[33,2372,715],{"class":56},[33,2374,2375],{"class":52},"mktemp",[33,2377,2130],{"class":56},[33,2379,2380,2383,2385,2387,2389,2392],{"class":35,"line":90},[33,2381,2382],{"class":56},"tmpdir",[33,2384,158],{"class":99},[33,2386,715],{"class":56},[33,2388,2375],{"class":52},[33,2390,2391],{"class":63}," -d",[33,2393,2130],{"class":56},[33,2395,2396,2399,2402],{"class":35,"line":96},[33,2397,2398],{"class":63},"trap",[33,2400,2401],{"class":67}," 'rm -f \"$tmpfile\"; rm -rf \"$tmpdir\"'",[33,2403,2404],{"class":67}," EXIT\n",[33,2406,2407],{"class":35,"line":108},[33,2408,2409],{"class":39},"# EXIT fires on: normal end, `exit`, `set -e` abort, and signals (if no specific trap).\n",[33,2411,2412],{"class":35,"line":119},[33,2413,2414],{"class":39},"# It fires in the exiting shell — so $tmpfile is still in scope.\n",[33,2416,2417],{"class":35,"line":124},[33,2418,87],{"emptyLinePlaceholder":86},[33,2420,2421],{"class":35,"line":130},[33,2422,2423],{"class":39},"# ── Graceful shutdown of a server ──\n",[33,2425,2426,2429,2431],{"class":35,"line":135},[33,2427,2428],{"class":56},"server_pid",[33,2430,158],{"class":99},[33,2432,2433],{"class":67},"\"\"\n",[33,2435,2436,2439],{"class":35,"line":141},[33,2437,2438],{"class":52},"cleanup",[33,2440,57],{"class":56},[33,2442,2443,2445,2448,2451,2453,2456,2458,2461,2464,2467,2469,2471,2473,2476,2479,2481],{"class":35,"line":149},[33,2444,584],{"class":99},[33,2446,2447],{"class":56}," [[ ",[33,2449,2450],{"class":99},"-n",[33,2452,731],{"class":67},[33,2454,2455],{"class":56},"$server_pid",[33,2457,408],{"class":67},[33,2459,2460],{"class":56}," ]] && ",[33,2462,2463],{"class":63},"kill",[33,2465,2466],{"class":63}," -0",[33,2468,731],{"class":67},[33,2470,2455],{"class":56},[33,2472,408],{"class":67},[33,2474,2475],{"class":99}," 2>",[33,2477,2478],{"class":67},"\u002Fdev\u002Fnull",[33,2480,547],{"class":56},[33,2482,603],{"class":99},[33,2484,2485,2488,2491,2493,2496],{"class":35,"line":179},[33,2486,2487],{"class":63},"        echo",[33,2489,2490],{"class":67}," \"shutting down server (PID ",[33,2492,2455],{"class":56},[33,2494,2495],{"class":67},")...\"",[33,2497,2498],{"class":99}," >&2\n",[33,2500,2501,2504,2506,2508,2510],{"class":35,"line":220},[33,2502,2503],{"class":63},"        kill",[33,2505,731],{"class":67},[33,2507,2455],{"class":56},[33,2509,408],{"class":67},[33,2511,2512],{"class":39},"           # SIGTERM — let the server clean up\n",[33,2514,2515,2518,2520,2522,2524,2526],{"class":35,"line":232},[33,2516,2517],{"class":63},"        wait",[33,2519,731],{"class":67},[33,2521,2455],{"class":56},[33,2523,408],{"class":67},[33,2525,2475],{"class":99},[33,2527,2528],{"class":67},"\u002Fdev\u002Fnull\n",[33,2530,2531],{"class":35,"line":255},[33,2532,632],{"class":99},[33,2534,2535],{"class":35,"line":269},[33,2536,80],{"class":56},[33,2538,2539,2541,2544,2547,2550],{"class":35,"line":289},[33,2540,2398],{"class":63},[33,2542,2543],{"class":67}," cleanup",[33,2545,2546],{"class":67}," EXIT",[33,2548,2549],{"class":67}," INT",[33,2551,2552],{"class":67}," TERM\n",[33,2554,2555],{"class":35,"line":300},[33,2556,2557],{"class":39},"# EXIT: normal exit or set -e abort\n",[33,2559,2560],{"class":35,"line":306},[33,2561,2562],{"class":39},"# INT:  Ctrl-C (user interrupt)\n",[33,2564,2565],{"class":35,"line":311},[33,2566,2567],{"class":39},"# TERM: kill (termination request)\n",[33,2569,2570],{"class":35,"line":316},[33,2571,87],{"emptyLinePlaceholder":86},[33,2573,2574],{"class":35,"line":331},[33,2575,2576],{"class":39},"# ── ERR trap: log the failing command and line ──\n",[33,2578,2579,2581,2584],{"class":35,"line":342},[33,2580,960],{"class":63},[33,2582,2583],{"class":63}," -E",[33,2585,2586],{"class":39},"   # -E makes ERR trap inherit into functions (essential!)\n",[33,2588,2589,2592],{"class":35,"line":348},[33,2590,2591],{"class":52},"on_error",[33,2593,57],{"class":56},[33,2595,2596,2598,2601,2603],{"class":35,"line":354},[33,2597,152],{"class":99},[33,2599,2600],{"class":56}," exit_code",[33,2602,158],{"class":99},[33,2604,2605],{"class":63},"$?\n",[33,2607,2608,2610,2613,2616,2619,2622,2625,2628,2630],{"class":35,"line":360},[33,2609,64],{"class":63},[33,2611,2612],{"class":67}," \"ERROR: '",[33,2614,2615],{"class":56},"$BASH_COMMAND",[33,2617,2618],{"class":67},"' failed (exit ",[33,2620,2621],{"class":56},"$exit_code",[33,2623,2624],{"class":67},") at line ",[33,2626,2627],{"class":56},"$LINENO",[33,2629,408],{"class":67},[33,2631,2498],{"class":99},[33,2633,2634],{"class":35,"line":366},[33,2635,2636],{"class":39},"    # BASH_COMMAND: the command that triggered the error\n",[33,2638,2639],{"class":35,"line":372},[33,2640,2641],{"class":39},"    # LINENO: current line number\n",[33,2643,2644],{"class":35,"line":377},[33,2645,2646],{"class":39},"    # $?: the exit status (capture FIRST — any command overwrites $?)\n",[33,2648,2649],{"class":35,"line":383},[33,2650,80],{"class":56},[33,2652,2653,2655,2658],{"class":35,"line":391},[33,2654,2398],{"class":63},[33,2656,2657],{"class":67}," on_error",[33,2659,2660],{"class":67}," ERR\n",[33,2662,2663],{"class":35,"line":414},[33,2664,2665],{"class":39},"# ERR fires on any command failure (with set -e). Without set -e, ERR doesn't fire.\n",[33,2667,2668],{"class":35,"line":430},[33,2669,87],{"emptyLinePlaceholder":86},[33,2671,2672],{"class":35,"line":435},[33,2673,2674],{"class":39},"# ── DEBUG trap: trace every command ──\n",[33,2676,2677,2679],{"class":35,"line":440},[33,2678,144],{"class":52},[33,2680,57],{"class":56},[33,2682,2683,2685,2688,2690,2693,2695,2697],{"class":35,"line":456},[33,2684,64],{"class":63},[33,2686,2687],{"class":67}," \"DEBUG: line ",[33,2689,2627],{"class":56},[33,2691,2692],{"class":67},": ",[33,2694,2615],{"class":56},[33,2696,408],{"class":67},[33,2698,2498],{"class":99},[33,2700,2701],{"class":35,"line":470},[33,2702,80],{"class":56},[33,2704,2705,2707,2710,2713],{"class":35,"line":891},[33,2706,2398],{"class":63},[33,2708,2709],{"class":67}," trace",[33,2711,2712],{"class":67}," DEBUG",[33,2714,2715],{"class":39},"   # fires BEFORE every command — very verbose, but powerful for debugging\n",[33,2717,2718],{"class":35,"line":896},[33,2719,87],{"emptyLinePlaceholder":86},[33,2721,2722],{"class":35,"line":901},[33,2723,2724],{"class":39},"# ── Ignoring a signal (critical section) ──\n",[33,2726,2727,2729,2732,2734],{"class":35,"line":912},[33,2728,2398],{"class":63},[33,2730,2731],{"class":67}," ''",[33,2733,2549],{"class":67},[33,2735,2736],{"class":39},"    # ignore Ctrl-C during critical section\n",[33,2738,2739],{"class":35,"line":933},[33,2740,2741],{"class":39},"# ... critical work that shouldn't be interrupted ...\n",[33,2743,2744,2746,2749,2751],{"class":35,"line":2268},[33,2745,2398],{"class":63},[33,2747,2748],{"class":67}," -",[33,2750,2549],{"class":67},[33,2752,2753],{"class":39},"    # restore default INT behavior (Ctrl-C kills the script)\n",[33,2755,2756],{"class":35,"line":2279},[33,2757,87],{"emptyLinePlaceholder":86},[33,2759,2760],{"class":35,"line":2284},[33,2761,2762],{"class":39},"# ── HUP for config reload ──\n",[33,2764,2765,2768],{"class":35,"line":2295},[33,2766,2767],{"class":52},"load_config",[33,2769,57],{"class":56},[33,2771,2772],{"class":35,"line":2302},[33,2773,2774],{"class":39},"    # shellcheck source=\u002Fdev\u002Fnull\n",[33,2776,2777,2780,2782,2785],{"class":35,"line":2325},[33,2778,2779],{"class":63},"    source",[33,2781,731],{"class":67},[33,2783,2784],{"class":56},"$CONFIG_FILE",[33,2786,453],{"class":67},[33,2788,2790,2792],{"class":35,"line":2789},53,[33,2791,64],{"class":63},[33,2793,2794],{"class":67}," \"config reloaded\"\n",[33,2796,2798],{"class":35,"line":2797},54,[33,2799,80],{"class":56},[33,2801,2803,2805,2808,2811],{"class":35,"line":2802},55,[33,2804,2398],{"class":63},[33,2806,2807],{"class":67}," load_config",[33,2809,2810],{"class":67}," HUP",[33,2812,2813],{"class":39},"   # kill -HUP $pid reloads config without restart\n",[33,2815,2817],{"class":35,"line":2816},56,[33,2818,87],{"emptyLinePlaceholder":86},[33,2820,2822],{"class":35,"line":2821},57,[33,2823,2824],{"class":39},"# ── Combining traps ──\n",[33,2826,2828],{"class":35,"line":2827},58,[33,2829,2830],{"class":39},"# Multiple trap EXIT overwrite — last one wins. Combine in one function:\n",[33,2832,2834,2837],{"class":35,"line":2833},59,[33,2835,2836],{"class":52},"cleanup_all",[33,2838,57],{"class":56},[33,2840,2842,2844,2846,2848],{"class":35,"line":2841},60,[33,2843,152],{"class":99},[33,2845,2600],{"class":56},[33,2847,158],{"class":99},[33,2849,2605],{"class":63},[33,2851,2853,2856,2858,2860,2862,2864,2866,2868,2870,2872,2874,2876],{"class":35,"line":2852},61,[33,2854,2855],{"class":56},"    [[ ",[33,2857,2450],{"class":99},[33,2859,731],{"class":67},[33,2861,2455],{"class":56},[33,2863,408],{"class":67},[33,2865,2460],{"class":56},[33,2867,2463],{"class":63},[33,2869,731],{"class":67},[33,2871,2455],{"class":56},[33,2873,408],{"class":67},[33,2875,2475],{"class":99},[33,2877,2528],{"class":67},[33,2879,2881,2883,2885,2887,2890,2892,2894,2897,2900,2902,2904],{"class":35,"line":2880},62,[33,2882,2855],{"class":56},[33,2884,2450],{"class":99},[33,2886,731],{"class":67},[33,2888,2889],{"class":56},"$tmpfile",[33,2891,408],{"class":67},[33,2893,2460],{"class":56},[33,2895,2896],{"class":52},"rm",[33,2898,2899],{"class":63}," -f",[33,2901,731],{"class":67},[33,2903,2889],{"class":56},[33,2905,453],{"class":67},[33,2907,2909,2911,2913,2915,2918,2920,2922,2924,2927,2929,2931],{"class":35,"line":2908},63,[33,2910,2855],{"class":56},[33,2912,2450],{"class":99},[33,2914,731],{"class":67},[33,2916,2917],{"class":56},"$tmpdir",[33,2919,408],{"class":67},[33,2921,2460],{"class":56},[33,2923,2896],{"class":52},[33,2925,2926],{"class":63}," -rf",[33,2928,731],{"class":67},[33,2930,2917],{"class":56},[33,2932,453],{"class":67},[33,2934,2936,2938,2941,2944,2946,2948],{"class":35,"line":2935},64,[33,2937,584],{"class":99},[33,2939,2940],{"class":56}," ((exit_code ",[33,2942,2943],{"class":99},"!=",[33,2945,190],{"class":63},[33,2947,214],{"class":56},[33,2949,603],{"class":99},[33,2951,2953,2955,2958,2960,2963],{"class":35,"line":2952},65,[33,2954,2487],{"class":63},[33,2956,2957],{"class":67}," \"script failed (exit ",[33,2959,2621],{"class":56},[33,2961,2962],{"class":67},")\"",[33,2964,2498],{"class":99},[33,2966,2968],{"class":35,"line":2967},66,[33,2969,632],{"class":99},[33,2971,2973,2976,2978,2980,2982],{"class":35,"line":2972},67,[33,2974,2975],{"class":63},"    exit",[33,2977,731],{"class":67},[33,2979,2621],{"class":56},[33,2981,408],{"class":67},[33,2983,2984],{"class":39},"   # preserve exit code\n",[33,2986,2988],{"class":35,"line":2987},68,[33,2989,80],{"class":56},[33,2991,2993,2995,2998,3000,3002],{"class":35,"line":2992},69,[33,2994,2398],{"class":63},[33,2996,2997],{"class":67}," cleanup_all",[33,2999,2546],{"class":67},[33,3001,2549],{"class":67},[33,3003,2552],{"class":67},[33,3005,3007],{"class":35,"line":3006},70,[33,3008,3009],{"class":39},"# Don't use separate trap cleanup EXIT + trap cleanup INT — the second overwrites the first.\n",[15,3011,3013],{"id":3012},"production-pattern-worker-with-graceful-shutdown","Production Pattern: Worker with Graceful Shutdown",[20,3015,3016],{"language":22},[24,3017,3019],{"className":26,"code":3018,"language":22,"meta":28,"style":28},"#!\u002Fusr\u002Fbin\u002Fenv bash\nset -Eeuo pipefail\n\n# ── A worker that processes jobs and shuts down gracefully on signal ──\n\nrunning=true\ncurrent_job=\"\"\n\nshutdown() {\n    echo \"[$(date -Iseconds)] shutdown requested, finishing current job...\" >&2\n    running=false\n    # Don't exit immediately — let the main loop finish the current job.\n    # The loop checks $running and exits cleanly.\n}\ntrap shutdown INT TERM\n\nprocess_job() {\n    local job=$1\n    current_job=\"$job\"\n    echo \"[$(date -Iseconds)] processing: $job\" >&2\n    sleep 1   # simulate work\n    current_job=\"\"\n}\n\nmain() {\n    local jobs=(\"task1\" \"task2\" \"task3\" \"task4\" \"task5\")\n    for job in \"${jobs[@]}\"; do\n        $running || break   # check shutdown flag\n        process_job \"$job\"\n    done\n    echo \"[$(date -Iseconds)] all jobs processed or shutdown complete\" >&2\n}\n\nmain \"$@\"\n",[30,3020,3021,3026,3036,3040,3045,3049,3058,3067,3071,3078,3095,3105,3110,3115,3119,3130,3134,3141,3152,3166,3185,3195,3203,3207,3211,3218,3246,3270,3284,3295,3299,3314,3318,3322],{"__ignoreMap":28},[33,3022,3023],{"class":35,"line":36},[33,3024,3025],{"class":39},"#!\u002Fusr\u002Fbin\u002Fenv bash\n",[33,3027,3028,3030,3033],{"class":35,"line":43},[33,3029,960],{"class":63},[33,3031,3032],{"class":63}," -Eeuo",[33,3034,3035],{"class":67}," pipefail\n",[33,3037,3038],{"class":35,"line":49},[33,3039,87],{"emptyLinePlaceholder":86},[33,3041,3042],{"class":35,"line":60},[33,3043,3044],{"class":39},"# ── A worker that processes jobs and shuts down gracefully on signal ──\n",[33,3046,3047],{"class":35,"line":77},[33,3048,87],{"emptyLinePlaceholder":86},[33,3050,3051,3054,3056],{"class":35,"line":83},[33,3052,3053],{"class":56},"running",[33,3055,158],{"class":99},[33,3057,2276],{"class":67},[33,3059,3060,3063,3065],{"class":35,"line":90},[33,3061,3062],{"class":56},"current_job",[33,3064,158],{"class":99},[33,3066,2433],{"class":67},[33,3068,3069],{"class":35,"line":96},[33,3070,87],{"emptyLinePlaceholder":86},[33,3072,3073,3076],{"class":35,"line":108},[33,3074,3075],{"class":52},"shutdown",[33,3077,57],{"class":56},[33,3079,3080,3082,3085,3087,3090,3093],{"class":35,"line":119},[33,3081,64],{"class":63},[33,3083,3084],{"class":67}," \"[$(",[33,3086,691],{"class":52},[33,3088,3089],{"class":63}," -Iseconds",[33,3091,3092],{"class":67},")] shutdown requested, finishing current job...\"",[33,3094,2498],{"class":99},[33,3096,3097,3100,3102],{"class":35,"line":124},[33,3098,3099],{"class":56},"    running",[33,3101,158],{"class":99},[33,3103,3104],{"class":67},"false\n",[33,3106,3107],{"class":35,"line":130},[33,3108,3109],{"class":39},"    # Don't exit immediately — let the main loop finish the current job.\n",[33,3111,3112],{"class":35,"line":135},[33,3113,3114],{"class":39},"    # The loop checks $running and exits cleanly.\n",[33,3116,3117],{"class":35,"line":141},[33,3118,80],{"class":56},[33,3120,3121,3123,3126,3128],{"class":35,"line":149},[33,3122,2398],{"class":63},[33,3124,3125],{"class":67}," shutdown",[33,3127,2549],{"class":67},[33,3129,2552],{"class":67},[33,3131,3132],{"class":35,"line":179},[33,3133,87],{"emptyLinePlaceholder":86},[33,3135,3136,3139],{"class":35,"line":220},[33,3137,3138],{"class":52},"process_job",[33,3140,57],{"class":56},[33,3142,3143,3145,3148,3150],{"class":35,"line":232},[33,3144,152],{"class":99},[33,3146,3147],{"class":56}," job",[33,3149,158],{"class":99},[33,3151,2245],{"class":504},[33,3153,3154,3157,3159,3161,3164],{"class":35,"line":255},[33,3155,3156],{"class":56},"    current_job",[33,3158,158],{"class":99},[33,3160,408],{"class":67},[33,3162,3163],{"class":56},"$job",[33,3165,453],{"class":67},[33,3167,3168,3170,3172,3174,3176,3179,3181,3183],{"class":35,"line":269},[33,3169,64],{"class":63},[33,3171,3084],{"class":67},[33,3173,691],{"class":52},[33,3175,3089],{"class":63},[33,3177,3178],{"class":67},")] processing: ",[33,3180,3163],{"class":56},[33,3182,408],{"class":67},[33,3184,2498],{"class":99},[33,3186,3187,3190,3192],{"class":35,"line":289},[33,3188,3189],{"class":52},"    sleep",[33,3191,205],{"class":63},[33,3193,3194],{"class":39},"   # simulate work\n",[33,3196,3197,3199,3201],{"class":35,"line":300},[33,3198,3156],{"class":56},[33,3200,158],{"class":99},[33,3202,2433],{"class":67},[33,3204,3205],{"class":35,"line":306},[33,3206,80],{"class":56},[33,3208,3209],{"class":35,"line":311},[33,3210,87],{"emptyLinePlaceholder":86},[33,3212,3213,3216],{"class":35,"line":316},[33,3214,3215],{"class":52},"main",[33,3217,57],{"class":56},[33,3219,3220,3222,3225,3227,3229,3232,3235,3238,3241,3244],{"class":35,"line":331},[33,3221,152],{"class":99},[33,3223,3224],{"class":56}," jobs",[33,3226,158],{"class":99},[33,3228,1982],{"class":56},[33,3230,3231],{"class":67},"\"task1\"",[33,3233,3234],{"class":67}," \"task2\"",[33,3236,3237],{"class":67}," \"task3\"",[33,3239,3240],{"class":67}," \"task4\"",[33,3242,3243],{"class":67}," \"task5\"",[33,3245,2130],{"class":56},[33,3247,3248,3250,3253,3255,3257,3260,3262,3264,3266,3268],{"class":35,"line":342},[33,3249,182],{"class":99},[33,3251,3252],{"class":56}," job ",[33,3254,1948],{"class":99},[33,3256,278],{"class":67},[33,3258,3259],{"class":56},"jobs",[33,3261,922],{"class":67},[33,3263,170],{"class":99},[33,3265,927],{"class":67},[33,3267,547],{"class":56},[33,3269,217],{"class":99},[33,3271,3272,3275,3278,3281],{"class":35,"line":348},[33,3273,3274],{"class":56},"        $running ",[33,3276,3277],{"class":99},"||",[33,3279,3280],{"class":99}," break",[33,3282,3283],{"class":39},"   # check shutdown flag\n",[33,3285,3286,3289,3291,3293],{"class":35,"line":354},[33,3287,3288],{"class":52},"        process_job",[33,3290,731],{"class":67},[33,3292,3163],{"class":56},[33,3294,453],{"class":67},[33,3296,3297],{"class":35,"line":360},[33,3298,303],{"class":99},[33,3300,3301,3303,3305,3307,3309,3312],{"class":35,"line":366},[33,3302,64],{"class":63},[33,3304,3084],{"class":67},[33,3306,691],{"class":52},[33,3308,3089],{"class":63},[33,3310,3311],{"class":67},")] all jobs processed or shutdown complete\"",[33,3313,2498],{"class":99},[33,3315,3316],{"class":35,"line":372},[33,3317,80],{"class":56},[33,3319,3320],{"class":35,"line":377},[33,3321,87],{"emptyLinePlaceholder":86},[33,3323,3324,3326,3328,3330],{"class":35,"line":383},[33,3325,3215],{"class":52},[33,3327,731],{"class":67},[33,3329,450],{"class":63},[33,3331,453],{"class":67},[15,3333,3335],{"id":3334},"exporting-functions-to-child-processes","Exporting Functions to Child Processes",[20,3337,3338],{"language":22},[24,3339,3341],{"className":26,"code":3340,"language":22,"meta":28,"style":28},"# ── Functions are NOT inherited by child processes (only subshells) ──\nmy_func() { echo \"hello from $0\"; }\nmy_func                  # hello from script.sh (current shell)\nbash -c 'my_func'        # bash: my_func: command not found (child doesn't see it)\n\n# ── `export -f` makes a function available to child processes ──\nexport -f my_func\nbash -c 'my_func'        # hello from bash (child now has the function)\n\n# ── Use case: xargs\u002Ffind -exec (they run in child processes) ──\nprocess_file() {\n    local f=$1\n    echo \"processing $f\"\n    # ... real work ...\n}\nexport -f process_file\n\n# xargs runs `bash -c` in a subprocess — the function must be exported\nfind . -name '*.txt' -print0 | xargs -0 -P4 -I{} bash -c 'process_file \"$1\"' _ {}\n\n# ── ⚠️ `export -f` serializes the function body as an environment variable ──\n# The function becomes BASH_FUNC_my_func%% env var (with the function body as value).\n# This is a potential injection vector if function names come from untrusted input.\n# Don't export functions with names from untrusted sources.\n\n# ── ⚠️ `export -f` doesn't work with local-scope variables ──\n# If the function references global variables, those must ALSO be exported:\nDATA=\"shared\"\nexport DATA\nuse_data() { echo \"$DATA\"; }\nexport -f use_data\nbash -c 'use_data'   # shared — DATA was exported too\n",[30,3342,3343,3348,3367,3374,3387,3391,3396,3406,3417,3421,3426,3433,3444,3455,3460,3464,3473,3477,3482,3516,3520,3525,3530,3535,3540,3544,3549,3554,3564,3571,3589,3598],{"__ignoreMap":28},[33,3344,3345],{"class":35,"line":36},[33,3346,3347],{"class":39},"# ── Functions are NOT inherited by child processes (only subshells) ──\n",[33,3349,3350,3353,3355,3357,3360,3363,3365],{"class":35,"line":43},[33,3351,3352],{"class":52},"my_func",[33,3354,322],{"class":56},[33,3356,728],{"class":63},[33,3358,3359],{"class":67}," \"hello from ",[33,3361,3362],{"class":63},"$0",[33,3364,408],{"class":67},[33,3366,328],{"class":56},[33,3368,3369,3371],{"class":35,"line":49},[33,3370,3352],{"class":52},[33,3372,3373],{"class":39},"                  # hello from script.sh (current shell)\n",[33,3375,3376,3378,3381,3384],{"class":35,"line":60},[33,3377,22],{"class":52},[33,3379,3380],{"class":63}," -c",[33,3382,3383],{"class":67}," 'my_func'",[33,3385,3386],{"class":39},"        # bash: my_func: command not found (child doesn't see it)\n",[33,3388,3389],{"class":35,"line":77},[33,3390,87],{"emptyLinePlaceholder":86},[33,3392,3393],{"class":35,"line":83},[33,3394,3395],{"class":39},"# ── `export -f` makes a function available to child processes ──\n",[33,3397,3398,3401,3403],{"class":35,"line":90},[33,3399,3400],{"class":99},"export",[33,3402,2899],{"class":63},[33,3404,3405],{"class":56}," my_func\n",[33,3407,3408,3410,3412,3414],{"class":35,"line":96},[33,3409,22],{"class":52},[33,3411,3380],{"class":63},[33,3413,3383],{"class":67},[33,3415,3416],{"class":39},"        # hello from bash (child now has the function)\n",[33,3418,3419],{"class":35,"line":108},[33,3420,87],{"emptyLinePlaceholder":86},[33,3422,3423],{"class":35,"line":119},[33,3424,3425],{"class":39},"# ── Use case: xargs\u002Ffind -exec (they run in child processes) ──\n",[33,3427,3428,3431],{"class":35,"line":124},[33,3429,3430],{"class":52},"process_file",[33,3432,57],{"class":56},[33,3434,3435,3437,3440,3442],{"class":35,"line":130},[33,3436,152],{"class":99},[33,3438,3439],{"class":56}," f",[33,3441,158],{"class":99},[33,3443,2245],{"class":504},[33,3445,3446,3448,3451,3453],{"class":35,"line":135},[33,3447,64],{"class":63},[33,3449,3450],{"class":67}," \"processing ",[33,3452,1971],{"class":56},[33,3454,453],{"class":67},[33,3456,3457],{"class":35,"line":141},[33,3458,3459],{"class":39},"    # ... real work ...\n",[33,3461,3462],{"class":35,"line":149},[33,3463,80],{"class":56},[33,3465,3466,3468,3470],{"class":35,"line":179},[33,3467,3400],{"class":99},[33,3469,2899],{"class":63},[33,3471,3472],{"class":56}," process_file\n",[33,3474,3475],{"class":35,"line":220},[33,3476,87],{"emptyLinePlaceholder":86},[33,3478,3479],{"class":35,"line":232},[33,3480,3481],{"class":39},"# xargs runs `bash -c` in a subprocess — the function must be exported\n",[33,3483,3484,3487,3490,3493,3496,3499,3502,3505,3507,3510,3513],{"class":35,"line":255},[33,3485,3486],{"class":52},"find",[33,3488,3489],{"class":67}," .",[33,3491,3492],{"class":63}," -name",[33,3494,3495],{"class":67}," '*.txt'",[33,3497,3498],{"class":63}," -print0",[33,3500,3501],{"class":99}," |",[33,3503,3504],{"class":52}," xargs",[33,3506,2466],{"class":63},[33,3508,3509],{"class":63}," -P4",[33,3511,3512],{"class":63}," -I",[33,3514,3515],{"class":56},"{} bash -c 'process_file \"$1\"' _ {}\n",[33,3517,3518],{"class":35,"line":269},[33,3519,87],{"emptyLinePlaceholder":86},[33,3521,3522],{"class":35,"line":289},[33,3523,3524],{"class":39},"# ── ⚠️ `export -f` serializes the function body as an environment variable ──\n",[33,3526,3527],{"class":35,"line":300},[33,3528,3529],{"class":39},"# The function becomes BASH_FUNC_my_func%% env var (with the function body as value).\n",[33,3531,3532],{"class":35,"line":306},[33,3533,3534],{"class":39},"# This is a potential injection vector if function names come from untrusted input.\n",[33,3536,3537],{"class":35,"line":311},[33,3538,3539],{"class":39},"# Don't export functions with names from untrusted sources.\n",[33,3541,3542],{"class":35,"line":316},[33,3543,87],{"emptyLinePlaceholder":86},[33,3545,3546],{"class":35,"line":331},[33,3547,3548],{"class":39},"# ── ⚠️ `export -f` doesn't work with local-scope variables ──\n",[33,3550,3551],{"class":35,"line":342},[33,3552,3553],{"class":39},"# If the function references global variables, those must ALSO be exported:\n",[33,3555,3556,3559,3561],{"class":35,"line":348},[33,3557,3558],{"class":56},"DATA",[33,3560,158],{"class":99},[33,3562,3563],{"class":67},"\"shared\"\n",[33,3565,3566,3568],{"class":35,"line":354},[33,3567,3400],{"class":99},[33,3569,3570],{"class":56}," DATA\n",[33,3572,3573,3576,3578,3580,3582,3585,3587],{"class":35,"line":360},[33,3574,3575],{"class":52},"use_data",[33,3577,322],{"class":56},[33,3579,728],{"class":63},[33,3581,731],{"class":67},[33,3583,3584],{"class":56},"$DATA",[33,3586,408],{"class":67},[33,3588,328],{"class":56},[33,3590,3591,3593,3595],{"class":35,"line":366},[33,3592,3400],{"class":99},[33,3594,2899],{"class":63},[33,3596,3597],{"class":56}," use_data\n",[33,3599,3600,3602,3604,3607],{"class":35,"line":372},[33,3601,22],{"class":52},[33,3603,3380],{"class":63},[33,3605,3606],{"class":67}," 'use_data'",[33,3608,3609],{"class":39},"   # shared — DATA was exported too\n",[15,3611,3613],{"id":3612},"recursion-works-but-slow","Recursion: Works But Slow",[20,3615,3616],{"language":22},[24,3617,3619],{"className":26,"code":3618,"language":22,"meta":28,"style":28},"# ── Each $(...) call forks a subprocess — recursion is O(2^n) subprocesses ──\nfib() {\n    if (( $1 \u003C= 1 )); then\n        echo \"$1\"\n    else\n        echo $(( $(fib $(($1 - 1))) + $(fib $(($1 - 2))) ))\n        # Each fib call forks: 2 subprocesses per call → exponential\n    fi\n}\n\ntime fib 15   # ~2s (300+ subprocess forks)\ntime fib 20   # ~30s (thousands of subprocess forks)\ntime fib 30   # minutes — don't do this\n\n# ── Iterative version (no subprocesses) ──\nfib_iter() {\n    local n=$1 a=0 b=1\n    for ((i = 0; i \u003C n; i++)); do\n        ((a, b = b, a + b))   # swap: a gets b's old value, b gets a+b (parallel assignment)\n    done\n    echo \"$a\"\n}\ntime fib_iter 30   # \u003C0.01s — iterative is 1000x faster\n\n# ── If you MUST recurse, avoid $() — use namerefs or a different language ──\n# For pure computation, Bash is the wrong tool. Use Python\u002Fawk for complex algorithms.\n",[30,3620,3621,3626,3633,3650,3660,3664,3691,3696,3700,3704,3708,3719,3729,3739,3743,3748,3755,3782,3805,3834,3838,3849,3853,3863,3867,3872],{"__ignoreMap":28},[33,3622,3623],{"class":35,"line":36},[33,3624,3625],{"class":39},"# ── Each $(...) call forks a subprocess — recursion is O(2^n) subprocesses ──\n",[33,3627,3628,3631],{"class":35,"line":43},[33,3629,3630],{"class":52},"fib",[33,3632,57],{"class":56},[33,3634,3635,3637,3639,3641,3644,3646,3648],{"class":35,"line":49},[33,3636,584],{"class":99},[33,3638,587],{"class":56},[33,3640,71],{"class":504},[33,3642,3643],{"class":99}," \u003C=",[33,3645,205],{"class":63},[33,3647,600],{"class":56},[33,3649,603],{"class":99},[33,3651,3652,3654,3656,3658],{"class":35,"line":60},[33,3653,2487],{"class":63},[33,3655,731],{"class":67},[33,3657,71],{"class":63},[33,3659,453],{"class":67},[33,3661,3662],{"class":35,"line":77},[33,3663,618],{"class":99},[33,3665,3666,3668,3671,3674,3677,3679,3682,3684,3686,3688],{"class":35,"line":83},[33,3667,2487],{"class":63},[33,3669,3670],{"class":56}," $(( ",[33,3672,3673],{"class":52},"$(fib",[33,3675,3676],{"class":56}," $((",[33,3678,71],{"class":504},[33,3680,3681],{"class":56}," - 1))) + $(",[33,3683,3630],{"class":52},[33,3685,3676],{"class":56},[33,3687,71],{"class":504},[33,3689,3690],{"class":56}," - 2))) ))\n",[33,3692,3693],{"class":35,"line":90},[33,3694,3695],{"class":39},"        # Each fib call forks: 2 subprocesses per call → exponential\n",[33,3697,3698],{"class":35,"line":96},[33,3699,632],{"class":99},[33,3701,3702],{"class":35,"line":108},[33,3703,80],{"class":56},[33,3705,3706],{"class":35,"line":119},[33,3707,87],{"emptyLinePlaceholder":86},[33,3709,3710,3713,3716],{"class":35,"line":124},[33,3711,3712],{"class":99},"time",[33,3714,3715],{"class":56}," fib 15   ",[33,3717,3718],{"class":39},"# ~2s (300+ subprocess forks)\n",[33,3720,3721,3723,3726],{"class":35,"line":130},[33,3722,3712],{"class":99},[33,3724,3725],{"class":56}," fib 20   ",[33,3727,3728],{"class":39},"# ~30s (thousands of subprocess forks)\n",[33,3730,3731,3733,3736],{"class":35,"line":135},[33,3732,3712],{"class":99},[33,3734,3735],{"class":56}," fib 30   ",[33,3737,3738],{"class":39},"# minutes — don't do this\n",[33,3740,3741],{"class":35,"line":141},[33,3742,87],{"emptyLinePlaceholder":86},[33,3744,3745],{"class":35,"line":149},[33,3746,3747],{"class":39},"# ── Iterative version (no subprocesses) ──\n",[33,3749,3750,3753],{"class":35,"line":179},[33,3751,3752],{"class":52},"fib_iter",[33,3754,57],{"class":56},[33,3756,3757,3759,3762,3764,3766,3769,3771,3774,3777,3779],{"class":35,"line":220},[33,3758,152],{"class":99},[33,3760,3761],{"class":56}," n",[33,3763,158],{"class":99},[33,3765,71],{"class":504},[33,3767,3768],{"class":56}," a",[33,3770,158],{"class":99},[33,3772,3773],{"class":63},"0",[33,3775,3776],{"class":56}," b",[33,3778,158],{"class":99},[33,3780,3781],{"class":63},"1\n",[33,3783,3784,3786,3788,3790,3792,3794,3796,3799,3801,3803],{"class":35,"line":232},[33,3785,182],{"class":99},[33,3787,185],{"class":56},[33,3789,158],{"class":99},[33,3791,190],{"class":63},[33,3793,193],{"class":56},[33,3795,196],{"class":99},[33,3797,3798],{"class":56}," n; i",[33,3800,211],{"class":99},[33,3802,214],{"class":56},[33,3804,217],{"class":99},[33,3806,3807,3810,3813,3816,3818,3820,3822,3825,3828,3831],{"class":35,"line":255},[33,3808,3809],{"class":56},"        ((a",[33,3811,3812],{"class":99},",",[33,3814,3815],{"class":56}," b ",[33,3817,158],{"class":99},[33,3819,3776],{"class":56},[33,3821,3812],{"class":99},[33,3823,3824],{"class":56}," a ",[33,3826,3827],{"class":99},"+",[33,3829,3830],{"class":56}," b))   ",[33,3832,3833],{"class":39},"# swap: a gets b's old value, b gets a+b (parallel assignment)\n",[33,3835,3836],{"class":35,"line":269},[33,3837,303],{"class":99},[33,3839,3840,3842,3844,3847],{"class":35,"line":289},[33,3841,64],{"class":63},[33,3843,731],{"class":67},[33,3845,3846],{"class":56},"$a",[33,3848,453],{"class":67},[33,3850,3851],{"class":35,"line":300},[33,3852,80],{"class":56},[33,3854,3855,3857,3860],{"class":35,"line":306},[33,3856,3712],{"class":99},[33,3858,3859],{"class":56}," fib_iter 30   ",[33,3861,3862],{"class":39},"# \u003C0.01s — iterative is 1000x faster\n",[33,3864,3865],{"class":35,"line":311},[33,3866,87],{"emptyLinePlaceholder":86},[33,3868,3869],{"class":35,"line":316},[33,3870,3871],{"class":39},"# ── If you MUST recurse, avoid $() — use namerefs or a different language ──\n",[33,3873,3874],{"class":35,"line":331},[33,3875,3876],{"class":39},"# For pure computation, Bash is the wrong tool. Use Python\u002Fawk for complex algorithms.\n",[15,3878,3880],{"id":3879},"tips-tricks","💡 Tips & Tricks",[20,3882,3883],{"language":22},[24,3884,3886],{"className":26,"code":3885,"language":22,"meta":28,"style":28},"# ── Parallel assignment via arithmetic context ──\na=1; b=2\n((a, b = b, a))   # swap! a=2, b=1 (comma operator in ((...)) evaluates left-to-right)\n# This is the Bash idiom for swapping two variables without a temp.\n\n# ── Default arguments for functions ──\ngreet() {\n    local name=${1:-World}   # default to \"World\" if no arg\n    local greeting=${2:-Hello} # default to \"Hello\" if no second arg\n    echo \"$greeting, $name!\"\n}\ngreet          # Hello, World!\ngreet Alice    # Hello, Alice!\ngreet Alice Hi # Hi, Alice!\n\n# ── Function that accepts a variable number of args ──\nsum() {\n    local total=0 n\n    for n in \"$@\"; do\n        ((total += n))\n    done\n    echo \"$total\"\n}\nsum 1 2 3 4 5   # 15\n\n# ── `local -A` for function-private associative arrays ──\nparse_kv() {\n    local -A result   # function-local associative array\n    local pair\n    for pair in \"$@\"; do\n        local key=\"${pair%%=*}\"   # everything before first =\n        local val=\"${pair#*=}\"    # everything after first =\n        result[\"$key\"]=\"$val\"\n    done\n    # Can't return an array — use nameref for the caller\n    # Or print as key=value pairs and parse in the caller\n    for key in \"${!result[@]}\"; do\n        printf '%s=%s\\n' \"$key\" \"${result[$key]}\"\n    done\n}\n\n# ── `trap '' ''` to clear a trap ──\ntrap - EXIT   # remove the EXIT trap (useful in subshells that shouldn't clean up parent's state)\n",[30,3887,3888,3893,3910,3931,3936,3940,3945,3951,3975,3997,4014,4018,4025,4035,4047,4051,4056,4063,4077,4096,4106,4110,4121,4125,4143,4147,4152,4159,4171,4178,4197,4227,4249,4273,4277,4282,4287,4314,4337,4341,4345,4349,4354],{"__ignoreMap":28},[33,3889,3890],{"class":35,"line":36},[33,3891,3892],{"class":39},"# ── Parallel assignment via arithmetic context ──\n",[33,3894,3895,3898,3900,3902,3905,3907],{"class":35,"line":43},[33,3896,3897],{"class":56},"a",[33,3899,158],{"class":99},[33,3901,2122],{"class":67},[33,3903,3904],{"class":56},"; b",[33,3906,158],{"class":99},[33,3908,3909],{"class":67},"2\n",[33,3911,3912,3915,3917,3919,3921,3923,3925,3928],{"class":35,"line":49},[33,3913,3914],{"class":56},"((a",[33,3916,3812],{"class":99},[33,3918,3815],{"class":56},[33,3920,158],{"class":99},[33,3922,3776],{"class":56},[33,3924,3812],{"class":99},[33,3926,3927],{"class":56}," a))   ",[33,3929,3930],{"class":39},"# swap! a=2, b=1 (comma operator in ((...)) evaluates left-to-right)\n",[33,3932,3933],{"class":35,"line":60},[33,3934,3935],{"class":39},"# This is the Bash idiom for swapping two variables without a temp.\n",[33,3937,3938],{"class":35,"line":77},[33,3939,87],{"emptyLinePlaceholder":86},[33,3941,3942],{"class":35,"line":83},[33,3943,3944],{"class":39},"# ── Default arguments for functions ──\n",[33,3946,3947,3949],{"class":35,"line":90},[33,3948,53],{"class":52},[33,3950,57],{"class":56},[33,3952,3953,3955,3958,3960,3963,3966,3969,3972],{"class":35,"line":96},[33,3954,152],{"class":99},[33,3956,3957],{"class":56}," name",[33,3959,158],{"class":99},[33,3961,3962],{"class":504},"${1",[33,3964,3965],{"class":99},":-",[33,3967,3968],{"class":56},"World",[33,3970,3971],{"class":504},"}",[33,3973,3974],{"class":39},"   # default to \"World\" if no arg\n",[33,3976,3977,3979,3982,3984,3987,3989,3992,3994],{"class":35,"line":108},[33,3978,152],{"class":99},[33,3980,3981],{"class":56}," greeting",[33,3983,158],{"class":99},[33,3985,3986],{"class":504},"${2",[33,3988,3965],{"class":99},[33,3990,3991],{"class":56},"Hello",[33,3993,3971],{"class":504},[33,3995,3996],{"class":39}," # default to \"Hello\" if no second arg\n",[33,3998,3999,4001,4003,4006,4009,4012],{"class":35,"line":119},[33,4000,64],{"class":63},[33,4002,731],{"class":67},[33,4004,4005],{"class":56},"$greeting",[33,4007,4008],{"class":67},", ",[33,4010,4011],{"class":56},"$name",[33,4013,74],{"class":67},[33,4015,4016],{"class":35,"line":124},[33,4017,80],{"class":56},[33,4019,4020,4022],{"class":35,"line":130},[33,4021,53],{"class":52},[33,4023,4024],{"class":39},"          # Hello, World!\n",[33,4026,4027,4029,4032],{"class":35,"line":135},[33,4028,53],{"class":52},[33,4030,4031],{"class":67}," Alice",[33,4033,4034],{"class":39},"    # Hello, Alice!\n",[33,4036,4037,4039,4041,4044],{"class":35,"line":141},[33,4038,53],{"class":52},[33,4040,4031],{"class":67},[33,4042,4043],{"class":67}," Hi",[33,4045,4046],{"class":39}," # Hi, Alice!\n",[33,4048,4049],{"class":35,"line":149},[33,4050,87],{"emptyLinePlaceholder":86},[33,4052,4053],{"class":35,"line":179},[33,4054,4055],{"class":39},"# ── Function that accepts a variable number of args ──\n",[33,4057,4058,4061],{"class":35,"line":220},[33,4059,4060],{"class":52},"sum",[33,4062,57],{"class":56},[33,4064,4065,4067,4070,4072,4074],{"class":35,"line":232},[33,4066,152],{"class":99},[33,4068,4069],{"class":56}," total",[33,4071,158],{"class":99},[33,4073,3773],{"class":63},[33,4075,4076],{"class":56}," n\n",[33,4078,4079,4081,4084,4086,4088,4090,4092,4094],{"class":35,"line":255},[33,4080,182],{"class":99},[33,4082,4083],{"class":56}," n ",[33,4085,1948],{"class":99},[33,4087,731],{"class":67},[33,4089,450],{"class":63},[33,4091,408],{"class":67},[33,4093,547],{"class":56},[33,4095,217],{"class":99},[33,4097,4098,4101,4103],{"class":35,"line":269},[33,4099,4100],{"class":56},"        ((total ",[33,4102,1979],{"class":99},[33,4104,4105],{"class":56}," n))\n",[33,4107,4108],{"class":35,"line":289},[33,4109,303],{"class":99},[33,4111,4112,4114,4116,4119],{"class":35,"line":300},[33,4113,64],{"class":63},[33,4115,731],{"class":67},[33,4117,4118],{"class":56},"$total",[33,4120,453],{"class":67},[33,4122,4123],{"class":35,"line":306},[33,4124,80],{"class":56},[33,4126,4127,4129,4131,4133,4135,4137,4140],{"class":35,"line":311},[33,4128,4060],{"class":52},[33,4130,205],{"class":63},[33,4132,244],{"class":63},[33,4134,2127],{"class":63},[33,4136,544],{"class":63},[33,4138,4139],{"class":63}," 5",[33,4141,4142],{"class":39},"   # 15\n",[33,4144,4145],{"class":35,"line":316},[33,4146,87],{"emptyLinePlaceholder":86},[33,4148,4149],{"class":35,"line":331},[33,4150,4151],{"class":39},"# ── `local -A` for function-private associative arrays ──\n",[33,4153,4154,4157],{"class":35,"line":342},[33,4155,4156],{"class":52},"parse_kv",[33,4158,57],{"class":56},[33,4160,4161,4163,4165,4168],{"class":35,"line":348},[33,4162,152],{"class":99},[33,4164,2289],{"class":63},[33,4166,4167],{"class":56}," result   ",[33,4169,4170],{"class":39},"# function-local associative array\n",[33,4172,4173,4175],{"class":35,"line":354},[33,4174,152],{"class":99},[33,4176,4177],{"class":56}," pair\n",[33,4179,4180,4182,4185,4187,4189,4191,4193,4195],{"class":35,"line":360},[33,4181,182],{"class":99},[33,4183,4184],{"class":56}," pair ",[33,4186,1948],{"class":99},[33,4188,731],{"class":67},[33,4190,450],{"class":63},[33,4192,408],{"class":67},[33,4194,547],{"class":56},[33,4196,217],{"class":99},[33,4198,4199,4202,4205,4207,4210,4213,4216,4218,4221,4224],{"class":35,"line":366},[33,4200,4201],{"class":99},"        local",[33,4203,4204],{"class":56}," key",[33,4206,158],{"class":99},[33,4208,4209],{"class":67},"\"${",[33,4211,4212],{"class":56},"pair",[33,4214,4215],{"class":99},"%%",[33,4217,158],{"class":67},[33,4219,4220],{"class":99},"*",[33,4222,4223],{"class":67},"}\"",[33,4225,4226],{"class":39},"   # everything before first =\n",[33,4228,4229,4231,4234,4236,4238,4240,4243,4246],{"class":35,"line":372},[33,4230,4201],{"class":99},[33,4232,4233],{"class":56}," val",[33,4235,158],{"class":99},[33,4237,4209],{"class":67},[33,4239,4212],{"class":56},[33,4241,4242],{"class":99},"#*",[33,4244,4245],{"class":67},"=}\"",[33,4247,4248],{"class":39},"    # everything after first =\n",[33,4250,4251,4254,4256,4259,4261,4264,4266,4268,4271],{"class":35,"line":377},[33,4252,4253],{"class":52},"        result[",[33,4255,408],{"class":52},[33,4257,4258],{"class":56},"$key",[33,4260,408],{"class":52},[33,4262,4263],{"class":52},"]",[33,4265,158],{"class":67},[33,4267,408],{"class":52},[33,4269,4270],{"class":56},"$val",[33,4272,453],{"class":52},[33,4274,4275],{"class":35,"line":383},[33,4276,303],{"class":99},[33,4278,4279],{"class":35,"line":391},[33,4280,4281],{"class":39},"    # Can't return an array — use nameref for the caller\n",[33,4283,4284],{"class":35,"line":414},[33,4285,4286],{"class":39},"    # Or print as key=value pairs and parse in the caller\n",[33,4288,4289,4291,4294,4296,4298,4301,4304,4306,4308,4310,4312],{"class":35,"line":430},[33,4290,182],{"class":99},[33,4292,4293],{"class":56}," key ",[33,4295,1948],{"class":99},[33,4297,278],{"class":67},[33,4299,4300],{"class":99},"!",[33,4302,4303],{"class":56},"result",[33,4305,922],{"class":67},[33,4307,170],{"class":99},[33,4309,927],{"class":67},[33,4311,547],{"class":56},[33,4313,217],{"class":99},[33,4315,4316,4318,4321,4323,4325,4327,4329,4331,4333,4335],{"class":35,"line":435},[33,4317,223],{"class":63},[33,4319,4320],{"class":67}," '%s=%s\\n'",[33,4322,731],{"class":67},[33,4324,4258],{"class":56},[33,4326,408],{"class":67},[33,4328,278],{"class":67},[33,4330,4303],{"class":56},[33,4332,922],{"class":67},[33,4334,4258],{"class":56},[33,4336,2107],{"class":67},[33,4338,4339],{"class":35,"line":440},[33,4340,303],{"class":99},[33,4342,4343],{"class":35,"line":456},[33,4344,80],{"class":56},[33,4346,4347],{"class":35,"line":470},[33,4348,87],{"emptyLinePlaceholder":86},[33,4350,4351],{"class":35,"line":891},[33,4352,4353],{"class":39},"# ── `trap '' ''` to clear a trap ──\n",[33,4355,4356,4358,4360,4362],{"class":35,"line":896},[33,4357,2398],{"class":63},[33,4359,2748],{"class":67},[33,4361,2546],{"class":67},[33,4363,4364],{"class":39},"   # remove the EXIT trap (useful in subshells that shouldn't clean up parent's state)\n",[15,4366,4368],{"id":4367},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,4370,4371],{"language":22},[24,4372,4374],{"className":26,"code":4373,"language":22,"meta":28,"style":28},"# ── Functions must be defined BEFORE they're called ──\n# Bash reads top-to-bottom — no hoisting (unlike Python\u002FJS).\n# A function called before its definition: \"command not found\".\n# Put all functions at the top, call main \"$@\" at the bottom.\n\n# ── `$0` inside a function is the script name, not the function ──\nmy_func() { echo \"$0\"; }   # prints script name, NOT \"my_func\"\n# Use ${FUNCNAME[0]} for the current function name:\nmy_func() { echo \"${FUNCNAME[0]}\"; }   # \"my_func\"\n\n# ── `return` vs `exit` in a sourced script ──\n# `exit` in a sourced script closes the CALLER's shell!\n# `return` exits the function\u002Fsourced script and returns to the caller.\n# In a sourced library: use `return`, never `exit`.\n\n# ── `trap` in a subshell doesn't affect the parent ──\n# Traps are shell-specific. `( trap 'echo sub' EXIT )` — the subshell's trap\n# doesn't apply to the parent shell.\n\n# ── `trap EXIT` with `set -e` ──\n# On a `set -e` abort, EXIT still fires (good — cleanup runs).\n# But the exit code in the EXIT trap is the failure's code, not 0.\n# Capture it: cleanup() { local code=$?; ...; exit \"$code\"; }\n\n# ── Nameref to a non-existent variable creates it ──\ndeclare -n ref=nonexistent\nref=\"value\"        # creates `nonexistent` with value \"value\" (not an error)\necho \"$nonexistent\"  # value — created on first write\n\n# ── `local` on a nameref is Bash 4.3+ ──\n# `local -n` requires Bash 4.3+. On older Bash, use `declare -n` (but not function-scoped).\n\n# ── Functions can be overridden ──\ngreet() { echo \"v1\"; }\ngreet() { echo \"v2\"; }   # redefinition — silently replaces v1\ngreet   # v2 — last definition wins. No warning. Be careful with sourcing libraries.\n",[30,4375,4376,4381,4386,4391,4396,4400,4405,4425,4430,4450,4454,4459,4464,4469,4474,4478,4483,4488,4493,4497,4502,4507,4512,4517,4521,4526,4539,4551,4565,4569,4574,4579,4583,4588,4601,4617],{"__ignoreMap":28},[33,4377,4378],{"class":35,"line":36},[33,4379,4380],{"class":39},"# ── Functions must be defined BEFORE they're called ──\n",[33,4382,4383],{"class":35,"line":43},[33,4384,4385],{"class":39},"# Bash reads top-to-bottom — no hoisting (unlike Python\u002FJS).\n",[33,4387,4388],{"class":35,"line":49},[33,4389,4390],{"class":39},"# A function called before its definition: \"command not found\".\n",[33,4392,4393],{"class":35,"line":60},[33,4394,4395],{"class":39},"# Put all functions at the top, call main \"$@\" at the bottom.\n",[33,4397,4398],{"class":35,"line":77},[33,4399,87],{"emptyLinePlaceholder":86},[33,4401,4402],{"class":35,"line":83},[33,4403,4404],{"class":39},"# ── `$0` inside a function is the script name, not the function ──\n",[33,4406,4407,4409,4411,4413,4415,4417,4419,4422],{"class":35,"line":90},[33,4408,3352],{"class":52},[33,4410,322],{"class":56},[33,4412,728],{"class":63},[33,4414,731],{"class":67},[33,4416,3362],{"class":63},[33,4418,408],{"class":67},[33,4420,4421],{"class":56},"; }   ",[33,4423,4424],{"class":39},"# prints script name, NOT \"my_func\"\n",[33,4426,4427],{"class":35,"line":96},[33,4428,4429],{"class":39},"# Use ${FUNCNAME[0]} for the current function name:\n",[33,4431,4432,4434,4436,4438,4440,4442,4445,4447],{"class":35,"line":108},[33,4433,3352],{"class":52},[33,4435,322],{"class":56},[33,4437,728],{"class":63},[33,4439,278],{"class":67},[33,4441,261],{"class":56},[33,4443,4444],{"class":67},"[0]}\"",[33,4446,4421],{"class":56},[33,4448,4449],{"class":39},"# \"my_func\"\n",[33,4451,4452],{"class":35,"line":119},[33,4453,87],{"emptyLinePlaceholder":86},[33,4455,4456],{"class":35,"line":124},[33,4457,4458],{"class":39},"# ── `return` vs `exit` in a sourced script ──\n",[33,4460,4461],{"class":35,"line":130},[33,4462,4463],{"class":39},"# `exit` in a sourced script closes the CALLER's shell!\n",[33,4465,4466],{"class":35,"line":135},[33,4467,4468],{"class":39},"# `return` exits the function\u002Fsourced script and returns to the caller.\n",[33,4470,4471],{"class":35,"line":141},[33,4472,4473],{"class":39},"# In a sourced library: use `return`, never `exit`.\n",[33,4475,4476],{"class":35,"line":149},[33,4477,87],{"emptyLinePlaceholder":86},[33,4479,4480],{"class":35,"line":179},[33,4481,4482],{"class":39},"# ── `trap` in a subshell doesn't affect the parent ──\n",[33,4484,4485],{"class":35,"line":220},[33,4486,4487],{"class":39},"# Traps are shell-specific. `( trap 'echo sub' EXIT )` — the subshell's trap\n",[33,4489,4490],{"class":35,"line":232},[33,4491,4492],{"class":39},"# doesn't apply to the parent shell.\n",[33,4494,4495],{"class":35,"line":255},[33,4496,87],{"emptyLinePlaceholder":86},[33,4498,4499],{"class":35,"line":269},[33,4500,4501],{"class":39},"# ── `trap EXIT` with `set -e` ──\n",[33,4503,4504],{"class":35,"line":289},[33,4505,4506],{"class":39},"# On a `set -e` abort, EXIT still fires (good — cleanup runs).\n",[33,4508,4509],{"class":35,"line":300},[33,4510,4511],{"class":39},"# But the exit code in the EXIT trap is the failure's code, not 0.\n",[33,4513,4514],{"class":35,"line":306},[33,4515,4516],{"class":39},"# Capture it: cleanup() { local code=$?; ...; exit \"$code\"; }\n",[33,4518,4519],{"class":35,"line":311},[33,4520,87],{"emptyLinePlaceholder":86},[33,4522,4523],{"class":35,"line":316},[33,4524,4525],{"class":39},"# ── Nameref to a non-existent variable creates it ──\n",[33,4527,4528,4530,4532,4534,4536],{"class":35,"line":331},[33,4529,1123],{"class":99},[33,4531,835],{"class":63},[33,4533,1832],{"class":56},[33,4535,158],{"class":99},[33,4537,4538],{"class":56},"nonexistent\n",[33,4540,4541,4543,4545,4548],{"class":35,"line":342},[33,4542,1859],{"class":56},[33,4544,158],{"class":99},[33,4546,4547],{"class":67},"\"value\"",[33,4549,4550],{"class":39},"        # creates `nonexistent` with value \"value\" (not an error)\n",[33,4552,4553,4555,4557,4560,4562],{"class":35,"line":348},[33,4554,728],{"class":63},[33,4556,731],{"class":67},[33,4558,4559],{"class":56},"$nonexistent",[33,4561,408],{"class":67},[33,4563,4564],{"class":39},"  # value — created on first write\n",[33,4566,4567],{"class":35,"line":354},[33,4568,87],{"emptyLinePlaceholder":86},[33,4570,4571],{"class":35,"line":360},[33,4572,4573],{"class":39},"# ── `local` on a nameref is Bash 4.3+ ──\n",[33,4575,4576],{"class":35,"line":366},[33,4577,4578],{"class":39},"# `local -n` requires Bash 4.3+. On older Bash, use `declare -n` (but not function-scoped).\n",[33,4580,4581],{"class":35,"line":372},[33,4582,87],{"emptyLinePlaceholder":86},[33,4584,4585],{"class":35,"line":377},[33,4586,4587],{"class":39},"# ── Functions can be overridden ──\n",[33,4589,4590,4592,4594,4596,4599],{"class":35,"line":383},[33,4591,53],{"class":52},[33,4593,322],{"class":56},[33,4595,728],{"class":63},[33,4597,4598],{"class":67}," \"v1\"",[33,4600,328],{"class":56},[33,4602,4603,4605,4607,4609,4612,4614],{"class":35,"line":391},[33,4604,53],{"class":52},[33,4606,322],{"class":56},[33,4608,728],{"class":63},[33,4610,4611],{"class":67}," \"v2\"",[33,4613,4421],{"class":56},[33,4615,4616],{"class":39},"# redefinition — silently replaces v1\n",[33,4618,4619,4621],{"class":35,"line":414},[33,4620,53],{"class":52},[33,4622,4623],{"class":39},"   # v2 — last definition wins. No warning. Be careful with sourcing libraries.\n",[15,4625,4627],{"id":4626},"quick-quiz","🧠 Quick Quiz",[4629,4630,4631],"p",{},"What's wrong with this cleanup?",[20,4633,4634],{"language":22},[24,4635,4637],{"className":26,"code":4636,"language":22,"meta":28,"style":28},"set -e\ntmpfile=$(mktemp)\ntrap 'rm -f \"$tmpfile\"; exit 0' EXIT\nfalse\n",[30,4638,4639,4645,4657,4666],{"__ignoreMap":28},[33,4640,4641,4643],{"class":35,"line":36},[33,4642,960],{"class":63},[33,4644,963],{"class":63},[33,4646,4647,4649,4651,4653,4655],{"class":35,"line":43},[33,4648,2368],{"class":56},[33,4650,158],{"class":99},[33,4652,715],{"class":56},[33,4654,2375],{"class":52},[33,4656,2130],{"class":56},[33,4658,4659,4661,4664],{"class":35,"line":49},[33,4660,2398],{"class":63},[33,4662,4663],{"class":67}," 'rm -f \"$tmpfile\"; exit 0'",[33,4665,2404],{"class":67},[33,4667,4668],{"class":35,"line":60},[33,4669,3104],{"class":63},[4671,4672,4673,4677,4680,4716,4722,4814],"details",{},[4674,4675,4676],"summary",{},"Answer",[4629,4678,4679],{},"Two problems:",[4681,4682,4683,4704],"ol",{},[4684,4685,4686,4693,4694,4696,4697,4699,4700,4703],"li",{},[4687,4688,4689,4692],"strong",{},[30,4690,4691],{},"exit 0"," in the EXIT trap overrides the error exit code."," The script fails on ",[30,4695,993],{}," (set -e), which should exit with code 1. But the EXIT trap runs ",[30,4698,4691],{},", so the script exits with 0 (success) — the failure is ",[4687,4701,4702],{},"masked",". CI\u002FCD would see success.",[4684,4705,4706,4712,4713,4715],{},[4687,4707,4708,4709,4711],{},"The EXIT trap fires on ",[30,4710,947],{}," abort"," — which is good (cleanup runs), but ",[30,4714,4691],{}," discards the error.",[4629,4717,4718,4721],{},[4687,4719,4720],{},"Fix",": preserve the exit code:",[20,4723,4724],{"language":22},[24,4725,4727],{"className":26,"code":4726,"language":22,"meta":28,"style":28},"set -e\ntmpfile=$(mktemp)\ncleanup() {\n    local code=$?    # capture the exit code BEFORE any other command\n    rm -f \"$tmpfile\"\n    exit \"$code\"     # preserve the original exit code (0 for success, non-zero for error)\n}\ntrap cleanup EXIT\nfalse   # exits 1 → cleanup runs → exits 1 (correct)\n",[30,4728,4729,4735,4747,4753,4768,4781,4795,4799,4807],{"__ignoreMap":28},[33,4730,4731,4733],{"class":35,"line":36},[33,4732,960],{"class":63},[33,4734,963],{"class":63},[33,4736,4737,4739,4741,4743,4745],{"class":35,"line":43},[33,4738,2368],{"class":56},[33,4740,158],{"class":99},[33,4742,715],{"class":56},[33,4744,2375],{"class":52},[33,4746,2130],{"class":56},[33,4748,4749,4751],{"class":35,"line":49},[33,4750,2438],{"class":52},[33,4752,57],{"class":56},[33,4754,4755,4757,4760,4762,4765],{"class":35,"line":60},[33,4756,152],{"class":99},[33,4758,4759],{"class":56}," code",[33,4761,158],{"class":99},[33,4763,4764],{"class":63},"$?",[33,4766,4767],{"class":39},"    # capture the exit code BEFORE any other command\n",[33,4769,4770,4773,4775,4777,4779],{"class":35,"line":77},[33,4771,4772],{"class":52},"    rm",[33,4774,2899],{"class":63},[33,4776,731],{"class":67},[33,4778,2889],{"class":56},[33,4780,453],{"class":67},[33,4782,4783,4785,4787,4790,4792],{"class":35,"line":83},[33,4784,2975],{"class":63},[33,4786,731],{"class":67},[33,4788,4789],{"class":56},"$code",[33,4791,408],{"class":67},[33,4793,4794],{"class":39},"     # preserve the original exit code (0 for success, non-zero for error)\n",[33,4796,4797],{"class":35,"line":90},[33,4798,80],{"class":56},[33,4800,4801,4803,4805],{"class":35,"line":96},[33,4802,2398],{"class":63},[33,4804,2543],{"class":67},[33,4806,2404],{"class":67},[33,4808,4809,4811],{"class":35,"line":108},[33,4810,993],{"class":63},[33,4812,4813],{"class":39},"   # exits 1 → cleanup runs → exits 1 (correct)\n",[4629,4815,4816,4819,4820,4822,4823,4826,4827,4829],{},[4687,4817,4818],{},"The lesson",": always capture ",[30,4821,4764],{}," as the first thing in an EXIT trap and ",[30,4824,4825],{},"exit \"$?\""," to preserve the original exit code. ",[30,4828,4691],{}," in cleanup masks failures.",[4831,4832,4833],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}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 .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 .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":28,"searchDepth":43,"depth":43,"links":4835},[4836,4837,4838,4840,4842,4843,4844,4845,4846,4847,4848,4849,4850],{"id":17,"depth":43,"text":18},{"id":476,"depth":43,"text":477},{"id":939,"depth":43,"text":4839},"Anti-Pattern: local Masks set -e Failures",{"id":1165,"depth":43,"text":4841},"Dynamic Scoping: How local Actually Works",{"id":1441,"depth":43,"text":1442},{"id":1791,"depth":43,"text":1792},{"id":2331,"depth":43,"text":2332},{"id":3012,"depth":43,"text":3013},{"id":3334,"depth":43,"text":3335},{"id":3612,"depth":43,"text":3613},{"id":3879,"depth":43,"text":3880},{"id":4367,"depth":43,"text":4368},{"id":4626,"depth":43,"text":4627},"Deep-dive into Bash functions: call stack (FUNCNAME\u002FBASH_SOURCE), dynamic scoping internals, return values (exit status vs stdout vs nameref), signal traps for cleanup, and production patterns for error propagation. Code-first reference for senior engineers.","md",{},"\u002Fbash\u002F05-functions-and-scope",{"title":5,"description":4851},"bash\u002F05-functions-and-scope","M1GeT7qWW3bNd4-JGIzS3WWSuV0R2v4lv40JSXpaDEo",1789924644816]