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