[{"data":1,"prerenderedAt":555},["ShallowReactive",2],{"mdc-65r9sx-key":3,"mdc-955uni-key":21,"mdc--pmkzzt-key":91,"mdc--b3uiey-key":203,"mdc--3phavk-key":215,"mdc--r8k83y-key":326,"mdc--ndpz3f-key":340,"mdc--glhd4q-key":483,"mdc--hgm46m-key":496,"mdc-4lr70a-key":520,"mdc--tezjia-key":530},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,16],{"type":9,"tag":10,"props":11,"children":12},"element","p",{},[13],{"type":14,"value":15},"text","In the first lesson I said the shell lets you combine small programs in ways nobody anticipated. This is that lesson.",{"type":9,"tag":10,"props":17,"children":18},{},[19],{"type":14,"value":20},"Everything so far has been individual commands. What makes the command line genuinely powerful — more powerful than any GUI, for a large class of problems — is that its programs are designed to be connected.",{"data":22,"body":23},{},{"type":6,"children":24},[25,32,37,73,86],{"type":9,"tag":26,"props":27,"children":29},"h2",{"id":28},"three-streams",[30],{"type":14,"value":31},"Three streams",{"type":9,"tag":10,"props":33,"children":34},{},[35],{"type":14,"value":36},"Every process starts with three channels already open:",{"type":9,"tag":38,"props":39,"children":40},"ul",{},[41,53,63],{"type":9,"tag":42,"props":43,"children":44},"li",{},[45,51],{"type":9,"tag":46,"props":47,"children":48},"strong",{},[49],{"type":14,"value":50},"stdin",{"type":14,"value":52}," (0) — where input comes from. Usually your keyboard.",{"type":9,"tag":42,"props":54,"children":55},{},[56,61],{"type":9,"tag":46,"props":57,"children":58},{},[59],{"type":14,"value":60},"stdout",{"type":14,"value":62}," (1) — where normal output goes. Usually your screen.",{"type":9,"tag":42,"props":64,"children":65},{},[66,71],{"type":9,"tag":46,"props":67,"children":68},{},[69],{"type":14,"value":70},"stderr",{"type":14,"value":72}," (2) — where error messages go. Also usually your screen.",{"type":9,"tag":10,"props":74,"children":75},{},[76,78,84],{"type":14,"value":77},"The screen is just the ",{"type":9,"tag":79,"props":80,"children":81},"em",{},[82],{"type":14,"value":83},"default",{"type":14,"value":85},". Every one of these can be pointed somewhere else, and that's all redirection is.",{"type":9,"tag":10,"props":87,"children":88},{},[89],{"type":14,"value":90},"The split between stdout and stderr is deliberate and useful: it means you can capture a program's results while still seeing its complaints, because they travel on separate channels even though they land in the same place by default.",{"data":92,"body":93},{},{"type":6,"children":94},[95,101,114,162,167,176,189,194],{"type":9,"tag":26,"props":96,"children":98},{"id":97},"redirecting-to-files",[99],{"type":14,"value":100},"Redirecting to files",{"type":9,"tag":102,"props":103,"children":107},"pre",{"className":104,"code":106,"language":14},[105],"language-text","ls > files.txt          # stdout into a file, replacing its contents\nls >> files.txt         # stdout appended to the end instead\n",[108],{"type":9,"tag":109,"props":110,"children":112},"code",{"__ignoreMap":111},"",[113],{"type":14,"value":106},{"type":9,"tag":10,"props":115,"children":116},{},[117,119,125,127,133,135,145,147,152,154,160],{"type":14,"value":118},"The difference between ",{"type":9,"tag":109,"props":120,"children":122},{"className":121},[],[123],{"type":14,"value":124},">",{"type":14,"value":126}," and ",{"type":9,"tag":109,"props":128,"children":130},{"className":129},[],[131],{"type":14,"value":132},">>",{"type":14,"value":134}," is worth burning in: ",{"type":9,"tag":46,"props":136,"children":137},{},[138,143],{"type":9,"tag":109,"props":139,"children":141},{"className":140},[],[142],{"type":14,"value":124},{"type":14,"value":144}," truncates the file first.",{"type":14,"value":146}," Pointing ",{"type":9,"tag":109,"props":148,"children":150},{"className":149},[],[151],{"type":14,"value":124},{"type":14,"value":153}," at something that already has contents destroys them, instantly, with no warning. It's the second-most common way people lose work at a terminal, after ",{"type":9,"tag":109,"props":155,"children":157},{"className":156},[],[158],{"type":14,"value":159},"rm",{"type":14,"value":161},".",{"type":9,"tag":10,"props":163,"children":164},{},[165],{"type":14,"value":166},"Errors need their own redirect, because they're on channel 2:",{"type":9,"tag":102,"props":168,"children":171},{"className":169,"code":170,"language":14},[105],"./script.sh 2> errors.txt         # errors to a file, output still on screen\n./script.sh > out.txt 2>&1        # both into one file\n./script.sh &> everything.txt     # shorthand for the same thing (bash)\n",[172],{"type":9,"tag":109,"props":173,"children":174},{"__ignoreMap":111},[175],{"type":14,"value":170},{"type":9,"tag":10,"props":177,"children":178},{},[179,181,187],{"type":14,"value":180},"That ",{"type":9,"tag":109,"props":182,"children":184},{"className":183},[],[185],{"type":14,"value":186},"2>&1",{"type":14,"value":188}," reads as \"send channel 2 to wherever channel 1 is currently going\". It looks cryptic and it's worth recognising, because it appears in nearly every cron job and CI script you'll ever read.",{"type":9,"tag":10,"props":190,"children":191},{},[192],{"type":14,"value":193},"And input can come from a file instead of the keyboard:",{"type":9,"tag":102,"props":195,"children":198},{"className":196,"code":197,"language":14},[105],"sort \u003C names.txt\n",[199],{"type":9,"tag":109,"props":200,"children":201},{"__ignoreMap":111},[202],{"type":14,"value":197},{"data":204,"body":205},{},{"type":6,"children":206},[207],{"type":9,"tag":208,"props":209,"children":214},"quiz",{":answer":210,":options":211,"explanation":212,"question":213},"0","[\"It was truncated — the old contents are gone and the file now holds one line\",\"The new line was added to the end\",\"The command failed because the file already existed\"]","`>` truncates before writing. `>>` is the one that appends. This is why the habit of typing `>>` unless you specifically mean to replace is worth building.","You run `echo hello > notes.txt`, but notes.txt already contained a week of work. What happened to it?",[],{"data":216,"body":217},{},{"type":6,"children":218},[219,225,250,259,292,312,321],{"type":9,"tag":26,"props":220,"children":222},{"id":221},"the-pipe",[223],{"type":14,"value":224},"The pipe",{"type":9,"tag":10,"props":226,"children":227},{},[228,233,235,241,243,248],{"type":9,"tag":109,"props":229,"children":231},{"className":230},[],[232],{"type":14,"value":124},{"type":14,"value":234}," sends output to a file. The pipe, ",{"type":9,"tag":109,"props":236,"children":238},{"className":237},[],[239],{"type":14,"value":240},"|",{"type":14,"value":242},", sends it to ",{"type":9,"tag":46,"props":244,"children":245},{},[246],{"type":14,"value":247},"another program's input",{"type":14,"value":249},":",{"type":9,"tag":102,"props":251,"children":254},{"className":252,"code":253,"language":14},[105],"ps aux | grep node\n",[255],{"type":9,"tag":109,"props":256,"children":257},{"__ignoreMap":111},[258],{"type":14,"value":253},{"type":9,"tag":10,"props":260,"children":261},{},[262,268,270,276,278,283,285,290],{"type":9,"tag":109,"props":263,"children":265},{"className":264},[],[266],{"type":14,"value":267},"ps aux",{"type":14,"value":269}," writes a long list to stdout. Instead of your screen, that list becomes ",{"type":9,"tag":109,"props":271,"children":273},{"className":272},[],[274],{"type":14,"value":275},"grep",{"type":14,"value":277},"'s stdin. ",{"type":9,"tag":109,"props":279,"children":281},{"className":280},[],[282],{"type":14,"value":275},{"type":14,"value":284}," reads it, keeps only lines matching \"node\", and writes those to ",{"type":9,"tag":79,"props":286,"children":287},{},[288],{"type":14,"value":289},"its",{"type":14,"value":291}," stdout — which is your screen.",{"type":9,"tag":10,"props":293,"children":294},{},[295,297,303,305,310],{"type":14,"value":296},"Neither program knows the other exists. ",{"type":9,"tag":109,"props":298,"children":300},{"className":299},[],[301],{"type":14,"value":302},"ps",{"type":14,"value":304}," doesn't know it's being filtered; ",{"type":9,"tag":109,"props":306,"children":308},{"className":307},[],[309],{"type":14,"value":275},{"type":14,"value":311}," doesn't know where the text came from. That independence is the whole design, and it's why pipes compose without limit:",{"type":9,"tag":102,"props":313,"children":316},{"className":314,"code":315,"language":14},[105],"cat access.log | grep \" 500 \" | wc -l\n",[317],{"type":9,"tag":109,"props":318,"children":319},{"__ignoreMap":111},[320],{"type":14,"value":315},{"type":9,"tag":10,"props":322,"children":323},{},[324],{"type":14,"value":325},"Read left to right: take the log, keep only lines containing a 500 status, count them. Three simple tools answering a question none of them was written for.",{"data":327,"body":328},{},{"type":6,"children":329},[330,335],{"type":9,"tag":331,"props":332,"children":334},"terminal-teaser",{":lines":333},"[{\"cmd\":\"cat access.log | grep \\\" 500 \\\" | wc -l\",\"out\":\"27\"},{\"cmd\":\"cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -3\",\"out\":\"   412 10.0.0.7\\n   288 10.0.0.31\\n   106 10.0.0.4\"}]",[],{"type":9,"tag":10,"props":336,"children":337},{},[338],{"type":14,"value":339},"That second one finds the three IP addresses hitting your server hardest: take each line's first field, sort them, count each unique value, sort those counts numerically in reverse, show the top three. Six tools, one line, no script, no program written.",{"data":341,"body":342},{},{"type":6,"children":343},[344,350,355],{"type":9,"tag":26,"props":345,"children":347},{"id":346},"the-pieces-worth-knowing",[348],{"type":14,"value":349},"The pieces worth knowing",{"type":9,"tag":10,"props":351,"children":352},{},[353],{"type":14,"value":354},"A small vocabulary covers most real pipelines:",{"type":9,"tag":38,"props":356,"children":357},{},[358,393,404,431,442,461,472],{"type":9,"tag":42,"props":359,"children":360},{},[361,367,369,375,377,383,385,391],{"type":9,"tag":109,"props":362,"children":364},{"className":363},[],[365],{"type":14,"value":366},"grep pattern",{"type":14,"value":368}," — keep matching lines (",{"type":9,"tag":109,"props":370,"children":372},{"className":371},[],[373],{"type":14,"value":374},"-v",{"type":14,"value":376}," inverts it, ",{"type":9,"tag":109,"props":378,"children":380},{"className":379},[],[381],{"type":14,"value":382},"-i",{"type":14,"value":384}," ignores case, ",{"type":9,"tag":109,"props":386,"children":388},{"className":387},[],[389],{"type":14,"value":390},"-c",{"type":14,"value":392}," counts)",{"type":9,"tag":42,"props":394,"children":395},{},[396,402],{"type":9,"tag":109,"props":397,"children":399},{"className":398},[],[400],{"type":14,"value":401},"wc -l",{"type":14,"value":403}," — count lines",{"type":9,"tag":42,"props":405,"children":406},{},[407,413,415,421,423,429],{"type":9,"tag":109,"props":408,"children":410},{"className":409},[],[411],{"type":14,"value":412},"sort",{"type":14,"value":414}," — sort lines (",{"type":9,"tag":109,"props":416,"children":418},{"className":417},[],[419],{"type":14,"value":420},"-n",{"type":14,"value":422}," numerically, ",{"type":9,"tag":109,"props":424,"children":426},{"className":425},[],[427],{"type":14,"value":428},"-r",{"type":14,"value":430}," reversed)",{"type":9,"tag":42,"props":432,"children":433},{},[434,440],{"type":9,"tag":109,"props":435,"children":437},{"className":436},[],[438],{"type":14,"value":439},"uniq -c",{"type":14,"value":441}," — collapse adjacent duplicates and count them (needs sorted input)",{"type":9,"tag":42,"props":443,"children":444},{},[445,451,453,459],{"type":9,"tag":109,"props":446,"children":448},{"className":447},[],[449],{"type":14,"value":450},"head",{"type":14,"value":452}," / ",{"type":9,"tag":109,"props":454,"children":456},{"className":455},[],[457],{"type":14,"value":458},"tail",{"type":14,"value":460}," — first or last N lines",{"type":9,"tag":42,"props":462,"children":463},{},[464,470],{"type":9,"tag":109,"props":465,"children":467},{"className":466},[],[468],{"type":14,"value":469},"cut -d, -f2",{"type":14,"value":471}," — pull out a field by delimiter",{"type":9,"tag":42,"props":473,"children":474},{},[475,481],{"type":9,"tag":109,"props":476,"children":478},{"className":477},[],[479],{"type":14,"value":480},"awk '{print $1}'",{"type":14,"value":482}," — pull out a column by position",{"data":484,"body":485},{},{"type":6,"children":486},[487],{"type":9,"tag":488,"props":489,"children":495},"fill-blank",{":answer":490,"explanation":491,"hint":492,"placeholder":493,"prompt":494},"[\"grep error access.log | wc -l\",\"grep -c error access.log\",\"cat access.log | grep error | wc -l\",\"grep \\\"error\\\" access.log | wc -l\"]","`grep error access.log | wc -l` is the classic pipeline. `grep -c error access.log` does it in one step — both are right, and knowing the pipeline version matters more because it generalises.","Filter the file with grep, then count the surviving lines.","grep ...","Count how many lines in `access.log` contain the word \"error\".",[],{"data":497,"body":498},{},{"type":6,"children":499},[500],{"type":9,"tag":501,"props":502,"children":504},"deep-dive",{"title":503},"Why this design won",[505,510,515],{"type":9,"tag":10,"props":506,"children":507},{},[508],{"type":14,"value":509},"In 1978 Doug McIlroy wrote down the Unix philosophy as: write programs that do one thing well, and write programs to work together on text streams.",{"type":9,"tag":10,"props":511,"children":512},{},[513],{"type":14,"value":514},"The bet was that a small set of sharp tools plus a way to connect them beats a large set of feature-rich ones. Nearly fifty years later that bet keeps paying: the pipeline above analysing a web server log was composed from programs written decades before web servers existed, by people who had no idea what they'd eventually be used for.",{"type":9,"tag":10,"props":516,"children":517},{},[518],{"type":14,"value":519},"That's the real reason to learn this. Not because the commands are hard — they aren't — but because once you think in pipelines, a whole class of problems stops requiring you to write a program at all.",{"data":521,"body":522},{},{"type":6,"children":523},[524],{"type":9,"tag":208,"props":525,"children":529},{":answer":210,":options":526,"explanation":527,"question":528},"[\"The stdout of `ps aux`, connected to grep's stdin by the pipe\",\"A temporary file that ps wrote first\",\"The terminal, after ps finished printing to it\"]","The pipe connects one program's stdout directly to the next one's stdin — no temp file, and the data flows while both are running. Neither program knows anything about the other.","In `ps aux | grep node`, what is grep reading from?",[],{"data":531,"body":532},{},{"type":6,"children":533},[534,540,545,550],{"type":9,"tag":26,"props":535,"children":537},{"id":536},"where-this-goes-next",[538],{"type":14,"value":539},"Where this goes next",{"type":9,"tag":10,"props":541,"children":542},{},[543],{"type":14,"value":544},"You now have the model the rest of Linux is built on — a filesystem tree, permissions, processes, and streams you can wire together.",{"type":9,"tag":10,"props":546,"children":547},{},[548],{"type":14,"value":549},"The natural next step is to stop typing these one at a time and start saving them: shell scripts, variables, loops, and conditionals. After that, containers — because a Docker container is, at bottom, a process with its own view of the filesystem, and everything in this course applies directly.",{"type":9,"tag":10,"props":551,"children":552},{},[553],{"type":14,"value":554},"That's what the next courses cover.",1787908866327]