[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"projects-ray-shell":3},{"id":4,"title":5,"about":6,"body":7,"date":257,"description":88,"extension":258,"githubLink":259,"image":260,"isFeatured":261,"keywords":262,"liveLink":267,"meta":268,"navigation":261,"path":269,"seo":270,"stem":271,"__hash__":272},"projects\u002Fprojects\u002Fray-shell.md","RayShell","A POSIX shell interpreter written in Go focused on low-level process orchestration and robust tokenization.",{"type":8,"value":9,"toc":247},"minimark",[10,243],[11,12,19,24,41,57,66,76,82,117,120,158,165,169,184,215,219,236,240],"project-layout",{":date":13,":github-link":14,":image":15,":keywords":16,":live-link":17,":title":18},"date","githubLink","image","keywords","liveLink","title",[20,21,23],"h2",{"id":22},"ray-shell","Ray Shell",[25,26,27,28,32,33,36,37,40],"p",{},"Most of us who use Linux will be using terminal for their everyday programming. We type ",[29,30,31],"code",{},"ls"," and files are shown. We type ",[29,34,35],{},"cat \u002Fetc\u002Fpasswd | awk -F: '{print $1 \" -> \" $7}'",", we see users mapped with their default shell, we just spam ",[29,38,39],{},"Tab"," for command autocompletes. But how? How are files read and processed? and How does it even autocomplete?",[25,42,43,44,48,49,56],{},"As a person who loves looking under the hood, I took some time to learn and understand about how shell works. And what better way for a developer to understand things, ",[45,46,47],"strong",{},"We build it",". So that's exactly what I did. ",[50,51,55],"a",{"href":52,"rel":53},"https:\u002F\u002Fapp.codecrafters.io\u002Fcourses\u002Fshell\u002F",[54],"nofollow","CodeCrafters.io"," has this wonderful interactive shell building exercise and it even had tests to check if we implemented the features as intended.",[58,59,61,62,65],"h3",{"id":60},"what-do-you-mean-i-cant-use-split","What do you mean I can't use ",[29,63,64],{},".split(\" \")","?",[25,67,68,69,72,73,75],{},"We all know how ",[29,70,71],{},"echo"," command works. According to the ",[29,74,71],{},"'s man page description,",[77,78,79],"blockquote",{},[25,80,81],{},"Echo the STRING(s) to standard output.",[83,84,89],"pre",{"className":85,"code":86,"language":87,"meta":88,"style":88},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","echo hello world \n# hello world\n","bash","",[29,90,91,110],{"__ignoreMap":88},[92,93,96,99,103,106],"span",{"class":94,"line":95},"line",1,[92,97,71],{"class":98},"s2Zo4",[92,100,102],{"class":101},"sfazB"," hello",[92,104,105],{"class":101}," world",[92,107,109],{"class":108},"sTEyZ"," \n",[92,111,113],{"class":94,"line":112},2,[92,114,116],{"class":115},"sHwdD","# hello world\n",[25,118,119],{},"Looks pretty simple enough build a parser by splitting strings by spaces, right? What about something like this",[83,121,123],{"className":85,"code":122,"language":87,"meta":88,"style":88},"echo \"spaces    will    work\" spaces    wont    work    now \n# spaces    will    work spaces wont work\n",[29,124,125,153],{"__ignoreMap":88},[92,126,127,129,133,136,139,142,145,148,151],{"class":94,"line":95},[92,128,71],{"class":98},[92,130,132],{"class":131},"sMK4o"," \"",[92,134,135],{"class":101},"spaces    will    work",[92,137,138],{"class":131},"\"",[92,140,141],{"class":101}," spaces",[92,143,144],{"class":101},"    wont",[92,146,147],{"class":101},"    work",[92,149,150],{"class":101},"    now",[92,152,109],{"class":108},[92,154,155],{"class":94,"line":112},[92,156,157],{"class":115},"# spaces    will    work spaces wont work\n",[25,159,160,161,164],{},"See the problem now? That's when I realized splitting by spaces will no longer work and we have to build a ",[45,162,163],{},"lexer"," for parsing.",[58,166,168],{"id":167},"they-dont-just-run","They don't just run?",[25,170,171,172,175,176,179,180,183],{},"One of big moment was realizing that a shell doesn't actually ",[45,173,174],{},"\"run\""," programs, it gives birth to them. To run an external command like ",[29,177,178],{},"grep"," or ",[29,181,182],{},"wc",", the shell can't just jump to that code or it would lose its own state.",[185,186,187,199,205],"ul",{},[188,189,190,193,194,198],"li",{},[45,191,192],{},"fork():"," The shell creates a literal clone of itself (",[195,196,197],"em",{},"child process",").",[188,200,201,204],{},[45,202,203],{},"exec():"," The child process replaces its own memory space with the new program's code.",[188,206,207,210,211,214],{},[45,208,209],{},"wait():"," The parent (",[195,212,213],{},"shell",") pauses until the child finishes, collecting its exit code.",[58,216,218],{"id":217},"where-is-the-binary-is-it-here","Where is the binary, is it here?",[25,220,221,222,224,225,228,229,231,232,235],{},"We just type ",[29,223,31],{},", but where is it's binary and how does shell know about it? That's were ",[29,226,227],{},"$PATH"," environment variable comes in. We have to implement a manual lookup system that reads the ",[29,230,227],{}," environment variable, splits it into directories (",[195,233,234],{},"delimited by colons","), and iterate all the binaries and check for executable permissions and all these happens in milliseconds. Autocomplete is implemented the same way but with a lot more iterations.",[20,237,239],{"id":238},"reinventing-the-wheel","Reinventing the wheel?",[25,241,242],{},"Did I reinvent the wheel? Absolutely. Did I learn things that I have never learnt before? Yes, a lot. I ventured into the deep grounds where keystrokes interacts with software, it was one hell of a ride. The contract between the kernel and the software will never seize to amaze me and that's why I will keep building and practice these dark arts.",[244,245,246],"style",{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}",{"title":88,"searchDepth":112,"depth":112,"links":248},[249,256],{"id":22,"depth":112,"text":23,"children":250},[251,254,255],{"id":60,"depth":252,"text":253},3,"What do you mean I can't use .split(\" \")?",{"id":167,"depth":252,"text":168},{"id":217,"depth":252,"text":218},{"id":238,"depth":112,"text":239},"2026-02-11","md","https:\u002F\u002Fgithub.com\u002FJa4V8s28Ck\u002FRay-Shell","\u002Fprojects\u002Fray-shell.jpg",true,[263,264,265,266],"golang","unix-shell","operating-system","system-programming",null,{},"\u002Fprojects\u002Fray-shell",{"title":5,"description":88},"projects\u002Fray-shell","pUvwwEoY83NDc--_BovFiT9drHgHgg8a4Tb5-KQGaDo"]