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