[{"data":1,"prerenderedAt":565},["ShallowReactive",2],{"mdc--k86gp2-key":3,"mdc--4pkcfa-key":29,"mdc-db2qqq-key":66,"mdc-cndc51-key":75,"mdc-tmgs8k-key":189,"mdc--fkpart-key":238,"mdc--jzpqb1-key":250,"mdc-hx0axb-key":298,"mdc--vbfq8z-key":310,"mdc--obj0h9-key":339,"mdc--fin2c-key":420,"mdc-aap381-key":550},{"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","A variable is a name holding a piece of text. The shell has no types worth speaking of — everything is a string, and the programs you hand those strings to decide whether they look like numbers.",{"type":9,"tag":10,"props":17,"children":18},{},[19,21,27],{"type":14,"value":20},"Assignment looks like every other language. It is the two rules ",{"type":9,"tag":22,"props":23,"children":24},"em",{},[25],{"type":14,"value":26},"around",{"type":14,"value":28}," the assignment that trip people up, and they trip up everyone exactly once.",{"data":30,"body":31},{},{"type":6,"children":32},[33,40,53],{"type":9,"tag":34,"props":35,"children":37},"h2",{"id":36},"assigning-and-reading",[38],{"type":14,"value":39},"Assigning and reading",{"type":9,"tag":41,"props":42,"children":46},"pre",{"className":43,"code":45,"language":14},[44],"language-text","NAME=\"Ada\"\necho $NAME\n",[47],{"type":9,"tag":48,"props":49,"children":51},"code",{"__ignoreMap":50},"",[52],{"type":14,"value":45},{"type":9,"tag":10,"props":54,"children":55},{},[56,58,64],{"type":14,"value":57},"Set with a bare name, read with a ",{"type":9,"tag":48,"props":59,"children":61},{"className":60},[],[62],{"type":14,"value":63},"$",{"type":14,"value":65}," in front. That asymmetry is the shell telling you the difference between \"the box\" and \"what's in the box\".",{"data":67,"body":68},{},{"type":6,"children":69},[70],{"type":9,"tag":71,"props":72,"children":74},"terminal-teaser",{":lines":73},"[{\"cmd\":\"NAME=\\\"Ada\\\"\",\"out\":\"\"},{\"cmd\":\"echo $NAME\",\"out\":\"Ada\"},{\"cmd\":\"echo \\\"Hello, $NAME\\\"\",\"out\":\"Hello, Ada\"},{\"cmd\":\"echo 'Hello, $NAME'\",\"out\":\"Hello, $NAME\"}]",[],{"data":76,"body":77},{},{"type":6,"children":78},[79,91,96,105,110,119,170],{"type":9,"tag":34,"props":80,"children":82},{"id":81},"rule-one-no-spaces-around-the",[83,85],{"type":14,"value":84},"Rule one: no spaces around the ",{"type":9,"tag":48,"props":86,"children":88},{"className":87},[],[89],{"type":14,"value":90},"=",{"type":9,"tag":10,"props":92,"children":93},{},[94],{"type":14,"value":95},"This works:",{"type":9,"tag":41,"props":97,"children":100},{"className":98,"code":99,"language":14},[44],"NAME=\"Ada\"\n",[101],{"type":9,"tag":48,"props":102,"children":103},{"__ignoreMap":50},[104],{"type":14,"value":99},{"type":9,"tag":10,"props":106,"children":107},{},[108],{"type":14,"value":109},"This does not:",{"type":9,"tag":41,"props":111,"children":114},{"className":112,"code":113,"language":14},[44],"NAME = \"Ada\"\nsh: NAME: command not found\n",[115],{"type":9,"tag":48,"props":116,"children":117},{"__ignoreMap":50},[118],{"type":14,"value":113},{"type":9,"tag":10,"props":120,"children":121},{},[122,124,130,132,137,139,145,147,153,155,160,162,168],{"type":14,"value":123},"The error tells you exactly what happened. The shell splits a line into words and assumes the first word is a command. ",{"type":9,"tag":48,"props":125,"children":127},{"className":126},[],[128],{"type":14,"value":129},"NAME=Ada",{"type":14,"value":131}," has no spaces, so it is one word containing an ",{"type":9,"tag":48,"props":133,"children":135},{"className":134},[],[136],{"type":14,"value":90},{"type":14,"value":138},", which the shell recognises as an assignment. ",{"type":9,"tag":48,"props":140,"children":142},{"className":141},[],[143],{"type":14,"value":144},"NAME = \"Ada\"",{"type":14,"value":146}," is three words, so the shell tried to run a program called ",{"type":9,"tag":48,"props":148,"children":150},{"className":149},[],[151],{"type":14,"value":152},"NAME",{"type":14,"value":154}," with the arguments ",{"type":9,"tag":48,"props":156,"children":158},{"className":157},[],[159],{"type":14,"value":90},{"type":14,"value":161}," and ",{"type":9,"tag":48,"props":163,"children":165},{"className":164},[],[166],{"type":14,"value":167},"Ada",{"type":14,"value":169},".",{"type":9,"tag":10,"props":171,"children":172},{},[173,175,180,182,187],{"type":14,"value":174},"Once you see it that way it stops being an arbitrary rule. There is no way the shell ",{"type":9,"tag":22,"props":176,"children":177},{},[178],{"type":14,"value":179},"could",{"type":14,"value":181}," allow the spaces without losing the ability to pass ",{"type":9,"tag":48,"props":183,"children":185},{"className":184},[],[186],{"type":14,"value":90},{"type":14,"value":188}," as an argument.",{"data":190,"body":191},{},{"type":6,"children":192},[193,199,208,229],{"type":9,"tag":34,"props":194,"children":196},{"id":195},"rule-two-quote-the-value",[197],{"type":14,"value":198},"Rule two: quote the value",{"type":9,"tag":41,"props":200,"children":203},{"className":201,"code":202,"language":14},[44],"MSG=Hello World\n",[204],{"type":9,"tag":48,"props":205,"children":206},{"__ignoreMap":50},[207],{"type":14,"value":202},{"type":9,"tag":10,"props":209,"children":210},{},[211,213,219,221,227],{"type":14,"value":212},"fails too, and for the same reason: the shell sees ",{"type":9,"tag":48,"props":214,"children":216},{"className":215},[],[217],{"type":14,"value":218},"MSG=Hello",{"type":14,"value":220}," (an assignment) followed by ",{"type":9,"tag":48,"props":222,"children":224},{"className":223},[],[225],{"type":14,"value":226},"World",{"type":14,"value":228}," (a command to run with that variable set). A variable holds one value, so anything containing a space has to be quoted.",{"type":9,"tag":41,"props":230,"children":233},{"className":231,"code":232,"language":14},[44],"MSG=\"Hello World\"\n",[234],{"type":9,"tag":48,"props":235,"children":236},{"__ignoreMap":50},[237],{"type":14,"value":232},{"data":239,"body":240},{},{"type":6,"children":241},[242],{"type":9,"tag":243,"props":244,"children":249},"quiz",{":answer":245,":options":246,"explanation":247,"question":248},"0","[\"The shell reads it as running the command `COUNT` with arguments `=` and `5`\",\"Numbers must be assigned with `-eq` rather than `=`\",\"The value needs quoting, as `COUNT = \\\"5\\\"`\"]","Spaces separate words, and the first word of a line is a command. Without spaces, `COUNT=5` is a single word the shell recognises as an assignment. Adding quotes doesn't help — `COUNT = \"5\"` still has spaces, so it still tries to run `COUNT`.","Why does `COUNT = 5` produce \"command not found\"?",[],{"data":251,"body":252},{},{"type":6,"children":253},[254,260,265,274,279],{"type":9,"tag":34,"props":255,"children":257},{"id":256},"quoting-when-you-read-too",[258],{"type":14,"value":259},"Quoting when you read, too",{"type":9,"tag":10,"props":261,"children":262},{},[263],{"type":14,"value":264},"Reading a variable is where quoting really earns its keep. Compare:",{"type":9,"tag":41,"props":266,"children":269},{"className":267,"code":268,"language":14},[44],"FILE=\"my report.txt\"\nrm $FILE       # runs: rm my report.txt   — two files, both wrong\nrm \"$FILE\"     # runs: rm \"my report.txt\" — one file, correct\n",[270],{"type":9,"tag":48,"props":271,"children":272},{"__ignoreMap":50},[273],{"type":14,"value":268},{"type":9,"tag":10,"props":275,"children":276},{},[277],{"type":14,"value":278},"After substituting the value, the shell splits the result into words again. Without quotes, a value containing a space becomes two arguments. This is the single most common bug in shell scripts, and it is invisible until someone's filename has a space in it — which, on any machine touched by a human, is eventually.",{"type":9,"tag":10,"props":280,"children":281},{},[282,284],{"type":14,"value":283},"The habit worth building now: ",{"type":9,"tag":285,"props":286,"children":287},"strong",{},[288,290,296],{"type":14,"value":289},"write ",{"type":9,"tag":48,"props":291,"children":293},{"className":292},[],[294],{"type":14,"value":295},"\"$VAR\"",{"type":14,"value":297},", always, unless you have a specific reason not to.",{"data":299,"body":300},{},{"type":6,"children":301},[302],{"type":9,"tag":303,"props":304,"children":309},"fill-blank",{":answer":305,"hint":306,"placeholder":307,"prompt":308},"[\"rm \\\"$TARGET\\\"\",\"rm -- \\\"$TARGET\\\"\"]","The variable expansion needs to survive word splitting.","rm ...","Delete the file whose name is held in the variable `TARGET`, safely, even if that name contains spaces.",[],{"data":311,"body":312},{},{"type":6,"children":313},[314,320,325,334],{"type":9,"tag":34,"props":315,"children":317},{"id":316},"curly-braces-disambiguate",[318],{"type":14,"value":319},"Curly braces disambiguate",{"type":9,"tag":10,"props":321,"children":322},{},[323],{"type":14,"value":324},"The shell reads a variable name greedily — as far as the characters allow.",{"type":9,"tag":41,"props":326,"children":329},{"className":327,"code":328,"language":14},[44],"FOO=sun\necho $fooshine     # empty: there is no variable \"fooshine\"\necho ${FOO}shine   # sunshine\n",[330],{"type":9,"tag":48,"props":331,"children":332},{"__ignoreMap":50},[333],{"type":14,"value":328},{"type":9,"tag":10,"props":335,"children":336},{},[337],{"type":14,"value":338},"The braces say where the name stops. You will mostly see them used exactly like this, gluing a variable to text that follows it. In the next lessons they pick up some genuinely powerful extra behaviour.",{"data":340,"body":341},{},{"type":6,"children":342},[343,349,360,369],{"type":9,"tag":34,"props":344,"children":346},{"id":345},"reading-input",[347],{"type":14,"value":348},"Reading input",{"type":9,"tag":10,"props":350,"children":351},{},[352,358],{"type":9,"tag":48,"props":353,"children":355},{"className":354},[],[356],{"type":14,"value":357},"read",{"type":14,"value":359}," takes a line from standard input and puts it in a variable:",{"type":9,"tag":41,"props":361,"children":364},{"className":362,"code":363,"language":14},[44],"#!/bin/sh\nprintf \"What is your name? \"\nread NAME\necho \"Hello, $NAME\"\n",[365],{"type":9,"tag":48,"props":366,"children":367},{"__ignoreMap":50},[368],{"type":14,"value":363},{"type":9,"tag":10,"props":370,"children":371},{},[372,374,380,382,388,390,396,398,403,405,411,413,418],{"type":14,"value":373},"Note ",{"type":9,"tag":48,"props":375,"children":377},{"className":376},[],[378],{"type":14,"value":379},"printf",{"type":14,"value":381}," rather than ",{"type":9,"tag":48,"props":383,"children":385},{"className":384},[],[386],{"type":14,"value":387},"echo -n",{"type":14,"value":389},". Suppressing the trailing newline is the one thing ",{"type":9,"tag":48,"props":391,"children":393},{"className":392},[],[394],{"type":14,"value":395},"echo",{"type":14,"value":397}," does differently on every shell: ",{"type":9,"tag":48,"props":399,"children":401},{"className":400},[],[402],{"type":14,"value":387},{"type":14,"value":404}," works on bash, ",{"type":9,"tag":48,"props":406,"children":408},{"className":407},[],[409],{"type":14,"value":410},"echo \"...\\c\"",{"type":14,"value":412}," works on dash and the original Bourne shell, and each prints the other's syntax literally. ",{"type":9,"tag":48,"props":414,"children":416},{"className":415},[],[417],{"type":14,"value":379},{"type":14,"value":419}," behaves the same everywhere, which is why portable scripts use it whenever the output is anything more than a plain line of text.",{"data":421,"body":422},{},{"type":6,"children":423},[424],{"type":9,"tag":425,"props":426,"children":428},"deep-dive",{"title":427},"Shell variables and environment variables",[429,441,450,468,477,537],{"type":9,"tag":10,"props":430,"children":431},{},[432,434,439],{"type":14,"value":433},"Setting a variable makes it visible to ",{"type":9,"tag":22,"props":435,"children":436},{},[437],{"type":14,"value":438},"this shell",{"type":14,"value":440},". It does not make it visible to programs the shell runs:",{"type":9,"tag":41,"props":442,"children":445},{"className":443,"code":444,"language":14},[44],"GREETING=\"hi\"\nsh -c 'echo \"child sees: $GREETING\"'\nchild sees:\n",[446],{"type":9,"tag":48,"props":447,"children":448},{"__ignoreMap":50},[449],{"type":14,"value":444},{"type":9,"tag":10,"props":451,"children":452},{},[453,459,461,466],{"type":9,"tag":48,"props":454,"children":456},{"className":455},[],[457],{"type":14,"value":458},"export",{"type":14,"value":460}," is what promotes a shell variable into the ",{"type":9,"tag":285,"props":462,"children":463},{},[464],{"type":14,"value":465},"environment",{"type":14,"value":467}," — the set of strings the kernel hands to every program the shell starts:",{"type":9,"tag":41,"props":469,"children":472},{"className":470,"code":471,"language":14},[44],"export GREETING=\"hi\"\nsh -c 'echo \"child sees: $GREETING\"'\nchild sees: hi\n",[473],{"type":9,"tag":48,"props":474,"children":475},{"__ignoreMap":50},[476],{"type":14,"value":471},{"type":9,"tag":10,"props":478,"children":479},{},[480,482,488,490,496,497,503,505,511,513,519,521,527,529,535],{"type":14,"value":481},"This is the whole mechanism behind ",{"type":9,"tag":48,"props":483,"children":485},{"className":484},[],[486],{"type":14,"value":487},"PATH",{"type":14,"value":489},", ",{"type":9,"tag":48,"props":491,"children":493},{"className":492},[],[494],{"type":14,"value":495},"HOME",{"type":14,"value":489},{"type":9,"tag":48,"props":498,"children":500},{"className":499},[],[501],{"type":14,"value":502},"LANG",{"type":14,"value":504},", and every ",{"type":9,"tag":48,"props":506,"children":508},{"className":507},[],[509],{"type":14,"value":510},"DATABASE_URL",{"type":14,"value":512}," you have ever put in a ",{"type":9,"tag":48,"props":514,"children":516},{"className":515},[],[517],{"type":14,"value":518},".env",{"type":14,"value":520}," file. Run ",{"type":9,"tag":48,"props":522,"children":524},{"className":523},[],[525],{"type":14,"value":526},"env",{"type":14,"value":528}," to see the exported set; ",{"type":9,"tag":48,"props":530,"children":532},{"className":531},[],[533],{"type":14,"value":534},"set",{"type":14,"value":536}," shows everything, exported or not.",{"type":9,"tag":10,"props":538,"children":539},{},[540,542,548],{"type":14,"value":541},"The direction only goes one way. A child process cannot change its parent's environment — which is, once again, why a script cannot ",{"type":9,"tag":48,"props":543,"children":545},{"className":544},[],[546],{"type":14,"value":547},"cd",{"type":14,"value":549}," your shell for you.",{"data":551,"body":552},{},{"type":6,"children":553},[554,560],{"type":9,"tag":243,"props":555,"children":559},{":answer":245,":options":556,"explanation":557,"question":558},"[\"`TOKEN` was never exported, so it isn't in the child process's environment\",\"Shell variables are lowercase-only when exported\",\"Python cannot read variables set after the interpreter starts\"]","Assignment creates a shell variable, which is private to that shell. Only `export TOKEN=abc123` (or `TOKEN=abc123 python prog.py`, which exports it for that one command) puts it in the environment the child inherits.","A script sets `TOKEN=abc123` then runs a Python program that reads `os.environ[\"TOKEN\"]`. The program raises KeyError. Why?",[],{"type":9,"tag":10,"props":561,"children":562},{},[563],{"type":14,"value":564},"Next up: quoting and wildcards — the rest of what happens to a line between you pressing enter and a program actually running.",1787908866327]