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