[{"data":1,"prerenderedAt":1089},["ShallowReactive",2],{"mdc--sdtajm-key":3,"mdc--y5fhi8-key":37,"mdc--nxuahq-key":55,"mdc-qqcbfq-key":128,"mdc--7i8gtq-key":140,"mdc-sqzygi-key":247,"mdc-bitlj8-key":257,"mdc--hhq6se-key":708,"mdc--wvl8lh-key":847,"mdc-v80jz2-key":981,"mdc--jay2oz-key":993,"mdc--9seybd-key":1074},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,24],{"type":9,"tag":10,"props":11,"children":12},"element","p",{},[13,16,22],{"type":14,"value":15},"text","Every command that finishes leaves behind a number: ",{"type":9,"tag":17,"props":18,"children":19},"strong",{},[20],{"type":14,"value":21},"0 if it succeeded, non-zero if it didn't",{"type":14,"value":23},". That number is the only thing the shell knows about whether the last thing you did worked, and it is the foundation every conditional in this lesson is built on.",{"type":9,"tag":10,"props":25,"children":26},{},[27,29,35],{"type":14,"value":28},"Zero meaning success is backwards from most languages, and there is a reason: there is exactly one way to succeed, and many ways to fail. Non-zero values let a program say ",{"type":9,"tag":30,"props":31,"children":32},"em",{},[33],{"type":14,"value":34},"which",{"type":14,"value":36}," failure.",{"data":38,"body":39},{},{"type":6,"children":40},[41],{"type":9,"tag":42,"props":43,"children":45},"h2",{"id":44},"holds-the-last-exit-status",[46,53],{"type":9,"tag":47,"props":48,"children":50},"code",{"className":49},[],[51],{"type":14,"value":52},"$?",{"type":14,"value":54}," holds the last exit status",{"data":56,"body":57},{},{"type":6,"children":58},[59,64,82,94,107,116],{"type":9,"tag":60,"props":61,"children":63},"terminal-teaser",{":lines":62},"[{\"cmd\":\"ls /etc/hostname\",\"out\":\"/etc/hostname\"},{\"cmd\":\"echo $?\",\"out\":\"0\"},{\"cmd\":\"ls /nope\",\"out\":\"ls: cannot access '/nope': No such file or directory\"},{\"cmd\":\"echo $?\",\"out\":\"2\"}]",[],{"type":9,"tag":10,"props":65,"children":66},{},[67,72,74,80],{"type":9,"tag":47,"props":68,"children":70},{"className":69},[],[71],{"type":14,"value":52},{"type":14,"value":73}," is overwritten by every command — including the ",{"type":9,"tag":47,"props":75,"children":77},{"className":76},[],[78],{"type":14,"value":79},"echo",{"type":14,"value":81}," that prints it. Capture it in a variable the moment you need to keep it:",{"type":9,"tag":83,"props":84,"children":88},"pre",{"className":85,"code":87,"language":14},[86],"language-text","some_command\nSTATUS=$?\n",[89],{"type":9,"tag":47,"props":90,"children":92},{"__ignoreMap":91},"",[93],{"type":14,"value":87},{"type":9,"tag":10,"props":95,"children":96},{},[97,99,105],{"type":14,"value":98},"Your own script sets its exit status with ",{"type":9,"tag":47,"props":100,"children":102},{"className":101},[],[103],{"type":14,"value":104},"exit",{"type":14,"value":106},":",{"type":9,"tag":83,"props":108,"children":111},{"className":109,"code":110,"language":14},[86],"exit 0      # success\nexit 1      # generic failure\n",[112],{"type":9,"tag":47,"props":113,"children":114},{"__ignoreMap":91},[115],{"type":14,"value":110},{"type":9,"tag":10,"props":117,"children":118},{},[119,121,126],{"type":14,"value":120},"A script with no ",{"type":9,"tag":47,"props":122,"children":124},{"className":123},[],[125],{"type":14,"value":104},{"type":14,"value":127}," returns the status of its last command, which is very often not what you meant.",{"data":129,"body":130},{},{"type":6,"children":131},[132],{"type":9,"tag":133,"props":134,"children":139},"quiz",{":answer":135,":options":136,"explanation":137,"question":138},"0","[\"0, because `echo` succeeded and it was the last command\",\"Non-zero, because a command inside the script failed\",\"1, the default for any script containing a failure\"]","The script's status is the status of its last command, and `echo` almost never fails. This is exactly how a broken deploy script reports success to CI. Either end with an explicit `exit`, or use `set -e` so the script stops at the failing `cp`.","A script ends with `echo \"done\"` after a `cp` that failed. What exit status does the script return?",[],{"data":141,"body":142},{},{"type":6,"children":143},[144,162,186,203,212],{"type":9,"tag":42,"props":145,"children":147},{"id":146},"test-better-known-as",[148,154,156],{"type":9,"tag":47,"props":149,"children":151},{"className":150},[],[152],{"type":14,"value":153},"test",{"type":14,"value":155},", better known as ",{"type":9,"tag":47,"props":157,"children":159},{"className":158},[],[160],{"type":14,"value":161},"[",{"type":9,"tag":10,"props":163,"children":164},{},[165,170,172,177,179,184],{"type":9,"tag":47,"props":166,"children":168},{"className":167},[],[169],{"type":14,"value":153},{"type":14,"value":171}," evaluates a condition and exits 0 if it's true. It is more commonly written as ",{"type":9,"tag":47,"props":173,"children":175},{"className":174},[],[176],{"type":14,"value":161},{"type":14,"value":178},", which is a genuine command — a link to ",{"type":9,"tag":47,"props":180,"children":182},{"className":181},[],[183],{"type":14,"value":153},{"type":14,"value":185}," on most systems, and a builtin in every modern shell.",{"type":9,"tag":10,"props":187,"children":188},{},[189,191,196,198],{"type":14,"value":190},"That ",{"type":9,"tag":47,"props":192,"children":194},{"className":193},[],[195],{"type":14,"value":161},{"type":14,"value":197}," is a command, not punctuation, explains its single most notorious rule: ",{"type":9,"tag":17,"props":199,"children":200},{},[201],{"type":14,"value":202},"it needs spaces around it.",{"type":9,"tag":83,"props":204,"children":207},{"className":205,"code":206,"language":14},[86],"[ \"$NAME\" = \"Ada\" ]     # correct\n[\"$NAME\" = \"Ada\"]       # sh: [Ada: command not found\n",[208],{"type":9,"tag":47,"props":209,"children":210},{"__ignoreMap":91},[211],{"type":14,"value":206},{"type":9,"tag":10,"props":213,"children":214},{},[215,217,223,225,231,233,238,240,245],{"type":14,"value":216},"Without the space the shell reads ",{"type":9,"tag":47,"props":218,"children":220},{"className":219},[],[221],{"type":14,"value":222},"[\"$NAME\"",{"type":14,"value":224}," as one word — a command whose name happens to start with a bracket. And the closing ",{"type":9,"tag":47,"props":226,"children":228},{"className":227},[],[229],{"type":14,"value":230},"]",{"type":14,"value":232}," is not decoration either: ",{"type":9,"tag":47,"props":234,"children":236},{"className":235},[],[237],{"type":14,"value":153},{"type":14,"value":239}," requires it as its final argument when invoked as ",{"type":9,"tag":47,"props":241,"children":243},{"className":242},[],[244],{"type":14,"value":161},{"type":14,"value":246},", purely so the thing reads like a bracket.",{"data":248,"body":249},{},{"type":6,"children":250},[251],{"type":9,"tag":133,"props":252,"children":256},{":answer":135,":options":253,"explanation":254,"question":255},"[\"`[` is a command, and a command must be its own word to be recognised\",\"The shell's parser requires whitespace after all punctuation\",\"Without it the condition is treated as a string rather than a test\"]","`[` really is a program — `ls -l /usr/bin/[` shows it. `[$X = y ]` is the shell trying to run a command literally called `[value`, which is why the error names something that looks like your data.","Why must there be a space after `[`?",[],{"data":258,"body":259},{},{"type":6,"children":260},[261,267,272,400,405,500,550,643],{"type":9,"tag":42,"props":262,"children":264},{"id":263},"the-operators-worth-memorising",[265],{"type":14,"value":266},"The operators worth memorising",{"type":9,"tag":10,"props":268,"children":269},{},[270],{"type":14,"value":271},"Files:",{"type":9,"tag":273,"props":274,"children":275},"table",{},[276,295],{"type":9,"tag":277,"props":278,"children":279},"thead",{},[280],{"type":9,"tag":281,"props":282,"children":283},"tr",{},[284,290],{"type":9,"tag":285,"props":286,"children":287},"th",{},[288],{"type":14,"value":289},"Test",{"type":9,"tag":285,"props":291,"children":292},{},[293],{"type":14,"value":294},"True when",{"type":9,"tag":296,"props":297,"children":298},"tbody",{},[299,317,334,351,383],{"type":9,"tag":281,"props":300,"children":301},{},[302,312],{"type":9,"tag":303,"props":304,"children":305},"td",{},[306],{"type":9,"tag":47,"props":307,"children":309},{"className":308},[],[310],{"type":14,"value":311},"-e FILE",{"type":9,"tag":303,"props":313,"children":314},{},[315],{"type":14,"value":316},"it exists at all",{"type":9,"tag":281,"props":318,"children":319},{},[320,329],{"type":9,"tag":303,"props":321,"children":322},{},[323],{"type":9,"tag":47,"props":324,"children":326},{"className":325},[],[327],{"type":14,"value":328},"-f FILE",{"type":9,"tag":303,"props":330,"children":331},{},[332],{"type":14,"value":333},"it exists and is a regular file",{"type":9,"tag":281,"props":335,"children":336},{},[337,346],{"type":9,"tag":303,"props":338,"children":339},{},[340],{"type":9,"tag":47,"props":341,"children":343},{"className":342},[],[344],{"type":14,"value":345},"-d FILE",{"type":9,"tag":303,"props":347,"children":348},{},[349],{"type":14,"value":350},"it exists and is a directory",{"type":9,"tag":281,"props":352,"children":353},{},[354,378],{"type":9,"tag":303,"props":355,"children":356},{},[357,363,365,371,372],{"type":9,"tag":47,"props":358,"children":360},{"className":359},[],[361],{"type":14,"value":362},"-r",{"type":14,"value":364}," / ",{"type":9,"tag":47,"props":366,"children":368},{"className":367},[],[369],{"type":14,"value":370},"-w",{"type":14,"value":364},{"type":9,"tag":47,"props":373,"children":375},{"className":374},[],[376],{"type":14,"value":377},"-x FILE",{"type":9,"tag":303,"props":379,"children":380},{},[381],{"type":14,"value":382},"you can read / write / execute it",{"type":9,"tag":281,"props":384,"children":385},{},[386,395],{"type":9,"tag":303,"props":387,"children":388},{},[389],{"type":9,"tag":47,"props":390,"children":392},{"className":391},[],[393],{"type":14,"value":394},"-s FILE",{"type":9,"tag":303,"props":396,"children":397},{},[398],{"type":14,"value":399},"it exists and is not empty",{"type":9,"tag":10,"props":401,"children":402},{},[403],{"type":14,"value":404},"Strings:",{"type":9,"tag":273,"props":406,"children":407},{},[408,422],{"type":9,"tag":277,"props":409,"children":410},{},[411],{"type":9,"tag":281,"props":412,"children":413},{},[414,418],{"type":9,"tag":285,"props":415,"children":416},{},[417],{"type":14,"value":289},{"type":9,"tag":285,"props":419,"children":420},{},[421],{"type":14,"value":294},{"type":9,"tag":296,"props":423,"children":424},{},[425,442,459,483],{"type":9,"tag":281,"props":426,"children":427},{},[428,437],{"type":9,"tag":303,"props":429,"children":430},{},[431],{"type":9,"tag":47,"props":432,"children":434},{"className":433},[],[435],{"type":14,"value":436},"\"$A\" = \"$B\"",{"type":9,"tag":303,"props":438,"children":439},{},[440],{"type":14,"value":441},"equal",{"type":9,"tag":281,"props":443,"children":444},{},[445,454],{"type":9,"tag":303,"props":446,"children":447},{},[448],{"type":9,"tag":47,"props":449,"children":451},{"className":450},[],[452],{"type":14,"value":453},"\"$A\" != \"$B\"",{"type":9,"tag":303,"props":455,"children":456},{},[457],{"type":14,"value":458},"not equal",{"type":9,"tag":281,"props":460,"children":461},{},[462,471],{"type":9,"tag":303,"props":463,"children":464},{},[465],{"type":9,"tag":47,"props":466,"children":468},{"className":467},[],[469],{"type":14,"value":470},"-z \"$A\"",{"type":9,"tag":303,"props":472,"children":473},{},[474,476,481],{"type":14,"value":475},"empty (",{"type":9,"tag":17,"props":477,"children":478},{},[479],{"type":14,"value":480},"z",{"type":14,"value":482},"ero length)",{"type":9,"tag":281,"props":484,"children":485},{},[486,495],{"type":9,"tag":303,"props":487,"children":488},{},[489],{"type":9,"tag":47,"props":490,"children":492},{"className":491},[],[493],{"type":14,"value":494},"-n \"$A\"",{"type":9,"tag":303,"props":496,"children":497},{},[498],{"type":14,"value":499},"not empty",{"type":9,"tag":10,"props":501,"children":502},{},[503,505,511,513,518,520,526,528,534,536,542,544,549],{"type":14,"value":504},"Numbers — different operators entirely, because ",{"type":9,"tag":47,"props":506,"children":508},{"className":507},[],[509],{"type":14,"value":510},"=",{"type":14,"value":512}," on strings and ",{"type":9,"tag":47,"props":514,"children":516},{"className":515},[],[517],{"type":14,"value":510},{"type":14,"value":519}," on numbers disagree about whether ",{"type":9,"tag":47,"props":521,"children":523},{"className":522},[],[524],{"type":14,"value":525},"10",{"type":14,"value":527}," equals ",{"type":9,"tag":47,"props":529,"children":531},{"className":530},[],[532],{"type":14,"value":533},"10.0",{"type":14,"value":535},", and about whether ",{"type":9,"tag":47,"props":537,"children":539},{"className":538},[],[540],{"type":14,"value":541},"9",{"type":14,"value":543}," is less than ",{"type":9,"tag":47,"props":545,"children":547},{"className":546},[],[548],{"type":14,"value":525},{"type":14,"value":106},{"type":9,"tag":273,"props":551,"children":552},{},[553,568],{"type":9,"tag":277,"props":554,"children":555},{},[556],{"type":9,"tag":281,"props":557,"children":558},{},[559,563],{"type":9,"tag":285,"props":560,"children":561},{},[562],{"type":14,"value":289},{"type":9,"tag":285,"props":564,"children":565},{},[566],{"type":14,"value":567},"Meaning",{"type":9,"tag":296,"props":569,"children":570},{},[571,595,619],{"type":9,"tag":281,"props":572,"children":573},{},[574,590],{"type":9,"tag":303,"props":575,"children":576},{},[577,583,584],{"type":9,"tag":47,"props":578,"children":580},{"className":579},[],[581],{"type":14,"value":582},"-eq",{"type":14,"value":364},{"type":9,"tag":47,"props":585,"children":587},{"className":586},[],[588],{"type":14,"value":589},"-ne",{"type":9,"tag":303,"props":591,"children":592},{},[593],{"type":14,"value":594},"equal / not equal",{"type":9,"tag":281,"props":596,"children":597},{},[598,614],{"type":9,"tag":303,"props":599,"children":600},{},[601,607,608],{"type":9,"tag":47,"props":602,"children":604},{"className":603},[],[605],{"type":14,"value":606},"-lt",{"type":14,"value":364},{"type":9,"tag":47,"props":609,"children":611},{"className":610},[],[612],{"type":14,"value":613},"-le",{"type":9,"tag":303,"props":615,"children":616},{},[617],{"type":14,"value":618},"less than / less or equal",{"type":9,"tag":281,"props":620,"children":621},{},[622,638],{"type":9,"tag":303,"props":623,"children":624},{},[625,631,632],{"type":9,"tag":47,"props":626,"children":628},{"className":627},[],[629],{"type":14,"value":630},"-gt",{"type":14,"value":364},{"type":9,"tag":47,"props":633,"children":635},{"className":634},[],[636],{"type":14,"value":637},"-ge",{"type":9,"tag":303,"props":639,"children":640},{},[641],{"type":14,"value":642},"greater than / greater or equal",{"type":9,"tag":10,"props":644,"children":645},{},[646,648,653,655,660,662,668,670,676,678,684,686,691,693,698,700,706],{"type":14,"value":647},"Use ",{"type":9,"tag":47,"props":649,"children":651},{"className":650},[],[652],{"type":14,"value":510},{"type":14,"value":654}," for strings and ",{"type":9,"tag":47,"props":656,"children":658},{"className":657},[],[659],{"type":14,"value":582},{"type":14,"value":661}," for integers, and never mix them. ",{"type":9,"tag":47,"props":663,"children":665},{"className":664},[],[666],{"type":14,"value":667},"[ \"10\" = \"10.0\" ]",{"type":14,"value":669}," is false; ",{"type":9,"tag":47,"props":671,"children":673},{"className":672},[],[674],{"type":14,"value":675},"[ 10 -eq 10 ]",{"type":14,"value":677}," is true. And ",{"type":9,"tag":47,"props":679,"children":681},{"className":680},[],[682],{"type":14,"value":683},"[ \"9\" \\> \"10\" ]",{"type":14,"value":685}," is ",{"type":9,"tag":30,"props":687,"children":688},{},[689],{"type":14,"value":690},"true",{"type":14,"value":692}," as a string comparison, because ",{"type":9,"tag":47,"props":694,"children":696},{"className":695},[],[697],{"type":14,"value":541},{"type":14,"value":699}," sorts after ",{"type":9,"tag":47,"props":701,"children":703},{"className":702},[],[704],{"type":14,"value":705},"1",{"type":14,"value":707},".",{"data":709,"body":710},{},{"type":6,"children":711},[712,733,742,777,782,826],{"type":9,"tag":10,"props":713,"children":714},{},[715,717,723,725,731],{"type":14,"value":716},"::deep-dive{title=\"Why ",{"type":9,"tag":47,"props":718,"children":720},{"className":719},[],[721],{"type":14,"value":722},"[ -z \\\"$VAR\\\" ]",{"type":14,"value":724}," needs those quotes\"}\nSuppose ",{"type":9,"tag":47,"props":726,"children":728},{"className":727},[],[729],{"type":14,"value":730},"VAR",{"type":14,"value":732}," is empty and you write it unquoted:",{"type":9,"tag":83,"props":734,"children":737},{"className":735,"code":736,"language":14},[86],"[ -z $VAR ]\n",[738],{"type":9,"tag":47,"props":739,"children":740},{"__ignoreMap":91},[741],{"type":14,"value":736},{"type":9,"tag":10,"props":743,"children":744},{},[745,747,752,754,760,762,768,770,775],{"type":14,"value":746},"The shell substitutes the value — nothing — and word splitting removes the word entirely. ",{"type":9,"tag":47,"props":748,"children":750},{"className":749},[],[751],{"type":14,"value":153},{"type":14,"value":753}," is called as ",{"type":9,"tag":47,"props":755,"children":757},{"className":756},[],[758],{"type":14,"value":759},"[ -z ]",{"type":14,"value":761},", which is a one-argument test asking \"is the string ",{"type":9,"tag":47,"props":763,"children":765},{"className":764},[],[766],{"type":14,"value":767},"-z",{"type":14,"value":769}," non-empty?\". That is true. So an ",{"type":9,"tag":30,"props":771,"children":772},{},[773],{"type":14,"value":774},"empty",{"type":14,"value":776}," variable makes the emptiness check report false.",{"type":9,"tag":10,"props":778,"children":779},{},[780],{"type":14,"value":781},"With quotes, the shell passes an empty argument rather than no argument, and the test does what it says.",{"type":9,"tag":10,"props":783,"children":784},{},[785,787,793,795,801,803,809,811,817,818,824],{"type":14,"value":786},"The same failure hits every comparison. If ",{"type":9,"tag":47,"props":788,"children":790},{"className":789},[],[791],{"type":14,"value":792},"NAME",{"type":14,"value":794}," is unset, ",{"type":9,"tag":47,"props":796,"children":798},{"className":797},[],[799],{"type":14,"value":800},"[ $NAME = \"Ada\" ]",{"type":14,"value":802}," becomes ",{"type":9,"tag":47,"props":804,"children":806},{"className":805},[],[807],{"type":14,"value":808},"[ = \"Ada\" ]",{"type":14,"value":810}," — a syntax error, from a line that looks fine. ",{"type":9,"tag":47,"props":812,"children":814},{"className":813},[],[815],{"type":14,"value":816},"[ \"$NAME\" = \"Ada\" ]",{"type":14,"value":802},{"type":9,"tag":47,"props":819,"children":821},{"className":820},[],[822],{"type":14,"value":823},"[ \"\" = \"Ada\" ]",{"type":14,"value":825},", which is simply false.",{"type":9,"tag":10,"props":827,"children":828},{},[829,831,837,839,845],{"type":14,"value":830},"An older trick you will still meet in the wild prefixes both sides with a character: ",{"type":9,"tag":47,"props":832,"children":834},{"className":833},[],[835],{"type":14,"value":836},"[ \"x$NAME\" = \"xAda\" ]",{"type":14,"value":838},". It solves the same problem for shells so old they mishandled a leading ",{"type":9,"tag":47,"props":840,"children":842},{"className":841},[],[843],{"type":14,"value":844},"-",{"type":14,"value":846}," in the value. Quoting is the modern answer.\n::",{"data":848,"body":849},{},{"type":6,"children":850},[851,860,869,882,891,926,952,961],{"type":9,"tag":42,"props":852,"children":854},{"id":853},"if",[855],{"type":9,"tag":47,"props":856,"children":858},{"className":857},[],[859],{"type":14,"value":853},{"type":9,"tag":83,"props":861,"children":864},{"className":862,"code":863,"language":14},[86],"if [ -f \"$CONFIG\" ]\nthen\n  echo \"Found config\"\nelif [ -f \"$CONFIG.example\" ]\nthen\n  echo \"Only the example is here\"\nelse\n  echo \"No config at all\"\n  exit 1\nfi\n",[865],{"type":9,"tag":47,"props":866,"children":867},{"__ignoreMap":91},[868],{"type":14,"value":863},{"type":9,"tag":10,"props":870,"children":871},{},[872,874,880],{"type":14,"value":873},"Written on one line, the ",{"type":9,"tag":47,"props":875,"children":877},{"className":876},[],[878],{"type":14,"value":879},"then",{"type":14,"value":881}," needs a separator — that is what the semicolon is doing in the form you see everywhere:",{"type":9,"tag":83,"props":883,"children":886},{"className":884,"code":885,"language":14},[86],"if [ -d /var/log ]; then echo \"yes\"; fi\n",[887],{"type":9,"tag":47,"props":888,"children":889},{"__ignoreMap":91},[890],{"type":14,"value":885},{"type":9,"tag":10,"props":892,"children":893},{},[894,896,902,903,908,910,916,918,924],{"type":14,"value":895},"And ",{"type":9,"tag":47,"props":897,"children":899},{"className":898},[],[900],{"type":14,"value":901},"fi",{"type":14,"value":685},{"type":9,"tag":47,"props":904,"children":906},{"className":905},[],[907],{"type":14,"value":853},{"type":14,"value":909}," backwards. So is ",{"type":9,"tag":47,"props":911,"children":913},{"className":912},[],[914],{"type":14,"value":915},"esac",{"type":14,"value":917}," for ",{"type":9,"tag":47,"props":919,"children":921},{"className":920},[],[922],{"type":14,"value":923},"case",{"type":14,"value":925},", in the next lesson but one. It is a Bourne-shell joke that stuck.",{"type":9,"tag":10,"props":927,"children":928},{},[929,931,936,938,943,945,950],{"type":14,"value":930},"The condition is not special syntax. ",{"type":9,"tag":47,"props":932,"children":934},{"className":933},[],[935],{"type":14,"value":853},{"type":14,"value":937}," runs a ",{"type":9,"tag":30,"props":939,"children":940},{},[941],{"type":14,"value":942},"command",{"type":14,"value":944}," and branches on its exit status. ",{"type":9,"tag":47,"props":946,"children":948},{"className":947},[],[949],{"type":14,"value":161},{"type":14,"value":951}," is simply the command people run most often there:",{"type":9,"tag":83,"props":953,"children":956},{"className":954,"code":955,"language":14},[86],"if grep -q \"ERROR\" app.log; then\n  echo \"Errors present\"\nfi\n",[957],{"type":9,"tag":47,"props":958,"children":959},{"__ignoreMap":91},[960],{"type":14,"value":955},{"type":9,"tag":10,"props":962,"children":963},{},[964,966,971,973,979],{"type":14,"value":965},"No brackets, no ",{"type":9,"tag":47,"props":967,"children":969},{"className":968},[],[970],{"type":14,"value":52},{"type":14,"value":972},". ",{"type":9,"tag":47,"props":974,"children":976},{"className":975},[],[977],{"type":14,"value":978},"grep -q",{"type":14,"value":980}," exits 0 when it finds a match, which is precisely the question being asked.",{"data":982,"body":983},{},{"type":6,"children":984},[985],{"type":9,"tag":986,"props":987,"children":992},"fill-blank",{":answer":988,"hint":989,"placeholder":990,"prompt":991},"[\"[ -d /var/backups ]\",\"[ -d \\\"/var/backups\\\" ]\",\"test -d /var/backups\"]","One operator asks about directories; remember the spaces.","[ ... ]","Write the condition that tests whether the directory `/var/backups` exists. Include the brackets.",[],{"data":994,"body":995},{},{"type":6,"children":996},[997,1015,1020,1029,1053],{"type":9,"tag":42,"props":998,"children":1000},{"id":999},"and",[1001,1007,1009],{"type":9,"tag":47,"props":1002,"children":1004},{"className":1003},[],[1005],{"type":14,"value":1006},"&&",{"type":14,"value":1008}," and ",{"type":9,"tag":47,"props":1010,"children":1012},{"className":1011},[],[1013],{"type":14,"value":1014},"||",{"type":9,"tag":10,"props":1016,"children":1017},{},[1018],{"type":14,"value":1019},"Two commands can be chained on the exit status of the first:",{"type":9,"tag":83,"props":1021,"children":1024},{"className":1022,"code":1023,"language":14},[86],"mkdir /tmp/build && cd /tmp/build     # cd only if mkdir worked\ncd /tmp/build || exit 1               # bail out if cd failed\n",[1025],{"type":9,"tag":47,"props":1026,"children":1027},{"__ignoreMap":91},[1028],{"type":14,"value":1023},{"type":9,"tag":10,"props":1030,"children":1031},{},[1032,1037,1039,1044,1046,1051],{"type":9,"tag":47,"props":1033,"children":1035},{"className":1034},[],[1036],{"type":14,"value":1006},{"type":14,"value":1038}," runs the right side only if the left succeeded; ",{"type":9,"tag":47,"props":1040,"children":1042},{"className":1041},[],[1043],{"type":14,"value":1014},{"type":14,"value":1045}," only if it failed. Both ",{"type":9,"tag":17,"props":1047,"children":1048},{},[1049],{"type":14,"value":1050},"short-circuit",{"type":14,"value":1052}," — the right side isn't run at all otherwise.",{"type":9,"tag":10,"props":1054,"children":1055},{},[1056,1058,1064,1066,1072],{"type":14,"value":1057},"The ",{"type":9,"tag":47,"props":1059,"children":1061},{"className":1060},[],[1062],{"type":14,"value":1063},"|| exit 1",{"type":14,"value":1065}," idiom is worth adopting immediately. Without it, a script whose ",{"type":9,"tag":47,"props":1067,"children":1069},{"className":1068},[],[1070],{"type":14,"value":1071},"cd",{"type":14,"value":1073}," fails carries on running the rest of its commands in whatever directory it happened to be in, which is how a cleanup script ends up deleting the wrong tree.",{"data":1075,"body":1076},{},{"type":6,"children":1077},[1078,1084],{"type":9,"tag":133,"props":1079,"children":1083},{":answer":135,":options":1080,"explanation":1081,"question":1082},"[\"The script continuing to run its remaining commands in the wrong directory\",\"`/opt/app` being deleted while the script runs\",\"A permissions error being printed to the terminal\"]","If `cd` fails, everything after it still runs — just somewhere else. For a script that then does `rm -rf ./cache`, \"somewhere else\" could be your home directory. The `|| exit 1` turns a silent wrong-place run into a stop.","What does `cd /opt/app || exit 1` protect against?",[],{"type":9,"tag":10,"props":1085,"children":1086},{},[1087],{"type":14,"value":1088},"Next up: loops — doing the same thing to every file, every line, or every server in a list.",1787908866327]