Thanks
Many people have contributed their time and support to help build this book. Thank-you to everyone who has helped on this journey!
diff --git a/pr-preview/pr-346/.nojekyll b/pr-preview/pr-346/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/pr-preview/pr-346/404.html b/pr-preview/pr-346/404.html new file mode 100644 index 00000000..e40bc4ad --- /dev/null +++ b/pr-preview/pr-346/404.html @@ -0,0 +1,26 @@ + + +
+ + +We could not find what you were looking for.
Please contact the owner of the site that linked you to the original URL and let them know their link is broken.
Many people have contributed their time and support to help build this book. Thank-you to everyone who has helped on this journey!
I[0](I[1],A))):A.addEventListener(g,I)}function eA(A,g,I={}){const B=A.style;if(null==g||"string"==typeof g)return B.cssText=g;let Q,C;for(C in"string"==typeof I&&(I={}),I)null==g[C]&&B.removeProperty(C),delete I[C];for(C in g)Q=g[C],Q!==I[C]&&(B.setProperty(C,Q),I[C]=Q);return I}function iA(A,g,I,B){if(void 0===I||B||(B=[]),"function"!=typeof g)return rA(A,g,B,I);d((B=>rA(A,g(),B,I)),B)}function nA(A,g,I){const B=g.trim().split(/\s+/);for(let Q=0,C=B.length;QI||document});null!==I;){const B=I[g];if(B&&!I.disabled){const Q=I[`${g}Data`];if(void 0!==Q?B(Q,A):B(A),A.cancelBubble)return}I=I.host&&I.host!==I&&I.host instanceof Node?I.host:I.parentNode}}function rA(A,g,I,B,Q){for(;"function"==typeof I;)I=I();if(g===I)return I;const C=typeof g,E=void 0!==B;if(A=E&&I[0]&&I[0].parentNode||A,"string"===C||"number"===C)if("number"===C&&(g=g.toString()),E){let Q=I[0];Q&&3===Q.nodeType?Q.data=g:Q=document.createTextNode(g),I=cA(A,I,B,Q)}else I=""!==I&&"string"==typeof I?A.firstChild.data=g:A.textContent=g;else if(null==g||"boolean"===C)I=cA(A,I,B);else{if("function"===C)return d((()=>{let Q=g();for(;"function"==typeof Q;)Q=Q();I=rA(A,Q,I,B)})),()=>I;if(Array.isArray(g)){const C=[];if(sA(C,g,Q))return d((()=>I=rA(A,C,I,B,!0))),()=>I;if(0===C.length){if(I=cA(A,I,B),E)return I}else Array.isArray(I)?0===I.length?aA(A,C,B):function(A,g,I){let B=I.length,Q=g.length,C=B,E=0,t=0,e=g[Q-1].nextSibling,i=null;for(;E B-t){const Q=g[E];for(;t=0;C--){const E=g[C];if(Q!==E){const g=E.parentNode===A;B||C?g&&E.remove():g?A.replaceChild(Q,E):A.insertBefore(Q,I)}else B=!0}}else A.insertBefore(Q,I);return[Q]}const uA=Symbol("store-raw"),wA=Symbol("store-node"),DA=Symbol("store-name");function lA(A,g){let I=A[c];if(!I){Object.defineProperty(A,c,{value:I=new Proxy(A,kA)});const g=Object.keys(A),B=Object.getOwnPropertyDescriptors(A);for(let Q=0,C=g.length;Q!0,deleteProperty:()=>!0,ownKeys:function(A){if(S()){const g=yA(A);(g._||(g._=GA()))()}return Reflect.ownKeys(A)},getOwnPropertyDescriptor:function(A,g){const I=Reflect.getOwnPropertyDescriptor(A,g);return I&&!I.get&&I.configurable&&g!==c&&g!==wA&&g!==DA?(delete I.value,delete I.writable,I.get=()=>A[c][g],I):I}};function NA(A,g,I){if(A[g]===I)return;const B=Array.isArray(A),Q=A.length,C=void 0===I,E=B||C===g in A;C?delete A[g]:A[g]=I;let t,e=yA(A);(t=e[g])&&t.$(),B&&A.length!==Q&&(t=e.length)&&t.$(),E&&(t=e._)&&t.$()}function MA(A,g,I=[]){let B,Q=A;if(g.length>1){B=g.shift();const C=typeof B,E=Array.isArray(A);if(Array.isArray(B)){for(let Q=0;Q 1)return void MA(A[B],g,[B].concat(I));Q=A[B],I=[B].concat(I)}let C=g[0];"function"==typeof C&&(C=C(Q,I),C===Q)||void 0===B&&null==C||(C=fA(C),void 0===B||hA(Q)&&hA(C)&&!Array.isArray(C)?function(A,g){const I=Object.keys(g);for(let B=0;B =E&&e>=E&&(C[t]===A[e]||Q&&C[t][Q]===A[e][Q]);t--,e--)r[e]=C[t];if(E>e||E>t){for(I=E;I<=e;I++)NA(C,I,A[I]);for(;I A.length&&NA(C,"length",A.length))}for(n=new Array(e+1),I=e;I>=E;I--)i=A[I],o=Q?i[Q]:i,g=s.get(o),n[I]=void 0===g?-1:g,s.set(o,I);for(g=E;g<=t;g++)i=C[g],o=Q?i[Q]:i,I=s.get(o),void 0!==I&&-1!==I&&(r[I]=C[g],I=n[I],s.set(o,I));for(I=E;I A.length&&NA(C,"length",A.length))}const E=Object.keys(A);for(let e=0,i=E.length;ehA(A)&&hA(Q)?(FA(Q,{state:A},"state",I,B),A):Q}var pA,RA=new Array(32).fill(void 0);function JA(A){return RA[A]}RA.push(void 0,null,!0,!1);var LA=RA.length;function YA(A){var g=JA(A);return function(A){A<36||(RA[A]=LA,LA=A)}(A),g}function SA(A){LA===RA.length&&RA.push(RA.length+1);var g=LA;return LA=RA[g],RA[g]=A,g}var vA=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});vA.decode();var UA=null;function bA(){return null!==UA&&UA.buffer===pA.memory.buffer||(UA=new Uint8Array(pA.memory.buffer)),UA}function KA(A,g){return vA.decode(bA().subarray(A,A+g))}function mA(A){var g=B(A);if("number"==g||"boolean"==g||null==A)return"".concat(A);if("string"==g)return'"'.concat(A,'"');if("symbol"==g){var I=A.description;return null==I?"Symbol":"Symbol(".concat(I,")")}if("function"==g){var Q=A.name;return"string"==typeof Q&&Q.length>0?"Function(".concat(Q,")"):"Function"}if(Array.isArray(A)){var C=A.length,E="[";C>0&&(E+=mA(A[0]));for(var t=1;t 1))return toString.call(A);if("Object"==(e=i[1]))try{return"Object("+JSON.stringify(A)+")"}catch(n){return"Object"}return A instanceof Error?"".concat(A.name,": ").concat(A.message,"\n").concat(A.stack):e}var qA=0,HA=new TextEncoder("utf-8"),xA="function"==typeof HA.encodeInto?function(A,g){return HA.encodeInto(A,g)}:function(A,g){var I=HA.encode(A);return g.set(I),{read:A.length,written:I.length}};function jA(A,g,I){if(void 0===I){var B=HA.encode(A),Q=g(B.length);return bA().subarray(Q,Q+B.length).set(B),qA=B.length,Q}for(var C=A.length,E=g(C),t=bA(),e=0;e 127)break;t[E+e]=i}if(e!==C){0!==e&&(A=A.slice(e)),E=I(E,C,C=e+3*A.length);var n=bA().subarray(E+e,E+C);e+=xA(A,n).written}return qA=e,E}var ZA=null;function TA(){return null!==ZA&&ZA.buffer===pA.memory.buffer||(ZA=new Int32Array(pA.memory.buffer)),ZA}var OA=null;function WA(A,g){return(null!==OA&&OA.buffer===pA.memory.buffer||(OA=new Uint32Array(pA.memory.buffer)),OA).subarray(A/4,A/4+g)}var XA=function(){function A(){r(this,A)}return a(A,[{key:"__destroy_into_raw",value:function(){var A=this.ptr;return this.ptr=0,A}},{key:"free",value:function(){var A=this.__destroy_into_raw();pA.__wbg_vtwrapper_free(A)}},{key:"feed",value:function(A){try{var g=pA.__wbindgen_add_to_stack_pointer(-16),I=jA(A,pA.__wbindgen_malloc,pA.__wbindgen_realloc),B=qA;pA.vtwrapper_feed(g,this.ptr,I,B);var Q=TA()[g/4+0],C=TA()[g/4+1],E=WA(Q,C).slice();return pA.__wbindgen_free(Q,4*C),E}finally{pA.__wbindgen_add_to_stack_pointer(16)}}},{key:"inspect",value:function(){try{var A=pA.__wbindgen_add_to_stack_pointer(-16);pA.vtwrapper_inspect(A,this.ptr);var g=TA()[A/4+0],I=TA()[A/4+1];return KA(g,I)}finally{pA.__wbindgen_add_to_stack_pointer(16),pA.__wbindgen_free(g,I)}}},{key:"get_line",value:function(A){return YA(pA.vtwrapper_get_line(this.ptr,A))}},{key:"get_cursor",value:function(){return YA(pA.vtwrapper_get_cursor(this.ptr))}}],[{key:"__wrap",value:function(g){var I=Object.create(A.prototype);return I.ptr=g,I}}]),A}();function PA(A,g){return zA.apply(this,arguments)}function zA(){return(zA=t(o().mark((function A(g,I){var B,Q;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:if(!("function"==typeof Response&&g instanceof Response)){A.next=23;break}if("function"!=typeof WebAssembly.instantiateStreaming){A.next=15;break}return A.prev=2,A.next=5,WebAssembly.instantiateStreaming(g,I);case 5:case 20:return A.abrupt("return",A.sent);case 8:if(A.prev=8,A.t0=A.catch(2),"application/wasm"==g.headers.get("Content-Type")){A.next=14;break}console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",A.t0),A.next=15;break;case 14:throw A.t0;case 15:return A.next=17,g.arrayBuffer();case 17:return B=A.sent,A.next=20,WebAssembly.instantiate(B,I);case 23:return A.next=25,WebAssembly.instantiate(g,I);case 25:if(!((Q=A.sent)instanceof WebAssembly.Instance)){A.next=30;break}return A.abrupt("return",{instance:Q,module:g});case 30:return A.abrupt("return",Q);case 31:case"end":return A.stop()}}),A,null,[[2,8]])})))).apply(this,arguments)}function VA(A){return _A.apply(this,arguments)}function _A(){return(_A=t(o().mark((function A(g){var I,B,Q,C;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:return void 0===g&&(g=new URL("index_bg.wasm","")),(I={}).wbg={},I.wbg.__wbindgen_object_drop_ref=function(A){YA(A)},I.wbg.__wbindgen_number_new=function(A){return SA(A)},I.wbg.__wbindgen_string_new=function(A,g){return SA(KA(A,g))},I.wbg.__wbg_set_f1a4ac8f3a605b11=function(A,g,I){JA(A)[YA(g)]=YA(I)},I.wbg.__wbg_new_949bbc1147195c4e=function(){return SA(new Array)},I.wbg.__wbg_new_ac32179a660db4bb=function(){return SA(new Map)},I.wbg.__wbg_new_0b83d3df67ecb33e=function(){return SA(new Object)},I.wbg.__wbindgen_is_string=function(A){return"string"==typeof JA(A)},I.wbg.__wbg_push_284486ca27c6aa8b=function(A,g){return JA(A).push(JA(g))},I.wbg.__wbg_new_342a24ca698edd87=function(A,g){return SA(new Error(KA(A,g)))},I.wbg.__wbg_set_a46091b120cc63e9=function(A,g,I){return SA(JA(A).set(JA(g),JA(I)))},I.wbg.__wbindgen_debug_string=function(A,g){var I=jA(mA(JA(g)),pA.__wbindgen_malloc,pA.__wbindgen_realloc),B=qA;TA()[A/4+1]=B,TA()[A/4+0]=I},I.wbg.__wbindgen_throw=function(A,g){throw new Error(KA(A,g))},("string"==typeof g||"function"==typeof Request&&g instanceof Request||"function"==typeof URL&&g instanceof URL)&&(g=fetch(g)),A.t0=PA,A.next=20,g;case 20:return A.t1=A.sent,A.t2=I,A.next=24,(0,A.t0)(A.t1,A.t2);case 24:return B=A.sent,Q=B.instance,C=B.module,pA=Q.exports,VA.__wbindgen_wasm_module=C,A.abrupt("return",pA);case 30:case"end":return A.stop()}}),A)})))).apply(this,arguments)}var $A=Object.freeze({__proto__:null,create:function(A,g){var I=pA.create(A,g);return XA.__wrap(I)},VtWrapper:XA,default:VA});const Ag=[62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51];function gg(A){return Ag[A-43]}const Ig=function(A){let g,I=A.endsWith("==")?2:A.endsWith("=")?1:0,B=A.length,Q=new Uint8Array(B/4*3);for(let C=0,E=0;C>16,Q[E+1]=g>>8&255,Q[E+2]=255&g;return Q.subarray(0,Q.length-I)}("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AgggASACNgIMIAIgADYCDCACIAE2AgggA0EIaiECDAILQdS8wABB/x82AgBByLzAACAGNgIAQcC8wAAgBTYCAEG8vMAAIAI2AgBBqLnAAEGcucAANgIAQbC5wABBpLnAADYCAEGkucAAQZy5wAA2AgBBuLnAAEGsucAANgIAQay5wABBpLnAADYCAEHAucAAQbS5wAA2AgBBtLnAAEGsucAANgIAQci5wABBvLnAADYCAEG8ucAAQbS5wAA2AgBB0LnAAEHEucAANgIAQcS5wABBvLnAADYCAEHYucAAQcy5wAA2AgBBzLnAAEHEucAANgIAQeC5wABB1LnAADYCAEHUucAAQcy5wAA2AgBB6LnAAEHcucAANgIAQdy5wABB1LnAADYCAEHkucAAQdy5wAA2AgBB8LnAAEHkucAANgIAQey5wABB5LnAADYCAEH4ucAAQey5wAA2AgBB9LnAAEHsucAANgIAQYC6wABB9LnAADYCAEH8ucAAQfS5wAA2AgBBiLrAAEH8ucAANgIAQYS6wABB/LnAADYCAEGQusAAQYS6wAA2AgBBjLrAAEGEusAANgIAQZi6wABBjLrAADYCAEGUusAAQYy6wAA2AgBBoLrAAEGUusAANgIAQZy6wABBlLrAADYCAEGousAAQZy6wAA2AgBBsLrAAEGkusAANgIAQaS6wABBnLrAADYCAEG4usAAQay6wAA2AgBBrLrAAEGkusAANgIAQcC6wABBtLrAADYCAEG0usAAQay6wAA2AgBByLrAAEG8usAANgIAQby6wABBtLrAADYCAEHQusAAQcS6wAA2AgBBxLrAAEG8usAANgIAQdi6wABBzLrAADYCAEHMusAAQcS6wAA2AgBB4LrAAEHUusAANgIAQdS6wABBzLrAADYCAEHousAAQdy6wAA2AgBB3LrAAEHUusAANgIAQfC6wABB5LrAADYCAEHkusAAQdy6wAA2AgBB+LrAAEHsusAANgIAQey6wABB5LrAADYCAEGAu8AAQfS6wAA2AgBB9LrAAEHsusAANgIAQYi7wABB/LrAADYCAEH8usAAQfS6wAA2AgBBkLvAAEGEu8AANgIAQYS7wABB/LrAADYCAEGYu8AAQYy7wAA2AgBBjLvAAEGEu8AANgIAQaC7wABBlLvAADYCAEGUu8AAQYy7wAA2AgBBnLvAAEGUu8AANgIAQbC8wAAgAkEIaiIAQQdqQXhxIABrIgEgAmoiADYCAEGovMAAIAUgAWtBWGoiATYCACAAIAFBAXI2AgQgACABakEoNgIEQcy8wABBgICAATYCAAtBACECQai8wAAoAgAiACAETQ0AQai8wAAgACAEayIBNgIAQbC8wABBsLzAACgCACIAIARqIgI2AgAgAiABQQFyNgIEIAAgBEEDcjYCBCAAQQhqIQILIAhBEGokACACC9cKAQd/IwBB0ABrIgIkAAJAAkACQAJAAkACQAJAIABBFGooAgBFBEAgAUFgcUHAAEYNASABQUlqDgICAwQLIAAoAgwhAwJAIAFBMEcEQCABQThGDQEgAygCACEBDAcLIAMoAgAiAUEoRw0GIABBAToAoQEMBwsgAygCACIBQSNHDQUgACgCHCIFRQ0GIAJBEWohBCACQcMAaiIGQQRqIQdBACEDA0AgACgCGCIIBEBBACEBA0AgB0EAOwAAIAZBADYAACAEIAIpAEA3AAAgBEEIaiACQcgAai0AADoAACACQQI6ABAgAkECOgAMIAJBxQA2AgggACABIAMgAkEIahCDASAIIAFBAWoiAUcNAAsLIAAoAowBIgEgA00NBSAAKAKEASADakEBOgAAIAUgA0EBaiIDRw0ACwwGCyAAIAFBQGsQLQwFCyAAQdgAaiAAKAI8NgIAIABB3ABqIAApAJMBNwAAIABB6gBqIAAvAKMBOwEAIABB4gBqIABBmQFqKQAANwAAIAAgACgCGEF/aiIBIAAoAjgiACAAIAFLGzYCVAwECyAAQQA6AKYBIAAgACkCVDcCOCAAIABB3ABqKQAANwCTASAAQZkBaiAAQeIAaikAADcAACAAIABB6gBqLwEAOwCjAQwDCyABQeMARw0CIAJBIGoiASAAKAIYIAAoAhwQTCACQTBqIAEQWCAAQQA6AJABQfCSwAAoAgAhAQJAIAAoAgQiA0UNACADQQF0RQ0AIAAoAgAQEwsgAEIANwIEIAAgATYCACAAQQAQvgEgACgCACAAKAIIQQF0akEAOwEAIAAgACgCCEEBajYCCEHoksAAKAIAIQECQCAAQRBqKAIAIgNFDQAgA0ECdEUNACAAKAIMEBMLIABCADcCECAAIAE2AgwgAkEQaiIDIAJBKGooAgA2AgAgAiACKQMgNwMIIABBIGoiARCxAQJAIABBJGooAgAiBEUNACAEQQxsRQ0AIAEoAgAQEwsgASACKQMINwIAIAFBCGogAygCADYCACAAQSxqIgEQsQECQCAAQTBqKAIAIgNFDQAgA0EMbEUNACABKAIAEBMLIAEgAikDMDcCACAAQQA6AJEBIAFBCGogAkE4aigCADYCACACQQhqIAAoAhgQbiAAQUBrIQECQCAAQcQAaigCACIDRQ0AIANBAnRFDQAgASgCABATCyABIAIpAwg3AgAgAUEIaiACQQhqIgVBCGoiASgCADYCACAAQQE6AJIBIABCADcCOCACQQ9qIgNBADsAACAAQZcBakECOgAAIABBAjoAkwEgAkEANgALIABBmAFqIAIpAAg3AAAgAEGgAWogAS0AADoAACAAQQA7AKUBIABBgICACDYAoQEgAEEANgJMIAAgACgCHCIEQX9qNgJQIANBADsAACACQQA2AAsgAEHhAGogAikACDcAACAAQekAaiABLQAAOgAAIABB6gBqQYACOwEAIABB4ABqQQI6AAAgAEHcAGpBAjoAACAAQgA3AlQgA0EAOwAAIAJBADYACyAAQfkAaiACKQAINwAAIABBgQFqIAEtAAA6AAAgAEGCAWpBgAI7AQAgAEH4AGpBAjoAACAAQfQAakECOgAAIABCADcCbCACIAQQpwEgAUEANgIAIAIgAikDADcDCCAFIAQQhAEgAkHIAGogASgCADYCACACIAIpAwg3A0AgAEGEAWohASAAQYgBaigCAARAIAEoAgAQEwsgASACKQNANwIAIAFBCGogAkHIAGooAgA2AgAMAgsgAyABQdyMwAAQmQEACyABQShHDQAgAEEAOgChAQsgAkHQAGokAAuvDAEIfyMAQSBrIgMkAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIABBFGooAgBFBEAgAUFAag4zHAcbChoZGBcGFhUUExIfHxEfHxAPHx8ODR8MHx8fHx8LCgkfCAcGBQQfHx8DAh8fHx8BHwsgACgCDCECAkACQCABQZR/ag4FASAgIB4ACyABQegARg0eDB8LIAIoAgBBP0cNHiAAKAIAIQIgA0EIaiAAKAIIIgEQkwEgAygCDCEHIAMoAgggAiABQQF0IggQLCECIAEEQCAA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BANAIAQgBWoiAykAACEOIAMpAAghDyADKQAQIRAgA0EYaiIIKQAAIREgCCABIARqIghBGGoiCSkAADcAACADQRBqIAhBEGoiCykAADcAACADQQhqIAhBCGoiDCkAADcAACADIAgpAAA3AAAgCSARNwAAIAsgEDcAACAMIA83AAAgCCAONwAAIARBQGsgBEEgaiIDIQQgB00NAAsLIAcgA0sEQCAGQRBqIgggAyAFaiIJIAcgA2siBBAsGiAJIAEgA2oiASAEECwaIAEgCCAEECwaCyAFIQEgACACayIAIAJPDQALDAILIAZBCGoiB0EAIABrIghBDGwgAWoiA0EIaigCADYCACAGIAMpAgA3AwAgAkEMbCEKIAIiASEEA0AgBEEMbCADaiEFA0AgBkEYaiIJIAVBCGoiCygCADYCACAGIAUpAgA3AxAgBygCACEMIAUgBikDADcCACALIAw2AgAgByAJKAIANgIAIAYgBikDEDcDACAEIABPRQRAIAUgCmohBSACIARqIQQMAQsLIAQgCGoiBARAIAQgASAEIAFJGyEBDAEFIAYpAwAhDiADQQhqIAZBCGoiBygCADYCACADIA43AgAgAUECSQ0GQQEhBANAIARBDGwgA2oiCCkCACEOIAcgCEEIaiIJKAIANgIAIAYgDjcDACACIARqIQUDQCAGQRhqIgsgBUEMbCADaiIKQQhqIgwoAgA2AgAgBiAKKQIANwMQIAcoAgAhDSAKIAYpAwA3AgAgDCANNgIAIAcgCygCADYCACAGIAYpAxA3AwAgBSAASQRAIAIgBWohBQwBCyAFIABrIgUgBEcNAAsgBikDACEOIAkgBygCADYCACAIIA43AgAgASAEQQFqIgRHDQALDAYLAAsACyAAQXRsIQggAEEMbCEHQQAgAGshCgNAQQAhAyAHQSBPBEAgASAIaiEJQQAhBANAIAQgCWoiBSkAACEOIAUpAAghDyAFKQAQIRAgBUEYaiIDKQAAIREgAyABIARqIgNBGGoiCykAADcAACAFQRBqIANBEGoiDCkAADcAACAFQQhqIANBCGoiDSkAADcAACAFIAMpAAA3AAAgCyARNwAAIAwgEDcAACANIA83AAAgAyAONwAAIARBQGsgBEEgaiIDIQQgB00NAAsLIAcgA0sEQCAGQRBqIgUgCkEMbCABaiADaiIJIAcg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IAFB2I/AABAnIAIgAEE8ajYCDCACQeiPwABBCCABQdiPwAAQJyACIABBkgFqNgIMIAJBoJHAAEEOIAFByI7AABAnIAIgAEGTAWo2AgwgAkHwj8AAQQMgAUGUj8AAECcgAiAAQaEBajYCDCACQa6RwABBByABQbiRwAAQJyACIABBQGs2AgwgAkHIkcAAQQQgAUHMkcAAECcgAiAAQaIBajYCDCACQdyRwABBCyABQciOwAAQJyACIABBowFqNgIMIAJB84/AAEELIAFByI7AABAnIAIgAEGkAWo2AgwgAkH+j8AAQQ4gAUHIjsAAECcgAiAAQaUBajYCDCACQeeRwABBDSABQciOwAAQJyACIABBpgFqNgIMIAJB9JHAAEEQIAFByI7AABAnIAIgAEHMAGo2AgwgAkGEksAAQQogAUHYj8AAECcgAiAAQdAAajYCDCACQY6SwABBDSABQdiPwAAQJyACIABB1ABqNgIMIAJBm5LAAEEJIAFBpJLAABAnIAIgAEHsAGo2AgwgAkG0ksAAQRMgAUGkksAAECcgAiAAQYQBajYCDCACQceSwABBDiABQdiSwAAQJyACEI4BIAJBEGokAAv6BAEKfyMAQTBrIgMkACADQSRqIAE2AgAgA0EDOgAoIANCgICAgIAENwMIIAMgADYCICADQQA2AhggA0EANgIQAkACQAJAIAIoAggiCkUEQCACQRRqKAIAIgRFDQEgAigCACEBIAIoAhAhACAEQQN0QXhqQQN2QQFqIgchBANAIAFBBGooAgAiBQRAIAMoAiAgASgCACAFIAMoAiQoAgwRAgANBAsgACgCACADQQhqIABBBGooAgARAAANAyAAQQhqIQAgAUEIaiEBIARBf2oiBA0ACwwBCyACQQxqKAIAIgBFDQAgAEEFdCILQWBqQQV2QQFqIQcgAigCACEBA0AgAUEEaigCACIABEAgAygCICABKAIAIAAgAygCJCgCDBECAA0DCyADIAQgCmoiBUEcai0AADoAKCADIAVBBGopAgBCIIk3AwggBUEYaigCACEGIAIoAhAhCEEAIQlBACEAAkACQAJAIAVBFGooAgBBAWsOAgACAQsgBkEDdCAIaiIMKAIEQeYARw0BIAwoAgAoAgAhBgtBASEACyADIAY2AhQgAyAANgIQIAVBEGooAgAhAAJAAkACQCAFQQxqKAIAQQFrDgIAAgELIABBA3QgCGoiBigCBEHmAEcNASAGKAIAKAIAIQALQQEhCQsgAyAANgIcIAMgCTYCGCAFKAIAQQN0IAhqIgAoAgAgA0EIaiAAKAIEEQAADQIgAUEIaiEBIAsgBEEgaiIERw0ACwtBACEAIAcgAigCBEkiAUUNASADKAIgIAIoAgAgB0EDdGpBACABGyIBKAIAIAEoAgQgAygCJCgCDBECAEUNAQtBASEACyADQTBqJAAgAAv6BAEJfyMAQYABayIDJAAgA0EIaiIEIAEgAhBMIANBGGoiByAEEFggA0EwaiIIIARBCGooAgA2AgAgAyADKQMINwMoIANBOGoiCSABEG4gA0HHAGoiCkEHakEAOwAAIANBADYASiADQfAAaiIGQQdqIgVBADsAACADQdgAaiILIAZBCGoiBC0AADoAACADQQA2AHMgAyADKQBwNwNQIAVBADsAACADQegAaiIFIAQtAAA6AAAgA0EANgBzIAMgAykAcDcDYCADIAIQpwEgBEEANgIAIAMgAykDADcDcCAGIAIQhAEgAEGMAWogBCgCADYCACAAIAMpA3A3AoQBIAAgAjYCHCAAIAE2AhggAEEQakIANwIAIABB6JLAACgCADYCDCAAQgA3AgQgAEHwksAAKAIANgIAIABBgICEEDYCkAEgACADKQMoNwIgIABBKGogCCgCADYCACAAIAMpAxg3AiwgAEE0aiAHQQhqKAIANgIAIABBlwFqQQI6AAAgAEIANwI4IABBADoAoQEgAEGAgAQ2AaIBIABBADoApgEgAEEANgJMIAAgAkF/ajYCUCAAQgA3AlQgAEHgAGpBAjoAACAAQdwAakECOgAAIABBmAFqIAMpAEc3AAAgAEGgAWogCkEIai0AADoAACAAIAMpAzg3AkAgAEHIAGogCUEIaigCADYCACAAQekAaiALLQAAOgAAIABB4QBqIAMpA1A3AAAgAEH4AGpBAjoAACAAQfQAakECOgAAIABCADcCbCAAQeoAakGAAjsBACAAQYEBaiAFLQAAOgAAIABB+QBqIAMpA2A3AAAgAEGCAWpBgAI7AQAgA0GAAWokAAuhBQEEfyAAIAFqIQICQAJAAkAgACgCBEEBcQ0AIAAoAgAhAwJAIAAtAARBA3EEQCABIANqIQEgACADayIAQay8wAAoAgBHDQEgAigCBEEDcUEDRw0CQaS8wAAgATYCACACIAIoAgRBfnE2AgQgACABQQFyNgIEIAAgAWogATYCAA8LDAILIANBgAJPBEAgABA4DAELIABBDGooAgAiBCAAQQhqKAIAIgVHBEAgBSAENgIMIAQgBTYCCAwBC0GUucAAQZS5wAAoAgBBfiADQQN2d3E2AgALIAItAARBAnFBAXYEQCACIAIoAgRBfnE2AgQgACABQQFyNgIEIAAgAWogATYCAAwCCwJAQbC8wAAoAgAgAkcEQEGsvMAAKAIAIAJHDQFBrLzAACAANgIAQaS8wABBpLzAACgCACABaiIBNgIAIAAgAUEBcjYCBCAAIAFqIAE2AgAPC0GwvMAAIAA2AgBBqLzAAEGovMAAKAIAIAFqIgE2AgAgACABQQFyNgIEQay8wAAoAgAgAEcNAUGkvMAAQQA2AgBBrLzAAEEANgIADwsgAigCBEF4cSIDIAFqIQECQCADQYACTwRAIAIQOAwBCyACQQxqKAIAIgQgAkEIaigCACICRwRAIAIgBDYCDCAEIAI2AggMAQtBlLnAAEGUucAAKAIAQX4gA0EDdndxNgIACyAAIAFBAXI2AgQgACABaiABNgIAQay8wAAoAgAgAEcNAUGkvMAAIAE2AgALDwsgAUGAAk8EQCAAIAEQNg8LIAFBA3YiAkEDdEGcucAAaiEBAn9BlLnAACgCACIDQQEgAnQiAnEEQCABKAIIDAELQZS5wAAgAiADcjYCACABCyECIAEgADYCCCACIAA2AgwgACABNgIMIAAgAjYCCAuCBAEJfyMAQSBrIgUkACABQRRqKAIAIQkgASgCACEIAkAgAUEEaigCACIKQQN0IgJFDQAgAkF4aiICQQN2QQFqIgZBB3EhByACQThJBH8gCAUgCEE8aiECQQAgBkH4////A3FrIQQDQCACKAIAIAJBeGooAgAgAkFwaigCACACQWhqKAIAIAJBYGooAgAgAkFYaigCACACQVBqKAIAIAJBSGooAgAgA2pqampqampqIQMgAkFAayECIARBCGoiBA0ACyACQURqCyAHRQ0AQQAgB2shAkEEaiEEA0AgBCgCACADaiEDIAJBAWoiBiACTyAGIQIgBEEIaiEEDQALCwJAAkACQCAJRQRAIAMhAgwBCwJAIApFDQAgCCgCBA0AIANBEEkNAgsgAyADaiICIANJDQELQQAhAwJAIAJBAE4EQCACRQRAQQEhBAwECyACQQEQigIiBEUNASACIQMMAwsQkQIACyACQQFBgLnAACgCACIAQdQAIAAbEQEAAAtBASEEQQAhAwsgAEEANgIIIAAgAzYCBCAAIAQ2AgAgBSAANgIEIAVBCGoiAEEQaiABQRBqKQIANwMAIABBCGogAUEIaikCADcDACAFIAEpAgA3AwggBUEEakG4ncAAIAAQHQRAQZibwABBMyAFQQhqQaidwABB5JvAABCKAQALIAVBIGokAAuYBAILfwJ+IwBB0ABrIQQCQCACRQ0AIABFDQAgBEEIaiIGQRBqIgdBACAAayIKQRRsIAFqIgVBEGooAgA2AgAgBkEIaiIIIAVBCGopAgA3AwAgBCAFKQIANwMIIAJBFGwhCSACIgYhAwNAIANBFGwgBWohAQNAIAEpAgAhDiABIAQpAwg3AgAgCCkDACEPIAggAUEIaiILKQIANwMAIAsgDzcCACAHKAIAIQsgByABQRBqIgwoAgA2AgAgDCALNgIAIAQgDjcDCCADIABPRQRAIAEgCWohASACIANqIQMMAQsLIAMgCmoiAwRAIAMgBiADIAZJGyEGDAEFIAUgBCkDCDcCACAFQRBqIARBCGoiAUEQaiIHKAIANgIAIAVBCGogAUEIaiIIKQMANwIAIAZBAkkNAkEBIQMDQCAHIANBFGwgBWoiCkEQaiILKAIANgIAIAggCkEIaiIMKQIANwMAIAQgCikCADcDCCACIANqIQEDQCABQRRsIAVqIgkpAgAhDiAJIAQpAwg3AgAgCCkDACEPIAggCUEIaiINKQIANwMAIA0gDzcCACAHKAIAIQ0gByAJQRBqIgkoAgA2AgAgCSANNgIAIAQgDjcDCCABIABJBEAgASACaiEBDAELIAMgASAAayIBRw0ACyAKIAQpAwg3AgAgCyAHKAIANgIAIAwgCCkDADcCACADQQFqIgMgBkcNAAsLCwsLhAQBBn8jAEEwayIDJAACQCAALQCkASIHRQ0AIAAtAKYBRQ0AIABBADoApgEgAEEANgI4IAAoAjxBAWoiAiAAKAIcRwRAIABBADoApgEgACACNgI8IABBADYCOAwBCyAAQQEQggELAkAgAUGgf2oiAkEeSw0AIAAtAKEBQQFHDQAgAkECdEGsicAAaigCACEBCyADIAApAJMBNwMIIAMgAEGZAWopAAA3AQ5BASEFAkACQAJAAkACQCAAKAI4IgRBAWoiBiAAKAIYIgJJBEAgAC0AogEEQCAAQShqKAIAIgUgACgCPCICTQ0EIAAoAiAgAkEMbGoiBSgCCCICIARJDQUgBSgCACAEQRRsaiACIARrQQEQ2wEgACgCOCEECyAAKAI8IQIgA0EiaiADKQEONwEAIAMgATYCGCADIAMpAwg3AhwgACAEIAIgA0EYahCDAUEAIQUgBiECDAELIAAoAjwhBiADQSJqIABBkwFqIgRBBmopAAA3AQAgAyABNgIYIAMgBCkAADcCHCAAIAJBf2ogBiADQRhqEIMBIAdFDQELIAAgBToApgEgACACNgI4CyAAQYwBaigCACICIAAoAjwiAUsNAiABIAJB3IzAABCZAQALIAIgBUG4isAAEJkBAAsgBCACQbiKwAAQmgEACyAAKAKEASABakEBOgAAIANBMGokAAvbAwEHfyMAQRBrIgUkAAJ/QQEgASgCGCIGQScgAUEcaigCACgCECIHEQAADQAaIAUgACgCABAmIAVBDGotAAAhAyAFQQhqKAIAIQQgBSgCACEBAkACQCAFKAIEIghBgIDEAEcEQANAIAEhAEHcACECQQEhAQJAAkACQAJAIABBAWsOAwEDAAcLIANB/wFxIQBBACEDQQMhAUH9ACECAkACQAJAIABBAWsOBQUEAAECCQtBAiEDQfsAIQIMBAtB9QAhAkEDIQMMAwtBBCEDQdwAIQIMAgtBACEBIAghAgwBC0ECQQEgBBshAyAIIARBAnR2QQ9xIgBBMEHXACAAQQpJG2ohAiAEQX9qQQAgBBshBAsgBiACIAcRAABFDQAMAgsACwNAIAEhAEHcACECQQEhAQJAAkAgAEECaw4CAQAECyADQf8BcSEAQQAhA0EDIQFB/QAhAgJAAkACQAJAIABBAWsOBQQDAgEABwtBBCEDQdwAIQIMAwtB9QAhAkEDIQMMAgtBAiEDQfsAIQIMAQtBAkEBIAQbIQNBgIDEACAEQQJ0dkEBcUEwciECIARBf2pBACAEGyEECyAGIAIgBxEAAEUNAAsLQQEMAQsgBkEnIAcRAAALIAVBEGokAAutAgEDfwJAAkACQAJAIAFBCU8EQEEQIAFLDQEMAgsgABANIQMMAgtBECEBC0HN/3sgAWsgAE0NAEEQIABBBGpBCyAASxtBB2pBeHEiBCABakEMahANIgJFDQAgAkF4aiEAAkAgAUF/aiIDIAJxRQRAIAAhAQwBCyAAKAIEQXhxIAIgA2pBACABa3FBeGoiAkEAIAEgAiAAa0EQSxtqIgEgAGsiAmshAyAALQAEQQNxBEAgASADEOYBIAAgAhDmASAAIAIQHwwBCyAAKAIAIQAgASADNgIEIAEgACACajYCAAsgAS0ABEEDcUUNASABKAIEQXhxIgAgBEEQak0NASABIAQQ5gEgASAEaiICIAAgBGsiABDmASACIAAQHwwBCyADDwsgAS0ABBogAUEIagvhAgEHf0EBIQkCQAJAIAJFDQAgAkEBdCABaiEKIABBgP4DcUEIdiELIABB/wFxIQ0CQANAIAFBAmohDCAHIAEtAAEiAmohCCALIAEtAAAiAUcEQCABIAtLDQMgCCEHIAogDCIBRw0BDAMLIAggB08EQCAIIARLDQIgAyAHaiEBAkADQCACRQ0BIAJBf2ohAiABLQAAIAFBAWohASANRw0AC0EAIQkMBQsgCCEHIAogDCIBRw0BDAMLCyAHIAhBrKbAABCcAQALIAggBEGspsAAEJsBAAsgBkUNACAFIAZqIQMgAEH//wNxIQEDQAJAIAVBAWohAAJ/IAAgBS0AACICQRh0QRh1IgRBAE4NABogACADRg0BIAUtAAEgBEH/AHFBCHRyIQIgBUECagshBSABIAJrIgFBAEgNAiAJQQFzIQkgAyAFRw0BDAILC0HcncAAQStBvKbAABDNAQALIAlBAXEL9gICAn8BfkH0ACECQQIhAwJAAkACQAJAAkACQCABQXdqDh8FAgQEAQQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQDAAtB3AAhAiABQdwARg0EDAMLQfIAIQIMAwtB7gAhAgwCC0EnIQIMAQsCQCABECgNAAJAAkAgAUGAgARPBEAgAUGAgAhPDQEgAUHrq8AAQSpBv6zAAEHAAUH/rcAAQbYDECUNAgwDCyABQcymwABBKEGcp8AAQaACQbypwABBrwIQJUUNAgwBCyABQeD//wBxQeDNCkYNASABQceRdWpBB0kNASABQf7//wBxQZ7wCkYNASABQd7idGpBDkkNASABQZ+odGpBnxhJDQEgAUHii3RqQeILSQ0BIAFBtdlzakG12ytJDQEgAUHwgzhJDQAMAQtBASEDIAEhAgwBCyABQQFyZ0ECdkEHc61CgICAgNAAhCEEQQMhAyABIQILIAAgAjYCBCAAIAM2AgAgAEEIaiAENwIAC/8CAgR/An4jAEFAaiIFJABBASEHAkAgAC0ABA0AIAAtAAUhCCAAKAIAIgYtAABBBHFFBEAgBigCGEHRn8AAQdOfwAAgCBtBAkEDIAgbIAZBHGooAgAoAgwRAgANASAGKAIYIAEgAiAGKAIcKAIMEQIADQEgBigCGEHdnsAAQQIgBigCHCgCDBECAA0BIAMgBiAEKAIMEQAAIQcMAQsgCEUEQCAGKAIYQcyfwABBAyAGQRxqKAIAKAIMEQIADQELIAVBAToAFyAFQTRqQfCewAA2AgAgBUEQaiAFQRdqNgIAIAUgBikCGDcDCCAGKQIIIQkgBikCECEKIAUgBi0AIDoAOCAFIAo3AyggBSAJNwMgIAUgBikCADcDGCAFIAVBCGoiBjYCMCAGIAEgAhAYDQAgBUEIakHdnsAAQQIQGA0AIAMgBUEYaiAEKAIMEQAADQAgBSgCMEHPn8AAQQIgBSgCNCgCDBECACEHCyAAQQE6AAUgACAHOgAEIAVBQGskAAvjAgEFfyAAQQt0IQRBICECQSAhAwJAA0ACQAJAIAJBAXYgAWoiAkECdEGYssAAaigCAEELdCIFIARPBEAgBCAFRg0CIAIhAwwBCyACQQFqIQELIAMgAWshAiADIAFLDQEMAgsLIAJBAWohAQsCQAJAIAFBH00EQCABQQJ0IQRBwwUhAyABQR9HBEAgBEGcssAAaigCAEEVdiEDC0EAIQUgAUF/aiICIAFNBEAgAkEgTw0CIAJBAnRBmLLAAGooAgBB////AHEhBQsCQCAEQZiywABqKAIAQRV2IgFBAWogA0YNACAAIAVrIQQgAUHDBSABQcMFSxshAiADQX9qIQBBACEDA0AgASACRg0EIAFBmLPAAGotAAAgA2oiAyAESw0BIAAgAUEBaiIBRw0ACyAAIQELIAFBAXEPCyABQSBB4LHAABCZAQALIAJBIEGAssAAEJkBAAsgAkHDBUHwscAAEJkBAAvYAgEDfyMAQRBrIgIkACAAKAIAIQACQCABQf8ATQRAIAAoAggiAyAAQQRqKAIARgRAIAAgAxBXIAAoAgghAwsgACADQQFqNgIIIAAoAgAgA2ogAToAAAwBCyACQQA2AgwCfyABQYAQTwRAIAFBgIAETwRAIAIgAUE/cUGAAXI6AA8gAiABQRJ2QfABcjoADCACIAFBBnZBP3FBgAFyOgAOIAIgAUEMdkE/cUGAAXI6AA1BBAwCCyACIAFBP3FBgAFyOgAOIAIgAUEMdkHgAXI6AAwgAiABQQZ2QT9xQYABcjoADUEDDAELIAIgAUE/cUGAAXI6AA0gAiABQQZ2QcABcjoADEECCyEBIABBBGooAgAgAEEIaiIEKAIAIgNrIAFJBEAgACADIAEQViAEKAIAIQMLIAAoAgAgA2ogAkEMaiABECwaIAQgASADajYCAAsgAkEQaiQAQQAL0gIBBX8jAEFAaiIDJAAgA0EQaiAAKAIYIgQQjAEgA0EANgIgIAMgAykDEDcDGCADQTJqIABBmQFqKQAANwEAIANBIDYCKCADIAApAJMBNwIsIANBGGogBCADQShqEFUCQCACIAFPBEAgAEEoaigCACIEIAJJDQEgASACRwRAIAJBDGwgAUEMbCICayEBIAAoAiAgAmohAgNAIAMoAhghACADQQhqIAMoAiAiBBCMASADKAIMIQUgAygCCCAAIARBFGwQLCEGAkAgAiIAQQRqIgcoAgAiAkUNACACQRRsRQ0AIAAoAgAQEwsgAEEMaiECIAAgBjYCACAAQQhqIAQ2AgAgByAFNgIAIAFBdGoiAQ0ACwsCQCADKAIcIgBFDQAgAEEUbEUNACADKAIYEBMLIANBQGskAA8LIAEgAkGIjMAAEJwBAAsgAiAEQYiMwAAQmwEAC88CAQN/IwBBEGsiAiQAAkAgAUH/AE0EQCAAKAIIIgMgAEEEaigCAEYEQCAAIAMQVyAAKAIIIQMLIAAgA0EBajYCCCAAKAIAIANqIAE6AAAMAQsgAkEANgIMAn8gAUGAEE8EQCABQYCABEkEQCACIAFBP3FBgAFyOgAOIAIgAUEMdkHgAXI6AAwgAiABQQZ2QT9xQYABcjoADUEDDAILIAIgAUE/cUGAAXI6AA8gAiABQRJ2QfABcjoADCACIAFBBnZBP3FBgAFyOgAOIAIgAUEMdkE/cUGAAXI6AA1BBAwBCyACIAFBP3FBgAFyOgANIAIgAUEGdkHAAXI6AAxBAgshASAAQQRqKAIAIABBCGoiBCgCACIDayABSQRAIAAgAyABEFYgBCgCACEDCyAAKAIAIANqIAJBDGogARAsGiAEIAEgA2o2AgALIAJBEGokAAu5AgEHfwJAIAJBD00EQCAAIQMMAQtBACAAa0EDcSIEIABqIQUgBARAIAAhAyABIQYDQCADIAYtAAA6AAAgBkEBaiEGIANBAWoiAyAFSQ0ACwsgAiAEayICQXxxIgcgBWohAwJAIAEgBGoiBEEDcQRAIAdBAUgNASAEQQN0IgFBGHEhCEEAIAFrQRhxIQkgBEF8cSIGQQRqIQEgBigCACEGA0AgBSAGIAh2IAEoAgAiBiAJdHI2AgAgAUEEaiEBIAVBBGoiBSADSQ0ACwwBCyAHQQFIDQAgBCEBA0AgBSABKAIANgIAIAFBBGohASAFQQRqIgUgA0kNAAsLIAJBA3EhAiAEIAdqIQELIAJBAU4EQCACIANqIQIDQCADIAEtAAA6AAAgAUEBaiEBIANBAWoiAyACSQ0ACwsgAAvEAgACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCABQXhqDggBAgMEBQ8GBwALIAFB/H5qDgoHCAsLCQsLCwsKCwsgAEEAOgCmASAAQQAgACgCOEF/aiIBIAAoAhgiAEF/aiABIABJGyABQQBIGzYCOA8LIABBARA/DwsgABCwASAALQClAUUNCAwLCyAAELABIAAtAKUBRQ0HDAoLIAAQsAEgAC0ApQFFDQYMCQsgAEEBOgChAQ8LIABBADoAoQEPCyAAELABIAAtAKUBRQ0DDAYLIAAQsAEMBQsgABBxDwsgACgCPCIBIAAoAkxGDQEgAQ0CCw8LIABBARCJAQ8LIABBADoApgEgACABQX9qNgI8IAAgACgCGEF/aiIBIAAoAjgiACAAIAFLGzYCOA8LIABBADoApgEgAEEANgI4C8ACAgV/AX4jAEEwayIEJABBJyECAkAgAEKQzgBUBEAgACEHDAELA0AgBEEJaiACaiIDQXxqIAAgAEKQzgCAIgdCkM4Afn2nIgVB//8DcUHkAG4iBkEBdEGOoMAAai8AADsAACADQX5qIAUgBkHkAGxrQf//A3FBAXRBjqDAAGovAAA7AAAgAkF8aiECIABC/8HXL1YgByEADQALCyAHpyIDQeMASgRAIAenIgVB//8DcUHkAG4hAyACQX5qIgIgBEEJamogBSADQeQAbGtB//8DcUEBdEGOoMAAai8AADsAAAsCQCADQQpOBEAgAkF+aiICIARBCWpqIANBAXRBjqDAAGovAAA7AAAMAQsgAkF/aiICIARBCWpqIANBMGo6AAALIAFB0J3AAEEAIARBCWogAmpBJyACaxAWIARBMGokAAu7AgEDfyMAQYABayIEJAACQAJAAkACQCABKAIAIgJBEHFFBEAgAkEgcQ0BIAA1AgAgARAuIQAMBAsgACgCACEAQQAhAgNAIAIgBGpB/wBqIABBD3EiA0EwQdcAIANBCkkbajoAACACQX9qIQIgAEEPSyAAQQR2IQANAAsgAkGAAWoiAEGBAU8NASABQYygwABBAiACIARqQYABakEAIAJrEBYhAAwDCyAAKAIAIQBBACECA0AgAiAEakH/AGogAEEPcSIDQTBBNyADQQpJG2o6AAAgAkF/aiECIABBD0sgAEEEdiEADQALIAJBgAFqIgBBgQFPDQEgAUGMoMAAQQIgAiAEakGAAWpBACACaxAWIQAMAgsgAEGAAUH8n8AAEJoBAAsgAEGAAUH8n8AAEJoBAAsgBEGAAWokACAAC7ECAQN/IwBBEGsiAyQAIAAgAUcEQANAIABBBGohBQJAIAAoAgAiAEH/AE0EQCACKAIIIgQgAigCBEYEfyACIAQQwQEgAigCCAUgBAsgAigCAGogADoAACACIAIoAghBAWo2AggMAQsgA0EANgIMIAIgA0EMagJ/IABBgBBPBEAgAEGAgARJBEAgAyAAQT9xQYABcjoADiADIABBDHZB4AFyOgAMIAMgAEEGdkE/cUGAAXI6AA1BAwwCCyADIABBP3FBgAFyOgAPIAMgAEESdkHwAXI6AAwgAyAAQQZ2QT9xQYABcjoADiADIABBDHZBP3FBgAFyOgANQQQMAQsgAyAAQT9xQYABcjoADSADIABBBnZBwAFyOgAMQQILEIsCCyABIAUiAEcNAAsLIANBEGokAAugAgEJfyAAKAIAQZiMwAAgACgCCBsvAQAhAiMAQRBrIghBADYCDCAAKAJAIgQgAEHIAGooAgBBAnRqIQECQCACQQEgAhtBf2oiBwRAIAAoAjghBkEBIQUDQEEAIQIgASAERg0CIANBAWohAyABQXxqIQEDQAJAIAVFDQAgBiABKAIASw0AIAEgBEYgAUF8aiEBRQ0BDAQLC0EAIQUgAyAHRw0ACwtBACECIAEgBEYNACABQXxqIQMgACgCOCEFA0AgAUF8aiEBIAcEQCABIQIMAgsgBSADKAIATQRAIAMgBEYgA0F8aiEDDQIMAQsLIAMhAgsgAiAIQQxqIAIbKAIAIQIgAEEAOgCmASAAIAIgACgCGCIAQX9qIAAgAksbNgI4C8MCAgR/AX4jAEEgayICJAAgASkCDCEGIAFBADYCDAJ/AkAgBqcEQCACIAZCIIinIgM2AhggASgCABogAkEQaiIEQSJBI0HWgcAALQAAGzYCBCAEQQA2AgAgAigCFCEEAkACQCACKAIQRQRAIAIgBDYCHCABKAIEQQFHBEAgAUEIaiACQRhqIAJBHGoQiAIiAUEkTwRAIAEQAAsgAigCHCIBQSRPBEAgARAACyACKAIYIgFBJEkNAyABEAAMAwsgAkEIaiADELgBIAIoAgwhAyACKAIIRQ0BEHQhBSADQSRPBEAgAxAACyAEQSRJDQQgBBAADAQLIAQhBSADQSRJDQMgAxAADAMLIAFBCGogAyAEEI8CC0EADAILQaOBwABBK0HggMAAEM0BAAtBAQshASAAIAU2AgQgACABNgIAIAJBIGokAAutAgIDfwF+IwBBIGsiAyQAIAEpAgwhBiABQQA2AgwCfwJAIAanBEAgAyAGQiCIpyIENgIYIANBEGogAiABKAIAEEEgAygCFCECAkACQCADKAIQRQRAIAMgAjYCHCABKAIEQQFHBEAgAUEIaiADQRhqIANBHGoQiAIiAUEkTwRAIAEQAAsgAygCHCIBQSRPBEAgARAACyADKAIYIgFBJEkNAyABEAAMAwsgA0EIaiAEELgBIAMoAgwhBCADKAIIRQ0BEHQhBSAEQSRPBEAgBBAACyACQSRJDQQgAhAADAQLIAIhBSAEQSRJDQMgBBAADAMLIAFBCGogBCACEI8CC0EADAILQaOBwABBK0HggMAAEM0BAAtBAQshASAAIAU2AgQgACABNgIAIANBIGokAAvZAgEGfyMAQUBqIgMkACADQTBqIAIQ9wECQAJAAkACfwJAIAMoAjBBAUcEQCADIAMpAjQ3AyggA0EgaiICIAEoAgg2AgQgAiABKAIANgIAIANCADcCNCADQdyFwAAoAgA2AjAgAygCICECIAMoAiQiBCEIIANBMGoiBSIGKAIIIQcgBigCBCAHayAESQRAIAYgByAIELsBCyACIARBAnQgAmogBRAwIANBGGogA0EoaiAFEHUgAygCGEUNASADKAIcDAILIAMoAjQhAQwDCyADQRBqIANBKGogAUEMahCLASADKAIQRQ0BIAMoAhQLIQEgA0EwahD7ASADKAIsIgJBJEkNASACEAAMAQsgAygCKBogA0EIaiIBIAMoAiw2AgQgAUEANgIAIAMoAgwhASADKAIIIQIgA0EwahD7AQwBC0EBIQILIAAgATYCBCAAIAI2AgAgA0FAayQAC6ACAQR/IwBBEGsiBCQAIAFBjAFqKAIAIgYEQCABKAKEAUEAIAYQXAsCQCADRQ0AIAIgA2ohBgNAAn8gAiwAACIDQX9KBEAgA0H/AXEhAyACQQFqDAELIAItAAFBP3EhBSADQR9xIQcgA0H/AXEiA0HfAU0EQCAHQQZ0IAVyIQMgAkECagwBCyACLQACQT9xIAVBBnRyIQUgA0HwAUkEQCAHQQx0IAVyIQMgAkEDagwBCyAHQRJ0QYCA8ABxIAItAANBP3EgBUEGdHJyIgNBgIDEAEYNAiACQQRqCyECIAEgAxASIAIgBkcNAAsLIAEoAowBIQIgASgChAEhASAEQQA2AgggBCABIAJqNgIEIAQgATYCACAAIAQQTyAEQRBqJAALvQIBBH8gAEIANwIQIAACf0EAIAFBgAJJDQAaQR8gAUH///8HSw0AGiABQQYgAUEIdmciA2t2QQFxIANBAXRrQT5qCyIDNgIcIANBAnRBpLvAAGohBCAAIQICQAJAAkACQEGYucAAKAIAIgBBASADdCIFcQRAQQBBGSADQQF2ayADQR9GGyEAIAQoAgAiAygCBEF4cSABRw0BIAMhAAwCC0GYucAAIAAgBXI2AgAgBCACNgIAIAIgBDYCGAwDCyABIAB0IQQDQCAEQR12QQRxIANqQRBqIgUoAgAiAEUNAiAEQQF0IQQgACIDKAIEQXhxIAFHDQALCyAAKAIIIgEgAjYCDCAAIAI2AgggAiAANgIMIAIgATYCCCACQQA2AhgPCyAFIAI2AgAgAiADNgIYCyACIAI2AgggAiACNgIMC8kCAgN/An4jAEFAaiIDJAAgAAJ/IAAtAAgEQCAAKAIEIQVBAQwBCyAAKAIEIQUgACgCACIELQAAQQRxRQRAQQEgBCgCGEHRn8AAQdufwAAgBRtBAkEBIAUbIARBHGooAgAoAgwRAgANARogASAEIAIoAgwRAAAMAQsCQCAFDQAgBCgCGEHZn8AAQQIgBEEcaigCACgCDBECAEUNAEEAIQVBAQwBCyADQQE6ABcgA0E0akHwnsAANgIAIANBEGogA0EXajYCACADIAQpAhg3AwggBCkCCCEGIAQpAhAhByADIAQtACA6ADggAyAHNwMoIAMgBjcDICADIAQpAgA3AxggAyADQQhqNgIwQQEgASADQRhqIAIoAgwRAAANABogAygCMEHPn8AAQQIgAygCNCgCDBECAAs6AAggACAFQQFqNgIEIANBQGskAAu2AgEFfyAAKAIYIQQCQAJAIAAoAgwgAEYEQEEUQRAgAEEUaiIBKAIAIgMbIABqKAIAIgINAUEAIQEMAgsgACgCCCICIAAoAgwiATYCDCABIAI2AggMAQsgASAAQRBqIAMbIQMDQCADIQUgAiIBQRRqIgMoAgAiAkUEQCABQRBqIQMgASgCECECCyACDQALIAVBADYCAAsCQCAERQ0AAkAgACgCHEECdEGku8AAaiICKAIAIABHBEBBEEEUIAQoAhAgAEYbIARqIAE2AgAgAQ0BDAILIAIgATYCACABDQBBmLnAAEGYucAAKAIAQX4gACgCHHdxNgIADwsgASAENgIYIAAoAhAiAgRAIAEgAjYCECACIAE2AhgLIABBFGooAgAiAEUNACABQRRqIAA2AgAgACABNgIYCwufAgECfyMAQRBrIgIkACAAKAIAIQACQCABQf8ATQRAIAAoAggiAyAAKAIERgR/IAAgAxDBASAAKAIIBSADCyAAKAIAaiABOgAAIAAgACgCCEEBajYCCAwBCyACQQA2AgwgACACQQxqIgACfyABQYAQTwRAIAFBgIAESQRAIAIgAUE/cUGAAXI6AA4gAiABQQx2QeABcjoADCACIAFBBnZBP3FBgAFyOgANQQMMAgsgAiABQT9xQYABcjoADyACIAFBEnZB8AFyOgAMIAIgAUEGdkE/cUGAAXI6AA4gAiABQQx2QT9xQYABcjoADUEEDAELIAIgAUE/cUGAAXI6AA0gAiABQQZ2QcABcjoADEECCyAAahDAAQsgAkEQaiQAQQAL0QICA38CfiMAQUBqIgMkAEEBIQUCQCAALQAEDQAgAC0ABSEFAkACQAJAAkAgACgCACIELQAAQQRxRQRAIAVB/wFxDQEMBAsgBUH/AXFFDQEMAgtBASEFIAQoAhhB0Z/AAEECIARBHGooAgAoAgwRAgBFDQIMAwtBASEFIAQoAhhB3p/AAEEBIARBHGooAgAoAgwRAgANAgtBASEFIANBAToAFyADQTRqQfCewAA2AgAgA0EQaiADQRdqNgIAIAMgBCkCGDcDCCAEKQIIIQYgBCkCECEHIAMgBC0AIDoAOCADIAc3AyggAyAGNwMgIAMgBCkCADcDGCADIANBCGo2AjAgASADQRhqIAIoAgwRAAANASADKAIwQc+fwABBAiADKAI0KAIMEQIAIQUMAQsgASAEIAIoAgwRAAAhBQsgAEEBOgAFIAAgBToABCADQUBrJAALYgEEf0HEvMAAKAIAIgBFBEBB1LzAAEH/HzYCAEEADwsDQCAAIgEoAgghACABKAIEGiABKAIAGiABQQxqKAIAGiACQQFqIQIgAA0AC0HUvMAAIAJB/x8gAkH/H0sbNgIAQQALvQICBn8BfiMAQTBrIgIkACABQQRqIQQCQCABKAIEBEBBiJnAACgCACEFDAELIAEoAgAhAyACQgA3AgwgAkGImcAAKAIAIgU2AgggAiACQQhqIgc2AhQgAkEYaiIGQRBqIANBEGopAgA3AwAgBkEIaiADQQhqKQIANwMAIAIgAykCADcDGCACQRRqQcCawAAgBhAdGiAEQQhqIAdBCGooAgA2AgAgBCACKQMINwIACyACQSBqIgMgBEEIaigCADYCACABQQxqQQA2AgAgBCkCACEIIAFBCGpBADYCACABIAU2AgQgAiAINwMYQQxBBBCKAiIBRQRAQQxBBEGAucAAKAIAIgBB1AAgABsRAQAACyABIAIpAxg3AgAgAUEIaiADKAIANgIAIABB+JnAADYCBCAAIAE2AgAgAkEwaiQAC6kCAQV/AkACQAJAIABBKGooAgAiAiAAKAI8IgRLBEAgACgCICAEQQxsaiICKAIIIgMgACgCOCIBSQ0BIAIoAgAgAUEUbGoiAiADIAFrIgMgACgCGCABayIBIAAoAgBBmIzAACAAKAIIGy8BACIFQQEgBRsiBSABIAVJGyIBENsBIAEgA0sNAiABBEAgAUEUbCACaiEBIABBkwFqIgNBBmohBQNAIAJBIDYCACACQQRqIAMpAAA3AAAgAkEKaiAFKQAANwAAIAEgAkEUaiICRw0ACwsgAEGMAWooAgAiAiAETQ0DIAAoAoQBIARqQQE6AAAPCyAEIAJB2IrAABCZAQALIAEgA0HYisAAEJoBAAsgASADQeiKwAAQmwEACyAEIAJB3IzAABCZAQALkwIBAn8jAEEQayICJAACQCABQf8ATQRAIAAoAggiAyAAKAIERgR/IAAgAxDBASAAKAIIBSADCyAAKAIAaiABOgAAIAAgACgCCEEBajYCCAwBCyACQQA2AgwgACACQQxqAn8gAUGAEE8EQCABQYCABEkEQCACIAFBP3FBgAFyOgAOIAIgAUEMdkHgAXI6AAwgAiABQQZ2QT9xQYABcjoADUEDDAILIAIgAUE/cUGAAXI6AA8gAiABQRJ2QfABcjoADCACIAFBBnZBP3FBgAFyOgAOIAIgAUEMdkE/cUGAAXI6AA1BBAwBCyACIAFBP3FBgAFyOgANIAIgAUEGdkHAAXI6AAxBAgsQiwILIAJBEGokAEEAC/wBAQp/IwBBEGsiCCAAKAIYIglBf2oiCjYCDCAAKAJAIgIgAEHIAGooAgBBAnRqIQUCQCABQX9qIgYEQCAAKAI4IQtBASEHA0AgAiAFRg0CIARBAWohBCACIQEDQAJAIAdFDQAgCyABKAIASQ0AIAFBBGoiASAFRw0BDAQLCyABQQRqIQJBACEHIAQgBkcNAAsgAUEEaiECCyACIAVGDQAgACgCOCEEIAIhAQNAIAYEQCACIQMMAgsgBCABKAIATwRAIAUgAUEEaiIBRg0CDAELCyABIQMLIAMgCEEMaiADGygCACEBIABBADoApgEgACABIAogCSABSxs2AjgLigIBCX8jAEEQayIFJAAgACgCBCAAKAIIIgNrIAFJBEAgACADIAEQvQEgACgCCCEDCyAAKAIAIANBDGxqIQQgAUECTwRAIAFBf2ohBiACKAIIIgdBFGwhCCACKAIAIQkDQCAFQQhqIAcQjAEgBSgCDCEKIAUoAgggCSAIECwhCyAEQQhqIAc2AgAgBEEEaiAKNgIAIAQgCzYCACAEQQxqIQQgBkF/aiIGDQALIAEgA2pBf2ohAwsCQCABBEAgBCACKQIANwIAIAAgA0EBajYCCCAEQQhqIAJBCGooAgA2AgAMAQsgACADNgIIIAIoAgQiAEUNACAAQRRsRQ0AIAIoAgAQEwsgBUEQaiQAC6sCAQJ/IwBB8ABrIgMkAAJAIAEtAABBAUcEQCADQRhqIgIgAS0AAbgQATYCBCACQQA2AgAgAygCHCEBIAMoAhghAgwBCyADIAFBAWo2AiQgAyABQQJqNgIoIAMgAUEDajYCLCADQUBrIgFBFGpBAzYCACADQdgAaiIEQRRqQQE2AgAgA0HkAGpBATYCACADQgQ3AkQgA0GIgsAANgJAIANBATYCXCADIAQ2AlAgAyADQSxqNgJoIAMgA0EoajYCYCADIANBJGo2AlggA0EwaiIEIAEQICADQRBqIgEgBCgCCDYCBCABIAQoAgA2AgAgA0EIaiACIAMoAhAgAygCFBD/ASADKAIMIQEgAygCCCECIAQQ+wELIAAgAjYCACAAIAE2AgQgA0HwAGokAAvlAQEEfyAAKAIAQZiMwAAgACgCCBsvAQAiAUEBIAEbIQICQAJAAkACQAJAIAAoAjwiAyAAKAJQIgFLBEAgACgCHCIBIANJDQIgAEEoaigCACIEIAFJDQMMAQsgAUEBaiIBIANJDQMgAEEoaigCACIEIAFJDQQLIAEgA2siBCACIAQgAkkbIQIgACgCICADQQxsaiAEIAIQ5AEgACABIAJrIAEQKiAAIAMgARCtAQ8LIAMgAUGoi8AAEJwBAAsgASAEQaiLwAAQmwEACyADIAFBmIvAABCcAQALIAEgBEGYi8AAEJsBAAunAgEBfyMAQRBrIgIkACACIAGtQoCAgIAQQgAgASgCGEGbjsAAQQMgAUEcaigCACgCDBECABuENwMAIAIgADYCDCACQZ6OwABBCiACQQxqIgFBqI7AABAnIAIgAEEEajYCDCACQbiOwABBCiABQaiOwAAQJyACIABBCGo2AgwgAkHCjsAAQQQgAUHIjsAAECcgAiAAQQlqNgIMIAJB2I7AAEEGIAFByI7AABAnIAIgAEEKajYCDCACQd6OwABBCSABQciOwAAQJyACIABBC2o2AgwgAkHnjsAAQQ0gAUHIjsAAECcgAiAAQQxqNgIMIAJB9I7AAEEFIAFByI7AABAnIAIgAEENajYCDCACQfmOwABBByABQciOwAAQJyACEI4BIAJBEGokAAuKAgEEfyAAKAIAQZiMwAAgACgCCBsvAQAiAUEBIAEbIQQCQAJAAkAgACgCPCIBIAAoAlAiAksEQCAAQShqKAIAIgMgAUkNAiAAKAIgIAFBDGxqIAMgAWsgACgCHCIDIAFrIgIgBCACIARJGyIEEN0BDAELAkAgAkEBaiIDIAJPBEAgAyABSQ0EIAIgAEEoaigCACICSQ0BIAMgAkH4isAAEJsBAAtBxKPAAEEsQfiKwAAQzQEACyADIAFrIgIgBCACIARJGyEEIAAoAiAgAUEMbGogAiAEEN0BCyAAIAEgASAEahAqIAAgASADEK0BDwsgASADQYiLwAAQmgEACyABIANB+IrAABCcAQAL5gEBAX8jAEEQayICJAAgACgCACACQQA2AgwgAkEMagJ/AkACQCABQYABTwRAIAFBgBBJDQEgAUGAgARPDQIgAiABQT9xQYABcjoADiACIAFBDHZB4AFyOgAMIAIgAUEGdkE/cUGAAXI6AA1BAwwDCyACIAE6AAxBAQwCCyACIAFBP3FBgAFyOgANIAIgAUEGdkHAAXI6AAxBAgwBCyACIAFBP3FBgAFyOgAPIAIgAUESdkHwAXI6AAwgAiABQQZ2QT9xQYABcjoADiACIAFBDHZBP3FBgAFyOgANQQQLEBggAkEQaiQAC+kBAgN/AX4jAEEgayIEJAACQCACIANqIgMgAk8EQCABKAIEIgJBAXQiBSADIAUgA0sbIgNBBCADQQRLGyIDrUIUfiIHQiCIp0VBAnQhBSAHpyEGAkAgAgRAIARBGGpBBDYCACAEIAJBFGw2AhQgBCABKAIANgIQDAELIARBADYCEAsgBCAGIAUgBEEQahBkQQEhAiAEKAIAQQFHBEAgBCgCBCECIAEgAzYCBCABIAI2AgBBACECDAILIAAgBCkCBDcCBAwBCyAAIAM2AgQgAEEIakEANgIAQQEhAgsgACACNgIAIARBIGokAAvpAQIDfwF+IwBBIGsiBCQAAkAgAiADaiIDIAJPBEAgASgCBCICQQF0IgUgAyAFIANLGyIDQQQgA0EESxsiA61CDH4iB0IgiKdFQQJ0IQUgB6chBgJAIAIEQCAEQRhqQQQ2AgAgBCACQQxsNgIUIAQgASgCADYCEAwBCyAEQQA2AhALIAQgBiAFIARBEGoQZEEBIQIgBCgCAEEBRwRAIAQoAgQhAiABIAM2AgQgASACNgIAQQAhAgwCCyAAIAQpAgQ3AgQMAQsgACADNgIEIABBCGpBADYCAEEBIQILIAAgAjYCACAEQSBqJAAL6QECBH8BfiMAQSBrIgMkAAJAIAJBAWoiBCACTwRAIAEoAgQiAkEBdCIFIAQgBSAESxsiBEEEIARBBEsbIgStQhx+IgdCIIinRUECdCEFIAenIQYCQCACBEAgA0EYakEENgIAIAMgAkEcbDYCFCADIAEoAgA2AhAMAQsgA0EANgIQCyADIAYgBSADQRBqEGRBASECIAMoAgBBAUcEQCADKAIEIQIgASAENgIEIAEgAjYCAEEAIQIMAgsgACADKQIENwIEDAELIAAgBDYCBCAAQQhqQQA2AgBBASECCyAAIAI2AgAgA0EgaiQAC+MBAQF/IwBBEGsiAiQAIAJBADYCDCAAIAJBDGoCfwJAAkAgAUGAAU8EQCABQYAQSQ0BIAFBgIAETw0CIAIgAUE/cUGAAXI6AA4gAiABQQx2QeABcjoADCACIAFBBnZBP3FBgAFyOgANQQMMAwsgAiABOgAMQQEMAgsgAiABQT9xQYABcjoADSACIAFBBnZBwAFyOgAMQQIMAQsgAiABQT9xQYABcjoADyACIAFBEnZB8AFyOgAMIAIgAUEGdkE/cUGAAXI6AA4gAiABQQx2QT9xQYABcjoADUEECxAYIAJBEGokAAvoAQEEfyMAQSBrIgMkAAJAIAJBAWoiBCACTwRAIAEoAgQiAkEBdCIFIAQgBSAESxsiBEEEIARBBEsbIgQgBEH/////A3FGQQJ0IQUgBEECdCEGAkAgAgRAIANBGGpBBDYCACADIAJBAnQ2AhQgAyABKAIANgIQDAELIANBADYCEAsgAyAGIAUgA0EQahBkQQEhAiADKAIAQQFHBEAgAygCBCECIAEgBDYCBCABIAI2AgBBACECDAILIAAgAykCBDcCBAwBCyAAIAQ2AgQgAEEIakEANgIAQQEhAgsgACACNgIAIANBIGokAAvzAQEEfyMAQdAAayICJAACQCABBEAgASgCACIDQX9GDQEgASADQQFqNgIAIAJBzABqQQE2AgAgAkIBNwI8IAJB7IPAADYCOCACQQg2AiwgAiABQQRqNgIoIAIgAkEoaiIDNgJIIAJBGGoiBCACQThqIgUQICABIAEoAgBBf2o2AgAgA0EIaiIBIARBCGooAgA2AgAgAiACKQMYNwMoIAJBEGoiBCADKAIINgIEIAQgAygCADYCACAFQQhqIAEoAgA2AgAgAiACKQMoNwM4IAJBCGogBRDlASAAIAIpAwg3AwAgAkHQAGokAA8LEKUCAAsQpgIAC+UBAgZ/AX4jAEHQAGsiAyQAIANBP2pBADsAACADQTBqIgYgA0E4aiIEQQhqIgUtAAA6AAAgA0EANgA7IAMgAykAODcDKCADQRBqIAEQjAEgA0EYaiIHQQhqIghBADYCACADIAMpAxA3AxggBUECOgAAIANBwQBqIAMpAyg3AAAgA0HJAGogBi0AADoAACADQQI6ADwgA0EgNgI4IAcgASAEEFUgA0EIaiACEI0BIAMpAwghCSAAQQA2AgggACAJNwIAIAUgCCgCADYCACADIAMpAxg3AzggACACIAQQQCADQdAAaiQAC9wBAQV/IwBBIGsiAyQAAkAgAkEBaiIEIAJPBEAgASgCBCIFQQF0IgIgBCACIARLGyIEQQQgBEEESxsiBCAEaiIGIARPQQF0IQcCQCAFBEAgA0EYakECNgIAIAMgAjYCFCADIAEoAgA2AhAMAQsgA0EANgIQCyADIAYgByADQRBqEGRBASECIAMoAgBBAUcEQCADKAIEIQIgASAENgIEIAEgAjYCAEEAIQIMAgsgACADKQIENwIEDAELIAAgBDYCBCAAQQhqQQA2AgBBASECCyAAIAI2AgAgA0EgaiQAC+sBAQV/IAAoAjgiASAAKAIYIgRPBEAgAEEAOgCmASAAIARBf2oiATYCOAsCQAJAIABBKGooAgAiAiAAKAI8IgNLBEAgACgCICADQQxsaiIFKAIIIgIgAUkNASAFKAIAIAFBFGxqIAIgAWsgBCABayIBIAAoAgBBmIzAACAAKAIIGy8BACICQQEgAhsiAiABIAJJGyIBEOMBIAAgBCABayAEEFogAEGMAWooAgAiASADTQ0CIAAoAoQBIANqQQE6AAAPCyADIAJBuIvAABCZAQALIAEgAkG4i8AAEJoBAAsgAyABQdyMwAAQmQEAC+UBAQZ/IwBBMGsiAiQAIAEoAgAhAyABKAIEIQQCQAJAA0AgAyAERg0BIAEgA0EBaiIFNgIAIAEgASgCCCIGQQFqNgIIIAMtAAAgBSEDRQ0ACyACQQhqEJEBIAIoAgwhAyACKAIIIgUgBjYCACACQRBqIgRBCGoiBkEBNgIAIAIgAzYCFCACIAU2AhAgAkEgaiIDQQhqIAFBCGooAgA2AgAgAiABKQIANwMgIAQgAxB+IABBCGogBigCADYCACAAIAIpAxA3AgAMAQsgAEIANwIEIABBrJTAACgCADYCAAsgAkEwaiQAC/sBAQR/IwBBMGsiAyQAIAIoAgAhBCACKAIIIQUQBCEGIANBIGoiAiABNgIEIAJBADYCACACQQhqIAY2AgACfwJAAkAgAygCIEEBRwRAIAMgAykCJDcDGCAFQRxsIQIDQCACRQ0DIAJBZGohAiADIAQ2AiAgBEEcaiEEIANBEGogA0EYaiADQSBqEIUBIAMoAhBFDQALIAMoAhQhBCADKAIcIgFBJEkNASABEAAMAQsgAygCJCEEC0EBDAELIAMoAhgaIANBCGoiASADKAIcNgIEIAFBADYCACADKAIMIQQgAygCCAshASAAIAQ2AgQgACABNgIAIANBMGokAAvMAQEBfyMAQTBrIgQkAAJAIAEEQCABKAIADQEgAUF/NgIAIAQgAzYCKCAEIAM2AiQgBCACNgIgIARBCGogBEEgahDlASAEQRBqIAFBBGogBCgCCCICIAQoAgwiAxA1IAMEQCACEBMLIAFBADYCACAEQShqIARBGGooAgAiATYCACAEIAQpAxA3AyAgBCgCJCABSwRAIARBIGogARDCASAEKAIoIQELIAQoAiAhAiAAIAE2AgQgACACNgIAIARBMGokAA8LEKUCAAsQpgIAC88BAQJ/IwBBIGsiBCQAAkAgAiADaiIDIAJPBEAgASgCBCICQQF0IgUgAyAFIANLGyIDQQggA0EISxshAwJAIAIEQCAEQRhqQQE2AgAgBCACNgIUIAQgASgCADYCEAwBCyAEQQA2AhALQQEhAiAEIANBASAEQRBqEGQgBCgCAEEBRwRAIAQoAgQhAiABIAM2AgQgASACNgIAQQAhAgwCCyAAIAQpAgQ3AgQMAQsgACADNgIEIABBCGpBADYCAEEBIQILIAAgAjYCACAEQSBqJAALiwIBA38jAEEgayIEJABBASEFQZC5wABBkLnAACgCACIGQQFqNgIAAkBB2LzAACgCAEEBRgRAQdy8wAAoAgBBAWohBQwBC0HYvMAAQQE2AgALQdy8wAAgBTYCAAJAAkAgBkEASA0AIAVBAksNACAEIAM2AhwgBCACNgIYQYS5wAAoAgAiAkF/TA0AQYS5wAAgAkEBaiICNgIAQYS5wABBjLnAACgCACIDBH9BiLnAACgCACAEQQhqIAAgASgCEBEBACAEIAQpAwg3AxAgBEEQaiADKAIUEQEAQYS5wAAoAgAFIAILQX9qNgIAIAVBAU0NAQsACyMAQRBrIgIkACACIAE2AgwgAiAANgIIAAvTAQEBfyMAQTBrIgMkACADQSBqIAEQ9wECfwJAAkACfwJAIAMoAiBBAUcEQCADIAMpAiQ3AxggA0EQaiADQRhqIAIQhgEgAygCEEUNASADKAIUDAILIAMoAiQhAgwDCyADQQhqIANBGGogAkEEahCGASADKAIIRQ0BIAMoAgwLIQIgAygCHCIBQSRJDQEgARAADAELIAMoAhgaIAMgAygCHDYCBCADQQA2AgAgAygCBCECIAMoAgAMAQtBAQshASAAIAI2AgQgACABNgIAIANBMGokAAvGAQEDfyAAKAIEIAAoAggiA2sgAUkEQCAAIAMgARC6ASAAKAIIIQMLIAAoAgAgA0EUbGohBCABQQJPBEAgAUF/aiEFA0AgBCACKQIANwIAIARBEGogAkEQaigCADYCACAEQQhqIAJBCGopAgA3AgAgBEEUaiEEIAVBf2oiBQ0ACyABIANqQX9qIQMLIAAgAQR/IAQgAikCADcCACAEQRBqIAJBEGooAgA2AgAgBEEIaiACQQhqKQIANwIAIANBAWoFIAMLNgIIC80BAQJ/IwBBIGsiAyQAAkAgASACaiICIAFJDQAgAEEEaigCACIBQQF0IgQgAiAEIAJLGyICQQggAkEISxshAgJAIAEEQCADQRhqQQE2AgAgAyABNgIUIAMgACgCADYCEAwBCyADQQA2AhALIAMgAiADQRBqEGcgAygCAEEBRgRAIANBCGooAgAiAEUNASADKAIEIABBgLnAACgCACIAQdQAIAAbEQEAAAsgAygCBCEBIABBBGogAjYCACAAIAE2AgAgA0EgaiQADwsQkQIAC80BAQN/IwBBIGsiAiQAAkAgAUEBaiIDIAFJDQAgAEEEaigCACIBQQF0IgQgAyAEIANLGyIDQQggA0EISxshAwJAIAEEQCACQRhqQQE2AgAgAiABNgIUIAIgACgCADYCEAwBCyACQQA2AhALIAIgAyACQRBqEGcgAigCAEEBRgRAIAJBCGooAgAiAEUNASACKAIEIABBgLnAACgCACIAQdQAIAAbEQEAAAsgAigCBCEBIABBBGog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IAIgAikDCDcDKCACIAJBKGo2AiAgASADEJ4BIAAQ+wEgAkFAayQAC4kBAQZ/AkAgACgCOCIDRQ0AIAMgACgCGE8NAAJAIABByABqKAIAIgFFBEAMAQsgACgCQCEFIAEhBANAAkAgAUEBdiACaiIBQQJ0IAVqKAIAIgYgA08EQCABIQQgAyAGRw0BDAQLIAFBAWohAgsgBCACayEBIAQgAksNAAsLIABBQGsgAiADEJ8BCwuPAQEFfwJAAkAgASgCBCIDIAJPBEAgA0UNAiADQQJ0IQMgASgCACEEIAJBAnQiBUUEQEEEIQYgA0UNAiAEEBMMAgsgBCADQQQgBRD+ASIGDQEgACAFNgIEIABBCGpBBDYCAEEBIQcMAgtByIjAAEEkQeyIwAAQzQEACyABIAI2AgQgASAGNgIACyAAIAc2AgALowEBAn8CQAJAAkACQCAAKAIAQZiMwAAgACgCCBsvAQAOAwABAgMLIAAgACgCOCAAKAIYEFogACAAKAI8IgFBAWogACgCHCICECogACABIAIQrQEPCyAAQQAgACgCGCIBIAAoAjhBAWoiAiACIAFLGxBaIABBACAAKAI8IgEQKiAAQQAgAUEBahCtAQ8LIABBACAAKAIcIgEQKiAAQQAgARCtAQsLrAEBA38jAEHQAGsiACQAIABBMzYCDCAAQfCAwAA2AgggAEIANwIUIABB1IPAACgCADYCECAAQSBqIgEgAEEQakGogsAAEOkBIABBCGoiAigCACACKAIEIAEQqQIEQEHAgsAAQTcgAEHIAGpB3IPAAEHEg8AAEIoBAAsgACAAQRBqIgEoAgg2AgQgACABKAIANgIAIAAoAgAgACgCBBCqAiABEPsBIABB0ABqJAALlgEBA38jAEEgayIDJAAgASgCACEEIANBEGoiBSACKAIINgIEIAUgAigCADYCACADQQhqIAQgAygCECADKAIUEP8BIAMoAgwhAgJ/IAMoAghFBEAgAyACNgIcIAFBBGogA0EcahCQAiADKAIcIgFBJE8EQCABEAALQQAMAQtBAQshASAAIAI2AgQgACABNgIAIANBIGokAAuWAQECfyAALQAIIQEgACgCBCICBEAgAUH/AXEhASAAAn9BASABDQAaAkAgAkEBRw0AIAAtAAlFDQAgACgCACICLQAAQQRxDQBBASACKAIYQdyfwABB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CyACIARB2IvAABCZAQALIAEgAkHYi8AAEJkBAAt2AQN/IAAoAgQgACgCCCICayABSQRAIAAgAiABELsBIAAoAgghAgsgACgCACIEIAJqIQMCQAJAIAFBAk8EQCADQQEgAUF/aiIBEFwgBCABIAJqIgJqIQMMAQsgAUUNAQsgA0EBOgAAIAJBAWohAgsgACACNgIIC3ABAX8jAEEQayIDJAAgAyACKAIAIAEoAgAQNCADKAIEIQICfyADKAIARQRAIAMgAjYCDCABQQRqIANBDGoQkAIgAygCDCIBQSRPBEAgARAAC0EADAELQQELIQEgACACNgIEIAAgATYCACADQRBqJAALcAEBfyMAQRBrIgMkACADIAEoAgAgAjUCABB4IAMoAgQhAgJ/IAMoAgBFBEAgAyACNgIMIAFBBGogA0EMahCQAiADKAIMIgFBJE8EQCABEAALQQAMAQtBAQshASAAIAI2AgQgACABNgIAIANBEGokAAt2AQJ/IwBBsAFrIgEkACABQQhqIgIgABC2AQJAIAIoAgQiAEUNACAAQQF0RQ0AIAIoAgAQEwsgAUEUahDsASABQShqIgAQsQEgABDtASABQTRqIgAQsQEgABDtASABQcgAahDnASABQYwBahD7ASABQbABaiQAC3gBAn8jAEEgayICJAAgAkEQaiIDIAAgARDcASACQQA6ABwgAkEIaiACQRxqIAMQUCACKAIMIQAgAigCCARAIAIgADYCHEGghMAAQSsgAkEcakHMhMAAQYCEwAAQigEACyACQRBqIgEQ0QEgARDOASACQSBqJAAgAAt6AQN/AkAgACgCUEEBaiICIAAoAkwiBE8EQCAAQShqKAIAIgMgAkkNASACIARrIgMgASADIAFJGyEBIAAoAiAgBEEMbGogAyABEN0BIABBACABECogAEEAIAIQrQEPCyAEIAJBrIzAABCcAQALIAIgA0GsjMAAEJsBAAuAAQEBfyMAQUBqIgUkACAFIAE2AgwgBSAANgIIIAUgAzYCFCAFIAI2AhAgBUEsakECNgIAIAVBPGpB5wA2AgAgBUICNwIcIAVB4J7AADYCGCAFQegANgI0IAUgBUEwajYCKCAFIAVBEGo2AjggBSAFQQhqNgIwIAVBGGogBBDoAQALbQEBfyMAQRBrIgMkACADIAIgASgCABAbIAMoAgQhAgJ/IAMoAgBFBEAgAyACNgIMIAFBBGogA0EMahCQAiADKAIMIgFBJE8EQCABEAALQQAMAQtBAQshASAAIAI2AgQgACABNgIAIANBEGokAAtlAgJ/AX4CQAJAAkAgAa1CFH4iBEIgiKcNACAEpyICQQBIDQAgAkUNASACQQQQigIiAw0CIAJBBEGAucAAKAIAIgBB1AAgABsRAQAACxCRAgALQQQhAwsgACABNgIEIAAgAzYCAAtlAgJ/AX4CQAJAAkAgAa1CDH4iBEIgiKcNACAEpyICQQBIDQAgAkUNASACQQQQigIiAw0CIAJBBEGAucAAKAIAIgBB1AAgABsRAQAACxCRAgALQQQhAwsgACABNgIEIAAgAzYCAAt8AQF/IAAtAAQhASAALQAFBEAgAUH/AXEhASAAAn9BASABDQAaIAAoAgAiAS0AAEEEcUUEQCABKAIYQdefwABBAiABQRxqKAIAKAIMEQIADAELIAEoAhhB1p/AAEEBIAFBHGooAgAoAgwRAgALIgE6AAQLIAFB/wFxQQBHC28BAX8jAEEgayICJAAgAkEAOgAfIAACfyABKAIAQQFHBEAgAkEQakKAgICAgAQ3AwAgAigCFCEBIAIoAhAMAQsgAkEIaiACQR9qIAFBBGoQVCACKAIMIQEgAigCCAs2AgAgACABNgIEIAJBIGokAAtuAQJ/An8gACgCPCICIAAoAlAiA00EQCABIAJqIgEgAyADIAFLGwwBCyABIAJqIgEgACgCHEF/aiICIAIgAUsbCyEBIABBADoApgEgACABNgI8IAAgACgCGEF/aiIBIAAoAjgiACAAIAFLGzYCOAs3AQF/QQRBBBCKAiIBRQRAQQRBBEGAucAAKAIAIgBB1AAgABsRAQAACyAAQQE2AgQgACABNgIAC24BAn8gAEEAOgCmASAAIAAoAhhBf2oiASAAKAI4IgIgAiABSxs2AjggACAAKAI8IgIgACgCAEGYjMAAIAAoAggbLwEAIgFBASABG2siAUEAIAFBAEobIAEgACgCTCIAIAEgAEobIAIgAEkbNgI8C14BAn8CQAJAAkAgASABaiICIAFJDQAgAkEASA0AIAJFDQEgAkECEIoCIgMNAiACQQJBgLnAACgCACIAQdQAIAAbEQEAAAsQkQIAC0ECIQMLIAAgATYCBCAAIAM2AgALVQEBfyAAIABBCGoiAEEHakF4cSAAayICaiEAQai8wAAgASACayIBNgIAQbC8wAAgADYCACAAIAFBAXI2AgQgACABakEoNgIEQcy8wABBgICAATYCAAtvAQR/IwBBIGsiAiQAQQEhAwJAIAAgARAvDQAgAUEcaigCACEEIAEoAhggAkEcakEANgIAIAJB0J3AADYCGCACQgE3AgwgAkHUncAANgIIIAQgAkEIahAdDQAgAEEEaiABEC8hAwsgAkEgaiQAIAMLcQECfyAAKAIYIAAoAjgiAWshAiAAIAEgASACIAAoAgBBmIzAACAAKAIIGy8BACIBQQEgARsiASACIAFJG2oQWiAAQYwBaigCACICIAAoAjwiAU0EQCABIAJB3IzAABCZAQALIAAoAoQBIAFqQQE6AAALcgEBfyMAQTBrIgIkACACIAE2AgQgAiAANgIAIAJBHGpBAjYCACACQSxqQeEANgIAIAJCAzcCDCACQdScwAA2AgggAkHhADYCJCACIAJBIGo2AhggAiACQQRqNgIoIAIgAjYCICACQQhqQeycwAAQ6AEAC3IBAX8jAEEwayICJAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakHhADYCACACQgM3AgwgAkGQncAANgIIIAJB4QA2AiQgAiACQSBqNgIYIAIgAkEEajYCKCACIAI2AiAgAkEIakHoi8AAEOgBAAtvAQF/IwBBMGsiAyQAIAMgATYCBCADIAA2AgAgA0EcakECNgIAIANBLGpB4QA2AgAgA0ICNwIMIANBzJ7AADYCCCADQeEANgIkIAMgA0EgajYCGCADIAM2AiggAyADQQRqNgIgIANBCGogAhDoAQALbwEBfyMAQTBrIgMkACADIAE2AgQgAyAANgIAIANBHGpBAjYCACADQSxqQeEANgIAIANCAjcCDCADQeCiwAA2AgggA0HhADYCJCADIANBIGo2AhggAyADQQRqNgIoIAMgAzYCICADQQhqIAIQ6AEAC28BAX8jAEEwayIDJAAgAyABNgIEIAMgADYCACADQRxqQQI2AgAgA0EsakHhADYCACADQgI3AgwgA0GAo8AANgIIIANB4QA2AiQgAyADQSBqNgIYIAMgA0EEajYCKCADIAM2AiAgA0EIaiACEOgBAAtvAQF/IwBBMGsiAyQAIAMgATYCBCADIAA2AgAgA0EcakECNgIAIANBLGpB4QA2AgAgA0ICNwIMIANBtKPAADYCCCADQeEANgIkIAMgA0EgajYCGCADIANBBGo2AiggAyADNgIgIANBCGogAhDoAQALWAEDfyMAQSBrIgIkACABQRxqKAIAIQMgASgCGCACQQhqIgFBEGogAEEQaikCADcDACABQQhqIABBCGopAgA3AwAgAiAAKQIANwMIIAMgARAdIAJBIGokAAtYAQN/IwBBIGsiAiQAIABBHGooAgAhAyAAKAIYIAJBCGoiAEEQaiABQRBqKQIANwMAIABBCGogAUEIaikCADcDACACIAEpAgA3AwggAyAAEB0gAkEgaiQAC1kBAn8gACgCCCIDIAFPBEAgAyAAKAIERgRAIAAgAxC8AQsgACgCACABQQJ0aiIEQQRqIAQgAyABa0ECdBAZIAAgA0EBajYCCCAEIAI2AgAPCyABIAMQlwEAC1sBAX8jAEEgayICJAAgAiAAKAIANgIEIAJBCGoiAEEQaiABQRBqKQIANwMAIABBCGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakHwmMAAIAAQHSACQSBqJAALWwEBfyMAQSBrIgIkACACIAAoAgA2AgQgAkEIaiIAQRBqIAFBEGopAgA3AwAgAEEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQcCawAAgABAdIAJBIGokAAtbAQF/IwBBIGsiAiQAIAIgACgCADYCBCACQQhqIgBBEGogAUEQaikCADcDACAAQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpBuJ3AACAAEB0gAkEgaiQAC1sBAX8jAEEgayICJAAgAiAAKAIANgIEIAJBCGoiAEEQaiABQRBqKQIANwMAIABBCGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakHYocAAIAAQHSACQSBqJAALWAEBfyMAQSBrIgIkACACIAA2AgQgAkEIaiIAQRBqIAFBEGopAgA3AwAgAEEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQfCYwAAgABAdIAJBIGokAAtYAQF/IwBBIGsiAiQAIAIgADYCBCACQQhqIgBBEGogAUEQaikCADcDACAAQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpB2KHAACAAEB0gAkEgaiQAC58BAQF/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CBsvAQAOBAACAgECCyAAEHcPCyAAQcgAakEANgIACwsyAQF/IAAgACgCAEGYjMAAIAAoAggbLwEAIgFBASABGxCQASAAQQA6AKYBIABBADYCOAsgAQF/AkAgACgCBCIBRQ0AIAFBAnRFDQAgACgCABATCwsgAQF/AkAgACgCBCIBRQ0AIAFBDGxFDQAgACgCABATCwseAAJAIABBBGooAgBFDQAgACgCACIARQ0AIAAQEwsLIAEBfwJAIAAoAgQiAUUNACAAQQhqKAIARQ0AIAEQEwsLHwACQCABQXxNBEAgACABQQQgAhD+ASIADQELAAsgAAslACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgBSABKAIQEQsAC0IAIAAtAABBAUcEQCABKAIYQcGPwABBByABQRxqKAIAKAIMEQIADwsgASgCGEG4j8AAQQkgAUEcaigCACgCDBECAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBEFAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBESAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBERAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBEJAAsgAQF/EAQhAiAAIAE2AgQgAEEANgIAIABBCGogAjYCAAshACAARQRAQfyIwABBMBCkAgALIAAgAiADIAEoAhARAwALHwAgAEUEQEH8iMAAQTAQpAIACyAAIAIgASgCEBEAAAstACABKAIYQbSPwABBto/AACAAKAIALQAAQQFGG0ECIAFBHGooAgAoAgwRAgALEQAgACgCBARAIAAoAgAQEwsLHAAgASgCGEGQssAAQQUgAUEcaigCACgCDBECAAsTACAAKAIAIgBBJE8EQCAAEAALCwwAIAAgASACIAMQFwsUACAAIAIgAxACNgIEIABBADYCAAsWACAAKAIAIgAoAgAgACgCCCABEKoBCxYAIAAoAgAiACgCACAAKAIIIAEQqQELFgAgACgCACIAKAIAIAAoAgggARC1AQsWACAAKAIAIgAoAgAgACgCCCABEKgBCxYAIAAoAgAiACgCACAAKAIIIAEQrgELFgAgACgCACIAKAIAIAAoAgggARCmAQsLACABBEAgABATCwsTACAAKAIAIAEgASACahDAAUEACxMAIAAoAgAgASgCACACKAIAEAoLFAAgACgCACABIAAoAgQoAgwRAAALCAAgACABECQLDgAgACABIAEgAmoQwAELEwAgAEGImsAANgIEIAAgATYCAAsQACABIAAoAgAgACgCBBAVCw0AIAAgASACEIsCQQALDQAgACgCACABIAIQAwsPACAAKAIAIAEoAgAQCBoLEgBB9JrAAEERQYibwAAQzQEACw0AIAAoAgAgARArQQALDgAgACgCABoDQAwACwALCwAgADUCACABEC4LCgAgACABIAIQOgsNACAAKAIAIAEgAhAYCwsAIAAxAAAgARAuCwsAIAApAwAgARAuCwsAIwAgAGokACMACwcAIAAQ+wELDAAgACgCACABEJcCCwsAIAAoAgAgARBwCwsAIAAoAgAgARBeCwwAIAAoAgAgARC3AQsMACAAKAIAIAEQ8gELCwAgACgCACABEF8LCwAgACgCACABEEMLCwAgACgCACABEGYLDAAgACgCACABELQBCwkAIAAgARAMAAsNAEGEl8AAQRsQpAIACw4AQZ+XwABBzwAQpAIACwsAIAAoAgAgARAjCykAAn8gACgCAC0AAEUEQCABQfShwABBBRAVDAELIAFB8KHAAEEEEBULCwoAIAIgACABEBULCAAgACABEAkLDQBC9Pme5u6jqvn+AAsMAELRy/+wrqSi1goLDABCwPTl+cSQy/10CwMAAQsDAAELC+Q4AQBBgIDAAAvaOC9ob21lL3J1bm5lci8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9zZXJkZS13YXNtLWJpbmRnZW4tMC4zLjEvc3JjL3Nlci5ycwAAEABgAAAAmwAAACgAAABNYXAga2V5IGlzIG5vdCBhIHN0cmluZyBhbmQgY2Fubm90IGJlIGFuIG9iamVjdCBrZXljYWxsZWQgYE9wdGlvbjo6dW53cmFwKClgIG9uIGEgYE5vbmVgIHZhbHVlZmdiZ2JvbGQBaXRhbGljdW5kZXJsaW5lc3RyaWtldGhyb3VnaGJsaW5raW52ZXJzZXJnYigsKQAAAP8AEAAEAAAAAwEQAAEAAAADARAAAQAAAAQBEAABAAAAAgAAAAwAAAAEAAAAAwAAAAQAAAAFAAAAYSBEaXNwbGF5IGltcGxlbWVudGF0aW9uIHJldHVybmVkIGFuIGVycm9yIHVuZXhwZWN0ZWRseS9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3N0cmluZy5ycwAAdwEQAEsAAABfCQAADgAAAAEAAAAAAAAABgAAAAAAAAABAAAABwAAAOwBEAAAAAAAc3JjL2xpYi5ycwAA9AEQAAoAAAAjAAAALQAAAPQBEAAKAAAAKAAAAC8AAABjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlAAkAAAAEAAAABAAAAAoAAABUcmllZCB0byBzaHJpbmsgdG8gYSBsYXJnZXIgY2FwYWNpdHkvcnVzdGMvOWQxYjIxMDZlMjNiMWFiZDMyZmNlMWYxNzI2NzYwNGE1MTAyZjU3YS9saWJyYXJ5L2FsbG9jL3NyYy9yYXdfdmVjLnJzgAIQAEwAAACrAQAACQAAAAEAAAAAAAAAIGNhbid0IGJlIHJlcHJlc2VudGVkIGFzIGEgSmF2YVNjcmlwdCBudW1iZXLkAhAAAAAAAOQCEAAsAAAARXJyb3IAAAAMAAAABAAAAAQAAAANAAAADgAAAAwAAAAEAAAADwAAABAAAAAFAAAAYSBEaXNwbGF5IGltcGxlbWVudGF0aW9uIHJldHVybmVkIGFuIGVycm9yIHVuZXhwZWN0ZWRseS9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3N0cmluZy5ycwAAhwMQAEsAAABfCQAADgAAAAEAAAAAAAAAEQAAAAAAAAABAAAABwAAAC9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMucnNUcmllZCB0byBzaHJpbmsgdG8gYSBsYXJnZXIgY2FwYWNpdHn8AxAATAAAAKsBAAAJAAAAY2xvc3VyZSBpbnZva2VkIHJlY3Vyc2l2ZWx5IG9yIGRlc3Ryb3llZCBhbHJlYWR5ZiYAAJIlAAAJJAAADCQAAA0kAAAKJAAAsAAAALEAAAAkJAAACyQAABglAAAQJQAADCUAABQlAAA8JQAAuiMAALsjAAAAJQAAvCMAAL0jAAAcJQAAJCUAADQlAAAsJQAAAiUAAGQiAABlIgAAwAMAAGAiAACjAAAAxSIAAHZ0LXJzL3NyYy9saWIucnMoBRAAEAAAAEsCAAARAAAAKAUQABAAAABuAgAAGgAAACgFEAAQAAAA7AIAABoAAAAoBRAAEAAAAO8CAAAaAAAAKAUQABAAAABUAwAADQAAACgFEAAQAAAAWQMAAA0AAAAoBRAAEAAAAGUDAAANAAAAKAUQABAAAABqAwAADQAAACgFEAAQAAAAdwMAAAkAAAAoBRAAEAAAAJcDAAAYAAAAKAUQABAAAACwBAAACQAAACgFEAAQAAAAvgQAACQAAAAoBRAAEAAAAMoEAAAaAAAAKAUQABAAAADSBAAAGgAAAAAAAAAoBRAAEAAAAGkFAAAJAAAAKAUQABAAAABxBQAACQAAACgFEAAQAAAAtQUAABoAAAAoBRAAEAAAANgFAAAXAAAAKAUQABAAAADeBQAACQAAAFNvc1BtQXBjU3RyaW5nT3NjU3RyaW5nRGNzSWdub3JlRGNzUGFzc3Rocm91Z2hEY3NJbnRlcm1lZGlhdGVEY3NQYXJhbURjc0VudHJ5Q3NpSWdub3JlQ3NpSW50ZXJtZWRpYXRlQ3NpUGFyYW1Dc2lFbnRyeUVzY2FwZUludGVybWVkaWF0ZUVzY2FwZUdyb3VuZFJHQgAAJgAAAAQAAAAEAAAAJwAAAEluZGV4ZWRQZW5mb3JlZ3JvdW5kKAAAAAQAAAAEAAAAKQAAAGJhY2tncm91bmRib2xkAAAqAAAABAAAAAQAAAArAAAAaXRhbGljdW5kZXJsaW5lc3RyaWtldGhyb3VnaGJsaW5raW52ZXJzZUNlbGwsAAAABAAAAAQAAAAtAAAALgAAAAQAAAAEAAAALwAAADAAAAAEAAAABAAAADEAAABHMUcwQWx0ZXJuYXRlUHJpbWFyeVNhdmVkQ3R4Y3Vyc29yX3gyAAAABAAAAAQAAAAzAAAAY3Vyc29yX3lwZW5vcmlnaW5fbW9kZWF1dG9fd3JhcF9tb2RlVlRzdGF0ZQA0AAAABAAAAAQAAAA1AAAAcGFyYW1zAAA2AAAABAAAAAQAAAA3AAAAaW50ZXJtZWRpYXRlc2NvbHVtbnNyb3dzYnVmZmVyAAA4AAAABAAAAAQAAAA5AAAAYWx0ZXJuYXRlX2J1ZmZlcmFjdGl2ZV9idWZmZXJfdHlwZQAAOgAAAAQAAAAEAAAAOwAAAGN1cnNvcl92aXNpYmxlY2hhcnNldAAAADwAAAAEAAAABAAAAD0AAAB0YWJzPgAAAAQAAAAEAAAAPwAAAGluc2VydF9tb2RlbmV3X2xpbmVfbW9kZW5leHRfcHJpbnRfd3JhcHN0b3BfbWFyZ2luYm90dG9tX21hcmdpbnNhdmVkX2N0eEAAAAAEAAAABAAAAEEAAABhbHRlcm5hdGVfc2F2ZWRfY3R4YWZmZWN0ZWRfbGluZXMAAABCAAAABAAAAAQAAABDAAAABAAAAAAAAAACAAAAAAAAAGFzc2VydGlvbiBmYWlsZWQ6IG1pZCA8PSBzZWxmLmxlbigpL3J1c3RjLzlkMWIyMTA2ZTIzYjFhYmQzMmZjZTFmMTcyNjc2MDRhNTEwMmY1N2EvbGlicmFyeS9jb3JlL3NyYy9zbGljZS9tb2QucnObCRAATQAAAKILAAAJAAAAYXNzZXJ0aW9uIGZhaWxlZDogayA8PSBzZWxmLmxlbigpAAAAmwkQAE0AAADNCwAACQAAAAQAAAAAAAAAYXNzZXJ0aW9uIGZhaWxlZDogbWlkIDw9IHNlbGYubGVuKCkvcnVzdGMvOWQxYjIxMDZlMjNiMWFiZDMyZmNlMWYxNzI2NzYwNGE1MTAyZjU3YS9saWJyYXJ5L2NvcmUvc3JjL3NsaWNlL21vZC5yc1cKEABNAAAAogsAAAkAAABhc3NlcnRpb24gZmFpbGVkOiBrIDw9IHNlbGYubGVuKCkAAABXChAATQAAAM0LAAAJAAAARAAAAAQAAAAEAAAARQAAAEYAAAAEAAAABAAAACsAAABHAAAABAAAAAQAAABIAAAASQAAAAQAAAAEAAAASgAAAEsAAAAEAAAABAAAADMAAABMAAAABAAAAAQAAAAtAAAAU29tZU0AAAAEAAAABAAAAE4AAABOb25lSnNWYWx1ZSgpAAAAYAsQAAgAAABoCxAAAQAAAAQAAAAAAAAAbnVsbCBwb2ludGVyIHBhc3NlZCB0byBydXN0cmVjdXJzaXZlIHVzZSBvZiBhbiBvYmplY3QgZGV0ZWN0ZWQgd2hpY2ggd291bGQgbGVhZCB0byB1bnNhZmUgYWxpYXNpbmcgaW4gcnVzdC9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMucnNUcmllZCB0byBzaHJpbmsgdG8gYSBsYXJnZXIgY2FwYWNpdHkAAO4LEABMAAAAqwEAAAkAAABQAAAABAAAAAQAAABRAAAAUgAAAFMAAAABAAAAAAAAAGNhbGxlZCBgT3B0aW9uOjp1bndyYXAoKWAgb24gYSBgTm9uZWAgdmFsdWVsaWJyYXJ5L3N0ZC9zcmMvcGFuaWNraW5nLnJzALsMEAAcAAAA8AEAAB8AAAC7DBAAHAAAAPEBAAAeAAAAVQAAAAwAAAAEAAAAVgAAAFcAAAAIAAAABAAAAFgAAABZAAAAEAAAAAQAAABaAAAAWwAAAFcAAAAIAAAABAAAAFwAAABdAAAAVwAAAAQAAAAEAAAAXgAAAF8AAABgAAAAbGlicmFyeS9hbGxvYy9zcmMvcmF3X3ZlYy5yc2NhcGFjaXR5IG92ZXJmbG93AAAAWA0QABwAAAAGAgAABQAAAGEgZm9ybWF0dGluZyB0cmFpdCBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvcmxpYnJhcnkvYWxsb2Mvc3JjL2ZtdC5ycwDLDRAAGAAAAFUCAAAcAAAAKSBzaG91bGQgYmUgPCBsZW4gKGlzIClsaWJyYXJ5L2FsbG9jL3NyYy92ZWMvbW9kLnJzaW5zZXJ0aW9uIGluZGV4IChpcyApIHNob3VsZCBiZSA8PSBsZW4gKGlzIAAAJw4QABQAAAA7DhAAFwAAAAoOEAABAAAACw4QABwAAAA9BQAADQAAAHJlbW92YWwgaW5kZXggKGlzIAAAfA4QABIAAAD0DRAAFgAAAAoOEAABAAAAYgAAAAAAAAABAAAABwAAAGIAAAAEAAAABAAAAGMAAABkAAAAZQAAAC4uAADQDhAAAgAAAGNhbGxlZCBgT3B0aW9uOjp1bndyYXAoKWAgb24gYSBgTm9uZWAgdmFsdWUAawAAAAAAAAABAAAAbAAAAGluZGV4IG91dCBvZiBib3VuZHM6IHRoZSBsZW4gaXMgIGJ1dCB0aGUgaW5kZXggaXMgAAAYDxAAIAAAADgPEAASAAAAYDogANAOEAAAAAAAXQ8QAAIAAABrAAAADAAAAAQAAABtAAAAbgAAAG8AAAAgICAgbGlicmFyeS9jb3JlL3NyYy9mbXQvYnVpbGRlcnMucnOMDxAAIAAAAC8AAAAhAAAAjA8QACAAAAAwAAAAEgAAACB7CiwKLCAgeyB9IH0oCigsKQpbXWxpYnJhcnkvY29yZS9zcmMvZm10L251bS5yc+EPEAAbAAAAZQAAABQAAAAweDAwMDEwMjAzMDQwNTA2MDcwODA5MTAxMTEyMTMxNDE1MTYxNzE4MTkyMDIxMjIyMzI0MjUyNjI3MjgyOTMwMzEzMjMzMzQzNTM2MzczODM5NDA0MTQyNDM0NDQ1NDY0NzQ4NDk1MDUxNTI1MzU0NTU1NjU3NTg1OTYwNjE2MjYzNjQ2NTY2Njc2ODY5NzA3MTcyNzM3NDc1NzY3Nzc4Nzk4MDgxODI4Mzg0ODU4Njg3ODg4OTkwOTE5MjkzOTQ5NTk2OTc5ODk5AABrAAAABAAAAAQAAABwAAAAcQAAAHIAAAB0cnVlZmFsc2VsaWJyYXJ5L2NvcmUvc3JjL3NsaWNlL21lbWNoci5ycwAAAPkQEAAgAAAAWwAAAAUAAAByYW5nZSBzdGFydCBpbmRleCAgb3V0IG9mIHJhbmdlIGZvciBzbGljZSBvZiBsZW5ndGggLBEQABIAAAA+ERAAIgAAAHJhbmdlIGVuZCBpbmRleCBwERAAEAAAAD4REAAiAAAAc2xpY2UgaW5kZXggc3RhcnRzIGF0ICBidXQgZW5kcyBhdCAAkBEQABYAAACmERAADQAAAGF0dGVtcHRlZCB0byBpbmRleCBzbGljZSB1cCB0byBtYXhpbXVtIHVzaXplbGlicmFyeS9jb3JlL3NyYy9zdHIvdmFsaWRhdGlvbnMucnMA8BEQACMAAAAeAQAAEQAAAFsuLi5dYnl0ZSBpbmRleCAgaXMgb3V0IG9mIGJvdW5kcyBvZiBgAAApEhAACwAAADQSEAAWAAAAXA8QAAEAAABiZWdpbiA8PSBlbmQgKCA8PSApIHdoZW4gc2xpY2luZyBgAABkEhAADgAAAHISEAAEAAAAdhIQABAAAABcDxAAAQAAACBpcyBub3QgYSBjaGFyIGJvdW5kYXJ5OyBpdCBpcyBpbnNpZGUgIChieXRlcyApIG9mIGAp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AQoCGgECAjkBBAIEAgIDAwEeAgMBCwI5AQQFAQIEARQCFgYBAToBAQIBBAgBBwMKAh4BOwEBAQwBCQEoAQMBNwEBAwUDAQQHAgsCHQE6AQIBAgEDAQUCBwILAhwCOQIBAQIECAEJAQoCHQFIAQQBAgMBAQgBUQECBwwIYgECCQsGSgIbAQEBAQE3DgEFAQIFCwEkCQFmBAEGAQICAhkCBAMQBA0BAgIGAQ8BAAMAAx0CHgIeAkACAQcIAQILCQEtAwEBdQIiAXYDBAIJAQYD2wICAToBAQcBAQEBAggGCgIBMB8xBDAHAQEFASgJDAIgBAICAQM4AQECAwEBAzoIAgKYAwENAQcEAQYBAwLGQAABwyEAA40BYCAABmkCAAQBCiACUAIAAQMBBAEZAgUBlwIaEg0BJggZCy4DMAECBAICJwFDBgICAgIMAQgBLwEzAQEDAgIFAgEBKgIIAe4BAgEEAQABABAQEAACAAHiAZUFAAMBAgUEKAMEAaUCAAQAApkLMQR7ATYPKQECAgoDMQQCAgcBPQMkBQEIPgEMAjQJCgQCAV8DAgEBAgYBoAEDCBUCOQIBAQEBFgEOBwMFwwgCAwEBFwFRAQIGAQECAQECAQLrAQIEBgIBAhsCVQgCAQECagEBAQIGAQFlAwIEAQUACQEC9QEKAgEBBAGQBAICBAEgCigGAgQIAQkGAgMuDQECAAcBBgEBUhYCBwECAQJ6BgMBAQIBBwEBSAIDAQEBAAIABTsHAAE/BFEBAAIALgIXAAEBAwQFCAgCBx4ElAMANwQyCAEOARYFAQ8ABwERAgcBAgEFAAcAAT0EAAdtBwBggPAAewlwcm9kdWNlcnMCCGxhbmd1YWdlAQRSdXN0AAxwcm9jZXNzZWQtYnkDBXJ1c3RjHTEuNTkuMCAoOWQxYjIxMDZlIDIwMjItMDItMjMpBndhbHJ1cwYwLjE5LjAMd2FzbS1iaW5kZ2VuEjAuMi43OCAoN2Y4MjBkYjRiKQ==");function Bg(A){return"number"==typeof A?A:"string"==typeof A?A.split(":").reverse().map(parseFloat).reduce((function(A,g,I){return A+g*Math.pow(60,I)})):void 0}function Qg(A,g){var I="undefined"!=typeof Symbol&&A[Symbol.iterator]||A["@@iterator"];if(!I){if(Array.isArray(A)||(I=function(A,g){if(!A)return;if("string"==typeof A)return Cg(A,g);var I=Object.prototype.toString.call(A).slice(8,-1);"Object"===I&&A.constructor&&(I=A.constructor.name);if("Map"===I||"Set"===I)return Array.from(A);if("Arguments"===I||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(I))return Cg(A,g)}(A))||g&&A&&"number"==typeof A.length){I&&(A=I);var B=0,Q=function(){};return{s:Q,n:function(){return B>=A.length?{done:!0}:{done:!1,value:A[B++]}},e:function(A){throw A},f:Q}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var C,E=!0,t=!1;return{s:function(){I=I.call(A)},n:function(){var A=I.next();return E=A.done,A},e:function(A){t=!0,C=A},f:function(){try{E||null==I.return||I.return()}finally{if(t)throw C}}}}function Cg(A,g){(null==g||g>A.length)&&(g=A.length);for(var I=0,B=new Array(g);I (await VA(Ig),$A))(),tg=function(){function A(g,I){var B;r(this,A),this.state="initial",this.driver=null,this.driverFn=g,this.changedLines=new Set,this.cursor=void 0,this.duration=null,this.cols=I.cols,this.rows=I.rows,this.startTime=null,this.speed=null!==(B=I.speed)&&void 0!==B?B:1,this.loop=I.loop,this.idleTimeLimit=I.idleTimeLimit,this.preload=I.preload,this.startAt=Bg(I.startAt),this.poster=I.poster,this.onSize=I.onSize,this.onFinish=I.onFinish,this.onTerminalUpdate=I.onTerminalUpdate}var g,I,B,Q,C,E;return a(A,[{key:"init",value:function(){var A=t(o().mark((function A(){var g,I,B,Q,C,E,t,e,i,n=this;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:return B=0,Q=this.feed.bind(this),C=this.now.bind(this),E=function(A,g){return window.setTimeout(A,g/n.speed)},t=function(A,g){return window.setInterval(A,g/n.speed)},e=function(A,g){n.resetVt(A,g)},i=function(){B++,!0===n.loop||"number"==typeof n.loop&&B 0){var A,g=new Map,I=Qg(this.changedLines);try{for(I.s();!(A=I.n()).done;){var B=A.value;g.set(B,{id:B,segments:this.vt.get_line(B)})}}catch(Q){I.e(Q)}finally{I.f()}return this.changedLines.clear(),g}}},{key:"getCursor",value:function(){var A;void 0===this.cursor&&this.vt&&(this.cursor=null!==(A=this.vt.get_cursor())&&void 0!==A&&A);return this.cursor}},{key:"getCurrentTime",value:function(){return"function"==typeof this.driver.getCurrentTime?this.driver.getCurrentTime():this.startTime?(this.now()-this.startTime)/1e3:void 0}},{key:"getRemainingTime",value:function(){if("number"==typeof this.duration)return this.duration-Math.min(this.getCurrentTime(),this.duration)}},{key:"getProgress",value:function(){if("number"==typeof this.duration)return Math.min(this.getCurrentTime(),this.duration)/this.duration}},{key:"getDuration",value:function(){return this.duration}},{key:"start",value:(Q=t(o().mark((function A(){var g;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:return A.next=2,this.initializeDriver();case 2:return this.onTerminalUpdate(),A.next=5,this.driver.start();case 5:"function"==typeof(g=A.sent)&&(this.driver.stop=g),this.startTime=this.now(),this.state="playing";case 9:case"end":return A.stop()}}),A,this)}))),function(){return Q.apply(this,arguments)})},{key:"pause",value:function(){"function"==typeof this.driver.pauseOrResume&&(this.driver.pauseOrResume(),this.state="paused")}},{key:"resume",value:function(){"function"==typeof this.driver.pauseOrResume&&(this.state="playing",this.driver.pauseOrResume())}},{key:"restart",value:(B=t(o().mark((function A(){return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:return A.next=2,this.seek(0);case 2:if(!A.sent){A.next=4;break}this.resume();case 4:case"end":return A.stop()}}),A,this)}))),function(){return B.apply(this,arguments)})},{key:"feed",value:function(A){var g=this;this.vt.feed(A).forEach((function(A){return g.changedLines.add(A)})),this.cursor=void 0,this.onTerminalUpdate()}},{key:"now",value:function(){return performance.now()*this.speed}},{key:"initializeDriver",value:function(){return void 0===this.initializeDriverPromise&&(this.initializeDriverPromise=this.doInitializeDriver()),this.initializeDriverPromise}},{key:"doInitializeDriver",value:(I=t(o().mark((function A(){var g,I,B,Q;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:if("function"!=typeof this.driver.init){A.next=7;break}return A.next=3,this.driver.init();case 3:Q=A.sent,this.duration=null!==(g=this.duration)&&void 0!==g?g:Q.duration,this.cols=null!==(I=this.cols)&&void 0!==I?I:Q.cols,this.rows=null!==(B=this.rows)&&void 0!==B?B:Q.rows;case 7:this.ensureVt();case 8:case"end":return A.stop()}}),A,this)}))),function(){return I.apply(this,arguments)})},{key:"ensureVt",value:function(){var A,g,I=null!==(A=this.cols)&&void 0!==A?A:80,B=null!==(g=this.rows)&&void 0!==g?g:24;void 0!==this.vt&&this.vt.cols===I&&this.vt.rows===B||this.initializeVt(I,B)}},{key:"resetVt",value:function(A,g){this.cols=A,this.rows=g,this.initializeVt(A,g)}},{key:"initializeVt",value:function(A,g){this.vt=this.wasm.create(A,g),this.vt.cols=A,this.vt.rows=g,this.changedLines.clear();for(var I=0;I ");var ig=function(A){return iA(g=eg.cloneNode(!0),(function(){return A.text})),d((function(I){var B,Q=function(A,g){var I=A.get("inverse")?A.has("bg")?A.get("bg"):"bg":A.get("fg"),B=A.get("inverse")?A.has("fg")?A.get("fg"):"fg":A.get("bg"),Q=ng(I,A.get("bold"),"fg-"),C=ng(B,A.get("blink"),"bg-"),E=null!=g?g:"";return Q&&(E+=" "+Q),C&&(E+=" "+C),E}(A.attrs,A.extraClass),C={bright:(B=A.attrs).has("bold"),italic:B.has("italic"),underline:B.has("underline"),blink:B.has("blink")},E=function(A){var g=A.get("inverse")?A.get("bg"):A.get("fg"),I=A.get("inverse")?A.get("fg"):A.get("bg"),B={};return"string"==typeof g&&(B.color=g),"string"==typeof I&&(B["background-color"]=I),B}(A.attrs);return Q!==I._v$&&(g.className=I._v$=Q),I._v$2=function(A,g,I={}){const B=Object.keys(g||{}),Q=Object.keys(I);let C,E;for(C=0,E=Q.length;C ');var rg=function(A){var g;return iA(g=og.cloneNode(!0),V($,{get each(){return function(){if("number"==typeof A.cursor){for(var g=[],I=0,B=0;B 0&&g.push([C[0].substring(0,e),C[1]]),g.push([C[0][e],E," cursor-a"]),g.push([C[0][e],t," cursor-b"]),e ');var ag=function(A){var g,I,B=function(){var g;return null!==(g=A.lineHeight)&&void 0!==g?g:1.3333333333},Q=R((function(){return{width:"".concat(A.cols,"ch"),height:"".concat(B()*A.rows,"em"),"font-size":"".concat(100*(A.scale||1),"%"),"font-family":A.fontFamily,"line-height":"".concat(B(),"em")}}));return g=sg.cloneNode(!0),"function"==typeof(I=A.ref)?I(g):A.ref=g,iA(g,V(_,{get each(){return A.lines},children:function(g,I){return C=R((function(){return I()===(null===(g=A.cursor)||void 0===g?void 0:g[1]);var g}),void 0,(Q=!0)?void 0:{equals:Q}),V(rg,{get segments(){return g.segments},get cursor(){return C()?null===(g=A.cursor)||void 0===g?void 0:g[0]:null;var g},get height(){return"".concat(B(),"em")}});var Q,C}})),d((function(I){var B=A.blink||A.cursorHold,C=A.blink,E=Q();return B!==I._v$&&g.classList.toggle("cursor",I._v$=B),C!==I._v$2&&g.classList.toggle("blink",I._v$2=C),I._v$3=eA(g,E,I._v$3),I}),{_v$:void 0,_v$2:void 0,_v$3:void 0}),g};const cg=CA(''),ug=CA(''),wg=CA(''),Dg=CA(' '),lg=CA(' ');function hg(A){A=Math.floor(A);var g=Math.floor(A/60),I=A%60,B="";return g<10&&(B+="0"),B+="".concat(g,":"),I<10&&(B+="0"),B+="".concat(I)}var fg=function(A){var g,I,B,Q,C,E=function(A){return function(g){g.preventDefault(),A(g)}},t=function(){return"number"==typeof A.currentTime?hg(A.currentTime):"--:--"},e=function(){return"number"==typeof A.remainingTime?"-"+hg(A.remainingTime):t()},i=function(g){if(!(g.altKey||g.shiftKey||g.metaKey||g.ctrlKey)){var I=g.currentTarget.offsetWidth,B=g.currentTarget.getBoundingClientRect(),Q=(g.clientX-B.left)/I;return A.onSeekClick("".concat(100*Q,"%"))}};return g=lg.cloneNode(!0),I=g.firstChild,B=I.firstChild,Q=B.nextSibling,C=I.nextSibling,iA(g,V(AA,{get when(){return A.isPausable},get children(){var g=wg.cloneNode(!0);return tA(g,"click",E(A.onPlayClick),!0),iA(g,V(gA,{get children(){return[V(IA,{get when(){return A.isPlaying},get children(){return cg.cloneNode(!0)}}),V(IA,{get when(){return!A.isPlaying},get children(){return ug.cloneNode(!0)}})]}})),g}}),I),iA(B,t),iA(Q,e),tA(C,"click",E(A.onFullscreenClick),!0),iA(g,V(AA,{get when(){return"number"==typeof A.progress||A.isSeekable},get children(){var g=Dg.cloneNode(!0),I=g.firstChild,B=I.firstChild.firstChild;return I.$$mousedown=i,d((function(g){return eA(B,{width:"100%",transform:"scaleX(".concat(A.progress||0),"transform-origin":"left center"},g)})),g}}),null),d((function(){return g.classList.toggle("seekable",A.isSeekable)})),g};EA(["click","mousedown"]);const yg=CA('');var Gg=function(A){return yg.cloneNode(!0)};const kg=CA('');var Ng=function(A){var g,I;return tA(I=kg.cloneNode(!0),"click",(g=A.onClick,function(A){A.preventDefault(),g(A)}),!0),I};EA(["click"]);const Mg=CA('');var Fg=function(A){var g,I,B,Q,C,E,e,n,r,s,a=i(function(A,g){const I=fA(A||{});return[lA(I),function(...A){J((()=>MA(I,A)))}]}({state:"initial",cols:A.cols,rows:A.rows,lines:[],cursor:void 0,charW:null,charH:null,bordersW:null,bordersH:null,containerW:null,containerH:null,showControls:!1,isPausable:!0,isSeekable:!0,isFullscreen:!1,currentTime:null,remainingTime:null,progress:null,blink:!0,cursorHold:!1}),2),c=a[0],u=a[1],w=null!==(g=A.autoPlay)&&void 0!==g?g:A.autoplay,D=function(){return c.cols||80},l=function(){return c.rows||24},h=new tg(A.driverFn,{cols:A.cols,rows:A.rows,loop:A.loop,speed:A.speed,preload:A.preload,startAt:A.startAt,poster:A.poster,idleTimeLimit:A.idleTimeLimit,onSize:function(A,g){gL(s))),Y((function(){h.stop(),H(),K(),r.disconnect()})),p((function(){var A=c.state;"playing"===A?(q(),b(),E.dispatchEvent(new CustomEvent("play"))):"paused"===A&&(H(),K(),m(),E.dispatchEvent(new CustomEvent("pause")))}));var f=function(){var A=t(o().mark((function A(){var g,I;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:if(void 0===(g=h.play())){A.next=7;break}return u("state","loading"),I=setTimeout((function(){u("state","waiting")}),1e3),A.next=6,g;case 6:clearTimeout(I);case 7:u("state","playing");case 8:case"end":return A.stop()}}),A)})));return function(){return A.apply(this,arguments)}}(),y=function(){var A=t(o().mark((function A(){var g;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:return A.next=2,h.pauseOrResume();case 2:g=A.sent,u("state",g?"playing":"paused");case 4:case"end":return A.stop()}}),A)})));return function(){return A.apply(this,arguments)}}(),G=function(){var A=t(o().mark((function A(g){return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:return A.next=2,h.seek(g);case 2:if(!A.sent){A.next=4;break}m();case 4:case"end":return A.stop()}}),A)})));return function(g){return A.apply(this,arguments)}}(),k=function(){var A=h.getChangedLines();A&&A.forEach((function(A,g){u("lines",g,dA(A))})),u("cursor",dA(h.getCursor())),u("cursorHold",!0),I=void 0},N=R((function(){var g;if(c.charW){console.debug("containerW = ".concat(c.containerW));var I=c.charW*D()+c.bordersW,B=c.charH*l()+c.bordersH,Q=null!==(g=A.fit)&&void 0!==g?g:"width";if("both"===Q||c.isFullscreen)Q=c.containerW/c.containerH>I/B?"height":"width";if(!1===Q||"none"===Q)return{};if("width"===Q){var C=c.containerW/I;return{scale:C,width:c.containerW,height:B*C}}if("height"===Q){var E=c.containerH/B;return{scale:E,width:I*E,height:c.containerH}}throw"unsupported fit mode: ".concat(Q)}})),M=function(){var A;u("isFullscreen",null!==(A=document.fullscreenElement)&&void 0!==A?A:document.webkitFullscreenElement)},F=function(){var A,g,I,B;c.isFullscreen?(null!==(A=null!==(g=document.exitFullscreen)&&void 0!==g?g:document.webkitExitFullscreen)&&void 0!==A?A:function(){}).apply(document):(null!==(I=null!==(B=E.requestFullscreen)&&void 0!==B?B:E.webkitRequestFullscreen)&&void 0!==I?I:function(){}).apply(E)},S=function(A){if(!(A.altKey||A.metaKey||A.ctrlKey))if(A.shiftKey){if("ArrowLeft"==A.key)G("<<<");else{if("ArrowRight"!=A.key)return;G(">>>")}A.preventDefault()}else{if(" "==A.key)y();else if("f"==A.key)F();else if("ArrowLeft"==A.key)G("<<");else if("ArrowRight"==A.key)G(">>");else{if(!(A.key.charCodeAt(0)>=48&&A.key.charCodeAt(0)<=57))return;var g=(A.key.charCodeAt(0)-48)/10;G("".concat(100*g,"%"))}A.preventDefault()}},v=function(){c.isFullscreen&&x(!0)},U=function(){c.isFullscreen||x(!1)},b=function(){Q=setInterval(m,100)},K=function(){clearInterval(Q)},m=function(){var A=h.getCurrentTime(),g=h.getRemainingTime(),I=h.getProgress();u({currentTime:A,remainingTime:g,progress:I})},q=function(){C=setInterval((function(){u((function(A){var g={blink:!A.blink};return g.blink&&(g.cursorHold=!1),g}))}),500)},H=function(){clearInterval(C),u("blink",!0)},x=function A(g){clearTimeout(B),g&&(B=setTimeout((function(){return A(!1)}),2e3)),u("showControls",g)},j=function(){var g=Mg.cloneNode(!0),I=g.firstChild;"function"==typeof E?E(g):E=g,g.addEventListener("webkitfullscreenchange",M),g.addEventListener("fullscreenchange",M),g.$$mousemove=v,g.$$keydown=S,g.addEventListener("keypress",S);return"function"==typeof e?e(I):e=I,I.$$mousemove=function(){return x(!0)},I.addEventListener("mouseleave",U),iA(I,V(ag,{get cols(){return D()},get rows(){return l()},get scale(){return null===(A=N())||void 0===A?void 0:A.scale;var A},get blink(){return c.blink},get lines(){return c.lines},get cursor(){return c.cursor},get cursorHold(){return c.cursorHold},get fontFamily(){return A.terminalFontFamily},get lineHeight(){return A.terminalLineHeight},ref:function(A){"function"==typeof n?n(A):n=A}}),null),iA(I,V(fg,{get currentTime(){return c.currentTime},get remainingTime(){return c.remainingTime},get progress(){return c.progress},get isPlaying(){return"playing"==c.state},get isPausable(){return c.isPausable},get isSeekable(){return c.isSeekable},onPlayClick:y,onFullscreenClick:F,onSeekClick:G}),null),iA(I,V(gA,{get children(){return[V(IA,{get when(){return"initial"==c.state&&!w},get children(){return V(Ng,{onClick:f})}}),V(IA,{get when(){return"waiting"==c.state},get children(){return V(Gg,{})}})]}}),null),d((function(B){var Q,C=c.showControls,E="asciinema-player asciinema-theme-".concat(null!==(Q=A.theme)&&void 0!==Q?Q:"asciinema"),t=function(){var g={};!1!==A.fit&&"none"!==A.fit||void 0===A.terminalFontSize||("small"===A.terminalFontSize?g["font-size"]="12px":"medium"===A.terminalFontSize?g["font-size"]="18px":"big"===A.terminalFontSize?g["font-size"]="24px":g["font-size"]=A.terminalFontSize);var I=N();return void 0===I?(g.height=0,g):(void 0!==I.width&&(g.width="".concat(I.width,"px"),g.height="".concat(I.height,"px")),g)}();return C!==B._v$&&g.classList.toggle("hud",B._v$=C),E!==B._v$2&&(I.className=B._v$2=E),B._v$3=eA(I,t,B._v$3),B}),{_v$:void 0,_v$2:void 0,_v$3:void 0}),g}();return j.__controller={getCurrentTime:function(){return h.getCurrentTime()},getDuration:function(){return h.getDuration()},play:f,pause:function(){"playing"===c.state&&y()},seek:G},j};EA(["keydown","mousemove"]);var dg=function(A){function g(A,I){r(this,g),this.input=A,this.xfs=null!=I?I:[]}return a(g,[{key:"map",value:function(A){return this.transform(function(A){return function(g){return function(I){g(A(I))}}}(A))}},{key:"flatMap",value:function(A){return this.transform(function(A){return function(g){return function(I){A(I).forEach(g)}}}(A))}},{key:"filter",value:function(A){return this.transform(function(A){return function(g){return function(I){A(I)&&g(I)}}}(A))}},{key:"take",value:function(A){return this.transform(function(A){var g=0;return function(I){return function(B){gA&&I(B)}}}(A))}},{key:"transform",value:function(A){return new g(this.input,this.xfs.concat([A]))}},{key:"toArray",value:function(){return Array.from(this)}},{key:Symbol.iterator,value:function(){var A,g,I=this,B=0,Q=0,C=[],E=!1,t=(A=this.xfs,g=function(A){return C.push(A)},A.reverse().reduce((function(A,g){var I=pg(g(A.step));return{step:I.step,flush:function(){I.flush(),A.flush()}}}),pg(g)));return{next:function(){for(Q===C.length&&(C=[],Q=0);0===C.length&&B 0?{done:!1,value:C[Q++]}:{done:!0}}}}}]),g}();function pg(A){return"function"==typeof A?{step:A,flush:function(){}}:A}function Rg(A,g,I){var B,Q,C,E,e,i,n,r,s,a=A.url,c=A.fetchOpts,u=void 0===c?{}:c,w=g.feed,D=g.now,l=g.setTimeout,h=g.onFinish,f=I.idleTimeLimit,y=I.startAt,G=0,k=0;function N(){return M.apply(this,arguments)}function M(){return(M=t(o().mark((function A(){var g,I,t,i;return o().wrap((function(A){for(;;)switch(A.prev=A.next){case 0:if(C){A.next=18;break}return A.next=3,fetch(a,u);case 3:if((I=A.sent).ok){A.next=6;break}throw"failed fetching asciicast file: ".concat(I.statusText," (").concat(I.status,")");case 6:return A.t0=Jg,A.next=9,I.text();case 9:A.t1=A.sent,t=(0,A.t0)(A.t1),B=t.cols,Q=t.rows,f=null!==(g=f)&&void 0!==g?g:t.idleTimeLimit,i=Yg(t.frames,f,y),C=i.frames,e=i.effectiveStartAt,E=C[C.length-1][0];case 18:case"end":return A.stop()}}),A)})))).apply(this,arguments)}function F(){var A=C[G];if(A){var g=1e3*A[0]-(D()-n);g<0&&(g=0),i=l(d,g)}else i=null,r=1e3*E,h()}function d(){var A,g=C[G];do{w(g[1]),k=1e3*g[0],g=C[++G],A=D()-n}while(g&&A>1e3*g[0]);F()}function p(){clearTimeout(i),i=null,r=D()-n}function R(){n=D()-r,r=null,F()}function J(A){var g=!!i;if(g&&p(),"string"==typeof A){var I,B=(null!==(I=r)&&void 0!==I?I:0)/1e3;"<<"===A?A=B-5:">>"===A?A=B+5:"<<<"===A?A=B-.1*E:">>>"===A?A=B+.1*E:"%"===A[A.length-1]&&(A=parseFloat(A.substring(0,A.length-1))/100*E)}var Q=1e3*Math.min(Math.max(A,0),E);Q 1&&void 0!==arguments[1]?arguments[1]:1/0,I=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,B=0,Q=0,C=I,E=Array.from(Lg(A).map((function(A){var E=A[0]-B-g;return B=A[0],E>0&&(Q+=E,A[0]C)){B.next=5;break}return B.next=5,Kg(E-C);case 5:A(Q[2]);case 6:case"end":return B.stop()}}),B)})));return function(A){return B.apply(this,arguments)}}());return{pushEvent:function(A){void 0===I&&(I=bg()),"o"==A[1]&&B.push(A)},pushText:function(A){void 0===I&&(I=bg());var g=(bg()-I)/1e3;B.push([g,"o",A])},stop:function(){Q()}}}function bg(){return(new Date).getTime()}function Kg(A){return new Promise((function(g){setTimeout(g,A)}))}function mg(A,g){var I,B,Q=A.url,C=A.bufferTime,E=void 0===C?0:C,t=g.feed,e=g.reset,i=new TextDecoder,n=250,o=!1;function r(){void 0!==B&&B.stop(),B=Ug(t,E)}function s(){(I=new WebSocket(Q)).binaryType="arraybuffer",I.onopen=function(){console.debug("websocket: opened"),r(),n=250},I.onmessage=function(A){if("string"==typeof A.data){var g,I,Q=JSON.parse(A.data);if(void 0!==Q.cols||void 0!==Q.width)r(),e(null!==(g=Q.cols)&&void 0!==g?g:Q.width,null!==(I=Q.rows)&&void 0!==I?I:Q.height);else B.pushEvent(Q)}else B.pushText(i.decode(A.data))},I.onclose=function(A){o||A.wasClean?console.debug("websocket: closed"):(console.debug("websocket: unclean close, reconnecting in ".concat(n,"...")),setTimeout(s,n),n=Math.min(2*n,5e3))}}return{start:function(){s()},stop:function(){o=!0,void 0!==B&&B.stop(),void 0!==I&&I.close()}}}function qg(A,g){var I,B,Q=A.url,C=A.bufferTime,E=void 0===C?0:C,t=g.feed,e=g.reset;function i(){void 0!==B&&B.stop(),B=Ug(t,E)}return{start:function(){(I=new EventSource(Q)).addEventListener("open",(function(){console.debug("eventsource: opened"),i()})),I.addEventListener("message",(function(A){var g,I,Q=JSON.parse(A.data);void 0!==Q.cols||void 0!==Q.width?(i(),e(null!==(g=Q.cols)&&void 0!==g?g:Q.width,null!==(I=Q.rows)&&void 0!==I?I:Q.height)):B.pushEvent(Q)})),I.addEventListener("done",(function(){console.debug("eventsource: closed"),I.close()}))},stop:function(){void 0!==B&&B.stop(),void 0!==I&&I.close()}}}function Hg(A,g){var I=Object.keys(A);if(Object.getOwnPropertySymbols){var B=Object.getOwnPropertySymbols(A);g&&(B=B.filter((function(g){return Object.getOwnPropertyDescriptor(A,g).enumerable}))),I.push.apply(I,B)}return I}function xg(A){for(var g=1;g 2&&void 0!==arguments[2]?arguments[2]:{},Q=xg({driverFn:Zg(A)},B),C=QA((function(){return I=V(Fg,Q)}),g),E=I.__controller,t={el:I,dispose:C,getCurrentTime:E.getCurrentTime,getDuration:E.getDuration,play:E.play,pause:E.pause,seek:E.seek,addEventListener:function(A,g){var B=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;return I.addEventListener(A,g.bind(t),B)}};return t}function Zg(A){"string"==typeof A&&(A="ws://"==A.substring(0,5)||"wss://"==A.substring(0,6)?{driver:"websocket",url:A}:"test://"==A.substring(0,7)?{driver:"test",kind:A.substring(7)}:{driver:"asciicast",url:A}),void 0===A.driver&&(A.driver="asciicast");var g=new Map([["asciicast",Rg],["websocket",mg],["eventsource",qg],["test",Sg]]);if("function"==typeof A)return A;if(g.has(A.driver)){var I=g.get(A.driver);return function(g,B){return I(A,g,B)}}throw"unsupported driver: ".concat(JSON.stringify(A))}},1262:(A,g,I)=>{"use strict";I.r(g),I.d(g,{default:()=>C});var B=I(7294),Q=I(2389);function C(A){let{children:g,fallback:I}=A;return(0,Q.Z)()?B.createElement(B.Fragment,null,g?.()):I??null}},7061:(A,g,I)=>{var B=I(8698).default;function Q(){"use strict";A.exports=Q=function(){return g},A.exports.__esModule=!0,A.exports.default=A.exports;var g={},I=Object.prototype,C=I.hasOwnProperty,E=Object.defineProperty||function(A,g,I){A[g]=I.value},t="function"==typeof Symbol?Symbol:{},e=t.iterator||"@@iterator",i=t.asyncIterator||"@@asyncIterator",n=t.toStringTag||"@@toStringTag";function o(A,g,I){return Object.defineProperty(A,g,{value:I,enumerable:!0,configurable:!0,writable:!0}),A[g]}try{o({},"")}catch(J){o=function(A,g,I){return A[g]=I}}function r(A,g,I,B){var Q=g&&g.prototype instanceof c?g:c,C=Object.create(Q.prototype),t=new d(B||[]);return E(C,"_invoke",{value:k(A,I,t)}),C}function s(A,g,I){try{return{type:"normal",arg:A.call(g,I)}}catch(J){return{type:"throw",arg:J}}}g.wrap=r;var a={};function c(){}function u(){}function w(){}var D={};o(D,e,(function(){return this}));var l=Object.getPrototypeOf,h=l&&l(l(p([])));h&&h!==I&&C.call(h,e)&&(D=h);var f=w.prototype=c.prototype=Object.create(D);function y(A){["next","throw","return"].forEach((function(g){o(A,g,(function(A){return this._invoke(g,A)}))}))}function G(A,g){function I(Q,E,t,e){var i=s(A[Q],A,E);if("throw"!==i.type){var n=i.arg,o=n.value;return o&&"object"==B(o)&&C.call(o,"__await")?g.resolve(o.__await).then((function(A){I("next",A,t,e)}),(function(A){I("throw",A,t,e)})):g.resolve(o).then((function(A){n.value=A,t(n)}),(function(A){return I("throw",A,t,e)}))}e(i.arg)}var Q;E(this,"_invoke",{value:function(A,B){function C(){return new g((function(g,Q){I(A,B,g,Q)}))}return Q=Q?Q.then(C,C):C()}})}function k(A,g,I){var B="suspendedStart";return function(Q,C){if("executing"===B)throw new Error("Generator is already running");if("completed"===B){if("throw"===Q)throw C;return R()}for(I.method=Q,I.arg=C;;){var E=I.delegate;if(E){var t=N(E,I);if(t){if(t===a)continue;return t}}if("next"===I.method)I.sent=I._sent=I.arg;else if("throw"===I.method){if("suspendedStart"===B)throw B="completed",I.arg;I.dispatchException(I.arg)}else"return"===I.method&&I.abrupt("return",I.arg);B="executing";var e=s(A,g,I);if("normal"===e.type){if(B=I.done?"completed":"suspendedYield",e.arg===a)continue;return{value:e.arg,done:I.done}}"throw"===e.type&&(B="completed",I.method="throw",I.arg=e.arg)}}}function N(A,g){var I=g.method,B=A.iterator[I];if(void 0===B)return g.delegate=null,"throw"===I&&A.iterator.return&&(g.method="return",g.arg=void 0,N(A,g),"throw"===g.method)||"return"!==I&&(g.method="throw",g.arg=new TypeError("The iterator does not provide a '"+I+"' method")),a;var Q=s(B,A.iterator,g.arg);if("throw"===Q.type)return g.method="throw",g.arg=Q.arg,g.delegate=null,a;var C=Q.arg;return C?C.done?(g[A.resultName]=C.value,g.next=A.nextLoc,"return"!==g.method&&(g.method="next",g.arg=void 0),g.delegate=null,a):C:(g.method="throw",g.arg=new TypeError("iterator result is not an object"),g.delegate=null,a)}function M(A){var g={tryLoc:A[0]};1 in A&&(g.catchLoc=A[1]),2 in A&&(g.finallyLoc=A[2],g.afterLoc=A[3]),this.tryEntries.push(g)}function F(A){var g=A.completion||{};g.type="normal",delete g.arg,A.completion=g}function d(A){this.tryEntries=[{tryLoc:"root"}],A.forEach(M,this),this.reset(!0)}function p(A){if(A){var g=A[e];if(g)return g.call(A);if("function"==typeof A.next)return A;if(!isNaN(A.length)){var I=-1,B=function g(){for(;++I =0;--B){var Q=this.tryEntries[B],E=Q.completion;if("root"===Q.tryLoc)return I("end");if(Q.tryLoc<=this.prev){var t=C.call(Q,"catchLoc"),e=C.call(Q,"finallyLoc");if(t&&e){if(this.prev =0;--I){var B=this.tryEntries[I];if(B.tryLoc<=this.prev&&C.call(B,"finallyLoc")&&this.prev =0;--g){var I=this.tryEntries[g];if(I.finallyLoc===A)return this.complete(I.completion,I.afterLoc),F(I),a}},catch:function(A){for(var g=this.tryEntries.length-1;g>=0;--g){var I=this.tryEntries[g];if(I.tryLoc===A){var B=I.completion;if("throw"===B.type){var Q=B.arg;F(I)}return Q}}throw new Error("illegal catch attempt")},delegateYield:function(A,g,I){return this.delegate={iterator:p(A),resultName:g,nextLoc:I},"next"===this.method&&(this.arg=void 0),a}},g}A.exports=Q,A.exports.__esModule=!0,A.exports.default=A.exports},8698:A=>{function g(I){return A.exports=g="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&"function"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?"symbol":typeof A},A.exports.__esModule=!0,A.exports.default=A.exports,g(I)}A.exports=g,A.exports.__esModule=!0,A.exports.default=A.exports},4687:(A,g,I)=>{var B=I(7061)();A.exports=B;try{regeneratorRuntime=B}catch(Q){"object"==typeof globalThis?globalThis.regeneratorRuntime=B:Function("r","regeneratorRuntime = r")(B)}}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/1599.f225e795.js.LICENSE.txt b/pr-preview/pr-346/assets/js/1599.f225e795.js.LICENSE.txt new file mode 100644 index 00000000..ae386fb7 --- /dev/null +++ b/pr-preview/pr-346/assets/js/1599.f225e795.js.LICENSE.txt @@ -0,0 +1 @@ +/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ diff --git a/pr-preview/pr-346/assets/js/17896441.0ad10f0d.js b/pr-preview/pr-346/assets/js/17896441.0ad10f0d.js new file mode 100644 index 00000000..d04349bf --- /dev/null +++ b/pr-preview/pr-346/assets/js/17896441.0ad10f0d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[7918],{2452:(e,t,l)=>{l.d(t,{Z:()=>a});var n=l(7294);const r={color:"#333333",fontStyle:"italic"},a=e=>{let{annotation:t,children:l}=e;return n.createElement("div",null,n.createElement("span",null,n.createElement("code",null,l)),n.createElement("br",null),n.createElement("br",null),n.createElement("small",{style:r},t))}},1789:(e,t,l)=>{l.d(t,{Z:()=>c});var n=l(7294),r=l(1262),a=l(412);const c=e=>{let{src:t,style:c,...s}=e;return n.createElement(r.default,null,(()=>{if(!a.Z.canUseDOM)return n.createElement("div",null,"ASCII Cinema Player Unavailable");const e=l(4828),r=(0,n.useRef)(null);return(0,n.useEffect)((()=>{const l=r.current;e.create(t,l,s)}),[t]),n.createElement("div",{ref:r,style:c})}))}},5626:(e,t,l)=>{l.d(t,{Z:()=>o});var n,r=l(7294);!function(e){e.default="block",e.block="block",e.line="line"}(n||(n={}));const a={color:"#FFFFFF",background:"#333333"},c={boxShadow:"inset 1px 0px #000000",background:"#FFFFFF11"},s=e=>{switch(e){case n.block:return a;case n.line:return c;default:return a}},o=e=>{let{caretStyle:t=n.block,children:l}=e;return r.createElement("span",{style:s(t)},l)}},1769:(e,t,l)=>{l.d(t,{Z:()=>c});l(7294);var n=l(9191),r=l(2452),a=l(5626);const c={...n.Z,AnnotatedCommand:r.Z,Caret:a.Z}},6922:(e,t,l)=>{l.d(t,{Z:()=>u});var n=l(7294),r=l(8985),a=l(2452),c=l(1789),s=l(5626);const o=e=>n.createElement(n.Fragment,null,n.createElement(r.Z,e));o.AnnotatedCommand=a.Z,o.AsciinemaPlayer=c.Z,o.Caret=s.Z;const u=o}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/1a4e3797.2586d31e.js b/pr-preview/pr-346/assets/js/1a4e3797.2586d31e.js new file mode 100644 index 00000000..0463af9a --- /dev/null +++ b/pr-preview/pr-346/assets/js/1a4e3797.2586d31e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[7920],{8374:(e,t,a)=>{a.r(t),a.d(t,{default:()=>k});var n=a(7294),c=a(2263),r=a(8765),l=a(5742),s=a(9960),u=a(6775),o=a(412);const m=function(){const e=(0,u.k6)(),t=(0,u.TH)(),{siteConfig:{baseUrl:a}}=(0,c.Z)();return{searchValue:o.Z.canUseDOM&&new URLSearchParams(t.search).get("q")||"",updateSearchPath:a=>{const n=new URLSearchParams(t.search);a?n.set("q",a):n.delete("q"),e.replace({search:n.toString()})},generateSearchPageLink:e=>`${a}search?q=${encodeURIComponent(e)}`}};var h=a(22),i=a(8202),p=a(2539),f=a(726),d=a(1073),_=a(311),E=a(1029);function g(e,t){return e.replace(/\{\{\s*(\w+)\s*\}\}/g,((e,a)=>Object.prototype.hasOwnProperty.call(t,a)?String(t[a]):e))}const I="searchQueryInput_CFBF",S="searchResultItem_U687",y="searchResultItemPath_uIbk",w="searchResultItemSummary_oZHr";function b(e){let{searchResult:{document:t,type:a,page:c,tokens:r,metadata:l}}=e;const u=0===a,o=2===a,m=(u?t.b:c.b).slice(),h=o?t.s:t.t;return u||m.push(c.t),n.createElement("article",{className:S},n.createElement("h2",null,n.createElement(s.Z,{to:t.u+(t.h||""),dangerouslySetInnerHTML:{__html:o?(0,p.C)(h,r):(0,f.o)(h,(0,d.m)(l,"t"),r,100)}})),m.length>0&&n.createElement("p",{className:y},m.join(" \u203a ")),o&&n.createElement("p",{className:w,dangerouslySetInnerHTML:{__html:(0,f.o)(t.t,(0,d.m)(l,"t"),r,100)}}))}const k=function(){const{siteConfig:{baseUrl:e}}=(0,c.Z)(),{searchValue:t,updateSearchPath:a}=m(),[s,u]=(0,n.useState)(t),[o,p]=(0,n.useState)(),[f,d]=(0,n.useState)(),S=(0,n.useMemo)((()=>s?g(E.Iz.search_results_for,{keyword:s}):E.Iz.search_the_documentation),[s]);(0,n.useEffect)((()=>{a(s),o&&(s?o(s,(e=>{d(e)})):d(void 0))}),[s,o]);const y=(0,n.useCallback)((e=>{u(e.target.value)}),[]);return(0,n.useEffect)((()=>{t&&t!==s&&u(t)}),[t]),(0,n.useEffect)((()=>{!async function(){const{wrappedIndexes:t,zhDictionary:a}=await(0,h.w)(e);p((()=>(0,i.v)(t,a,100)))}()}),[e]),n.createElement(r.Z,{title:S},n.createElement(l.Z,null,n.createElement("meta",{property:"robots",content:"noindex, follow"})),n.createElement("div",{className:"container margin-vert--lg"},n.createElement("h1",null,S),n.createElement("input",{type:"search",name:"q",className:I,"aria-label":"Search",onChange:y,value:s,autoComplete:"off",autoFocus:!0}),!o&&s&&n.createElement("div",null,n.createElement(_.Z,null)),f&&(f.length>0?n.createElement("p",null,g(1===f.length?E.Iz.count_documents_found:E.Iz.count_documents_found_plural,{count:f.length})):n.createElement("p",null,E.Iz.no_documents_were_found)),n.createElement("section",null,f&&f.map((e=>n.createElement(b,{key:e.document.i,searchResult:e}))))))}}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/1be78505.c7e8024d.js b/pr-preview/pr-346/assets/js/1be78505.c7e8024d.js new file mode 100644 index 00000000..f011604a --- /dev/null +++ b/pr-preview/pr-346/assets/js/1be78505.c7e8024d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[9514,4972],{9963:(e,t,n)=>{n.r(t),n.d(t,{default:()=>Ie});var a=n(7294),l=n(6010),o=n(1944),r=n(5281),c=n(3320),i=n(2802),s=n(4477),d=n(1116),m=n(8765),u=n(5999),b=n(2466),p=n(5936);const h="backToTopButton_sjWU",E="backToTopButtonShow_xfvO";function f(){const{shown:e,scrollToTop:t}=function(e){let{threshold:t}=e;const[n,l]=(0,a.useState)(!1),o=(0,a.useRef)(!1),{startScroll:r,cancelScroll:c}=(0,b.Ct)();return(0,b.RF)(((e,n)=>{let{scrollY:a}=e;const r=n?.scrollY;r&&(o.current?o.current=!1:a>=r?(c(),l(!1)):a {e.location.hash&&(o.current=!0,l(!1))})),{shown:n,scrollToTop:()=>r(0)}}({threshold:300});return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.BackToTopButton.buttonAriaLabel",message:"Scroll back to top",description:"The ARIA label for the back to top button"}),className:(0,l.Z)("clean-btn",r.k.common.backToTopButton,h,e&&E),type:"button",onClick:t})}var v=n(6775),g=n(7524),_=n(6668),k=n(1327),C=n(7462);function I(e){return a.createElement("svg",(0,C.Z)({width:"20",height:"20","aria-hidden":"true"},e),a.createElement("g",{fill:"#7a7a7a"},a.createElement("path",{d:"M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"}),a.createElement("path",{d:"M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"})))}const N="collapseSidebarButton_PEFL",S="collapseSidebarButtonIcon_kv0_";function Z(e){let{onClick:t}=e;return a.createElement("button",{type:"button",title:(0,u.I)({id:"theme.docs.sidebar.collapseButtonTitle",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.collapseButtonAriaLabel",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),className:(0,l.Z)("button button--secondary button--outline",N),onClick:t},a.createElement(I,{className:S}))}var x=n(9689),y=n(902);const T=Symbol("EmptyContext"),w=a.createContext(T);function L(e){let{children:t}=e;const[n,l]=(0,a.useState)(null),o=(0,a.useMemo)((()=>({expandedItem:n,setExpandedItem:l})),[n]);return a.createElement(w.Provider,{value:o},t)}var M=n(6043),A=n(8596),B=n(9960),F=n(2389);function P(e){let{categoryLabel:t,onClick:n}=e;return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel",message:"Toggle the collapsible sidebar category '{label}'",description:"The ARIA label to toggle the collapsible sidebar category"},{label:t}),type:"button",className:"clean-btn menu__caret",onClick:n})}function H(e){let{item:t,onItemClick:n,activePath:o,level:c,index:s,...d}=e;const{items:m,label:u,collapsible:b,className:p,href:h}=t,{docs:{sidebar:{autoCollapseCategories:E}}}=(0,_.L)(),f=function(e){const t=(0,F.Z)();return(0,a.useMemo)((()=>e.href?e.href:!t&&e.collapsible?(0,i.Wl)(e):void 0),[e,t])}(t),v=(0,i._F)(t,o),g=(0,A.Mg)(h,o),{collapsed:k,setCollapsed:I}=(0,M.u)({initialState:()=>!!b&&(!v&&t.collapsed)}),{expandedItem:N,setExpandedItem:S}=function(){const e=(0,a.useContext)(w);if(e===T)throw new y.i6("DocSidebarItemsExpandedStateProvider");return e}(),Z=function(e){void 0===e&&(e=!k),S(e?null:s),I(e)};return function(e){let{isActive:t,collapsed:n,updateCollapsed:l}=e;const o=(0,y.D9)(t);(0,a.useEffect)((()=>{t&&!o&&n&&l(!1)}),[t,o,n,l])}({isActive:v,collapsed:k,updateCollapsed:Z}),(0,a.useEffect)((()=>{b&&null!=N&&N!==s&&E&&I(!0)}),[b,N,s,I,E]),a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemCategory,r.k.docs.docSidebarItemCategoryLevel(c),"menu__list-item",{"menu__list-item--collapsed":k},p)},a.createElement("div",{className:(0,l.Z)("menu__list-item-collapsible",{"menu__list-item-collapsible--active":g})},a.createElement(B.Z,(0,C.Z)({className:(0,l.Z)("menu__link",{"menu__link--sublist":b,"menu__link--sublist-caret":!h&&b,"menu__link--active":v}),onClick:b?e=>{n?.(t),h?Z(!1):(e.preventDefault(),Z())}:()=>{n?.(t)},"aria-current":g?"page":void 0,"aria-expanded":b?!k:void 0,href:b?f??"#":f},d),u),h&&b&&a.createElement(P,{categoryLabel:u,onClick:e=>{e.preventDefault(),Z()}})),a.createElement(M.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:k},a.createElement(q,{items:m,tabIndex:k?-1:0,onItemClick:n,activePath:o,level:c+1})))}var W=n(3919),D=n(9471);const R="menuExternalLink_NmtK";function z(e){let{item:t,onItemClick:n,activePath:o,level:c,index:s,...d}=e;const{href:m,label:u,className:b,autoAddBaseUrl:p}=t,h=(0,i._F)(t,o),E=(0,W.Z)(m);return a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemLink,r.k.docs.docSidebarItemLinkLevel(c),"menu__list-item",b),key:u},a.createElement(B.Z,(0,C.Z)({className:(0,l.Z)("menu__link",!E&&R,{"menu__link--active":h}),autoAddBaseUrl:p,"aria-current":h?"page":void 0,to:m},E&&{onClick:n?()=>n(t):void 0},d),u,!E&&a.createElement(D.Z,null)))}const U="menuHtmlItem_M9Kj";function V(e){let{item:t,level:n,index:o}=e;const{value:c,defaultStyle:i,className:s}=t;return a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemLink,r.k.docs.docSidebarItemLinkLevel(n),i&&[U,"menu__list-item"],s),key:o,dangerouslySetInnerHTML:{__html:c}})}function K(e){let{item:t,...n}=e;switch(t.type){case"category":return a.createElement(H,(0,C.Z)({item:t},n));case"html":return a.createElement(V,(0,C.Z)({item:t},n));default:return a.createElement(z,(0,C.Z)({item:t},n))}}function j(e){let{items:t,...n}=e;return a.createElement(L,null,t.map(((e,t)=>a.createElement(K,(0,C.Z)({key:t,item:e,index:t},n)))))}const q=(0,a.memo)(j),G="menu_SIkG",Y="menuWithAnnouncementBar_GW3s";function O(e){let{path:t,sidebar:n,className:o}=e;const c=function(){const{isActive:e}=(0,x.nT)(),[t,n]=(0,a.useState)(e);return(0,b.RF)((t=>{let{scrollY:a}=t;e&&n(0===a)}),[e]),e&&t}();return a.createElement("nav",{className:(0,l.Z)("menu thin-scrollbar",G,c&&Y,o)},a.createElement("ul",{className:(0,l.Z)(r.k.docs.docSidebarMenu,"menu__list")},a.createElement(q,{items:n,activePath:t,level:1})))}const X="sidebar_njMd",J="sidebarWithHideableNavbar_wUlq",Q="sidebarHidden_VK0M",$="sidebarLogo_isFc";function ee(e){let{path:t,sidebar:n,onCollapse:o,isHidden:r}=e;const{navbar:{hideOnScroll:c},docs:{sidebar:{hideable:i}}}=(0,_.L)();return a.createElement("div",{className:(0,l.Z)(X,c&&J,r&&Q)},c&&a.createElement(k.Z,{tabIndex:-1,className:$}),a.createElement(O,{path:t,sidebar:n}),i&&a.createElement(Z,{onClick:o}))}const te=a.memo(ee);var ne=n(3102),ae=n(2961);const le=e=>{let{sidebar:t,path:n}=e;const o=(0,ae.e)();return a.createElement("ul",{className:(0,l.Z)(r.k.docs.docSidebarMenu,"menu__list")},a.createElement(q,{items:t,activePath:n,onItemClick:e=>{"category"===e.type&&e.href&&o.toggle(),"link"===e.type&&o.toggle()},level:1}))};function oe(e){return a.createElement(ne.Zo,{component:le,props:e})}const re=a.memo(oe);function ce(e){const t=(0,g.i)(),n="desktop"===t||"ssr"===t,l="mobile"===t;return a.createElement(a.Fragment,null,n&&a.createElement(te,e),l&&a.createElement(re,e))}const ie="expandButton_m80_",se="expandButtonIcon_BlDH";function de(e){let{toggleSidebar:t}=e;return a.createElement("div",{className:ie,title:(0,u.I)({id:"theme.docs.sidebar.expandButtonTitle",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.expandButtonAriaLabel",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),tabIndex:0,role:"button",onKeyDown:t,onClick:t},a.createElement(I,{className:se}))}const me="docSidebarContainer_b6E3",ue="docSidebarContainerHidden_b3ry";function be(e){let{children:t}=e;const n=(0,d.V)();return a.createElement(a.Fragment,{key:n?.name??"noSidebar"},t)}function pe(e){let{sidebar:t,hiddenSidebarContainer:n,setHiddenSidebarContainer:o}=e;const{pathname:c}=(0,v.TH)(),[i,s]=(0,a.useState)(!1),d=(0,a.useCallback)((()=>{i&&s(!1),o((e=>!e))}),[o,i]);return a.createElement("aside",{className:(0,l.Z)(r.k.docs.docSidebarContainer,me,n&&ue),onTransitionEnd:e=>{e.currentTarget.classList.contains(me)&&n&&s(!0)}},a.createElement(be,null,a.createElement(ce,{sidebar:t,path:c,onCollapse:d,isHidden:i})),i&&a.createElement(de,{toggleSidebar:d}))}const he={docMainContainer:"docMainContainer_gTbr",docMainContainerEnhanced:"docMainContainerEnhanced_Uz_u",docItemWrapperEnhanced:"docItemWrapperEnhanced_czyv"};function Ee(e){let{hiddenSidebarContainer:t,children:n}=e;const o=(0,d.V)();return a.createElement("main",{className:(0,l.Z)(he.docMainContainer,(t||!o)&&he.docMainContainerEnhanced)},a.createElement("div",{className:(0,l.Z)("container padding-top--md padding-bottom--lg",he.docItemWrapper,t&&he.docItemWrapperEnhanced)},n))}const fe="docPage__5DB",ve="docsWrapper_BCFX";function ge(e){let{children:t}=e;const n=(0,d.V)(),[l,o]=(0,a.useState)(!1);return a.createElement(m.Z,{wrapperClassName:ve},a.createElement(f,null),a.createElement("div",{className:fe},n&&a.createElement(pe,{sidebar:n.items,hiddenSidebarContainer:l,setHiddenSidebarContainer:o}),a.createElement(Ee,{hiddenSidebarContainer:l},t)))}var _e=n(4972),ke=n(197);function Ce(e){const{versionMetadata:t}=e;return a.createElement(a.Fragment,null,a.createElement(ke.Z,{version:t.version,tag:(0,c.os)(t.pluginId,t.version)}),a.createElement(o.d,null,t.noIndex&&a.createElement("meta",{name:"robots",content:"noindex, nofollow"})))}function Ie(e){const{versionMetadata:t}=e,n=(0,i.hI)(e);if(!n)return a.createElement(_e.default,null);const{docElement:c,sidebarName:m,sidebarItems:u}=n;return a.createElement(a.Fragment,null,a.createElement(Ce,e),a.createElement(o.FG,{className:(0,l.Z)(r.k.wrapper.docsPages,r.k.page.docsDocPage,e.versionMetadata.className)},a.createElement(s.q,{version:t},a.createElement(d.b,{name:m,items:u},a.createElement(ge,null,c)))))}},4972:(e,t,n)=>{n.r(t),n.d(t,{default:()=>c});var a=n(7294),l=n(5999),o=n(1944),r=n(8765);function c(){return a.createElement(a.Fragment,null,a.createElement(o.d,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),a.createElement(r.Z,null,a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}},4477:(e,t,n)=>{n.d(t,{E:()=>c,q:()=>r});var a=n(7294),l=n(902);const o=a.createContext(null);function r(e){let{children:t,version:n}=e;return a.createElement(o.Provider,{value:n},t)}function c(){const e=(0,a.useContext)(o);if(null===e)throw new l.i6("DocsVersionProvider");return e}}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/1eb89aaf.6a3d6b1d.js b/pr-preview/pr-346/assets/js/1eb89aaf.6a3d6b1d.js new file mode 100644 index 00000000..2f016341 --- /dev/null +++ b/pr-preview/pr-346/assets/js/1eb89aaf.6a3d6b1d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[692],{3769:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-docs","id":"default"}')}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/2312db46.82e40856.js b/pr-preview/pr-346/assets/js/2312db46.82e40856.js new file mode 100644 index 00000000..ccf570c7 --- /dev/null +++ b/pr-preview/pr-346/assets/js/2312db46.82e40856.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[141],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t =0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var r=a.createContext({}),h=function(e){var t=a.useContext(r),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=h(e.components);return a.createElement(r.Provider,{value:t},e.children)},u="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,r=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=h(n),d=o,m=u["".concat(r,".").concat(d)]||u[d]||c[d]||i;return n?a.createElement(m,s(s({ref:t},p),{},{components:n})):a.createElement(m,s({ref:t},p))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,s=new Array(i);s[0]=d;var l={};for(var r in t)hasOwnProperty.call(t,r)&&(l[r]=t[r]);l.originalType=e,l[u]="string"==typeof e?e:o,s[1]=l;for(var h=2;h{n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>s,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>h});var a=n(7462),o=(n(7294),n(3905));const i={title:"Getting Started",slug:"/part-1-transitioning-to-the-shell/getting-started/"},s=void 0,l={unversionedId:"transitioning-to-the-shell/getting-started/index",id:"transitioning-to-the-shell/getting-started/index",title:"Getting Started",description:"This section is for those who are completely new to this topic. In this section we'll introduce just what the shell is, who this book is useful for, and what you can expect to learn.",source:"@site/docs/01-transitioning-to-the-shell/01-getting-started/index.md",sourceDirName:"01-transitioning-to-the-shell/01-getting-started",slug:"/part-1-transitioning-to-the-shell/getting-started/",permalink:"/part-1-transitioning-to-the-shell/getting-started/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/01-transitioning-to-the-shell/01-getting-started/index.md",tags:[],version:"current",frontMatter:{title:"Getting Started",slug:"/part-1-transitioning-to-the-shell/getting-started/"},sidebar:"sidebar",previous:{title:"Part 1 - Transitioning to the Shell",permalink:"/part-1-transitioning-to-the-shell/"},next:{title:"Navigating Your System",permalink:"/part-1-transitioning-to-the-shell/navigating-your-system/"}},r={},h=[{value:"What is the Shell?",id:"what-is-the-shell",level:2},{value:"Opening the Shell",id:"opening-the-shell",level:2},{value:"Microsoft Windows",id:"microsoft-windows",level:3},{value:"MacOS",id:"macos",level:3},{value:"Linux / Unix",id:"linux--unix",level:3},{value:"Configuring the Shell",id:"configuring-the-shell",level:2},{value:"Microsoft Windows",id:"microsoft-windows-1",level:3},{value:"Option 1: Install Linux Tools",id:"option-1-install-linux-tools",level:4},{value:"Option 2: Use a Virtual Machine",id:"option-2-use-a-virtual-machine",level:4},{value:"Option 3: Setup the Windows Subsystem for Linux",id:"option-3-setup-the-windows-subsystem-for-linux",level:4},{value:"MacOS",id:"macos-1",level:3},{value:"Linux",id:"linux",level:3},{value:"That's It!",id:"thats-it",level:3},{value:"A Quick Demo of the Shell",id:"a-quick-demo-of-the-shell",level:2},{value:"The Echo Command",id:"the-echo-command",level:3},{value:"Move Around",id:"move-around",level:3},{value:"Summary",id:"summary",level:2}],p={toc:h};function u(e){let{components:t,...i}=e;return(0,o.kt)("wrapper",(0,a.Z)({},p,i,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"This section is for those who are completely new to this topic. In this section we'll introduce just what the shell is, who this book is useful for, and what you can expect to learn."),(0,o.kt)("p",null,"We'll also look at how to set your computer up so that you can follow along with the examples. We'll finish by demonstrating a few basic skills so that you can learn to move around in the shell and get started with the rest of the book."),(0,o.kt)("p",null,"If you are already comfortable with running a shell, know what ",(0,o.kt)("inlineCode",{parentName:"p"},"bash")," is, and know how to run basic commands like ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"cd"),", are familiar with terms like ",(0,o.kt)("em",{parentName:"p"},"command")," and ",(0,o.kt)("em",{parentName:"p"},"parameter")," then you can skip this section. You could also just review the ",(0,o.kt)("a",{parentName:"p",href:"#summary"},"Summary")," to make sure that you are comfortable with the material which has been introduced and then move onto the next section."),(0,o.kt)("h2",{id:"what-is-the-shell"},"What is the Shell?"),(0,o.kt)("p",null,"If you don't know what the shell is, then this is the place to start!"),(0,o.kt)("p",null,'When we talk about "The Shell", we\'re normally referring to the simple, text-based interface which is used to control a computer or a program.'),(0,o.kt)("p",null,"Here's what the shell looks like on Windows:"),(0,o.kt)("img",{src:n(8307).Z,width:"800px"}),(0,o.kt)("p",null,"And here's what it looks like on a Mac:"),(0,o.kt)("img",{src:n(8805).Z,width:"800px"}),(0,o.kt)("p",null,"And here's what it looks like on Fedora, a popular Linux distribution:"),(0,o.kt)("img",{src:n(3790).Z,width:"800px"}),(0,o.kt)("p",null,"When we are talking about the shell in this book, we're talking about the simple program which can be used to operate the computer using this text based interface."),(0,o.kt)("p",null,"Why would you want to do this? There are a few reasons!"),(0,o.kt)("p",null,"Firstly, using the shell can help you learn more about the internals of how your computer can work. This can be really helpful if you are a technology professional or work with computers."),(0,o.kt)("p",null,"Secondly, there are some scenarios where you ",(0,o.kt)("em",{parentName:"p"},"have")," to use a shell. Not every program or system can be operated with a ",(0,o.kt)("em",{parentName:"p"},"Graphical User Interface"),", which is the visual point-and-click interface you are probably using now. Some lower-level programs do not have such interfaces, and many computers do not either."),(0,o.kt)("p",null,"Finally, there are some scenarios where it can be ",(0,o.kt)("em",{parentName:"p"},"more efficient")," to use the shell. Operations which might be time consuming or repetitive to perform using the user interface might be much faster to perform in a shell. You can also write ",(0,o.kt)("em",{parentName:"p"},"shell scripts")," to automate these kinds of operations."),(0,o.kt)("p",null,"In the next section you'll learn how to startup the shell on your computer. Once this is done you are ready to continue with the book."),(0,o.kt)("h2",{id:"opening-the-shell"},"Opening the Shell"),(0,o.kt)("p",null,"Now let's actually learn how to open the shell on your computer."),(0,o.kt)("p",null,"Once we've done this, we might need to make some configuration changes so that we get it to behave in a way which as consistent with ",(0,o.kt)("em",{parentName:"p"},"other")," shells as possible - we'll get to that in the next chapter."),(0,o.kt)("h3",{id:"microsoft-windows"},"Microsoft Windows"),(0,o.kt)("p",null,'There are a number of shell programs on Microsoft Windows. We\'ll be using the basic shell which is pre-installed, which is called the "Command Prompt".'),(0,o.kt)("p",null,"To open the command prompt, start by clicking the start button on the bottom left hand side of the screen, and type ",(0,o.kt)("inlineCode",{parentName:"p"},"command prompt"),". Open the Command Prompt program:"),(0,o.kt)("img",{alt:"Screenshot: Search for Command Prompt",src:n(6544).Z,width:"800px"}),(0,o.kt)("p",null,"Once the program has opened, type ",(0,o.kt)("inlineCode",{parentName:"p"},"whoami")," then hit the Return key. The ",(0,o.kt)("inlineCode",{parentName:"p"},"whoami")," program will show the username of the logged in user:"),(0,o.kt)("img",{alt:"Screenshot: whoami on Windows",src:n(8262).Z,width:"800px"}),(0,o.kt)("p",null,"That's it! We've still got some configuration to do to make this shell behave more like a Linux shell, which this book uses as the standard, but we'll come to that in the next section."),(0,o.kt)("h3",{id:"macos"},"MacOS"),(0,o.kt)("p",null,'If you are using a Mac, then you just need to run the "Terminal" program to open your shell. Hold down the Command Key and press Space, then type ',(0,o.kt)("inlineCode",{parentName:"p"},"terminal"),". Open the terminal program which is shown:"),(0,o.kt)("img",{alt:"Screenshot: Search for Terminal",src:n(7781).Z,width:"800px"}),(0,o.kt)("p",null,"Once the program has opened, type ",(0,o.kt)("inlineCode",{parentName:"p"},"whoami")," then hit the Return key. The ",(0,o.kt)("inlineCode",{parentName:"p"},"whoami")," program will show the username of the logged in user:"),(0,o.kt)("img",{alt:"Screenshot: whoami on OSX",src:n(4889).Z,width:"800px"}),(0,o.kt)("p",null,"That's it! In the next section we'll make some minor configuration changes to keep things consistent with the samples in the book."),(0,o.kt)("h3",{id:"linux--unix"},"Linux / Unix"),(0,o.kt)("p",null,"If you are using a Linux or Unix system, I'll assume that you are familiar enough with it to open a shell. Which terminal you use should not affect how you use this book, but for consistencies sake be aware that most of the examples are assuming that the user is using Bash version 5."),(0,o.kt)("h2",{id:"configuring-the-shell"},"Configuring the Shell"),(0,o.kt)("p",null,'Shells can vary enormously between different systems. In general, Linux systems tend to use the "Bash" shell and require little configuration. Apple\'s MacOS operating system is actually based on BSD Unix, and under the hood is somewhat different to most Linux systems. Microsoft Windows is a completely unrelated operating system to either Linux or Unix and operates in a fundamentally different way from both of them.'),(0,o.kt)("p",null,'In this book, we assume that you are using a "Linux-like" system, something which operates like a modern Linux distribution. This is a deliberate choice. If you become comfortable using a Linux-like shell, you can generally apply the techniques we\'ll show to MacOS with no difficulties. For Windows, the techniques are not necessarily transferable immediately, but still valuable to know. Windows is actually being updated at the time of writing to provide a Linux-like shell interface as part of the core operating system (this is known as the ',(0,o.kt)("a",{parentName:"p",href:"https://docs.microsoft.com/en-us/windows/wsl"},"Windows Subsystem for Linux"),". As time progresses it will be easier to run commands using the techniques in this book natively, but for now we'll have to tweak a few things."),(0,o.kt)("p",null,'In this section we\'ll make sure that we are running with a setup which is close to Linux, and aim to set the latest version of our shell to the popular "Bash" program. If you are familiar with Bash but prefer to use another shell, that is fine, most of the book will work with any modern shell. However, if you are not sure what shell you should be using, I would recommend you follow the guides below to setup the most popular shell at its latest version.'),(0,o.kt)("p",null,"Once this is done then we are ready to get into the book properly!"),(0,o.kt)("h3",{id:"microsoft-windows-1"},"Microsoft Windows"),(0,o.kt)("p",null,"Windows is not anything like Linux under the hood. So to get a shell working, we have three options:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"Use a tool which provides common Linux tools which have been written to work with Windows"),(0,o.kt)("li",{parentName:"ol"},'Use a "virtual machine" running Linux'),(0,o.kt)("li",{parentName:"ol"},"Use the Windows Subsystem for Linux")),(0,o.kt)("p",null,"The first option is the best if you want to actually be able to work with the files on your computer quickly and easily day to day."),(0,o.kt)("p",null,"The second option is best if you want to be able to experiment with the shell, but keep it completely separate from your main computer and its files."),(0,o.kt)("p",null,"The final option is best if you are a power user or expert who wants to use the latest WSL features and build the skills with the platform as soon as possible."),(0,o.kt)("p",null,"We'll go through all options here."),(0,o.kt)("h4",{id:"option-1-install-linux-tools"},"Option 1: Install Linux Tools"),(0,o.kt)("p",null,"This is probably the easiest option and the one I would recommend for most user. It will let you run something like a Linux shell when you choose to, but not get in your way during day-to-day usage of your computer."),(0,o.kt)("p",null,"To get a Linux-like experience on a Windows machine, we'll install ",(0,o.kt)("a",{parentName:"p",href:"https://www.cygwin.com/"},"Cygwin"),". Cygwin provides a large set of programs which are generally available on Linux systems, which are designed to work on Windows."),(0,o.kt)("p",null,"Download the Cygwin installer and start the installation process. You should see something like this:"),(0,o.kt)("img",{alt:"Screenshot: Cygwin Setup",src:n(223).Z,width:"400px"}),(0,o.kt)("p",null,"Start the installation and tell it to install from the internet (the default option):"),(0,o.kt)("img",{alt:"Screenshot: Cygwin Setup",src:n(7973).Z,width:"400px"}),(0,o.kt)("p",null,"Install for all users in the default location. It is also fine to change the options if you prefer:"),(0,o.kt)("img",{alt:"Screenshot: Cygwin Setup",src:n(1018).Z,width:"400px"}),(0,o.kt)("p",null,'Cygwin will ask you where to install downloaded packages, whether a proxy is needed, and what download sites to use. Leave these options at their default unless you know what you are doing and why you\'d need to change them. It will then start downloading. Once it has downloaded the list of available packages to install, it will ask which packages you want. Choose the default option "All":'),(0,o.kt)("img",{alt:"Screenshot: Cygwin Setup",src:n(9258).Z,width:"400px"}),(0,o.kt)("p",null,"The installer will now start downloading and installing the programs:"),(0,o.kt)("img",{alt:"Screenshot: Cygwin Setup",src:n(9167).Z,width:"400px"}),(0,o.kt)("p",null,"Once Cygwin has finished installing, you will have a link to open Cygwin available on the desktop and start menu."),(0,o.kt)("p",null,'You can use this link to start using the "Bash" shell, or if you prefer you can open the "Command Prompt" as described in ',(0,o.kt)("a",{parentName:"p",href:"#Opening-The-Shell"},"Opening the Shell")," and run the ",(0,o.kt)("inlineCode",{parentName:"p"},"bash")," program:"),(0,o.kt)("img",{alt:"Screenshot: Cygwin Setup",src:n(2868).Z,width:"400px"}),(0,o.kt)("p",null,"Note that you shouldn't use the ",(0,o.kt)("inlineCode",{parentName:"p"},"--norc")," option. I have used it in the screenshot above just so that my Bash looks like it would after a clean install, without my own customisations added."),(0,o.kt)("p",null,"At this point you have a ready-to-go bash environment and can continue on to the ",(0,o.kt)("a",{parentName:"p",href:"#Summary"},"Summary")," and ",(0,o.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/navigating-your-system/"},"Next Section"),"."),(0,o.kt)("h4",{id:"option-2-use-a-virtual-machine"},"Option 2: Use a Virtual Machine"),(0,o.kt)("p",null,"We can run a virtual machine on Windows which will give us a complete Linux environment. This is an ideal way to create a safe sandbox for experimentation, without changing how the rest of the system is setup."),(0,o.kt)("p",null,"There are many ways to run a virtual machine on Windows. For this example we'll use the free 'Oracle VirtualBox' tool. VirtualBox will run a virtual machine, and on that virtual machine we will install the popular ",(0,o.kt)("a",{parentName:"p",href:"https://ubuntu.com/"},"Ubuntu")," distribution of Linux."),(0,o.kt)("p",null,"First, start downloading Ubuntu, which might take some time as the download is quite large. You will want to install the latest Desktop Edition (which at the time of writing is version 18):"),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Download",src:n(5851).Z,width:"400px"}),(0,o.kt)("p",null,"While the Ubuntu software downloads, we can install VirtualBox. Go to the ",(0,o.kt)("a",{parentName:"p",href:"https://www.virtualbox.org"},"VirtualBox Website")," and download the VirtualBox installer. You will need the installer for 'Windows Hosts'."),(0,o.kt)("p",null,"Once the installer has downloaded, run it to start the installation:"),(0,o.kt)("img",{alt:"Screenshot: VirtualBox Setup",src:n(4548).Z,width:"400px"}),(0,o.kt)("p",null,"Next you will be asked to configure the installation options. The defaults will be fine for most users:"),(0,o.kt)("img",{alt:"Screenshot: VirtualBox Setup",src:n(5709).Z,width:"400px"}),(0,o.kt)("img",{alt:"Screenshot: VirtualBox Setup",src:n(3476).Z,width:"400px"}),(0,o.kt)("p",null,"Then the installation will start:"),(0,o.kt)("img",{alt:"Screenshot: VirtualBox Setup",src:n(3057).Z,width:"400px"}),(0,o.kt)("p",null,"Once the installation is complete and the Ubuntu installer has downloaded we can move onto the next step."),(0,o.kt)("p",null,'Open VirtualBox and choose \'New\' to create a new Virtual Machine. Ensure "Expert Mode" is selected. Provide a name for the machine and choose "Linux" as the type and "Ubuntu_64" as the version type. Everything else can be left as the default, unless you want to tweak the machine settings:'),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(123).Z,width:"800px"}),(0,o.kt)("p",null,"You will be asked to setup a virtual hard disk. I would recommend the default options for most users:"),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(2567).Z,width:"800px"}),(0,o.kt)("p",null,'Once the machine has been created it will be shown in the main VirtualBox window. Select the machine and choose "Start":'),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(2569).Z,width:"800px"}),(0,o.kt)("p",null,'When the machine starts up it will ask you for a "Startup Disk". This is the disk which will be used to setup the operating system. Press the "browse" icon, then choose "open" and select the Ubuntu file which you downloaded, which should end in ',(0,o.kt)("inlineCode",{parentName:"p"},".iso"),":"),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(4833).Z,width:"800px"}),(0,o.kt)("p",null,'If this step fails, you may need to disable "Hyper-V" and "Windows Sandbox" by going to "Add or Remove Windows features":'),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(1089).Z,width:"400px"}),(0,o.kt)("p",null,'After a short while you will see the Ubuntu installer start up. Choose the "Install Ubuntu" option:'),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(9211).Z,width:"800px"}),(0,o.kt)("p",null,'You can specify language settings, what components are installed and more. These options can be left at the default. On the final page, choose the "Erase disk and install Ubuntu" option:'),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(4163).Z,width:"800px"}),(0,o.kt)("p",null,"The final step will be to choose a name for the computer, and a username and password to log in with. You can use any values you like here, just don't forget them!"),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(7560).Z,width:"800px"}),(0,o.kt)("p",null,'After this the installation will proceed. It might take a little while. After the installation is complete, you will need to restart. If you get an error saying "Please remove installation medium" just power off the machine and restart it. After restarting you can log into the machine with the credentials you specified earlier.'),(0,o.kt)("p",null,'When you have logged in, press the applications icon on the bottom-left of the screen and search for the "Terminal" application:'),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(2582).Z,width:"800px"}),(0,o.kt)("p",null,'You are now running the "Bash" shell in the terminal. You can run the ',(0,o.kt)("inlineCode",{parentName:"p"},"whoami")," command to show the current user, or ",(0,o.kt)("inlineCode",{parentName:"p"},"bash --version")," to see the version of Bash which is installed:"),(0,o.kt)("img",{alt:"Screenshot: Ubuntu Installation",src:n(1549).Z,width:"800px"}),(0,o.kt)("p",null,"That's it! You now have a virtual machine running Ubuntu and Bash which you can use to learn about the shell."),(0,o.kt)("h4",{id:"option-3-setup-the-windows-subsystem-for-linux"},"Option 3: Setup the Windows Subsystem for Linux"),(0,o.kt)("p",null,'The Windows Subsystem for Linux is a relatively new set of features for Microsoft Windows. It allows users to install a Linux distribution on their Windows machine. This is a great way for us to be able to use the "Bash" shell without having to set up a virtual machine.'),(0,o.kt)("p",null,'First, open up the "Turn Windows Features on or off" option from the control panel:'),(0,o.kt)("img",{alt:"Screenshot: Turn Windows Features on or off",src:n(3653).Z,width:"800px"}),(0,o.kt)("p",null,'Then enable the "Windows Subsystem for Linux" feature:'),(0,o.kt)("img",{alt:"Screenshot: Enable Windows Subsystem for Linux",src:n(8031).Z,width:"800px"}),(0,o.kt)("p",null,'After your computer has restarted, open up the Windows App Store and search for "Ubuntu":'),(0,o.kt)("img",{alt:"Screenshot: Ubuntu on App Store",src:n(8936).Z,width:"800px"}),(0,o.kt)("p",null,"Once Ubuntu has installed, open up the app. It will then initialise (which can take a little while):"),(0,o.kt)("img",{alt:"Screenshot: Initialise Ubuntu",src:n(6005).Z,width:"800px"}),(0,o.kt)("p",null,"Choose a username and password to complete the setup:"),(0,o.kt)("img",{alt:"Screenshot: Choose Username and Password",src:n(2867).Z,width:"800px"}),(0,o.kt)("p",null,"And that's it! You can now open the Ubuntu app at any time to use Ubuntu on Windows, interfacing using the Bash shell."),(0,o.kt)("h3",{id:"macos-1"},"MacOS"),(0,o.kt)("p",null,"If you are running a Mac, then you can probably run the standard Terminal program and follow the material in this book without making any changes. However, the version of ",(0,o.kt)("em",{parentName:"p"},"Bash")," which comes installed by default on MacOS is version 3, which is a little out of date. I would strongly suggest that you upgrade the default installation. On MacOS Catalina, the default shell has changed to ",(0,o.kt)("em",{parentName:"p"},"Z Shell")," - this should work fine for all of the examples in this book, but you might want to switch it to Bash to be on the safe side (you can always change back later)."),(0,o.kt)("p",null,"To install the right software, we'll use a tool called ",(0,o.kt)("em",{parentName:"p"},"Homebrew"),". Homebrew is a 'package manager', a tool used to install software on your computer, from the shell. It's kind of like the App Store but for shell users!"),(0,o.kt)("p",null,"First, follow the instructions online to install ",(0,o.kt)("a",{parentName:"p",href:"https://brew.sh/"},"Homebrew"),":"),(0,o.kt)("img",{alt:"Screenshot: OSX Installation",src:n(5157).Z,width:"800px"}),(0,o.kt)("p",null,"In most cases, this will require opening the Terminal program and running a snippet which looks like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"\n')),(0,o.kt)("p",null,"However, this might have changed since the time of writing so do ",(0,o.kt)("a",{parentName:"p",href:"https://brew.sh/"},"check the website")," to see what the latest instructions are. You don't actually need to know what is going on with this command (but by the time you've worked through a bit more of this book it will make sense!), but in a nutshell it runs a basic installation script, using the ",(0,o.kt)("em",{parentName:"p"},"Ruby")," programming language (which comes pre-installed on MacOS)."),(0,o.kt)("p",null,"Once this has installed, install Bash by running the following command in the shell:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"brew install bash\n")),(0,o.kt)("p",null,"This uses the ",(0,o.kt)("inlineCode",{parentName:"p"},"brew")," command, which we have just installed, to install the ",(0,o.kt)("inlineCode",{parentName:"p"},"bash")," program."),(0,o.kt)("p",null,"Finally, update the Terminal preferences to use the version of Bash you have just installed, rather than the default, by setting the shell location to ",(0,o.kt)("inlineCode",{parentName:"p"},"/usr/local/bin/bash"),":"),(0,o.kt)("img",{alt:"Screenshot: OSX Installation",src:n(9073).Z,width:"800px"}),(0,o.kt)("p",null,"Again, why we make these changes is not essential to know for now, we'll go into more details in a later section. Once you've made this change, whenever you open a new ",(0,o.kt)("inlineCode",{parentName:"p"},"terminal")," window, it will run the latest version of Bash, which you can confirm by running ",(0,o.kt)("inlineCode",{parentName:"p"},"echo $BASH_VERSION"),":"),(0,o.kt)("img",{alt:"Screenshot: OSX Installation",src:n(630).Z,width:"800px"}),(0,o.kt)("p",null,"There is actually a more sophisticated way to change what shell is used in a system, which is the special ",(0,o.kt)("inlineCode",{parentName:"p"},"chsh")," command (short for \"change shell\"). We'll see this in a later section. We'll also see what ",(0,o.kt)("inlineCode",{parentName:"p"},"echo")," is in more detail shortly."),(0,o.kt)("h3",{id:"linux"},"Linux"),(0,o.kt)("p",null,"As before, if you are running Linux I will assume you are able to open a terminal and setup the appropriate shell. You can follow along with the content in this book with any recent Bash-like shell."),(0,o.kt)("h3",{id:"thats-it"},"That's It!"),(0,o.kt)("p",null,"Later on we'll see a little more about the differences between different shell programs, what the difference between a shell and a terminal is and more. But for now, you are ready to go and move onto the ",(0,o.kt)("a",{parentName:"p",href:"#Summary"},"Summary"),"."),(0,o.kt)("h2",{id:"a-quick-demo-of-the-shell"},"A Quick Demo of the Shell"),(0,o.kt)("p",null,"If you have never used the shell before, then this is where we'll start. We're not going to go into lots of detail, there's plenty of that later on in book. Instead we'll do a quick crash course on the basics. If you have not used the shell before this'll give you a chance to see how it works."),(0,o.kt)("p",null,"Start by opening your shell. This is covered in ",(0,o.kt)("a",{parentName:"p",href:"#Opening-The-Shell"},"Opening the Shell"),". Your shell should be ",(0,o.kt)("em",{parentName:"p"},"Bash")," - if this doesn't sound familiar, then make sure you have followed the instructions in ",(0,o.kt)("a",{parentName:"p",href:"#Configuring-The-Shell"},"Configuring the Shell"),"."),(0,o.kt)("p",null,"You should see your terminal program running your shell. You can see what the ",(0,o.kt)("em",{parentName:"p"},"version")," is of your shell by running:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"bash --version\n")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Bash Version",src:n(8932).Z,width:"1980",height:"690"})),(0,o.kt)("p",null,"Let's quickly dissect this. We have run the ",(0,o.kt)("inlineCode",{parentName:"p"},"bash")," ",(0,o.kt)("em",{parentName:"p"},"command"),". A command can be a program on your computer, or it can be something built into the shell. We'll look at this in a lot more detail later, but for now it's important to understand that a lot of what you will be doing is running commands."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"--version")," text is a ",(0,o.kt)("em",{parentName:"p"},"parameter"),". Parameters affect how commands work. This is actually easier to see with an example."),(0,o.kt)("p",null,"Let's move to the ",(0,o.kt)("em",{parentName:"p"},"home")," folder. On most computers your home folder is your personal space where things like documents, photos, music, downloads and so on are kept."),(0,o.kt)("p",null,"Let's switch to the home folder by running the following command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd ~\n")),(0,o.kt)("p",null,"Once you've done that, run the ",(0,o.kt)("inlineCode",{parentName:"p"},"pwd")," command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"pwd\n")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Moving to the home directory",src:n(3865).Z,width:"1814",height:"700"})),(0,o.kt)("p",null,"So what has happened here? The first command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd ~\n")),(0,o.kt)("p",null,"Is used to ",(0,o.kt)("em",{parentName:"p"},"change directory")," - that's what ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," stands for. The ",(0,o.kt)("em",{parentName:"p"},"parameter")," we passed to ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," was just the 'tilde' character (",(0,o.kt)("inlineCode",{parentName:"p"},"~"),'). This character has a special meaning in the shell - it means "the current user\'s home directory".'),(0,o.kt)("p",null,"Finally, we ran the ",(0,o.kt)("inlineCode",{parentName:"p"},"pwd")," command. This command is short for ",(0,o.kt)("em",{parentName:"p"},"print working directory"),". It writes out to the screen ",(0,o.kt)("em",{parentName:"p"},"where")," you currently are. On my Mac, my home directory is located at ",(0,o.kt)("inlineCode",{parentName:"p"},"/Users/dwmkerr"),", which is what the command has shown me."),(0,o.kt)("p",null,"Let's take another look at a command. Run the following in your shell:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ls\n")),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," command is short for ",(0,o.kt)("em",{parentName:"p"},"list directory contents")," - it shows you everything that is in the current directory. On my computer you can see things like the 'Downloads', 'Music' and 'Pictures' folders, which are set up by default on a Mac, as well as some of my own folders."),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: List directory contents",src:n(5487).Z,width:"2980",height:"582"})),(0,o.kt)("p",null,"We can pass different parameters to ",(0,o.kt)("inlineCode",{parentName:"p"},"ls"),". The main parameter is the location of the folder we'd like to list the contents of. So if we wanted to see what was in the ",(0,o.kt)("inlineCode",{parentName:"p"},"Music")," folder, we'd just run:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ls Music\n")),(0,o.kt)("p",null,"Not much to see here:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: List the contents of the Music directory",src:n(9186).Z,width:"1994",height:"744"})),(0,o.kt)("p",null,"Many commands actually allow us to pass multiple parameters. For example, we could list the contents of my Movies and my personal applications:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ls Movies Applications\n")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: List the contents of the Movies and Applications directory",src:n(2743).Z,width:"2152",height:"456"})),(0,o.kt)("p",null,"There's not much in either. You might wonder why ",(0,o.kt)("em",{parentName:"p"},"Applications")," is so empty - that's because we're looking at the applications ",(0,o.kt)("em",{parentName:"p"},"only installed for the current user"),", because we are in the user's home directory. To see the applications for ",(0,o.kt)("em",{parentName:"p"},"everyone")," we'd need to use the folder where applications are kept for ",(0,o.kt)("em",{parentName:"p"},"all")," users."),(0,o.kt)("p",null,"We can do this by running ",(0,o.kt)("inlineCode",{parentName:"p"},"ls /Applications"),":"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: List global applications",src:n(1859).Z,width:"3022",height:"1114"})),(0,o.kt)("p",null,"The trick here is that we start with a leading forward slash - this means the ",(0,o.kt)("inlineCode",{parentName:"p"},"Applications")," folder in the ",(0,o.kt)("em",{parentName:"p"},"root")," of the computer, not the one in my current folder."),(0,o.kt)("p",null,"On Windows, applications are kept in different places, but we can see some of the installed applications by running ",(0,o.kt)("inlineCode",{parentName:"p"},'ls "c:\\program files\\"'),":"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: List applications on Windows",src:n(3009).Z,width:"2074",height:"898"})),(0,o.kt)("p",null,"Why do we have the extra quotation marks here? If we ran the command without the quotation marks, the shell would think we were giving it ",(0,o.kt)("em",{parentName:"p"},"two")," parameters. It would think we wanted to see the contents of the ",(0,o.kt)("inlineCode",{parentName:"p"},"c:\\program")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"files")," folders - and they don't exist!"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: List applications on Windows incorrectly",src:n(3107).Z,width:"2078",height:"898"})),(0,o.kt)("p",null,"The error above shows what happens when we miss the quotation marks."),(0,o.kt)("p",null,"Now we can take a look at how a ",(0,o.kt)("em",{parentName:"p"},"flag")," would work. A flag is a parameter which changes how a command works. Flags normally start with a hyphen. Let's say we wanted to know the ",(0,o.kt)("em",{parentName:"p"},"size")," of the files in the folder. We do this by using the ",(0,o.kt)("inlineCode",{parentName:"p"},"-lh")," pass the parameter, which is short for ",(0,o.kt)("em",{parentName:"p"},"long list, human readable"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ls -lh Downloads/*.jpg\n")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: List downloaded photos",src:n(598).Z,width:"1952",height:"874"})),(0,o.kt)("p",null,"Now I can see all of the ",(0,o.kt)("inlineCode",{parentName:"p"},"jpg")," files (",(0,o.kt)("inlineCode",{parentName:"p"},"jpg")," files are images) in my ",(0,o.kt)("inlineCode",{parentName:"p"},"Downloads")," folder. I can see it looks like I've got two pictures of \"Mardi Himal\" (a mountain in the Himalayas) which are both 384 Kilobytes in size, as well as some other images. Blow by blow, this is what we've got:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"ls")," - List the contents of a folder"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"-lh")," - This is the ",(0,o.kt)("em",{parentName:"li"},"long list in human-readable sizes")," parameter, which means we see how big the files are in a friendly format (like ",(0,o.kt)("inlineCode",{parentName:"li"},"911K")," for Kilobytes, rather than showing something like ",(0,o.kt)("inlineCode",{parentName:"li"},"911012")," which would be the number of bytes - and harder to read!)"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"Downloads/*.jpg")," - Show the contents of the ",(0,o.kt)("inlineCode",{parentName:"li"},"Downloads")," folder, including any files which end with ",(0,o.kt)("inlineCode",{parentName:"li"},".jpg")," - the ",(0,o.kt)("inlineCode",{parentName:"li"},"*")," is a wildcard which means that we don't mind what the filename is")),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"-lh")," parameter is ",(0,o.kt)("em",{parentName:"p"},"shorthand"),". Many commands offer longhand parameters (such as ",(0,o.kt)("inlineCode",{parentName:"p"},"--version"),") as well as shorthand (such as ",(0,o.kt)("inlineCode",{parentName:"p"},"-v")," as an alternative for ",(0,o.kt)("inlineCode",{parentName:"p"},"--version"),"). Longhand is easier to read, shorthand is faster to type."),(0,o.kt)("p",null,"Don't worry - in the next section we'll see how to look up the available parameters for a command. You don't need to remember all of these details, only understand which part is the command and which parts are the parameters. This is just an introduction for now!"),(0,o.kt)("p",null,"Now let's look at one more command."),(0,o.kt)("h3",{id:"the-echo-command"},"The Echo Command"),(0,o.kt)("p",null,"The 'echo' command is used to write out a message in the shell. Here's an example of how it works:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'echo "Hello Shell!"\n')),(0,o.kt)("p",null,"This command writes out the text ",(0,o.kt)("inlineCode",{parentName:"p"},"Hello Shell!"),":"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Echo command",src:n(3146).Z,width:"1628",height:"262"})),(0,o.kt)("p",null,"Why would we do this? One of the most common reasons would be to ",(0,o.kt)("em",{parentName:"p"},"see")," what the shell thinks a certain value is. For example, try this command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'echo "My home directory is at: $HOME"\n')),(0,o.kt)("p",null,"You'll see something like this:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Echo the home directory",src:n(6894).Z,width:"2374",height:"326"})),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"$HOME")," part of the text is called a ",(0,o.kt)("em",{parentName:"p"},"variable"),". We can recognise variables because they start with a dollar symbol. ",(0,o.kt)("inlineCode",{parentName:"p"},"$HOME")," is a built-in variable which holds the location of the current user's home directory."),(0,o.kt)("p",null,"We're going to see all sorts of cool things we can do with ",(0,o.kt)("inlineCode",{parentName:"p"},"echo")," as we continue in the book!"),(0,o.kt)("h3",{id:"move-around"},"Move Around"),(0,o.kt)("p",null,"One common thing we can do in a visual file explorer is move around. We can open folders, and go 'up' from the current folder. We often also see visually where we are in the folder structure with an 'address bar'."),(0,o.kt)("p",null,"A useful reference might be the picture below:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Shell quick reference",src:n(3556).Z,width:"1762",height:"638"})),(0,o.kt)("p",null,"Here we map the shell commands to the visual interface's equivalents:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"pwd")," shows the current working directory - where you currently are in the file system"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"ls")," lists the files in the current directory (or any directory you tell it)"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"cd ..")," changes the directory to another location - if you use the special ",(0,o.kt)("inlineCode",{parentName:"li"},"..")," directory, you are telling it to change to the ",(0,o.kt)("em",{parentName:"li"},"parent")," directory, i.e. 'go up' in the file system ")),(0,o.kt)("p",null,"As a final trick, lets see how we open a file or folder. Let's say I want to open one of the photos in my Downloads folder. Here's how I can do it:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd ~/Downloads\nopen himalayas.jpg\n")),(0,o.kt)("p",null,"We can see the result here:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Open a photo",src:n(1441).Z,width:"2382",height:"1582"})),(0,o.kt)("p",null,"Running ",(0,o.kt)("inlineCode",{parentName:"p"},"open himalayas.jpg")," has opened the photo in the application which is used for photos by default in the operating system."),(0,o.kt)("p",null,"Be aware - this command is different on different operating systems (but we're going to see later on how to fix that and make it consistent everywhere!). The ",(0,o.kt)("inlineCode",{parentName:"p"},"open")," command will open a file on MacOS. On Windows you can use ",(0,o.kt)("inlineCode",{parentName:"p"},"start"),", and on Linux you can generally use ",(0,o.kt)("inlineCode",{parentName:"p"},"xdg-open"),"."),(0,o.kt)("p",null,"As a nifty trick, trying running ",(0,o.kt)("inlineCode",{parentName:"p"},"open ."),(0,o.kt)("sup",{parentName:"p",id:"fnref-1"},(0,o.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),":"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Open the current directory",src:n(4818).Z,width:"2022",height:"1080"})),(0,o.kt)("p",null,". This will open the ",(0,o.kt)("em",{parentName:"p"},"current folder"),". Every folder contains two 'special' folders. The first is ",(0,o.kt)("inlineCode",{parentName:"p"},".."),", which we've seen means 'my parent folder' and the second is ",(0,o.kt)("inlineCode",{parentName:"p"},"."),", which means 'myself'. Having this ",(0,o.kt)("inlineCode",{parentName:"p"},".")," folder is convenient, as it means we can do things like this - run a command to open the current folder."),(0,o.kt)("p",null,"We're going to go into a lot more detail on how to work with files and folders, move around, but hopefully this has provided a crash course for the basics. They key ",(0,o.kt)("em",{parentName:"p"},"concepts")," to remember, which are much more important than the individual commands we've see are:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"In the shell we run ",(0,o.kt)("em",{parentName:"li"},"commands")),(0,o.kt)("li",{parentName:"ul"},"We can change how commands work by using ",(0,o.kt)("em",{parentName:"li"},"parameters")),(0,o.kt)("li",{parentName:"ul"},"Some parameters just go at the end of the command - like ",(0,o.kt)("inlineCode",{parentName:"li"},"ls Downloads")),(0,o.kt)("li",{parentName:"ul"},"Some parameters start with a hyphen, and change how the command behaves - these are often called 'flags'. An example is ",(0,o.kt)("inlineCode",{parentName:"li"},"ls -lh"),", which lists the files in the current folder with a human-readable file size")),(0,o.kt)("p",null,"We've also learned:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"cd")," changes the current directory"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"pwd")," prints the current directory"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"ls")," lists the files in a directory"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"echo")," can be used to write out text to the screen"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"open"),", ",(0,o.kt)("inlineCode",{parentName:"li"},"start")," and ",(0,o.kt)("inlineCode",{parentName:"li"},"xdg-open")," can be used to open a file or folder on MacOS, Windows and Linux respectively")),(0,o.kt)("p",null,"Now we can start to get into more detail!"),(0,o.kt)("h2",{id:"summary"},"Summary"),(0,o.kt)("p",null,"In this section we learnt:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"That this book is for IT professionals, hobbyists or anyone who wants to learn more about how to work with computers"),(0,o.kt)("li",{parentName:"ul"},"What the shell is, and why we might want to use it"),(0,o.kt)("li",{parentName:"ul"},"How to open the shell programs for Windows, Mac and Linux which are installed by default"),(0,o.kt)("li",{parentName:"ul"},"How to configure the shells for Windows or Mac to behave in a Linux-like way to allow us to follow on with the rest of the book")),(0,o.kt)("p",null,"We introduced the following commands:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"cd")," - which ",(0,o.kt)("em",{parentName:"li"},"changes directory")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"pwd")," - which ",(0,o.kt)("em",{parentName:"li"},"prints the current working directory")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"ls")," - which ",(0,o.kt)("em",{parentName:"li"},"lists")," the contents of a directory"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"echo")," - which writes text to the screen"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"open")," - which will open a file or folder")),(0,o.kt)("p",null,"We also briefly introduced ",(0,o.kt)("em",{parentName:"p"},"variables"),", which are special values which start with the dollar symbol, such as ",(0,o.kt)("inlineCode",{parentName:"p"},"$HOME")," which stores the user's home directory. We saw that each directory contains two special directories - ",(0,o.kt)("inlineCode",{parentName:"p"},"..")," which represents the ",(0,o.kt)("em",{parentName:"p"},"parent directory"),", and ",(0,o.kt)("inlineCode",{parentName:"p"},".")," which represents the ",(0,o.kt)("em",{parentName:"p"},"current directory"),"."),(0,o.kt)("p",null,"With these tasks complete we can now move onto the next section."),(0,o.kt)("div",{className:"footnotes"},(0,o.kt)("hr",{parentName:"div"}),(0,o.kt)("ol",{parentName:"div"},(0,o.kt)("li",{parentName:"ol",id:"fn-1"},"On Windows you might need to run ",(0,o.kt)("inlineCode",{parentName:"li"},"start .")," and on Linux, ",(0,o.kt)("inlineCode",{parentName:"li"},"xdg-open ."),".",(0,o.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")))))}u.isMDXComponent=!0},223:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cygwin-1-c2222807ad8531efa8f904ebd7d47ac5.png"},7973:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cygwin-2-9a538f448ea5789e7fbdcaecb5c85c88.png"},1018:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cygwin-3-089db6ba17fc27105f49bfeb2b32394f.png"},9258:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cygwin-6-99fce814e1395c8a33c78e3c5a47718a.png"},9167:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cygwin-7-12d4c00e0534c45cbfa33c0243730157.png"},2868:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cygwin-8-68d2071d9e5ad3122291f85c3c977252.png"},3790:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/linux-shell-5ef723374126e0ea4c3c54483987a629.png"},8805:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mac-shell-e2a527be5dd0a060b69e863b34e3573a.png"},7781:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/osx-search-terminal-7ac82426f49389bc7f8018a88258fbc5.png"},4889:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/osx-shell-whoami-c8395feac9a865fec59e4cb9ee037615.png"},5157:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-osx-1-4fc7d2ebdf8a24d89935281dad1f0c07.png"},9073:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-osx-2-4d3098e3ad33dc2dce7788de368e6cf6.png"},630:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-osx-3-93e47b2eafb69b28b7e912d73366a70e.png"},123:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-1-41d6240f93fa7636937af9f31124bc68.png"},1549:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-11-95ad07511fb360ee6ad0e8d977c85cfb.png"},2567:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-2-ee5741a5bb4d4faa4b0f7e9d6f23ffd7.png"},2569:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-3-4e91f8a9fb849a66312fecb62b4b9bea.png"},4833:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-4-35bf528013ee3bef60ab7c3671c0bf45.png"},1089:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-5-683a093ee97c0e298d809e1b68f07b0e.png"},9211:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-6-503dda8507f6f47c965e34a09f267fe0.png"},4163:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-7-8222d073de6fd7cd0f5379564f46b456.png"},7560:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-8-4ae6fe35c79a7737ae8edf0232a8552e.png"},2582:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-ubuntu-9-463d9fdcd9eb20b6b7c9a81fdd0375ba.png"},3653:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-wsl-1-dc11f93fb578549b24b935c4182b7ab6.png"},8031:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-wsl-2-76bac1a1fc1a859ecfa64924141b48d9.png"},8936:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-wsl-3-f12276a493809ef2fec57c99ede1d8a7.png"},6005:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-wsl-4-9134675242a163fce181afc99b1c68c7.png"},2867:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/setup-wsl-5-9687c657fd3f8d6c01bcc193b4d57918.png"},5851:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ubuntu-1-32da6aea9163bf8c0634b03e58690f08.png"},4548:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/virtualbox-1-b55e8eca96f9ad58a2d2b4b07c10d7f7.png"},5709:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/virtualbox-2-56e9a56597638f636bb037cfecc9e979.png"},3476:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/virtualbox-3-8b9a8364c7a91467f0c3dbb792cb8fe3.png"},3057:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/virtualbox-4-8937d9eb77a0f58c4e04887fbe21c903.png"},6544:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/windows-search-command-prompt-3649f5245909e5d709626e6ae028234a.png"},8262:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/windows-shell-whoami-ade4a68eb3fc37c74d58144f35e128ce.png"},8307:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/windows-shell-ade4a68eb3fc37c74d58144f35e128ce.png"},8932:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/bash-version-14d5b16e4a01db7b421bc9128a1f35e3.png"},3146:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/echo-hello-shell-f1d92f9c20f15c0e5741c07537284d18.png"},6894:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/echo-home-45ee24e5927f9a6f2845504ae08ef800.png"},3107:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-applications-windows-error-95571fda84ee478ee5678922b29fe185.png"},3009:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-applications-windows-1d30de599a44ee545ad631eb5c3176b9.png"},1859:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-applications-95adadfe6a72bee754edda918dfdcac0.png"},598:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-downloads-0dd8669df2ed45c5cced6b0c642b676d.png"},2743:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-movies-apps-5f10acf5a3b015e153365771d99caf13.png"},9186:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-music-8dfbb67185c5a33fb2f3fd8d2c6d8baa.png"},5487:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-df7bd66eeae98e04b0422d10ccd6cb68.png"},3865:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/moving-to-home-164dc619f0084c7e1ba79cd1b6486e89.png"},1441:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/open-command-596e1bd58a6b9244a6a653f0bad118bb.png"},4818:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/open-current-directory-b657b2ed173070551c7f7301e4655876.png"},3556:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/shell-commands-dd4418126f860c1bb692c2bf54b827d7.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/29e1d613.125a661d.js b/pr-preview/pr-346/assets/js/29e1d613.125a661d.js new file mode 100644 index 00000000..7c4fd618 --- /dev/null +++ b/pr-preview/pr-346/assets/js/29e1d613.125a661d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[1987],{9999:(e,t,a)=>{a.r(t),a.d(t,{default:()=>c});var l=a(7294),n=a(8765);function c(){return l.createElement(n.Z,{title:"Effective Shell",description:"Effective Shell - Donate"},l.createElement("div",{className:"container"},l.createElement("div",{className:"row row--no-gutters"},l.createElement("div",{className:"col col--4 col--offset-4 padding-top--lg",style:{justifyContent:"center",alignItems:"center"}},l.createElement("p",null,"Thank you for your payment. Your transaction has been completed and we've emailed you a receipt for your purchase. Log in to your PayPal account to view transaction details.")))))}}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/2a3552dc.a262a369.js b/pr-preview/pr-346/assets/js/2a3552dc.a262a369.js new file mode 100644 index 00000000..bfab1889 --- /dev/null +++ b/pr-preview/pr-346/assets/js/2a3552dc.a262a369.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[9602],{3905:(e,t,a)=>{a.d(t,{Zo:()=>m,kt:()=>u});var n=a(7294);function s(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t =0||(s[a]=e[a]);return s}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,a)&&(s[a]=e[a])}return s}var o=n.createContext({}),h=function(e){var t=n.useContext(o),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},m=function(e){var t=h(e.components);return n.createElement(o.Provider,{value:t},e.children)},p="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var a=e.components,s=e.mdxType,r=e.originalType,o=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),p=h(a),d=s,u=p["".concat(o,".").concat(d)]||p[d]||c[d]||r;return a?n.createElement(u,i(i({ref:t},m),{},{components:a})):n.createElement(u,i({ref:t},m))}));function u(e,t){var a=arguments,s=t&&t.mdxType;if("string"==typeof e||s){var r=a.length,i=new Array(r);i[0]=d;var l={};for(var o in t)hasOwnProperty.call(t,o)&&(l[o]=t[o]);l.originalType=e,l[p]="string"==typeof e?e:s,i[1]=l;for(var h=2;h {a.r(t),a.d(t,{assets:()=>o,contentTitle:()=>i,default:()=>p,frontMatter:()=>r,metadata:()=>l,toc:()=>h});var n=a(7462),s=(a(7294),a(3905));const r={title:"What is a Shell?",slug:"/part-2-core-skills/what-is-a-shell"},i=void 0,l={unversionedId:"core-skills/what-is-a-shell/index",id:"core-skills/what-is-a-shell/index",title:"What is a Shell?",description:'This is the second of the "interludes" which end each section of the book. These interludes give flavour, concepts, context and the history of some of the concepts we\'re dealing with. These interlude are not essential to mastering the skills of the shell, but you might find them interesting.',source:"@site/docs/02-core-skills/12-what-is-a-shell/index.md",sourceDirName:"02-core-skills/12-what-is-a-shell",slug:"/part-2-core-skills/what-is-a-shell",permalink:"/part-2-core-skills/what-is-a-shell",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/02-core-skills/12-what-is-a-shell/index.md",tags:[],version:"current",frontMatter:{title:"What is a Shell?",slug:"/part-2-core-skills/what-is-a-shell"},sidebar:"sidebar",previous:{title:"Finding Files",permalink:"/part-2-core-skills/finding-files"},next:{title:"Part 3 - Manipulating Text and Streams",permalink:"/part-3-manipulating-text/"}},o={},h=[{value:"A Computer in a Nutshell",id:"a-computer-in-a-nutshell",level:2},{value:"The Operating System",id:"the-operating-system",level:3},{value:"The Kernel",id:"the-kernel",level:3},{value:"User Space",id:"user-space",level:3},{value:"The Shell",id:"the-shell",level:3},{value:"The Terminal",id:"the-terminal",level:3},{value:"Back to the Shell",id:"back-to-the-shell",level:2},{value:"The Command Prompt or Command Line",id:"the-command-prompt-or-command-line",level:3},{value:"Shell Commands and Different Shells",id:"shell-commands-and-different-shells",level:3},{value:"That's a Wrap!",id:"thats-a-wrap",level:3},{value:"Example: iTerm 2 / tmux / zsh",id:"example-iterm-2--tmux--zsh",level:4},{value:"Example: Bash",id:"example-bash",level:4},{value:"Example: Windows Explorer",id:"example-windows-explorer",level:4},{value:"Example: Windows Command Prompt",id:"example-windows-command-prompt",level:4},{value:"Example: Windows PowerShell",id:"example-windows-powershell",level:4},{value:"Example: Windows Subsystem for Linux (WSL)",id:"example-windows-subsystem-for-linux-wsl",level:4},{value:"Share and Discuss",id:"share-and-discuss",level:2}],m={toc:h};function p(e){let{components:t,...r}=e;return(0,s.kt)("wrapper",(0,n.Z)({},m,r,{components:t,mdxType:"MDXLayout"}),(0,s.kt)("p",null,'This is the second of the "interludes" which end each section of the book. These interludes give flavour, concepts, context and the history of some of the concepts we\'re dealing with. These interlude are not essential to mastering the ',(0,s.kt)("em",{parentName:"p"},"skills")," of the shell, but you might find them interesting."),(0,s.kt)("p",null,"In this interlude we'll actually look at what a shell is, all the way from the highest level, which non-technical readers will be able to comfortably follow, to the low level, which advanced users may find illuminating."),(0,s.kt)("h1",{id:"introduction-for-the-non-technical-reader"},"Introduction for the Non-Technical Reader"),(0,s.kt)("p",null,"It might come as a surprise that ",(0,s.kt)("em",{parentName:"p"},"many")," technical computer users (programmers, data scientists, systems administrators etc) spend a lot of time using an interface which looks like it's from the sixties:"),(0,s.kt)("img",{width:"600px",alt:"Diagram: The Shell",src:a(5071).Z}),(0,s.kt)("p",null,"If you work with technologists, you might have seen them using an interface like this. This kind of simple, text-based interface is called a ",(0,s.kt)("em",{parentName:"p"},"shell"),", and it has been a common way to interface with computers ever since the first screens and keyboards were created."),(0,s.kt)("p",null,"Given how much computing has advanced, why would people use such an interface? Just look at how much the Windows operating-system has changed over the last three decades:"),(0,s.kt)("img",{width:"600px",alt:"Image: The Evolution of Windows",src:a(1174).Z}),(0,s.kt)("p",null,(0,s.kt)("em",{parentName:"p"},"(By Source (WP:NFCC#4), Fair use, ",(0,s.kt)("a",{parentName:"em",href:"https://en.wikipedia.org/w/index.php?curid=58853841"},"https://en.wikipedia.org/w/index.php?curid=58853841"),")")),(0,s.kt)("p",null,"Why would people choose to use such an archaic interface as a shell?"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"Typing is ",(0,s.kt)("em",{parentName:"li"},"fast"),": A skilled shell user can manipulate a system at dazzling speeds just using a keyboard. Typing commands is generally ",(0,s.kt)("em",{parentName:"li"},"much")," faster than exploring through user interfaces with a mouse"),(0,s.kt)("li",{parentName:"ul"},"Shells are ",(0,s.kt)("em",{parentName:"li"},"programmable"),": Users will often being programming as they work in a shell, creating scripts to automate time-consuming or repetitive processes"),(0,s.kt)("li",{parentName:"ul"},"Shells are ",(0,s.kt)("em",{parentName:"li"},"portable"),": A shell can be used to interface to almost any type of computer, from a mainframe to a Raspberry Pi, in a very similar way.")),(0,s.kt)("p",null,"Not all technical users will use a shell regularly, but there are many who will spend the bulk of their time in such an interface. It is such a crucial skill to be able to operate one effectively that I have been writing this series primarily to show ways to be more efficient with this kind of interface."),(0,s.kt)("h1",{id:"introduction-for-the-technical-reader"},"Introduction for the Technical Reader"),(0,s.kt)("p",null,"You may be familiar with the shell, but it can be useful to understand some of the surrounding concepts in detail. How does a shell differ from a terminal? What is a ",(0,s.kt)("em",{parentName:"p"},"tty"),"? How do shells really work? Hopefully as you read this chapter you'll discover something that you didn't know about shells."),(0,s.kt)("h1",{id:"lets-get-started"},"Let's Get Started!"),(0,s.kt)("p",null,"To understand what shells, terminals, command-prompts and so on are and how they relate, we need to start with the basics: how a modern computer works!"),(0,s.kt)("h2",{id:"a-computer-in-a-nutshell"},"A Computer in a Nutshell"),(0,s.kt)("p",null,"The diagram below shows a simplified view of a typical computer:"),(0,s.kt)("img",{width:"600px",alt:"Diagram: Operating System",src:a(620).Z}),(0,s.kt)("p",null,"Already there's a lot going on."),(0,s.kt)("p",null,"Your computer is going to have a CPU",(0,s.kt)("sup",{parentName:"p",id:"fnref-1"},(0,s.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1"))," and memory",(0,s.kt)("sup",{parentName:"p",id:"fnref-2"},(0,s.kt)("a",{parentName:"sup",href:"#fn-2",className:"footnote-ref"},"2")),", and almost certainly a network adapter",(0,s.kt)("sup",{parentName:"p",id:"fnref-3"},(0,s.kt)("a",{parentName:"sup",href:"#fn-3",className:"footnote-ref"},"3"))," and display adapter",(0,s.kt)("sup",{parentName:"p",id:"fnref-4"},(0,s.kt)("a",{parentName:"sup",href:"#fn-4",className:"footnote-ref"},"4")),". Most computers will have at least one hard disk. For home PCs, there'll also likely be a bunch of peripherals, such as a mouse, keyboard, printers, flash drives, webcams and so on."),(0,s.kt)("h3",{id:"the-operating-system"},"The Operating System"),(0,s.kt)("p",null,"The operating system is the piece of software installed on a computer that can interface with the ",(0,s.kt)("em",{parentName:"p"},"hardware"),". Without hardware, such as a CPU, memory, a network adapter, a graphics card, disk drives and so on, there's not much that you can do with the computer. The operating system is the primary interface to this hardware. No normal programs will talk to hardware directly - the operating system abstracts this hardware away and provides a ",(0,s.kt)("em",{parentName:"p"},"software")," interface to it."),(0,s.kt)("p",null,"The abstraction the operating system provides is essential. Developers don't need to know the specifics of how to work with individual devices from different vendors; the operating system provides a standardised interface to all of this. It also handles various tasks such as making sure the system starts up properly."),(0,s.kt)("p",null,"The operating system is generally broken down into two parts - the ",(0,s.kt)("em",{parentName:"p"},"kernel")," and ",(0,s.kt)("em",{parentName:"p"},"user space"),":"),(0,s.kt)("img",{width:"600px",alt:"Diagram: The Kernel and User Space",src:a(279).Z}),(0,s.kt)("p",null,"Let's look at these in more detail."),(0,s.kt)("h3",{id:"the-kernel"},"The Kernel"),(0,s.kt)("p",null,"This is the part of the operating system that is responsible for the most sensitive tasks: interfacing with physical devices, managing the resources that are available for users and programs, starting up the various systems that are needed, and so on."),(0,s.kt)("p",null,"Software running in the kernel has direct access to resources, so is ",(0,s.kt)("em",{parentName:"p"},"extremely")," sensitive. The kernel will balance resources between the programs in user space, which we'll look at shortly. If you've ever had to install 'drivers', these are examples of pieces of software that will run in the kernel - they'll have direct access to a physical device you've installed, and expose it to the rest of the software on the computer."),(0,s.kt)("p",null,"Why 'kernel'? The kernel is the soft, edible part of a nut or seed, which is surrounded by a shell. Below you can see a walnut - the kernel is the soft bit in the middle, and the shell surrounds and protects it. This is a useful metaphor that is used for parts of a computer."),(0,s.kt)("img",{width:"200px",alt:"Image: Photo of a walnut, showing the kernel and the shell",src:a(3794).Z}),(0,s.kt)("p",null,(0,s.kt)("em",{parentName:"p"},"(By Kkchaudhary11 - Own work, CC BY-SA 4.0, ",(0,s.kt)("a",{parentName:"em",href:"https://commons.wikimedia.org/w/index.php?curid=49069244"},"https://commons.wikimedia.org/w/index.php?curid=49069244"),")")),(0,s.kt)("p",null,"The operating system kernel really is the ",(0,s.kt)("em",{parentName:"p"},"core")," of the operating system. It's such a sensitive area of the operating system that we actually want to avoid running software in it if possible",(0,s.kt)("sup",{parentName:"p",id:"fnref-5"},(0,s.kt)("a",{parentName:"sup",href:"#fn-5",className:"footnote-ref"},"5")),". And that is where ",(0,s.kt)("em",{parentName:"p"},"user space")," comes in."),(0,s.kt)("h3",{id:"user-space"},"User Space"),(0,s.kt)("p",null,"The vast majority of programs run in 'user space' (also commonly called 'user land')."),(0,s.kt)("p",null,"When a program starts, the kernel will allocate it a private segment of memory and provide ",(0,s.kt)("em",{parentName:"p"},"limited")," access to resources. The program is given access to a library of functions by the operating system, which it can use to access resources such as files, devices and so on. Programs in user space are essentially in sandboxes, where there is a limit to how much damage they can do."),(0,s.kt)("p",null,"For example, a program running in user space can use the standard ",(0,s.kt)("a",{parentName:"p",href:"http://man7.org/linux/man-pages/man3/fopen.3.html"},(0,s.kt)("inlineCode",{parentName:"a"},"fopen"))," function, which is provided on almost every operating system as part of the ",(0,s.kt)("a",{parentName:"p",href:"https://www.gnu.org/software/libc/"},"C Standard Library"),". This allows a program to attempt to open a file. The operating system will make a decision on whether the program is ",(0,s.kt)("em",{parentName:"p"},"allowed")," to open the file (based on things such as permissions, where the file is and so on) and then, if it is OK with the call, will give the program access to the file. Under the hood, this 'user space' call translates to a system call in the kernel."),(0,s.kt)("p",null,"Now that the key components have been introduced, we can look at the ",(0,s.kt)("em",{parentName:"p"},"shell"),". The name should come as no surprise, as it is a ",(0,s.kt)("em",{parentName:"p"},"wrapper")," or outer layer to the operating system (which itself contains the sensitive nugget of the kernel)."),(0,s.kt)("h3",{id:"the-shell"},"The Shell"),(0,s.kt)("p",null,"So what is the shell? The shell is just a general name for any ",(0,s.kt)("em",{parentName:"p"},"user space")," program that allows access to resources in the system, via some kind of interface."),(0,s.kt)("p",null,"Shells come in many different flavours but are generally provided to aid a human operator in accessing the system. This could be interactively, by typing at a terminal, or via scripts, which are files that contain a sequence of commands."),(0,s.kt)("p",null,"For example, to see all of the files in a folder, the human operator ",(0,s.kt)("em",{parentName:"p"},"could")," write a program in a language such as C, making system calls to do what they want. But for day-to-day tasks, this would be repetitive. A shell will normally offer us a quick way to do that exact task, without having to manually write a program to do it."),(0,s.kt)("p",null,"Here's an example, where a shell is being used to show the 'png' images in the folder I am working in",(0,s.kt)("sup",{parentName:"p",id:"fnref-6"},(0,s.kt)("a",{parentName:"sup",href:"#fn-6",className:"footnote-ref"},"6")),":"),(0,s.kt)("img",{width:"600px",alt:"Screenshot: Browsing Contents of the File System the the Bourne Again Shell",src:a(1434).Z}),(0,s.kt)("p",null,"So a shell is a user-space program to interface with the computer. But there a few more moving parts than just a shell we are seeing in the image above. There are different types of shells, there are terminal programs, and there are the programs or commands that the shell calls (in the example above, ",(0,s.kt)("inlineCode",{parentName:"p"},"tree")," is a program). Let's pick these apart."),(0,s.kt)("p",null,"Here's a diagram that more accurately shows what is going on:"),(0,s.kt)("img",{width:"600px",alt:"Diagram: The Terminal & The Shell",src:a(5593).Z}),"We've introduced a few new things here. There's a _user_, who is interfacing with a _terminal_, which is running a _shell_, which is showing a _command prompt_. The user has written a command that is calling a program (in this case, the `tree` program).",(0,s.kt)("p",null,"Let's dissect this bit by bit."),(0,s.kt)("h3",{id:"the-terminal"},"The Terminal"),(0,s.kt)("p",null,"We're not ",(0,s.kt)("em",{parentName:"p"},"directly")," interacting with the 'shell' in this diagram. We're actually using a ",(0,s.kt)("em",{parentName:"p"},"terminal"),". When a user wants to work with a shell interactively, using a keyboard to provide input and a display to see the output on the screen, the user uses a ",(0,s.kt)("em",{parentName:"p"},"terminal"),"."),(0,s.kt)("p",null,"A terminal is just a program that reads input from the keyboard, passes that input to another program (normally a shell), and displays the results on the screen. A shell program on its own does not do this - it requires a terminal as an interface."),(0,s.kt)("p",null,"Why the word ",(0,s.kt)("em",{parentName:"p"},"terminal"),"? This makes sense when you look at how people interfaced with computers historically. Input to a computer might be through punch cards, and output would often be via a printer. The ",(0,s.kt)("em",{parentName:"p"},"Teletype Terminal"),(0,s.kt)("sup",{parentName:"p",id:"fnref-7"},(0,s.kt)("a",{parentName:"sup",href:"#fn-7",className:"footnote-ref"},"7"))," became a common way for users to interface with computers."),(0,s.kt)("img",{width:"600px",alt:"Photo: ASR-33 TTY",src:a(1721).Z}),(0,s.kt)("p",null,(0,s.kt)("em",{parentName:"p"},"(Photograph by Rama, Wikimedia Commons, Cc-by-sa-2.0-fr, CC BY-SA 2.0 fr, ",(0,s.kt)("a",{parentName:"em",href:"https://commons.wikimedia.org/w/index.php?curid=17821795"},"https://commons.wikimedia.org/w/index.php?curid=17821795"),")")),(0,s.kt)("p",null,"At this time, computers were very large, complex, and expensive machines. It was common to have ",(0,s.kt)("em",{parentName:"p"},"many")," terminals connected to a single large machine (or 'mainframe'), or a few terminals that people would share. But the terminal itself was just a human interface to the operating system. A more modern terminal would be something like an IBM 3486:"),(0,s.kt)("img",{width:"600px",alt:"Photo: IBM 3486",src:a(5752).Z}),(0,s.kt)("p",null,(0,s.kt)("em",{parentName:"p"},"(By ClickRick - Own work, CC BY-SA 3.0, ",(0,s.kt)("a",{parentName:"em",href:"https://commons.wikimedia.org/w/index.php?curid=6693700"},"https://commons.wikimedia.org/w/index.php?curid=6693700"),")")),(0,s.kt)("p",null,"This is a very small computer in its own right but still basically just a dumb screen and keyboard connected by a cable to a larger mainframe computer in another location."),(0,s.kt)("p",null,"This mechanism is still very much the case today. When I want to work with a computer in a data centre, I don't go and find the machine, plug in a keyboard and a display and directly interface to it. I run a ",(0,s.kt)("em",{parentName:"p"},"terminal program")," on my computer to provide access to the remote machine. My terminal program allows me to use my keyboard and display to work with a remote machine - all via a ",(0,s.kt)("em",{parentName:"p"},"secure shell")," - which is a secured-shell connection over a network."),(0,s.kt)("p",null,"So terminals in many ways are quite simple - they are interfaces. But because they are quite simple programs, we can't do much with them. So normally, the first thing that a terminal program will do is run a ",(0,s.kt)("em",{parentName:"p"},"shell")," program - a program that we can use to operate the computer."),(0,s.kt)("p",null,"There's nothing special about terminals - anyone can write a program to operate as a terminal, which is why you will see many different terminals around. Examples are the standard 'terminal' app for MacOS X, the ",(0,s.kt)("a",{parentName:"p",href:"https://wiki.gnome.org/Apps/Terminal/VTE"},"gnome-terminal")," for Linux, and ",(0,s.kt)("a",{parentName:"p",href:"https://www.iterm2.com/"},"iTerm2")," and ",(0,s.kt)("a",{parentName:"p",href:"https://hyper.is/"},"Hyper"),". There's a bunch of screenshots of different setups at the end of the article."),(0,s.kt)("h2",{id:"back-to-the-shell"},"Back to the Shell"),(0,s.kt)("p",null,"Now that we've described the terminal, we can go back and look at the shell in detail."),(0,s.kt)("p",null,"The shell is the program that is going to take input from somewhere and run a series of commands. When the shell is running in a terminal, it is normally taking input interactively from the user. As the user types in commands, the terminal feeds the input to the shell and presents the output of the shell on the screen."),(0,s.kt)("p",null,"A shell program can also take input from files; these files will then generally be 'shell scripts'. This might be used to run automated operations, such as cleaning up certain folders when a computer starts."),(0,s.kt)("p",null,"Shells can write output to files or other locations, and so on. You can run a shell program outside of a terminal - you just won't be able to interface with it using a keyboard or display. And in fact, lots of operations happen in this way: automated scripts, startup tasks, installers and so on."),(0,s.kt)("p",null,"So what else does a shell do? Most of the features are related to helping human operators work with the system more efficiently."),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"Quickly enter commands, see the history of commands and quickly restructure commands (see ",(0,s.kt)("a",{parentName:"li",href:"/part-2-core-skills/fly-on-the-command-line"},"Chapter 8 - Fly on the Command Line"),")"),(0,s.kt)("li",{parentName:"ul"},"Navigate through the file system, moving from folder to folder (see ",(0,s.kt)("a",{parentName:"li",href:"/part-1-transitioning-to-the-shell/navigating-your-system/"},"Chapter 1- Navigating Your System"),"), which makes it easier for an operator to navigate the file system"),(0,s.kt)("li",{parentName:"ul"},"Chain the output of commands together - for example, taking the output of one basic program, such as the ",(0,s.kt)("inlineCode",{parentName:"li"},"tree")," program we saw, and writing it to a file (see ",(0,s.kt)("a",{parentName:"li",href:"/part-2-core-skills/thinking-in-pipelines/"},"Chapter 7 - Thinking in Pipelines"),")"),(0,s.kt)("li",{parentName:"ul"},"Offer a programming language, allowing the operator to perform more complicated tasks")),(0,s.kt)("p",null,"And a lot more! In fact, that's what the whole book is about - how to get the most from these powerful programs, particularly for those who use them regularly."),(0,s.kt)("h3",{id:"the-command-prompt-or-command-line"},"The Command Prompt or Command Line"),(0,s.kt)("p",null,"The last part of the diagram, which we haven't covered yet, is the ",(0,s.kt)("em",{parentName:"p"},"command prompt"),"."),(0,s.kt)("img",{width:"300px",alt:"Diagram: Command Prompt",src:a(9152).Z}),(0,s.kt)("p",null,"When a ",(0,s.kt)("em",{parentName:"p"},"shell")," is running in ",(0,s.kt)("em",{parentName:"p"},"terminal"),", it knows that a human operator will be interfacing with it. So to make sure that the operator has some kind of visual hint that ",(0,s.kt)("em",{parentName:"p"},"they have to enter commands"),", the shell will output some kind of prompt."),(0,s.kt)("p",null,"I've included a set of screenshots at the end of the article, just after this section, and you can see how some different command prompts look."),(0,s.kt)("p",null,"Note that shells don't have to use command prompts - if you use a shell program to execute a script, there will be no command prompt. Shells only show a prompt when they know they are being used interactively. Many programs which allow a user to operate interactively will show a command prompt."),(0,s.kt)("p",null,"Shell command prompts can be customised, so they will often look different from machine to machine. Below is an example that shows a ",(0,s.kt)("em",{parentName:"p"},"lot")," of technical information. This is from the highly popular ",(0,s.kt)("a",{parentName:"p",href:"https://ohmyz.sh/"},"oh-my-zsh")," framework for the 'Z Shell' shell, which is very popular among developers:"),(0,s.kt)("img",{width:"600px",alt:"Image: Customised oh-my-zsh",src:a(1331).Z}),(0,s.kt)("p",null,"*(Source: ",(0,s.kt)("a",{parentName:"p",href:"https://ohmyz.sh/"},"https://ohmyz.sh/"),")"),(0,s.kt)("h3",{id:"shell-commands-and-different-shells"},"Shell Commands and Different Shells"),(0,s.kt)("p",null,"A lot of the 'commands' in a shell, such as ",(0,s.kt)("inlineCode",{parentName:"p"},"cat")," (which shows the contents of a file), are actually just simple programs, which will interface with the kernel. No matter what shell you use, these commands will behave the same way, because really all you are doing is calling another program."),(0,s.kt)("p",null,"Some commands, such as ",(0,s.kt)("inlineCode",{parentName:"p"},"cd")," (change directory), are built into the shell. Some commands are functions that have been defined, or aliases to other commands (for more details on commands, see ",(0,s.kt)("a",{parentName:"p",href:"/part-2-core-skills/understanding-commands"},"Chapter 10 - Understanding Commands"),"). Commands will often differ between shells."),(0,s.kt)("p",null,"Not all shells are created equal - anyone can write a shell program, maybe creating a simple interface to the computer or a highly complex one with many features. In fact, a later article in this series will look at the genealogy of the most common shells."),(0,s.kt)("p",null,"On most Unix-like systems, the default shell is a program called ",(0,s.kt)("inlineCode",{parentName:"p"},"bash"),', which stands for " Bourne Again Shell" (the name and history around it will be discussed at length in the later article). But there are many other shells: the C Shell, the Korn Shell, Z Shell and Fish, just to name just a few.'),(0,s.kt)("p",null,"Users and administrators can configure what shell they like to use. When a terminal opens, it will immediately start the user's preferred shell program. It is possible to change this. Different users will have different preferences, given that shells offer varying features. This can cause complexity when working with systems, as we cannot always expect every user to have the same shell, or even for the same shell to be set up consistently, as they can be extensively customised."),(0,s.kt)("p",null,"Let's review the earlier diagram again:"),(0,s.kt)("img",{width:"600px",alt:"Diagram: The Terminal & The Shell",src:a(5593).Z}),(0,s.kt)("p",null,'We can see the real internals of what is going on in this "Terminal -> Shell -> Program" chain in the diagram above quite easily.'),(0,s.kt)("p",null,"Try the command ",(0,s.kt)("inlineCode",{parentName:"p"},"pstree -psa $$")," in a shell",(0,s.kt)("sup",{parentName:"p",id:"fnref-8"},(0,s.kt)("a",{parentName:"sup",href:"#fn-8",className:"footnote-ref"},"8")),":"),(0,s.kt)("img",{width:"600px",alt:"Image: Process Tree",src:a(7825).Z}),(0,s.kt)("p",null,"The first ",(0,s.kt)("inlineCode",{parentName:"p"},"systemd")," process is the primary process for the OS - it is process number ",(0,s.kt)("inlineCode",{parentName:"p"},"1"),", which initialises everything else. The second ",(0,s.kt)("inlineCode",{parentName:"p"},"systemd")," process is the process that is running the interface for my user. We can ignore these for now; they are internals to how the operating system boots and starts processes."),(0,s.kt)("p",null,"What is interesting is that we can see a ",(0,s.kt)("em",{parentName:"p"},"terminal")," (the gnome terminal), which has started my preferred ",(0,s.kt)("em",{parentName:"p"},"shell")," (which is ",(0,s.kt)("inlineCode",{parentName:"p"},"zsh"),"), which is running a ",(0,s.kt)("em",{parentName:"p"},"command")," (the program ",(0,s.kt)("inlineCode",{parentName:"p"},"pstree"),"). Here we can see the exact chain as shown in the diagram earlier."),(0,s.kt)("h3",{id:"thats-a-wrap"},"That's a Wrap!"),(0,s.kt)("p",null,"These are the key technologies and concepts that surround a shell."),(0,s.kt)("p",null,"If you are interested in more technical details of working with shells, then my ",(0,s.kt)("a",{parentName:"p",href:"https://github.com/effective-shell"},"Effective Shell")," series goes into these topics in depth. The goal of this series is to help teach techniques that making working with shells more efficient."),(0,s.kt)("p",null,"To close the article, below are some examples of different terminals, shells, command prompts and so on."),(0,s.kt)("h4",{id:"example-iterm-2--tmux--zsh"},"Example: iTerm 2 / tmux / zsh"),(0,s.kt)("img",{width:"600px",alt:"Example: iTerm 2, tmux, zsh",src:a(4637).Z}),(0,s.kt)("p",null,"In this example, we have:"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"A MacOS operating system"),(0,s.kt)("li",{parentName:"ul"},"iTerm2 as the terminal program"),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},"tmux")," running as a 'terminal multiplexer' (see ",(0,s.kt)("a",{parentName:"li",href:"https://github.com/dwmkerr/effective-shell#coming-soon"},"Effective Shell: Terminal Multiplexers"),")"),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},"zsh")," (Z Shell) as the shell program, using 'oh my zsh', which is easily recognised by the ",(0,s.kt)("inlineCode",{parentName:"li"},"%")," sign in the command prompt."),(0,s.kt)("li",{parentName:"ul"},"A customised command line, which shows the user and folder on one line, with only the ",(0,s.kt)("inlineCode",{parentName:"li"},"%")," symbol below, to leave lots of space for the input commands",(0,s.kt)("sup",{parentName:"li",id:"fnref-9"},(0,s.kt)("a",{parentName:"sup",href:"#fn-9",className:"footnote-ref"},"9")),".")),(0,s.kt)("h4",{id:"example-bash"},"Example: Bash"),(0,s.kt)("img",{width:"600px",alt:"Example: Bash",src:a(2485).Z}),(0,s.kt)("img",{width:"600px",alt:"Example: Bash Elevated",src:a(2358).Z}),(0,s.kt)("p",null,"In this example, we have:"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"A Linux operating system (Ubuntu 14)"),(0,s.kt)("li",{parentName:"ul"},"The gnome terminal"),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},"bash")," as the shell"),(0,s.kt)("li",{parentName:"ul"},"In the second screenshot, the user has 'root privileges', and to indicate this, ",(0,s.kt)("inlineCode",{parentName:"li"},"bash")," helpfully changes the default command prompt from a dollar sign to a hash sign")),(0,s.kt)("h4",{id:"example-windows-explorer"},"Example: Windows Explorer"),(0,s.kt)("img",{width:"600px",alt:"Example: Windows Explorer",src:a(6025).Z}),(0,s.kt)("p",null,"In this example, we have:"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"The Windows 10 operating system"),(0,s.kt)("li",{parentName:"ul"},"No terminal"),(0,s.kt)("li",{parentName:"ul"},"The ",(0,s.kt)("inlineCode",{parentName:"li"},"explorer.exe")," program showing us a ",(0,s.kt)("em",{parentName:"li"},"graphical")," shell")),(0,s.kt)("p",null,"This looks different from previous examples. The program, which shows the familiar Windows interface, ",(0,s.kt)("inlineCode",{parentName:"p"},"explorer.exe"),", is in fact a shell as well, offering interactive access to the operating system and computer resources. The bulk of the Windows APIs to interact with this interface are in the ",(0,s.kt)("a",{parentName:"p",href:"https://msdn.microsoft.com/en-us/library/windows/desktop/bb773177(v=vs.85).aspx"},"Shell Library"),". I also maintain a popular library for building extensions to the graphical Windows shell - ",(0,s.kt)("a",{parentName:"p",href:"https://github.com/dwmkerr/sharpshell"},"sharpshell"),"."),(0,s.kt)("h4",{id:"example-windows-command-prompt"},"Example: Windows Command Prompt"),(0,s.kt)("img",{width:"600px",alt:"Example: Command Prompt",src:a(2079).Z}),(0,s.kt)("p",null,"In this example, we have:"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"The Windows 10 operating system"),(0,s.kt)("li",{parentName:"ul"},"The command prompt terminal and shell")),(0,s.kt)("p",null,"In Windows, the terminal and shell are combined into a single ",(0,s.kt)("inlineCode",{parentName:"p"},"cmd.exe")," program. There's an excellent article on the internals - ",(0,s.kt)("a",{parentName:"p",href:"https://devblogs.microsoft.com/commandline/windows-command-line-inside-the-windows-console/"},"Microsoft DevBlogs: Windows Command-Line: Inside the Windows Console")),(0,s.kt)("h4",{id:"example-windows-powershell"},"Example: Windows PowerShell"),(0,s.kt)("img",{width:"600px",alt:"Example: Windows Powershell",src:a(5142).Z}),(0,s.kt)("p",null,"In this example, we have:"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"The Windows 10 operating system"),(0,s.kt)("li",{parentName:"ul"},"The PowerShell terminal")),(0,s.kt)("p",null,"PowerShell is an improvement on the 'command prompt' program that was originally used in Windows, offering much more functionality for scripting and other modern shell features."),(0,s.kt)("h4",{id:"example-windows-subsystem-for-linux-wsl"},"Example: Windows Subsystem for Linux (WSL)"),(0,s.kt)("img",{width:"600px",alt:"Example: WSL",src:a(9089).Z}),(0,s.kt)("p",null,"In this example, we have:"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"The Windows 10 operating system"),(0,s.kt)("li",{parentName:"ul"},"The ",(0,s.kt)("inlineCode",{parentName:"li"},"Bash.exe")," program")),(0,s.kt)("p",null,"This screenshot, from ",(0,s.kt)("a",{parentName:"p",href:"https://docs.microsoft.com/en-us/windows/wsl/faq"},"MSDN: Frequently Asked Questions about Windows Subsystem for Linux")," shows Bash running in Windows. This is a relatively new feature at the time of writing, allowing Windows users to use a Linux interface to the PC. This is a feature that may become increasingly valuable, as in general it is challenging to write shell code that can run on Windows and Unix-like systems."),(0,s.kt)("h2",{id:"share-and-discuss"},"Share and Discuss"),(0,s.kt)("p",null,"If you enjoyed this article, please do share it! Feel free to include suggestions, improvements or corrections in the comments below."),(0,s.kt)("hr",null),(0,s.kt)("p",null,(0,s.kt)("strong",{parentName:"p"},"Useful References")),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"A simple Linux kernel module, showing how basic kernel programming works in Linux: ",(0,s.kt)("a",{parentName:"li",href:"https://github.com/dwmkerr/linux-kernel-module"},"github.com/dwmkerr/linux-kernel-module")),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("a",{parentName:"li",href:"https://www.amazon.com/How-Linux-Works-2nd-Superuser/dp/1593275676"},"How Linux Works - Brian Ward")),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("a",{parentName:"li",href:"https://unix.stackexchange.com/questions/4126/what-is-the-exact-difference-between-a-terminal-a-shell-a-tty-and-a-con/4132"},"StackExchange: What is the exact difference between a 'terminal', a 'shell', a 'tty', and a console?")),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("a",{parentName:"li",href:"https://devblogs.microsoft.com/commandline/windows-command-line-inside-the-windows-console/"},"Microsoft: Inside the Windows Console"))),(0,s.kt)("hr",null),(0,s.kt)("p",null,(0,s.kt)("strong",{parentName:"p"},"Footnotes")),(0,s.kt)("div",{className:"footnotes"},(0,s.kt)("hr",{parentName:"div"}),(0,s.kt)("ol",{parentName:"div"},(0,s.kt)("li",{parentName:"ol",id:"fn-1"},"CPU: central processing unit. This is the chip in the computer that does most of the work (which after many layers of abstraction eventually becomes arithmetic and sending simple instructions to other places).",(0,s.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-2"},"Memory is the 'working space' where the state of your system is stored. If you are writing a document, the text lives in memory, until you save it, when it then gets written to a hard drive. Memory is ",(0,s.kt)("em",{parentName:"li"},"ephemeral")," - everything is gone when you turn off the power to it.",(0,s.kt)("a",{parentName:"li",href:"#fnref-2",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-3"},"This is the part of your computer that knows how to do things like connect to a WiFi network, or has a network socket you might plug a network cable into.",(0,s.kt)("a",{parentName:"li",href:"#fnref-3",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-4"},"This is the part of your computer you plug the screen into.",(0,s.kt)("a",{parentName:"li",href:"#fnref-4",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-5"},"This is because a mistake in ",(0,s.kt)("em",{parentName:"li"},"Kernel Mode")," programs can have disastrous effects. It could access any files, no matter who they belong do, control the hardware, install more software - almost anything. Errors in this code can cause terrible issues (like the infamous Windows 'blue screen of death'), and malicious code in the kernel essentially has full access to not only all your data but also your webcam, network adapter and so on.",(0,s.kt)("a",{parentName:"li",href:"#fnref-5",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-6"},"As an aside, if you are curious about the visual style of my setup or customisations that have been made, everything in my setup is available online on my 'dotfiles' repo - ",(0,s.kt)("a",{parentName:"li",href:"https://github.com/dwmkerr/dotfiles"},"github.com/dwmkerr/dotfiles"),".",(0,s.kt)("a",{parentName:"li",href:"#fnref-6",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-7"},"And that's where the 'TTY' acronym you will see sometimes comes from. Enter the ",(0,s.kt)("inlineCode",{parentName:"li"},"ps")," command, and you'll actually see the TTY interface each process is attached to. This is a topic that will come up later in the series.",(0,s.kt)("a",{parentName:"li",href:"#fnref-7",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-8"},(0,s.kt)("inlineCode",{parentName:"li"},"$$")," is a Bash ",(0,s.kt)("a",{parentName:"li",href:"https://www.tldp.org/LDP/abs/html/internalvariables.html#PROCCID"},"internal variable"),". These will also be covered in a later article in the series.",(0,s.kt)("a",{parentName:"li",href:"#fnref-8",className:"footnote-backref"},"\u21a9")),(0,s.kt)("li",{parentName:"ol",id:"fn-9"},"Feel free to see my ",(0,s.kt)("a",{parentName:"li",href:"https://github.com/dwmkerr/dotfiles"},"dotfiles")," to configure a similar setup for yourself.",(0,s.kt)("a",{parentName:"li",href:"#fnref-9",className:"footnote-backref"},"\u21a9")))))}p.isMDXComponent=!0},620:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/diagram1-operating-system-3da3b52f3ef73f3e08db4eaa1fd7d8c7.png"},279:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/diagram2-the-kernel-and-user-space-48ea8009fa452ddb13745e5747286c4f.png"},5593:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/diagram3-terminal-and-shell-31620f593a4c3838051a5a6dcea17577.png"},9152:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/diagram4-command-prompt-d1a46f63387ec11ebeb2cebb76b30696.png"},2358:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/example-bash-root-073a27146aa9bfbbcafc6464d071fa46.png"},2485:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/example-bash-c0f87d553dded0884b96787b2a631d94.png"},2079:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/example-cmd-899ba21f4f211c9670c85bf64cb8d426.png"},6025:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/example-explorer-086bee5a143984e5a134d1b73dcc3d3f.png"},4637:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/example-iterm-zsh-0410a3717ef4f0e5b437ae51e1d62d2d.png"},5142:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/example-powershell-ca476a5744e3e0feb12489ea113ad238.png"},9089:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/example-wsl-d00882cbdb7343c88834a9a3df92ae84.png"},1721:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/image-asr-33-989ef8e0dc1a64b4d00052abeae25e40.jpg"},5752:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/image-ibm3486-1acadd2ab0ee18512fe79316bec727fd.jpg"},1331:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/image-ohmyzsh-a26c1a89776b0966d0577fb6f7e35576.jpg"},7825:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/image-psforest-689f9f137b2e62cfebe51023f8b35202.png"},3794:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/image-walnut-93cb9b0e2b015cbc5de26cd2c4483ecd.jpg"},5071:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/screenshot-shell-c4e71ad86af3edb1ad3a967f78691842.png"},1174:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/screenshot-windows-evolution-8e669f56c5e4ccbff1dbf1c885f6cc2f.png"},1434:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/screenshot1-example-shell-9be2c6d6c9bd91bb97bdf4b8b6ce0046.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/2b0d86ac.6d3c8eaa.js b/pr-preview/pr-346/assets/js/2b0d86ac.6d3c8eaa.js new file mode 100644 index 00000000..80cfde5e --- /dev/null +++ b/pr-preview/pr-346/assets/js/2b0d86ac.6d3c8eaa.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[95],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t =0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,s=e.originalType,l=e.parentName,d=o(e,["components","mdxType","originalType","parentName"]),c=p(n),u=i,m=c["".concat(l,".").concat(u)]||c[u]||h[u]||s;return n?a.createElement(m,r(r({ref:t},d),{},{components:n})):a.createElement(m,r({ref:t},d))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var s=n.length,r=new Array(s);r[0]=u;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o[c]="string"==typeof e?e:i,r[1]=o;for(var p=2;p {n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>c,frontMatter:()=>s,metadata:()=>o,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const s={title:"Advanced Text Manipulation",slug:"/part-3-manipulating-text/advanced-text-manipulation/"},r=void 0,o={unversionedId:"manipulating-text/advanced-text-manipulation/index",id:"manipulating-text/advanced-text-manipulation/index",title:"Advanced Text Manipulation",description:"In Chapter 15 we introduced some simple commands to work with text - specifically head, tail, tr and cut. Now we are going to introduce sed the Stream Editor command.",source:"@site/docs/03-manipulating-text/16-advanced-text-manipulation/index.md",sourceDirName:"03-manipulating-text/16-advanced-text-manipulation",slug:"/part-3-manipulating-text/advanced-text-manipulation/",permalink:"/part-3-manipulating-text/advanced-text-manipulation/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/03-manipulating-text/16-advanced-text-manipulation/index.md",tags:[],version:"current",frontMatter:{title:"Advanced Text Manipulation",slug:"/part-3-manipulating-text/advanced-text-manipulation/"},sidebar:"sidebar",previous:{title:"Slice and Dice Text",permalink:"/part-3-manipulating-text/slice-and-dice-text/"},next:{title:"Build Commands on the Fly",permalink:"/part-3-manipulating-text/build-commands-on-the-fly/"}},l={},p=[{value:"Introducing the Stream Editor",id:"introducing-the-stream-editor",level:2},{value:"Transformations with Sed",id:"transformations-with-sed",level:2},{value:"Replacing Text",id:"replacing-text",level:3},{value:"Applying Multiple Expressions",id:"applying-multiple-expressions",level:3},{value:"Replacing Text on Specific Lines",id:"replacing-text-on-specific-lines",level:4},{value:"Removing Parts of a Line",id:"removing-parts-of-a-line",level:4},{value:"Stripping Comments",id:"stripping-comments",level:3},{value:"Appending Text",id:"appending-text",level:3},{value:"Prepending Text",id:"prepending-text",level:3},{value:"Extracting Information",id:"extracting-information",level:3},{value:"Advanced - Surrounding Parts of Text with Quotes",id:"advanced---surrounding-parts-of-text-with-quotes",level:3},{value:"Advanced - Template Files",id:"advanced---template-files",level:3},{value:"What About 'In Place' Editing?",id:"what-about-in-place-editing",level:3},{value:"What about Awk?",id:"what-about-awk",level:2},{value:"When to Program",id:"when-to-program",level:2},{value:"Summary",id:"summary",level:2}],d={toc:p};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"In ",(0,i.kt)("a",{parentName:"p",href:"../../part-3-manipulating-text/slice-and-dice-text"},"Chapter 15")," we introduced some simple commands to work with text - specifically ",(0,i.kt)("inlineCode",{parentName:"p"},"head"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"tail"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"cut"),". Now we are going to introduce ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," the ",(0,i.kt)("em",{parentName:"p"},"Stream Editor")," command. "),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"sed")," can be used to perform a variety of tasks with text. In many cases a small command involving ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," can quickly solve problems. We'll look at some of the common ways to use ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),", some more sophisticated examples and discuss when you might want to consider using alternative tools like ",(0,i.kt)("inlineCode",{parentName:"p"},"awk")," or a programming language."),(0,i.kt)("h2",{id:"introducing-the-stream-editor"},"Introducing the Stream Editor"),(0,i.kt)("p",null,"The stream editor command takes input from a ",(0,i.kt)("em",{parentName:"p"},"stream")," - which in many cases will simply be a file. It then performs operations on the text as it is read, and writes the output to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),". This command is ",(0,i.kt)("em",{parentName:"p"},"extremely")," powerful - there are many sophisticated transformations which can be applied."),(0,i.kt)("p",null,"Seeing ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," commands can be a little intimidating - they can look complex! But we'll start with the basics and hopefully you'll see just how effective this tool can make you!"),(0,i.kt)("h2",{id:"transformations-with-sed"},"Transformations with Sed"),(0,i.kt)("p",null,"Rather than dissecting each and every option or flag and every nuance of the program, I'm going to try and show some real world examples of how you can use ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),". This will allow us to see the functionality in easier to digest chunks. It also keeps things practical! Let's dive in and look at some common tasks and how we can solve them with ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),"."),(0,i.kt)("admonition",{title:"Downloading the Samples",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Run the following command in your shell to download the samples:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"curl effective.sh | sh\n"))),(0,i.kt)("h3",{id:"replacing-text"},"Replacing Text"),(0,i.kt)("p",null,"The samples folder has a script which copies some configuration files to a backup folder. Let's take a quick look at it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cd scripts\n$ cat backup-config.sh\n\n#!/usr/bin/env bash\n\n# Make sure we have a backup directory.\nmkdir ~/backup\n\n# Copy over alicloud, aws, azure, gcp and ssh config and credentials.\ncp ~/.aliyun/config.json ~/backup/settings/aliyun/\ncp ~/.aws/config ~/backup/settings/aws/\ncp ~/.aws/credentials ~/backup/settings/aws/\ncp ~/.azure/config ~/backup/settings/azure/\ncp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/\ncp ~/.ssh/config ~/backup/settings/ssh/\ncp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?\ncp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/\n")),(0,i.kt)("p",null,"We could use the ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," command to change the name of the folder we backup to. If we wanted to change the ",(0,i.kt)("inlineCode",{parentName:"p"},"settings")," folder to ",(0,i.kt)("inlineCode",{parentName:"p"},"configuration")," we could run the command below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed 's/settings/configuration/' backup-config.sh\n#!/usr/bin/env bash\n\n# Copy over alicloud, aws, azure, gcp and ssh config and credentials.\ncp ~/.aliyun/config.json ~/backup/configuration/aliyun/\ncp ~/.aws/config ~/backup/configuration/aws/\ncp ~/.aws/credentials ~/backup/configuration/aws/\ncp ~/.azure/config ~/backup/configuration/azure/\ncp ~/.config/gcloud/credentials.db ~/backup/configuration/gcloud/\ncp ~/.ssh/config ~/backup/configuration/ssh/\ncp ~/.ssh/id_rsa ~/backup/configuration/ssh/ # is this safe?\ncp ~/.ssh/id_rsa.pub ~/backup/configuration/ssh/\n")),(0,i.kt)("p",null,"This shows the basics of how ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," works. We give ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," an ",(0,i.kt)("em",{parentName:"p"},"expression"),", which describes a set of operations we want to perform and it then applies that expression to the file we specify. As with most commands we've seen it can apply the expression to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin"),"."),(0,i.kt)("p",null,"Let's look at the expression in detail:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"s/settings/dotfiles/\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"s")," indicates that we are going to run the ",(0,i.kt)("em",{parentName:"li"},"substitute")," function, which is used to replace text"),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"/")," indicates the start of the ",(0,i.kt)("em",{parentName:"li"},"pattern")," we are searching for..."),(0,i.kt)("li",{parentName:"ul"},"...",(0,i.kt)("inlineCode",{parentName:"li"},"settings")," is the pattern - which is just the literal text 'settings'"),(0,i.kt)("li",{parentName:"ul"},"The second ",(0,i.kt)("inlineCode",{parentName:"li"},"/")," indicates the start of the ",(0,i.kt)("em",{parentName:"li"},"replacement")," we will make when the ",(0,i.kt)("em",{parentName:"li"},"pattern")," is found"),(0,i.kt)("li",{parentName:"ul"},"The final ",(0,i.kt)("inlineCode",{parentName:"li"},"/")," indicates the end of the replacement - we can also optionally put ",(0,i.kt)("em",{parentName:"li"},"flags")," after this slash")),(0,i.kt)("p",null,"Just as we saw in ",(0,i.kt)("a",{parentName:"p",href:"../../part-3-manipulating-text/get-to-grips-with-grep"},"Chapter 13 - Get to Grips with Grep"),", we can provide a ",(0,i.kt)("em",{parentName:"p"},"regular expression")," as the pattern to search for. By default, ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," will use ",(0,i.kt)("em",{parentName:"p"},"basic")," regular expressions. We can use ",(0,i.kt)("em",{parentName:"p"},"extended regular expressions")," by providing the ",(0,i.kt)("inlineCode",{parentName:"p"},"-E")," flag."),(0,i.kt)("admonition",{title:"Basic and Extended Regular Expressions",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"If you are not sure about the difference between Basic and Extended Regular Expressions, you can use the:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},"man re_pattern\n")),(0,i.kt)("p",{parentName:"admonition"},"Command to get detailed documentation.")),(0,i.kt)("h3",{id:"applying-multiple-expressions"},"Applying Multiple Expressions"),(0,i.kt)("p",null,"We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-e")," flag to supply multiple expressions to ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),"."),(0,i.kt)("p",null,"If we wanted to delete our backup folders, we could run two substitutions - one which changes the ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," command to ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir")," command and one which deletes the first parameter, which was the source directory for ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," but is not needed for ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir"),(0,i.kt)("sup",{parentName:"p",id:"fnref-1"},(0,i.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),"."),(0,i.kt)("h4",{id:"replacing-text-on-specific-lines"},"Replacing Text on Specific Lines"),(0,i.kt)("p",null,"Let's start with the first expression, which should change ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -e 's/cp/rmdir/' backup-config.sh\n#!/usr/bin/env bash\n\n# Make sure we have a backup directory.\nmkdir ~/backup\n\n# Copy over alicloud, aws, azure, grmdir and ssh config and credentials.\nrmdir ~/.aliyun/config.json ~/backup/settings/aliyun/\nrmdir ~/.aws/config ~/backup/settings/aws/\nrmdir ~/.aws/credentials ~/backup/settings/aws/\nrmdir ~/.azure/config ~/backup/settings/azure/\nrmdir ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/\nrmdir ~/.ssh/config ~/backup/settings/ssh/\nrmdir ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?\nrmdir ~/.ssh/id_rsa.pub ~/backup/settings/ssh/\n")),(0,i.kt)("p",null,"We have provided a single expression with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-e")," parameter. If you are providing a single expression only then this parameter is superfluous, but we will shortly add another expression."),(0,i.kt)("p",null,"This has changed all of the ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," commands to ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir"),". But did you notice the bug? It has also changed the letters ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," in the comment which mentions ",(0,i.kt)("inlineCode",{parentName:"p"},"gcp")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir"),", meaning our comment line has changed. I've made the changes bold to make this easier to see below:Before, it was:"),(0,i.kt)("pre",null,"# Copy over alicloud, aws, azure, g",(0,i.kt)("strong",null,"cp")," and ssh config and credentials."),(0,i.kt)("p",null,"Now it is:"),(0,i.kt)("pre",null,"# Copy over alicloud, aws, azure, g",(0,i.kt)("strong",null,"rmdir")," and ssh config and credentials."),(0,i.kt)("p",null,"This isn't the worst bug in the world, but we can do better. Let's change the expression to only run on lines which start with the letters ",(0,i.kt)("inlineCode",{parentName:"p"},"cp"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -e '/^cp/s/cp/rmdir/' backup-config.sh\n#!/usr/bin/env bash\n\n# Make sure we have a backup directory.\nmkdir ~/backup\n\n# Copy over alicloud, aws, azure, gcp and ssh config and credentials.\nrmdir ~/.aliyun/config.json ~/backup/settings/aliyun/\nrmdir ~/.aws/config ~/backup/settings/aws/\nrmdir ~/.aws/credentials ~/backup/settings/aws/\nrmdir ~/.azure/config ~/backup/settings/azure/\nrmdir ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/\nrmdir ~/.ssh/config ~/backup/settings/ssh/\nrmdir ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?\nrmdir ~/.ssh/id_rsa.pub ~/backup/settings/ssh/\n")),(0,i.kt)("p",null,"Let's look at the expression in detail. Before the ",(0,i.kt)("inlineCode",{parentName:"p"},"s")," symbol, which indicates that we are performing a ",(0,i.kt)("em",{parentName:"p"},"substitution"),", we now include a ",(0,i.kt)("em",{parentName:"p"},"line pattern"),". A line pattern tells ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," which lines it should apply the expression to. Our line pattern is just ",(0,i.kt)("inlineCode",{parentName:"p"},"^cp"),', which is the regular expression meaning "any line which starts with ',(0,i.kt)("inlineCode",{parentName:"p"},"cp"),'".'),(0,i.kt)("p",null,"Line patterns can be quite sophisticated and are covered in the box later on in this chapter!"),(0,i.kt)("p",null,"So all in all:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"^cp"),"- use the line pattern ",(0,i.kt)("inlineCode",{parentName:"li"},"^cp")," (lines which start with ",(0,i.kt)("inlineCode",{parentName:"li"},"cp"),") for the expression"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"s")," - use the substitution function (which replaces text)"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cp")," - find the pattern ",(0,i.kt)("inlineCode",{parentName:"li"},"cp")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"rmdir")," - replace each occurrence with ",(0,i.kt)("inlineCode",{parentName:"li"},"rmdir"),"\non lines which start with the text ",(0,i.kt)("inlineCode",{parentName:"li"},"cp"),", replace any occurrences of the text text ",(0,i.kt)("inlineCode",{parentName:"li"},"cp")," with the text ",(0,i.kt)("inlineCode",{parentName:"li"},"rmdir"))),(0,i.kt)("p",null,"We combine these parts together using a ",(0,i.kt)("inlineCode",{parentName:"p"},"/")," symbol, this is needed so that ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," knows where the line pattern starts and end, the function starts and end and so on",(0,i.kt)("sup",{parentName:"p",id:"fnref-2"},(0,i.kt)("a",{parentName:"sup",href:"#fn-2",className:"footnote-ref"},"2")),"."),(0,i.kt)("h4",{id:"removing-parts-of-a-line"},"Removing Parts of a Line"),(0,i.kt)("p",null,"Now we need to remove the first parameter for the ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir")," command, it was used when we had ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," as the command but doesn't make sense any more."),(0,i.kt)("p",null,"Let's apply a new expression:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -E -e '/^cp/s/cp/rmdir/' -e '/^rmdir/s/~[^ ]+ //' backup-config.sh\n#!/usr/bin/env bash\n\n# Make sure we have a backup directory.\nmkdir ~/backup\n\n# Copy over alicloud, aws, azure, gcp and ssh config and credentials.\nrmdir ~/backup/settings/aliyun/\nrmdir ~/backup/settings/aws/\nrmdir ~/backup/settings/aws/\nrmdir ~/backup/settings/azure/\nrmdir ~/backup/settings/gcloud/\nrmdir ~/backup/settings/ssh/\nrmdir ~/backup/settings/ssh/ # is this safe?\nrmdir ~/backup/settings/ssh/\n")),(0,i.kt)("p",null,"Let's take a look at the second expression, component by component:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"^rmdir")," - use a line pattern which matches lines which start with ",(0,i.kt)("inlineCode",{parentName:"li"},"rmdir")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"s")," - use the ",(0,i.kt)("em",{parentName:"li"},"substitution")," function to replace text"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"~[^ ]+ ")," - search for the tilde ",(0,i.kt)("inlineCode",{parentName:"li"},"~")," symbol and any non-space characters, up until the first space character")),(0,i.kt)("p",null,"We actually don't even include a replacement - which is why the expression ends with ",(0,i.kt)("inlineCode",{parentName:"p"},"//")," - the inside of the replacement part of the expression is empty. This means we search for a tilde ",(0,i.kt)("inlineCode",{parentName:"p"},"~"),", match everything up until the next space ",(0,i.kt)("inlineCode",{parentName:"p"}," ")," and then replace it with nothing - meaning we remove it!"),(0,i.kt)("p",null,"Note as well that we've used the ",(0,i.kt)("inlineCode",{parentName:"p"},"-E")," flag to tell ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," to use ",(0,i.kt)("em",{parentName:"p"},"extended regular expressions"),". This allows us to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"+")," symbol without escaping it with a ",(0,i.kt)("inlineCode",{parentName:"p"},"\\")," first. In general I would recommend always using extended regular expressions as they are more commonly used and if you work with programming languages as well as the shell, those languages will likely use something closer to extended regular expressions rather than basic ones."),(0,i.kt)("p",null,"Let's look at a few other ways to use ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," for some real-world tasks."),(0,i.kt)("h3",{id:"stripping-comments"},"Stripping Comments"),(0,i.kt)("p",null,"In the script we've used above, there are some comments. Let's use ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," to remove them:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -E 's/#.*$//' backup-config.sh\n\n\n\n\nmkdir ~/backup\n\n\ncp ~/.aliyun/config.json ~/backup/settings/aliyun/\ncp ~/.aws/config ~/backup/settings/aws/\ncp ~/.aws/credentials ~/backup/settings/aws/\ncp ~/.azure/config ~/backup/settings/azure/\ncp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/\ncp ~/.ssh/config ~/backup/settings/ssh/\ncp ~/.ssh/id_rsa ~/backup/settings/ssh/\ncp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/\n")),(0,i.kt)("p",null,"Notice how we have removed the comments which started at the beginning of the file, as well as the comment after the ",(0,i.kt)("inlineCode",{parentName:"p"},"cp ~/.ssh/id_rsa")," line. This is because the regular expression matches any hash symbol ",(0,i.kt)("inlineCode",{parentName:"p"},"#")," and strips everything which follows it."),(0,i.kt)("p",null,"What about if we want to get rid of those empty lines? We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"d")," or ",(0,i.kt)("em",{parentName:"p"},"delete")," function:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -E -e 's/#.*$//' -e '/^ *$/d' backup-config.sh\nmkdir ~/backup\ncp ~/.aliyun/config.json ~/backup/settings/aliyun/\ncp ~/.aws/config ~/backup/settings/aws/\ncp ~/.aws/credentials ~/backup/settings/aws/\ncp ~/.azure/config ~/backup/settings/azure/\ncp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/\ncp ~/.ssh/config ~/backup/settings/ssh/\ncp ~/.ssh/id_rsa ~/backup/settings/ssh/\ncp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/\n")),(0,i.kt)("p",null,"The delete command is applied to all lines which match the ",(0,i.kt)("em",{parentName:"p"},"line pattern"),". In this case, the line pattern is a regular expression, ",(0,i.kt)("inlineCode",{parentName:"p"},"^ *$"),", which matches any line made up only of space characters (including zero space characters, i.e. empty lines)."),(0,i.kt)("admonition",{title:"Line Patterns",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Line patterns, which can be used on many ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," functions, are actually quite sophisticated. Here are a few examples:"),(0,i.kt)("ul",{parentName:"admonition"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"/test/")," - any line matching the pattern ",(0,i.kt)("inlineCode",{parentName:"li"},"test")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"/test/!")," - any line ",(0,i.kt)("em",{parentName:"li"},"not")," matching ",(0,i.kt)("inlineCode",{parentName:"li"},"test")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"6")," - line six"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"$")," - the last line"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"1-10")," - lines one to ten"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"1-10!")," - lines ",(0,i.kt)("em",{parentName:"li"},"except _one")," to ten")),(0,i.kt)("p",{parentName:"admonition"},"Check ",(0,i.kt)("inlineCode",{parentName:"p"},"man sed")," to see more about line patterns.")),(0,i.kt)("h3",{id:"appending-text"},"Appending Text"),(0,i.kt)("p",null,"In a regular expression the dollar-sign ",(0,i.kt)("inlineCode",{parentName:"p"},"$")," symbol represents the end of a line."),(0,i.kt)("p",null,"We can use this symbol to add content to the end of lines - we just search for ",(0,i.kt)("inlineCode",{parentName:"p"},"$")," and replace it with whatever we want to end the line with! And if we want to only do this on certain lines, we can use a line pattern to limit where we apply the expression."),(0,i.kt)("p",null,"Here's how we can make sure that the ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," command in the script doesn't fail:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -E -e '/^cp/s/$/ || true/' backup-config.sh\n\n#!/usr/bin/env bash\n\n# Make sure we have a backup directory.\nmkdir ~/backup\n\n# Copy over alicloud, aws, azure, gcp and ssh config and credentials.\ncp ~/.aliyun/config.json ~/backup/settings/aliyun/ || true\ncp ~/.aws/config ~/backup/settings/aws/ || true\ncp ~/.aws/credentials ~/backup/settings/aws/ || true\ncp ~/.azure/config ~/backup/settings/azure/ || true\ncp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/ || true\ncp ~/.ssh/config ~/backup/settings/ssh/ || true\ncp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe? || true\ncp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ || true\n")),(0,i.kt)("p",null,"Now we have a command which strips comments, deletes empty lines and then adds the text ",(0,i.kt)("inlineCode",{parentName:"p"}," || true")," to the end of the line. This little trick ensures that the script won't fail if one of the individual ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," commands fails. We'll see more about shell scripts later."),(0,i.kt)("p",null,"What if we want to add a semicolon to the end of all lines?"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sed 's/$/;/'\n")),(0,i.kt)("p",null,"Easy!"),(0,i.kt)("h3",{id:"prepending-text"},"Prepending Text"),(0,i.kt)("p",null,"In a regular expression the caret ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," symbol represents the start of a line."),(0,i.kt)("p",null,"We can apply the same trick as with the dollar-sign ",(0,i.kt)("inlineCode",{parentName:"p"},"$")," symbol to add text to the start of a line - we just replace ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," with whatever we want the line to start with."),(0,i.kt)("p",null,"Here's how we can use this trick!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ sed -E -e \'/^cp/s/$/"/\' -e \'/"$/s/^/echo "/\' backup-config.sh\n#!/usr/bin/env bash\n\n# Make sure we have a backup directory.\nmkdir ~/backup\n\n# Copy over alicloud, aws, azure, gcp and ssh config and credentials.\necho "cp ~/.aliyun/config.json ~/backup/settings/aliyun/"\necho "cp ~/.aws/config ~/backup/settings/aws/"\necho "cp ~/.aws/credentials ~/backup/settings/aws/"\necho "cp ~/.azure/config ~/backup/settings/azure/"\necho "cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/"\necho "cp ~/.ssh/config ~/backup/settings/ssh/"\necho "cp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?"\necho "cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/"\n')),(0,i.kt)("p",null,"We first put at quote at the end of each line which starts with ",(0,i.kt)("inlineCode",{parentName:"p"},"cp"),", then put ",(0,i.kt)("inlineCode",{parentName:"p"},'echo "')," at the beginning of each line which ends with a quote!"),(0,i.kt)("p",null,"This has now tweaked our script so that it doesn't actually copy the files, it just prints out the commands to the screen. It also demonstrates how the order of the expressions is important, the second expression is applied after the first. If you are using multiple expressions be careful that an earlier expression doesn't alter the line in a way which breaks the next expression!"),(0,i.kt)("h3",{id:"extracting-information"},"Extracting Information"),(0,i.kt)("p",null,"What about if we want to ",(0,i.kt)("em",{parentName:"p"},"extract")," some information from lines in a file?"),(0,i.kt)("p",null,"Let's take a quick look at our movies file in our data folder for reference:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ cd ~/effective-shell/data\n$ head -n 3 top100.csv\n\n"Rank","Rating","Title","Reviews"\n"1","97","Black Panther (2018)","515"\n"2","94","Avengers: Endgame (2019)","531"\n')),(0,i.kt)("p",null,"It should be easy to create a regular expression to find the year for each movie - it would just have to match all numeric values between brackets. Let's try it out!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ head -n 3 data/top100.csv | sed -E \'s/.*\\(([0-9]+)\\).*/\\1/\' \n\n"Rank","Rating","Title","Reviews"\n2018\n2019\n')),(0,i.kt)("p",null,"This works because we match any text, then capture any digits enclosed in brackets, then replace with the ",(0,i.kt)("inlineCode",{parentName:"p"},"\\1")," text, which means 'the first match'."),(0,i.kt)("p",null,"Given what we know about ",(0,i.kt)("em",{parentName:"p"},"line patterns"),", we can also easily exclude the first line:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -E -e '1d' -e 's/.*\\(([0-9]+)\\).*/\\1/' data/top100.csv \n\n2018\n2019\n...\n")),(0,i.kt)("p",null,"Here we have just added the ",(0,i.kt)("inlineCode",{parentName:"p"},"1d")," expression - delete the first line."),(0,i.kt)("h3",{id:"advanced---surrounding-parts-of-text-with-quotes"},"Advanced - Surrounding Parts of Text with Quotes"),(0,i.kt)("p",null,"Let's look at another example. Here we have some ",(0,i.kt)("inlineCode",{parentName:"p"},"yaml")," content, a set of keys and values. Note that some of the values are quoted, and some are not:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'title: "Advanced Text Manipulation"\nslug: advanced-text-manipulation\nweight: 14\n')),(0,i.kt)("p",null,"In fact this is the text content at the top of the file I am working on now. But this file is not a YAML file, it is a Markdown file (which is fairly close to plain text), so also has a lot of other content in it."),(0,i.kt)("p",null,"First, let's see if we can extract the keys:"),(0,i.kt)("pre",null,"$ grep -E '[^:]+:' ~/effective-shell/docs/chapter12.md",(0,i.kt)("strong",null,"title"),': "Advanced Text Manipulation"',(0,i.kt)("strong",null,"slug"),": advanced-text-manipulation",(0,i.kt)("strong",null,"weight"),": 14",(0,i.kt)("strong",null,"Let's say we have a script which is used to backup some local files to an Amazon S3 bucket. We can see a script like this here: We could use the `sed` command to change the S3 bucket. For example, if we wanted to change the `settings` text to `dotfiles` we could run the command below: Let's look at the expression in detail:"),"...snip..."),(0,i.kt)("p",null,"Well this kind of worked. The first three matches were correct, but we then found lots of other text. It looks like our pattern is not correct."),(0,i.kt)("p",null,"The pattern is ",(0,i.kt)("inlineCode",{parentName:"p"},"[^:]+:"),", which means 'at least one character which is ",(0,i.kt)("em",{parentName:"p"},"not")," the ",(0,i.kt)("inlineCode",{parentName:"p"},":")," character, followed by ",(0,i.kt)("inlineCode",{parentName:"p"},":"),"."),(0,i.kt)("p",null,"We can improve on it by telling it to not include spaces before the colon, and be explicit that this pattern we are searching for must be at the start of the line:"),(0,i.kt)("pre",null,"% grep -E '^[^: ]+:' ~/effective-shell/docs/chapter12.md",(0,i.kt)("strong",null,"title"),': "Advanced Text Manipulation"',(0,i.kt)("strong",null,"slug"),": advanced-text-manipulation",(0,i.kt)("strong",null,"weight"),": 14",(0,i.kt)("strong",null,'"2","94","Avengers'),': Endgame (2019)","531"'),(0,i.kt)("p",null,"Much better. The pattern now starts with ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," meaning 'start of line', and the type of characters we search for ",(0,i.kt)("inlineCode",{parentName:"p"},"[^: ]")," is not 'anything which is not a colon or space. But we've also found a film title from the text - we can improve our pattern further by eliminating quotes, which are not valid for YAML keys anyway:"),(0,i.kt)("pre",null,"$ grep -E '^[^: \"]+:' ~/effective-shell/docs/chapter12.md",(0,i.kt)("strong",null,"title"),': "Advanced Text Manipulation"',(0,i.kt)("strong",null,"slug"),": advanced-text-manipulation",(0,i.kt)("strong",null,"weight"),": 14"),(0,i.kt)("p",null,"Awesome!"),(0,i.kt)("admonition",{title:"Building Regular Expressions",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"I was a hold out for years, but regular expressions are ",(0,i.kt)("em",{parentName:"p"},"incredibly useful")," if you take the time to learn them. Exercises like this are a great way to do it. Start simple, add the elements you need bit by bit. It's a great way to learn exactly what each element does."),(0,i.kt)("p",{parentName:"admonition"},"Avoid just searching online for the perfect expression - they'll often be very long (because they are bullet-proof and cover every possible edge case hopefully). If you are building an expression which is critical to get right, then do search online for help if you need it, but for day to day tasks, practicing like this will really help.")),(0,i.kt)("p",null,"Now let's start using this pattern in ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),". Let's print all lines which match the pattern:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ sed -E -n \'/^[^: "]+:/p\' ~/effective-shell/docs/chapter12.md\ntitle: "Advanced Text Manipulation"\nslug: advanced-text-manipulation\nweight: 14\n')),(0,i.kt)("p",null,"Now there are two critical things we've added to ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," here while we're working. The first is the ",(0,i.kt)("inlineCode",{parentName:"p"},"-n")," flag, which means ",(0,i.kt)("em",{parentName:"p"},"no automatic printing")," - meaning it will show no output unless told. Then in the pattern, we finish the command with ",(0,i.kt)("inlineCode",{parentName:"p"},"p"),", meaning 'print lines which match the pattern'."),(0,i.kt)("p",null,"These options make ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," behave like ",(0,i.kt)("inlineCode",{parentName:"p"},"grep"),", which is useful because we are still building the command."),(0,i.kt)("p",null,"Now let's actually quote the result. To do that, we need to find lines where the ",(0,i.kt)("em",{parentName:"p"},"value")," is not already quoted - so let's add that to our pattern:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -E -n '/^[^: \"]+: +[^\"]+$/p' ~/effective-shell/docs/chapter12.md\nslug: advanced-text-manipulation\nweight: 14\n")),(0,i.kt)("p",null,"Our pattern now reads like this:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},'^[^ :"]+:')," - match the start of a line, any characters which are not space, colon or quote, which then finish with a colon and a space."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},' +[^"]+$')," - match at least one space, then any set of characters which don't have a quote in them all the way to the end of the line.")),(0,i.kt)("p",null,"The pattern is working - its found our two unquoted keys. Now let's get it to print the substitution."),(0,i.kt)("p",null,"First, we're going to surround the key part and value part in brackets - this will make them 'capture groups' - chunks of text we can use in the substitution. Here's how capture groups work:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ sed -E -n \'s/(^[^: "]+:)( +[^"]+$)/Key is "\\1"/p\' ~/effective-shell/docs/chapter12.md\nKey is "slug:"\nKey is "weight:"\n')),(0,i.kt)("p",null,"We are now not just searching for a pattern, we're using the ",(0,i.kt)("inlineCode",{parentName:"p"},"s")," (",(0,i.kt)("em",{parentName:"p"},"substitute"),") function to replace all of the matched text with ",(0,i.kt)("inlineCode",{parentName:"p"},'Key is "\\1"'),". The ",(0,i.kt)("inlineCode",{parentName:"p"},"\\1")," just means 'what you found in the first capture group\"."),(0,i.kt)("p",null,"We could just as easily show the value:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ sed -E -n \'s/(^[^: "]+:)( +[^"]+$)/Value is "\\2"/p\' ~/effective-shell/docs/chapter12.md\nValue is " advanced-text-manipulation"\nValue is " 14"\n')),(0,i.kt)("p",null,"Here we're printing the second capture group. Now you might have noticed that in the key, we're including the colon, and in the value, we're including the space (or spaces). This isn't strictly right - they're the separators. So let's capture them separately:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ sed -E -n \'s/(^[^: "]+)(: +)([^"]+$)/Key "\\1", Value "\\3", Separator "\\2"/p\' ~/effective-shell/docs/chapter12.md\nKey "slug", Value "advanced-text-manipulation", Separator ": "\nKey "weight", Value "14", Separator ": "\n')),(0,i.kt)("p",null,"I think this is really starting to show just how powerful ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," and regular expressions are."),(0,i.kt)("p",null,"Let's finally tie it together - add quotes around unquoted values:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ sed -E -n \'s/(^[^: "]+)(: +)([^"]+$)/\\1\\2"\\3"/p\' ~/effective-shell/docs/chapter12.md\nslug: "advanced-text-manipulation"\nweight: "14"\n')),(0,i.kt)("p",null,"Awesome!"),(0,i.kt)("p",null,"If we wanted to actually change the file, we could remove the ",(0,i.kt)("inlineCode",{parentName:"p"},"-n")," flag, so that we write out everything. This makes the ",(0,i.kt)("inlineCode",{parentName:"p"},"p")," option at the end of the substitution superfluous:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ sed -E \'s/(^[^: "]+)(: +)([^"]+$)/\\1\\2"\\3"/\' ~/effective-shell/docs/chapter12.md > updated.md\n')),(0,i.kt)("p",null,"Let's peep at the top of the file we created to see if it looks right:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ head -n 10 updated.md\n---\ntitle: "Advanced Text Manipulation"\nslug: "advanced-text-manipulation"\nweight: "15"\n---\n\n# Chapter 15 - Advanced Text Manipulation\n\nIn [Chapter 14](../../part-3-manipulating-text/slice-and-dice-text) we introduced some simple commands to work with text - specifically `head`, `tail`, `tr` and `cut`. Now we are going to take a look at how we can perform more sophisticated tasks with text.\n')),(0,i.kt)("p",null,"Impressive - we've found a very specific pattern in a large file, substituted to match what we need and then saved the results."),(0,i.kt)("p",null,"This is just scratching the surface - but even with these basic tools, there is an incredible amount you can do. For example, what about if we didn't want to quote values which are just numbers? We'd just change the pattern from:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -E -n '/(^[^: \"]+:)( +[^\"0-9]+$)/p' ~/effective-shell/docs/chapter12.md\nslug: advanced-text-manipulation\n")),(0,i.kt)("p",null,"All we've done is changed the value pattern from ",(0,i.kt)("inlineCode",{parentName:"p"},'[^"]')," (anything except quotes) to ",(0,i.kt)("inlineCode",{parentName:"p"},"[^0-9]")," (anything except quotes and digits)."),(0,i.kt)("h3",{id:"advanced---template-files"},"Advanced - Template Files"),(0,i.kt)("p",null,"Here's another example which I have found useful again and again. We can use ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),"'s text replacement capabilities to create a basic templating system."),(0,i.kt)("p",null,"For example, let's say we have the file below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"apiVersion: v1\nkind: Secret\nmetadata:\n name: database-credentials\n namespace: dev\nstringData:\n username: admin\n password: ThisIsVerySensitive!\n")),(0,i.kt)("p",null,"This the definition of a 'secret' for Kubernetes. It doesn't really matter how the file is structured because what we'll do in this example could work for any file."),(0,i.kt)("p",null,"Let's say we want to be able to ",(0,i.kt)("em",{parentName:"p"},"not")," have the username and password stored in the file itself, because they are sensitive. We also want to make the 'namespace' value configurable."),(0,i.kt)("p",null,"We can do this by putting some easy to find patterns as placeholders in the file, then replacing them at runtime when we need them with ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),"."),(0,i.kt)("p",null,"First, let's create the 'template' version of this file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cat << EOF > secret.template.yaml\napiVersion: v1\nkind: Secret\nmetadata:\n name: database-credentials\n namespace: %NAMESPACE%\nstringData:\n username: %DB_USERNAME%\n password: %DB_PASSWORD%\nEOF\n")),(0,i.kt)("p",null,"The first line is using a 'heredoc' to write multiple lines of text to a file. We see heredocs in detail in a later chapter. The file is also in the samples at ",(0,i.kt)("inlineCode",{parentName:"p"},"effective-shell/templates/secret.template.yaml"),"."),(0,i.kt)("p",null,"Now let's apply our substitution:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -e 's/%NAMESPACE%/staging/' \\\n -e 's/%DB_USERNAME%/admin/' \\\n -e 's/%DB_PASSWORD%/secret/' \\\n ~/effective-shell/templates/secret.template.yaml\napiVersion: v1\nkind: Secret\nmetadata:\n name: database-credentials\n namespace: staging\nstringData:\n username: admin\n password: secret\n")),(0,i.kt)("p",null,"I've used the ",(0,i.kt)("inlineCode",{parentName:"p"},"\\")," backslash character to split up the command into multiple lines. This command is really quite straightforward - we search for the very obvious to find patterns and replace them with values."),(0,i.kt)("p",null,"What if we wanted this to be dynamic, and instead of using hard-coded values get the values from environment variables? This is very straightforward:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ export NAMESPACE=production\n$ export DB_USERNAME=prod-admin\n$ export DB_PASSWORD=Dhhs22kfid9c\n$ sed -e "s/%NAMESPACE%/${NAMESPACE}/" \\\n -e "s/%DB_USERNAME%/${DB_USERNAME}/" \\\n -e "s/%DB_PASSWORD%/${DB_PASSWORD}/" \\\n ~/effective-shell/templates/secret.template.yaml\napiVersion: v1\nkind: Secret\nmetadata:\n name: database-credentials\n namespace: production\nstringData:\n username: prod-admin\n password: Dhhs22kfid9c\n')),(0,i.kt)("p",null,"In fact, we could even take this a step further and simply replace ",(0,i.kt)("em",{parentName:"p"},"every")," environment variable!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'file_path="~/effective-shell/templates/secret.template.yaml"\nenv_var_names=$(env | sed -E -n \'s/^([^=]+)(=.*)/\\1/p\')\n\nfor env_var_name in ${env_var_names}; do\n echo "Checking for \'${env_var_name}\'..."\n\n if grep -q "%${env_var_name}%" "${file_path}"; then\n echo "-> Found \'${env_var_name}\', expanding now..."\n\n env_var_value="${!env_var_name}"\n escaped_env_var_value=$(echo ${env_var_value} | sed -e \'s/[\\/&]/\\\\&/g\')\n\n sed -e "s/%${env_var_name}%/${escaped_env_var_value}/" \\\n "${file_path}" > "${file_path}.tmp"\n mv "${file_path}.tmp" "${file_path}"\n fi\ndone\n')),(0,i.kt)("p",null,"Now this might look like a lot at first, and to be fair it is! But almost everything here is actually using commands and concepts we've already seen."),(0,i.kt)("p",null,"Here's what's going on blow-by-blow:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("inlineCode",{parentName:"li"},"file_path")," - we're just creating a variable to hold the name of the file, this makes it easier to apply the command to other files"),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("inlineCode",{parentName:"li"},"env_var_names=...")," - we use ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")," to get the name of each environment variable. This comes from everything before the ",(0,i.kt)("inlineCode",{parentName:"li"},"=")," sign in the output of the ",(0,i.kt)("inlineCode",{parentName:"li"},"env")," command."),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("inlineCode",{parentName:"li"},"for ...")," - this lets us 'loop' through each environment variable name found - we'll see more about this in the sections on scripting."),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("inlineCode",{parentName:"li"},"if grep -q")," - check to see if the environment variable name is used in the file..."),(0,i.kt)("li",{parentName:"ol"},"...and if it is, get the value of it with ",(0,i.kt)("inlineCode",{parentName:"li"},"${!env_var_name}")," - the ",(0,i.kt)("inlineCode",{parentName:"li"},"!")," exclamation mark is ",(0,i.kt)("em",{parentName:"li"},"variable indirection")," and is Bash specific. It allows us to get the value of a variable which has a variable name"),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("inlineCode",{parentName:"li"},"escaped_env_var_value")," we need to replace some of the special characters which might be in the environment variable so that they don't confuse ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("inlineCode",{parentName:"li"},"sed -e")," run the replacement, putting the results in a temporary file..."),(0,i.kt)("li",{parentName:"ol"},"...replace the source file with the temporary one")),(0,i.kt)("p",null,"Many of these concepts, like ",(0,i.kt)("inlineCode",{parentName:"p"},"for")," loops and variable indirection we will see in more detail later. But this little snippet really shows the power of Linux, the GNU tools and the shell - we can create sophisticated operations by composing together small and simple commands."),(0,i.kt)("admonition",{title:"A Note on Security",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"It is very important to be careful when running commands like this or writing scripts which use this kind of pattern."),(0,i.kt)("p",{parentName:"admonition"},"Allowing the contents of your environment variables to be put into files, or even the shell's history can be a serious security concern."),(0,i.kt)("p",{parentName:"admonition"},"As an example - note what would happen if we replaced ",(0,i.kt)("inlineCode",{parentName:"p"},"${DB_USERNAME}")," with ",(0,i.kt)("inlineCode",{parentName:"p"},"${USERNAME}")," in the script above? In ",(0,i.kt)("em",{parentName:"p"},"Z Shell")," rather than the value we provided being put in the file, the ",(0,i.kt)("em",{parentName:"p"},"actual username of the user running the script")," would be written (which in my case would be ",(0,i.kt)("inlineCode",{parentName:"p"},"dwmkerr"),")."),(0,i.kt)("p",{parentName:"admonition"},"Be very careful when working with environment variables to make sure you avoid exposing private information. Even more sensitive variables might be things like ",(0,i.kt)("inlineCode",{parentName:"p"},"${AWS_SECRET_ACCESS_KEY}")," - exposing variables like this could allow attackers to start accessing resources on your cloud environments.")),(0,i.kt)("h3",{id:"what-about-in-place-editing"},"What About 'In Place' Editing?"),(0,i.kt)("p",null,"You might be aware that there is an 'in-place' feature in ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," which allows you to change the file you pass it directly. This allows you to do something like the below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -i '.bak' 's/staging/production/' test.txt\n")),(0,i.kt)("p",null,"This would perform the substitutions and then put them in a file with ",(0,i.kt)("inlineCode",{parentName:"p"},".bak")," appended. To just overwrite the existing file, you could use:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sed -i '' 's/staging/production/' test.txt\n")),(0,i.kt)("p",null,"In this case we don't append anything to the name of the overwritten file, so we end up replacing the original file itself."),(0,i.kt)("p",null,"How the ",(0,i.kt)("inlineCode",{parentName:"p"},"-i")," flag works can vary on some systems so I generally prefer to simply output the result of ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," to a new file and then replace the old one. However, it is useful to know what this flag is and how it is used, as you will often see it in examples."),(0,i.kt)("h2",{id:"what-about-awk"},"What about Awk?"),(0,i.kt)("p",null,"There is another very powerful text manipulation tool - ",(0,i.kt)("inlineCode",{parentName:"p"},"awk"),". Often if you trying to find out how to perform more complex text based operations, you'll see ",(0,i.kt)("inlineCode",{parentName:"p"},"awk")," in the mix as a potential solution."),(0,i.kt)("p",null,"Awk is very sophisticated and has its own language to support complex transformations and operations. My advice is to first master ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," and then consider learning ",(0,i.kt)("inlineCode",{parentName:"p"},"awk")," if you regularly find yourself limited by ",(0,i.kt)("inlineCode",{parentName:"p"},"sed"),"."),(0,i.kt)("h2",{id:"when-to-program"},"When to Program"),(0,i.kt)("p",null,"Personally, if I have tasks which are too complex for me to solve with the fairly basic knowledge of ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," that I have, I will generally write a small program in Python, Node or another high-level and expressive language to do the work, and call that instead."),(0,i.kt)("p",null,"This will often be easier to maintain and understand than an extremely complex ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," expression. But when you decide to move from a shell command to a programming language will be a decision which you will have to make on a case by case basis."),(0,i.kt)("p",null,"In a later chapter, we'll look at how to write well-behaved command line programs which we can compose together using familiar mechanisms like pipelines to build more tools for our toolkit."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"In this chapter we:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Introduced ",(0,i.kt)("inlineCode",{parentName:"li"},"sed"),", the stream editor command"),(0,i.kt)("li",{parentName:"ul"},"Saw that when we need to transform or manipulate text, ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")," is often a great tool for the job"),(0,i.kt)("li",{parentName:"ul"},"Learnt how to perform simple substitutions with ",(0,i.kt)("inlineCode",{parentName:"li"},"sed"),", using commands such as ",(0,i.kt)("inlineCode",{parentName:"li"},"sed 's/old-text/new-text/'")),(0,i.kt)("li",{parentName:"ul"},"Saw the components which make up a ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")," expression - the function, the expression and when needed, the line pattern"),(0,i.kt)("li",{parentName:"ul"},"Saw how to use ",(0,i.kt)("inlineCode",{parentName:"li"},"-e")," to apply multiple patterns"),(0,i.kt)("li",{parentName:"ul"},"Went deeper into ",(0,i.kt)("em",{parentName:"li"},"extended regular expressions")),(0,i.kt)("li",{parentName:"ul"},"Saw an example of how to strip comments from a file with ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")),(0,i.kt)("li",{parentName:"ul"},"Saw how to remove empty lines with ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")),(0,i.kt)("li",{parentName:"ul"},"Looked into ",(0,i.kt)("em",{parentName:"li"},"line patterns")," in more detail, showing how we can choose what lines ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")," operates on"),(0,i.kt)("li",{parentName:"ul"},"Saw how to append text to lines with ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")),(0,i.kt)("li",{parentName:"ul"},"Saw how to use patterns and capture groups to extract information from lines with ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")),(0,i.kt)("li",{parentName:"ul"},"Saw more advanced examples of capture groups, allowing us to capture different parts of a match and manipulate them individually, or recompose them"),(0,i.kt)("li",{parentName:"ul"},"Saw how ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")," can be used to implement a simple 'template' mechanism for files"),(0,i.kt)("li",{parentName:"ul"},"Saw how the ",(0,i.kt)("inlineCode",{parentName:"li"},"-i")," (",(0,i.kt)("em",{parentName:"li"},"in place"),") parameter works for ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")),(0,i.kt)("li",{parentName:"ul"},"Briefly described ",(0,i.kt)("inlineCode",{parentName:"li"},"awk")," as a potential alternative tool to learn about for more sophisticated text manipulation"),(0,i.kt)("li",{parentName:"ul"},"Suggested that if something is too complex to write easily in ",(0,i.kt)("inlineCode",{parentName:"li"},"sed"),", it may be faster to implement it with a programming language")),(0,i.kt)("div",{className:"footnotes"},(0,i.kt)("hr",{parentName:"div"}),(0,i.kt)("ol",{parentName:"div"},(0,i.kt)("li",{parentName:"ol",id:"fn-1"},"Note that if we used ",(0,i.kt)("inlineCode",{parentName:"li"},"rm -r")," instead of ",(0,i.kt)("inlineCode",{parentName:"li"},"rmdir"),", which is very common, we run the risk of making a big mistake - passing the source directory for the ",(0,i.kt)("inlineCode",{parentName:"li"},"cp")," command as the first parameter to remove. This would mean we run ",(0,i.kt)("inlineCode",{parentName:"li"},"rm -r")," on the source files for the backup and the backup folder itself, deleting both! This is one place where using ",(0,i.kt)("inlineCode",{parentName:"li"},"rmdir")," makes a bit more sense - it won't delete the source files if we make a mistake!",(0,i.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-2"},"You can use any character to delimit expressions - sometimes people will use ",(0,i.kt)("inlineCode",{parentName:"li"},"#")," or ",(0,i.kt)("inlineCode",{parentName:"li"},"|")," rather than a forward slash, typically because they want to use the forward slash as part of a pattern.",(0,i.kt)("a",{parentName:"li",href:"#fnref-2",className:"footnote-backref"},"\u21a9")))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/2ca7ae7a.50cc4ba5.js b/pr-preview/pr-346/assets/js/2ca7ae7a.50cc4ba5.js new file mode 100644 index 00000000..c06b22ad --- /dev/null +++ b/pr-preview/pr-346/assets/js/2ca7ae7a.50cc4ba5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[658],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>c});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t =0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=a.createContext({}),h=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=h(e.components);return a.createElement(s.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,l=e.originalType,s=e.parentName,d=r(e,["components","mdxType","originalType","parentName"]),p=h(n),m=o,c=p["".concat(s,".").concat(m)]||p[m]||u[m]||l;return n?a.createElement(c,i(i({ref:t},d),{},{components:n})):a.createElement(c,i({ref:t},d))}));function c(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var l=n.length,i=new Array(l);i[0]=m;var r={};for(var s in t)hasOwnProperty.call(t,s)&&(r[s]=t[s]);r.originalType=e,r[p]="string"==typeof e?e:o,i[1]=r;for(var h=2;h {n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>l,metadata:()=>r,toc:()=>h});var a=n(7462),o=(n(7294),n(3905));const l={title:"Managing your Dotfiles",slug:"/part-5-building-your-toolkit/managing-your-dotfiles"},i=void 0,r={unversionedId:"building-your-toolkit/managing-your-dotfiles/index",id:"building-your-toolkit/managing-your-dotfiles/index",title:"Managing your Dotfiles",description:"As you build up more and more customisations for your shell and environment, it becomes important to find a way to manage these changes and files effectively.",source:"@site/docs/05-building-your-toolkit/26-managing-your-dotfiles/index.md",sourceDirName:"05-building-your-toolkit/26-managing-your-dotfiles",slug:"/part-5-building-your-toolkit/managing-your-dotfiles",permalink:"/part-5-building-your-toolkit/managing-your-dotfiles",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/05-building-your-toolkit/26-managing-your-dotfiles/index.md",tags:[],version:"current",frontMatter:{title:"Managing your Dotfiles",slug:"/part-5-building-your-toolkit/managing-your-dotfiles"},sidebar:"sidebar",previous:{title:"Customising Your Command Prompt",permalink:"/part-5-building-your-toolkit/customising-your-command-prompt"},next:{title:"Controlling Changes with Git",permalink:"/part-5-building-your-toolkit/controlling-changes-with-git"}},s={},h=[{value:"Dotfiles",id:"dotfiles",level:2},{value:"The Default Shell Dotfile",id:"the-default-shell-dotfile",level:2},{value:"Creating a Dotfiles Folder",id:"creating-a-dotfiles-folder",level:2},{value:"Creating a Shell Dotfile",id:"creating-a-shell-dotfile",level:2},{value:"Testing the Shell Dotfile",id:"testing-the-shell-dotfile",level:2},{value:"Sourcing the Shell Dotfile",id:"sourcing-the-shell-dotfile",level:2},{value:"Sourcing Files from a Folder",id:"sourcing-files-from-a-folder",level:2},{value:"A Dotfile Install Script",id:"a-dotfile-install-script",level:2},{value:"Summary",id:"summary",level:2}],d={toc:h};function p(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"As you build up more and more customisations for your shell and environment, it becomes important to find a way to manage these changes and files effectively."),(0,o.kt)("p",null,"Configuration files are often called 'dotfiles'. In this chapter we'll see how to manage your configuration - and 'dotfiles' - in a way that allows you to easily manage changes over time and build up a library of scripts and features for your preferred shell. We'll also look at how we can use your 'dotfiles' across different shells."),(0,o.kt)("admonition",{title:"Z-Shell",type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"We will start by discussing Bash configuration in this chapter. However, we'll quickly switch to creating configuration that works across many shells - including Z-Shell! So if you are a Z-Shell user don't worry, all of this material will be applicable to your environment as well.")),(0,o.kt)("p",null,"In this chapter we will be creating some files and folders, if you just want to see the results, install the samples. You can then find them in the ",(0,o.kt)("em",{parentName:"p"},"~/effective-shell/dotfiles")," folder."),(0,o.kt)("admonition",{title:"Downloading the Samples",type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"Run the following command in your shell to download the samples:"),(0,o.kt)("pre",{parentName:"admonition"},(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"curl effective.sh | sh\n"))),(0,o.kt)("h2",{id:"dotfiles"},"Dotfiles"),(0,o.kt)("p",null,"Any file or folder on your system that starts with a ",(0,o.kt)("inlineCode",{parentName:"p"},".")," dot symbol is a 'dotfile'. On many systems dotfiles are hidden by default. This means that they will not show up if you run commands like ",(0,o.kt)("inlineCode",{parentName:"p"},"ls"),", unless you provide flags such as ",(0,o.kt)("inlineCode",{parentName:"p"},"-a")," (",(0,o.kt)("em",{parentName:"p"},"show all files and folders"),") flag. In desktop environments such as Gnome, KDE and MacOS X dotfiles are also hidden by default."),(0,o.kt)("p",null,"Dotfiles are often used 'behind the scenes' as configuration files or system files. This is why they are hidden by default - 'normal' users shouldn't have to worry about them or their contents."),(0,o.kt)("p",null,"You will mostly see dotfiles in your ",(0,o.kt)("inlineCode",{parentName:"p"},"HOME")," directory. They have a dot to mark them as hidden to distinguish them from your personal files and folders. When there are configuration files that are ",(0,o.kt)("em",{parentName:"p"},"outside")," your home directory, the dot is normally not used, because it is clear from the folder that the file is in that the file is in that it is a configuration file."),(0,o.kt)("p",null,"As an example, a user's personal Bash configuration is stored in ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc"),", but the global Bash configuration applied to ",(0,o.kt)("em",{parentName:"p"},"all")," users is stored in ",(0,o.kt)("em",{parentName:"p"},"/etc/bash.bashrc"),". The second configuration file does not need a dot in front of it - the ",(0,o.kt)("em",{parentName:"p"},"/etc")," folder is where configuration is kept so there is no need to differentiate it from other files like a user's personal files."),(0,o.kt)("p",null,'Nowadays, when a user says "my dotfiles", they typically mean their ',(0,o.kt)("em",{parentName:"p"},"configuration")," files that are kept in their home directory. In a sense, your dotfiles are a bit like your personal settings for your computer. On a desktop environment your settings might be things like your theme or wallpaper. For a shell user, you settings will be files like ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," for your shell configuration, ",(0,o.kt)("em",{parentName:"p"},"~/.ssh/config")," for your SSH configuration and so on."),(0,o.kt)("p",null,"You will likely change the dotfiles over time to suit your preferences. Let's take a look at some sensible ways to organise and structure your dotfiles so that you can easily see what is your personal configuration, rather than what is the default configuration provided by the system, and easily manage these configurations."),(0,o.kt)("h2",{id:"the-default-shell-dotfile"},"The Default Shell Dotfile"),(0,o.kt)("p",null,"On many platforms the default ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," file will contain a number of customisations out-of-the-box."),(0,o.kt)("p",null,"Let's take a look at the ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," file that comes with Ubuntu 20 as an example. We'll take a look at a few snippets. If you look at your own machine's ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," file the contents may be different as it will vary from distribution to distribution:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# don't put duplicate lines or lines starting with space in the history.\n# See bash(1) for more options\nHISTCONTROL=ignoreboth\n\n# append to the history file, don't overwrite it\nshopt -s histappend\n\n# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)\nHISTSIZE=1000\nHISTFILESIZE=2000\n")),(0,o.kt)("p",null,"Here we have a number of options that relate to the shell history - making it slightly larger than the default, appending to the history file rather than over-writing it and so on."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'# If set, the pattern "**" used in a pathname expansion context will\n# match all files and zero or more directories and subdirectories.\n#shopt -s globstar\n')),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"shopt -s globstar")," command has been commented out, so that users can quickly remove the comment symbol to enable pathname expansion across subdirectories."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'# set a fancy prompt (non-color, unless we know we "want" color)\ncase "$TERM" in\n xterm-color|*-256color) color_prompt=yes;;\nesac\n\n# uncomment for a colored prompt, if the terminal has the capability; turned\n# off by default to not distract the user: the focus in a terminal window\n# should be on the output of commands, not on the prompt\n#force_color_prompt=yes\n\nif [ -n "$force_color_prompt" ]; then\n if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then\n # We have color support; assume it\'s compliant with Ecma-48\n # (ISO/IEC-6429). (Lack of such support is extremely rare, and such\n # a case would tend to support setf rather than setaf.)\n color_prompt=yes\n else\n color_prompt=\n fi\nfi\n\nif [ "$color_prompt" = yes ]; then\n PS1=\'${debian_chroot:+($debian_chroot)}\\[\\033[01;32m\\]\\u@\\h\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ \'\nelse\n PS1=\'${debian_chroot:+($debian_chroot)}\\u@\\h:\\w\\$ \'\nfi\nunset color_prompt force_color_prompt\n')),(0,o.kt)("p",null,"This rather complex looking code determines whether the shell supports colour, and if so, updates the command prompt appropriately",(0,o.kt)("sup",{parentName:"p",id:"fnref-1"},(0,o.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),"."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# enable color support of ls and also add handy aliases\nif [ -x /usr/bin/dircolors ]; then\n test -r ~/.dircolors && eval \"$(dircolors -b ~/.dircolors)\" || eval \"$(dircolors -b)\"\n alias ls='ls --color=auto'\n #alias dir='dir --color=auto'\n #alias vdir='vdir --color=auto'\n\n alias grep='grep --color=auto'\n alias fgrep='fgrep --color=auto'\n alias egrep='egrep --color=auto'\nfi\n")),(0,o.kt)("p",null,"If the shell supports colour, then ",(0,o.kt)("em",{parentName:"p"},"aliases")," are used so that common commands like ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," will show their output in colour."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# some more ls aliases\nalias ll='ls -alF'\nalias la='ls -A'\nalias l='ls -CF'\n\n# Add an \"alert\" alias for long running commands. Use like so:\n# sleep 10; alert\nalias alert='notify-send --urgency=low -i \"$([ $? = 0 ] && echo terminal || echo error)\" \"$(history|tail -n1|sed -e '\\''s/^\\s*[0-9]\\+\\s*//;s/[;&|]\\s*alert$//'\\'')\"'\n\n# Alias definitions.\n# You may want to put all your additions into a separate file like\n# ~/.bash_aliases, instead of adding them here directly.\n# See /usr/share/doc/bash-doc/examples in the bash-doc package.\n\nif [ -f ~/.bash_aliases ]; then\n . ~/.bash_aliases\nfi\n")),(0,o.kt)("p",null,"More aliases are added as shortcuts for useful commands. We also are sourcing the ",(0,o.kt)("em",{parentName:"p"},"~/.bash_aliases")," file if it exists."),(0,o.kt)("p",null,"There will likely be a number of other configuration commands that are set in the file, such as setting up the 'auto-completion' feature of Bash."),(0,o.kt)("p",null,"We could add our own customisations to this file, and many people will do so. However it might be better to keep our changes in our own configuration file. This allows us to differentiate between the 'out-of-the-box' configuration and our own personal changes. Let's see how to do that."),(0,o.kt)("h2",{id:"creating-a-dotfiles-folder"},"Creating a Dotfiles Folder"),(0,o.kt)("p",null,"If we keep our shell customisations in our own dotfile, then it is much easier for us to see what are our personal configuration settings rather than the system-provided configuration settings. Also, if we keep these settings in a separate file, it becomes easier to then share this file across different machines. All we need to do is copy it to each machine where we want it, and ",(0,o.kt)("inlineCode",{parentName:"p"},"source")," it from the ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," file."),(0,o.kt)("p",null,"The other great thing about keeping your shell configuration in its own file is that you can then use it for ",(0,o.kt)("em",{parentName:"p"},"different")," shells if you want to. Or you can check in the file to see what the type of shell is and then load a configuration specifically for that shell."),(0,o.kt)("p",null,"It is entirely possible (and quite likely) that you will over time build up a collection of many dotfiles - some might be used for the shell, such as a file to set your favourite aliases or functions and some may be for other tools."),(0,o.kt)("p",null,"To keep things organised I'm going to show a technique to manage your dotfiles that I have found useful. You will see this technique, and many similar techniques, used by many people and demonstrated in blogs and so on. As I walk through the process feel free to customise or adapt it to suit your preferences!"),(0,o.kt)("p",null,"First, let's start by creating a sensible location for all of our per-user personal configuration files, a folder called ",(0,o.kt)("em",{parentName:"p"},"~/dotfiles"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir ~/dotfiles\n")),(0,o.kt)("p",null,"Keeping our dotfiles in a single folder will make it easier for us to later on package them up and share them, track changes to them, update them, and so on."),(0,o.kt)("h2",{id:"creating-a-shell-dotfile"},"Creating a Shell Dotfile"),(0,o.kt)("p",null,"Rather than changing the system-provided ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," file to contain all of our customisations, let's create our own shell configuration file in the dotfiles folder:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"touch ~/dotfiles/shell.sh\n")),(0,o.kt)("p",null,"You can call this file whatever you like, it really comes down to preferences. But here are a few points about the name I have suggested:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"I have not put a dot in front of the name! This is because ",(0,o.kt)("em",{parentName:"li"},"within")," the ",(0,o.kt)("em",{parentName:"li"},"~/dotfiles")," folder I don't actually want this file to be hidden - if I am looking in the ",(0,o.kt)("em",{parentName:"li"},"~/dotfiles")," folder I want to see this file"),(0,o.kt)("li",{parentName:"ul"},"I have not used the ",(0,o.kt)("em",{parentName:"li"},"name")," of a shell program in this file - this is because I will make this file work with ",(0,o.kt)("em",{parentName:"li"},"any")," shell that I regularly use - so whether I am using ",(0,o.kt)("inlineCode",{parentName:"li"},"zsh"),", ",(0,o.kt)("inlineCode",{parentName:"li"},"bash")," or ",(0,o.kt)("inlineCode",{parentName:"li"},"sh"),", this file should still be able to be loaded"),(0,o.kt)("li",{parentName:"ul"},"I have put ",(0,o.kt)("em",{parentName:"li"},".sh")," at the end of the file name - this is not really needed or even common in the world of Linux or Unix, but does make it immediately clear to the reader (or any program that opens the file) that it is a shell script")),(0,o.kt)("p",null,"Now let's edit the ",(0,o.kt)("em",{parentName:"p"},"~/dotfiles/shell.sh")," file to add some configuration that might be useful for our shell:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# Note: there is no shebang in this script. This script sets my preferred shell\n# configuration and should be able to be sourced from any Bash-like shell or\n# from Z shell.\n\n# If we are not running interactively do not continue loading this file.\ncase $- in\n *i*) ;;\n *) return;;\nesac\n")),(0,o.kt)("p",null,"We'll start the file with a comment that clearly explains why this file does not have a ",(0,o.kt)("em",{parentName:"p"},"shebang")," and that it should be able to be sourced from any Bash-like shell or Z-Shell. Then we perform a quick check on the parameters that the shell was started with (which are available in the special ",(0,o.kt)("inlineCode",{parentName:"p"},"$-")," parameter) to see if the ",(0,o.kt)("inlineCode",{parentName:"p"},"i")," (",(0,o.kt)("em",{parentName:"p"},"interactive"),") parameter is set. If the interactive parameter is ",(0,o.kt)("em",{parentName:"p"},"not")," set then we call ",(0,o.kt)("inlineCode",{parentName:"p"},"return")," to stop loading the script."),(0,o.kt)("p",null,"This is standard for shell configuration files - we only change shell configuration when running interactively (otherwise things like aliases that we add could cause shell scripts and other processes that run non-interactively to have unexpected behaviour)."),(0,o.kt)("p",null,"Next, let's set our preferred editor:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# Set our editor. Some tools use 'VISUAL', some use 'EDITOR'.\nVISUAL=nano\nEDITOR=nano\n")),(0,o.kt)("p",null,"There are two variables are used by the shell and command line programs to run an editor. The first, and original, variable was ",(0,o.kt)("inlineCode",{parentName:"p"},"EDITOR"),". This was originally often a ",(0,o.kt)("em",{parentName:"p"},"line mode")," editor - i.e. a text editor that doesn't take up the whole screen. This was useful in the days of printed output, before screens were used. The ",(0,o.kt)("inlineCode",{parentName:"p"},"VISUAL")," variable was used to specify the editor that could be used for 'full screen' terminal editing. Some programs use ",(0,o.kt)("inlineCode",{parentName:"p"},"EDITOR")," and some use ",(0,o.kt)("inlineCode",{parentName:"p"},"VISUAL")," so it is best to set both."),(0,o.kt)("p",null,"I have used the ",(0,o.kt)("inlineCode",{parentName:"p"},"nano")," editor in this example as it available on many distributions and is a little easier than ",(0,o.kt)("inlineCode",{parentName:"p"},"vi")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"emacs"),", but you can use whatever you like. For my personal dotfiles I use ",(0,o.kt)("inlineCode",{parentName:"p"},"vi"),"."),(0,o.kt)("p",null,"At this stage you can start to go a bit over the top - for example here's an alternative way to set the editor:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'# Set our preferred editor to the first available editor in the array below.\npreferred_editors=(nano vi)\nfor editor in ${preferred_editors[@]}; do\n if command -v "${editor}" >/dev/null 2>&1; then\n # Note that \'VISUAL\' can be a full screen terminal editor. On legacy\n # systems \'EDITOR\' was normally a line mode editor but there is\n # generally no need to differentiate any more.\n VISUAL="$(command -v ${editor})"\n EDITOR="${VISUAL}"\n break\n fi\ndone\nunset editor preferred_editors\n')),(0,o.kt)("p",null,"In this method we specify an array of editors, go through each one, check to see if it exists",(0,o.kt)("sup",{parentName:"p",id:"fnref-2"},(0,o.kt)("a",{parentName:"sup",href:"#fn-2",className:"footnote-ref"},"2")),", and if it does set it, otherwise we look for the next in the list. This is completely over the top and unnecessary! But the great thing about your dotfiles is - they're yours! If you want to do this, that's absolutely fine. If you want to check to see if Sublime Text is installed and use that, or Visual Studio Code, then that's not a problem - it's your personal configuration so do what works for you!"),(0,o.kt)("p",null,"You'll notice that in the ",(0,o.kt)("em",{parentName:"p"},"~/effective-shell/dotfiles/shell.sh")," folder I ",(0,o.kt)("inlineCode",{parentName:"p"},"unset")," every shell variable after I use it. This is just to clean up after myself and try to leave the environment as pristine as possible after sourcing the file."),(0,o.kt)("p",null,"Another useful option to set is ",(0,o.kt)("inlineCode",{parentName:"p"},"stty -ixon"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# Allow us to use Ctrl+S to perform forward search, by disabling the start and\n# stop output control signals, which are not needed on modern systems.\nstty -ixon\n")),(0,o.kt)("p",null,"This command tells the terminal driver that we don't need to control transmission with the Ctrl+Q and Ctrl+S commands, meaning we can instead use Ctrl+S to perform a forward search."),(0,o.kt)("p",null,"Now let's set some sensible settings for working with folders:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# Set a shell option but don't fail if it doesn't exist!\nsafe_set() { shopt -s \"$1\" >/dev/null 2>&1 || true; }\n\n# Set some options to make working with folders a little easier. Note that we\n# send all output to '/dev/null' as startup files should not write to the\n# terminal and older shells might not have these options.\nsafe_set autocd # Enter a folder name to 'cd' to it.\nsafe_set cdspell # Fix minor spelling issues with 'cd'.\nsafe_set dirspell # Fix minor spelling issues for commands.\nsafe_set cdable_vars # Allow 'cd varname' to switch directory.\n\n# Uncomment the below if you want to be able to 'cd' into directories that are\n# not just relative to the current location. For example, if the below was\n# uncommented we could 'cd my_project' from anywhere if 'my_project' is in\n# the 'repos' folder.\n# CDPATH=\"~:~/repos\"\n")),(0,o.kt)("p",null,"If we run this script on an older shell, some of these options might not be present. This is why we have created a ",(0,o.kt)("inlineCode",{parentName:"p"},"safe_set")," function that won't fail if the ",(0,o.kt)("inlineCode",{parentName:"p"},"shopt")," function fails and will pipe any output to ",(0,o.kt)("em",{parentName:"p"},"/dev/null"),". You can use these settings or remove them, it's really up to you. Each one is described below:"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Setting"),(0,o.kt)("th",{parentName:"tr",align:null},"Description"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"autocd")),(0,o.kt)("td",{parentName:"tr",align:null},"Allows you to simply type a directory name or path and press enter to ",(0,o.kt)("inlineCode",{parentName:"td"},"cd")," to it.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"cdspell")),(0,o.kt)("td",{parentName:"tr",align:null},"When running commands like ",(0,o.kt)("inlineCode",{parentName:"td"},"cd dirname"),", have the shell fix minor typos.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"dirspell")),(0,o.kt)("td",{parentName:"tr",align:null},"When running commands like ",(0,o.kt)("inlineCode",{parentName:"td"},"cat dirname/test"),", have the shell fix minor typos.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"cdable_vars")),(0,o.kt)("td",{parentName:"tr",align:null},"If you create a var like ",(0,o.kt)("inlineCode",{parentName:"td"},'repos="$HOME/repos'),", then ",(0,o.kt)("inlineCode",{parentName:"td"},"cd repos")," to move into it.")))),(0,o.kt)("p",null,"I have also left a comment on how you can use the ",(0,o.kt)("inlineCode",{parentName:"p"},"CDPATH")," shell variable to allow you to ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," into relative folders outside of your current path. This option you should be a little careful with as it can be a bit misleading - but you might find it useful."),(0,o.kt)("p",null,"Finally, let's set some common shell history options:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# Configure the history to make it large and support multi-line commands.\nsafe_set histappend # Don't overwrite the history file, append.\nsafe_set cmdhist # Multi-line commands are one entry only.\nPROMPT_COMMAND='history -a' # Before we prompt, save the history.\nHISTSIZE=10000 # A large number of commands per session.\nHISTFILESIZE=100000 # A huge number of commands in the file.\n# HISTCONTROL=\"ignorespace:ignoredup\" # Ignore starting with space or duplicates?\n# export HISTIGNORE=\"ls:history\" # Any commands we want to not record?\n# HISTTIMEFORMAT='%F %T ' # Do we want a timestamp for commands?\n")),(0,o.kt)("p",null,"These shell options and variables can be used to fine-tune how the history works. Here's a description of each one:"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Setting"),(0,o.kt)("th",{parentName:"tr",align:null},"Description"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"shopt -s histappend")),(0,o.kt)("td",{parentName:"tr",align:null},"When we write to the history file, append entries, don't overwrite the old file.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"shopt -s cmdhist")),(0,o.kt)("td",{parentName:"tr",align:null},"If we have a multi-line command, record it as one entry, not one per line.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"PROMPT_COMMAND")),(0,o.kt)("td",{parentName:"tr",align:null},"Before we show the ",(0,o.kt)("inlineCode",{parentName:"td"},"PS1")," prompt, update the history file.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"HISTSIZE")),(0,o.kt)("td",{parentName:"tr",align:null},"Store up to 10000 items in the history for the current session.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"HISTFILESIZE")),(0,o.kt)("td",{parentName:"tr",align:null},"Store up to 100000 items in the history file for all sessions.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"HISTCONTROL")),(0,o.kt)("td",{parentName:"tr",align:null},"Uncomment to ignore commands that start with a space, or ignore duplicated commands.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"HISTIGNORE")),(0,o.kt)("td",{parentName:"tr",align:null},"Uncomment to not record certain commands in the history.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"HISTTIMEFORMAT")),(0,o.kt)("td",{parentName:"tr",align:null},"Uncomment to keep a date and time next to each command in the history file.")))),(0,o.kt)("p",null,"At this stage we got a sensible set of basic options for our shell, that should work in Bash, or Bash-like shells, as well as Z-Shell."),(0,o.kt)("p",null,"Now let's look at how we could test this file, before we actually source it."),(0,o.kt)("h2",{id:"testing-the-shell-dotfile"},"Testing the Shell Dotfile"),(0,o.kt)("p",null,"Before we ",(0,o.kt)("inlineCode",{parentName:"p"},"source")," the shell dotfile during shell startup, we should test that it runs without errors. Fortunately, there's a really easy way to do this!"),(0,o.kt)("p",null,"From your shell, just run the command below:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ sh -iex ~/dotfiles/shell.sh\n+ case $- in\n+ EDITOR=vi\n+ VISUAL=vi\n+ safe_set autocd\n+ shopt -s autocd\n...\n")),(0,o.kt)("p",null,"What we have done is run a shell program, in this case the ",(0,o.kt)("inlineCode",{parentName:"p"},"sh")," program, and passed the following flags:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"i")," - this makes the shell interactive - our script only runs in interactive shells so we need this to test it!"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"e")," - this causes the shell to exit if a command fails"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"x")," - this sets the tracing output")),(0,o.kt)("p",null,"By running this script in a shell this way we can see ",(0,o.kt)("em",{parentName:"p"},"exactly")," what is being run, and if there is an error we will see the tracing stop at the point that the error occurs. You could perform exactly the same test with other shells, such as ",(0,o.kt)("inlineCode",{parentName:"p"},"bash")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"zsh"),"."),(0,o.kt)("p",null,"This is a great way to verify that the script works as expected, before we actually commit to sourcing it as part of our shell start up."),(0,o.kt)("h2",{id:"sourcing-the-shell-dotfile"},"Sourcing the Shell Dotfile"),(0,o.kt)("p",null,"Now that we have a working shell dotfile, we can source it as part of our shell startup."),(0,o.kt)("p",null,"Rather than having our shell startup file know about our ",(0,o.kt)("em",{parentName:"p"},"~/dotfiles")," folder, we will create a symlink to the shell script from our home directory:"),(0,o.kt)("p",null,"Finally, we can create a symlink in our home directory that points to our ",(0,o.kt)("em",{parentName:"p"},"~/dotfiles/shell.sh")," file and we are good to go!"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'$ ln -sf "$HOME/dotfiles/shell.sh" "$HOME/.shell.sh"\n')),(0,o.kt)("p",null,"Note that in this example we used the ",(0,o.kt)("inlineCode",{parentName:"p"},"ln -sf")," command to create a symlink, the ",(0,o.kt)("inlineCode",{parentName:"p"},"-s")," flag ensures we create a normal symlink (rather than a 'hard' link) and the ",(0,o.kt)("inlineCode",{parentName:"p"},"-f")," flag forces the creation of the link by overwriting any link that already exists."),(0,o.kt)("p",null,"Now all we need to do is add the following lines to our ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," (or for Z-Shell, ",(0,o.kt)("em",{parentName:"p"},"~/.zshrc")," file):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# Source our shell configuration if it exists.\n[ -r ~/.shell.sh ] && source ~/.shell.sh\n")),(0,o.kt)("p",null,"This command uses the ",(0,o.kt)("inlineCode",{parentName:"p"},"-r")," (",(0,o.kt)("em",{parentName:"p"},"does file exist and is it readable"),") test to check whether we have a ",(0,o.kt)("em",{parentName:"p"},"~/.shell.sh")," file and if it exists, sources it."),(0,o.kt)("p",null,"We're going to make a couple more changes and then bring this all together by creating one final script that sets performs the steps above for us. If this is enough dotfile configuration for you, then feel free to stop now, if you'd like to go deeper we'll look at loading additional files."),(0,o.kt)("h2",{id:"sourcing-files-from-a-folder"},"Sourcing Files from a Folder"),(0,o.kt)("p",null,"A common pattern with Linux and Unix systems is to allow ",(0,o.kt)("em",{parentName:"p"},"multiple")," configuration files to be stored in a folder. A convention is to have a folder with the letters ",(0,o.kt)("inlineCode",{parentName:"p"},".d")," at the end, to differentiate between a single configuration file and a configuration folder."),(0,o.kt)("p",null,"This pattern became popular over the years as individual configuration files became larger and more complex, and operators wanted to be able to spread their configuration across multiple files."),(0,o.kt)("p",null,"Here are some common examples:"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Configuration File"),(0,o.kt)("th",{parentName:"tr",align:null},"Configuration Directory"),(0,o.kt)("th",{parentName:"tr",align:null},"Notes"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"/etc/crontab")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"/etc/cron.d")),(0,o.kt)("td",{parentName:"tr",align:null},"Configuration for scheduled tasks.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"/etc/bash_completion")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"/etc/bash_completion.d")),(0,o.kt)("td",{parentName:"tr",align:null},"Configuration for Bash auto-complete.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"/etc/sudoers")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"/etc/sudoers.d")),(0,o.kt)("td",{parentName:"tr",align:null},"Configuration for super-users.")))),(0,o.kt)("p",null,"We can follow exactly the same pattern for our shell configuration. Let's say for example that we want to customise our command prompt when we start the shell, we could put the file that contains the definition of the ",(0,o.kt)("inlineCode",{parentName:"p"},"set_ps1")," function from the last chapter in our configuration folder. The file will be loaded and then we can use it to set the ",(0,o.kt)("inlineCode",{parentName:"p"},"PS1")," variable in our shell configuration."),(0,o.kt)("p",null,"First, let's make a directory to hold our shell configuration files:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ mkdir ~/dotfiles/shell.d\n")),(0,o.kt)("p",null,"Now let's copy over our ",(0,o.kt)("em",{parentName:"p"},"~/effective-shell/scripts/set_ps1.sh")," file:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ cp ~/effective-shell/scripts/set_ps1.sh ~/dotfiles/shell.d\n")),(0,o.kt)("p",null,"Now let's update our ",(0,o.kt)("inlineCode",{parentName:"p"},"shell.sh")," file to source all of the files in the ",(0,o.kt)("em",{parentName:"p"},"~/.shell.d")," folder:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'# If we are not running interactively do not continue loading this file.\ncase $- in\n *i*) ;;\n *) return;;\nesac\n\n# Source any files in our ~/.shell.d folder.\nif [ -x ~/.shell.d ]; then\n for shellfile in ~/.shell.d/*; do\n [ -r "$shellfile" ] && source "$shellfile"\n done\n unset shellfile\nfi\n')),(0,o.kt)("p",null,"The new code goes after the test to see whether the shell is interactive. We check to see whether there is a directory that can be searched (using the ",(0,o.kt)("inlineCode",{parentName:"p"},"-x")," test), and then we loop through each file in the directory. If the file can be read (using the ",(0,o.kt)("inlineCode",{parentName:"p"},"-r")," test) then we source it."),(0,o.kt)("p",null,"At the end of the ",(0,o.kt)("em",{parentName:"p"},"shell.sh")," file we can now call the ",(0,o.kt)("inlineCode",{parentName:"p"},"set_ps1")," function to set our theme:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'# Set the theme. Do not fail if the function doesn\'t exist.\nset_ps1 "debian" || true\n')),(0,o.kt)("p",null,"Finally, let's create a symlink in our home directory for the shell configuration files:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'$ ln -sf "$HOME/dotfiles/shell.d" "$HOME/.shell.d"\n')),(0,o.kt)("p",null,"At this stage we've now successfully created a ",(0,o.kt)("em",{parentName:"p"},"dotfiles")," folder to store our configuration, symlinks in our ",(0,o.kt)("inlineCode",{parentName:"p"},"$HOME")," directory that point to our dotfiles and we have also updated our ",(0,o.kt)("em",{parentName:"p"},"~/.bashrc")," or ",(0,o.kt)("em",{parentName:"p"},"~/.zshrc")," to load our shell configuration."),(0,o.kt)("p",null,"If you want to see the new links you've created you can run the ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," command just like so (I've abbreviated the output to make it more readable):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ ls -al ~ | grep shell\nlrwxr-xr-x dwmkerr .shell.d -> /home/dwmkerr/dotfiles/shell.d\nlrwxr-xr-x dwmkerr .shell.sh -> /home/dwmkerr/dotfiles/shell.sh\n")),(0,o.kt)("h2",{id:"a-dotfile-install-script"},"A Dotfile Install Script"),(0,o.kt)("p",null,"The manual steps we performed to setup the links for our dotfiles can be easily run using a shell script. "),(0,o.kt)("p",null,"The script below shows how we can easily setup the links to the dotfiles, and source the appropriate files from our shell configuration:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'#!/usr/bin/env sh\n\n# This script installs the dotfiles locally. Note that it should be run from the\n# dotfiles folder so that the links are set properly!\n\n# Create links for the shell configuration.\nln -sf "$PWD/shell.sh" "$HOME/.shell.sh"\nln -sf "$PWD/shell.d" "$HOME/.shell.d"\n\n# Source our shell configuration in any local shell config files.\nconfig_files=(~/.bashrc ~/.zshrc)\nfor config_file in ${config_files[@]}; do\n # Skip config files that don\'t exist.\n [ -r "${config_file}" ] || continue\n\n # If we don\'t have the \'source ~/.shell.d\' line in our config, add it.\n source_command="[ -r ~/.shell.sh ] && source ~/.shell.sh"\n if ! grep -q "${source_command}" "${config_file}"; then\n echo ".shell.sh is not sourced in \'${config_file}\' adding this now..."\n echo "${source_command}" >> "${config_file}"\n fi\ndone\n')),(0,o.kt)("p",null,"This script creates the symlinks to our dotfiles and loops through a set of shell configuration files, adding a line to source the ",(0,o.kt)("em",{parentName:"p"},"~/.shell.sh")," in the configuration file if it doesn't exist."),(0,o.kt)("p",null,"Note how we use the ",(0,o.kt)("inlineCode",{parentName:"p"},"grep -q")," command to search through the shell configuration file to see if the line that sources our dotfile exists. The ",(0,o.kt)("inlineCode",{parentName:"p"},"grep")," command returns ",(0,o.kt)("inlineCode",{parentName:"p"},"0")," if it finds a result and ",(0,o.kt)("inlineCode",{parentName:"p"},"1")," otherwise, meaning we can easily use it in an 'if' statement"),(0,o.kt)("p",null,"This script can be run from the dotfiles folder like so:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ cd ~/dotfiles\n$ ./install.sh\n.shell.sh is not sourced in '/home/dwmkerr/.bashrc' adding this now...\n")),(0,o.kt)("p",null,"And that is it - we now have a ",(0,o.kt)("em",{parentName:"p"},"~/dotfiles")," folder with our configuration, a sensible set of options for the shell, and the ability to quickly configure our dotfiles for different shells."),(0,o.kt)("p",null,"The dotfiles that we have a created are available in the ",(0,o.kt)("em",{parentName:"p"},"~/effective-shell/dotfiles")," folder from the samples. The install script shown above is also in that folder."),(0,o.kt)("h2",{id:"summary"},"Summary"),(0,o.kt)("p",null,"In this chapter we looked at some sensible configuration settings for shells. We also looked at how to keep our settings separated from the system provided configuration file. We also saw how to manage our configuration files and folders in a 'dotfiles' folder. Finally, we created a simple script to 'install' our dotfiles for the local user."),(0,o.kt)("p",null,"In the next chapter we'll introduce Git - a version control tool we can use to manage changes to files like the 'dotfiles' easily over time. We can also use this tool to share our dotfiles across many machines."),(0,o.kt)("div",{className:"footnotes"},(0,o.kt)("hr",{parentName:"div"}),(0,o.kt)("ol",{parentName:"div"},(0,o.kt)("li",{parentName:"ol",id:"fn-1"},"If you are curious, the ",(0,o.kt)("inlineCode",{parentName:"li"},"debian_chroot")," variable is set when you are running as a user that has run the ",(0,o.kt)("inlineCode",{parentName:"li"},"chroot")," (",(0,o.kt)("em",{parentName:"li"},"change root"),") command. The ",(0,o.kt)("inlineCode",{parentName:"li"},"chroot")," command allows you to create an isolated file system tree. This lets you run programs in what is sometimes called a 'jail', which is a little like a container. ",(0,o.kt)("inlineCode",{parentName:"li"},"chroot")," is an advanced topic and out of the scope of this book, but the ",(0,o.kt)("inlineCode",{parentName:"li"},"debian_chroot")," command in the ",(0,o.kt)("inlineCode",{parentName:"li"},"PS1")," variable is used to help make it clear when running a shell if you are in a 'changed root' environment.",(0,o.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")),(0,o.kt)("li",{parentName:"ol",id:"fn-2"},"For a reminder on how to check whether a command is available, see ",(0,o.kt)("em",{parentName:"li"},"Checking for Installed Programs")," in ",(0,o.kt)("a",{parentName:"li",href:"../../part-4-shell-scripting/useful-patterns-for-shell-scripts"},"Chapter 23 - Useful Patterns for Shell Scripts"),".",(0,o.kt)("a",{parentName:"li",href:"#fnref-2",className:"footnote-backref"},"\u21a9")))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/2faed7c2.e1957766.js b/pr-preview/pr-346/assets/js/2faed7c2.e1957766.js new file mode 100644 index 00000000..bd44934a --- /dev/null +++ b/pr-preview/pr-346/assets/js/2faed7c2.e1957766.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[9682],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>u});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t =0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},h="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),h=p(n),d=i,u=h["".concat(s,".").concat(d)]||h[d]||m[d]||r;return n?a.createElement(u,l(l({ref:t},c),{},{components:n})):a.createElement(u,l({ref:t},c))}));function u(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,l=new Array(r);l[0]=d;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[h]="string"==typeof e?e:i,l[1]=o;for(var p=2;p {n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>o,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={title:"Slice and Dice Text",slug:"/part-3-manipulating-text/slice-and-dice-text/"},l=void 0,o={unversionedId:"manipulating-text/slice-and-dice-text/index",id:"manipulating-text/slice-and-dice-text/index",title:"Slice and Dice Text",description:"In Chapter 14 we looked at how to use the grep command to search through text and filter text. In this chapter we're going to look at some of the basic commands which we can use to manipulate text. There are a whole raft of commands and options available.",source:"@site/docs/03-manipulating-text/15-slice-and-dice-text/index.md",sourceDirName:"03-manipulating-text/15-slice-and-dice-text",slug:"/part-3-manipulating-text/slice-and-dice-text/",permalink:"/part-3-manipulating-text/slice-and-dice-text/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/03-manipulating-text/15-slice-and-dice-text/index.md",tags:[],version:"current",frontMatter:{title:"Slice and Dice Text",slug:"/part-3-manipulating-text/slice-and-dice-text/"},sidebar:"sidebar",previous:{title:"Get to Grips with Grep",permalink:"/part-3-manipulating-text/get-to-grips-with-grep/"},next:{title:"Advanced Text Manipulation",permalink:"/part-3-manipulating-text/advanced-text-manipulation/"}},s={},p=[{value:"Heads and Tails",id:"heads-and-tails",level:2},{value:"Replacing Text",id:"replacing-text",level:2},{value:"How to Cut",id:"how-to-cut",level:2},{value:"A Trick with Rev",id:"a-trick-with-rev",level:2},{value:"Sort and Unique",id:"sort-and-unique",level:2},{value:"Don't Forget Your Pager!",id:"dont-forget-your-pager",level:2},{value:"Summary",id:"summary",level:2}],c={toc:p};function h(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"In ",(0,i.kt)("a",{parentName:"p",href:"../../part-3-manipulating-text/get-to-grips-with-grep"},"Chapter 14")," we looked at how to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"grep")," command to search through text and filter text. In this chapter we're going to look at some of the basic commands which we can use to ",(0,i.kt)("em",{parentName:"p"},"manipulate")," text. There are a whole raft of commands and options available."),(0,i.kt)("p",null,"We'll start with the basics and move onto some of the more sophisticated commands in the next chapter."),(0,i.kt)("h2",{id:"heads-and-tails"},"Heads and Tails"),(0,i.kt)("p",null,"The commands ",(0,i.kt)("inlineCode",{parentName:"p"},"head")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," are very simple but incredibly useful."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"head")," is used to extract part of the ",(0,i.kt)("em",{parentName:"p"},"top")," of a file and ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," is used to extract part of the ",(0,i.kt)("em",{parentName:"p"},"end")," of a file. Once you starting using these commands you'll find yourself using them regularly."),(0,i.kt)("p",null,"Let's start with ",(0,i.kt)("inlineCode",{parentName:"p"},"head"),". Imagine we have a data file which has been sent to us, we don't know exactly what is in it, but we know it is large. How can we take a quick look?"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ head ~/effective-shell/data/top100.csv\n\n"Rank","Rating","Title","Reviews"\n"1","97","Black Panther (2018)","515"\n"2","94","Avengers: Endgame (2019)","531"\n"3","93","Us (2019)","536"\n"4","97","Toy Story 4 (2019)","445"\n"5","99","Lady Bird (2017)","393"\n"6","100","Citizen Kane (1941)","94"\n"7","97","Mission: Impossible - Fallout (2018)","430"\n"8","98","The Wizard of Oz (1939)","120"\n"9","96","The Irishman (2019)","441"\n')),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"head")," command just shows the first ten lines of a file. Here we can see that this is a ",(0,i.kt)("em",{parentName:"p"},"comma separated values")," file which seems to be a list of movies. This file is actually a list of the top 100 films on 'Rotten Tomatoes' at the time of writing, with the score, tomato meter, name and number of votes. We'll use it a lot in this chapter to demonstrate text manipulation."),(0,i.kt)("p",null,"You can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-n")," flag to specify the number of lines you want to see, for example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ head -n 3 ~/effective-shell/data/top100.csv\n\n"Rank","Rating","Title","Reviews"\n"1","97","Black Panther (2018)","515"\n"2","94","Avengers: Endgame (2019)","531"\n')),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," command works in the same way - but looks at the ",(0,i.kt)("em",{parentName:"p"},"end")," of a file. This is more useful when you are looking content which changes over time, like log files. In this case you probably want to see only the most ",(0,i.kt)("em",{parentName:"p"},"recent")," entries."),(0,i.kt)("p",null,"Here's how we can see the ten most recent commands we entered in our shell:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ tail $HISTFILE\n\n: 1606818280:0;ls\n: 1606818300:0;ln -s $(pwd) ~/effective-shell\n: 1606818308:0;cat ~/effective-shell/data/top100.csv\n: 1606818342:0;head -n 3 ~/effective-shell/data/top100.csv\n: 1606819062:0;head ~/effective-shell/data/top100.csv\n: 1606819647:0;gcd\n: 1606819649:0;git stash\n: 1606819650:0;gcd\n: 1606819662:0;git stash pop\n: 1606819803:0;tail $HISTFILE\n")),(0,i.kt)("admonition",{title:"What is $HISTFILE?",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Most Bash-like shells keep a file called the ",(0,i.kt)("em",{parentName:"p"},"history")," file. This is essentially a record of all of the commands which have been written in the shell. The ",(0,i.kt)("inlineCode",{parentName:"p"},"history")," command can be used to show the contents of this file. But if we want to work with the file directly, we can find its location with the special variable called ",(0,i.kt)("inlineCode",{parentName:"p"},"$HISTFILE"),". "),(0,i.kt)("p",{parentName:"admonition"},"Enter ",(0,i.kt)("inlineCode",{parentName:"p"},"help history")," for more information on the shell history.")),(0,i.kt)("p",null,"We can be more specific, just like with ",(0,i.kt)("inlineCode",{parentName:"p"},"head"),", by specifying the number of lines to show:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ tail -n 3 $HISTFILE\n\n: 1606819650:0;gcd\n: 1606819662:0;git stash pop\n: 1606819803:0;tail $HISTFILE\n")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"tail")," can also be used to show the ",(0,i.kt)("em",{parentName:"p"},"changes")," to a file in real time. Add the ",(0,i.kt)("inlineCode",{parentName:"p"},"-f")," flag to ",(0,i.kt)("em",{parentName:"p"},"follow")," the contents of the file - this means the ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," command show each new line as it gets added to the file."),(0,i.kt)("p",null,"To try it out, run the following command in one shell:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ tail -f $HISTFILE\n")),(0,i.kt)("p",null,"In another terminal window, start entering commands. You'll see that the ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," command in the first window is writing the updates to the terminal as they are entered in the file. Press ",(0,i.kt)("inlineCode",{parentName:"p"},"Ctrl+C")," to close the ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," program."),(0,i.kt)("p",null,"Another trick I use a lot with ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," is to use ",(0,i.kt)("inlineCode",{parentName:"p"},"-n +2"),". This shows everything ",(0,i.kt)("em",{parentName:"p"},"from the second line")," - the ",(0,i.kt)("inlineCode",{parentName:"p"},"+")," symbol indicates we show everything from the given line onwards. This makes it easy to strip the header, or first line, from content. Here's how you might use it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ head ~/effective-shell/data/top100.csv | tail -n +2\n\n"1","97","Black Panther (2018)","515"\n"2","94","Avengers: Endgame (2019)","531"\n"3","93","Us (2019)","536"\n"4","97","Toy Story 4 (2019)","445"\n"5","99","Lady Bird (2017)","393"\n"6","100","Citizen Kane (1941)","94"\n"7","97","Mission: Impossible - Fallout (2018)","430"\n"8","98","The Wizard of Oz (1939)","120"\n"9","96","The Irishman (2019)","441"\n')),(0,i.kt)("p",null,"Here I've taken the ",(0,i.kt)("inlineCode",{parentName:"p"},"head")," of the file (otherwise the output gets quite difficult to follow), then piped the results into ",(0,i.kt)("inlineCode",{parentName:"p"},"tail -n +2")," to grab everything from the second line onwards - which removes the heading line. We see the films only, not the titles of each column."),(0,i.kt)("p",null,"We're going to use ",(0,i.kt)("inlineCode",{parentName:"p"},"head")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," quite a lot when working with text. These are two crucial tools which can really speed up your work."),(0,i.kt)("h2",{id:"replacing-text"},"Replacing Text"),(0,i.kt)("p",null,"The next tool we'll look at is ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," (",(0,i.kt)("em",{parentName:"p"},"translate characters"),"). This program is very simple. My most common use for ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," is to perform a simple substitution of characters."),(0,i.kt)("p",null,"Let's create a list of each of the columns in the data file we saw before to show how the command works:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ head -n 1 ~/effective-shell/data/top100.csv | tr \',\' \'\\n\'\n\n"Rank"\n"Rating"\n"Title"\n"Reviews"\n')),(0,i.kt)("p",null,"What about if we wanted to remove the quotes?"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ head -n 1 ~/effective-shell/data/top100.csv | tr ',' '\\n' | tr -d '\"'\n\nRank\nRating\nTitle\nReviews\n")),(0,i.kt)("p",null,"Here we've seen two variations on how we can run the command. The first form is used to ",(0,i.kt)("em",{parentName:"p"},"replace")," characters. Running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"tr ',' '\\n'\n")),(0,i.kt)("p",null,"Replaces the first specified character with the second. The ",(0,i.kt)("inlineCode",{parentName:"p"},"\\n")," character is the special ",(0,i.kt)("em",{parentName:"p"},"newline")," character, which is used to create a line break at the end of a file."),(0,i.kt)("p",null,"The second form uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"-d")," flag to specify a set of characters to delete:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"tr -d '\"'\n")),(0,i.kt)("p",null,"In the form above we delete quote (",(0,i.kt)("inlineCode",{parentName:"p"},'"'),") characters."),(0,i.kt)("p",null,"When using ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," remember that it works on ",(0,i.kt)("em",{parentName:"p"},"characters"),". For example, the following might not work as you expect:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo \"Welcome to the shell\" | tr 'shell' 'machine'\n\nWcicomc to tac macii\n")),(0,i.kt)("p",null,"The reason the output is like this is that we're specifying ",(0,i.kt)("em",{parentName:"p"},"character")," replacements - so we're changing characters as shown below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"s -> m\nh -> a\ne -> c\nl -> h\nl -> i\n")),(0,i.kt)("p",null,"There ",(0,i.kt)("em",{parentName:"p"},"are")," plenty of ways to replace entire words or perform more complex operations, but we'll use ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"awk")," for these operations - which we'll see in the following chapter."),(0,i.kt)("p",null,"There is one final thing it is worth mentioning about ",(0,i.kt)("inlineCode",{parentName:"p"},"tr"),". It can be provided with ",(0,i.kt)("em",{parentName:"p"},"character classes"),". This is easiest to explain with an example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo \"Use your inside voice...\" | tr '[[:lower:]]' '[[:upper:]]'\n\nUSE YOUR INSIDE VOICE...\n")),(0,i.kt)("p",null,"In this case we are transforming characters in the ",(0,i.kt)("inlineCode",{parentName:"p"},"lower")," class (lowercase characters) to the ",(0,i.kt)("inlineCode",{parentName:"p"},"upper")," class (uppercase characters)."),(0,i.kt)("p",null,"On Linux systems you can find more about character classes with ",(0,i.kt)("inlineCode",{parentName:"p"},"man 7 regex"),". I am not going to go deeper into character classes at this stage. They provide a simple way to specify things like digits, alphabetic characters and so on, but there are other ways to do this (with ",(0,i.kt)("em",{parentName:"p"},"extended regexes"),") which I think are likely to be more useful to learn about instead."),(0,i.kt)("h2",{id:"how-to-cut"},"How to Cut"),(0,i.kt)("p",null,"The next command is one which I've used far more than I expected. The ",(0,i.kt)("inlineCode",{parentName:"p"},"cut")," command ",(0,i.kt)("em",{parentName:"p"},"splits")," a line of text, using a given delimiter. Let's see some examples:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ cut -d\',\' -f 3 ~/effective-shell/data/top100.csv | head\n\n"Title"\n"Black Panther (2018)"\n"Avengers: Endgame (2019)"\n"Us (2019)"\n"Toy Story 4 (2019)"\n"Lady Bird (2017)"\n"Citizen Kane (1941)"\n"Mission: Impossible - Fallout (2018)"\n"The Wizard of Oz (1939)"\n"The Irishman (2019)"\n')),(0,i.kt)("p",null,"This is the first way to use ",(0,i.kt)("inlineCode",{parentName:"p"},"cut"),". We specify the ",(0,i.kt)("inlineCode",{parentName:"p"},"-d")," flag to choose a ",(0,i.kt)("em",{parentName:"p"},"delimiter")," which we will cut the text with, then ",(0,i.kt)("inlineCode",{parentName:"p"},"-f")," to choose ",(0,i.kt)("em",{parentName:"p"},"which field")," we want to see. In this case we show split on the command character and show the third field - the ",(0,i.kt)("em",{parentName:"p"},"title")," of the film in the data file."),(0,i.kt)("p",null,"This can be extraordinarily useful. Let's see how to get the names of the Kubernetes pods I have running on a cluster. I can use the following command to get the pods:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ kubectl get pods\n\nNAME READY STATUS RESTARTS AGE\nelastic-operator-0 1/1 Running 0 35d\nelk-apm-server-65b698fb8c-rzncz 1/1 Running 0 13d\nelk-es-default-0 1/1 Running 0 35d\nelk-kb-6f8bb6457b-bbbnn 1/1 Running 0 35d\nfilebeat-beat-filebeat-ccgl7 1/1 Running 1 13d\nfilebeat-beat-filebeat-dvf2l 1/1 Running 2 13d\nfilebeat-beat-filebeat-mnpms 1/1 Running 329 13d\nkube-state-metrics-5cb57bdc45-mqv9d 1/1 Running 0 35d\nmetricbeat-beat-metricbeat-2xm7t 1/1 Running 6103 35d\nmetricbeat-beat-metricbeat-96dkt 1/1 Running 6097 35d\nmetricbeat-beat-metricbeat-n7kxm 1/1 Running 6109 35d\n")),(0,i.kt)("p",null,"Now to get the name I can just ",(0,i.kt)("inlineCode",{parentName:"p"},"cut")," the lines on the 'space' character and grab the first field:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ kubectl get pods | cut -d' ' -f 1\n\nNAME\nelastic-operator-0\nelk-apm-server-65b698fb8c-rzncz\nelk-es-default-0\nelk-kb-6f8bb6457b-bbbnn\nfilebeat-beat-filebeat-ccgl7\nfilebeat-beat-filebeat-dvf2l\nfilebeat-beat-filebeat-mnpms\nkube-state-metrics-5cb57bdc45-mqv9d\nmetricbeat-beat-metricbeat-2xm7t\nmetricbeat-beat-metricbeat-96dkt\nmetricbeat-beat-metricbeat-n7kxm\n")),(0,i.kt)("p",null,"And if we want to strip the first line? We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"tail -n +2")," command to tail everything from the second line onwards:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ kubectl get pods | cut -d' ' -f 1 | tail -n +2\n\nelastic-operator-0\nelk-apm-server-65b698fb8c-rzncz\nelk-es-default-0\nelk-kb-6f8bb6457b-bbbnn\nfilebeat-beat-filebeat-ccgl7\nfilebeat-beat-filebeat-dvf2l\nfilebeat-beat-filebeat-mnpms\nkube-state-metrics-5cb57bdc45-mqv9d\nmetricbeat-beat-metricbeat-2xm7t\nmetricbeat-beat-metricbeat-96dkt\nmetricbeat-beat-metricbeat-n7kxm\n")),(0,i.kt)("p",null,"Bingo - we've removed the heading line. If you remember ",(0,i.kt)("inlineCode",{parentName:"p"},"grep")," from the previous chapter, you might have spotted that we could also just filter the content:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ kubectl get pods | cut -d' ' -f 1 | grep -v NAME\n\nelastic-operator-0\nelk-apm-server-65b698fb8c-rzncz\nelk-es-default-0\nelk-kb-6f8bb6457b-bbbnn\nfilebeat-beat-filebeat-ccgl7\nfilebeat-beat-filebeat-dvf2l\nfilebeat-beat-filebeat-mnpms\nkube-state-metrics-5cb57bdc45-mqv9d\nmetricbeat-beat-metricbeat-2xm7t\nmetricbeat-beat-metricbeat-96dkt\nmetricbeat-beat-metricbeat-n7kxm\n")),(0,i.kt)("p",null,"With even just a few simple shell commands there are often many ways to accomplish the same goal!"),(0,i.kt)("p",null,"There is another way we can ",(0,i.kt)("inlineCode",{parentName:"p"},"cut")," text. We can ",(0,i.kt)("inlineCode",{parentName:"p"},"cut")," by slicing a number of characters from each line."),(0,i.kt)("p",null,"Let's take a look at our web logs file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ tail ~/effective-shell/logs/web-server-logs.txt\n\n2020-11-29T12:50:52.721Z: info - Request: GET /en.search.min.1f83b222e24a227c0f5763727cb9e4f3b435f08b936f6ce529c9c9359f6b61a8.js\n2020-11-29T12:50:52.722Z: info - Serving file '../../../website/public/en.search.min.1f83b222e24a227c0f5763727cb9e4f3b435f08b936f6ce529c9c9359f6b61a8.js'...\n2020-11-29T12:50:52.762Z: info - Request: GET /svg/menu.svg\n2020-11-29T12:50:52.763Z: info - Serving file '../../../website/public/svg/menu.svg'...\n2020-11-29T12:50:52.763Z: info - Request: GET /svg/calendar.svg\n2020-11-29T12:50:52.764Z: info - Serving file '../../../website/public/svg/calendar.svg'...\n2020-11-29T12:50:52.765Z: info - Request: GET /svg/edit.svg\n2020-11-29T12:50:52.766Z: info - Serving file '../../../website/public/svg/edit.svg'...\n2020-11-29T12:50:52.784Z: info - Request: GET /fonts/roboto-v19-latin-300italic.woff2\n2020-11-29T12:50:52.785Z: info - Serving file '../../../website/public/fonts/roboto-v19-latin-300italic.woff2'...\n")),(0,i.kt)("p",null,"We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-c")," (",(0,i.kt)("em",{parentName:"p"},"characters"),") flag to specify the characters in the line we want to see. Let's extract the timestamp only:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ tail -n 3 ~/effective-shell/logs/web-server-logs.txt | cut -c 12-19\n\n12:50:52\n12:50:52\n12:50:52\n")),(0,i.kt)("p",null,"We can also use the character option to extract everything from a specific point onwards:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ tail -n 3 ~/effective-shell/logs/web-server-logs.txt | cut -c 27-\n\ninfo - Serving file '../../../website/public/svg/edit.svg'...\ninfo - Request: GET /fonts/roboto-v19-latin-300italic.woff2\ninfo - Serving file '../../../website/public/fonts/roboto-v19-latin-300italic.woff2'...\n")),(0,i.kt)("p",null,"By cutting from the 27th character onwards (",(0,i.kt)("inlineCode",{parentName:"p"},"-c 27-"),") we remove the timestamp and just get the log message."),(0,i.kt)("p",null,"As a nice trick you can use the same syntax when splitting by fields:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ tail -n 3 ~/effective-shell/data/top100.csv | cut -d\',\' -f 3-\n\n"Pinocchio (1940)","55"\n"Chinatown (1974)","75"\n"The Dark Knight (2008)","342"\n')),(0,i.kt)("p",null,"This is field three onwards. If we just want fields two and three, we use:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ tail -n 3 ~/effective-shell/data/top100.csv | cut -d\',\' -f 2,3\n\n"100","Pinocchio (1940)"\n"99","Chinatown (1974)"\n"94","The Dark Knight (2008)"\n')),(0,i.kt)("p",null,"There's a surprising amount you can do with the ",(0,i.kt)("inlineCode",{parentName:"p"},"cut")," tool. As we introduce more complex tools later on, like ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"awk"),", we'll see other ways to accomplish the same goals, but I often find that by filtering down the content with ",(0,i.kt)("inlineCode",{parentName:"p"},"grep")," first I can ",(0,i.kt)("inlineCode",{parentName:"p"},"cut")," my way to what I need without having to use more complex tools."),(0,i.kt)("h2",{id:"a-trick-with-rev"},"A Trick with Rev"),(0,i.kt)("p",null,"There is a very simple command called ",(0,i.kt)("inlineCode",{parentName:"p"},"rev")," which reverses the given input. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ echo "A nut for a jar of tuna" | rev\n\nanut fo raj a rof tun A\n')),(0,i.kt)("p",null,"At first glance this doesn't seem very useful - but there's a nice trick we can do with this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ pwd | rev | cut -d'/' -f 1 | rev\n\neffective-shell\n")),(0,i.kt)("p",null,"Here we take the current working directory, reverse it, cut the first field, then reverse it again. Here's what's happening at each stage:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"pwd /Users/dwmkerr/effective-shell\nrev llehs-evitceffe/rrekmwd/sresU/\ncut -d'/' -f 1 llehs-evitceffe\nrev effective-shell\n")),(0,i.kt)("p",null,"This is a neat trick to rip all of the text from the ",(0,i.kt)("em",{parentName:"p"},"final")," occurrence of a character. You might not use it very often but it's an interesting reminder that you can often do more than you think by chaining together simple commands into a pipeline!"),(0,i.kt)("h2",{id:"sort-and-unique"},"Sort and Unique"),(0,i.kt)("p",null,"Two other commands which can be really helpful are ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"uniq"),". Let's see ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," first:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ cut -d\',\' -f 3 ~/effective-shell/data/top100.csv | sort | head\n\n"12 Years a Slave (2013)"\n"A Hard Day\'s Night (1964)"\n"A Night at the Opera (1935)"\n"A Quiet Place (2018)"\n"A Star Is Born (2018)"\n"Alien (1979)"\n"All About Eve (1950)"\n"Argo (2012)"\n"Arrival (2016)"\n"Avengers: Endgame (2019)"\n')),(0,i.kt)("p",null,"Here we've grabbed the third field in our data file (the name of the film), sorted, then shown the first ten values."),(0,i.kt)("p",null,"You can ",(0,i.kt)("em",{parentName:"p"},"reverse")," the direction of ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-r")," flag:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ cut -d\',\' -f 3 ~/effective-shell/data/top100.csv | sort -r | head\n\n"Zootopia (2016)"\n"Wonder Woman (2017)"\n"Won\'t You Be My Neighbor? (2018)"\n"Widows (2018)"\n"War for the Planet of the Apes (2017)"\n"Us (2019)"\n"Up (2009)"\n"Toy Story 4 (2019)"\n"Toy Story 3 (2010)"\n"Toy Story 2 (1999)"\n')),(0,i.kt)("p",null,"There are actually quite a few other options for sort, you can see them with ",(0,i.kt)("inlineCode",{parentName:"p"},"man sort"),". However, most of them perform functionality which you can get from other tools (such as making the lines unique, which we can do with ",(0,i.kt)("inlineCode",{parentName:"p"},"uniq"),"). You might find some of them useful so don't be shy to explore some of the other options."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"uniq")," command removes duplicate lines from a stream of text. Note that this ",(0,i.kt)("em",{parentName:"p"},"only")," removes duplicate lines when they are ",(0,i.kt)("em",{parentName:"p"},"next to each other"),". This means that you will often have to ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," first."),(0,i.kt)("p",null,"Here's an example of where I might use ",(0,i.kt)("inlineCode",{parentName:"p"},"uniq")," - getting all unique error messages in a log file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cut -c 27- ~/effective-shell/logs/web-server-logs.txt | grep error | sort | uniq\n\nerror - Unhandled error EACCES trying to read '../../../website/public/docs/part-1-transitioning-to-the-shell/5-getting-help/index.html', returning a 500\nerror - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500\nerror - Unhandled error EACCES trying to read '../../../website/public/svg/edit.svg', returning a 500\ninfo - Request: GET /docs/1-getting-started/images/ls-applications-windows-error.png\ninfo - Request: GET /docs/part-1-transitioning-to-the-shell/3-managing-your-files/images/rm-error-directory.png\ninfo - Serving file '../../../website/public/docs/1-getting-started/images/ls-applications-windows-error.png'...\ninfo - Serving file '../../../website/public/docs/part-1-transitioning-to-the-shell/3-managing-your-files/images/rm-error-directory.png'...\n")),(0,i.kt)("p",null,"Let's break this down:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cut -c 27- ~/effective-shell/logs/web-server-logs.txt")," - extract log messages from a log file, skipping the timestamp"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"grep error")," - filter down to lines which contain the text ",(0,i.kt)("inlineCode",{parentName:"li"},"error")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sort")," - sort the output"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"uniq")," - show only unique values")),(0,i.kt)("p",null,"This is a powerful technique - if we had thousands of errors in the file, this would make sure we only see ",(0,i.kt)("em",{parentName:"p"},"distinct")," errors, rather than showing ",(0,i.kt)("em",{parentName:"p"},"every")," error."),(0,i.kt)("h2",{id:"dont-forget-your-pager"},"Don't Forget Your Pager!"),(0,i.kt)("p",null,"In ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/getting-help/"},"Chapter 5 - Getting Help")," we talked about the ",(0,i.kt)("em",{parentName:"p"},"pager")," - the program your shell uses to make it easier to look through larger text files, giving the option to move backwards and forwards a page at a time (or searching and so on). Don't forget to use your pager when you are working with text. When you are trying to build a pipeline and want to see intermediate results (perhaps ",(0,i.kt)("em",{parentName:"p"},"before")," you use ",(0,i.kt)("inlineCode",{parentName:"p"},"head")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"tail"),") then you can use the pager to avoid filling your screen and terminal with too much text."),(0,i.kt)("p",null,"For example, when looking at the sorted list of films, I might run this:"),(0,i.kt)("pre",null,"$ cut -d',' -f 3 ~/effective-shell/data/top100.csv | sort | less \"",(0,i.kt)("strong",null,"Jaws"),' (1975)" "King Kong (1933)" "La Grande illusion (Grand Illusion) (1938)" "La La Land (2016)" "Lady Bird (2017)" "Laura (1944)" /',(0,i.kt)("strong",null,"Jaws")),(0,i.kt)("p",null,"I've made the output smaller so that it is easier to see what is happening. In this example I've cut out the film name from my data file, sorted it, then piped the result into ",(0,i.kt)("inlineCode",{parentName:"p"},"less")," so that I can page through the data and ensure it is correct - I've also searched for the text ",(0,i.kt)("inlineCode",{parentName:"p"},"Jaws")," to see where it is in the file."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"In this chapter we introduced a number of basic tools which let us work with text."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"head")," will show the first ten lines of a file."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"head -n 30")," will show the first thirty lines of a file, using the ",(0,i.kt)("inlineCode",{parentName:"li"},"-n")," flag to specify the number of lines."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tail")," will show the final ten lines of a file."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tail -n 3")," uses the ",(0,i.kt)("inlineCode",{parentName:"li"},"-n")," flag to specify three lines only."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"$HISTFILE")," environment variable holds the path to the shell command history file."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tail -f $HISTFILE")," uses the ",(0,i.kt)("inlineCode",{parentName:"li"},"-f")," flag to ",(0,i.kt)("em",{parentName:"li"},"follow")," the file, printing output as it is written to the file."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tr 'a' 'b'")," is the ",(0,i.kt)("em",{parentName:"li"},"translate text")," command, which turns one set of characters into another"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tr -d '!'")," shows how the ",(0,i.kt)("inlineCode",{parentName:"li"},"-d")," or ",(0,i.kt)("em",{parentName:"li"},"delete")," flag can specify characters to delete."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"cut")," command can be used to extract parts of a line of text."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cut -d',' -f 3")," shows how the ",(0,i.kt)("inlineCode",{parentName:"li"},"-d")," or ",(0,i.kt)("em",{parentName:"li"},"delimiter")," flag is used to specify the delimiter to cut on and how the ",(0,i.kt)("inlineCode",{parentName:"li"},"-f")," or ",(0,i.kt)("em",{parentName:"li"},"field")," flag specifies which of the fields the text has been cut into is printed."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cut -c 2-4")," uses the ",(0,i.kt)("inlineCode",{parentName:"li"},"-c")," or ",(0,i.kt)("em",{parentName:"li"},"characters")," flag to specify that we are extracting a subset of characters in the line, in this case characters two to four."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cut -c 10-")," cuts from character ten to the end of the line"),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"cut")," command also allows for multiple fields to be specified when cutting by field, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"-f 2,3")," for the second and third field, or ",(0,i.kt)("inlineCode",{parentName:"li"},"-f 4-")," for fields four onwards."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"rev")," reverses text - by reversing, cutting and then re-reversing you can quickly extract text from the ",(0,i.kt)("em",{parentName:"li"},"end")," of a line."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sort")," sorts the incoming text alphabetically."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"-r")," flag for ",(0,i.kt)("inlineCode",{parentName:"li"},"sort")," reverses the sort order."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"uniq")," command removes duplicate lines - but only when they are next to each other, so you'll often use it in combination with ",(0,i.kt)("inlineCode",{parentName:"li"},"sort"),"."),(0,i.kt)("li",{parentName:"ul"},"Your pager, for example the ",(0,i.kt)("inlineCode",{parentName:"li"},"less")," program can be useful when inspecting the output of your text transformation commands.")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/30a42ea0.d4f4a310.js b/pr-preview/pr-346/assets/js/30a42ea0.d4f4a310.js new file mode 100644 index 00000000..85b82681 --- /dev/null +++ b/pr-preview/pr-346/assets/js/30a42ea0.d4f4a310.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[2736],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t =0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),h=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=h(e.components);return a.createElement(l.Provider,{value:t},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=h(n),m=o,u=d["".concat(l,".").concat(m)]||d[m]||c[m]||r;return n?a.createElement(u,i(i({ref:t},p),{},{components:n})):a.createElement(u,i({ref:t},p))}));function u(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:o,i[1]=s;for(var h=2;h {n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>h});var a=n(7462),o=(n(7294),n(3905));const r={title:"Navigating Your System",slug:"/part-1-transitioning-to-the-shell/navigating-your-system/"},i=void 0,s={unversionedId:"transitioning-to-the-shell/navigating-your-system/index",id:"transitioning-to-the-shell/navigating-your-system/index",title:"Navigating Your System",description:"Switching from a graphical user interface to the shell can take some getting used to. First we'll take a look at how to navigate your system using the shell, and get information on files and folders in the system.",source:"@site/docs/01-transitioning-to-the-shell/02-navigating-your-system/index.md",sourceDirName:"01-transitioning-to-the-shell/02-navigating-your-system",slug:"/part-1-transitioning-to-the-shell/navigating-your-system/",permalink:"/part-1-transitioning-to-the-shell/navigating-your-system/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/01-transitioning-to-the-shell/02-navigating-your-system/index.md",tags:[],version:"current",frontMatter:{title:"Navigating Your System",slug:"/part-1-transitioning-to-the-shell/navigating-your-system/"},sidebar:"sidebar",previous:{title:"Getting Started",permalink:"/part-1-transitioning-to-the-shell/getting-started/"},next:{title:"Managing Your Files",permalink:"/part-1-transitioning-to-the-shell/managing-your-files/"}},l={},h=[{value:"The Working Directory",id:"the-working-directory",level:2},{value:"Listing the Contents of the Working Directory",id:"listing-the-contents-of-the-working-directory",level:2},{value:"Changing the Directory",id:"changing-the-directory",level:2},{value:"Understanding Paths",id:"understanding-paths",level:2},{value:"The Special Dot and Dot Dot Folders",id:"the-special-dot-and-dot-dot-folders",level:2},{value:"The Home Directory",id:"the-home-directory",level:2},{value:"Pushing and Popping the Working Directory",id:"pushing-and-popping-the-working-directory",level:2},{value:"Going Back",id:"going-back",level:2},{value:"Summary",id:"summary",level:2}],p={toc:h};function d(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,a.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"Switching from a graphical user interface to the shell can take some getting used to. First we'll take a look at how to navigate your system using the shell, and get information on files and folders in the system."),(0,o.kt)("p",null,"This section will introduce the ",(0,o.kt)("inlineCode",{parentName:"p"},"pwd"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"ls"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"cd"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"pushd")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"popd"),' commands, as well as the concepts of the "working directory" and "environment variables". We\'ll also take a bit of a look into how "Paths" work.'),(0,o.kt)("p",null,"If these commands are familiar to you then feel free to jump to the next chapter! Otherwise, let's get started."),(0,o.kt)("h2",{id:"the-working-directory"},"The Working Directory"),(0,o.kt)("p",null,"Perhaps the easiest way to start to understand how to navigate your system using the shell is to use a graphical interface as an illustration of how we often navigate. Open your shell, and enter the following command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"pwd\n")),(0,o.kt)("p",null,"You should see something like this:"),(0,o.kt)("img",{alt:"Screenshot: pwd",src:n(3993).Z,width:"800px"}),(0,o.kt)("p",null,"When we open a folder in a graphical user interface, we are always viewing the contents of a folder, or directory. When you open the shell, the same applies - we are always sitting in a specific directory."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"pwd")," command is the ",(0,o.kt)("em",{parentName:"p"},"Print Working Directory")," command. It shows the full path of the directory that you are in."),(0,o.kt)("p",null,"There's one more way to find the working directory. It is stored in an ",(0,o.kt)("em",{parentName:"p"},"Environment Variable")," called ",(0,o.kt)("inlineCode",{parentName:"p"},"PWD"),"."),(0,o.kt)("p",null,"An environment variable is just a bit of data that you can access from your shell. You can create them, you can change them, and there are some which are set for you by the system or the shell to help you out."),(0,o.kt)("p",null,"Try the following command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'echo "My current working directory is: $PWD"\n')),(0,o.kt)("p",null,"You should see something like this:"),(0,o.kt)("img",{alt:"Screenshot: PWD Environment Variable",src:n(2028).Z,width:"800px"}),(0,o.kt)("p",null,"The dollar symbol is used to tell the shell we want to use the ",(0,o.kt)("inlineCode",{parentName:"p"},"PWD")," variable, not write out the ",(0,o.kt)("em",{parentName:"p"},"text")," ",(0,o.kt)("inlineCode",{parentName:"p"},"PWD"),". We'll see a lot more about environment variables as we continue through the book. "),(0,o.kt)("h2",{id:"listing-the-contents-of-the-working-directory"},"Listing the Contents of the Working Directory"),(0,o.kt)("p",null,"In the graphical user interface, we can also see the files and folders in the current directory. In the shell, we don't see this content. But we can show the contents of the current working directory with the following command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ls\n")),(0,o.kt)("p",null,"You should see something like this:"),(0,o.kt)("img",{alt:"Screenshot: ls",src:n(4282).Z,width:"800px"}),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," command is the ",(0,o.kt)("em",{parentName:"p"},"List Directory Contents")," command. It will show the contents of a directory. If we don't give it any parameters, it will show the contents of the current directory."),(0,o.kt)("p",null,"There are a lot of options for the ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," command. For now, let's look at one of the most common options ",(0,o.kt)("inlineCode",{parentName:"p"},"-l"),". This shows the contents as a list:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ls -l\n")),(0,o.kt)("img",{alt:"Screenshot: ls -l",src:n(1742).Z,width:"800px"}),(0,o.kt)("p",null,"A little like the 'details' view in a graphical user interface, this list view shows us more details, such as who owns the file or folder, when it was modified, and more. Again, we'll see more details on this later."),(0,o.kt)("h2",{id:"changing-the-directory"},"Changing the Directory"),(0,o.kt)("p",null,"In a graphical user interface, you move to a different directory by clicking on it."),(0,o.kt)("p",null,"In the shell, you run the ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," command. Try it out with:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# Move to the pictures directory...\ncd Pictures\n\n# ...then list the contents of the directory.\n# Note that the '-al' flags mean show *all* files, as a *list*.\nls -al\n")),(0,o.kt)("p",null,'Note that when you see shell commands, everything which starts with a hash symbol is a comment. These comments are just for readability, you don\'t need to include them. But if you are saving your own shell snippets (or "scripts"), then you might find comments a useful way to remind yourself of what you are hoping to achieve with the commands, or to make the script more readable.'),(0,o.kt)("p",null,"On my system, we'll see the following output:"),(0,o.kt)("img",{alt:"Screenshot: cd",src:n(7964).Z,width:"800px"}),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," command is the ",(0,o.kt)("em",{parentName:"p"},"Change Directory")," command. You might see a pattern here - shell commands often are very short (to make it easier to type them quickly) and are often made up of the first letters of the description of the command (",(0,o.kt)("inlineCode",{parentName:"p"},"pwd")," for ",(0,o.kt)("em",{parentName:"p"},"Print Working Directory"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," for ",(0,o.kt)("em",{parentName:"p"},"Change Directory"),")."),(0,o.kt)("p",null,"Now that you know how the ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," command works, you will be able to move around to different folders. At this stage, it's important to talk a little bit about how ",(0,o.kt)("em",{parentName:"p"},"paths")," work in systems."),(0,o.kt)("h2",{id:"understanding-paths"},"Understanding Paths"),(0,o.kt)("p",null,"In Linux, Windows and MacOS (and most other operating systems), ",(0,o.kt)("em",{parentName:"p"},"paths")," are the 'addresses' of files or folders."),(0,o.kt)("p",null,"There are two types of paths - ",(0,o.kt)("em",{parentName:"p"},"Absolute Paths")," and ",(0,o.kt)("em",{parentName:"p"},"Relative Paths"),". An absolute path is one which gives the exact location of a file. For example, on my computer, the absolute path to the folder I am writing this book in is:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"/Users/dwmkerr/repos/github/dwmkerr/effective-shell\n")),(0,o.kt)("p",null,"Absolute paths ",(0,o.kt)("em",{parentName:"p"},"always")," start with a slash. That's how the system knows it is an absolute path. The ",(0,o.kt)("inlineCode",{parentName:"p"},"/")," is the ",(0,o.kt)("em",{parentName:"p"},"root")," of the file system - basically it's the folder which ",(0,o.kt)("em",{parentName:"p"},"everything")," else lives in."),(0,o.kt)("p",null,"If I have an absolute path, I know ",(0,o.kt)("em",{parentName:"p"},"exactly")," where the file or folder is. Let's compare this to a ",(0,o.kt)("em",{parentName:"p"},"relative path"),". Below is the ",(0,o.kt)("em",{parentName:"p"},"relative path")," in my shell for the file I'm writing right now:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"website/content/docs/part-1-transitioning-to-the-shell\n")),(0,o.kt)("p",null,"This path is ",(0,o.kt)("em",{parentName:"p"},"relative")," to my current working directory in the shell. This means that this path only makes sense if you use it from a specific directory. If I am in my ",(0,o.kt)("inlineCode",{parentName:"p"},"Pictures")," folder, and I want to move to the ",(0,o.kt)("inlineCode",{parentName:"p"},"2020-photos")," folder, I could do it in two ways. The first is with an absolute path:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd /Users/dwmkerr/Pictures/2020-photos\n")),(0,o.kt)("p",null,"The second is with a relative path:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd 2020-photos\n")),(0,o.kt)("p",null,"In short - relative paths are often useful if you want to move to something ",(0,o.kt)("em",{parentName:"p"},"within the current directory")," and absolute paths are useful if you need to move to ",(0,o.kt)("em",{parentName:"p"},"somewhere completely different"),"."),(0,o.kt)("h2",{id:"the-special-dot-and-dot-dot-folders"},"The Special Dot and Dot Dot Folders"),(0,o.kt)("p",null,"As you experiment with these commands, you might have noticed that every folder contains two other folders, one with the name ",(0,o.kt)("inlineCode",{parentName:"p"},".")," and one with the name ",(0,o.kt)("inlineCode",{parentName:"p"},".."),". Run ",(0,o.kt)("inlineCode",{parentName:"p"},"ls -al")," on the ",(0,o.kt)("inlineCode",{parentName:"p"},"pictures")," folder to see an example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"ls -al pictures\n")),(0,o.kt)("p",null,"You should see something like this:"),(0,o.kt)("img",{alt:"Screenshot: Special Dot Folders",src:n(7590).Z,width:"800px"}),(0,o.kt)("p",null,"This picture highlights two special folders - ",(0,o.kt)("inlineCode",{parentName:"p"},".")," and ",(0,o.kt)("inlineCode",{parentName:"p"},".."),". These are special folders which exist in ",(0,o.kt)("em",{parentName:"p"},"every")," folder in the system."),(0,o.kt)("p",null,"The first folder, ",(0,o.kt)("inlineCode",{parentName:"p"},"."),', represents the folder it is in. Why would this be useful? Well, sometimes we just want a quick way to say the equivalent of "right here" in a command. For example, if I wanted to copy the current folder to a backup folder, I could do this:'),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cp . /backup\n")),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"cp")," command is the ",(0,o.kt)("em",{parentName:"p"},"Copy")," command, and we'll see it in the next chapter. But the key thing to note is that we can use ",(0,o.kt)("inlineCode",{parentName:"p"},".")," to tell the command to copy the folder we are in right now."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"..")," folder means ",(0,o.kt)("em",{parentName:"p"},"the parent folder"),'. You can use this to "go up" to the parent folder, for example:'),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd ..\nls .\n")),(0,o.kt)("p",null,"Would give:"),(0,o.kt)("img",{alt:"Screenshot: cd dot dot",src:n(7341).Z,width:"800px"}),(0,o.kt)("p",null,"Note that we've used ",(0,o.kt)("inlineCode",{parentName:"p"},"cd ..")," to ",(0,o.kt)("em",{parentName:"p"},"change directory to the parent folder")," then ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," to ",(0,o.kt)("em",{parentName:"p"},"list the contents of the current folder"),". We could also just have used ",(0,o.kt)("inlineCode",{parentName:"p"},"ls")," on its own as it defaults to the current folder."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"..")," folder can be helpful if you need to navigate to a location which is outside of your current folder. For example, if I am in the ",(0,o.kt)("inlineCode",{parentName:"p"},"pictures")," folder and I want to move to the ",(0,o.kt)("inlineCode",{parentName:"p"},"scripts")," folder, I can just use:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd ../scripts\nls\n")),(0,o.kt)("p",null,"And we'll see this:"),(0,o.kt)("img",{alt:"Screenshot: cd dot dot scripts",src:n(9194).Z,width:"800px"}),(0,o.kt)("h2",{id:"the-home-directory"},"The Home Directory"),(0,o.kt)("p",null,"There is one more special part of the file system we have to know about. That is the ",(0,o.kt)("em",{parentName:"p"},"Home Directory"),". In Linux-like systems every user has their own personal directory where they can keep their files and folders."),(0,o.kt)("p",null,"This directory can always be accessed through the ",(0,o.kt)("inlineCode",{parentName:"p"},"~")," character. For example, no matter where I am in the system, I can run the following command to move to my home directory and show the contents:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd ~\nls\n")),(0,o.kt)("p",null,"This would show something like this:"),(0,o.kt)("img",{alt:"Screenshot: cd home",src:n(2065).Z,width:"800px"}),(0,o.kt)("p",null,"This makes moving around your home directory very easy. For example, on a Mac, to go to your pictures folder from anywhere, you can always just run:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd ~/Pictures\n")),(0,o.kt)("p",null,"Your home directory on most computers will be where you keep your documents, pictures, videos and so on. Normally this directory is ",(0,o.kt)("em",{parentName:"p"},"not accessible")," to other users of the system. Each user in a system gets their own home directory."),(0,o.kt)("p",null,"You can also see the home directory by using the special ",(0,o.kt)("inlineCode",{parentName:"p"},"HOME")," environment variable:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'echo "My home directory is: $HOME"\n')),(0,o.kt)("p",null,"This would show something like this:"),(0,o.kt)("img",{alt:"Screenshot: echo home",src:n(5478).Z,width:"800px"}),(0,o.kt)("p",null,"One useful trick - running ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," without any parameters will always take you home! So to go home, just run:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd\n")),(0,o.kt)("p",null,"Now that we know about relative paths, absolute paths, and the special dot and dot dot folders, and the home directory we can continue learning how to navigate the shell!"),(0,o.kt)("h2",{id:"pushing-and-popping-the-working-directory"},"Pushing and Popping the Working Directory"),(0,o.kt)("p",null,"One thing we might want to do is quickly move from one location to another, then go back again. Let's say for example I am working in on this chapter, but I want to check my downloads. One way to do this is with this ",(0,o.kt)("inlineCode",{parentName:"p"},"pushd")," command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"pushd ~/Downloads\nls\npopd\n")),(0,o.kt)("p",null,"After I've checked my downloads, I can run ",(0,o.kt)("inlineCode",{parentName:"p"},"popd")," to go back to where I was:"),(0,o.kt)("img",{alt:"Screenshot: pushd and popd",src:n(171).Z,width:"800px"}),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"pushd")," command 'pushes' a new working directory onto a stack - moving you there. The ",(0,o.kt)("inlineCode",{parentName:"p"},"popd")," command 'pops' the working directory off the top of the stack. A stack is a structure often used in computers; we can actually push lots of different files to the working directory stack."),(0,o.kt)("p",null,"Why is it called a stack? Well, the reason is that if we were to visualise the structure, it might look like a stack of plates or similar. Here's how ",(0,o.kt)("inlineCode",{parentName:"p"},"pushd")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"popd")," would look if we were to visualise it:"),(0,o.kt)("img",{alt:"Screenshot: pushd popd stack",src:n(2766).Z,width:"800px"}),(0,o.kt)("p",null,"These commands can be useful if you need to move to other locations but want to be able to quickly go back to where you were before afterwards."),(0,o.kt)("h2",{id:"going-back"},"Going Back"),(0,o.kt)("p",null,"One last trick which can save time is the following command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cd -\n")),(0,o.kt)("p",null,"This is a special parameter for ",(0,o.kt)("inlineCode",{parentName:"p"},"cd")," which tells it to ",(0,o.kt)("em",{parentName:"p"},"go back")," to the last location you moved to. Here's how it might look if you use it:"),(0,o.kt)("img",{alt:"Screenshot: cd dash",src:n(2619).Z,width:"800px"}),(0,o.kt)("p",null,"This can only be used to go back to the last directory. If you need to be able to go backwards multiple times or through a history of directories, you might need to use ",(0,o.kt)("inlineCode",{parentName:"p"},"pushd")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"popd")," instead."),(0,o.kt)("h2",{id:"summary"},"Summary"),(0,o.kt)("p",null,"In this chapter we introduced the following:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"pwd")," (",(0,o.kt)("em",{parentName:"li"},"print working directory"),") command shows the current working directory"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"$PWD")," environment variable holds the current working directory"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"ls")," (",(0,o.kt)("em",{parentName:"li"},"list"),") command shows the contents of the current directory or a given directory"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"ls -l")," command shows the contents of the current directory as list"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"cd")," (",(0,o.kt)("em",{parentName:"li"},"change directory"),") changes the current working directory"),(0,o.kt)("li",{parentName:"ul"},"Absolute paths are paths which specify the exact location of a file or folder..."),(0,o.kt)("li",{parentName:"ul"},"...Relative paths are paths which are relative to the current directory"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},".")," special folder means 'this folder'"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"..")," special folder means 'the parent folder'"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"~")," special folder is the 'home directory'"),(0,o.kt)("li",{parentName:"ul"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"$HOME")," environment variable holds the user's home directory"),(0,o.kt)("li",{parentName:"ul"},"You can run ",(0,o.kt)("inlineCode",{parentName:"li"},"cd")," at any time to quickly go to your home directory"),(0,o.kt)("li",{parentName:"ul"},"You can use ",(0,o.kt)("inlineCode",{parentName:"li"},"pushd")," and ",(0,o.kt)("inlineCode",{parentName:"li"},"popd")," to push and pop the working directory stack"),(0,o.kt)("li",{parentName:"ul"},"You can use the ",(0,o.kt)("inlineCode",{parentName:"li"},"cd -")," command to go back to the last location")))}d.isMDXComponent=!0},2619:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cd-dash-53e29f0f8230e4ec479620a71dc61a77.png"},9194:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cd-dot-dot-scripts-4fddc6579b157b2474429d88080cdad5.png"},7341:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cd-dot-dot-c778020499ddc2c49509232cdc15c9f1.png"},2065:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cd-home-cb88b95f55c0104849cff3bdd76f0e6a.png"},7964:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cd-11037ff9f63cacc208544113f72c2910.png"},5478:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/echo-home-6c8165310a37202fc6efe3eaa14a8456.png"},1742:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-l-feba8ca271f4f43e1580bd6445adfba3.png"},4282:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-620b55bfd11e8f1bbb53da22b569b2ee.png"},2766:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/pushd-popd-stack-ccd34132d513841c5b1d97c842b0413f.png"},171:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/pushd-popd-f69004387269ce04326a8d62cc60a635.png"},2028:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/pwd-env-var-6f01c6f9ac876cd2d9f8c91b2ed141b6.png"},3993:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/pwd-b4ec8f59e6ea3997888a4919fdd3dbb0.png"},7590:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/special-dot-folders-3e278df76dd750bfb6f0fec3d003bd97.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/30e8b9a3.498e82cd.js b/pr-preview/pr-346/assets/js/30e8b9a3.498e82cd.js new file mode 100644 index 00000000..ee1d5200 --- /dev/null +++ b/pr-preview/pr-346/assets/js/30e8b9a3.498e82cd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[9594],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>g});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t =0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var d=n.createContext({}),s=function(e){var t=n.useContext(d),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(d.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,d=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=s(r),p=a,g=c["".concat(d,".").concat(p)]||c[p]||m[p]||i;return r?n.createElement(g,o(o({ref:t},u),{},{components:r})):n.createElement(g,o({ref:t},u))}));function g(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,o=new Array(i);o[0]=p;var l={};for(var d in t)hasOwnProperty.call(t,d)&&(l[d]=t[d]);l.originalType=e,l[c]="string"==typeof e?e:a,o[1]=l;for(var s=2;s{r.r(t),r.d(t,{default:()=>i});var n=r(7294),a=r(2389);function i(e){let{children:t,fallback:r}=e;return(0,a.Z)()?n.createElement(n.Fragment,null,t?.()):r??null}},3973:(e,t,r)=>{var n=r(1262),a=r(7294);function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=i(n),l=i(a);!function(e,t){void 0===t&&(t={});var r=t.insertAt;if(e&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css","top"===r&&n.firstChild?n.insertBefore(a,n.firstChild):n.appendChild(a),a.styleSheet?a.styleSheet.cssText=e:a.appendChild(document.createTextNode(e))}}(".docusaurus-plugin-drawio {\n width: 100%;\n padding: 10px;\n border: 1px solid #ccc;\n text-align: center;\n overflow-x: auto;\n}\n\nhtml[data-theme='dark'] .docusaurus-plugin-drawio {\n background-color: #333;\n color: #fff;\n}\n\n.docusaurus-plugin-drawio > div {\n margin-left: auto;\n margin-right: auto;\n border: 1px solid transparent;\n min-width: 180px;\n}\n");var d=function(e){var t=e.content,r=a.useState("loading..."),n=r[0],i=r[1],o=a.useRef(null),d=window.GraphViewer;return a.useEffect((function(){if(d)if(t){var e={editable:"_blank",highlight:"#0000ff",nav:!0,resize:!0,toolbar:"zoom lightbox",xml:t},r=JSON.stringify(e);o.current.dataset.mxgraph=r,i(""),setTimeout((function(){d.createViewerForElement(o.current)}),0)}else i("drawio file is empty");else i("GraphViewer is not loaded")}),[]),l.default.createElement("div",{className:"docusaurus-plugin-drawio"},l.default.createElement("div",{className:"docusaurus-plugin-drawio__content",ref:o},n))},s=a.memo((function(e){var t=e.content;return l.default.createElement(o.default,{fallback:l.default.createElement(l.default.Fragment,null,"loading...")},(function(){return l.default.createElement(d,{content:t})}))}));e.exports=s},3844:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>g,frontMatter:()=>d,metadata:()=>u,toc:()=>m});var n=r(7462),a=(r(7294),r(3905)),i=r(3973),o=r.n(i),l=r(8210);const d={title:"Images and Diagrams"},s=void 0,u={unversionedId:"zz-developer-guide/images-and-diagrams",id:"zz-developer-guide/images-and-diagrams",title:"Images and Diagrams",description:"Images",source:"@site/docs/zz-developer-guide/images-and-diagrams.mdx",sourceDirName:"zz-developer-guide",slug:"/zz-developer-guide/images-and-diagrams",permalink:"/zz-developer-guide/images-and-diagrams",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/zz-developer-guide/images-and-diagrams.mdx",tags:[],version:"current",frontMatter:{title:"Images and Diagrams"},sidebar:"sidebar",previous:{title:"Components",permalink:"/zz-developer-guide/components"},next:{title:"Markdown",permalink:"/zz-developer-guide/markdown-guide"}},c={},m=[{value:"Images",id:"images",level:2},{value:"Diagrams",id:"diagrams",level:2}],p={toc:m};function g(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h2",{id:"images"},"Images"),(0,a.kt)("admonition",{title:"Not in Use",type:"info"},(0,a.kt)("p",{parentName:"admonition"},"Ideal Image is not currently enabled as it appears to clash with native Docusaurus lazy-loading.")),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-ideal-image"},(0,a.kt)("inlineCode",{parentName:"a"},"@docusaurus/plugin-ideal-image"))," is used to allow lazy loading and zoom of images."),(0,a.kt)("p",null,"Use the ",(0,a.kt)("inlineCode",{parentName:"p"},"Image")," tag as shown:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-markdown"},"import Image from '@theme/IdealImage';\n \n")),(0,a.kt)("p",null,"This will render an image as shown below:"),(0,a.kt)("p",null,"::: warn Currently Disabled"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"}," \n")),(0,a.kt)("p",null,":::"),(0,a.kt)("h2",{id:"diagrams"},"Diagrams"),(0,a.kt)("p",null,"Render a Draw.io diagram like so:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"import Drawio from '@theme/Drawio'\nimport asymmetricEncryption from '!!raw-loader!./images/asymmetric-encryption.drawio';\n\n \n")),(0,a.kt)("p",null,"This would render as below:"),(0,a.kt)(o(),{content:l.Z,mdxType:"Drawio"}))}g.isMDXComponent=!0},8210:(e,t,r)=>{r.d(t,{Z:()=>n});const n=' '}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/33510158.82c8c92b.js b/pr-preview/pr-346/assets/js/33510158.82c8c92b.js new file mode 100644 index 00000000..d1ced948 --- /dev/null +++ b/pr-preview/pr-346/assets/js/33510158.82c8c92b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[6868],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>k});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t 3VfbUtswEP2aPJKRrTikj+QC7RQGpulM26eOYiu2GtvryjKJ+fqubPmGk0A7gYHygnS02mjP2V1LAzqLdleSJcENeDwc2MTbDeh8YNuWRcb4TyN5iUzsSQn4UnjGqAGW4oEbkBg0Ex5PO4YKIFQi6YIuxDF3VQdjUsK2a7aGsPurCfN5D1i6LOyj34Sngiqu8Ydm4SMXfqCq+M7LhYhVxiaSNGAebFsQXQzoTAKochTtZjzU5FW8lPsuD6zWB5M8Vs/Z8EV+yleLW0c6V78p8ZQD/OeZieKehZkJ+E6Ke6b4wB6H6Ha6kjjy9egzz00gKq/YwZgSPYwBd9DpNhCKLxPmamyLCYFYoKIQZ5b2xdyNLyGLvdtMhSLmBveY3NziLqF0kpAhcRA0B+NS8d3BiK2aR0xADhFXEg9Jqg0jQ73JPWqNyvm2UXJiTIKWhhYxIDPJ49euG35xYCj+C7qdHoPcw3QzU5AqAB9iFi4adFoQxrVXgrPG5hogMQT+4krlpnZYpqBLO7Il8+9mfzH5UdDsVNP5rr04z82sPKs+4HH+MR7IpMuPxF1VLpM+V0fs6H49JQ+ZEvfdc5xcnHGvFpY89hBZxK7ME8X1+IanqW4Z+3S8Zivsfh3uWSj8GMcuUsYlAjqhBbaXC7MQCc8rZeapeGCrwp9mPwERqyJEZzpw5scqwvQ+s7npOG2lDqfjwfI5I0OLTGjp69kSGHd3+vitUqTdHbBep5gJjyWrD/HvKlo9FY18bfGwA2ADsEnd7Mi+7tatvCeaW6obWOx/LYqSNMA1X6suMgWlIOpiODtV07O7Tc+ummCr6dU27a43fqmmZ/cUweR3+yVUfUyyKLxwFbSrpaisO0iFEqCrZmUo7JWTgkeyQPmlmdXXA3IimsdOh2ZrD830NVmmPZbn/HDeZyuU4H9K+9HkraX9qCfIFFbvO+kd+40l/aTHcY/gFi1PEtv7WofaclpfW2cQaoXQLV0XfwfFSJWETf1isGuk5YGQCbksaqF6GJDiLpwGde1VyRHtfP26GgpIz4cCP/fpMAR3o+1Oc08edWSl1nlPVnvUl5W+lKzV46/9LDEd6/2/Shzyeq8SnDYPzPKG1TzT6eIP =0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},m=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,p=e.parentName,m=o(e,["components","mdxType","originalType","parentName"]),c=s(n),u=r,k=c["".concat(p,".").concat(u)]||c[u]||d[u]||i;return n?a.createElement(k,l(l({ref:t},m),{},{components:n})):a.createElement(k,l({ref:t},m))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,l=new Array(i);l[0]=u;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o[c]="string"==typeof e?e:r,l[1]=o;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>c,frontMatter:()=>i,metadata:()=>o,toc:()=>s});var a=n(7462),r=(n(7294),n(3905));const i={},l=void 0,o={unversionedId:"xx-appendices/essential-manpages",id:"xx-appendices/essential-manpages",title:"essential-manpages",description:"The Most Important Manpages",source:"@site/docs/xx-appendices/essential-manpages.md",sourceDirName:"xx-appendices",slug:"/xx-appendices/essential-manpages",permalink:"/xx-appendices/essential-manpages",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/xx-appendices/essential-manpages.md",tags:[],version:"current",frontMatter:{}},p={},s=[{value:"The Most Important Manpages",id:"the-most-important-manpages",level:3}],m={toc:s};function c(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h3",{id:"the-most-important-manpages"},"The Most Important Manpages"),(0,r.kt)("p",null,"As an appendix, or printed reference, list of the top ten manpages?"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man re_pattern")," - basic and extended regex patterns"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man test")," is an excellent way to quickly check common tests (existence of a file etc)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man set")," is super useful when checking options like ",(0,r.kt)("inlineCode",{parentName:"li"},"set -ex")," in scripts"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man re_format")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man getopt")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man XXX")," show signal commands (",(0,r.kt)("inlineCode",{parentName:"li"},"Ctrl+V")," etc)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man bash")," search for ",(0,r.kt)("inlineCode",{parentName:"li"},"ARITHMETIC\\ EVALUATION")," to find how arithmetic operators work in bash"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man bash")," search ",(0,r.kt)("inlineCode",{parentName:"li"},"GRAMMAR")," for pipelines, if statements, conditionals, loops, lists and so on"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man bash")," search for ",(0,r.kt)("inlineCode",{parentName:"li"},"^EXPANSION")," to see all shell expansion operators"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man bash")," search for ",(0,r.kt)("inlineCode",{parentName:"li"},"^INVOCATION")," to find details on startup and the startup files that are read"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"man bash")," search for ",(0,r.kt)("inlineCode",{parentName:"li"},"^[ ]+shopt")," to find descriptions of shell options")),(0,r.kt)("p",null,"Essential Bash Manpages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Manpage"),(0,r.kt)("th",{parentName:"tr",align:null},"Search"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"man bash")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"^PROMPTING")),(0,r.kt)("td",{parentName:"tr",align:null},"Details on how to set the ",(0,r.kt)("inlineCode",{parentName:"td"},"PS1")," prompt.")))),(0,r.kt)("p",null,"Essential Z-Shell Manpages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Manpage"),(0,r.kt)("th",{parentName:"tr",align:null},"Search"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"man zshmisc")),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"PROMPT\\ SEQUENCES")),(0,r.kt)("td",{parentName:"tr",align:null},"Details on how to set the ",(0,r.kt)("inlineCode",{parentName:"td"},"PS1")," prompt.")))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/3bea7d02.980d04cb.js b/pr-preview/pr-346/assets/js/3bea7d02.980d04cb.js new file mode 100644 index 00000000..6a43d2ce --- /dev/null +++ b/pr-preview/pr-346/assets/js/3bea7d02.980d04cb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[7348],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t =0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=l(n),h=o,f=d["".concat(c,".").concat(h)]||d[h]||p[h]||a;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=h;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[d]="string"==typeof e?e:o,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var r=n(7462),o=(n(7294),n(3905));const a={title:"Part 6 - Advanced Techniques",slug:"/part-6-advanced-techniques/"},i=void 0,s={unversionedId:"advanced-techniques/index",id:"advanced-techniques/index",title:"Part 6 - Advanced Techniques",description:"At this stage you are well on your way to shell mastery. Each of the chapters in this section goes introduces either an advanced technique to help you be more effective in the shell, or goes considerably deeper into one of the topics that we have already covered so that you can really understand the fundamentals.",source:"@site/docs/06-advanced-techniques/00-index.md",sourceDirName:"06-advanced-techniques",slug:"/part-6-advanced-techniques/",permalink:"/part-6-advanced-techniques/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/06-advanced-techniques/00-index.md",tags:[],version:"current",sidebarPosition:0,frontMatter:{title:"Part 6 - Advanced Techniques",slug:"/part-6-advanced-techniques/"},sidebar:"sidebar",previous:{title:"Managing Remote Git Repositories and Sharing Your Dotfiles",permalink:"/part-5-building-your-toolkit/managing-rempte-git-repositories/"},next:{title:"Understanding Shell Expansion",permalink:"/part-6-advanced-techniques/understanding-shell-expansion/"}},c={},l=[],u={toc:l};function d(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"At this stage you are well on your way to shell mastery. Each of the chapters in this section goes introduces either an advanced technique to help you be more effective in the shell, or goes considerably deeper into one of the topics that we have already covered so that you can really understand the fundamentals."),(0,o.kt)("p",null,"Depending on the kind of work you do in the shell, you might find some chapters more useful than others. For example, if you are a software developer you might find the chapter on 'Makefiles' of particular interest, and if you administer remote systems, you should definitely read up on 'Multiplexers'. Feel free to dip into this section of the book in the order that suits you or that you find the most interesting!"))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/3c89468b.b6f70c8a.js b/pr-preview/pr-346/assets/js/3c89468b.b6f70c8a.js new file mode 100644 index 00000000..8875812d --- /dev/null +++ b/pr-preview/pr-346/assets/js/3c89468b.b6f70c8a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[6165],{3905:(e,t,o)=>{o.d(t,{Zo:()=>h,kt:()=>m});var a=o(7294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,a)}return o}function i(e){for(var t=1;t =0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var s=a.createContext({}),u=function(e){var t=a.useContext(s),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},h=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var o=e.components,n=e.mdxType,r=e.originalType,s=e.parentName,h=l(e,["components","mdxType","originalType","parentName"]),c=u(o),p=n,m=c["".concat(s,".").concat(p)]||c[p]||d[p]||r;return o?a.createElement(m,i(i({ref:t},h),{},{components:o})):a.createElement(m,i({ref:t},h))}));function m(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=o.length,i=new Array(r);i[0]=p;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[c]="string"==typeof e?e:n,i[1]=l;for(var u=2;u {o.d(t,{v:()=>r});var a=o(7294),n=function(e){var t=e.elementId,o=e.url,n=e.title,r=e.subtitle;return a.createElement("div",{className:"subscribe-container"},a.createElement("div",{id:"".concat(t,"mc_embed_signup")},a.createElement("form",{action:o,method:"post",id:"".concat(t,"mc-embedded-subscribe-form"),name:"mc-embedded-subscribe-form",className:"validate",target:"_blank"},a.createElement("div",{id:"".concat(t,"mc_embed_signup_scroll")},a.createElement("label",{className:"subscribe-container__title",htmlFor:"mce-EMAIL"},n),r&&a.createElement("p",{className:"subscribe-container__subtitle"},r),a.createElement("input",{type:"email",defaultValue:"",name:"EMAIL",className:"subscribe-container__email",id:"".concat(t,"mce-EMAIL"),placeholder:"Your email address",required:!0}),a.createElement("div",{style:{position:"absolute",left:"-5000px"},"aria-hidden":"true"},a.createElement("input",{type:"text",name:"b_5f0b91c96bbdf35913a136639_ddfba3375e",tabIndex:-1,defaultValue:""})),a.createElement("div",null,a.createElement("button",{type:"submit",defaultValue:"Subscribe",name:"subscribe",id:"".concat(t,"mc-embedded-subscribe"),className:"subscribe-container__submit"},"Subscribe"))))))};const r=()=>a.createElement(n,{elementId:"email-signup-form",url:"https://effective-shell.us19.list-manage.com/subscribe/post?u=eac1a082b6db34d40aaff2caf&id=20c9542b27",title:"Subscribe for Updates",subtitle:"If you would like to get email updates when new chapters are published, please do provide your email below. I won't be using it for anything beyond updates to the book."})},8847:(e,t,o)=>{o.r(t),o.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>h});var a=o(7462),n=(o(7294),o(3905)),r=o(1123);const i={title:"Effective Shell",leanpub_title:"Introduction",leanpub_header:"{sample: true}\n{class: part}",sidebar_position:1,sidebar_label:"Introduction",slug:"/"},l=void 0,s={unversionedId:"index",id:"index",title:"Effective Shell",description:"Investing just a few hours in your ability to use text based interfaces for your computer can have an enormous impact on your productivity. It can also make your work more fun, allowing you to maintain that creative 'flow' state that can make technology so exciting.",source:"@site/docs/00-index.mdx",sourceDirName:".",slug:"/",permalink:"/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/00-index.mdx",tags:[],version:"current",sidebarPosition:1,frontMatter:{title:"Effective Shell",leanpub_title:"Introduction",leanpub_header:"{sample: true}\n{class: part}",sidebar_position:1,sidebar_label:"Introduction",slug:"/"},sidebar:"sidebar",next:{title:"Part 1 - Transitioning to the Shell",permalink:"/part-1-transitioning-to-the-shell/"}},u={};(0,n.kt)(r.v,null);const h=[{value:"Who Should Read This Book",id:"who-should-read-this-book",level:2},{value:"What's In This Book?",id:"whats-in-this-book",level:2},{value:"What You'll Need",id:"what-youll-need",level:2},{value:"How To Read This Book",id:"how-to-read-this-book",level:2},{value:"Email Updates",id:"email-updates",level:2}],c={toc:h};function d(e){let{components:t,...o}=e;return(0,n.kt)("wrapper",(0,a.Z)({},c,o,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"Investing just a few hours in your ability to use text based interfaces for your computer can have an enormous impact on your productivity. It can also make your work more fun, allowing you to maintain that creative 'flow' state that can make technology so exciting."),(0,n.kt)("p",null,"I've been lucky enough to spend many years working as a software engineer, but also with scientists, data engineers, site reliability engineers and technologists of all sorts. One thing that has stood out about great technologists has been their ability to make their tools work for them, stitching them together in creative ways that suits their style."),(0,n.kt)("p",null,"This is not a book on shell scripting or Linux administration! Each chapter in this book can be read as a standalone set of techniques to help you be more efficient, understand your system in more depth, and craft your environment to suit your working style. This book does not advocate that you totally change the way you work or drop your current tooling; it just brings together a set of skills that you can add to your toolkit and incorporate in your own way."),(0,n.kt)("p",null,"If you find this book useful, please consider ",(0,n.kt)("a",{parentName:"p",href:"/donate"},"donating")," to support my open source work."),(0,n.kt)("h2",{id:"who-should-read-this-book"},"Who Should Read This Book"),(0,n.kt)("p",null,"Developers and engineers should find almost every chapter of this book immediately applicable in day-to-day work. Whether you use Python, Golang, .NET, Java, whether you use an IDE or the terminal, these skills will help in day-to-day work."),(0,n.kt)("p",null,"Site reliability engineers, system administrators and DevSecOps professionals will find these skills essential - if you regularly administer remote machines, connect to containers, manage clusters or cloud environments, you will find many techniques to help you with your work."),(0,n.kt)("p",null,"Hobbyists, polymaths and explorers should also read on - as well as going into specific techniques, each chapter gives essential knowledge on how these computers, operating systems and networking works."),(0,n.kt)("h2",{id:"whats-in-this-book"},"What's In This Book?"),(0,n.kt)("p",null,"Each chapter of this book aims to work as a stand-alone description of a set of techniques that you should be able to apply immediately. I have focused on keeping the information to the essentials that allow you to use the skill, rather than create an exhaustive description of every possible feature. This should allow you to pick up the book and read a chapter over a coffee and try out the skills straight away."),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"/part-1-transitioning-to-the-shell/"},(0,n.kt)("em",{parentName:"a"},"Part 1 - Transitioning to the Shell"))," is aimed at people who are new to the shell. You'll learn how to set up your environment, navigate your system, manage files, move between a desktop environment and the shell and how to get help. More advanced users may want to skip some of this content."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"/part-2-core-skill/"},(0,n.kt)("em",{parentName:"a"},"Part 2 - Core Skills"))," introduces the essential techniques that you should be able to apply immediately to improve your productivity. You'll learn how to use pipelines, rapidly move around the command-line and commands quickly, manage multiple tasks in parallel with jobs, the different types of commands that are available and how to search for files."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"/part-3-manipulating-text/"},(0,n.kt)("em",{parentName:"a"},"Part 3 - Manipulating Text and Streams"))," demonstrates many techniques to work with text -whether it's code, data or configuration. You'll learn how to use regular expressions, how to search through text, how to slice and dice text, how to manipulate code and data, and then how to apply these techniques to your shell commands themselves."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"/part-4-shell-scripting/"},(0,n.kt)("em",{parentName:"a"},"Part 4 - Shell Scripting"))," is a crash course in shell scripting. You'll learn how to write and run scripts, read and process input, perform logical operations, iterate over files and folders, build functions, handle errors and a set of re-usable patterns you can apply to your own scripts."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"/part-5-building-your-toolkit/"},(0,n.kt)("em",{parentName:"a"},"Part 5 - Building Your Toolkit"))," goes into the techniques you can use to create and customize your own environment. You'll learn how to configure the shell, customize your command prompt, manage your configuration files and share and manage your configuration with the Git version control system."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"/part-6-advanced-techniques/"},(0,n.kt)("em",{parentName:"a"},"Part 6 - Advanced Techniques"))," introduces even more powerful tools and techniques, showing the fundamentals of each skill and how you might incorporate it into your work. You'll learn how shell expansion works, which is key to understanding many of the nuances of complex commands, when you should move away from shell scripts and into a full-blown programming language, how to build programs that integrate well into other tools in the shell, how to connect to other machines with the secure shell, how to use the popular terminal editor Vim and how to use terminal multiplexers. ")),(0,n.kt)("p",null,"For the newcomer, you'll learn what a shell is, how to use it on your system, and then how to become more effective everyday by integrating the shell into your work. For the experienced professional, there is a wealth of detailed tips and tricks in each chapter that go into advanced topics and techniques to make you even more of a power user."),(0,n.kt)("h2",{id:"what-youll-need"},"What You'll Need"),(0,n.kt)("p",null,"If you are using a computer, you have enough to get started! In ",(0,n.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/getting-started/"},(0,n.kt)("em",{parentName:"a"},"Chapter 1 - Setting Up Your Shell Environment"))," you'll learn how to setup your shell in Windows, Linux or MacOS. "),(0,n.kt)("p",null,"Run the following command in your shell to download the samples:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-bash"},"curl effective.sh | sh\n")),(0,n.kt)("p",null,"No Linux, shell or programming knowledge is required to use this book, all underlying concepts will be explained as we go along. Advanced users will be able to skip some of the explanations, but there is also enough depth in each chapter that users of all levels should be able to learn something new. "),(0,n.kt)("h2",{id:"how-to-read-this-book"},"How To Read This Book"),(0,n.kt)("p",null,"Commands that you can type into your shell, such as ",(0,n.kt)("inlineCode",{parentName:"p"},"grep")," are shown as ",(0,n.kt)("inlineCode",{parentName:"p"},"monospaced text"),". Paths to file and folders, such as the ",(0,n.kt)("em",{parentName:"p"},"~/effective-shell")," folder are shown in ",(0,n.kt)("em",{parentName:"p"},"italics"),"."),(0,n.kt)("p",null,"In larger code samples, the dollar sign is used to indicate where you would start typing - this is the command prompt. The text that you type is shown in ",(0,n.kt)("inlineCode",{parentName:"p"},"**bold**"),":"),(0,n.kt)("pre",null,"$ ",(0,n.kt)("strong",null,'echo "my shell is $SHELL"'),(0,n.kt)("br",null),"my shell is /bin/bash",(0,n.kt)("br",null)),(0,n.kt)("p",null,"This book assumes that you are using a Bash-like shell, most of these shells should operate in a similar way. However, given the popularity of the Z shell (Zsh) are called out like so:"),(0,n.kt)("admonition",{title:"Z shell",type:"info"},(0,n.kt)("p",{parentName:"admonition"},"Z shell specifics are highlighted like this.")),(0,n.kt)("h2",{id:"email-updates"},"Email Updates"),(0,n.kt)("p",null,"If you would like to get email updates when new chapters are published, please do provide your email below. I won't be using it for anything beyond updates to the book."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/44e90db8.dd6d13fe.js b/pr-preview/pr-346/assets/js/44e90db8.dd6d13fe.js new file mode 100644 index 00000000..1dd1dfd7 --- /dev/null +++ b/pr-preview/pr-346/assets/js/44e90db8.dd6d13fe.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[5927],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>b});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t =0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=s(r),m=o,b=p["".concat(l,".").concat(m)]||p[m]||d[m]||a;return r?n.createElement(b,i(i({ref:t},u),{},{components:r})):n.createElement(b,i({ref:t},u))}));function b(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[p]="string"==typeof e?e:o,i[1]=c;for(var s=2;s{r.d(t,{v:()=>a});var n=r(7294),o=function(e){var t=e.elementId,r=e.url,o=e.title,a=e.subtitle;return n.createElement("div",{className:"subscribe-container"},n.createElement("div",{id:"".concat(t,"mc_embed_signup")},n.createElement("form",{action:r,method:"post",id:"".concat(t,"mc-embedded-subscribe-form"),name:"mc-embedded-subscribe-form",className:"validate",target:"_blank"},n.createElement("div",{id:"".concat(t,"mc_embed_signup_scroll")},n.createElement("label",{className:"subscribe-container__title",htmlFor:"mce-EMAIL"},o),a&&n.createElement("p",{className:"subscribe-container__subtitle"},a),n.createElement("input",{type:"email",defaultValue:"",name:"EMAIL",className:"subscribe-container__email",id:"".concat(t,"mce-EMAIL"),placeholder:"Your email address",required:!0}),n.createElement("div",{style:{position:"absolute",left:"-5000px"},"aria-hidden":"true"},n.createElement("input",{type:"text",name:"b_5f0b91c96bbdf35913a136639_ddfba3375e",tabIndex:-1,defaultValue:""})),n.createElement("div",null,n.createElement("button",{type:"submit",defaultValue:"Subscribe",name:"subscribe",id:"".concat(t,"mc-embedded-subscribe"),className:"subscribe-container__submit"},"Subscribe"))))))};const a=()=>n.createElement(o,{elementId:"email-signup-form",url:"https://effective-shell.us19.list-manage.com/subscribe/post?u=eac1a082b6db34d40aaff2caf&id=20c9542b27",title:"Subscribe for Updates",subtitle:"If you would like to get email updates when new chapters are published, please do provide your email below. I won't be using it for anything beyond updates to the book."})},32:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>c,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var n=r(7462),o=(r(7294),r(3905)),a=r(1123);const i={title:"Work in Progress!",slug:"work-in-progress"},c=void 0,l={unversionedId:"work-in-progress",id:"work-in-progress",title:"Work in Progress!",description:"If you have landed here, then most likely you have clicked a link to a chapter which has not yet been completed. Sorry about that!",source:"@site/docs/work-in-progress.mdx",sourceDirName:".",slug:"/work-in-progress",permalink:"/work-in-progress",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/work-in-progress.mdx",tags:[],version:"current",frontMatter:{title:"Work in Progress!",slug:"work-in-progress"}},s={};(0,o.kt)(a.v,null);const u=[],p={toc:u};function d(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"If you have landed here, then most likely you have clicked a link to a chapter which has not yet been completed. Sorry about that!"),(0,o.kt)("p",null,"I'm trying to get a few chapters published each week, check back regularly to see if the section you are looking for is completed."),(0,o.kt)("p",null,"You can also sign up with the form below to be notified when I publish new chapters (I don't use this for anything beyond notifications of updates to the book and don't share any details)."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/491d70ff.258b0e28.js b/pr-preview/pr-346/assets/js/491d70ff.258b0e28.js new file mode 100644 index 00000000..b92d7f45 --- /dev/null +++ b/pr-preview/pr-346/assets/js/491d70ff.258b0e28.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[868],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>k});var r=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function o(e){for(var t=1;t =0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r =0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var s=r.createContext({}),m=function(e){var t=r.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},d=function(e){var t=m(e.components);return r.createElement(s.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,i=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),p=m(a),c=n,k=p["".concat(s,".").concat(c)]||p[c]||u[c]||i;return a?r.createElement(k,o(o({ref:t},d),{},{components:a})):r.createElement(k,o({ref:t},d))}));function k(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=a.length,o=new Array(i);o[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[p]="string"==typeof e?e:n,o[1]=l;for(var m=2;m{a.r(t),a.d(t,{assets:()=>m,contentTitle:()=>l,default:()=>k,frontMatter:()=>o,metadata:()=>s,toc:()=>d});var r=a(7462),n=(a(7294),a(3905)),i=a(8210);const o={title:"Markdown"},l=void 0,s={unversionedId:"zz-developer-guide/markdown-guide",id:"zz-developer-guide/markdown-guide",title:"Markdown",description:"The contents of this book are primarily written in MDX. Some key concepts to be aware of:",source:"@site/docs/zz-developer-guide/markdown-guide.mdx",sourceDirName:"zz-developer-guide",slug:"/zz-developer-guide/markdown-guide",permalink:"/zz-developer-guide/markdown-guide",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/zz-developer-guide/markdown-guide.mdx",tags:[],version:"current",frontMatter:{title:"Markdown"},sidebar:"sidebar",previous:{title:"Images and Diagrams",permalink:"/zz-developer-guide/images-and-diagrams"},next:{title:"Recording Terminal Sessions",permalink:"/zz-developer-guide/recording-terminal-sessions"}},m={},d=[{value:"Links",id:"links",level:2},{value:"Emphasis",id:"emphasis",level:2},{value:"Asides / Admonitions / Blurbs",id:"asides--admonitions--blurbs",level:2},{value:"Markua Tags",id:"markua-tags",level:2},{value:"Titles",id:"titles",level:2},{value:"Images",id:"images",level:2},{value:"Images",id:"images-1",level:2},{value:"Diagrams",id:"diagrams",level:2}],p=(u="Drawio",function(e){return console.warn("Component "+u+" was not imported, exported, or provided by MDXProvider as global scope"),(0,n.kt)("div",e)});var u;const c={toc:d};function k(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"The contents of this book are primarily written in MDX. Some key concepts to be aware of:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://github.com/mdx-js/specification"},"MDX")," is the language that most files in the ",(0,n.kt)("inlineCode",{parentName:"li"},"./doc")," folder are written in"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://commonmark.org/"},"Commonmark")," is the standard markdown spec that MDX is based on"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://github.github.com/gfm/"},"GitHub Flavored Markdown")," is also based on Commonmark"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://leanpub.com/markua/read"},"Markua")," is used internally for the Leanpub distribution")),(0,n.kt)("p",null,"Essentially this means that for the book to render properly in Docusaurus, preview nicely in GitHub, and be able to be transformed into a format for use with Leanpub, some specific rules should be followed."),(0,n.kt)("h2",{id:"links"},"Links"),(0,n.kt)("p",null,"TODO: links should be explicit e.g. ",(0,n.kt)("inlineCode",{parentName:"p"},"00-index.md")," rather than ",(0,n.kt)("inlineCode",{parentName:"p"},"index.md")),(0,n.kt)("h2",{id:"emphasis"},"Emphasis"),(0,n.kt)("p",null,"TODO: Use ",(0,n.kt)("em",{parentName:"p"},"single stars for italics")," and ",(0,n.kt)("strong",{parentName:"p"},"double stars for bold")," - because Markua confusingly uses ",(0,n.kt)("em",{parentName:"p"},"single underscores for underline"),"."),(0,n.kt)("h2",{id:"asides--admonitions--blurbs"},"Asides / Admonitions / Blurbs"),(0,n.kt)("p",null,"TODO"),(0,n.kt)("h2",{id:"markua-tags"},"Markua Tags"),(0,n.kt)("p",null,"TODO"),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"frontmatter")," marks content that is considered part of the introduction or preface. It is numbered in the manuscript with numerals such as ",(0,n.kt)("em",{parentName:"p"},"i"),", ",(0,n.kt)("em",{parentName:"p"},"ii"),", ",(0,n.kt)("em",{parentName:"p"},"xii"),"."),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"mainmatter")," indicates content that is part of the main book, it is numbered with numerals."),(0,n.kt)("p",null,(0,n.kt)("inlineCode",{parentName:"p"},"backmatter")," todo"),(0,n.kt)("h2",{id:"titles"},"Titles"),(0,n.kt)("p",null,"Frontmatter"),(0,n.kt)("h2",{id:"images"},"Images"),(0,n.kt)("p",null,"TODO image names must be unique"),(0,n.kt)("h2",{id:"images-1"},"Images"),(0,n.kt)("admonition",{title:"Not in Use",type:"info"},(0,n.kt)("p",{parentName:"admonition"},"Ideal Image is not currently enabled as it appears to clash with native Docusaurus lazy-loading.")),(0,n.kt)("p",null,"The ",(0,n.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-ideal-image"},(0,n.kt)("inlineCode",{parentName:"a"},"@docusaurus/plugin-ideal-image"))," is used to allow lazy loading and zoom of images."),(0,n.kt)("p",null,"Use the ",(0,n.kt)("inlineCode",{parentName:"p"},"Image")," tag as shown:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-markdown"},"import Image from '@theme/IdealImage';\n \n")),(0,n.kt)("p",null,"This will render an image as shown below:"),(0,n.kt)("p",null,"::: warn Currently Disabled"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre"}," \n")),(0,n.kt)("p",null,":::"),(0,n.kt)("h2",{id:"diagrams"},"Diagrams"),(0,n.kt)("p",null,"Render a Draw.io diagram like so:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre"},"import Drawio from '@theme/Drawio'\nimport asymmetricEncryption from '!!raw-loader!./images/asymmetric-encryption.drawio';\n\n \n")),(0,n.kt)("p",null,"This would render as below:"),(0,n.kt)(p,{content:i.Z,mdxType:"Drawio"}))}k.isMDXComponent=!0},8210:(e,t,a)=>{a.d(t,{Z:()=>r});const r=' '}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/494f22af.953c11c2.js b/pr-preview/pr-346/assets/js/494f22af.953c11c2.js new file mode 100644 index 00000000..b3fe4363 --- /dev/null +++ b/pr-preview/pr-346/assets/js/494f22af.953c11c2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[4246],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>m});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function s(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t 3VfbUtswEP2aPJKRrTikj+QC7RQGpulM26eOYiu2GtvryjKJ+fqubPmGk0A7gYHygnS02mjP2V1LAzqLdleSJcENeDwc2MTbDeh8YNuWRcb4TyN5iUzsSQn4UnjGqAGW4oEbkBg0Ex5PO4YKIFQi6YIuxDF3VQdjUsK2a7aGsPurCfN5D1i6LOyj34Sngiqu8Ydm4SMXfqCq+M7LhYhVxiaSNGAebFsQXQzoTAKochTtZjzU5FW8lPsuD6zWB5M8Vs/Z8EV+yleLW0c6V78p8ZQD/OeZieKehZkJ+E6Ke6b4wB6H6Ha6kjjy9egzz00gKq/YwZgSPYwBd9DpNhCKLxPmamyLCYFYoKIQZ5b2xdyNLyGLvdtMhSLmBveY3NziLqF0kpAhcRA0B+NS8d3BiK2aR0xADhFXEg9Jqg0jQ73JPWqNyvm2UXJiTIKWhhYxIDPJ49euG35xYCj+C7qdHoPcw3QzU5AqAB9iFi4adFoQxrVXgrPG5hogMQT+4krlpnZYpqBLO7Il8+9mfzH5UdDsVNP5rr04z82sPKs+4HH+MR7IpMuPxF1VLpM+V0fs6H49JQ+ZEvfdc5xcnHGvFpY89hBZxK7ME8X1+IanqW4Z+3S8Zivsfh3uWSj8GMcuUsYlAjqhBbaXC7MQCc8rZeapeGCrwp9mPwERqyJEZzpw5scqwvQ+s7npOG2lDqfjwfI5I0OLTGjp69kSGHd3+vitUqTdHbBep5gJjyWrD/HvKlo9FY18bfGwA2ADsEnd7Mi+7tatvCeaW6obWOx/LYqSNMA1X6suMgWlIOpiODtV07O7Tc+ummCr6dU27a43fqmmZ/cUweR3+yVUfUyyKLxwFbSrpaisO0iFEqCrZmUo7JWTgkeyQPmlmdXXA3IimsdOh2ZrD830NVmmPZbn/HDeZyuU4H9K+9HkraX9qCfIFFbvO+kd+40l/aTHcY/gFi1PEtv7WofaclpfW2cQaoXQLV0XfwfFSJWETf1isGuk5YGQCbksaqF6GJDiLpwGde1VyRHtfP26GgpIz4cCP/fpMAR3o+1Oc08edWSl1nlPVnvUl5W+lKzV46/9LDEd6/2/Shzyeq8SnDYPzPKG1TzT6eIP =0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var i=n.createContext({}),h=function(e){var t=n.useContext(i),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},c=function(e){var t=h(e.components);return n.createElement(i.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,s=e.originalType,i=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),p=h(a),d=r,m=p["".concat(i,".").concat(d)]||p[d]||u[d]||s;return a?n.createElement(m,l(l({ref:t},c),{},{components:a})):n.createElement(m,l({ref:t},c))}));function m(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var s=a.length,l=new Array(s);l[0]=d;var o={};for(var i in t)hasOwnProperty.call(t,i)&&(o[i]=t[i]);o.originalType=e,o[p]="string"==typeof e?e:r,l[1]=o;for(var h=2;h {a.r(t),a.d(t,{assets:()=>i,contentTitle:()=>l,default:()=>p,frontMatter:()=>s,metadata:()=>o,toc:()=>h});var n=a(7462),r=(a(7294),a(3905));const s={title:"Regex Essentials",slug:"/part-3-manipulating-text/regex-essentials/"},l=void 0,o={unversionedId:"manipulating-text/regex-essentials/index",id:"manipulating-text/regex-essentials/index",title:"Regex Essentials",description:"Many of the tools we're going to introduce in this part of the book support regular expressions or regexes - a sophisticated language which allows us to describe different patterns of text.",source:"@site/docs/03-manipulating-text/13-regex-essentials/index.md",sourceDirName:"03-manipulating-text/13-regex-essentials",slug:"/part-3-manipulating-text/regex-essentials/",permalink:"/part-3-manipulating-text/regex-essentials/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/03-manipulating-text/13-regex-essentials/index.md",tags:[],version:"current",frontMatter:{title:"Regex Essentials",slug:"/part-3-manipulating-text/regex-essentials/"},sidebar:"sidebar",previous:{title:"Part 3 - Manipulating Text and Streams",permalink:"/part-3-manipulating-text/"},next:{title:"Get to Grips with Grep",permalink:"/part-3-manipulating-text/get-to-grips-with-grep/"}},i={},h=[{value:"An Introduction to Regular Expressions",id:"an-introduction-to-regular-expressions",level:2},{value:"Managing Complexity with Regular Expressions",id:"managing-complexity-with-regular-expressions",level:2},{value:"Building Regexes - Start with the Basics",id:"building-regexes---start-with-the-basics",level:3},{value:"Building Regexes - Quantifiers",id:"building-regexes---quantifiers",level:3},{value:"Building Regexes - Character Sets and Metacharacters",id:"building-regexes---character-sets-and-metacharacters",level:3},{value:"Building Regexes - Anchors",id:"building-regexes---anchors",level:3},{value:"Building Regexes - Capture Groups",id:"building-regexes---capture-groups",level:3},{value:"Building Regexes - Lazy and Greedy Expressions",id:"building-regexes---lazy-and-greedy-expressions",level:3},{value:"Avoiding Advanced Topics - Backtracking, Lookarounds and Atomic Grouping",id:"avoiding-advanced-topics---backtracking-lookarounds-and-atomic-grouping",level:2},{value:"A Word of Warning",id:"a-word-of-warning",level:2},{value:"Summary",id:"summary",level:2}],c={toc:h};function p(e){let{components:t,...s}=e;return(0,r.kt)("wrapper",(0,n.Z)({},c,s,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"Many of the tools we're going to introduce in this part of the book support ",(0,r.kt)("em",{parentName:"p"},"regular expressions")," or regexes - a sophisticated language which allows us to describe different patterns of text."),(0,r.kt)("p",null,"Before we look at how to use regular expressions in the shell, it is important to understand some of the basic regular expression concepts and techniques. This chapter covers the essentials - if you are already familiar with regular expressions feel free to skip to the next chapter."),(0,r.kt)("p",null,"In this chapter we'll look at why regular expressions can be intimidating, how to manage complexity and not overwhelm yourself, some of the core concepts in regular expressions and a few things to watch out for. Once we've seen the theory we'll be able to apply it in practice in the following chapters!"),(0,r.kt)("h2",{id:"an-introduction-to-regular-expressions"},"An Introduction to Regular Expressions"),(0,r.kt)("p",null,"Regular Expressions (or ",(0,r.kt)("em",{parentName:"p"},"regexs"),") are special 'patterns' which describe text. They are infamous (or even notorious) amongst technologists as being complex and opaque. For many years I avoided regular expressions as I found them overly complicated and hard to reason about. But over time I discovered that used carefully, they can be incredibly powerful and useful."),(0,r.kt)("p",null,"It is no surprise that regular expressions can be seen as opaque. Let's say we wanted to find a way to see whether an arbitrary string matches the structure of a valid email address. A quick search on the internet will find a regular expression such as this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\n\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?\n:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[\n0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0\n-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\\nx7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])\n")),(0,r.kt)("p",null,"Note that I have split the expression into multiple lines so that it fits on the page."),(0,r.kt)("p",null,"This is ",(0,r.kt)("em",{parentName:"p"},"horrendously")," complex. It is extremely long, almost impossible for even an experienced user to parse or reason about, and attempting to change or modify it would be very risky."),(0,r.kt)("p",null,"Many people see examples like the above and decide (probably quite sensibly) that regular expressions are something they just simply will not learn - they're too complex."),(0,r.kt)("p",null,"So should we learn regular expressions? Are they even valuable if they are as complex as the example above?"),(0,r.kt)("h2",{id:"managing-complexity-with-regular-expressions"},"Managing Complexity with Regular Expressions"),(0,r.kt)("p",null,"Regular expressions do not have to be as complex as the example above. In fact, in most cases they shouldn't be. My general advice for regular expressions is ",(0,r.kt)("em",{parentName:"p"},"start simple")," and add complexity only if you need it."),(0,r.kt)("p",null,"We can build regular expressions using an 'iterative' process, starting with the basics, then adding more features as we need them. An invaluable tool I have used for this is the website ",(0,r.kt)("a",{parentName:"p",href:"https://regex101.com"},"regex101.com"),". This website not only lets you test regular expressions, it also breaks down how they work so that you can reason about what is happening."),(0,r.kt)("p",null,"Let's take validating an email address as an example. The way I would build a regular expression to validate an email address would be to use the following steps:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Create a small list of valid email address"),(0,r.kt)("li",{parentName:"ol"},"Add some items to the list which look 'kind of' valid but are not quite right"),(0,r.kt)("li",{parentName:"ol"},"Build a regular expression which matches the correct email address"),(0,r.kt)("li",{parentName:"ol"},"Refine the expression to eliminate the invalid addresses")),(0,r.kt)("p",null,"In most cases this will be sufficient."),(0,r.kt)("p",null,"Let's start with the following set of addresses:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dave@effective-shell.com\ndave@effective-shell\nto: dave@effective-shell.com\ndave@effective-shell.com\ntest123.effective-shell.com\n@yahoo.com\nwhatever123@\ud83d\ude02.com\ndave@kerr@effective.shell.com\n")),(0,r.kt)("p",null,"Some are valid, some are not. Some you might not be sure about - such as the one with the emoji. This one is valid if someone sets up a mail server which can handle it, but probably not a good address to use as some mail programs or servers will reject it (Gmail for example, at time of writing, would allow you to send and receive from an email address like this, but not create an email address like this)."),(0,r.kt)("h3",{id:"building-regexes---start-with-the-basics"},"Building Regexes - Start with the Basics"),(0,r.kt)("p",null,"Here's how I would start building a regex for an email address:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Any set of characters"),(0,r.kt)("li",{parentName:"ol"},"Followed by an ",(0,r.kt)("inlineCode",{parentName:"li"},"@")," at symbol"),(0,r.kt)("li",{parentName:"ol"},"Followed by any set of characters")),(0,r.kt)("p",null,"That regex would look like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},".*@.*\n")),(0,r.kt)("p",null,"The first bit, ",(0,r.kt)("inlineCode",{parentName:"p"},".*")," means 'any character' (this is what the ",(0,r.kt)("inlineCode",{parentName:"p"},".")," dot symbol means), 'any number of times (this is what the ",(0,r.kt)("inlineCode",{parentName:"p"},"*")," asterisk symbol means)."),(0,r.kt)("p",null,"The second bit is just the literal ",(0,r.kt)("inlineCode",{parentName:"p"},"@")," at symbol character."),(0,r.kt)("p",null,"The third bit is the same as the first - any characters any number of times."),(0,r.kt)("p",null,"Let's see how that would look in ",(0,r.kt)("a",{parentName:"p",href:"https://regex101.com"},"regex101"),":"),(0,r.kt)("img",{src:a(8544).Z,alt:"Regular Expression Example 1",width:"1024px"}),(0,r.kt)("p",null,"Here we can see in blue the lines that the regex has matched. We can also see which part of each line corresponds to which part of the expression. But perhaps the most useful thing we see is the 'explanation' on the right which explains exactly what each character does."),(0,r.kt)("p",null,"Now that we have a basic pattern which matches the valid address, we can refine it to eliminate invalid addresses."),(0,r.kt)("p",null,"Note that from this point onwards we'll not show screenshots of the results as you would see them in the regex101 website, instead we'll just highlight the matched part of the text in ",(0,r.kt)("strong",{parentName:"p"},"bold"),", this should make it easier to read. But to see a breakdown of how each part of the text is matched and what each part of the pattern means, feel free to run the examples in the regex101 website!"),(0,r.kt)("h3",{id:"building-regexes---quantifiers"},"Building Regexes - Quantifiers"),(0,r.kt)("p",null,"The regular expression we have is very simple - ",(0,r.kt)("inlineCode",{parentName:"p"},".*@.*"),". The complexity in regular expressions tends to come from the fact that we need to handle 'edge cases' and be very explicit about what we can and cannot allow."),(0,r.kt)("p",null,"Let's see how we can refine this expression further to eliminate some of the invalid addresses. Let's start with ",(0,r.kt)("inlineCode",{parentName:"p"},"@yahoo.com"),". It doesn't have anything before the at symbol."),(0,r.kt)("p",null,"This is being matched by our pattern because our pattern allows any characters before and after the at symbol ",(0,r.kt)("em",{parentName:"p"},"any number of times")," - including ",(0,r.kt)("em",{parentName:"p"},"zero times"),"."),(0,r.kt)("p",null,"Let's change number of characters before and after the at symbol to be 'between one and many'. To do this we use a different ",(0,r.kt)("em",{parentName:"p"},"quantifier")," (a 'quantifier' is the part of a pattern which says 'how many occurrences of the characters do we expect)."),(0,r.kt)("p",null,"Previously we used the ",(0,r.kt)("inlineCode",{parentName:"p"},"*")," asterisk quantifier (which means 'any number of times'). Now we'll use the ",(0,r.kt)("inlineCode",{parentName:"p"},"+")," plus quantifier (which means 'at least one time'). Let's see how it looks:"),(0,r.kt)("pre",null,".+@.+","\n","\n",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell"),"\n",(0,r.kt)("strong",null,"to: dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell.com "),"\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n",(0,r.kt)("strong",null,"whatever123@\ud83d\ude02.com"),"\n",(0,r.kt)("strong",null,"dave@kerr@effective.shell.com"),"\n"),(0,r.kt)("p",null,"This is better - we've eliminated some invalid addresses, ",(0,r.kt)("inlineCode",{parentName:"p"},"test123.effective-shell.com"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"@yahoo.com")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"dave@"),"."),(0,r.kt)("p",null,"We have introduced a key concept - the ",(0,r.kt)("em",{parentName:"p"},"quantifier"),". The quantifier we have used is the ",(0,r.kt)("inlineCode",{parentName:"p"},"+")," plus symbol. This is the part of a regular expression which says 'how many times can a character be matched?'."),(0,r.kt)("p",null,"There are a few different quantifiers, here is a quick reference:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Quantifier"),(0,r.kt)("th",{parentName:"tr",align:null},"Meaning"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"*")),(0,r.kt)("td",{parentName:"tr",align:null},"Any number of characters.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"+")),(0,r.kt)("td",{parentName:"tr",align:null},"At least one character.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"?")),(0,r.kt)("td",{parentName:"tr",align:null},"Between zero and one character.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"{10}")),(0,r.kt)("td",{parentName:"tr",align:null},"Exactly ten occurrences of the character.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"{10,}")),(0,r.kt)("td",{parentName:"tr",align:null},"Ten or more occurrences of the character.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"{10,20}")),(0,r.kt)("td",{parentName:"tr",align:null},"Between ten and twenty occurrences of the character.")))),(0,r.kt)("p",null,"Now let's look at the character itself."),(0,r.kt)("h3",{id:"building-regexes---character-sets-and-metacharacters"},"Building Regexes - Character Sets and Metacharacters"),(0,r.kt)("p",null,"When we are matching text, we match a set of characters a number of times. The set of characters we match can be a ",(0,r.kt)("em",{parentName:"p"},"character set")," (which is when we explicitly say what is allowed), or a ",(0,r.kt)("em",{parentName:"p"},"metacharacter")," (which is a predefined character set). This concept is far easier to explain with an example."),(0,r.kt)("p",null,"Let's look at the address ",(0,r.kt)("inlineCode",{parentName:"p"},"dave@kerr@effective.shell.com"),". This is clearly invalid, it has two at symbols. We can use character sets or metacharacters to fix this."),(0,r.kt)("p",null,"The reason this address matches our expressions is that we are using the ",(0,r.kt)("inlineCode",{parentName:"p"},".")," dot ",(0,r.kt)("em",{parentName:"p"},"metacharacter")," before and after the at symbol. The dot metacharacter means 'any character' (except a newline). This ",(0,r.kt)("em",{parentName:"p"},"includes")," the at symbol character."),(0,r.kt)("p",null,"There are a few ways we would be more explicit. Let's look at each of them, as each one will show a character set or metacharacter in detail."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Character Sets - Ranges")),(0,r.kt)("p",null,"A character set starts and ends with square brackets. We can use letters or numbers with a hyphen in-between to denote a range of characters for the character set. For example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"[A-Za-z0-9]\n")),(0,r.kt)("p",null,"This character set matches any of the letters A to Z (uppercase) or a-z (lowercase) or the digits 0-9. Let's see how it looks with the pattern:"),(0,r.kt)("pre",null,"[A-Za-z0-9]+@[A-Za-z0-9]+","\n","\n",(0,r.kt)("strong",null,"dave@effective"),"-shell.com","\n",(0,r.kt)("strong",null,"dave@effective"),"-shell","\n","to: ",(0,r.kt)("strong",null,"dave@effective-shell"),".com","\n",(0,r.kt)("strong",null,"dave@effective"),"-shell.com ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n","whatever123@\ud83d\ude02.com","\n",(0,r.kt)("strong",null,"dave@kerr"),"@effective.shell.com","\n"),(0,r.kt)("p",null,"This fails to match the valid email address ",(0,r.kt)("inlineCode",{parentName:"p"},"dave@effective-shell.com")," - because it has a hyphen after the at symbol, and the hyphen character is not in our character set. It also fails to match others for the same reason - we haven't got the 'dot' character in our character set."),(0,r.kt)("p",null,"Let's see how we can do better."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Character Sets - Special Characters")),(0,r.kt)("p",null,"We can add more characters to our character set. To include the dot and the hyphen, we just add them directly to the set:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"[A-Za-z0-9-.]\n")),(0,r.kt)("p",null,"That's all there is to it! We can now see our pattern is more correct:"),(0,r.kt)("pre",null,"[A-Za-z0-9-.]+@[A-Za-z0-9-.]+","\n","\n",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell"),"\n","to: ",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell.com")," ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n","whatever123@\ud83d\ude02.com","\n",(0,r.kt)("strong",null,"dave@kerr"),"@effective.shell.com","\n"),(0,r.kt)("p",null,"However, the expression is getting larger and larger. We can use a ",(0,r.kt)("em",{parentName:"p"},"metacharacter")," instead of the character range to make it easier to read. A metacharacter is a special character which is used to represent a range of characters. For example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"\\w+@\\w+\n")),(0,r.kt)("pre",null,"\\w+@\\w+","\n","\n",(0,r.kt)("strong",null,"dave@effective"),"-shell.com","\n",(0,r.kt)("strong",null,"dave@effective"),"-shell","\n","to: ",(0,r.kt)("strong",null,"dave@effective-shell"),".com","\n",(0,r.kt)("strong",null,"dave@effective"),"-shell.com ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n","whatever123@\ud83d\ude02.com","\n",(0,r.kt)("strong",null,"dave@kerr"),"@effective.shell.com","\n"),(0,r.kt)("p",null,"The uses the 'word' metacharacter, ",(0,r.kt)("inlineCode",{parentName:"p"},"\\w"),". This is just a shorthand for ",(0,r.kt)("inlineCode",{parentName:"p"},"[a-zA-Z0-9_]"),". But now our pattern fails again as the ",(0,r.kt)("inlineCode",{parentName:"p"},"\\w")," metacharacter doesn't include the hyphen or the dot. We can fix this easily - a ",(0,r.kt)("em",{parentName:"p"},"character set")," can include metacharacters! So we can just combine ",(0,r.kt)("inlineCode",{parentName:"p"},"\\w")," and the hyphen and dot:"),(0,r.kt)("pre",null,"[\\w+-.]+@[\\w+-.]+","\n","\n",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell"),"\n","to: ",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell.com")," ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n","whatever123@\ud83d\ude02.com","\n",(0,r.kt)("strong",null,"dave@kerr"),"@effective.shell.com","\n"),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Character Sets - Negating Characters")),(0,r.kt)("p",null,"We can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"^")," circumflex symbol to ",(0,r.kt)("em",{parentName:"p"},"negate")," a character. This allows us to build a character set which ",(0,r.kt)("em",{parentName:"p"},"doesn't")," match a pattern. For example, we could rewrite our pattern like this:"),(0,r.kt)("pre",null,"[\\S^@]+@[\\S^@]+","\n","\n",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell"),"\n","to: ",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell.com")," ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n",(0,r.kt)("strong",null,"whatever123@\ud83d\ude02.com"),"\n",(0,r.kt)("strong",null,"dave@kerr@effective.shell.com"),"\n"),(0,r.kt)("p",null,"We've used the character set ",(0,r.kt)("inlineCode",{parentName:"p"},"[\\S^@]")," which means 'any none-whitespace character' (this is the ",(0,r.kt)("inlineCode",{parentName:"p"},"\\S")," part) and 'not the at symbol character' (this is the ",(0,r.kt)("inlineCode",{parentName:"p"},"^@")," part)."),(0,r.kt)("p",null,"Notice that in this case we have more matches - because the set of characters we are using is larger than a set such as ",(0,r.kt)("inlineCode",{parentName:"p"},"\\w"),". This expression now covers the email address with the emoji, because the emoji is not a whitespace character or an at symbol."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Character Sets - Escaping Characters")),(0,r.kt)("p",null,"What do you do if you actually want to use the circumflex symbol in a character set? Just escape it! This means you put a slash first, the regex will then treat the character which follows the slash as a literal character. This is how we can match special characters like square brackets."),(0,r.kt)("p",null,"Here's an example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"[\\[\\]]+\n")),(0,r.kt)("p",null,"This matches square brackets between one and many times."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"[\\^]\\+\n")),(0,r.kt)("p",null,"This matches the circumflex followed by the plus sign. If there is ever a point at which you need to use a special character in a regular expression as a ",(0,r.kt)("em",{parentName:"p"},"literal")," character, escape it by putting a slash in front of it."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Character Sets - Quick Reference")),(0,r.kt)("p",null,"We've seen quite a few character sets and metacharacters, here's a quick reference for some commonly used ones:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Quantifier"),(0,r.kt)("th",{parentName:"tr",align:null},"Meaning"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},".")),(0,r.kt)("td",{parentName:"tr",align:null},"Any character except for a line break.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\w")),(0,r.kt)("td",{parentName:"tr",align:null},"Any 'word' character, ",(0,r.kt)("inlineCode",{parentName:"td"},"[a-zA-Z0-9_]"),".")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\W")),(0,r.kt)("td",{parentName:"tr",align:null},"Any non 'word' character.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\s")),(0,r.kt)("td",{parentName:"tr",align:null},"Any 'whitespace' character (space, tab, etc).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\S")),(0,r.kt)("td",{parentName:"tr",align:null},"Any non 'whitespace' character.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\d")),(0,r.kt)("td",{parentName:"tr",align:null},"Any 'digit' character ",(0,r.kt)("inlineCode",{parentName:"td"},"[0-9]"),".")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\D")),(0,r.kt)("td",{parentName:"tr",align:null},"Any non 'digit' character.")))),(0,r.kt)("p",null,"There are many other metacharacters, you can find more in the manpage ",(0,r.kt)("inlineCode",{parentName:"p"},"man re_pattern")),(0,r.kt)("h3",{id:"building-regexes---anchors"},"Building Regexes - Anchors"),(0,r.kt)("p",null,"At the moment, if we use the expression below:"),(0,r.kt)("pre",null,"[\\S^@]+@[\\S^@]+","\n","\n",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell"),"\n","to: ",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell.com")," ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n",(0,r.kt)("strong",null,"whatever123@\ud83d\ude02.com"),"\n",(0,r.kt)("strong",null,"dave@kerr@effective.shell.com"),"\n"),(0,r.kt)("p",null,"Then we match any line which ",(0,r.kt)("em",{parentName:"p"},"contains")," an email address. But what if we only want to match complete email addresses? What if we need to exclude lines which have extra stuff at the beginning or end?"),(0,r.kt)("p",null,"For this, we can use ",(0,r.kt)("em",{parentName:"p"},"anchors"),". Anchors represent special parts of a string, such as the start or beginning of a line."),(0,r.kt)("p",null,"If we want to only match lines which contain a complete email address, we can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"^")," circumflex (start of line) anchor and ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," dollar (end of line) anchor:"),(0,r.kt)("pre",null,"^[\\S^@]+@[\\S^@]+$","\n","\n",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n",(0,r.kt)("strong",null,"dave@effective-shell"),"\n","to: dave@effective-shell.com","\n","dave@effective-shell.com ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n",(0,r.kt)("strong",null,"whatever123@\ud83d\ude02.com"),"\n",(0,r.kt)("strong",null,"dave@kerr@effective.shell.com"),"\n"),(0,r.kt)("p",null,"This allows us to create expressions which match patterns of text at certain points - anchors. For example, if we want to match any line which ",(0,r.kt)("em",{parentName:"p"},"starts with")," the letters ",(0,r.kt)("inlineCode",{parentName:"p"},"to: ")," we could just use this:"),(0,r.kt)("pre",null,"^to: .* ","\n","\n","dave@effective-shell.com","\n","dave@effective-shell","\n",(0,r.kt)("strong",null,"to: dave@effective-shell.com"),"\n","dave@effective-shell.com ","\n","test123.effective-shell.com","\n","@yahoo.com","\n","dave@","\n",(0,r.kt)("strong",null,"whatever123@\ud83d\ude02.com"),"\n",(0,r.kt)("strong",null,"dave@kerr@effective.shell.com"),"\n"),(0,r.kt)("p",null,"You will see the start of line and end of line anchors quite often, they can be extremely useful when making a regular expression more specific."),(0,r.kt)("h3",{id:"building-regexes---capture-groups"},"Building Regexes - Capture Groups"),(0,r.kt)("p",null,"You can extract only ",(0,r.kt)("em",{parentName:"p"},"part")," of what you match in a regular expression using ",(0,r.kt)("em",{parentName:"p"},"capture groups"),". A capture group lets you break up the expression into smaller parts and then operate on either the entire match, or only one of the groups."),(0,r.kt)("p",null,"Here's an example:"),(0,r.kt)("pre",null,"(.+)@(.+)","\n","\n",(0,r.kt)("strong",null,"dave@effective-shell.com"),"\n"),(0,r.kt)("p",null,"Now the entire line matches, but everything surrounded by ",(0,r.kt)("inlineCode",{parentName:"p"},"()")," parentheses is a capture group. This means that the regular expression has actually made ",(0,r.kt)("em",{parentName:"p"},"three")," matches:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("inlineCode",{parentName:"li"},"dave@effective-shell.com")," - The first match in an expression is always the complete match"),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("inlineCode",{parentName:"li"},"dave")," - This is the first capture group, everything before the at symbol"),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("inlineCode",{parentName:"li"},"effective-shell.com")," - This is the second capture group, everything after the at symbol.")),(0,r.kt)("p",null,"We're actually going to see how to use capture groups directly in the shell in the next chapter so we won't go into much more detail now."),(0,r.kt)("h3",{id:"building-regexes---lazy-and-greedy-expressions"},"Building Regexes - Lazy and Greedy Expressions"),(0,r.kt)("p",null,"Regular expressions can be ",(0,r.kt)("em",{parentName:"p"},"lazy")," or ",(0,r.kt)("em",{parentName:"p"},"greedy"),". Whether an expression is lazy or greedy affects how it matches patterns - basically whether it ",(0,r.kt)("em",{parentName:"p"},"stops")," at the earliest point the pattern is matched, or ",(0,r.kt)("em",{parentName:"p"},"continues")," until the pattern no longer matches."),(0,r.kt)("p",null,"Regular expressions are ",(0,r.kt)("em",{parentName:"p"},"greedy")," by default. This means that if you are matching a pattern, the regular expression will capture as much as it possibly can - all the way to the last match. As an example, let's look at how we might capture the contents of an html tag:"),(0,r.kt)("pre",null,"<.+>","\n","\n","This text is ",(0,r.kt)("strong",null,"bold"),".","\n"),(0,r.kt)("p",null,"The regular expression ",(0,r.kt)("inlineCode",{parentName:"p"},"<.+>")," matches an angled bracket, then at least one character, then a closing angled bracket. Because regular expressions are greedy by default, it captures all the way until the ",(0,r.kt)("em",{parentName:"p"},"last angled bracket on the line")," - i.e. everything up until the closing ",(0,r.kt)("inlineCode",{parentName:"p"},"")," tag."),(0,r.kt)("p",null,"We can create a ",(0,r.kt)("em",{parentName:"p"},"lazy")," expression by using the ",(0,r.kt)("inlineCode",{parentName:"p"},"?")," question mark symbol after the quantifier - this means that the expression will capture as few characters as possible until the end of the pattern is found:"),(0,r.kt)("pre",null,"<.+?>","\n","\n","This text is ",(0,r.kt)("strong",null,""),"bold",(0,r.kt)("strong",null,""),".","\n"),(0,r.kt)("p",null,"In this example we have actually captured ",(0,r.kt)("em",{parentName:"p"},"two")," results - the contents of the opening and closing tag. Because the expression ",(0,r.kt)("inlineCode",{parentName:"p"},"<.+?>")," is ",(0,r.kt)("em",{parentName:"p"},"lazy")," it matches only until the first closing brace it finds, meaning that the results are quite different."),(0,r.kt)("p",null,"To get the same results without using the lazy quantifier, we'd have to have an expression like this:"),(0,r.kt)("pre",null,"<[^>]+>","\n","\n","This text is ",(0,r.kt)("strong",null,""),"bold",(0,r.kt)("strong",null,""),".","\n"),(0,r.kt)("p",null,"In this case we've changed the match to 'any character which is not a closing brace'. Whether this is easier for the reader to understand than the lazy quantifier is hard to say, but it is useful to understand the difference between lazy and greedy expressions."),(0,r.kt)("h2",{id:"avoiding-advanced-topics---backtracking-lookarounds-and-atomic-grouping"},"Avoiding Advanced Topics - Backtracking, Lookarounds and Atomic Grouping"),(0,r.kt)("p",null,"I think that if you have the basics of quantifiers, character sets and metacharacters and capture groups, then you are probably well equipped to use regular expressions. Knowing how to make an expression lazy can also make working with regexes more straightforward."),(0,r.kt)("p",null,"However - you might come across a few terms when you are working with regular expressions which may be unfamiliar. These relate to perhaps more advanced topics. I'll give brief overview here, but if you have had your fill of regexes for now then you can safely skip to the next section!"),(0,r.kt)("p",null,"I am not going to show examples of each of these concepts. I'll explain why after a brief summary of the concepts."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Backtracking")," - this refers to the process a regular expression engine goes through to try and identify a greedy match. In short, it is possible to inadvertently write a regular expression which has exponential processing complexity based on the length of the input string."),(0,r.kt)("p",null,"This has led to cases of what is called 'catastrophic backtracking' - where the processing involved to match a pattern can cause system failures or even lead to exploits",(0,r.kt)("sup",{parentName:"p",id:"fnref-1"},(0,r.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),"."),(0,r.kt)("p",null,"In short - very broad and greedy expressions such as ",(0,r.kt)("inlineCode",{parentName:"p"},".+")," (match ",(0,r.kt)("em",{parentName:"p"},"anything")," at least once) may be susceptible to this problem. Be careful when writing your expressions to test them with short and long strings to see if there's a noticeable performance difference. Regex101 and other tools can show you if your expression is time consuming. Avoid this by making expressions lazy when you can and matching more explicit characters."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Lookarounds"),' are special constructs which allow you to essentially say\n"find me a pattern, but only if it comes before or after another pattern". A lookahead is used to say "find me a pattern, but only match it if it comes before another pattern", a lookbehind says "find me a pattern, but only match it if it comes after another pattern". There are \'negative\' lookaheads and lookbehinds which essentially say "find me a pattern which is ',(0,r.kt)("em",{parentName:"p"},"not"),' preceded or followed by another pattern".'),(0,r.kt)("p",null,"As an example, the expression ",(0,r.kt)("inlineCode",{parentName:"p"},"\\d+(?=\u20ac)")," matches digits (this is the ",(0,r.kt)("inlineCode",{parentName:"p"},"\\d")," metacharacter), at least one or more (this is the ",(0,r.kt)("inlineCode",{parentName:"p"},"+")," plus symbol), but only if the digits are followed by a Euro symbol. In this case the ",(0,r.kt)("inlineCode",{parentName:"p"},"(?=\u20ac)")," part of the pattern is a 'positive lookahead'."),(0,r.kt)("p",null,"I have not yet found a situation where I've really needed lookaround expressions. For example, I would simply write the above expression as:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"(\\d+)\u20ac\n")),(0,r.kt)("p",null,"Which simply matches digits which precede a Euro symbol and puts them in a capture group."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Atomic Groups")," are a more advanced construct which can be used to avoid backtracking which is described above. Lookarounds are atomic. Essentially when an atomic group is matched all backtracking ceases, so can provide a 'get out' clause to avoid catastrophic backtracking."),(0,r.kt)("p",null,"This is somewhat opinionated, but in my years of engineering I am yet to find a situation which genuinely was made more simple with the use of lookarounds or atomic groups. I would instead advise that if your expression is highly complex, find a way to ",(0,r.kt)("em",{parentName:"p"},"break up the input")," first and then process it in multiple steps. That will likely lead to scripts and code which is easier for others to read and reason about."),(0,r.kt)("h2",{id:"a-word-of-warning"},"A Word of Warning"),(0,r.kt)("p",null,"Different tools process regular expressions in different ways. There are subtle differences between how they are processed in Bash, JavaScript, Perl, Python, Golang and other languages. This can make them painful to work with."),(0,r.kt)("p",null,"In general most of the features we've seen in this chapter will work the same regardless of the tool you are using, but as you move into more sophisticated features, you may find that some tools have slightly different syntaxes for certain types of capture groups. However, this generally only affects the more advanced features such as named capture groups (which is a special syntax allowing you to give capture groups a descriptive name)."),(0,r.kt)("p",null,"I would advise that you keep expressions simple if possible - if they are getting too complex then break up your input or break up the processing into smaller chunks of work!"),(0,r.kt)("p",null,"Remember that a regular expression does not have to be the only way you validate input. You might use a regular expression to do a quick check on a form on a website to make sure that an email address has at least the correct ",(0,r.kt)("em",{parentName:"p"},"structure"),", but you might then use a more sophisticated check later on (such as sending the user an activation email) to actually confirm that the address actually belongs to the user."),(0,r.kt)("p",null,"Using a website like regex101 you can quickly check how a regex works with different tools. Wherever you might encounter these differences in content in this book I've tried to call it out!"),(0,r.kt)("h2",{id:"summary"},"Summary"),(0,r.kt)("p",null,"Hopefully this gives a basic grounding in the fundamentals of regular expressions. Knowing only a few concepts - types of characters, quantifiers and capture groups is plenty for most people. And the online tool regex101 is a superb way to ",(0,r.kt)("em",{parentName:"p"},"learn")," regular expressions."),(0,r.kt)("p",null,"Now we've learned the theory - in the next chapter we'll see some built-in ways to manipulate text in the shell, which include some clever regular expression features."),(0,r.kt)("div",{className:"footnotes"},(0,r.kt)("hr",{parentName:"div"}),(0,r.kt)("ol",{parentName:"div"},(0,r.kt)("li",{parentName:"ol",id:"fn-1"},"There is a fascinating write up of how this led to a severe Cloudflare outage in 2019 available online at: ",(0,r.kt)("a",{parentName:"li",href:"https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/"},"https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/"),(0,r.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")))))}p.isMDXComponent=!0},8544:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/regex_v1-f91163e62ea21c34e399180ea629e8b3.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/4972.2bae19a3.js b/pr-preview/pr-346/assets/js/4972.2bae19a3.js new file mode 100644 index 00000000..3292ff31 --- /dev/null +++ b/pr-preview/pr-346/assets/js/4972.2bae19a3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[4972],{4972:(e,t,l)=>{l.r(t),l.d(t,{default:()=>i});var n=l(7294),a=l(5999),o=l(1944),r=l(8765);function i(){return n.createElement(n.Fragment,null,n.createElement(o.d,{title:(0,a.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),n.createElement(r.Z,null,n.createElement("main",{className:"container margin-vert--xl"},n.createElement("div",{className:"row"},n.createElement("div",{className:"col col--6 col--offset-3"},n.createElement("h1",{className:"hero__title"},n.createElement(a.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),n.createElement("p",null,n.createElement(a.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),n.createElement("p",null,n.createElement(a.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/5525.c11ef000.js b/pr-preview/pr-346/assets/js/5525.c11ef000.js new file mode 100644 index 00000000..b840b3d9 --- /dev/null +++ b/pr-preview/pr-346/assets/js/5525.c11ef000.js @@ -0,0 +1 @@ +(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[5525],{5525:()=>{}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/5ae90e10.0689b418.js b/pr-preview/pr-346/assets/js/5ae90e10.0689b418.js new file mode 100644 index 00000000..3b7c0bac --- /dev/null +++ b/pr-preview/pr-346/assets/js/5ae90e10.0689b418.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[6833],{3905:(e,t,n)=>{n.d(t,{Zo:()=>h,kt:()=>u});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t =0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},h=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},m="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,s=e.parentName,h=r(e,["components","mdxType","originalType","parentName"]),m=p(n),d=o,u=m["".concat(s,".").concat(d)]||m[d]||c[d]||i;return n?a.createElement(u,l(l({ref:t},h),{},{components:n})):a.createElement(u,l({ref:t},h))}));function u(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,l=new Array(i);l[0]=d;var r={};for(var s in t)hasOwnProperty.call(t,s)&&(r[s]=t[s]);r.originalType=e,r[m]="string"==typeof e?e:o,l[1]=r;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>r,toc:()=>p});var a=n(7462),o=(n(7294),n(3905));const i={title:"Becoming a Clipboard Gymnast",slug:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/"},l=void 0,r={unversionedId:"transitioning-to-the-shell/clipboard-gymnastics/index",id:"transitioning-to-the-shell/clipboard-gymnastics/index",title:"Becoming a Clipboard Gymnast",description:"For those who are new to the shell, we've covered a lot. In this chapter we'll slow down the pace of new commands a bit and instead focus on a core skill which you will already be familiar with from Graphical User Interfaces - using the clipboard.",source:"@site/docs/01-transitioning-to-the-shell/04-clipboard-gymnastics/index.md",sourceDirName:"01-transitioning-to-the-shell/04-clipboard-gymnastics",slug:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/",permalink:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/01-transitioning-to-the-shell/04-clipboard-gymnastics/index.md",tags:[],version:"current",frontMatter:{title:"Becoming a Clipboard Gymnast",slug:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/"},sidebar:"sidebar",previous:{title:"Managing Your Files",permalink:"/part-1-transitioning-to-the-shell/managing-your-files/"},next:{title:"Getting Help",permalink:"/part-1-transitioning-to-the-shell/getting-help/"}},s={},p=[{value:"The Clipboard Essentials",id:"the-clipboard-essentials",level:2},{value:"Preparing the Clipboard Commands",id:"preparing-the-clipboard-commands",level:2},{value:"Copy and Paste Basics",id:"copy-and-paste-basics",level:2},{value:"Removing Formatting",id:"removing-formatting",level:2},{value:"Sorting Text",id:"sorting-text",level:2},{value:"Manipulating Text",id:"manipulating-text",level:2},{value:"Summary",id:"summary",level:2}],h={toc:p};function m(e){let{components:t,...i}=e;return(0,o.kt)("wrapper",(0,a.Z)({},h,i,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"For those who are new to the shell, we've covered a lot. In this chapter we'll slow down the pace of new commands a bit and instead focus on a core skill which you will already be familiar with from Graphical User Interfaces - using the clipboard."),(0,o.kt)("p",null,"You have probably already been using the clipboard with the shell, copying and pasting commands and their outputs. However, there's a lot more we can do with the clipboard. Now we'll look at how to take this to the next level."),(0,o.kt)("p",null,"We'll also briefly introduce ",(0,o.kt)("em",{parentName:"p"},"aliases")," and ",(0,o.kt)("em",{parentName:"p"},"pipelines"),", which will be covered in a lot more detail in later chapters."),(0,o.kt)("h2",{id:"the-clipboard-essentials"},"The Clipboard Essentials"),(0,o.kt)("p",null,"I wouldn't be surprised if the keyboard shortcuts to access the clipboard are already firmly locked into muscle memory for almost all readers, but just in case, here's a reminder of the shortcuts across different systems:"),(0,o.kt)("table",null,(0,o.kt)("thead",{parentName:"table"},(0,o.kt)("tr",{parentName:"thead"},(0,o.kt)("th",{parentName:"tr",align:null},"Command"),(0,o.kt)("th",{parentName:"tr",align:null},"Windows Shortcut"),(0,o.kt)("th",{parentName:"tr",align:null},"Linux Shortcut"),(0,o.kt)("th",{parentName:"tr",align:null},"MacOS Shortcut"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Cut"),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"X")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"X")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"\u2318")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"X"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Copy"),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"C")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"C")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"\u2318")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"C"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},"Paste"),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"V")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"V")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"\u2318")," + ",(0,o.kt)("inlineCode",{parentName:"td"},"V"))))),(0,o.kt)("p",null,"In the shell, you may find that these commands don't run as expected. For example, in the screenshot below I have tried to use ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"V")," a few times to paste into terminal on Ubuntu:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: Ctrl + V on Ubuntu",src:n(2724).Z,width:"1488",height:"990"})),(0,o.kt)("p",null,"Instead of the contents of the clipboard being dropped into the shell, we see the text ",(0,o.kt)("inlineCode",{parentName:"p"},"^V"),". Why is this?"),(0,o.kt)("p",null,"Well, some of this is historical (the shell has been around for a long time so we'll see this answer a lot!). The reason the ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," key is ",(0,o.kt)("em",{parentName:"p"},"called")," the ",(0,o.kt)("em",{parentName:"p"},"Control Key")," is that it is used to send ",(0,o.kt)("em",{parentName:"p"},"control sequences")," to the computer. When we're using the Control Key, the characters we send are not plain text, they're used to perform actions. This is something that is probably pretty familiar. For example, ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"P")," is almost universally used as a shortcut for the 'Print' command."),(0,o.kt)("p",null,"We tend to think of these commands as ",(0,o.kt)("em",{parentName:"p"},"shortcuts")," to save us from finding the appropriate command in a menu or on a toolbar. But of course most shells and command-line interfaces pre-date graphical user interfaces. They needed a way to differentiate between a user entering plain old text, and a user wanting to execute a certain command."),(0,o.kt)("p",null,"Even modern shells tend to follow the conventions around control sequences which were established by earlier ones to ensure a consistent experience for users who are used to working with shells. Shells have a whole bunch of control sequences which actually pre-date the graphical user interface, the clipboard itself, and even screens!"),(0,o.kt)("p",null,"Some of the control sequences used in the shell you might already be familiar with. For example, if you have a program running and want to cancel it, you might be used to using ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"C"),". This actually sends a ",(0,o.kt)("em",{parentName:"p"},"signal")," to the program and typically the program responds by closing. We'll see signals again and again as we go through the book."),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"C")," combination terminates the current program. What about ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"V"),'? This is the grand-sounding "',(0,o.kt)("em",{parentName:"p"},"Verbatim Insert"),'" command. It tells the shell to write out the ',(0,o.kt)("em",{parentName:"p"},"next")," keystroke you give it. This allows you to write out 'special' characters like the escape key, left or right keys, or even the ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"V")," combination itself."),(0,o.kt)("p",null,"So if you type ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"V")," twice, the shell writes out the text ",(0,o.kt)("inlineCode",{parentName:"p"},"^V"),". The hat symbol ",(0,o.kt)("inlineCode",{parentName:"p"},"^")," represents ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl"),". The first command tells the shell to write out the following command, the second is then written out directly. You can try writing out some different sequences. You'll see various odd looking symbols drawn, which represent things like the ",(0,o.kt)("inlineCode",{parentName:"p"},"Alt")," key and other special keys."),(0,o.kt)("p",null,"So why do we need to care? Well the shell already has a command for ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"C")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl")," + ",(0,o.kt)("inlineCode",{parentName:"p"},"V"),", so we're going to need to work around this to use our familiar 'copy' and 'paste' commands."),(0,o.kt)("p",null,"How this works varies across platforms. Follow the instructions below for the platform you are using."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Windows")),(0,o.kt)("p",null,"If you are using a ",(0,o.kt)("em",{parentName:"p"},"Command Prompt"),", then the usual shortcuts will work fine. However, most of the time we will be using Bash. In this case the shortcuts will ",(0,o.kt)("em",{parentName:"p"},"not")," work. Instead, select the ",(0,o.kt)("em",{parentName:"p"},"Use Ctrl+Shift+C/V as Copy/Paste")," option from the properties menu:"),(0,o.kt)("img",{alt:"Screenshot: Use Ctrl+Shift+C/V as Copy/Paste on Bash on Windows",src:n(2759).Z,width:"380px"}),(0,o.kt)("p",null,"You can now use ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl+Shift+C")," for copy and ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl+Shift+V")," for paste. You can also copy text by just dragging the cursor over it with the right mouse button."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Linux")),(0,o.kt)("p",null,"On most Linux systems you'll be using the Gnome terminal, which means that you can use ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl+Shift+C")," for copy and ",(0,o.kt)("inlineCode",{parentName:"p"},"Ctrl+Shift+V")," for paste. You can also right click on text with the cursor to select it."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"MacOS")),(0,o.kt)("p",null,"Mac users can just use ",(0,o.kt)("inlineCode",{parentName:"p"},"\u2318 + C")," for copy and ",(0,o.kt)("inlineCode",{parentName:"p"},"\u2318 + V")," for paste. The shell doesn't use the special Mac Command character ",(0,o.kt)("inlineCode",{parentName:"p"},"\u2318"),", which means the default keyboard mappings on MacOS work fine in a shell as they do not clash with anything."),(0,o.kt)("p",null,"Now that we've got the basics out of the way, and learnt far more than we probably wanted to about control keys, we can look at more ways to use the clipboard."),(0,o.kt)("h2",{id:"preparing-the-clipboard-commands"},"Preparing the Clipboard Commands"),(0,o.kt)("p",null,"Copying and pasting text to and from the clipboard is useful, but there's a lot more we can do. With a couple of basic commands we can hugely expand what we can do with the shell and make a whole set of everyday tasks far easier to accomplish."),(0,o.kt)("p",null,"There is one small complexity we'll need to work through before we continue. The complexity is that the clipboard is accessed in different ways on Windows, Linux and MacOS. I'll first show you how to deal with this, just follow the instructions for the platform you are working on."),(0,o.kt)("p",null,"To make things easier for the reader I'm going to assume you have created the ",(0,o.kt)("inlineCode",{parentName:"p"},"pbcopy")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"pbpaste")," commands by following the instructions below. I am creating these commands so that regardless of the platform you are using the tutorials will work in the same way!"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Windows")),(0,o.kt)("p",null,"Assuming you are using WSL, you will need to run the following two commands. By the time this book is published there ",(0,o.kt)("em",{parentName:"p"},"may")," be a cleaner way, but for now this is a workaround for some limitations on the WSL system:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"alias pbcopy='clip.exe'\nalias pbpaste=\"powershell.exe -command 'Get-Clipboard' | tr -d '\\r' | head -n -1\"\n")),(0,o.kt)("p",null,"Don't worry about how these commands work - by the time you've gone through the book it should make perfect sense. For now you just need to know we're adding two new commands to our toolkit - ",(0,o.kt)("inlineCode",{parentName:"p"},"pbcopy")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"pbpaste"),", which will work in Bash on Windows."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Linux")),(0,o.kt)("p",null,"Hopefully if you are Linux user the commands below will seem familiar. They install the ",(0,o.kt)("inlineCode",{parentName:"p"},"xclip")," program and create shortcuts to copy and paste. You absolutely don't need to do this if you prefer to call ",(0,o.kt)("inlineCode",{parentName:"p"},"xclip")," directly, these commands are just setup so that across all platforms the tutorial looks the same."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},'sudo apt install -y xclip\nalias pbcopy="xclip -selection c"\nalias pbpaste="xclip -selection c -o"\n')),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"MacOS")),(0,o.kt)("p",null,"Nothing is required on MacOS - ",(0,o.kt)("inlineCode",{parentName:"p"},"pbcopy")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"pbpaste")," are built in."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Making these changes permanent")),(0,o.kt)("p",null,"We've used the ",(0,o.kt)("inlineCode",{parentName:"p"},"alias")," command to create ",(0,o.kt)("inlineCode",{parentName:"p"},"pbcopy")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"pbpaste"),". In Bash (and most shells) an ",(0,o.kt)("inlineCode",{parentName:"p"},"alias")," is something you can configure as a shortcut to avoid having to type longer commands. There's a whole chapter on commands in Section 2."),(0,o.kt)("p",null,"These instructions will need to be repeated when you re-open your terminal. In a later chapter we'll see how to make permanent customisations to our shells so that we don't have to repeat this setup."),(0,o.kt)("p",null,"We'll also see later on how to create configuration which works across many different platforms, so that you can use the same configuration regardless of what platform you are working on. This is very useful if you work across multiple machines or operating systems!"),(0,o.kt)("h2",{id:"copy-and-paste-basics"},"Copy and Paste Basics"),(0,o.kt)("p",null,"Now that we've created these commands, we can use them to access the clipboard. For example, if I copy the following text:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"Kirk Van Houten\nTimothy Lovejoy\nArtie Ziff\n")),(0,o.kt)("p",null,"Then I can paste it into the shell with the following command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"pbpaste\n")),(0,o.kt)("p",null,"And we'll see something like this:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: pbpaste in action",src:n(7872).Z,width:"1364",height:"954"})),(0,o.kt)("p",null,"Copying is just as straightforward. If you have downloaded the Effective Shell 'samples' folder you can see we have a list of characters from \"The Simpsons\" in the file ",(0,o.kt)("inlineCode",{parentName:"p"},"effective-shell/text/simpsons-characters.txt"),". Now we ",(0,o.kt)("em",{parentName:"p"},"could")," use the ",(0,o.kt)("inlineCode",{parentName:"p"},"cat")," command to show the contents of the file, and then manually select the text and copy it. Even easier though is to just ",(0,o.kt)("em",{parentName:"p"},"pipe")," the contents of the file to the ",(0,o.kt)("inlineCode",{parentName:"p"},"pbcopy")," command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cat ~/effective-shell/text/simpsons-characters.txt | pbcopy\n")),(0,o.kt)("p",null,"The output will look similar to the below (I've included the output of ",(0,o.kt)("inlineCode",{parentName:"p"},"cat")," for reference as well):"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Screenshot: pbcopy in action",src:n(4214).Z,width:"1364",height:"954"})),(0,o.kt)("p",null,"The vertical bar ",(0,o.kt)("inlineCode",{parentName:"p"},"|")," is the ",(0,o.kt)("em",{parentName:"p"},"pipe")," operator. It tells the shell to take the output from the command on the left and send it straight to the ",(0,o.kt)("em",{parentName:"p"},"input")," of the program on the right. We're going to see a ",(0,o.kt)("em",{parentName:"p"},"lot")," more of the pipeline operator as we continue. For now it's enough to know you can use it to 'chain' commands together."),(0,o.kt)("p",null,"This might not seem super useful so far - but if the text file was a lot larger then it would be much harder to ",(0,o.kt)("inlineCode",{parentName:"p"},"cat")," it out, use the mouse to select all of the text (scrolling up through the window) and then copy it. And if you didn't have a mouse, it would be even more tricky. We're aiming to be as effective as possible when using the shell so being able to use the keyboard quickly for common tasks is critical."),(0,o.kt)("p",null,"Now we can see some real world examples of how these commands can be useful in daily tasks!"),(0,o.kt)("h2",{id:"removing-formatting"},"Removing Formatting"),(0,o.kt)("p",null,"Don't you hate it when you have to copy formatted text and don't have an easy way to paste it as ",(0,o.kt)("em",{parentName:"p"},"unformatted")," text? Here's an example, I want to copy this Wikipedia page on 'bash', and paste it into a Word document:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Copying and pasting with formatting",src:n(616).Z,width:"1532",height:"1724"})),(0,o.kt)("p",null,"Many programs have a shortcut to paste the contents of the clipboard (such as 'command + shift + v') but if you are like me you might find yourself pasting ",(0,o.kt)("em",{parentName:"p"},"into")," a plain text editor just to copy ",(0,o.kt)("em",{parentName:"p"},"out")," the plain text."),(0,o.kt)("p",null,"If you just run the command ",(0,o.kt)("inlineCode",{parentName:"p"},"pbpaste | pbcopy"),", you can easily strip the formatting:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Stripping formatting from the clipboard",src:n(8599).Z,width:"1530",height:"1322"})),(0,o.kt)("p",null,"We're just piping out the clipboard (which ends up as plain text, cause we're in a terminal!) and then piping that plain text ",(0,o.kt)("em",{parentName:"p"},"back into the clipboard"),", replacing the formatted text which was there before."),(0,o.kt)("p",null,"This little trick can be very useful. But we can use the same pattern to quickly manipulate the contents of the clipboard in more sophisticated ways."),(0,o.kt)("h2",{id:"sorting-text"},"Sorting Text"),(0,o.kt)("p",null,"Because we can ",(0,o.kt)("em",{parentName:"p"},"pipe")," the contents of the clipboard to other programs, that means we can easily use the huge number of tools available to us to work with text."),(0,o.kt)("p",null,"Let's take another look at the list of characters we have in the ",(0,o.kt)("inlineCode",{parentName:"p"},"~/plaground/text/simpsons-characters.txt")," file:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"$ cat ~/effective-shell/text/simpsons-characters.txt\nArtie Ziff\nKirk Van Houten\nTimothy Lovejoy\nArtie Ziff\nNick Riviera\nSeymore Skinner\nHank Scorpio\nTimothy Lovejoy\nJohn Frink\nCletus Spuckler\nRuth Powers\nArtie Ziff\nAgnes Skinner\nHelen Lovejoy\n")),(0,o.kt)("p",null,"We can easily take this text, sort it and then directly copy the results:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"$ cat ~/effective-shell/text/simpsons-characters.txt | sort | pbcopy\n")),(0,o.kt)("p",null,"The contents of the clipboard will now contain:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"Agnes Skinner\nArtie Ziff\nArtie Ziff\nArtie Ziff\nCletus Spuckler\nHank Scorpio\nHelen Lovejoy\nJohn Frink\nKirk Van Houten\nNick Riviera\nRuth Powers\nSeymore Skinner\nTimothy Lovejoy\nTimothy Lovejoy\n")),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"sort")," command has lots of different options but the defaults work fine for this case. We can see we've got quite a few duplicates - now we can move onto how we'd handle that."),(0,o.kt)("h2",{id:"manipulating-text"},"Manipulating Text"),(0,o.kt)("p",null,"Let's say someone has emailed me a list of people I need to invite to an event:"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Email List",src:n(3092).Z,width:"324",height:"696"})),(0,o.kt)("p",null,"The problem is:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"The list is in Excel and is formatted"),(0,o.kt)("li",{parentName:"ol"},"The list has duplicates"),(0,o.kt)("li",{parentName:"ol"},"I need to turn each name into an email address like '",(0,o.kt)("a",{parentName:"li",href:"mailto:Artie_Ziff@simpsons.com"},"Artie_Ziff@simpsons.com"),"'")),(0,o.kt)("p",null,"I want to email get the email addresses on my clipboard ready to paste into my email client quickly. We can quickly handle this task without leaving the shell."),(0,o.kt)("p",null,"If you want to try out the same commands and follow along you can copy the raw text below (don't worry if the commands are unfamiliar, we'll be seeing them again and again and breaking down each one in later chapters):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"Artie Ziff\nKirk Van Houten\nTimothy Lovejoy\nArtie Ziff\nNick Riviera\nSeymore Skinner\nHank Scorpio\nTimothy Lovejoy\nJohn Frink\nCletus Spuckler\nRuth Powers\nArtie Ziff\nAgnes Skinner\nHelen Lovejoy\n")),(0,o.kt)("p",null,"First, we copy the text to the clipboard."),(0,o.kt)("p",null,"Now we can paste and sort:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ pbpaste | sort\nAgnes Skinner\nArtie Ziff\nArtie Ziff\nArtie Ziff\nCletus Spuckler\nHank Scorpio\nHelen Lovejoy\nJohn Frink\nKirk Van Houten\nNick Riviera\nRuth Powers\nSeymore Skinner\nTimothy Lovejoy\nTimothy Lovejoy\n")),(0,o.kt)("p",null,"Then remove the duplicates:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ pbpaste | sort | uniq\nAgnes Skinner\nArtie Ziff\nCletus Spuckler\nHank Scorpio\nHelen Lovejoy\nJohn Frink\nKirk Van Houten\nNick Riviera\nRuth Powers\nSeymore Skinner\nTimothy Lovejoy\n")),(0,o.kt)("p",null,"Replace the space with an underscore:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'$ pbpaste | sort | uniq | tr " " "_"\nAgnes_Skinner\nArtie_Ziff\nCletus_Spuckler\nHank_Scorpio\nHelen_Lovejoy\nJohn_Frink\nKirk_Van_Houten\nNick_Riviera\nRuth_Powers\nSeymore_Skinner\nTimothy_Lovejoy\n')),(0,o.kt)("p",null,"Then add the final part of the email address:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'$ pbpaste | sort | uniq | tr " " "_" | sed \'s/$/@simpsons.com/\'\nAgnes_Skinner@simpsons.com\nArtie_Ziff@simpsons.com\nCletus_Spuckler@simpsons.com\nHank_Scorpio@simpsons.com\nHelen_Lovejoy@simpsons.com\nJohn_Frink@simpsons.com\nKirk_Van_Houten@simpsons.com\nNick_Riviera@simpsons.com\nRuth_Powers@simpsons.com\nSeymore_Skinner@simpsons.com\nTimothy_Lovejoy@simpsons.com\n')),(0,o.kt)("p",null,"This looks perfect! We can now put the transformed text back onto the clipboard:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ pbpaste | sort | uniq | tr ' ' '_' | sed 's/$/@simpsons.com/' | pbcopy\n")),(0,o.kt)("p",null,"All in all we have the following pipeline:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"pbpaste")," - output the clipboard"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"sort")," - order the output"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"uniq")," - deduplicate the rows"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"tr ' ' '_'")," - replace spaces with underscores"),(0,o.kt)("li",{parentName:"ol"},(0,o.kt)("inlineCode",{parentName:"li"},"sed /$/@simpsons.com")," - add the email domain to the end of the row")),(0,o.kt)("p",null,"Now you don't need to remember all of these commands. We'll be going into them in detail as the book continues, and in the next chapter we'll be looking into how you can get help directly in the shell to discover how commands work. The key concept is that you can treat the clipboard just like a file - reading from it, manipulating it, and writing back to it, without ever leaving the shell."),(0,o.kt)("p",null,"In fact - if you are on a Linux system, try running:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"cat /dev/clipboard\n")),(0,o.kt)("p",null,"You'll see the contents of the clipboard written out. In Linux almost everything can be represented as a file - the clipboard included! Like a lot of the other topics this is something we'll visit again in detail later."),(0,o.kt)("p",null,"We're also going to spend a lot of time later on looking at ",(0,o.kt)("em",{parentName:"p"},"pipelines")," in detail, so don't worry too much if this seems overwhelming at this stage!"),(0,o.kt)("p",null,"As you go through the book you'll be able to apply every technique you learn to the clipboard itself - hopefully you'll find this can save you a lot of time and make you even faster with your day to day work."),(0,o.kt)("h2",{id:"summary"},"Summary"),(0,o.kt)("p",null,"In this chapter we learnt: "),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"You can copy and paste into the shell with keyboard commands which are the same, or at least very similar, to the commands you normally use."),(0,o.kt)("li",{parentName:"ul"},"Different operating systems access the clipboard in different ways, but we can work around this by creating an ",(0,o.kt)("inlineCode",{parentName:"li"},"alias")," command (which we'll see in detail later)"),(0,o.kt)("li",{parentName:"ul"},"We can use ",(0,o.kt)("inlineCode",{parentName:"li"},"pbcopy")," to copy and ",(0,o.kt)("inlineCode",{parentName:"li"},"pbpaste")," to paste."),(0,o.kt)("li",{parentName:"ul"},"We can 'chain' commands together with the ",(0,o.kt)("inlineCode",{parentName:"li"},"|")," (pipe) operator."),(0,o.kt)("li",{parentName:"ul"},"We can turn formatted text on the clipboard into plain text by just running ",(0,o.kt)("inlineCode",{parentName:"li"},"pbpaste | pbcopy"),"."),(0,o.kt)("li",{parentName:"ul"},"We can sort lines of text with the ",(0,o.kt)("inlineCode",{parentName:"li"},"sort")," command."),(0,o.kt)("li",{parentName:"ul"},"There is clearly a lot more we can do with text as we save hints of with the ",(0,o.kt)("inlineCode",{parentName:"li"},"uniq"),", ",(0,o.kt)("inlineCode",{parentName:"li"},"tr")," and ",(0,o.kt)("inlineCode",{parentName:"li"},"sed")," commands - which we'll introduce in detail later."),(0,o.kt)("li",{parentName:"ul"},"You can treat the clipboard a bit like a file in the shell."),(0,o.kt)("li",{parentName:"ul"},"On Linux, lots of things can be represented as files - including the clipboard (which is accessed via the ",(0,o.kt)("inlineCode",{parentName:"li"},"/dev/clipboard")," file).")))}m.isMDXComponent=!0},2759:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/windows-bash-options-ee40590c7b84ed3883d04b278c6cc605.png"},2724:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ctrl-v-on-ubuntu-6dd4bf41757a440a10f149f01068e3a6.png"},3092:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/email_list_excel-c952ce039f3d370ecb4bc9c17d8ab815.png"},4214:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/pbcopy-5efdcf7ad96fdbc4276d68c28deac528.png"},7872:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/pbpaste-e53160a5f1cb41cd6b52db2407fc9bee.png"},8599:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/strip-formatting-after-718d90c0c17b8c9c164bb4153944b7ad.png"},616:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/strip-formatting-before-9ecde3dedc50fcea493f01062986dcc3.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/5e550fe2.c8b2c63e.js b/pr-preview/pr-346/assets/js/5e550fe2.c8b2c63e.js new file mode 100644 index 00000000..866ced21 --- /dev/null +++ b/pr-preview/pr-346/assets/js/5e550fe2.c8b2c63e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[3829],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>u});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t =0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},m=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},h="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),h=p(n),c=i,u=h["".concat(l,".").concat(c)]||h[c]||d[c]||o;return n?a.createElement(u,r(r({ref:t},m),{},{components:n})):a.createElement(u,r({ref:t},m))}));function u(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,r=new Array(o);r[0]=c;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[h]="string"==typeof e?e:i,r[1]=s;for(var p=2;p {n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const o={title:"Finding Files",slug:"/part-2-core-skills/finding-files"},r=void 0,s={unversionedId:"core-skills/finding-files/index",id:"core-skills/finding-files/index",title:"Finding Files",description:"Searching through a system to find files or folders can be complex and time consuming, even with a graphical user interface. In this chapter we'll look at how to use the shell to search for files and folders and some quick ways to accomplish common tasks.",source:"@site/docs/02-core-skills/11-finding-files/index.md",sourceDirName:"02-core-skills/11-finding-files",slug:"/part-2-core-skills/finding-files",permalink:"/part-2-core-skills/finding-files",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/02-core-skills/11-finding-files/index.md",tags:[],version:"current",frontMatter:{title:"Finding Files",slug:"/part-2-core-skills/finding-files"},sidebar:"sidebar",previous:{title:"Understanding Commands",permalink:"/part-2-core-skills/understanding-commands"},next:{title:"What is a Shell?",permalink:"/part-2-core-skills/what-is-a-shell"}},l={},p=[{value:"Introducing the Find Command",id:"introducing-the-find-command",level:2},{value:"Searching for Files or Folders only",id:"searching-for-files-or-folders-only",level:2},{value:"Searching by Name",id:"searching-by-name",level:2},{value:"Searching by Path",id:"searching-by-path",level:2},{value:"Combining Searches - the AND and OR operators",id:"combining-searches---the-and-and-or-operators",level:2},{value:"Case Insensitive Searches",id:"case-insensitive-searches",level:2},{value:"Grouping and the NOT operator",id:"grouping-and-the-not-operator",level:2},{value:"Printing Paths",id:"printing-paths",level:2},{value:"Deleting Files",id:"deleting-files",level:2},{value:"Execute a Command",id:"execute-a-command",level:2},{value:"Execute a Command with a Confirmation",id:"execute-a-command-with-a-confirmation",level:2}],m={toc:p};function h(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Searching through a system to find files or folders can be complex and time consuming, even with a graphical user interface. In this chapter we'll look at how to use the shell to search for files and folders and some quick ways to accomplish common tasks."),(0,i.kt)("h2",{id:"introducing-the-find-command"},"Introducing the Find Command"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," (search for files) command is used to search for files and folders and to perform operations on the results. Let's see it in action by running it in the ",(0,i.kt)("inlineCode",{parentName:"p"},"~/effective-shell")," folder."),(0,i.kt)("admonition",{title:"Downloading the Samples",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Run the following command in your shell to download the samples:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"curl effective.sh | sh\n"))),(0,i.kt)("p",null,"Let's set the current working directory to the ",(0,i.kt)("inlineCode",{parentName:"p"},"effective-shell")," folder and run the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cd ~/effective-shell\n$ find\n.\n./text\n./text/simpsons-characters.txt\n./scripts\n./scripts/show-info.sh\n./websites\n./websites/simple\n./websites/simple/index.html\n./websites/simple/styles.css\n./websites/simple/code.js\n...\n")),(0,i.kt)("p",null,"By default, ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," will list all of the files and folders which are present in the current working directory. It will also show the ",(0,i.kt)("em",{parentName:"p"},"children")," of any folders it finds, meaning that it shows the full hierarchy of files and folders."),(0,i.kt)("admonition",{title:"Find not working on MacOS",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"If you are running these samples on MacOS, you will probably see the following output:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},"$ find\nusage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]\n")),(0,i.kt)("p",{parentName:"admonition"},"This is the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command telling you what parameters can be used. On MacOS the default ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command does ",(0,i.kt)("em",{parentName:"p"},"not")," assume the current working directory."),(0,i.kt)("p",{parentName:"admonition"},"This is because there is a difference between the MacOS and GNU versions of ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," and in this book I will use GNU wherever possible as it will be more compatible (MacOS is based on the BSD operating system, most Linux distributions use a set of tools which are part of the GNU project - there are sometimes differences)."),(0,i.kt)("p",{parentName:"admonition"},"To run the equivalent command on MacOS, just provide the current directory as a parameter:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},"$ find .\n")),(0,i.kt)("p",{parentName:"admonition"},"A better solution is to install the ",(0,i.kt)("inlineCode",{parentName:"p"},"findtools")," package, which will install the GNU versions of the tools we'll be using:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},"$ brew install findtools\n$ gfind\n")),(0,i.kt)("p",{parentName:"admonition"},"If you do install ",(0,i.kt)("inlineCode",{parentName:"p"},"findtools"),", remember that all of the GNU versions of the tools start with ",(0,i.kt)("inlineCode",{parentName:"p"},"g")," - so when reading this chapter substitute ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," with ",(0,i.kt)("inlineCode",{parentName:"p"},"gfind"),"."),(0,i.kt)("p",{parentName:"admonition"},"For more details on what BSD and GNU are, you can check ",(0,i.kt)("a",{parentName:"p",href:"../../work-in-progress"},"Chapter - Unix, Linux, GNU and POSIX"),", which covers these concepts in detail.")),(0,i.kt)("p",null,"So this is the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command - you can provide it a directory (or let it use the current directory) and the command will list all of files and folders in the given directory, including all children."),(0,i.kt)("p",null,"You can also provide multiple directories:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find /usr/bin /usr/sbin\n/usr/bin\n/usr/bin/fwupdtool\n/usr/bin/gnome-keyring\n...\n/usr/sbin\n/usr/sbin/cupsd\n/usr/sbin/pppdump\n...\n")),(0,i.kt)("p",null,"This is the most basic use of ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," - showing a file and folder hierarchy. Now let's look at how to search using this command."),(0,i.kt)("h1",{id:"searching-with-find"},"Searching with Find"),(0,i.kt)("p",null,"Perhaps the most common use for ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," is to search for files. There are a number of options which can be used to filter the results shown, which allow us to search for files. Let's look at some common ways to refine our searches, using the ",(0,i.kt)("inlineCode",{parentName:"p"},"~/effective-shell")," folder as a playground to search in."),(0,i.kt)("h2",{id:"searching-for-files-or-folders-only"},"Searching for Files or Folders only"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-type")," parameter can be used to search either for files or folders. Let's see both in action. First, we'll search for files only, using ",(0,i.kt)("inlineCode",{parentName:"p"},"-type f"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type f\n./text/simpsons-characters.txt\n./scripts/show-info.sh\n./websites/simple/index.html\n./websites/simple/styles.css\n./websites/simple/code.js\n...\n")),(0,i.kt)("p",null,"And for folders, using ",(0,i.kt)("inlineCode",{parentName:"p"},"-type d")," (remember, ",(0,i.kt)("inlineCode",{parentName:"p"},"d")," is for directory!):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type d\n.\n./text\n./scripts\n./websites\n./websites/simple\n...\n")),(0,i.kt)("p",null,"It's important to note that when searching for folders, the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command shows folders which are normally hidden, such as the special 'dot' folder",(0,i.kt)("sup",{parentName:"p",id:"fnref-1"},(0,i.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),"."),(0,i.kt)("p",null,"In both commands, I specified the 'dot' folder as the place to search. I could omit this parameter; I just think it makes it a little more readable."),(0,i.kt)("h2",{id:"searching-by-name"},"Searching by Name"),(0,i.kt)("p",null,"We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-name")," parameter to search for files and folders by name. For example, this is how we would search for anything with the letters ",(0,i.kt)("inlineCode",{parentName:"p"},"log")," in the name:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "*log*"\n./logs\n./logs/web-server-logs.txt\n./logs/apm-logs\n./logs/apm-logs/apm05.logs\n./logs/apm-logs/apm02.logs\n./logs/apm-logs/apm03.logs\n./logs/apm-logs/apm00.logs\n./logs/apm-logs/apm01.logs\n./logs/apm-logs/apm04.logs\n')),(0,i.kt)("p",null,"You can see I've used a ",(0,i.kt)("inlineCode",{parentName:"p"},"*")," wildcard before and after the letters ",(0,i.kt)("inlineCode",{parentName:"p"},"log")," - this means that I have actually supplied a ",(0,i.kt)("em",{parentName:"p"},"pattern"),", which could be read as 'any characters (including nothing), followed by the characters ",(0,i.kt)("inlineCode",{parentName:"p"},"log"),", followed by any other characters (including no characters)'."),(0,i.kt)("p",null,"If I don't use a wildcard, the ",(0,i.kt)("inlineCode",{parentName:"p"},"logs")," folder will not be found - because it doesn't match the ",(0,i.kt)("em",{parentName:"p"},"pattern")," ",(0,i.kt)("inlineCode",{parentName:"p"},"log")," - without using a wildcard, the pattern is explicitly looking for an ",(0,i.kt)("em",{parentName:"p"},"exact")," match:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "log"\n')),(0,i.kt)("p",null,"Note the output - no files or folders were found, as none have the ",(0,i.kt)("em",{parentName:"p"},"exact")," name ",(0,i.kt)("inlineCode",{parentName:"p"},"log"),". The ",(0,i.kt)("inlineCode",{parentName:"p"},"-name")," parameter is very specific - it will only match files or folders with the ",(0,i.kt)("em",{parentName:"p"},"name")," provided. Here's an example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "apm00.logs"\n./logs/apm-logs/apm00.logs\n')),(0,i.kt)("p",null,"Here I have used ",(0,i.kt)("inlineCode",{parentName:"p"},"-name")," to search for an ",(0,i.kt)("em",{parentName:"p"},"exact")," name. What about if I search for ",(0,i.kt)("inlineCode",{parentName:"p"},"apm-logs"),"?"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "apm-logs"\n./logs/apm-logs\n')),(0,i.kt)("p",null,"The ",(0,i.kt)("em",{parentName:"p"},"folder")," named ",(0,i.kt)("inlineCode",{parentName:"p"},"apm-logs")," is found, but not the ",(0,i.kt)("em",{parentName:"p"},"files")," in the folder - the names of those files don't match the pattern ",(0,i.kt)("inlineCode",{parentName:"p"},"apm-logs"),". What if we make it a wildcard pattern?"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "*apm-logs*"\n./logs/apm-logs\n')),(0,i.kt)("p",null,"The same results! This is because for the ",(0,i.kt)("em",{parentName:"p"},"files")," in the ",(0,i.kt)("inlineCode",{parentName:"p"},"apm-logs")," folder they don't have ",(0,i.kt)("inlineCode",{parentName:"p"},"apm-logs")," in their name anywhere - that is in their ",(0,i.kt)("em",{parentName:"p"},"path"),", i.e. the full address of the file including its folder. So let's look at how to search by path next!"),(0,i.kt)("admonition",{title:"An Important Note on Quotes",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Make sure you use quotes when building your patterns. This command:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},'$ find . -name "*log*"\n')),(0,i.kt)("p",{parentName:"admonition"},"Will give different output to this command:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},"$ find . -name *log*\n")),(0,i.kt)("p",{parentName:"admonition"},"Why is this? In the first case, we explicitly pass the text ",(0,i.kt)("inlineCode",{parentName:"p"},"*log*")," to the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command and let it deal with it. It uses the wildcards to build a pattern. Because we've surrounded the parameter with quotes, the shell itself doesn't try to do anything clever with the wildcard."),(0,i.kt)("p",{parentName:"admonition"},"In the second case, ",(0,i.kt)("em",{parentName:"p"},"the shell itself")," tries to deal with the wildcards, then passes the results to ",(0,i.kt)("inlineCode",{parentName:"p"},"find"),". And the shell deals with them differently. You can see exactly what the shell expands them to with this snippet:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},"$ parameter=(*log*)\n$ echo $parameter\nlogs\n")),(0,i.kt)("p",{parentName:"admonition"},"In the second case the shell is performing its own expansion of the wildcard and not searching through all of the child directories. We need to wrap the parameter with quotes so that the shell knows ",(0,i.kt)("em",{parentName:"p"},"not")," to interfere with the text and instead pass it to ",(0,i.kt)("inlineCode",{parentName:"p"},"find"),", so that find can deal with the wildcard."),(0,i.kt)("p",{parentName:"admonition"},"The shell is using ",(0,i.kt)("em",{parentName:"p"},"globbing")," in the second case, which is covered in a later chapter.")),(0,i.kt)("h2",{id:"searching-by-path"},"Searching by Path"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-path")," parameter can be used to filter the results based on a pattern in the ",(0,i.kt)("em",{parentName:"p"},"path")," of the file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -path "*apm-logs*"\n./logs/apm-logs\n./logs/apm-logs/apm05.logs\n./logs/apm-logs/apm02.logs\n./logs/apm-logs/apm03.logs\n./logs/apm-logs/apm00.logs\n./logs/apm-logs/apm01.logs\n./logs/apm-logs/apm04.logs\n')),(0,i.kt)("p",null,"Again, note that this is very specific, we've added wildcards to the pattern, making it ",(0,i.kt)("inlineCode",{parentName:"p"},"*apm-logs*"),". Without the wildcards we would find nothing, because none of the results have the ",(0,i.kt)("em",{parentName:"p"},"exact")," path ",(0,i.kt)("inlineCode",{parentName:"p"},"apm-logs"),"."),(0,i.kt)("h2",{id:"combining-searches---the-and-and-or-operators"},"Combining Searches - the AND and OR operators"),(0,i.kt)("p",null,"We can provide multiple search options. For example, if we want to search ",(0,i.kt)("em",{parentName:"p"},"only")," for files which end in ",(0,i.kt)("inlineCode",{parentName:"p"},".logs"),", we can do this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -type f -name "*.logs"\n./logs/apm-logs/apm05.logs\n./logs/apm-logs/apm02.logs\n./logs/apm-logs/apm03.logs\n./logs/apm-logs/apm00.logs\n./logs/apm-logs/apm01.logs\n./logs/apm-logs/apm04.logs\n')),(0,i.kt)("p",null,"By using both the ",(0,i.kt)("inlineCode",{parentName:"p"},"-type")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"-name")," parameters, we've created an 'AND' style search - i.e. we're looking for items which match ",(0,i.kt)("em",{parentName:"p"},"both")," of the given criteria."),(0,i.kt)("p",null,"What if we want to perform a search which returns items which match ",(0,i.kt)("em",{parentName:"p"},"either")," of the patterns (i.e an 'OR' search)? For that we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-or")," parameter:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "*.js" -or -name "*.html"\n./websites/simple/index.html\n./websites/simple/code.js\n./programs/web-server/web-server.js\n')),(0,i.kt)("p",null,"In this case we show results which match either of the expressions."),(0,i.kt)("h2",{id:"case-insensitive-searches"},"Case Insensitive Searches"),(0,i.kt)("p",null,"Any one of the search parameters you've seen so far can be made case-insensitive by putting the letter ",(0,i.kt)("inlineCode",{parentName:"p"},"i")," before the parameter name."),(0,i.kt)("p",null,"This means that the following commands are identical:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "*.js" -or -name "*.Js" -or -name "*.jS" -or name "*.JS"\n')),(0,i.kt)("p",null,"And:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -iname "*.js"\n')),(0,i.kt)("p",null,"I know which one I'd rather type! You can use ",(0,i.kt)("inlineCode",{parentName:"p"},"-iname")," for case-insensitive name searches, or ",(0,i.kt)("inlineCode",{parentName:"p"},"-ipath")," for case-insensitive path searches."),(0,i.kt)("h2",{id:"grouping-and-the-not-operator"},"Grouping and the NOT operator"),(0,i.kt)("p",null,"We can build more complex expressions by grouping together patterns using brackets, or by using the ",(0,i.kt)("inlineCode",{parentName:"p"},"-not")," pattern. Here's an example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . \\( -name "*.js" -or -name "*.html" \\) -and -not -path "*programs*"\n./websites/simple/index.html\n./websites/simple/code.js\n')),(0,i.kt)("p",null,'The first section groups together two expressions, meaning "files with names which match ',(0,i.kt)("inlineCode",{parentName:"p"},"*.js")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"*.html"),', the second section then says "and also the text ',(0,i.kt)("inlineCode",{parentName:"p"},"programs"),' must not be in the path".'),(0,i.kt)("p",null,"The only annoying thing about grouping is that you must ",(0,i.kt)("em",{parentName:"p"},"escape")," the brackets, by putting a ",(0,i.kt)("inlineCode",{parentName:"p"},"\\")," backslash before them, as brackets have a special meaning in the shell and we're telling the shell not to do anything smart with them but instead pass them to the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command."),(0,i.kt)("h1",{id:"why-the-weird-parameters"},"Why the Weird Parameters?"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command bothered me for years. The parameters looked odd - for example why is it ",(0,i.kt)("inlineCode",{parentName:"p"},"-name")," instead of ",(0,i.kt)("inlineCode",{parentName:"p"},"-n")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"--name"),", which would be more standard",(0,i.kt)("sup",{parentName:"p",id:"fnref-2"},(0,i.kt)("a",{parentName:"sup",href:"#fn-2",className:"footnote-ref"},"2")),"?"),(0,i.kt)("p",null,"Also, why is it that I cannot type this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'find -name "something.txt" /home/\n')),(0,i.kt)("p",null,"But instead have to put the folder name ",(0,i.kt)("em",{parentName:"p"},"before")," the parameters, which again is non-standard?"),(0,i.kt)("p",null,"The reason is actually quite simple - most of what we've seen so far are not parameters in the normal sense, they're just elements of a ",(0,i.kt)("em",{parentName:"p"},"search expression"),"."),(0,i.kt)("p",null,"The structure of the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command is as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"find [starting point...] [expression]\n")),(0,i.kt)("p",null,"The options (or parameters) for the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command ",(0,i.kt)("em",{parentName:"p"},"are")," short, one-letter options which go before the file name. The ",(0,i.kt)("inlineCode",{parentName:"p"},"-L")," (",(0,i.kt)("em",{parentName:"p"},"follow links"),") option is an example - it goes before the starting point of the search:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'find -L /usr/bin -name "*sh"\n')),(0,i.kt)("p",null,"All of the other things we've seen so far which we've described as parameters are actually used to build the ",(0,i.kt)("em",{parentName:"p"},"expression")," - the actual search pattern."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-name"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"-and"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"-or"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"-ipath")," and similar constructs we've looked at are actually part of a mini 'search language', they're not parameters to the command."),(0,i.kt)("p",null,"This might seem obvious, or it might seem silly, but either way, remembering that the structure of the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command is ",(0,i.kt)("inlineCode",{parentName:"p"},"find [starting points...] [expression]")," may help you remember what order to write each part of the command in."),(0,i.kt)("p",null,"It certainly took a few years for me to realise this was the reason, and until that point I used to get frustrated with ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," as I could never seem to remember how to use it properly! Once the structure of the command clicked it became far easier to quickly use the command in day-to-day work."),(0,i.kt)("p",null,"You can find details on this in the manpage for find, open it with ",(0,i.kt)("inlineCode",{parentName:"p"},"man find"),"."),(0,i.kt)("h1",{id:"performing-actions-on-search-results"},"Performing Actions on Search Results"),(0,i.kt)("p",null,"A lot of the time you are not just going to be searching for a file or folder - you'll be searching so that you can do something with what you find. It might be to delete, copy, edit, move or whatever."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command can perform operations on the files which are found."),(0,i.kt)("p",null,"Now before we continue, I'll warn that I'm ",(0,i.kt)("em",{parentName:"p"},"not")," going to go into much detail here. The reason is that I actually recommend not using these operations. Instead, use the ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," command which is covered in ",(0,i.kt)("a",{parentName:"p",href:"../../work-in-progress"},"Chapter 14 - Build Commands on the Fly with Xargs"),". The Xargs command lets you take the list of files from ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," and create any command you can think of. I think this is far more sensible than trying to learn all of the options for ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," - and it is closer to the Unix Philosophy of having the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command 'do one thing and do it well'."),(0,i.kt)("p",null,"However, you'll see these operations in other books and articles, or perhaps in scripts you have to work with, so let's take a quick look at some of the common operations and how they are used. Just remember that later on we'll see a more flexible (and easier to remember!) way of operating on the files we've found!"),(0,i.kt)("h2",{id:"printing-paths"},"Printing Paths"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-print")," action is the default action and doesn't need to be explicitly specified. But if you feel it makes your scripts or code more readable, you can always include it, and it gives us a way to show the syntax for actions."),(0,i.kt)("p",null,"Here's how we'd find all files in the user's home directory with the ending ",(0,i.kt)("inlineCode",{parentName:"p"},".tmp")," and show their path:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find ~ -name "*.tmp" -print\n/home/dwmkerr/commands1.tmp\n/home/dwmkerr/commands2.tmp\n/home/dwmkerr/commands3.tmp\n')),(0,i.kt)("p",null,"You are unlikely to need to use ",(0,i.kt)("inlineCode",{parentName:"p"},"-print")," - but it will come in handy to know it exists later on when we look at the ",(0,i.kt)("inlineCode",{parentName:"p"},"-print0")," option (we'll see this in Chapter 14). Let's look at the other actions we can perform."),(0,i.kt)("h2",{id:"deleting-files"},"Deleting Files"),(0,i.kt)("p",null,"We can delete the files and folders found by using the ",(0,i.kt)("inlineCode",{parentName:"p"},"-delete")," action:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find ~ -name "*.tmp" -delete\n')),(0,i.kt)("p",null,"This ",(0,i.kt)("em",{parentName:"p"},"can")," be a convenient way to delete files, but I would recommend ",(0,i.kt)("em",{parentName:"p"},"extreme caution"),". This command does not show what has been deleted and does not ask for confirmation. It also slightly changes the order of results processed so that the ",(0,i.kt)("em",{parentName:"p"},"children")," of folders are deleted where necessary before the folders themselves. This might not be what you expect because that's not what the ",(0,i.kt)("inlineCode",{parentName:"p"},"-print")," output would show (although you can force this behaviour with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-depth")," option)."),(0,i.kt)("p",null,"Check Chapter 14 for a better solution - in short we can use ",(0,i.kt)("inlineCode",{parentName:"p"},'find ~ -name "*.tmp" -print0 | xargs -p -0 rm')," to instead pass the files to ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," and ask the user to confirm before the deletion happens. This will be explained in a lot more detail in Chapter 14."),(0,i.kt)("h2",{id:"execute-a-command"},"Execute a Command"),(0,i.kt)("p",null,"You can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-exec")," action to execute an arbitrary command. Use the special characters ",(0,i.kt)("inlineCode",{parentName:"p"},"{}")," as a placeholder for the filename."),(0,i.kt)("p",null,"Here's an example - we'll find all text files and count the number of words in each one:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find ~/effective-shell -name "*.txt" -exec wc -w {} \\;\n29 /home/parallels/effective-shell/text/simpsons-characters.txt\n20 /home/parallels/effective-shell/quotes/iain-banks.txt\n16 /home/parallels/effective-shell/quotes/ursula-le-guin.txt\n10373 /home/parallels/effective-shell/logs/web-server-logs.txt\n')),(0,i.kt)("p",null,"We use ",(0,i.kt)("inlineCode",{parentName:"p"},"-exec")," to tell ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," we want to execute a command. Then we use ",(0,i.kt)("inlineCode",{parentName:"p"},"wc -w {}"),' to say "call the ',(0,i.kt)("inlineCode",{parentName:"p"},"wc")," (word count) command with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-w")," (words) flag. The ",(0,i.kt)("inlineCode",{parentName:"p"},"{}")," text is expanded to the list of files. Finally, we need a semi-colon to tell ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," where the ",(0,i.kt)("em",{parentName:"p"},"end")," of the ",(0,i.kt)("inlineCode",{parentName:"p"},"exec")," command is. And because the semi-colon has a special meaning in the shell, we have to ",(0,i.kt)("em",{parentName:"p"},"escape")," this semi-colon by putting a ",(0,i.kt)("inlineCode",{parentName:"p"},"\\")," backslash before it."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-exec")," action is very powerful - we can construct almost any arbitrary command with it, which can be really useful. But remember we'll see what I think is a more flexible way to build commands a little later."),(0,i.kt)("h2",{id:"execute-a-command-with-a-confirmation"},"Execute a Command with a Confirmation"),(0,i.kt)("p",null,"Now if there is one action to learn, it is the ",(0,i.kt)("inlineCode",{parentName:"p"},"-ok")," action, which works just like ",(0,i.kt)("inlineCode",{parentName:"p"},"-exec"),", but asks the user for a confirmation first. Here's how it might look if I use it to try and delete all files which end in ",(0,i.kt)("inlineCode",{parentName:"p"},"*.txt"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find ~/effective-shell -name "*.txt" -ok rm {} \\;\n< rm ... /home/parallels/effective-shell/text/simpsons-characters.txt > ? n\n< rm ... /home/parallels/effective-shell/quotes/iain-banks.txt > ? n\n< rm ... /home/parallels/effective-shell/quotes/ursula-le-guin.txt > ? n\n< rm ... /home/parallels/effective-shell/logs/web-server-logs.txt > ? n\n')),(0,i.kt)("p",null,"Note that each operation which will be performed is first printed, then I am asked for confirmation before the operation runs. In each case I've typed ",(0,i.kt)("inlineCode",{parentName:"p"},"n")," (for 'no'). Type ",(0,i.kt)("inlineCode",{parentName:"p"},"y")," (for 'yes') if you want to run the command."),(0,i.kt)("h1",{id:"handling-symlinks"},"Handling Symlinks"),(0,i.kt)("p",null,"It is worth briefly mentioning symlinks - because if you don't understand how ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," handles symlinks then you might be surprised."),(0,i.kt)("p",null,"As an example of how this might surprise you, compare the output of the two commands below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find /usr/bin\n/usr/bin\n/usr/bin/uux\n/usr/bin/cpan\n/usr/bin/BuildStrings\n/usr/bin/loads.d\n/usr/bin/write\n...\n$ find /bin\n/bin\n")),(0,i.kt)("p",null,"It seems that ",(0,i.kt)("inlineCode",{parentName:"p"},"/bin")," doesn't contain any files - but is that the case? Running ",(0,i.kt)("inlineCode",{parentName:"p"},"ls /bin")," will probably show that you have quite a few files. If you are on MacOS instead try running ",(0,i.kt)("inlineCode",{parentName:"p"},"find /tmp")," to show the same oddity - the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," program doesn't seem to show the contents of the files."),(0,i.kt)("p",null,"So why did ",(0,i.kt)("inlineCode",{parentName:"p"},"find /bin")," not show the files in the ",(0,i.kt)("inlineCode",{parentName:"p"},"/bin")," folder?"),(0,i.kt)("p",null,"The answer is that ",(0,i.kt)("inlineCode",{parentName:"p"},"/bin")," is a symlink (or if you are on MacOS and want to test the same behaviour and are using ",(0,i.kt)("inlineCode",{parentName:"p"},"/tmp"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"/tmp")," is a symlink). You can see this by running the command below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ ls -l / /usr | grep bin\nlrwxrwxrwx 1 root root 7 Aug 7 18:06 bin -> usr/bin\nlrwxrwxrwx 1 root root 8 Aug 7 18:06 sbin -> usr/sbin\ndrwxr-xr-x 2 root root 40960 Jan 25 17:17 bin\ndrwxr-xr-x 2 root root 20480 Jan 25 16:42 sbin\n")),(0,i.kt)("p",null,"Note how the root ",(0,i.kt)("inlineCode",{parentName:"p"},"bin")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"sbin")," folders are actually just symlinks to ",(0,i.kt)("inlineCode",{parentName:"p"},"usr/bin")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"usr/sbin")," respectively."),(0,i.kt)("p",null,"When using the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command just remember that it won't follow symlinks by default - provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"-L")," option to follow symlinks:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find -L /bin\n/bin\n/bin/fwupdtool\n/bin/gnome-keyring\n/bin/dpkg-gencontrol\n/bin/prltoolsd\n...\n")),(0,i.kt)("p",null,"There are more options which control how ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," works with links, check ",(0,i.kt)("inlineCode",{parentName:"p"},"man find")," for the details."),(0,i.kt)("h1",{id:"scratching-the-surface"},"Scratching the Surface"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command is ",(0,i.kt)("em",{parentName:"p"},"incredibly")," powerful. To go into detail on all of the options or potential ways these options can be combined to create operations could fill an entire book!"),(0,i.kt)("p",null,"My recommendation is to ensure that you know at least the basics we've shown so far. But just as a hint of what can be done with ",(0,i.kt)("inlineCode",{parentName:"p"},"find"),", which might make you want to learn more, here are a few commands which show just how versatile it can be!"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Find large files")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-size")," test can be used to search by file size - note how with a ",(0,i.kt)("inlineCode",{parentName:"p"},"+")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"-")," we can set the minimum and maximum sizes:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"find / -size +1G -500G\n")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Find recently edited files")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-mtime")," test can be used to find files which were recently modified:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'find . -not -path "*/\\.*" -mtime -2\n')),(0,i.kt)("p",null,"Note how a ",(0,i.kt)("inlineCode",{parentName:"p"},"-not -path")," test is used to skip anything which starts with a ",(0,i.kt)("inlineCode",{parentName:"p"},".")," dot (i.e files and folders which are normally considered hidden)."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Find files which have had permissions changed")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-ctime")," test can be used to find files which have had their attributes (such as permissions) changed:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"find ~/.ssh -ctime -30\n")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Find any executable scripts and make them non-executable")),(0,i.kt)("p",null,"We can search by permissions, as shown below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"find ~ -perm /a=x -exec chmod -x {} +\n")),(0,i.kt)("p",null,"This example uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"-perm")," test, checking if 'all' (users, the owner and group) have the ",(0,i.kt)("inlineCode",{parentName:"p"},"x")," (executable) bit set, then executes the ",(0,i.kt)("inlineCode",{parentName:"p"},"chmod -x")," command to remove the executable bit. We also end the command with ",(0,i.kt)("inlineCode",{parentName:"p"},"+")," rather than ",(0,i.kt)("inlineCode",{parentName:"p"},";"),", which means we will execute ",(0,i.kt)("inlineCode",{parentName:"p"},"chmod")," once with each file passed to the command (rather than running ",(0,i.kt)("inlineCode",{parentName:"p"},"chmod")," for ",(0,i.kt)("em",{parentName:"p"},"each")," file). Note that the ",(0,i.kt)("inlineCode",{parentName:"p"},"+")," operator can cause an error if the list of files is too big for the command you pass it to to handle!"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Find empty folders and remove them with a confirmation")),(0,i.kt)("p",null,"We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-empty")," test to find empty folders:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"find ~ -type d -maxdepth 3 -empty -ok rmdir {} \\;\n")),(0,i.kt)("p",null,"This example uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"-empty")," test, as well as the ",(0,i.kt)("inlineCode",{parentName:"p"},"-maxdepth")," parameter to limit the search to only three folders deep."),(0,i.kt)("p",null,"These examples just scratch the surface of what ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," can do. The goal of this chapter is not to have an exhaustive description of the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command, but equip you with the essentials. When you feel comfortable with the essentials you can then build on your knowledge of ",(0,i.kt)("inlineCode",{parentName:"p"},"find"),"."),(0,i.kt)("h1",{id:"summary"},"Summary"),(0,i.kt)("p",null,"In this chapter we introduced the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command, an incredibly powerful tool that lets us search for files and folders using simple or complex expressions. It also allows us to perform actions on the search results."),(0,i.kt)("p",null,"In the next chapter we'll take a look in a bit more detail into what a shell actually is!"),(0,i.kt)("hr",null),(0,i.kt)("p",null,'Share - with "why the hell are the parameters so stupid"\nBlog post on linux essentials, refer to alpine for an example of why find is important\nTest work in progress page'),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Footnotes")),(0,i.kt)("div",{className:"footnotes"},(0,i.kt)("hr",{parentName:"div"}),(0,i.kt)("ol",{parentName:"div"},(0,i.kt)("li",{parentName:"ol",id:"fn-1"},"If you are not sure what the 'dot' folder is, check ",(0,i.kt)("a",{parentName:"li",href:"https://effective-shell.com/docs/part-1-transitioning-to-the-shell/2-navigating-your-system/"},"Chapter 2 - Navigating Your System"),(0,i.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-2"},"This is not just a personal preference thing; this is based on the POSIX standard: ",(0,i.kt)("a",{parentName:"li",href:"https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html"},"https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html")," which recommends a specific set of patterns to make commands consistent and intuitive for user.",(0,i.kt)("a",{parentName:"li",href:"#fnref-2",className:"footnote-backref"},"\u21a9")))))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/60f57d81.c7979b27.js b/pr-preview/pr-346/assets/js/60f57d81.c7979b27.js new file mode 100644 index 00000000..b7549af0 --- /dev/null +++ b/pr-preview/pr-346/assets/js/60f57d81.c7979b27.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[1258],{3905:(e,t,a)=>{a.d(t,{Zo:()=>h,kt:()=>c});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function s(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function o(e){for(var t=1;t =0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},h=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},d="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,s=e.originalType,l=e.parentName,h=i(e,["components","mdxType","originalType","parentName"]),d=p(a),u=r,c=d["".concat(l,".").concat(u)]||d[u]||m[u]||s;return a?n.createElement(c,o(o({ref:t},h),{},{components:a})):n.createElement(c,o({ref:t},h))}));function c(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var s=a.length,o=new Array(s);o[0]=u;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i[d]="string"==typeof e?e:r,o[1]=i;for(var p=2;p {a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>s,metadata:()=>i,toc:()=>p});var n=a(7462),r=(a(7294),a(3905));const s={title:"Understanding Shell Expansion",slug:"/part-6-advanced-techniques/understanding-shell-expansion/"},o=void 0,i={unversionedId:"advanced-techniques/understanding-shell-expansion/index",id:"advanced-techniques/understanding-shell-expansion/index",title:"Understanding Shell Expansion",description:"When you are working with the shell there are a number of techniques that you can use to take simple commands and make more useful. For example, if we wanted to create three files, we could run touch file1 file2 file3, or we could use 'brace expansion' and just run touch file{1..3}. Another example would be to delete all files that have names that start with file - like this rm file*, this is wildcard expansion.",source:"@site/docs/06-advanced-techniques/29-understanding-shell-expansion/index.md",sourceDirName:"06-advanced-techniques/29-understanding-shell-expansion",slug:"/part-6-advanced-techniques/understanding-shell-expansion/",permalink:"/part-6-advanced-techniques/understanding-shell-expansion/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/06-advanced-techniques/29-understanding-shell-expansion/index.md",tags:[],version:"current",frontMatter:{title:"Understanding Shell Expansion",slug:"/part-6-advanced-techniques/understanding-shell-expansion/"},sidebar:"sidebar",previous:{title:"Part 6 - Advanced Techniques",permalink:"/part-6-advanced-techniques/"},next:{title:"How to Avoid Scripting - A Dictionary Lookup in Python",permalink:"/part-6-advanced-techniques/how-to-avoid-scripting/"}},l={},p=[{value:"What is Shell Expansion?",id:"what-is-shell-expansion",level:2},{value:"Shell Expansion",id:"shell-expansion",level:2},{value:"Brace Expansion",id:"brace-expansion",level:3},{value:"Tilde Expansion",id:"tilde-expansion",level:3},{value:"Parameter Expansion",id:"parameter-expansion",level:3},{value:"Command Substitution",id:"command-substitution",level:3},{value:"Arithmetic Expansion",id:"arithmetic-expansion",level:3},{value:"Word Splitting",id:"word-splitting",level:3},{value:"Pathname Expansion",id:"pathname-expansion",level:3},{value:"Summary",id:"summary",level:2}],h={toc:p};function d(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,n.Z)({},h,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"When you are working with the shell there are a number of techniques that you can use to take simple commands and make more useful. For example, if we wanted to create three files, we could run ",(0,r.kt)("inlineCode",{parentName:"p"},"touch file1 file2 file3"),", or we could use 'brace expansion' and just run ",(0,r.kt)("inlineCode",{parentName:"p"},"touch file{1..3}"),". Another example would be to delete all files that have names that start with ",(0,r.kt)("inlineCode",{parentName:"p"},"file")," - like this ",(0,r.kt)("inlineCode",{parentName:"p"},"rm file*"),", this is wildcard expansion."),(0,r.kt)("p",null,"Collectively, these features are called 'Shell Expansion'. I think that introducing the entire set of features that make up shell expansion in one go can be a bit overwhelming, but now that we are in the advanced chapters it makes sense to understand exactly what shell expansion is, when it occurs, when it doesn't and how understanding it can make you a more effective user."),(0,r.kt)("p",null,"There are seven types of expansion that occur in the shell - in this chapter we'll look at each in detail and then see how they work together."),(0,r.kt)("h2",{id:"what-is-shell-expansion"},"What is Shell Expansion?"),(0,r.kt)("p",null,"When the shell receives a command, either from the user typing at the keyboard, or from a shell script, it breaks it up into words. After this happens, the shell performs seven operations on the words, which can change how they are interpreted. These seven operations are collectively known as 'shell expansion'. You are probably familiar with most of them as we have used them throughout this book."),(0,r.kt)("p",null,"The seven operations that the shell performs are:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Brace Expansion - expanding values between braces, such as ",(0,r.kt)("inlineCode",{parentName:"li"},"file{1..3}")," into ",(0,r.kt)("inlineCode",{parentName:"li"},"file1 file2 file3")),(0,r.kt)("li",{parentName:"ol"},"Tilde Expansion - expanding the ",(0,r.kt)("inlineCode",{parentName:"li"},"~")," tilde symbol for the home directory into the path to the home directory, such as ",(0,r.kt)("inlineCode",{parentName:"li"},"~/effective-shell")," into ",(0,r.kt)("inlineCode",{parentName:"li"},"/home/dwmkerr/effective-shell")),(0,r.kt)("li",{parentName:"ol"},"Parameter Expansion - expanding terms that start with a ",(0,r.kt)("inlineCode",{parentName:"li"},"$")," symbol into parameter values, such as ",(0,r.kt)("inlineCode",{parentName:"li"},"$HOME")," into the value of the variable named ",(0,r.kt)("inlineCode",{parentName:"li"},"HOME")),(0,r.kt)("li",{parentName:"ol"},"Command Substitution - evaluation of the contents of ",(0,r.kt)("inlineCode",{parentName:"li"},"$(command)")," sequences, which are used to run commands and return the results to the shell command line"),(0,r.kt)("li",{parentName:"ol"},"Arithmetic Expansion - evaluation of the contents of ",(0,r.kt)("inlineCode",{parentName:"li"},"$((expression))")," sequences, which are used to perform basic mathematical operations"),(0,r.kt)("li",{parentName:"ol"},"Word Splitting - once all of the previous operations are run, the shell splits the command up into 'words', which are the units of text that you can run loops over"),(0,r.kt)("li",{parentName:"ol"},"Pathname Expansion - the shell expands wildcards and special characters in pathnames, such as ",(0,r.kt)("inlineCode",{parentName:"li"},"file*.txt")," into the set of files that are matched by the sequence")),(0,r.kt)("p",null,"If you want to see each of these operations in the manual, you can run ",(0,r.kt)("inlineCode",{parentName:"p"},"man bash")," and search for the text ",(0,r.kt)("inlineCode",{parentName:"p"},"^EXPANSION"),". Now let's see how each operation works in more detail."),(0,r.kt)("h2",{id:"shell-expansion"},"Shell Expansion"),(0,r.kt)("p",null,"Let's take a look through each of the forms of shell expansion that are available to use."),(0,r.kt)("h3",{id:"brace-expansion"},"Brace Expansion"),(0,r.kt)("p",null,"Brace expansion"," is the first shell expansion operation that occurs, it expands a simple expression that represents a sequence or range of characters."),(0,r.kt)("p",null,"In the examples below I'll show the expression on the first line and then what it expands to on the second line. The first example expands a set words or characters:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir /tmp/{one,two,three}\n\n# The line above is expanded to:\nmdkir /tmp/one /tmp/two /tmp/three\n")),(0,r.kt)("p",null,"Expansions of sets like this are a great way to perform operations that work on multiple files or folders at once."),(0,r.kt)("p",null,"We can also create sequences of numbers or characters:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"touch file{1..5}.txt\n\n# The line above is expanded to:\ntouch file1.txt file2.txt file3.txt file4.txt file5.txt\n")),(0,r.kt)("p",null,"You as well as specifying the start and end of a sequence, you can specify the increment, you might see this in for loops like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"for x in {0..10..2}; do print $x; done\n\n# The line above is expanded to:\nfor x in 0 2 4 6 8 10; do print $x; done\n")),(0,r.kt)("h3",{id:"tilde-expansion"},"Tilde Expansion"),(0,r.kt)("p",null,"If a word starts with a ",(0,r.kt)("inlineCode",{parentName:"p"},"~")," tilde character, then the shell will expand the tilde into the value of the ",(0,r.kt)("inlineCode",{parentName:"p"},"$HOME")," variable:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"cd ~/effective-shell\n\n# The line above is expanded to:\ncd $HOME/effective-shell\n")),(0,r.kt)("p",null,"If we were to unset the ",(0,r.kt)("inlineCode",{parentName:"p"},"$HOME")," variable, then the expansion would use the current user's home directory:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"unset HOME\ncd ~/effective-shell\n\n# The line above is expanded to:\ncd /home/dwmkerr/effective-shell\n")),(0,r.kt)("p",null,"Tilde expansion is very simple!"),(0,r.kt)("h3",{id:"parameter-expansion"},"Parameter Expansion"),(0,r.kt)("p",null,"When the dollar symbol ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," is used, this indicates that the shell is going to perform ",(0,r.kt)("em",{parentName:"p"},"parameter expansion"),", which expands variables or the parameters of a script. It can also be used to indicate ",(0,r.kt)("em",{parentName:"p"},"command substitution")," or ",(0,r.kt)("em",{parentName:"p"},"arithmetic expansion")," - which we will see once we've looked at parameter expansion."),(0,r.kt)("p",null,"A lot of these expansions are covered in detail in ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/variables-reading-input-and-mathematics/"},"Chapter 19 - Variables, Reading Input, and Mathematics")," but I have included each of the available expansions here for reference."),(0,r.kt)("p",null,"In its most simple form, parameter expansion simple replaces the name of a variable or parameter with its value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'fruit=apples\necho "I like $fruit"\n\n# The line above is expanded to:\necho "I like apples"\n')),(0,r.kt)("p",null,"When using parameter expansion it is generally preferable to surround the name of the parameter with braces - this allows you to tell the shell unambiguously what the name of the parameter is. For example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'echo "My backup folder is: ${HOME}backup"\n\n# The line above is expanded to:\necho "My backup folder is: /home/dwmkerrbackup"\n')),(0,r.kt)("p",null,"If we had ",(0,r.kt)("em",{parentName:"p"},"not")," used braces, then the shell would expand the expression like so:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'echo "My backup folder is: $HOMEbackup"\n\n# The line above is expanded to:\necho "My backup folder is: "\n')),(0,r.kt)("p",null,"The reason that the expansion doesn't work as expected in this case is that the shell is trying to expand a parameter with the name ",(0,r.kt)("inlineCode",{parentName:"p"},"HOMEbackup")," - the braces used in the first example make it clear to the shell that the parameter name is ",(0,r.kt)("inlineCode",{parentName:"p"},"HOME")," and that the text ",(0,r.kt)("inlineCode",{parentName:"p"},"backup")," should be added at the end of the expanded value."),(0,r.kt)("p",null,"There are a number of additional features available for parameter expansion that can make it more convenient. Let's look at each of them now."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Default Values")),(0,r.kt)("p",null,"The expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter:-default}")," will expand to the value of the parameter named ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter")," - but if that value is not set, then the value ",(0,r.kt)("inlineCode",{parentName:"p"},"default")," is used. This can be convenient if you want to provide a value for the shell to use when a parameter is not set."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Assign Default Values")),(0,r.kt)("p",null,"The expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter:=default}")," will expand to the value of the parameter named ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter")," - but if that value is not set, then the value ",(0,r.kt)("inlineCode",{parentName:"p"},"default")," is used. In this case, ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter")," is also set to ",(0,r.kt)("inlineCode",{parentName:"p"},"default"),". This means that this expression works just like the 'default values' expression above, but also sets the parameter at the same time."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Display Error if Null or Unset")),(0,r.kt)("p",null,"The expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter:?message}")," tells the shell to expand to the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter"),", and if that value is null or unset, to instead write the message ",(0,r.kt)("inlineCode",{parentName:"p"},"message")," to standard error and exit (unless the shell is interactive, in which case the shell is not closed)."),(0,r.kt)("p",null,"This can be a convenient way to put a 'guard' in place to ensure that a script aborts if a value is not set. Here's an example of how this can be used:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"backup_location=${BACKUP_DIR:?Please set BACKUP_DIR to use this script}\ncp -r ~/effective-shell ${BACKUP_DIR}\n")),(0,r.kt)("p",null,"In this script we copy the ",(0,r.kt)("em",{parentName:"p"},"~/effective-shell")," folder to the folder set in the ",(0,r.kt)("inlineCode",{parentName:"p"},"BACKUP_DIR")," parameter. However, if that parameter has not been set then the script will abort and show an error message telling the operator that the ",(0,r.kt)("inlineCode",{parentName:"p"},"BACKUP_DIR")," parameter must be set."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Use Alternate Value")),(0,r.kt)("p",null,"The expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter:+alternate}")," expands to an empty string if ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter")," is null or unset. However, if ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter")," ",(0,r.kt)("em",{parentName:"p"},"has")," a value, then the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"alternate")," is used instead."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Offset and Length")),(0,r.kt)("p",null,"You can tell the shell to expand only a subset of the value of a parameter by using the ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter:offset}")," expression. In this case, the shell will expand the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter"),", but skip ",(0,r.kt)("inlineCode",{parentName:"p"},"offset")," number of characters from the beginning:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'echo "My home folder name is: ${HOME:6}"\n\n# The line above is expanded to:\necho "My home folder name is: dwmkerr"\n')),(0,r.kt)("p",null,"You can also specify how many characters should be used by providing a ",(0,r.kt)("inlineCode",{parentName:"p"},"length")," value after the offset with the expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter:offset:length}"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'echo "The error message is: ${error_message:0:64}"\n')),(0,r.kt)("p",null,"In the expression above, only up to the first 64 characters of the parameter ",(0,r.kt)("inlineCode",{parentName:"p"},"error_message")," will be shown."),(0,r.kt)("p",null,"The offset and length values can also be used with arrays:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")\necho "${days[@]:2:3}"\n\n# The line above is expanded to:\necho "Tuesday Wednesday Thursday"\n')),(0,r.kt)("p",null,"It is important to note that when using this technique with arrays, you must specify the array name and then ",(0,r.kt)("inlineCode",{parentName:"p"},"[@]")," after the array name, to indicate that you want to work with all of the members of the array. If you ",(0,r.kt)("em",{parentName:"p"},"don't")," do this, the entire array is converted into a single string and the resulting string has the offset and length applied."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Expand Variable Names")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"${!name*}")," expression evaluates to the ",(0,r.kt)("em",{parentName:"p"},"name")," of every parameter that starts with the text ",(0,r.kt)("inlineCode",{parentName:"p"},"name"),". You can use this expression to find the full set of parameters that match a certain pattern."),(0,r.kt)("p",null,"How might this be useful? One nice trick is to use it to tidy up scripts. For example, if you are writing a script and create a set of variables for internal use, you could use this expression to find the names of all of the variables you have created and clean them up:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'_es_download_folder=~/downloads\n_es_backup_folder=~/backups\n_es_download_address=https://effective-shell.com/downloads/effective-shell-samples.tar.gz\n\n# At this point we might have a script that uses the variables above...\n\n# Now clean up any variables we created.\nfor var_name in ${!_es_*}\ndo\n echo "Cleaning up: ${var_name}..."\n unset ${var_name}\ndone\n')),(0,r.kt)("p",null,"This is rather an advanced technique but it does show how the 'expand variable names' expansion can be useful."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Array Expansion")),(0,r.kt)("p",null,"This topic is covered in detail in Chapter 19. The expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${!array[@}")," expands to the indices (or 'keys') for each item in an array:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")\necho "${!days[@]}"\n\n# The line above is expanded to:\necho "0 1 2 3 4 5 6"\n')),(0,r.kt)("p",null,"This expansion is convenient if you do not know the keys that make up an array and want to loop through them."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Parameter Length")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"${#parameter}")," expression expands to the length of the value in the parameter named ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter"),"."),(0,r.kt)("p",null,"You can also use this expression to find the length of an array - just add the ",(0,r.kt)("inlineCode",{parentName:"p"},"[@]")," subscript like so ",(0,r.kt)("inlineCode",{parentName:"p"},"${#array[@]}"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")\necho "There are ${#days[@]} days in the array"\n\n# The line above is expanded to:\necho "There are 7 days in the array"\n')),(0,r.kt)("p",null,"You may have noticed at pattern by this point - many of the expansions that can be performed on a parameter can ",(0,r.kt)("em",{parentName:"p"},"also")," be performed on an array, just by adding the ",(0,r.kt)("inlineCode",{parentName:"p"},"[@]")," subscript to the parameter name. Think of this subscript as saying 'all of the array members' - without it the shell combines all of the array members into a single string and performs the substitution on the result."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Remove Pattern from Front")),(0,r.kt)("p",null,"You can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter#pattern}")," expression to expand the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter"),", removing ",(0,r.kt)("inlineCode",{parentName:"p"},"pattern")," from the front of the value:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'address=https://effective-shell.com\necho "Address: ${address#https://}"\n\n# The line above is expanded to:\necho "Address: effective-shell.com"\n')),(0,r.kt)("p",null,"You can also tell the shell to remove as many sequential matches of ",(0,r.kt)("inlineCode",{parentName:"p"},"pattern")," as possible, by using the ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter##pattern}")," expression. This can be useful to strip out all of the characters up to a certain point in a parameter:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'folder=/home/dwmkerr/backups/2021-10-19\necho "Today\'s backup folder is: ${folder##*/}"\n\n# The line above is expanded to:\necho "Today\'s backup folder is: 2021-10-19"\n')),(0,r.kt)("p",null,"Notice that in this example we are using an asterisk ",(0,r.kt)("inlineCode",{parentName:"p"},"*")," symbol in the pattern, telling the shell to strip as many possible characters from the beginning of the parameter up until the final forward-slash ",(0,r.kt)("inlineCode",{parentName:"p"},"/")," is found."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Remove Pattern from Back")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter%pattern}")," expression works exactly like the expression above, but removes text from the ",(0,r.kt)("em",{parentName:"p"},"end")," of a parameter:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'echo "My working directory is: ${PWD}"\necho "My parent folder is: ${PWD%/*}"\n\n# The lines above are expanded to:\necho "My working directory is: /home/dwmkerr/repos/github/dwmkerr/effective-shell"\necho "My parent folder is: /home/dwmkerr/repos/github/dwmkerr"\n')),(0,r.kt)("p",null,"In this example we used an asterisk ",(0,r.kt)("inlineCode",{parentName:"p"},"*")," wildcard in the pattern to remove all of the text from the back of the parameter, up to and including the first forward-slash ",(0,r.kt)("inlineCode",{parentName:"p"},"/")," symbol found."),(0,r.kt)("p",null,"We can also remove as many matches as possible, by using the expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter%%pattern}"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'archive=effective-shell.tar.gz\necho "Name of archive is: ${archive%%.*}"\n\n# The line above is expanded to:\necho "Name of archive is: effective-shell"\n')),(0,r.kt)("p",null,"Notice that in this case the removal of the characters did not stop at the first period ",(0,r.kt)("inlineCode",{parentName:"p"},".")," symbol, it removed as many characters as possible until the ",(0,r.kt)("em",{parentName:"p"},"last")," period ",(0,r.kt)("inlineCode",{parentName:"p"},".")," symbol was found."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Pattern Replacement")),(0,r.kt)("p",null,"You can also replace a pattern in a parameter by using the expression ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter/pattern/string}"),". This can be used to perform substitutions:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'message="Hello Dave"\necho "${message/Hello/Goodbye}"\n\n# The line above is expanded to:\necho "Goodbye Dave"\n')),(0,r.kt)("p",null,"There are actually a number of options available for Pattern Replacement that can control things like the number of replacements that are performed and how arrays are treated. I would recommend not using overly complex replacements using these types of expressions though - instead use a command like ",(0,r.kt)("inlineCode",{parentName:"p"},"tr")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"sed")," to make it very explicit what is going on - the built-in shell parameter replacement can be quite complex for the reader to parse and can also vary from shell to shell."),(0,r.kt)("p",null,"For suggestions on alternative ways to manipulate text check ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/slice-and-dice-text/"},"Chapter 15 - Slice and Dice Text")," or ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/advanced-text-manipulation/"},"Chapter 16 - Advanced Text Manipulation with Sed"),"."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Lowercase or Uppercase")),(0,r.kt)("p",null,"You can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter^^}")," expression to return the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter")," converted to uppercase. You can also use the ",(0,r.kt)("inlineCode",{parentName:"p"},"${parameter,,}")," expression to return the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter")," converted to lowercase. An example is below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'message="Hello Reader"\necho ${message^^}\necho ${message,,}\n')),(0,r.kt)("p",null,"The output of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"HELLO READER\nhello reader\n")),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Parameter Indirection")),(0,r.kt)("p",null,"If you want to get the value of a parameter that has an arbitrary name you can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"${!parameter_name}")," expression. This will return the value of the parameter that has the name of the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"parameter_name")," - you can see this in action like so:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'parameter_name="HOME"\necho "${!parameter_name}"\n')),(0,r.kt)("p",null,"The output of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/home/dwmkerr\n")),(0,r.kt)("p",null,"This can be very useful if you are writing scripts that will work with arbitrary or variable parameter names."),(0,r.kt)("p",null,"You can see more examples of how parameter expansion works, and in particular how to use parameter expansion with the parameters to functions or scripts in ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/variables-reading-input-and-mathematics/"},"Chapter 19 - Variables, Reading Input, and Mathematics"),"."),(0,r.kt)("h3",{id:"command-substitution"},"Command Substitution"),(0,r.kt)("p",null,"The second form of expansion that starts with a dollar ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," symbol is ",(0,r.kt)("em",{parentName:"p"},"command substitution"),". This form of expansion instructs the shell to run a specific command. The syntax is simply ",(0,r.kt)("inlineCode",{parentName:"p"},"$(comand)"),"."),(0,r.kt)("p",null,"We have seen command substitution throughout the book - in the example below we expand the ",(0,r.kt)("inlineCode",{parentName:"p"},"date")," command to print the current date:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'echo "The date is: $(date)"\n\n# The line above is expanded to:\necho "The date is: Tue Oct 19 16:49:07 +08 2021"\n')),(0,r.kt)("p",null,"You may find that your scripts or commands are easier to manage if you store the results of a command in a variable like so:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'archives=$(find ~/downloads -type f -name "*.tar.gz")\n')),(0,r.kt)("p",null,"In this command we store the results of the ",(0,r.kt)("inlineCode",{parentName:"p"},"find")," operation in the parameter named ",(0,r.kt)("inlineCode",{parentName:"p"},"archives"),"."),(0,r.kt)("p",null,"There is an alternative syntax for command substitution that you might see. In this alternative syntax the command is surrounded by backtick symbol. The command above could be written like so:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'archives=`find ~/downloads -type f -name "*.tar.gz"`\n')),(0,r.kt)("p",null,"You may see this syntax from time to time, however I would suggest that you avoid it. The reason is that you cannot ",(0,r.kt)("em",{parentName:"p"},"nest")," commands using this syntax. If you want to run a command that itself performs command substitution it is not possible to do so with this backtick syntax. Instead, prefer the form that uses parentheses - such as ",(0,r.kt)("inlineCode",{parentName:"p"},"result=$(command1 $(command2))"),"."),(0,r.kt)("h3",{id:"arithmetic-expansion"},"Arithmetic Expansion"),(0,r.kt)("p",null,"The final form of shell expansion that starts with a dollar symbol ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," is ",(0,r.kt)("em",{parentName:"p"},"arithmetic expansion"),". This expansion can be used to perform simple arithmetic expressions:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'echo "The result of 23*4 is: $((23*4))"\necho "The result of 23*4 is: 92"\n')),(0,r.kt)("p",null,"Arithmetic expansion is covered in detail in ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/variables-reading-input-and-mathematics/"},"Chapter 19 - Variables, Reading Input, and Mathematics"),"."),(0,r.kt)("h3",{id:"word-splitting"},"Word Splitting"),(0,r.kt)("p",null,"Word splitting is a complex topic that can often cause confusion. Word splitting is the process that the shell goes through when it takes the results of parameter expansion, command substitution and arithmetic expansion and then attempts to split the result into 'words'. The easiest way to remember which expansions have word splitting applied are that it is applied to ",(0,r.kt)("em",{parentName:"p"},"any")," expansion that starts with a dollar symbol ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," and that does ",(0,r.kt)("em",{parentName:"p"},"not")," occur within double quotes."),(0,r.kt)("p",null,"The fact that word splitting only occurs if a substitution does ",(0,r.kt)("em",{parentName:"p"},"not")," use double quotes can also cause confusion. Let's take a look into word splitting in detail and see when it is useful and when it can be problematic."),(0,r.kt)("p",null,"To see word splitting in action, we'll run a command that returns a set of words. In the example note that there are different numbers of space characters between some of the days:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'days="Monday Tuesday Wednesday Thursday Friday Saturday Sunday"\nfor day in "$days"\ndo\n echo "${day}"\ndone\n')),(0,r.kt)("p",null,"The output of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Monday Tuesday Wednesday Thursday Friday Saturday Sunday\n")),(0,r.kt)("p",null,"In the expression ",(0,r.kt)("inlineCode",{parentName:"p"},'for day in "$day"')," we are using ",(0,r.kt)("em",{parentName:"p"},"shell parameter expansion")," to expand the ",(0,r.kt)("inlineCode",{parentName:"p"},"days")," parameter. We have surrounded ",(0,r.kt)("inlineCode",{parentName:"p"},"$day")," in quotes - this means that we are telling the shell ",(0,r.kt)("em",{parentName:"p"},"not")," to apply any word splitting. This means the shell preserves the spaces in the parameter. When we loop through the parameter we have one value only - the original set of days, including the spaces, that we set in the parameter."),(0,r.kt)("p",null,"Now let's run the same script but this time we will ",(0,r.kt)("em",{parentName:"p"},"not")," surround ",(0,r.kt)("inlineCode",{parentName:"p"},"$days")," in quotes, meaning that the shell ",(0,r.kt)("em",{parentName:"p"},"will")," perform word splitting:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'days="Monday Tuesday Wednesday Thursday Friday Saturday Sunday"\nfor day in $days\ndo\n echo "${day}"\ndone\n')),(0,r.kt)("p",null,"The output of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday\n")),(0,r.kt)("p",null,"In this case we can see that word splitting has occurred. The shell has performed the following operations:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"First, it searches through each character in the input"),(0,r.kt)("li",{parentName:"ol"},"Every time it finds a character in the ",(0,r.kt)("inlineCode",{parentName:"li"},"IFS")," (",(0,r.kt)("em",{parentName:"li"},"Input Field Separator"),") special variable, it splits the word"),(0,r.kt)("li",{parentName:"ol"},"If there are multiple instances of a separator character, they are removed, and replaced with a single instance only")),(0,r.kt)("p",null,"By default, the ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," variable is set to ",(0,r.kt)("inlineCode",{parentName:"p"},""),". This means that any spaces, tabs or newline characters in the input are considered as characters that the shell will use to split words. As you can see from the example above, when we have multiple instances of these characters sequentially (such as the five space characters after the ",(0,r.kt)("inlineCode",{parentName:"p"},"Wednesday")," value), they are replaced with a single instance of the first character (a space in this case) and then the splitting occurs."),(0,r.kt)("p",null,"The fact that the shell uses spaces, tabs and newlines as input field separators can sometimes cause confusion - in particular if you have a list of files:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'programs="/usr/bin/bash /usr/bin/zshell /usr/bin/new shell"\nfor program in $programs\ndo\n echo "${program}"\ndone\n')),(0,r.kt)("p",null,"The output of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/usr/bin/bash\n/usr/bin/zshell\n/usr/bin/new\nshell\n")),(0,r.kt)("p",null,"The final command, which has a space in the name, has been split into two words. You could avoid this issue by temporarily ",(0,r.kt)("em",{parentName:"p"},"changing")," the value of ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," to use a different separator for words:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'programs="/usr/bin/bash;/usr/bin/zshell;/usr/bin/new shell"\nOLDIFS=$IFS\nIFS=\';\'\nfor program in $programs\ndo\n echo "${program}"\ndone\nIFS=$OLDIFS\n')),(0,r.kt)("p",null,"The output of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/usr/bin/bash\n/usr/bin/zshell\n/usr/bin/new shell\n")),(0,r.kt)("p",null,"In this script we saved the original value of ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," into a parameter called ",(0,r.kt)("inlineCode",{parentName:"p"},"OLDIFS"),", changed ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," to use a semi-colon as a separator, ran the loop (which correctly split the programs and preserved the space in the last program name) then change ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," back to its original value."),(0,r.kt)("p",null,"You should be careful when changing ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," to make sure that you change it back to its original value straight afterwards - other programs or commands might expect ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," to be set to the default value so it should only be changed with caution."),(0,r.kt)("p",null,"If you were to look at the contents of the ",(0,r.kt)("inlineCode",{parentName:"p"},"PATH")," variable, which specifies the locations the shell should search for commands, you will see that they are actually separated by colons:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ echo $PATH\n/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games\n")),(0,r.kt)("p",null,"The results you see will vary depending on your operating system. But the fact that they are separated by colons means that you can easily change ",(0,r.kt)("inlineCode",{parentName:"p"},"IFS")," to a colon character to get each of the paths - even if they contain spaces:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'OLDIFS=$IFS\nIFS=":"\nfor path in $PATH\ndo\n echo "${path}"\ndone\nIFS=$OLDIFS\n')),(0,r.kt)("p",null,"The output of this script will look something like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/usr/local/sbin\n/usr/local/bin\n/usr/sbin\n/usr/bin\n/sbin\n/bin\n/usr/games\n/usr/local/games\n")),(0,r.kt)("p",null,"We will see a little more about how the shell can sometimes split up a filename with spaces (or even newlines) in the path when we look at the final shell expansion - pathname expansion."),(0,r.kt)("h3",{id:"pathname-expansion"},"Pathname Expansion"),(0,r.kt)("p",null,"When the shell encounters the asterisk ",(0,r.kt)("inlineCode",{parentName:"p"},"*"),", question mark ",(0,r.kt)("inlineCode",{parentName:"p"},"?")," or open square brackets ",(0,r.kt)("inlineCode",{parentName:"p"},"[")," characters, it marks the beginning of an expression that will have ",(0,r.kt)("em",{parentName:"p"},"pathname expansion")," applied to it. We have actually seen pathname expansion a number of times in this book - it is the expansion that occurs when we use wildcards or patterns in shell scripts to expand a list of paths:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ ls ~/downloads/*.tar.gz\n/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64 (1).tar.gz\n/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64.tar.gz\n/home/dwmkerr/downloads/dotnet-sdk-3.1.412-osx-x64.tar.gz\n/home/dwmkerr/downloads/effective-shell-playground.tar.gz\n/home/dwmkerr/downloads/effective-shell-samples (1).tar.gz\n/home/dwmkerr/downloads/effective-shell-samples (2).tar.gz\n/home/dwmkerr/downloads/effective-shell-samples.tar.gz\n")),(0,r.kt)("p",null,"This script shows all of the files in the ",(0,r.kt)("em",{parentName:"p"},"~/downloads")," folder that match the pattern ",(0,r.kt)("inlineCode",{parentName:"p"},"*.tar.gz"),". The results you see will depend on what you have in your own ",(0,r.kt)("em",{parentName:"p"},"~/downloads")," folder!"),(0,r.kt)("p",null,"It is important to remember that the shell performs all of the types of expansion that we have described ",(0,r.kt)("em",{parentName:"p"},"in order"),". This means that word expansion is performed ",(0,r.kt)("em",{parentName:"p"},"before")," pathname expansion. So if you loop through the results of an expanded path, word splitting will not be performed on those results. We can see that with the script below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'for $path in ~/downloads/*.tar.gz\ndo\n echo "${path}"\ndone\n')),(0,r.kt)("p",null,"The result of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64 (1).tar.gz\n/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64.tar.gz\n/home/dwmkerr/downloads/dotnet-sdk-3.1.412-osx-x64.tar.gz\n/home/dwmkerr/downloads/effective-shell-playground.tar.gz\n/home/dwmkerr/downloads/effective-shell-samples (1).tar.gz\n/home/dwmkerr/downloads/effective-shell-samples (2).tar.gz\n/home/dwmkerr/downloads/effective-shell-samples.tar.gz\n")),(0,r.kt)("p",null,"Note that the spaces in the path names have been preserved - pathname expansion happens ",(0,r.kt)("em",{parentName:"p"},"after")," word splitting - so the paths themselves are left as-is."),(0,r.kt)("p",null,"As well as the asterisk ",(0,r.kt)("inlineCode",{parentName:"p"},"*")," character, which can be used as a wildcard character in pathname expansion, there is also the question mark ",(0,r.kt)("inlineCode",{parentName:"p"},"?")," character which means 'any single character'. You can also use expressions such as ",(0,r.kt)("inlineCode",{parentName:"p"},"[abc]")," to match on a range of characters. The exact details of how these special characters are used can be found in ",(0,r.kt)("inlineCode",{parentName:"p"},"man bash"),"."),(0,r.kt)("p",null,"One feature of pathname expansion that people can sometimes be surprised by is what happens if the shell finds ",(0,r.kt)("em",{parentName:"p"},"no files")," that match the pattern. You can see this in action below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ echo ~/effective-shell/*.txt\n/home/dwmkerr/effective-shell/*.txt\n")),(0,r.kt)("p",null,"There are no files in the ",(0,r.kt)("em",{parentName:"p"},"~/effective-shell")," folder that match the pattern ",(0,r.kt)("inlineCode",{parentName:"p"},"*.txt")," and in this case the shell has left the text as-is. This means that you should always check the results of the expansion before assuming that the shell has found a file!"),(0,r.kt)("p",null,"For example, if I wanted to run the ",(0,r.kt)("inlineCode",{parentName:"p"},"touch")," command on a set of files, I would do the following:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'for file in ~/effective-shell/*.txt\n # If the file / folder doesn\'t exist, skip it.\n if ! [ -e "$file" ]; then continue; fi\n touch "$file"\ndo\n')),(0,r.kt)("p",null,"In this script we first check to see whether the file or folder exists by using the ",(0,r.kt)("inlineCode",{parentName:"p"},"-e")," test. If the file or folder doesn't exist then we skip through the loop. You can see more examples of this pattern in ",(0,r.kt)("a",{parentName:"p",href:"../../part-4-shell-scripting/loops-and-working-with-files-and-folders"},"Chapter 21 - Loops and working with Files and Folders"),"."),(0,r.kt)("p",null,"Pathname expansion has limitations - if you need a more sophisticated way to search for a set of files, check ",(0,r.kt)("a",{parentName:"p",href:"../../part-2-core-skills/finding-files"},"Chapter 11 - Finding Files"),"."),(0,r.kt)("h2",{id:"summary"},"Summary"),(0,r.kt)("p",null,"In this chapter we went into the lower level details of how ",(0,r.kt)("em",{parentName:"p"},"shell expansion")," works and looked at the seven types of expansion the shell will perform on the input it is provided. Whilst we have seen many of these expansions already throughout the book, I think it is useful to see all of them together in one place to really understand ",(0,r.kt)("em",{parentName:"p"},"what")," the shell does with the input you provide it in your commands."),(0,r.kt)("p",null,"Hopefully with this additional knowledge on shell expansion, you will be less likely to make mistakes around things like word splitting, or how empty results from filename expansion are treated, which often cause people confusion."),(0,r.kt)("p",null,"In the next chapter we will examine some of the limitations of shell scripting and alternatives to shell scripts that can be useful to become familiar with."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/61c307b9.f5924f96.js b/pr-preview/pr-346/assets/js/61c307b9.f5924f96.js new file mode 100644 index 00000000..76d9c9c7 --- /dev/null +++ b/pr-preview/pr-346/assets/js/61c307b9.f5924f96.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[8158],{3905:(e,t,l)=>{l.d(t,{Zo:()=>c,kt:()=>d});var n=l(7294);function a(e,t,l){return t in e?Object.defineProperty(e,t,{value:l,enumerable:!0,configurable:!0,writable:!0}):e[t]=l,e}function r(e,t){var l=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),l.push.apply(l,n)}return l}function o(e){for(var t=1;t =0||(a[l]=e[l]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,l)&&(a[l]=e[l])}return a}var s=n.createContext({}),p=function(e){var t=n.useContext(s),l=t;return e&&(l="function"==typeof e?e(t):o(o({},t),e)),l},c=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},m="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var l=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),m=p(l),u=a,d=m["".concat(s,".").concat(u)]||m[u]||f[u]||r;return l?n.createElement(d,o(o({ref:t},c),{},{components:l})):n.createElement(d,o({ref:t},c))}));function d(e,t){var l=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=l.length,o=new Array(r);o[0]=u;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[m]="string"==typeof e?e:a,o[1]=i;for(var p=2;p {l.r(t),l.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>i,toc:()=>p});var n=l(7462),a=(l(7294),l(3905));const r={title:"Installing the Samples",description:"How to install the Effective Shell Samples"},o=void 0,i={unversionedId:"xx-appendices/installing-samples/index",id:"xx-appendices/installing-samples/index",title:"Installing the Samples",description:"How to install the Effective Shell Samples",source:"@site/docs/xx-appendices/01-installing-samples/index.mdx",sourceDirName:"xx-appendices/01-installing-samples",slug:"/xx-appendices/installing-samples/",permalink:"/xx-appendices/installing-samples/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/xx-appendices/01-installing-samples/index.mdx",tags:[],version:"current",frontMatter:{title:"Installing the Samples",description:"How to install the Effective Shell Samples"},sidebar:"sidebar",previous:{title:"Master the Multiplexer",permalink:"/part-6-advanced-techniques/master-the-multiplexer/"},next:{title:"Recommended Reading",permalink:"/xx-appendices/recommended-reading/"}},s={},p=[{value:"Manually Downloading the Samples",id:"manually-downloading-the-samples",level:2},{value:"Deleting the Samples",id:"deleting-the-samples",level:2}],c={toc:p};function m(e){let{components:t,...l}=e;return(0,a.kt)("wrapper",(0,n.Z)({},c,l,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"There are many samples in the Effective Shell book. To allow you to play with a set of folders and files without affecting your own personal documents, a samples folder can be installed. Each of the examples in the book uses the samples from this folder."),(0,a.kt)("p",null,"The easiest way to install the samples is to run the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"curl effective.sh | sh\n")),(0,a.kt)("p",null,"This will install the samples to the following location:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"~/effective-shell\n")),(0,a.kt)("p",null,"It will also allow you to overwrite the samples, update or recreate them if you run the command multiple times."),(0,a.kt)("h2",{id:"manually-downloading-the-samples"},"Manually Downloading the Samples"),(0,a.kt)("p",null,"If you would prefer to manually download and unzip the samples, perhaps so that you can install them to a different location, you can download them as a zip file from:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://effective-shell.com/downloads/effective-shell.zip"},"https://effective-shell.com/downloads/effective-shell.zip"))),(0,a.kt)("p",null,"You can also download them as a tarball from:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://effective-shell.com/downloads/effective-shell.tar.gz"},"https://effective-shell.com/downloads/effective-shell.tar.gz"))),(0,a.kt)("h2",{id:"deleting-the-samples"},"Deleting the Samples"),(0,a.kt)("p",null,"If you have installed the samples to the default location, you can safely delete them with the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"rm -rf ~/effective-shell\n")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/67f00fa8.89201d49.js b/pr-preview/pr-346/assets/js/67f00fa8.89201d49.js new file mode 100644 index 00000000..653dd6f7 --- /dev/null +++ b/pr-preview/pr-346/assets/js/67f00fa8.89201d49.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[9696],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t =0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),h=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=h(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=h(n),u=o,d=c["".concat(l,".").concat(u)]||c[u]||m[u]||r;return n?a.createElement(d,i(i({ref:t},p),{},{components:n})):a.createElement(d,i({ref:t},p))}));function d(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[c]="string"==typeof e?e:o,i[1]=s;for(var h=2;h {n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>c,frontMatter:()=>r,metadata:()=>s,toc:()=>h});var a=n(7462),o=(n(7294),n(3905));const r={title:"The Renaissance of the Shell",slug:"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/"},i=void 0,s={unversionedId:"transitioning-to-the-shell/the-renaissance-of-the-shell/index",id:"transitioning-to-the-shell/the-renaissance-of-the-shell/index",title:"The Renaissance of the Shell",description:'This is the first of the "interludes" which end each section of the book. They don\'t teach any specific skills but instead give a little flavour and background about the world of the shell, Linux and modern computing.',source:"@site/docs/01-transitioning-to-the-shell/06-the-renaissance-of-the-shell/index.md",sourceDirName:"01-transitioning-to-the-shell/06-the-renaissance-of-the-shell",slug:"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/",permalink:"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/01-transitioning-to-the-shell/06-the-renaissance-of-the-shell/index.md",tags:[],version:"current",frontMatter:{title:"The Renaissance of the Shell",slug:"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/"},sidebar:"sidebar",previous:{title:"Getting Help",permalink:"/part-1-transitioning-to-the-shell/getting-help/"},next:{title:"Part 2 - Core Skills",permalink:"/part-2-core-skill/"}},l={},h=[{value:"Is there a Renaissance of the Shell?",id:"is-there-a-renaissance-of-the-shell",level:2},{value:"The Changing Technology Landscape",id:"the-changing-technology-landscape",level:2},{value:"The Diversity of Programming Languages",id:"the-diversity-of-programming-languages",level:3},{value:"Convergence of Operating Platforms",id:"convergence-of-operating-platforms",level:3},{value:"DevOps",id:"devops",level:3}],p={toc:h};function c(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,'This is the first of the "interludes" which end each section of the book. They don\'t teach any specific skills but instead give a little flavour and background about the world of the shell, Linux and modern computing.'),(0,o.kt)("p",null,"In this first interlude we'll look at just why the shell is experiencing something of a renaissance in the modern age of IT."),(0,o.kt)("h2",{id:"is-there-a-renaissance-of-the-shell"},"Is there a Renaissance of the Shell?"),(0,o.kt)("p",null,"To be honest, it is hard to know whether there is an increase in popularity of the use of the shell and command-line tooling in general. There are data sources which indicate there is more widespread usage amongst the technical community - Stack Overflow tag popularity is one. LinkedIn data on desired skillsets is another. However, disassociating whether there is a general increase in the need for diverse technical skillsets and whether there is a ",(0,o.kt)("em",{parentName:"p"},"specific")," increase in the popularity of keyboard and script operated systems is a challenge."),(0,o.kt)("p",null,"For the purposes of this chapter, we'll instead examine changes in the technology landscape over the last few decades and consider what those changes might mean for the shell, the command-line and similar tools."),(0,o.kt)("p",null,"We'll look at three specific developments in technology:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Diversity of programming languages"),(0,o.kt)("li",{parentName:"ul"},"Convergence of operating platforms"),(0,o.kt)("li",{parentName:"ul"},"DevOps")),(0,o.kt)("p",null,"Each of these developments has a potentially profound impact on how we work with computers, and might hint at the long term need for shell skills."),(0,o.kt)("h2",{id:"the-changing-technology-landscape"},"The Changing Technology Landscape"),(0,o.kt)("p",null,"So let's look at some of the key changes in the technology landscape over recent years and consider how they might affect the popularity and importance of the shell."),(0,o.kt)("h3",{id:"the-diversity-of-programming-languages"},"The Diversity of Programming Languages"),(0,o.kt)("p",null,"There have been many programming languages and platforms over the years. But in recent years it is possible that the diversity has increased at a greater rate than ever before."),(0,o.kt)("p",null,"With the advent of the internet and the increase in the size of the online technical community, programming has in a sense become more democratised (which we will discuss a little more in the 'citizen coder' section). When in the past it was necessary to find physical books or teachers and tutors to learn a programming language, students can now find a wealth of resources online."),(0,o.kt)("p",null,"It is perhaps this democratisation which has led to a startlingly diverse world of programming languages. For many years, there were a small number of 'general purpose' languages, and a larger number of highly specialised languages (and associated platforms)."),(0,o.kt)("p",null,'"C", and later, "C++" were the go-to languages for systems programming (sometimes backed up by assembly language). This was the language which kernels and compilers were written in.'),(0,o.kt)("p",null,"Alongside these giants were the workhorses for specific use cases. Erlang was (and is) a language which is highly popular in the telecommunications industry, where high availability and reliability were paramount. COBOL was the language for the financial industry, where mission critical systems ran on mainframes (and many still do)."),(0,o.kt)("p",null,"Of course there were many other languages, but many of these other languages were highly specific, in a sense C, Java, PHP and later C# dominated the landscape."),(0,o.kt)("p",null,"Transition to the time of writing. In the Stack Overflow 2020 Technology Survey",(0,o.kt)("sup",{parentName:"p",id:"fnref-1"},(0,o.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),", the top ten languages most wanted by employers are:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Python"),(0,o.kt)("li",{parentName:"ul"},"JavaScript"),(0,o.kt)("li",{parentName:"ul"},"Go"),(0,o.kt)("li",{parentName:"ul"},"TypeScript"),(0,o.kt)("li",{parentName:"ul"},"Rust"),(0,o.kt)("li",{parentName:"ul"},"Kotlin"),(0,o.kt)("li",{parentName:"ul"},"Java"),(0,o.kt)("li",{parentName:"ul"},"C++"),(0,o.kt)("li",{parentName:"ul"},"SQL"),(0,o.kt)("li",{parentName:"ul"},"C#")),(0,o.kt)("p",null,"Some of our old friends are there, but there are many new languages, languages which are evolving quickly. Later on in the list we will see Swift, Dart, Ruby, Haskell, Scala. There are many programming languages which are extremely popular today."),(0,o.kt)("p",null,"Why does this matter for the shell? The answer is that for ",(0,o.kt)("em",{parentName:"p"},"many")," new languages, developer tooling is not as mature (some might say bloated) as it is for the 'Workhorse' languages. Java developers are likely very familiar with the Eclipse IDE, Microsoft shops will be familiar with Visual Studio. These are products which have been evolving for years (or decades) to support developers with rich integrated development environments."),(0,o.kt)("p",null,"For server-side JavaScript, Golang, Rust, Python and other languages, the development environment really is the shell. Modern editors like Visual Studio Code, Atom and so on provide a vast amount of support and tooling, encompassing the features of a full fledged IDE if the user wants. But for modern programming languages, users often have ",(0,o.kt)("em",{parentName:"p"},"had")," to rely on the shell to compile, transpile, manage packages, bundle and so on. The average developer today is perhaps much more likely to have to use the shell - to manage Python virtual environments one day, to run Node.js another, to install packages for Golang another."),(0,o.kt)("p",null,"In time tooling will likely catch up and provide a 'friendly' interface on top of these operations, but many engineers have realised (or always known) that direct access to simple command line tools can be ",(0,o.kt)("em",{parentName:"p"},"extremely efficient")," when working, and that overly featured IDEs can get in the way and hide complexity."),(0,o.kt)("p",null,"The modern programming is often polyglot - having to be at least familiar in a number of languages. The shell provides a common environment and interface for tooling, which is accessible by all, without installing many complex components, for both development and runtime environments."),(0,o.kt)("h3",{id:"convergence-of-operating-platforms"},"Convergence of Operating Platforms"),(0,o.kt)("p",null,"Whilst the variety in programming languages and developer tooling may have increased, in many ways the ",(0,o.kt)("em",{parentName:"p"},"operating platforms")," engineers use have become more homogeneous."),(0,o.kt)("p",null,"In the early days of computing, each operating environment was highly diverse. There were many systems which were used for production and many of them were highly proprietary. Even popular application servers were often closed source and highly specialised."),(0,o.kt)("p",null,"The modern execution environment however is often fairly uniform. A Linux-like system, with few customisations, which the developer or operator can tweak to suit their needs."),(0,o.kt)("p",null,"More and more enterprise users have moved away from proprietary Unix platforms to Linux platforms (whether commercial or non-commercial). The earliest cloud environments were using open-source Linux distributions as the available operating systems."),(0,o.kt)("p",null,"Even Windows has increasing support for Linux-like operation, in the form of the Windows Subsystem for Linux."),(0,o.kt)("p",null,"Perhaps the greatest movement in this area has been the rapid adoption of Docker as a common container technology. Containers, or container-like systems have been around for a long time, but Docker brought containers to the masses. With Docker, engineers expect operating environments to be even more uniform and Linux-like."),(0,o.kt)("p",null,"This has made knowledge of the shell extremely valuable. For any containerised workloads, Linux and shell skills are crucial. Kubernetes (as an execution environment) has standardised things even more."),(0,o.kt)("p",null,"Whilst there are still many workloads which run on proprietary systems, modern solutions are often built to run in containers on Linux. The shell has historically been the most common way to manage Linux systems, and the standardisation of operating environments around Linux, or Linux-like systems has made shell skills even more critical."),(0,o.kt)("h3",{id:"devops"},"DevOps"),(0,o.kt)("p",null,"Love it or hate it, DevOps has exploded in popularity. DevOps engineers, site-reliability engineers, these kinds of roles may have been unheard of in companies not that long ago and are now becoming ubiquitous."),(0,o.kt)("p",null,"In attempting to unify the goals of development and operation of software, DevOps represents an organisational and cultural change. Rather than having one group focus on feature development and another group focus on reliable software operations, a single group is responsible for both. The theory is that this encourages software engineers to also consider security, reliability, maintainability etc, and operators to also consider speed of delivery."),(0,o.kt)("p",null,"Regardless of whether teams are genuinely combined, or specialised roles are added to teams, or even if teams are still separated, the lines between development and operations blur somewhat. Software developers are expected to build and plan with knowledge of the execution environment, operators are expected to work with developers to build features which support reliability."),(0,o.kt)("p",null,"The intersection of these two roles often is in the realm of automation. Automated deployments after testing, automated failover in case of errors, automated alerting when potential issues are discovered, automated provisioning of environments, automated scaling of systems when load increases."),(0,o.kt)("p",null,"The world of automation is intimately linked to the world of the shell and in particular shell scripting. Many tasks which require automation can be easily achieved using shell scripts. Many aspects of modern environments (such as cloud environments) support provisioning and management of services via scripting. In fact, services which ",(0,o.kt)("em",{parentName:"p"},"cannot")," be managed via shell scripts or simple interfaces are increasingly becoming obsolete. If it cannot be scripted, it cannot be automated, and the increasingly complex systems we build ",(0,o.kt)("em",{parentName:"p"},"require")," automation."),(0,o.kt)("p",null,"In practice, this means software engineers are far more likely to have to build shell scripts (or at least understand how to interface with systems via the shell) than they perhaps might have been. Similarly, operators are far more likely to have to ",(0,o.kt)("em",{parentName:"p"},"program")," automated routines to manage high availability and so on. Again, the shell and shell scripts are a common way to manage this (even if they are simply entrypoints to more complex systems, such as scripts which execute programs)."),(0,o.kt)("p",null,"The rise in popularity of DevOps as a set of practices and beliefs has perhaps made the shell more popular, and more important, than any other recent developments in software engineering."),(0,o.kt)("p",null,"And for these reasons and many more, learning how to use the shell effectively has never been more relevant or practical."),(0,o.kt)("div",{className:"footnotes"},(0,o.kt)("hr",{parentName:"div"}),(0,o.kt)("ol",{parentName:"div"},(0,o.kt)("li",{parentName:"ol",id:"fn-1"},(0,o.kt)("a",{parentName:"li",href:"https://insights.stackoverflow.com/survey/2020"},"https://insights.stackoverflow.com/survey/2020"),(0,o.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/6ce668ac.8166a774.js b/pr-preview/pr-346/assets/js/6ce668ac.8166a774.js new file mode 100644 index 00000000..518b33f9 --- /dev/null +++ b/pr-preview/pr-346/assets/js/6ce668ac.8166a774.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[9132],{2776:e=>{e.exports=JSON.parse('{"name":"@easyops-cn/docusaurus-search-local","id":"default"}')}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/6d205482.8c07631d.js b/pr-preview/pr-346/assets/js/6d205482.8c07631d.js new file mode 100644 index 00000000..39bb458f --- /dev/null +++ b/pr-preview/pr-346/assets/js/6d205482.8c07631d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[4550],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>k});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t =0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},m=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,p=e.parentName,m=i(e,["components","mdxType","originalType","parentName"]),d=s(n),u=r,k=d["".concat(p,".").concat(u)]||d[u]||c[u]||o;return n?a.createElement(k,l(l({ref:t},m),{},{components:n})):a.createElement(k,l({ref:t},m))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,l=new Array(o);l[0]=u;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i[d]="string"==typeof e?e:r,l[1]=i;for(var s=2;s {n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>s});var a=n(7462),r=(n(7294),n(3905));const o={title:"Components"},l=void 0,i={unversionedId:"zz-developer-guide/components",id:"zz-developer-guide/components",title:"Components",description:"This page shows the custom components that are available for Effective Shell. These are primarily used to improve the reading experience for code samples.",source:"@site/docs/zz-developer-guide/components.mdx",sourceDirName:"zz-developer-guide",slug:"/zz-developer-guide/components",permalink:"/zz-developer-guide/components",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/zz-developer-guide/components.mdx",tags:[],version:"current",frontMatter:{title:"Components"},sidebar:"sidebar",previous:{title:"Thanks",permalink:"/appendices/thanks/"},next:{title:"Images and Diagrams",permalink:"/zz-developer-guide/images-and-diagrams"}},p={},s=[{value:"AsciinemaPlayer",id:"asciinemaplayer",level:2},{value:"AnnotatedCommand",id:"annotatedcommand",level:2},{value:"Caret",id:"caret",level:2},{value:"Tips for Developing Components",id:"tips-for-developing-components",level:2}],m={toc:s};function d(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"This page shows the custom components that are available for Effective Shell. These are primarily used to improve the reading experience for code samples."),(0,r.kt)("p",null,"To add more components to this page, please check the ",(0,r.kt)("inlineCode",{parentName:"p"},"src/theme/ReactLiveScope/index.js")," file, which has been swizzled as per the guide at ",(0,r.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/markdown-features/code-blocks#interactive-code-editor"},"Docusaurus - Code Blocks - Interactive code editor"),". Each custom component you want to use must be included in this file."),(0,r.kt)("h2",{id:"asciinemaplayer"},"AsciinemaPlayer"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"AsciinemaPlayer")," component is renders an ",(0,r.kt)("a",{parentName:"p",href:"https://asciinema.org/"},(0,r.kt)("inlineCode",{parentName:"a"},"asciinema"))," recording. Note that the recording file must be available to be served to the browser, so it will normally live somewhere in the ",(0,r.kt)("inlineCode",{parentName:"p"},"static")," folder."),(0,r.kt)("p",null,"First, import the component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"},"import AsciinemaPlayer from '@site/src/components/AsciinemaPlayer/AsciinemaPlayer.tsc';\n")),(0,r.kt)("p",null,"Use the component as below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx",metastring:"live",live:!0}," \n")),(0,r.kt)("p",null,"The bulk of the properties that are exposed are directly from the ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/asciinema/asciinema-player"},(0,r.kt)("inlineCode",{parentName:"a"},"asciinema-player"))," component - this page has a more detailed description of many of the properties listed below. The following properties are exposed:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Property"),(0,r.kt)("th",{parentName:"tr",align:null},"Usage"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"src")),(0,r.kt)("td",{parentName:"tr",align:null},"The location of the cast file, must be available from the browser.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"style")),(0,r.kt)("td",{parentName:"tr",align:null},"Any additional CSS styles to apply.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"cols")),(0,r.kt)("td",{parentName:"tr",align:null},"The number of columns in the player's terminal.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"rows")),(0,r.kt)("td",{parentName:"tr",align:null},"The number of rows in the player's terminal.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"autoPlay")),(0,r.kt)("td",{parentName:"tr",align:null},"Set this option to ",(0,r.kt)("inlineCode",{parentName:"td"},"true")," if playback should start automatically.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"preload")),(0,r.kt)("td",{parentName:"tr",align:null},"Set this option to ",(0,r.kt)("inlineCode",{parentName:"td"},"true")," if the recording should be preloaded on player's initialization.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"loop")),(0,r.kt)("td",{parentName:"tr",align:null},"Set this option to either true or a number if playback should be looped. When set to a number (e.g. 3) then the recording will be re-played given number of times and stopped after that.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"startAt")),(0,r.kt)("td",{parentName:"tr",align:null},"Start playback at a given time.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"speed")),(0,r.kt)("td",{parentName:"tr",align:null},"Playback speed. The value of 2 means 2x faster.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"idleTimeLimit")),(0,r.kt)("td",{parentName:"tr",align:null},"Limit terminal inactivity to a given number of seconds.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"theme")),(0,r.kt)("td",{parentName:"tr",align:null},"Terminal color theme.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"poster")),(0,r.kt)("td",{parentName:"tr",align:null},"Poster (a preview frame) to display until the playback is started.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"fit")),(0,r.kt)("td",{parentName:"tr",align:null},"Controls the player's fitting (sizing) behaviour inside its container element.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"fontSize")),(0,r.kt)("td",{parentName:"tr",align:null},"Size of the terminal font.")))),(0,r.kt)("h2",{id:"annotatedcommand"},"AnnotatedCommand"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"AnnotatedCommand")," component is used to create a set of keystrokes, for example for Vim, with a small text annotation beneath. This is useful in Markdown tables showing how Vim commands work, as multiline text in tables is a bit fiddly to work with."),(0,r.kt)("p",null,"First, import the component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"},"import AnnotatedCommand from '@site/src/components/AnnotatedCommand/AnnotatedCommand.tsc';\n")),(0,r.kt)("p",null,"Use the component as below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx",metastring:"live",live:!0},' gg2cw \n')),(0,r.kt)("p",null,"The following properties are exposed:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Property"),(0,r.kt)("th",{parentName:"tr",align:null},"Usage"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"annotation")),(0,r.kt)("td",{parentName:"tr",align:null},"The text to show beneath the command.")))),(0,r.kt)("h2",{id:"caret"},"Caret"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"Caret")," component is useful when showing Vim or Terminal samples where you need to indicate the position of the caret. It can show a block caret, which is the standard for an ASCII terminal, or a line caret, which can be used in things like iTerm to indicate Insert Mode for Vim."),(0,r.kt)("p",null,"First, import the component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx"},"import Caret from '@site/src/components/Caret/Caret.tsx';\n")),(0,r.kt)("p",null,"Use the component as below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-jsx",metastring:"live",live:!0},"\n def search_for_word
\n")),(0,r.kt)("p",null,"The following properties are exposed:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Property"),(0,r.kt)("th",{parentName:"tr",align:null},"Usage"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"caretStyle")),(0,r.kt)("td",{parentName:"tr",align:null},"The style of the cursor, ",(0,r.kt)("inlineCode",{parentName:"td"},"block")," by default, or ",(0,r.kt)("inlineCode",{parentName:"td"},"line")," for an 'insert mode' cursor.")))),(0,r.kt)("h2",{id:"tips-for-developing-components"},"Tips for Developing Components"),(0,r.kt)("p",null,"Check the following resources for useful tips on Component Development:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docusaurus.io/docs/styling-layout"},"Docusaurus - Styling and Layout")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docusaurus.io/docs/markdown-features/code-blocks#interactive-code-editor"},"Docusaurus - Code Blocks - Interactive code editor"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/742472af.42e1bbb9.js b/pr-preview/pr-346/assets/js/742472af.42e1bbb9.js new file mode 100644 index 00000000..6ca18763 --- /dev/null +++ b/pr-preview/pr-346/assets/js/742472af.42e1bbb9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[2253],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>h});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t( word):\n=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=l(r),m=a,h=d["".concat(c,".").concat(m)]||d[m]||u[m]||i;return r?n.createElement(h,o(o({ref:t},p),{},{components:r})):n.createElement(h,o({ref:t},p))}));function h(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,o=new Array(i);o[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[d]="string"==typeof e?e:a,o[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var n=r(7462),a=(r(7294),r(3905));const i={title:"Recording Terminal Sessions"},o=void 0,s={unversionedId:"zz-developer-guide/recording-terminal-sessions",id:"zz-developer-guide/recording-terminal-sessions",title:"Recording Terminal Sessions",description:"There are a couple of techniques that can be useful to record terminal sessions. The first is the asciinema too. The second is the script and scriptreplay commands, which can be used to record the actual keystrokes typed and then replay them.",source:"@site/docs/zz-developer-guide/recording-terminal-sessions.mdx",sourceDirName:"zz-developer-guide",slug:"/zz-developer-guide/recording-terminal-sessions",permalink:"/zz-developer-guide/recording-terminal-sessions",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/zz-developer-guide/recording-terminal-sessions.mdx",tags:[],version:"current",frontMatter:{title:"Recording Terminal Sessions"},sidebar:"sidebar",previous:{title:"Markdown",permalink:"/zz-developer-guide/markdown-guide"}},c={},l=[{value:"Asciinema",id:"asciinema",level:2},{value:"Script Recording",id:"script-recording",level:2}],p={toc:l};function d(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"There are a couple of techniques that can be useful to record terminal sessions. The first is the ",(0,a.kt)("a",{parentName:"p",href:"https://asciinema.org"},(0,a.kt)("inlineCode",{parentName:"a"},"asciinema"))," too. The second is the ",(0,a.kt)("inlineCode",{parentName:"p"},"script")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"scriptreplay")," commands, which can be used to record the actual keystrokes typed and then replay them."),(0,a.kt)("h2",{id:"asciinema"},"Asciinema"),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"https://asciinema.org"},(0,a.kt)("inlineCode",{parentName:"a"},"asciinema"))," tool can record the output of terminal sessions. You can see the results in action in pages like ",(0,a.kt)("a",{parentName:"p",href:"/part-6-advanced-techniques/master-the-multiplexer/"},"Chapter 33 - Master the Multiplexer"),"."),(0,a.kt)("p",null,"Some tips for working with ",(0,a.kt)("inlineCode",{parentName:"p"},"asciinema"),":"),(0,a.kt)("p",null,"To record a Tmux session, you will need to start ",(0,a.kt)("em",{parentName:"p"},"detached")," from Tmux and then attach. You can do this by hand, simply using ",(0,a.kt)("inlineCode",{parentName:"p"},"tmux attach"),", but this adds some noise to the beginning of the recording. A better way is to use the command below:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'asciinema rec --command "tmux attach [-t session-name]"\n')),(0,a.kt)("h2",{id:"script-recording"},"Script Recording"),(0,a.kt)("p",null,"Record a shell session by running:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"# Start recording...\nscript recording.txt\n\n# ...run your commands...\n\n# Finish the recording.\nexit\n")),(0,a.kt)("p",null,"Once you have this recording, you can use it to rapidly record an ",(0,a.kt)("inlineCode",{parentName:"p"},"asciinema")," file:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'asciinema rec --command "tmux attach [-t session-name] && scriptreplay recording.txt"\n')),(0,a.kt)("p",null,"It can be helpful to ",(0,a.kt)("em",{parentName:"p"},"not")," record a timing file for the keystrokes. If your typing is slow or irregular, or you have to look something up halfway through a script, then having a consistent typing speed provided via a script is better. One way to do this is with the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/scoopex/scriptreplay_ng"},(0,a.kt)("inlineCode",{parentName:"a"},"scriptreplay_ng"))," tool."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/8443.9fbe43e3.js b/pr-preview/pr-346/assets/js/8443.9fbe43e3.js new file mode 100644 index 00000000..ea00005a --- /dev/null +++ b/pr-preview/pr-346/assets/js/8443.9fbe43e3.js @@ -0,0 +1,2 @@ +/*! For license information please see 8443.9fbe43e3.js.LICENSE.txt */ +(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[8443],{8443:(t,e,n)=>{"use strict";t.exports=n(295)},1228:(t,e,n)=>{"use strict";var i=n(2856),s={wrapper:{position:"relative",display:"inline-block"},hint:{position:"absolute",top:"0",left:"0",borderColor:"transparent",boxShadow:"none",opacity:"1"},input:{position:"relative",verticalAlign:"top",backgroundColor:"transparent"},inputWithNoHint:{position:"relative",verticalAlign:"top"},dropdown:{position:"absolute",top:"100%",left:"0",zIndex:"100",display:"none"},suggestions:{display:"block"},suggestion:{whiteSpace:"nowrap",cursor:"pointer"},suggestionChild:{whiteSpace:"normal"},ltr:{left:"0",right:"auto"},rtl:{left:"auto",right:"0"},defaultClasses:{root:"algolia-autocomplete",prefix:"aa",noPrefix:!1,dropdownMenu:"dropdown-menu",input:"input",hint:"hint",suggestions:"suggestions",suggestion:"suggestion",cursor:"cursor",dataset:"dataset",empty:"empty"},appendTo:{wrapper:{position:"absolute",zIndex:"100",display:"none"},input:{},inputWithNoHint:{},dropdown:{display:"block"}}};i.isMsie()&&i.mixin(s.input,{backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"}),i.isMsie()&&i.isMsie()<=7&&i.mixin(s.input,{marginTop:"-1px"}),t.exports=s},9050:(t,e,n)=>{"use strict";var i="aaDataset",s="aaValue",r="aaDatum",o=n(2856),a=n(4910),u=n(3561),c=n(1228),l=n(3109);function h(t){var e;(t=t||{}).templates=t.templates||{},t.source||o.error("missing source"),t.name&&(e=t.name,!/^[_a-zA-Z0-9-]+$/.test(e))&&o.error("invalid dataset name: "+t.name),this.query=null,this._isEmpty=!0,this.highlight=!!t.highlight,this.name=void 0===t.name||null===t.name?o.getUniqueId():t.name,this.source=t.source,this.displayFn=function(t){return t=t||"value",o.isFunction(t)?t:e;function e(e){return e[t]}}(t.display||t.displayKey),this.debounce=t.debounce,this.cache=!1!==t.cache,this.templates=function(t,e){return{empty:t.empty&&o.templatify(t.empty),header:t.header&&o.templatify(t.header),footer:t.footer&&o.templatify(t.footer),suggestion:t.suggestion||n};function n(t){return" "+e(t)+"
"}}(t.templates,this.displayFn),this.css=o.mixin({},c,t.appendTo?c.appendTo:{}),this.cssClasses=t.cssClasses=o.mixin({},c.defaultClasses,t.cssClasses||{}),this.cssClasses.prefix=t.cssClasses.formattedPrefix||o.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix);var n=o.className(this.cssClasses.prefix,this.cssClasses.dataset);this.$el=t.$menu&&t.$menu.find(n+"-"+this.name).length>0?a.element(t.$menu.find(n+"-"+this.name)[0]):a.element(u.dataset.replace("%CLASS%",this.name).replace("%PREFIX%",this.cssClasses.prefix).replace("%DATASET%",this.cssClasses.dataset)),this.$menu=t.$menu,this.clearCachedSuggestions()}h.extractDatasetName=function(t){return a.element(t).data(i)},h.extractValue=function(t){return a.element(t).data(s)},h.extractDatum=function(t){var e=a.element(t).data(r);return"string"==typeof e&&(e=JSON.parse(e)),e},o.mixin(h.prototype,l,{_render:function(t,e){if(this.$el){var n,c=this,l=[].slice.call(arguments,2);if(this.$el.empty(),n=e&&e.length,this._isEmpty=!n,!n&&this.templates.empty)this.$el.html(h.apply(this,l)).prepend(c.templates.header?f.apply(this,l):null).append(c.templates.footer?d.apply(this,l):null);else if(n)this.$el.html(p.apply(this,l)).prepend(c.templates.header?f.apply(this,l):null).append(c.templates.footer?d.apply(this,l):null);else if(e&&!Array.isArray(e))throw new TypeError("suggestions must be an array");this.$menu&&this.$menu.addClass(this.cssClasses.prefix+(n?"with":"without")+"-"+this.name).removeClass(this.cssClasses.prefix+(n?"without":"with")+"-"+this.name),this.trigger("rendered",t)}function h(){var e=[].slice.call(arguments,0);return e=[{query:t,isEmpty:!0}].concat(e),c.templates.empty.apply(this,e)}function p(){var t,n,l=[].slice.call(arguments,0),h=this,p=u.suggestions.replace("%PREFIX%",this.cssClasses.prefix).replace("%SUGGESTIONS%",this.cssClasses.suggestions);return t=a.element(p).css(this.css.suggestions),n=o.map(e,f),t.append.apply(t,n),t;function f(t){var e,n=u.suggestion.replace("%PREFIX%",h.cssClasses.prefix).replace("%SUGGESTION%",h.cssClasses.suggestion);return(e=a.element(n).attr({role:"option",id:["option",Math.floor(1e8*Math.random())].join("-")}).append(c.templates.suggestion.apply(this,[t].concat(l)))).data(i,c.name),e.data(s,c.displayFn(t)||void 0),e.data(r,JSON.stringify(t)),e.children().each((function(){a.element(this).css(h.css.suggestionChild)})),e}}function f(){var e=[].slice.call(arguments,0);return e=[{query:t,isEmpty:!n}].concat(e),c.templates.header.apply(this,e)}function d(){var e=[].slice.call(arguments,0);return e=[{query:t,isEmpty:!n}].concat(e),c.templates.footer.apply(this,e)}},getRoot:function(){return this.$el},update:function(t){function e(e){if(!this.canceled&&t===this.query){var n=[].slice.call(arguments,1);this.cacheSuggestions(t,e,n),this._render.apply(this,[t,e].concat(n))}}if(this.query=t,this.canceled=!1,this.shouldFetchFromCache(t))e.apply(this,[this.cachedSuggestions].concat(this.cachedRenderExtraArgs));else{var n=this,i=function(){n.canceled||n.source(t,e.bind(n))};if(this.debounce){clearTimeout(this.debounceTimeout),this.debounceTimeout=setTimeout((function(){n.debounceTimeout=null,i()}),this.debounce)}else i()}},cacheSuggestions:function(t,e,n){this.cachedQuery=t,this.cachedSuggestions=e,this.cachedRenderExtraArgs=n},shouldFetchFromCache:function(t){return this.cache&&this.cachedQuery===t&&this.cachedSuggestions&&this.cachedSuggestions.length},clearCachedSuggestions:function(){delete this.cachedQuery,delete this.cachedSuggestions,delete this.cachedRenderExtraArgs},cancel:function(){this.canceled=!0},clear:function(){this.$el&&(this.cancel(),this.$el.empty(),this.trigger("rendered",""))},isEmpty:function(){return this._isEmpty},destroy:function(){this.clearCachedSuggestions(),this.$el=null}}),t.exports=h},3354:(t,e,n)=>{"use strict";var i=n(2856),s=n(4910),r=n(3109),o=n(9050),a=n(1228);function u(t){var e,n,r,o=this;(t=t||{}).menu||i.error("menu is required"),i.isArray(t.datasets)||i.isObject(t.datasets)||i.error("1 or more datasets required"),t.datasets||i.error("datasets is required"),this.isOpen=!1,this.isEmpty=!0,this.minLength=t.minLength||0,this.templates={},this.appendTo=t.appendTo||!1,this.css=i.mixin({},a,t.appendTo?a.appendTo:{}),this.cssClasses=t.cssClasses=i.mixin({},a.defaultClasses,t.cssClasses||{}),this.cssClasses.prefix=t.cssClasses.formattedPrefix||i.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix),e=i.bind(this._onSuggestionClick,this),n=i.bind(this._onSuggestionMouseEnter,this),r=i.bind(this._onSuggestionMouseLeave,this);var c=i.className(this.cssClasses.prefix,this.cssClasses.suggestion);this.$menu=s.element(t.menu).on("mouseenter.aa",c,n).on("mouseleave.aa",c,r).on("click.aa",c,e),this.$container=t.appendTo?t.wrapper:this.$menu,t.templates&&t.templates.header&&(this.templates.header=i.templatify(t.templates.header),this.$menu.prepend(this.templates.header())),t.templates&&t.templates.empty&&(this.templates.empty=i.templatify(t.templates.empty),this.$empty=s.element(''),this.$menu.append(this.$empty),this.$empty.hide()),this.datasets=i.map(t.datasets,(function(e){return function(t,e,n){return new u.Dataset(i.mixin({$menu:t,cssClasses:n},e))}(o.$menu,e,t.cssClasses)})),i.each(this.datasets,(function(t){var e=t.getRoot();e&&0===e.parent().length&&o.$menu.append(e),t.onSync("rendered",o._onRendered,o)})),t.templates&&t.templates.footer&&(this.templates.footer=i.templatify(t.templates.footer),this.$menu.append(this.templates.footer()));var l=this;s.element(window).resize((function(){l._redraw()}))}i.mixin(u.prototype,r,{_onSuggestionClick:function(t){this.trigger("suggestionClicked",s.element(t.currentTarget))},_onSuggestionMouseEnter:function(t){var e=s.element(t.currentTarget);if(!e.hasClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0))){this._removeCursor();var n=this;setTimeout((function(){n._setCursor(e,!1)}),0)}},_onSuggestionMouseLeave:function(t){if(t.relatedTarget&&s.element(t.relatedTarget).closest("."+i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).length>0)return;this._removeCursor(),this.trigger("cursorRemoved")},_onRendered:function(t,e){if(this.isEmpty=i.every(this.datasets,(function(t){return t.isEmpty()})),this.isEmpty)if(e.length>=this.minLength&&this.trigger("empty"),this.$empty)if(e.length=this.minLength?this._show():this._hide());this.trigger("datasetRendered")},_hide:function(){this.$container.hide()},_show:function(){this.$container.css("display","block"),this._redraw(),this.trigger("shown")},_redraw:function(){this.isOpen&&this.appendTo&&this.trigger("redrawn")},_getSuggestions:function(){return this.$menu.find(i.className(this.cssClasses.prefix,this.cssClasses.suggestion))},_getCursor:function(){return this.$menu.find(i.className(this.cssClasses.prefix,this.cssClasses.cursor)).first()},_setCursor:function(t,e){t.first().addClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).attr("aria-selected","true"),this.trigger("cursorMoved",e)},_removeCursor:function(){this._getCursor().removeClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).removeAttr("aria-selected")},_moveCursor:function(t){var e,n,i,s;this.isOpen&&(n=this._getCursor(),e=this._getSuggestions(),this._removeCursor(),-1!==(i=((i=e.index(n)+t)+1)%(e.length+1)-1)?(i<-1&&(i=e.length-1),this._setCursor(s=e.eq(i),!0),this._ensureVisible(s)):this.trigger("cursorRemoved"))},_ensureVisible:function(t){var e,n,i,s;n=(e=t.position().top)+t.height()+parseInt(t.css("margin-top"),10)+parseInt(t.css("margin-bottom"),10),i=this.$menu.scrollTop(),s=this.$menu.height()+parseInt(this.$menu.css("padding-top"),10)+parseInt(this.$menu.css("padding-bottom"),10),e<0?this.$menu.scrollTop(i+e):s {"use strict";var i=n(2856),s=n(4910);function r(t){t&&t.el||i.error("EventBus initialized without el"),this.$el=s.element(t.el)}i.mixin(r.prototype,{trigger:function(t,e,n,s){var r=i.Event("autocomplete:"+t);return this.$el.trigger(r,[e,n,s]),r}}),t.exports=r},3109:(t,e,n)=>{"use strict";var i=n(624),s=/\s+/;function r(t,e,n,i){var r;if(!n)return this;for(e=e.split(s),n=i?function(t,e){return t.bind?t.bind(e):function(){t.apply(e,[].slice.call(arguments,0))}}(n,i):n,this._callbacks=this._callbacks||{};r=e.shift();)this._callbacks[r]=this._callbacks[r]||{sync:[],async:[]},this._callbacks[r][t].push(n);return this}function o(t,e,n){return function(){for(var i,s=0,r=t.length;!i&&s {"use strict";t.exports={wrapper:'',dropdown:'',dataset:'',suggestions:'',suggestion:''}},2534:(t,e,n)=>{"use strict";var i;i={9:"tab",27:"esc",37:"left",39:"right",13:"enter",38:"up",40:"down"};var s=n(2856),r=n(4910),o=n(3109);function a(t){var e,n,o,a,u,c=this;(t=t||{}).input||s.error("input is missing"),e=s.bind(this._onBlur,this),n=s.bind(this._onFocus,this),o=s.bind(this._onKeydown,this),a=s.bind(this._onInput,this),this.$hint=r.element(t.hint),this.$input=r.element(t.input).on("blur.aa",e).on("focus.aa",n).on("keydown.aa",o),0===this.$hint.length&&(this.setHint=this.getHint=this.clearHint=this.clearHintIfInvalid=s.noop),s.isMsie()?this.$input.on("keydown.aa keypress.aa cut.aa paste.aa",(function(t){i[t.which||t.keyCode]||s.defer(s.bind(c._onInput,c,t))})):this.$input.on("input.aa",a),this.query=this.$input.val(),this.$overflowHelper=(u=this.$input,r.element('').css({position:"absolute",visibility:"hidden",whiteSpace:"pre",fontFamily:u.css("font-family"),fontSize:u.css("font-size"),fontStyle:u.css("font-style"),fontVariant:u.css("font-variant"),fontWeight:u.css("font-weight"),wordSpacing:u.css("word-spacing"),letterSpacing:u.css("letter-spacing"),textIndent:u.css("text-indent"),textRendering:u.css("text-rendering"),textTransform:u.css("text-transform")}).insertAfter(u))}function u(t){return t.altKey||t.ctrlKey||t.metaKey||t.shiftKey}a.normalizeQuery=function(t){return(t||"").replace(/^\s*/g,"").replace(/\s{2,}/g," ")},s.mixin(a.prototype,o,{_onBlur:function(){this.resetInputValue(),this.$input.removeAttr("aria-activedescendant"),this.trigger("blurred")},_onFocus:function(){this.trigger("focused")},_onKeydown:function(t){var e=i[t.which||t.keyCode];this._managePreventDefault(e,t),e&&this._shouldTrigger(e,t)&&this.trigger(e+"Keyed",t)},_onInput:function(){this._checkInputValue()},_managePreventDefault:function(t,e){var n,i,s;switch(t){case"tab":i=this.getHint(),s=this.getInputValue(),n=i&&i!==s&&!u(e);break;case"up":case"down":n=!u(e);break;default:n=!1}n&&e.preventDefault()},_shouldTrigger:function(t,e){var n;if("tab"===t)n=!u(e);else n=!0;return n},_checkInputValue:function(){var t,e,n,i,s;t=this.getInputValue(),i=t,s=this.query,n=!(!(e=a.normalizeQuery(i)===a.normalizeQuery(s))||!this.query)&&this.query.length!==t.length,this.query=t,e?n&&this.trigger("whitespaceChanged",this.query):this.trigger("queryChanged",this.query)},focus:function(){this.$input.focus()},blur:function(){this.$input.blur()},getQuery:function(){return this.query},setQuery:function(t){this.query=t},getInputValue:function(){return this.$input.val()},setInputValue:function(t,e){void 0===t&&(t=this.query),this.$input.val(t),e?this.clearHint():this._checkInputValue()},expand:function(){this.$input.attr("aria-expanded","true")},collapse:function(){this.$input.attr("aria-expanded","false")},setActiveDescendant:function(t){this.$input.attr("aria-activedescendant",t)},removeActiveDescendant:function(){this.$input.removeAttr("aria-activedescendant")},resetInputValue:function(){this.setInputValue(this.query,!0)},getHint:function(){return this.$hint.val()},setHint:function(t){this.$hint.val(t)},clearHint:function(){this.setHint("")},clearHintIfInvalid:function(){var t,e,n;n=(t=this.getInputValue())!==(e=this.getHint())&&0===e.indexOf(t),""!==t&&n&&!this.hasOverflow()||this.clearHint()},getLanguageDirection:function(){return(this.$input.css("direction")||"ltr").toLowerCase()},hasOverflow:function(){var t=this.$input.width()-2;return this.$overflowHelper.text(this.getInputValue()),this.$overflowHelper.width()>=t},isCursorAtEnd:function(){var t,e,n;return t=this.$input.val().length,e=this.$input[0].selectionStart,s.isNumber(e)?e===t:!document.selection||((n=document.selection.createRange()).moveStart("character",-t),t===n.text.length)},destroy:function(){this.$hint.off(".aa"),this.$input.off(".aa"),this.$hint=this.$input=this.$overflowHelper=null}}),t.exports=a},6549:(t,e,n)=>{"use strict";var i="aaAttrs",s=n(2856),r=n(4910),o=n(50),a=n(2534),u=n(3354),c=n(3561),l=n(1228);function h(t){var e,n;if((t=t||{}).input||s.error("missing input"),this.isActivated=!1,this.debug=!!t.debug,this.autoselect=!!t.autoselect,this.autoselectOnBlur=!!t.autoselectOnBlur,this.openOnFocus=!!t.openOnFocus,this.minLength=s.isNumber(t.minLength)?t.minLength:1,this.autoWidth=void 0===t.autoWidth||!!t.autoWidth,this.clearOnSelected=!!t.clearOnSelected,this.tabAutocomplete=void 0===t.tabAutocomplete||!!t.tabAutocomplete,t.hint=!!t.hint,t.hint&&t.appendTo)throw new Error("[autocomplete.js] hint and appendTo options can't be used at the same time");this.css=t.css=s.mixin({},l,t.appendTo?l.appendTo:{}),this.cssClasses=t.cssClasses=s.mixin({},l.defaultClasses,t.cssClasses||{}),this.cssClasses.prefix=t.cssClasses.formattedPrefix=s.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix),this.listboxId=t.listboxId=[this.cssClasses.root,"listbox",s.getUniqueId()].join("-");var a=function(t){var e,n,o,a;e=r.element(t.input),n=r.element(c.wrapper.replace("%ROOT%",t.cssClasses.root)).css(t.css.wrapper),t.appendTo||"block"!==e.css("display")||"table"!==e.parent().css("display")||n.css("display","table-cell");var u=c.dropdown.replace("%PREFIX%",t.cssClasses.prefix).replace("%DROPDOWN_MENU%",t.cssClasses.dropdownMenu);o=r.element(u).css(t.css.dropdown).attr({role:"listbox",id:t.listboxId}),t.templates&&t.templates.dropdownMenu&&o.html(s.templatify(t.templates.dropdownMenu)());a=e.clone().css(t.css.hint).css(function(t){return{backgroundAttachment:t.css("background-attachment"),backgroundClip:t.css("background-clip"),backgroundColor:t.css("background-color"),backgroundImage:t.css("background-image"),backgroundOrigin:t.css("background-origin"),backgroundPosition:t.css("background-position"),backgroundRepeat:t.css("background-repeat"),backgroundSize:t.css("background-size")}}(e)),a.val("").addClass(s.className(t.cssClasses.prefix,t.cssClasses.hint,!0)).removeAttr("id name placeholder required").prop("readonly",!0).attr({"aria-hidden":"true",autocomplete:"off",spellcheck:"false",tabindex:-1}),a.removeData&&a.removeData();e.data(i,{"aria-autocomplete":e.attr("aria-autocomplete"),"aria-expanded":e.attr("aria-expanded"),"aria-owns":e.attr("aria-owns"),autocomplete:e.attr("autocomplete"),dir:e.attr("dir"),role:e.attr("role"),spellcheck:e.attr("spellcheck"),style:e.attr("style"),type:e.attr("type")}),e.addClass(s.className(t.cssClasses.prefix,t.cssClasses.input,!0)).attr({autocomplete:"off",spellcheck:!1,role:"combobox","aria-autocomplete":t.datasets&&t.datasets[0]&&t.datasets[0].displayKey?"both":"list","aria-expanded":"false","aria-label":t.ariaLabel,"aria-owns":t.listboxId}).css(t.hint?t.css.input:t.css.inputWithNoHint);try{e.attr("dir")||e.attr("dir","auto")}catch(l){}return n=t.appendTo?n.appendTo(r.element(t.appendTo).eq(0)).eq(0):e.wrap(n).parent(),n.prepend(t.hint?a:null).append(o),{wrapper:n,input:e,hint:a,menu:o}}(t);this.$node=a.wrapper;var u=this.$input=a.input;e=a.menu,n=a.hint,t.dropdownMenuContainer&&r.element(t.dropdownMenuContainer).css("position","relative").append(e.css("top","0")),u.on("blur.aa",(function(t){var n=document.activeElement;s.isMsie()&&(e[0]===n||e[0].contains(n))&&(t.preventDefault(),t.stopImmediatePropagation(),s.defer((function(){u.focus()})))})),e.on("mousedown.aa",(function(t){t.preventDefault()})),this.eventBus=t.eventBus||new o({el:u}),this.dropdown=new h.Dropdown({appendTo:t.appendTo,wrapper:this.$node,menu:e,datasets:t.datasets,templates:t.templates,cssClasses:t.cssClasses,minLength:this.minLength}).onSync("suggestionClicked",this._onSuggestionClicked,this).onSync("cursorMoved",this._onCursorMoved,this).onSync("cursorRemoved",this._onCursorRemoved,this).onSync("opened",this._onOpened,this).onSync("closed",this._onClosed,this).onSync("shown",this._onShown,this).onSync("empty",this._onEmpty,this).onSync("redrawn",this._onRedrawn,this).onAsync("datasetRendered",this._onDatasetRendered,this),this.input=new h.Input({input:u,hint:n}).onSync("focused",this._onFocused,this).onSync("blurred",this._onBlurred,this).onSync("enterKeyed",this._onEnterKeyed,this).onSync("tabKeyed",this._onTabKeyed,this).onSync("escKeyed",this._onEscKeyed,this).onSync("upKeyed",this._onUpKeyed,this).onSync("downKeyed",this._onDownKeyed,this).onSync("leftKeyed",this._onLeftKeyed,this).onSync("rightKeyed",this._onRightKeyed,this).onSync("queryChanged",this._onQueryChanged,this).onSync("whitespaceChanged",this._onWhitespaceChanged,this),this._bindKeyboardShortcuts(t),this._setLanguageDirection()}s.mixin(h.prototype,{_bindKeyboardShortcuts:function(t){if(t.keyboardShortcuts){var e=this.$input,n=[];s.each(t.keyboardShortcuts,(function(t){"string"==typeof t&&(t=t.toUpperCase().charCodeAt(0)),n.push(t)})),r.element(document).keydown((function(t){var i=t.target||t.srcElement,s=i.tagName;if(!i.isContentEditable&&"INPUT"!==s&&"SELECT"!==s&&"TEXTAREA"!==s){var r=t.which||t.keyCode;-1!==n.indexOf(r)&&(e.focus(),t.stopPropagation(),t.preventDefault())}}))}},_onSuggestionClicked:function(t,e){var n;(n=this.dropdown.getDatumForSuggestion(e))&&this._select(n,{selectionMethod:"click"})},_onCursorMoved:function(t,e){var n=this.dropdown.getDatumForCursor(),i=this.dropdown.getCurrentCursor().attr("id");this.input.setActiveDescendant(i),n&&(e&&this.input.setInputValue(n.value,!0),this.eventBus.trigger("cursorchanged",n.raw,n.datasetName))},_onCursorRemoved:function(){this.input.resetInputValue(),this._updateHint(),this.eventBus.trigger("cursorremoved")},_onDatasetRendered:function(){this._updateHint(),this.eventBus.trigger("updated")},_onOpened:function(){this._updateHint(),this.input.expand(),this.eventBus.trigger("opened")},_onEmpty:function(){this.eventBus.trigger("empty")},_onRedrawn:function(){this.$node.css("top","0px"),this.$node.css("left","0px");var t=this.$input[0].getBoundingClientRect();this.autoWidth&&this.$node.css("width",t.width+"px");var e=this.$node[0].getBoundingClientRect(),n=t.bottom-e.top;this.$node.css("top",n+"px");var i=t.left-e.left;this.$node.css("left",i+"px"),this.eventBus.trigger("redrawn")},_onShown:function(){this.eventBus.trigger("shown"),this.autoselect&&this.dropdown.cursorTopSuggestion()},_onClosed:function(){this.input.clearHint(),this.input.removeActiveDescendant(),this.input.collapse(),this.eventBus.trigger("closed")},_onFocused:function(){if(this.isActivated=!0,this.openOnFocus){var t=this.input.getQuery();t.length>=this.minLength?this.dropdown.update(t):this.dropdown.empty(),this.dropdown.open()}},_onBlurred:function(){var t,e;t=this.dropdown.getDatumForCursor(),e=this.dropdown.getDatumForTopSuggestion();var n={selectionMethod:"blur"};this.debug||(this.autoselectOnBlur&&t?this._select(t,n):this.autoselectOnBlur&&e?this._select(e,n):(this.isActivated=!1,this.dropdown.empty(),this.dropdown.close()))},_onEnterKeyed:function(t,e){var n,i;n=this.dropdown.getDatumForCursor(),i=this.dropdown.getDatumForTopSuggestion();var s={selectionMethod:"enterKey"};n?(this._select(n,s),e.preventDefault()):this.autoselect&&i&&(this._select(i,s),e.preventDefault())},_onTabKeyed:function(t,e){if(this.tabAutocomplete){var n;(n=this.dropdown.getDatumForCursor())?(this._select(n,{selectionMethod:"tabKey"}),e.preventDefault()):this._autocomplete(!0)}else this.dropdown.close()},_onEscKeyed:function(){this.dropdown.close(),this.input.resetInputValue()},_onUpKeyed:function(){var t=this.input.getQuery();this.dropdown.isEmpty&&t.length>=this.minLength?this.dropdown.update(t):this.dropdown.moveCursorUp(),this.dropdown.open()},_onDownKeyed:function(){var t=this.input.getQuery();this.dropdown.isEmpty&&t.length>=this.minLength?this.dropdown.update(t):this.dropdown.moveCursorDown(),this.dropdown.open()},_onLeftKeyed:function(){"rtl"===this.dir&&this._autocomplete()},_onRightKeyed:function(){"ltr"===this.dir&&this._autocomplete()},_onQueryChanged:function(t,e){this.input.clearHintIfInvalid(),e.length>=this.minLength?this.dropdown.update(e):this.dropdown.empty(),this.dropdown.open(),this._setLanguageDirection()},_onWhitespaceChanged:function(){this._updateHint(),this.dropdown.open()},_setLanguageDirection:function(){var t=this.input.getLanguageDirection();this.dir!==t&&(this.dir=t,this.$node.css("direction",t),this.dropdown.setLanguageDirection(t))},_updateHint:function(){var t,e,n,i,r;(t=this.dropdown.getDatumForTopSuggestion())&&this.dropdown.isVisible()&&!this.input.hasOverflow()?(e=this.input.getInputValue(),n=a.normalizeQuery(e),i=s.escapeRegExChars(n),(r=new RegExp("^(?:"+i+")(.+$)","i").exec(t.value))?this.input.setHint(e+r[1]):this.input.clearHint()):this.input.clearHint()},_autocomplete:function(t){var e,n,i,s;e=this.input.getHint(),n=this.input.getQuery(),i=t||this.input.isCursorAtEnd(),e&&n!==e&&i&&((s=this.dropdown.getDatumForTopSuggestion())&&this.input.setInputValue(s.value),this.eventBus.trigger("autocompleted",s.raw,s.datasetName))},_select:function(t,e){void 0!==t.value&&this.input.setQuery(t.value),this.clearOnSelected?this.setVal(""):this.input.setInputValue(t.value,!0),this._setLanguageDirection(),!1===this.eventBus.trigger("selected",t.raw,t.datasetName,e).isDefaultPrevented()&&(this.dropdown.close(),s.defer(s.bind(this.dropdown.empty,this.dropdown)))},open:function(){if(!this.isActivated){var t=this.input.getInputValue();t.length>=this.minLength?this.dropdown.update(t):this.dropdown.empty()}this.dropdown.open()},close:function(){this.dropdown.close()},setVal:function(t){t=s.toStr(t),this.isActivated?this.input.setInputValue(t):(this.input.setQuery(t),this.input.setInputValue(t,!0)),this._setLanguageDirection()},getVal:function(){return this.input.getQuery()},destroy:function(){this.input.destroy(),this.dropdown.destroy(),function(t,e){var n=t.find(s.className(e.prefix,e.input));s.each(n.data(i),(function(t,e){void 0===t?n.removeAttr(e):n.attr(e,t)})),n.detach().removeClass(s.className(e.prefix,e.input,!0)).insertAfter(t),n.removeData&&n.removeData(i);t.remove()}(this.$node,this.cssClasses),this.$node=null},getWrapper:function(){return this.dropdown.$container[0]}}),h.Dropdown=u,h.Input=a,h.sources=n(8840),t.exports=h},4910:t=>{"use strict";t.exports={element:null}},6177:t=>{"use strict";t.exports=function(t){var e=t.match(/Algolia for JavaScript \((\d+\.)(\d+\.)(\d+)\)/)||t.match(/Algolia for vanilla JavaScript (\d+\.)(\d+\.)(\d+)/);if(e)return[e[1],e[2],e[3]]}},2856:(t,e,n)=>{"use strict";var i,s=n(8820),r=n(4910);function o(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}t.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(t){if(void 0===t&&(t=navigator.userAgent),/(msie|trident)/i.test(t)){var e=t.match(/(msie |rv:)(\d+(.\d+)?)/i);if(e)return e[2]}return!1},escapeRegExChars:function(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(t){return"number"==typeof t},toStr:function(t){return null==t?"":t+""},cloneDeep:function(t){var e=this.mixin({},t),n=this;return this.each(e,(function(t,i){t&&(n.isArray(t)?e[i]=[].concat(t):n.isObject(t)&&(e[i]=n.cloneDeep(t)))})),e},error:function(t){throw new Error(t)},every:function(t,e){var n=!0;return t?(this.each(t,(function(i,s){n&&(n=e.call(null,i,s,t)&&n)})),!!n):n},any:function(t,e){var n=!1;return t?(this.each(t,(function(i,s){if(e.call(null,i,s,t))return n=!0,!1})),n):n},getUniqueId:(i=0,function(){return i++}),templatify:function(t){if(this.isFunction(t))return t;var e=r.element(t);return"SCRIPT"===e.prop("tagName")?function(){return e.text()}:function(){return String(t)}},defer:function(t){setTimeout(t,0)},noop:function(){},formatPrefix:function(t,e){return e?"":t+"-"},className:function(t,e,n){return n?t+e:"."+s(t+e,{isIdentifier:!0})},escapeHighlightedString:function(t,e,n){e=e||"";var i=document.createElement("div");i.appendChild(document.createTextNode(e)),n=n||"";var s=document.createElement("div");s.appendChild(document.createTextNode(n));var r=document.createElement("div");return r.appendChild(document.createTextNode(t)),r.innerHTML.replace(RegExp(o(i.innerHTML),"g"),e).replace(RegExp(o(s.innerHTML),"g"),n)}}},9983:(t,e,n)=>{"use strict";var i=n(2856),s=n(533),r=n(6177);var o,a,u=(o=[],a=window.Promise.resolve(),function(t,e){return function(n,s){(function(t,e){return window.Promise.resolve().then((function(){return o.length&&(a=t.search(o),o=[]),a})).then((function(t){if(t)return t.results[e]}))})(t.as,o.push({indexName:t.indexName,query:n,params:e})-1).then((function(t){t&&s(t.hits,t)})).catch((function(t){i.error(t.message)}))}});t.exports=function(t,e){var n=r(t.as._ua);if(n&&n[0]>=3&&n[1]>20){var i="autocomplete.js "+s;-1===t.as._ua.indexOf(i)&&(t.as._ua+="; "+i)}return u(t,e)}},8840:(t,e,n)=>{"use strict";t.exports={hits:n(9983),popularIn:n(4445)}},4445:(t,e,n)=>{"use strict";var i=n(2856),s=n(533),r=n(6177);t.exports=function(t,e,n,o){var a=r(t.as._ua);if(a&&a[0]>=3&&a[1]>20&&((e=e||{}).additionalUA="autocomplete.js "+s),!n.source)return i.error("Missing 'source' key");var u=i.isFunction(n.source)?n.source:function(t){return t[n.source]};if(!n.index)return i.error("Missing 'index' key");var c=n.index;return o=o||{},function(a,l){t.search(a,e,(function(t,a){if(t)i.error(t.message);else{if(a.hits.length>0){var h=a.hits[0],p=i.mixin({hitsPerPage:0},n);delete p.source,delete p.index;var f=r(c.as._ua);return f&&f[0]>=3&&f[1]>20&&(e.additionalUA="autocomplete.js "+s),void c.search(u(h),p,(function(t,e){if(t)i.error(t.message);else{var n=[];if(o.includeAll){var s=o.allTitle||"All departments";n.push(i.mixin({facet:{value:s,count:e.nbHits}},i.cloneDeep(h)))}i.each(e.facets,(function(t,e){i.each(t,(function(t,s){n.push(i.mixin({facet:{facet:e,value:s,count:t}},i.cloneDeep(h)))}))}));for(var r=1;r {"use strict";var i=n(6990);n(4910).element=i;var s=n(2856);s.isArray=i.isArray,s.isFunction=i.isFunction,s.isObject=i.isPlainObject,s.bind=i.proxy,s.each=function(t,e){i.each(t,(function(t,n){return e(n,t)}))},s.map=i.map,s.mixin=i.extend,s.Event=i.Event;var r="aaAutocomplete",o=n(6549),a=n(50);function u(t,e,n,u){n=s.isArray(n)?n:[].slice.call(arguments,2);var c=i(t).each((function(t,s){var c=i(s),l=new a({el:c}),h=u||new o({input:c,eventBus:l,dropdownMenuContainer:e.dropdownMenuContainer,hint:void 0===e.hint||!!e.hint,minLength:e.minLength,autoselect:e.autoselect,autoselectOnBlur:e.autoselectOnBlur,tabAutocomplete:e.tabAutocomplete,openOnFocus:e.openOnFocus,templates:e.templates,debug:e.debug,clearOnSelected:e.clearOnSelected,cssClasses:e.cssClasses,datasets:n,keyboardShortcuts:e.keyboardShortcuts,appendTo:e.appendTo,autoWidth:e.autoWidth,ariaLabel:e.ariaLabel||s.getAttribute("aria-label")});c.data(r,h)}));return c.autocomplete={},s.each(["open","close","getVal","setVal","destroy","getWrapper"],(function(t){c.autocomplete[t]=function(){var e,n=arguments;return c.each((function(s,o){var a=i(o).data(r);e=a[t].apply(a,n)})),e}})),c}u.sources=o.sources,u.escapeHighlightedString=s.escapeHighlightedString;var c="autocomplete"in window,l=window.autocomplete;u.noConflict=function(){return c?window.autocomplete=l:delete window.autocomplete,u},t.exports=u},533:t=>{t.exports="0.38.1"},6990:t=>{var e;e=window,t.exports=function(t){var e,n,i=function(){var e,n,i,s,r,o,a=[],u=a.concat,c=a.filter,l=a.slice,h=t.document,p={},f={},d={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},g=/^\s*<(\w+|!)[^>]*>/,m=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,y=/^(?:body|html)$/i,w=/([A-Z])/g,b=["val","css","html","text","data","width","height","offset"],C=["after","prepend","before","append"],x=h.createElement("table"),_=h.createElement("tr"),S={tr:h.createElement("tbody"),tbody:x,thead:x,tfoot:x,td:_,th:_,"*":h.createElement("div")},E=/complete|loaded|interactive/,A=/^[\w-]*$/,$={},T=$.toString,O={},D=h.createElement("div"),N={tabindex:"tabIndex",readonly:"readOnly",for:"htmlFor",class:"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},k=Array.isArray||function(t){return t instanceof Array};function I(t){return null==t?String(t):$[T.call(t)]||"object"}function P(t){return"function"==I(t)}function L(t){return null!=t&&t==t.window}function M(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function F(t){return"object"==I(t)}function R(t){return F(t)&&!L(t)&&Object.getPrototypeOf(t)==Object.prototype}function q(t){var e=!!t&&"length"in t&&t.length,n=i.type(t);return"function"!=n&&!L(t)&&("array"==n||0===e||"number"==typeof e&&e>0&&e-1 in t)}function V(t){return c.call(t,(function(t){return null!=t}))}function H(t){return t.length>0?i.fn.concat.apply([],t):t}function B(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function K(t){return t in f?f[t]:f[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function j(t,e){return"number"!=typeof e||d[B(t)]?e:e+"px"}function z(t){var e,n;return p[t]||(e=h.createElement(t),h.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),p[t]=n),p[t]}function U(t){return"children"in t?l.call(t.children):i.map(t.childNodes,(function(t){if(1==t.nodeType)return t}))}function Q(t,e){var n,i=t?t.length:0;for(n=0;n$2>")),n===e&&(n=g.test(t)&&RegExp.$1),n in S||(n="*"),(a=S[n]).innerHTML=""+t,r=i.each(l.call(a.childNodes),(function(){a.removeChild(this)}))),R(s)&&(o=i(r),i.each(s,(function(t,e){b.indexOf(t)>-1?o[t](e):o.attr(t,e)}))),r},O.Z=function(t,e){return new Q(t,e)},O.isZ=function(t){return t instanceof O.Z},O.init=function(t,n){var s;if(!t)return O.Z();if("string"==typeof t)if("<"==(t=t.trim())[0]&&g.test(t))s=O.fragment(t,RegExp.$1,n),t=null;else{if(n!==e)return i(n).find(t);s=O.qsa(h,t)}else{if(P(t))return i(h).ready(t);if(O.isZ(t))return t;if(k(t))s=V(t);else if(F(t))s=[t],t=null;else if(g.test(t))s=O.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==e)return i(n).find(t);s=O.qsa(h,t)}}return O.Z(s,t)},(i=function(t,e){return O.init(t,e)}).extend=function(t){var e,n=l.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach((function(n){W(t,n,e)})),t},O.qsa=function(t,e){var n,i="#"==e[0],s=!i&&"."==e[0],r=i||s?e.slice(1):e,o=A.test(r);return t.getElementById&&o&&i?(n=t.getElementById(r))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:l.call(o&&!i&&t.getElementsByClassName?s?t.getElementsByClassName(r):t.getElementsByTagName(e):t.querySelectorAll(e))},i.contains=h.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},i.type=I,i.isFunction=P,i.isWindow=L,i.isArray=k,i.isPlainObject=R,i.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},i.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},i.inArray=function(t,e,n){return a.indexOf.call(e,t,n)},i.camelCase=r,i.trim=function(t){return null==t?"":String.prototype.trim.call(t)},i.uuid=0,i.support={},i.expr={},i.noop=function(){},i.map=function(t,e){var n,i,s,r=[];if(q(t))for(i=0;i =0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each((function(){null!=this.parentNode&&this.parentNode.removeChild(this)}))},each:function(t){return a.every.call(this,(function(e,n){return!1!==t.call(e,n,e)})),this},filter:function(t){return P(t)?this.not(this.not(t)):i(c.call(this,(function(e){return O.matches(e,t)})))},add:function(t,e){return i(o(this.concat(i(t,e))))},is:function(t){return this.length>0&&O.matches(this[0],t)},not:function(t){var n=[];if(P(t)&&t.call!==e)this.each((function(e){t.call(this,e)||n.push(this)}));else{var s="string"==typeof t?this.filter(t):q(t)&&P(t.item)?l.call(t):i(t);this.forEach((function(t){s.indexOf(t)<0&&n.push(t)}))}return i(n)},has:function(t){return this.filter((function(){return F(t)?i.contains(this,t):i(this).find(t).size()}))},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!F(t)?t:i(t)},last:function(){var t=this[this.length-1];return t&&!F(t)?t:i(t)},find:function(t){var e=this;return t?"object"==typeof t?i(t).filter((function(){var t=this;return a.some.call(e,(function(e){return i.contains(e,t)}))})):1==this.length?i(O.qsa(this[0],t)):this.map((function(){return O.qsa(this,t)})):i()},closest:function(t,e){var n=[],s="object"==typeof t&&i(t);return this.each((function(i,r){for(;r&&!(s?s.indexOf(r)>=0:O.matches(r,t));)r=r!==e&&!M(r)&&r.parentNode;r&&n.indexOf(r)<0&&n.push(r)})),i(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=i.map(n,(function(t){if((t=t.parentNode)&&!M(t)&&e.indexOf(t)<0)return e.push(t),t}));return Z(e,t)},parent:function(t){return Z(o(this.pluck("parentNode")),t)},children:function(t){return Z(this.map((function(){return U(this)})),t)},contents:function(){return this.map((function(){return this.contentDocument||l.call(this.childNodes)}))},siblings:function(t){return Z(this.map((function(t,e){return c.call(U(e.parentNode),(function(t){return t!==e}))})),t)},empty:function(){return this.each((function(){this.innerHTML=""}))},pluck:function(t){return i.map(this,(function(e){return e[t]}))},show:function(){return this.each((function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=z(this.nodeName))}))},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=P(t);if(this[0]&&!e)var n=i(t).get(0),s=n.parentNode||this.length>1;return this.each((function(r){i(this).wrapAll(e?t.call(this,r):s?n.cloneNode(!0):n)}))},wrapAll:function(t){if(this[0]){var e;for(i(this[0]).before(t=i(t));(e=t.children()).length;)t=e.first();i(t).append(this)}return this},wrapInner:function(t){var e=P(t);return this.each((function(n){var s=i(this),r=s.contents(),o=e?t.call(this,n):t;r.length?r.wrapAll(o):s.append(o)}))},unwrap:function(){return this.parent().each((function(){i(this).replaceWith(i(this).children())})),this},clone:function(){return this.map((function(){return this.cloneNode(!0)}))},hide:function(){return this.css("display","none")},toggle:function(t){return this.each((function(){var n=i(this);(t===e?"none"==n.css("display"):t)?n.show():n.hide()}))},prev:function(t){return i(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return i(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each((function(e){var n=this.innerHTML;i(this).empty().append(X(this,t,e,n))})):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each((function(e){var n=X(this,t,e,this.textContent);this.textContent=null==n?"":""+n})):0 in this?this.pluck("textContent").join(""):null},attr:function(t,i){var s;return"string"!=typeof t||1 in arguments?this.each((function(e){if(1===this.nodeType)if(F(t))for(n in t)G(this,n,t[n]);else G(this,t,X(this,i,e,this.getAttribute(t)))})):0 in this&&1==this[0].nodeType&&null!=(s=this[0].getAttribute(t))?s:e},removeAttr:function(t){return this.each((function(){1===this.nodeType&&t.split(" ").forEach((function(t){G(this,t)}),this)}))},prop:function(t,e){return t=N[t]||t,1 in arguments?this.each((function(n){this[t]=X(this,e,n,this[t])})):this[0]&&this[0][t]},removeProp:function(t){return t=N[t]||t,this.each((function(){delete this[t]}))},data:function(t,n){var i="data-"+t.replace(w,"-$1").toLowerCase(),s=1 in arguments?this.attr(i,n):this.attr(i);return null!==s?Y(s):e},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each((function(e){this.value=X(this,t,e,this.value)}))):this[0]&&(this[0].multiple?i(this[0]).find("option").filter((function(){return this.selected})).pluck("value"):this[0].value)},offset:function(e){if(e)return this.each((function(t){var n=i(this),s=X(this,e,t,n.offset()),r=n.offsetParent().offset(),o={top:s.top-r.top,left:s.left-r.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)}));if(!this.length)return null;if(h.documentElement!==this[0]&&!i.contains(h.documentElement,this[0]))return{top:0,left:0};var n=this[0].getBoundingClientRect();return{left:n.left+t.pageXOffset,top:n.top+t.pageYOffset,width:Math.round(n.width),height:Math.round(n.height)}},css:function(t,e){if(arguments.length<2){var s=this[0];if("string"==typeof t){if(!s)return;return s.style[r(t)]||getComputedStyle(s,"").getPropertyValue(t)}if(k(t)){if(!s)return;var o={},a=getComputedStyle(s,"");return i.each(t,(function(t,e){o[e]=s.style[r(e)]||a.getPropertyValue(e)})),o}}var u="";if("string"==I(t))e||0===e?u=B(t)+":"+j(t,e):this.each((function(){this.style.removeProperty(B(t))}));else for(n in t)t[n]||0===t[n]?u+=B(n)+":"+j(n,t[n])+";":this.each((function(){this.style.removeProperty(B(n))}));return this.each((function(){this.style.cssText+=";"+u}))},index:function(t){return t?this.indexOf(i(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return!!t&&a.some.call(this,(function(t){return this.test(J(t))}),K(t))},addClass:function(t){return t?this.each((function(e){if("className"in this){s=[];var n=J(this);X(this,t,e,n).split(/\s+/g).forEach((function(t){i(this).hasClass(t)||s.push(t)}),this),s.length&&J(this,n+(n?" ":"")+s.join(" "))}})):this},removeClass:function(t){return this.each((function(n){if("className"in this){if(t===e)return J(this,"");s=J(this),X(this,t,n,s).split(/\s+/g).forEach((function(t){s=s.replace(K(t)," ")})),J(this,s.trim())}}))},toggleClass:function(t,n){return t?this.each((function(s){var r=i(this);X(this,t,s,J(this)).split(/\s+/g).forEach((function(t){(n===e?!r.hasClass(t):n)?r.addClass(t):r.removeClass(t)}))})):this},scrollTop:function(t){if(this.length){var n="scrollTop"in this[0];return t===e?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var n="scrollLeft"in this[0];return t===e?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),s=y.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(i(t).css("margin-top"))||0,n.left-=parseFloat(i(t).css("margin-left"))||0,s.top+=parseFloat(i(e[0]).css("border-top-width"))||0,s.left+=parseFloat(i(e[0]).css("border-left-width"))||0,{top:n.top-s.top,left:n.left-s.left}}},offsetParent:function(){return this.map((function(){for(var t=this.offsetParent||h.body;t&&!y.test(t.nodeName)&&"static"==i(t).css("position");)t=t.offsetParent;return t}))}},i.fn.detach=i.fn.remove,["width","height"].forEach((function(t){var n=t.replace(/./,(function(t){return t[0].toUpperCase()}));i.fn[t]=function(s){var r,o=this[0];return s===e?L(o)?o["inner"+n]:M(o)?o.documentElement["scroll"+n]:(r=this.offset())&&r[t]:this.each((function(e){(o=i(this)).css(t,X(this,s,e,o[t]()))}))}})),C.forEach((function(n,s){var r=s%2;i.fn[n]=function(){var n,o,a=i.map(arguments,(function(t){var s=[];return"array"==(n=I(t))?(t.forEach((function(t){return t.nodeType!==e?s.push(t):i.zepto.isZ(t)?s=s.concat(t.get()):void(s=s.concat(O.fragment(t)))})),s):"object"==n||null==t?t:O.fragment(t)})),u=this.length>1;return a.length<1?this:this.each((function(e,n){o=r?n:n.parentNode,n=0==s?n.nextSibling:1==s?n.firstChild:2==s?n:null;var c=i.contains(h.documentElement,o);a.forEach((function(e){if(u)e=e.cloneNode(!0);else if(!o)return i(e).remove();o.insertBefore(e,n),c&&tt(e,(function(e){if(!(null==e.nodeName||"SCRIPT"!==e.nodeName.toUpperCase()||e.type&&"text/javascript"!==e.type||e.src)){var n=e.ownerDocument?e.ownerDocument.defaultView:t;n.eval.call(n,e.innerHTML)}}))}))}))},i.fn[r?n+"To":"insert"+(s?"Before":"After")]=function(t){return i(t)[n](this),this}})),O.Z.prototype=Q.prototype=i.fn,O.uniq=o,O.deserializeValue=Y,i.zepto=O,i}();return function(e){var n,i=1,s=Array.prototype.slice,r=e.isFunction,o=function(t){return"string"==typeof t},a={},u={},c="onfocusin"in t,l={focus:"focusin",blur:"focusout"},h={mouseenter:"mouseover",mouseleave:"mouseout"};function p(t){return t._zid||(t._zid=i++)}function f(t,e,n,i){if((e=d(e)).ns)var s=g(e.ns);return(a[p(t)]||[]).filter((function(t){return t&&(!e.e||t.e==e.e)&&(!e.ns||s.test(t.ns))&&(!n||p(t.fn)===p(n))&&(!i||t.sel==i)}))}function d(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function g(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function m(t,e){return t.del&&!c&&t.e in l||!!e}function v(t){return h[t]||c&&l[t]||t}function y(t,i,s,r,o,u,c){var l=p(t),f=a[l]||(a[l]=[]);i.split(/\s/).forEach((function(i){if("ready"==i)return e(document).ready(s);var a=d(i);a.fn=s,a.sel=o,a.e in h&&(s=function(t){var n=t.relatedTarget;if(!n||n!==this&&!e.contains(this,n))return a.fn.apply(this,arguments)}),a.del=u;var l=u||s;a.proxy=function(e){if(!(e=S(e)).isImmediatePropagationStopped()){try{var i=Object.getOwnPropertyDescriptor(e,"data");i&&!i.writable||(e.data=r)}catch(e){}var s=l.apply(t,e._args==n?[e]:[e].concat(e._args));return!1===s&&(e.preventDefault(),e.stopPropagation()),s}},a.i=f.length,f.push(a),"addEventListener"in t&&t.addEventListener(v(a.e),a.proxy,m(a,c))}))}function w(t,e,n,i,s){var r=p(t);(e||"").split(/\s/).forEach((function(e){f(t,e,n,i).forEach((function(e){delete a[r][e.i],"removeEventListener"in t&&t.removeEventListener(v(e.e),e.proxy,m(e,s))}))}))}u.click=u.mousedown=u.mouseup=u.mousemove="MouseEvents",e.event={add:y,remove:w},e.proxy=function(t,n){var i=2 in arguments&&s.call(arguments,2);if(r(t)){var a=function(){return t.apply(n,i?i.concat(s.call(arguments)):arguments)};return a._zid=p(t),a}if(o(n))return i?(i.unshift(t[n],t),e.proxy.apply(null,i)):e.proxy(t[n],t);throw new TypeError("expected function")},e.fn.bind=function(t,e,n){return this.on(t,e,n)},e.fn.unbind=function(t,e){return this.off(t,e)},e.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var b=function(){return!0},C=function(){return!1},x=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,_={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};function S(t,i){if(i||!t.isDefaultPrevented){i||(i=t),e.each(_,(function(e,n){var s=i[e];t[e]=function(){return this[n]=b,s&&s.apply(i,arguments)},t[n]=C}));try{t.timeStamp||(t.timeStamp=Date.now())}catch(s){}(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?!1===i.returnValue:i.getPreventDefault&&i.getPreventDefault())&&(t.isDefaultPrevented=b)}return t}function E(t){var e,i={originalEvent:t};for(e in t)x.test(e)||t[e]===n||(i[e]=t[e]);return S(i,t)}e.fn.delegate=function(t,e,n){return this.on(e,t,n)},e.fn.undelegate=function(t,e,n){return this.off(e,t,n)},e.fn.live=function(t,n){return e(document.body).delegate(this.selector,t,n),this},e.fn.die=function(t,n){return e(document.body).undelegate(this.selector,t,n),this},e.fn.on=function(t,i,a,u,c){var l,h,p=this;return t&&!o(t)?(e.each(t,(function(t,e){p.on(t,i,a,e,c)})),p):(o(i)||r(u)||!1===u||(u=a,a=i,i=n),u!==n&&!1!==a||(u=a,a=n),!1===u&&(u=C),p.each((function(n,r){c&&(l=function(t){return w(r,t.type,u),u.apply(this,arguments)}),i&&(h=function(t){var n,o=e(t.target).closest(i,r).get(0);if(o&&o!==r)return n=e.extend(E(t),{currentTarget:o,liveFired:r}),(l||u).apply(o,[n].concat(s.call(arguments,1)))}),y(r,t,u,a,i,h||l)})))},e.fn.off=function(t,i,s){var a=this;return t&&!o(t)?(e.each(t,(function(t,e){a.off(t,i,e)})),a):(o(i)||r(s)||!1===s||(s=i,i=n),!1===s&&(s=C),a.each((function(){w(this,t,s,i)})))},e.fn.trigger=function(t,n){return(t=o(t)||e.isPlainObject(t)?e.Event(t):S(t))._args=n,this.each((function(){t.type in l&&"function"==typeof this[t.type]?this[t.type]():"dispatchEvent"in this?this.dispatchEvent(t):e(this).triggerHandler(t,n)}))},e.fn.triggerHandler=function(t,n){var i,s;return this.each((function(r,a){(i=E(o(t)?e.Event(t):t))._args=n,i.target=a,e.each(f(a,t.type||t),(function(t,e){if(s=e.proxy(i),i.isImmediatePropagationStopped())return!1}))})),s},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach((function(t){e.fn[t]=function(e){return 0 in arguments?this.bind(t,e):this.trigger(t)}})),e.Event=function(t,e){o(t)||(t=(e=t).type);var n=document.createEvent(u[t]||"Events"),i=!0;if(e)for(var s in e)"bubbles"==s?i=!!e[s]:n[s]=e[s];return n.initEvent(t,i,!0),S(n)}}(i),n=[],i.fn.remove=function(){return this.each((function(){this.parentNode&&("IMG"===this.tagName&&(n.push(this),this.src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",e&&clearTimeout(e),e=setTimeout((function(){n=[]}),6e4)),this.parentNode.removeChild(this))}))},function(t){var e={},n=t.fn.data,i=t.camelCase,s=t.expando="Zepto"+ +new Date,r=[];function o(r,o){var u=r[s],c=u&&e[u];if(void 0===o)return c||a(r);if(c){if(o in c)return c[o];var l=i(o);if(l in c)return c[l]}return n.call(t(r),o)}function a(n,r,o){var a=n[s]||(n[s]=++t.uuid),c=e[a]||(e[a]=u(n));return void 0!==r&&(c[i(r)]=o),c}function u(e){var n={};return t.each(e.attributes||r,(function(e,s){0==s.name.indexOf("data-")&&(n[i(s.name.replace("data-",""))]=t.zepto.deserializeValue(s.value))})),n}t.fn.data=function(e,n){return void 0===n?t.isPlainObject(e)?this.each((function(n,i){t.each(e,(function(t,e){a(i,t,e)}))})):0 in this?o(this[0],e):void 0:this.each((function(){a(this,e,n)}))},t.data=function(e,n,i){return t(e).data(n,i)},t.hasData=function(n){var i=n[s],r=i&&e[i];return!!r&&!t.isEmptyObject(r)},t.fn.removeData=function(n){return"string"==typeof n&&(n=n.split(/\s+/)),this.each((function(){var r=this[s],o=r&&e[r];o&&t.each(n||o,(function(t){delete o[n?i(this):t]}))}))},["remove","empty"].forEach((function(e){var n=t.fn[e];t.fn[e]=function(){var t=this.find("*");return"remove"===e&&(t=t.add(this)),t.removeData(),n.call(this)}}))}(i),i}(e)},8820:t=>{"use strict";var e={}.hasOwnProperty,n=/[ -,\.\/:-@\[-\^`\{-~]/,i=/[ -,\.\/:-@\[\]\^`\{-~]/,s=/(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g,r=function t(r,o){"single"!=(o=function(t,n){if(!t)return n;var i={};for(var s in n)i[s]=e.call(t,s)?t[s]:n[s];return i}(o,t.options)).quotes&&"double"!=o.quotes&&(o.quotes="single");for(var a="double"==o.quotes?'"':"'",u=o.isIdentifier,c=r.charAt(0),l="",h=0,p=r.length;h 126){if(d>=55296&&d<=56319&&h
{"use strict";var i,s,r,o=[n(5525),n(4785),n(8291),n(2709),n(2506),n(9176)],a=-1,u=[],c=!1;function l(){i&&s&&(i=!1,s.length?u=s.concat(u):a=-1,u.length&&h())}function h(){if(!i){c=!1,i=!0;for(var t=u.length,e=setTimeout(l);t;){for(s=u,u=[];s&&++a
1)for(var n=1;n {"use strict";e.test=function(){return!n.g.setImmediate&&void 0!==n.g.MessageChannel},e.install=function(t){var e=new n.g.MessageChannel;return e.port1.onmessage=t,function(){e.port2.postMessage(0)}}},8291:(t,e,n)=>{"use strict";var i=n.g.MutationObserver||n.g.WebKitMutationObserver;e.test=function(){return i},e.install=function(t){var e=0,s=new i(t),r=n.g.document.createTextNode("");return s.observe(r,{characterData:!0}),function(){r.data=e=++e%2}}},4785:(t,e,n)=>{"use strict";e.test=function(){return"function"==typeof n.g.queueMicrotask},e.install=function(t){return function(){n.g.queueMicrotask(t)}}},2506:(t,e,n)=>{"use strict";e.test=function(){return"document"in n.g&&"onreadystatechange"in n.g.document.createElement("script")},e.install=function(t){return function(){var e=n.g.document.createElement("script");return e.onreadystatechange=function(){t(),e.onreadystatechange=null,e.parentNode.removeChild(e),e=null},n.g.document.documentElement.appendChild(e),t}}},9176:(t,e)=>{"use strict";e.test=function(){return!0},e.install=function(t){return function(){setTimeout(t,0)}}}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/8443.9fbe43e3.js.LICENSE.txt b/pr-preview/pr-346/assets/js/8443.9fbe43e3.js.LICENSE.txt new file mode 100644 index 00000000..4f7ccd8a --- /dev/null +++ b/pr-preview/pr-346/assets/js/8443.9fbe43e3.js.LICENSE.txt @@ -0,0 +1 @@ +/*! https://mths.be/cssesc v3.0.0 by @mathias */ diff --git a/pr-preview/pr-346/assets/js/8800.a73f44b3.js b/pr-preview/pr-346/assets/js/8800.a73f44b3.js new file mode 100644 index 00000000..8eda09d7 --- /dev/null +++ b/pr-preview/pr-346/assets/js/8800.a73f44b3.js @@ -0,0 +1 @@ +(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[8800],{1413:(t,e,i)=>{"use strict";i.d(e,{Z:()=>Kr});var r=i(7462),n=i(7294),s=i(6010),a=i(2389),o=i(460),p=i(7410);const h={plain:{backgroundColor:"#2a2734",color:"#9a86fd"},styles:[{types:["comment","prolog","doctype","cdata","punctuation"],style:{color:"#6c6783"}},{types:["namespace"],style:{opacity:.7}},{types:["tag","operator","number"],style:{color:"#e09142"}},{types:["property","function"],style:{color:"#9a86fd"}},{types:["tag-id","selector","atrule-id"],style:{color:"#eeebff"}},{types:["attr-name"],style:{color:"#c4b9fe"}},{types:["boolean","string","entity","url","attr-value","keyword","control","directive","unit","statement","regex","atrule","placeholder","variable"],style:{color:"#ffcc99"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"#c4b9fe"}}]};var c={Prism:p.Z,theme:h};function l(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function u(){return u=Object.assign||function(t){for(var e=1;e 0&&t[i-1]===e?t:t.concat(e)},g=function(t,e){var i=t.plain,r=Object.create(null),n=t.styles.reduce((function(t,i){var r=i.languages,n=i.style;return r&&!r.includes(e)||i.types.forEach((function(e){var i=u({},t[e],n);t[e]=i})),t}),r);return n.root=i,n.plain=u({},i,{backgroundColor:null}),n};function y(t,e){var i={};for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&-1===e.indexOf(r)&&(i[r]=t[r]);return i}var v=function(t){function e(){for(var e=this,i=[],r=arguments.length;r--;)i[r]=arguments[r];t.apply(this,i),l(this,"getThemeDict",(function(t){if(void 0!==e.themeDict&&t.theme===e.prevTheme&&t.language===e.prevLanguage)return e.themeDict;e.prevTheme=t.theme,e.prevLanguage=t.language;var i=t.theme?g(t.theme,t.language):void 0;return e.themeDict=i})),l(this,"getLineProps",(function(t){var i=t.key,r=t.className,n=t.style,s=u({},y(t,["key","className","style","line"]),{className:"token-line",style:void 0,key:void 0}),a=e.getThemeDict(e.props);return void 0!==a&&(s.style=a.plain),void 0!==n&&(s.style=void 0!==s.style?u({},s.style,n):n),void 0!==i&&(s.key=i),r&&(s.className+=" "+r),s})),l(this,"getStyleForToken",(function(t){var i=t.types,r=t.empty,n=i.length,s=e.getThemeDict(e.props);if(void 0!==s){if(1===n&&"plain"===i[0])return r?{display:"inline-block"}:void 0;if(1===n&&!r)return s[i[0]];var a=r?{display:"inline-block"}:{},o=i.map((function(t){return s[t]}));return Object.assign.apply(Object,[a].concat(o))}})),l(this,"getTokenProps",(function(t){var i=t.key,r=t.className,n=t.style,s=t.token,a=u({},y(t,["key","className","style","token"]),{className:"token "+s.types.join(" "),children:s.content,style:e.getStyleForToken(s),key:void 0});return void 0!==n&&(a.style=void 0!==a.style?u({},a.style,n):n),void 0!==i&&(a.key=i),r&&(a.className+=" "+r),a})),l(this,"tokenize",(function(t,e,i,r){var n={code:e,grammar:i,language:r,tokens:[]};t.hooks.run("before-tokenize",n);var s=n.tokens=t.tokenize(n.code,n.grammar,n.language);return t.hooks.run("after-tokenize",n),s}))}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.render=function(){var t=this.props,e=t.Prism,i=t.language,r=t.code,n=t.children,s=this.getThemeDict(this.props),a=e.languages[i],o=function(t){for(var e=[[]],i=[t],r=[0],n=[t.length],s=0,a=0,o=[],p=[o];a>-1;){for(;(s=r[a]++) 0?c:["plain"],h=l):(c=m(c,l.type),l.alias&&(c=m(c,l.alias)),h=l.content),"string"==typeof h){var u=h.split(d),g=u.length;o.push({types:c,content:u[0]});for(var y=1;y t)return!1;if((i+=e[r+1])>=t)return!0}}function P(t,e){return t<65?36===t:t<91||(t<97?95===t:t<123||(t<=65535?t>=170&&C.test(String.fromCharCode(t)):!1!==e&&N(t,I)))}function T(t,e){return t<48?36===t:t<58||!(t<65)&&(t<91||(t<97?95===t:t<123||(t<=65535?t>=170&&A.test(String.fromCharCode(t)):!1!==e&&(N(t,I)||N(t,L)))))}var j=function(t,e){void 0===e&&(e={}),this.label=t,this.keyword=e.keyword,this.beforeExpr=!!e.beforeExpr,this.startsExpr=!!e.startsExpr,this.isLoop=!!e.isLoop,this.isAssign=!!e.isAssign,this.prefix=!!e.prefix,this.postfix=!!e.postfix,this.binop=e.binop||null,this.updateContext=null};function O(t,e){return new j(t,{beforeExpr:!0,binop:e})}var R={beforeExpr:!0},V={startsExpr:!0},B={};function D(t,e){return void 0===e&&(e={}),e.keyword=t,B[t]=new j(t,e)}var F={num:new j("num",V),regexp:new j("regexp",V),string:new j("string",V),name:new j("name",V),eof:new j("eof"),bracketL:new j("[",{beforeExpr:!0,startsExpr:!0}),bracketR:new j("]"),braceL:new j("{",{beforeExpr:!0,startsExpr:!0}),braceR:new j("}"),parenL:new j("(",{beforeExpr:!0,startsExpr:!0}),parenR:new j(")"),comma:new j(",",R),semi:new j(";",R),colon:new j(":",R),dot:new j("."),question:new j("?",R),arrow:new j("=>",R),template:new j("template"),invalidTemplate:new j("invalidTemplate"),ellipsis:new j("...",R),backQuote:new j("`",V),dollarBraceL:new j("${",{beforeExpr:!0,startsExpr:!0}),eq:new j("=",{beforeExpr:!0,isAssign:!0}),assign:new j("_=",{beforeExpr:!0,isAssign:!0}),incDec:new j("++/--",{prefix:!0,postfix:!0,startsExpr:!0}),prefix:new j("!/~",{beforeExpr:!0,prefix:!0,startsExpr:!0}),logicalOR:O("||",1),logicalAND:O("&&",2),bitwiseOR:O("|",3),bitwiseXOR:O("^",4),bitwiseAND:O("&",5),equality:O("==/!=/===/!==",6),relational:O(">/<=/>=",7),bitShift:O("<>>/>>>",8),plusMin:new j("+/-",{beforeExpr:!0,binop:9,prefix:!0,startsExpr:!0}),modulo:O("%",10),star:O("*",10),slash:O("/",10),starstar:new j("**",{beforeExpr:!0}),_break:D("break"),_case:D("case",R),_catch:D("catch"),_continue:D("continue"),_debugger:D("debugger"),_default:D("default",R),_do:D("do",{isLoop:!0,beforeExpr:!0}),_else:D("else",R),_finally:D("finally"),_for:D("for",{isLoop:!0}),_function:D("function",V),_if:D("if"),_return:D("return",R),_switch:D("switch"),_throw:D("throw",R),_try:D("try"),_var:D("var"),_const:D("const"),_while:D("while",{isLoop:!0}),_with:D("with"),_new:D("new",{beforeExpr:!0,startsExpr:!0}),_this:D("this",V),_super:D("super",V),_class:D("class",V),_extends:D("extends",R),_export:D("export"),_import:D("import"),_null:D("null",V),_true:D("true",V),_false:D("false",V),_in:D("in",{beforeExpr:!0,binop:7}),_instanceof:D("instanceof",{beforeExpr:!0,binop:7}),_typeof:D("typeof",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_void:D("void",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_delete:D("delete",{beforeExpr:!0,prefix:!0,startsExpr:!0})},M=/\r\n?|\n|\u2028|\u2029/,U=new RegExp(M.source,"g");function q(t,e){return 10===t||13===t||!e&&(8232===t||8233===t)}var z=/[\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]/,W=/(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g,H=Object.prototype,X=H.hasOwnProperty,J=H.toString;function K(t,e){return X.call(t,e)}var G=Array.isArray||function(t){return"[object Array]"===J.call(t)};function Z(t){return new RegExp("^(?:"+t.replace(/ /g,"|")+")$")}var $=function(t,e){this.line=t,this.column=e};$.prototype.offset=function(t){return new $(this.line,this.column+t)};var Q=function(t,e,i){this.start=e,this.end=i,null!==t.sourceFile&&(this.source=t.sourceFile)};function Y(t,e){for(var i=1,r=0;;){U.lastIndex=r;var n=U.exec(t);if(!(n&&n.index =2015&&(e.ecmaVersion-=2009),null==e.allowReserved&&(e.allowReserved=e.ecmaVersion<5),G(e.onToken)){var r=e.onToken;e.onToken=function(t){return r.push(t)}}return G(e.onComment)&&(e.onComment=function(t,e){return function(i,r,n,s,a,o){var p={type:i?"Block":"Line",value:r,start:n,end:s};t.locations&&(p.loc=new Q(this,a,o)),t.ranges&&(p.range=[n,s]),e.push(p)}}(e,e.onComment)),e}(t),this.sourceFile=t.sourceFile,this.keywords=Z(k[t.ecmaVersion>=6?6:5]);var r="";if(!t.allowReserved){for(var n=t.ecmaVersion;!(r=b[n]);n--);"module"===t.sourceType&&(r+=" await")}this.reservedWords=Z(r);var s=(r?r+" ":"")+b.strict;this.reservedWordsStrict=Z(s),this.reservedWordsStrictBind=Z(s+" "+b.strictBind),this.input=String(e),this.containsEsc=!1,i?(this.pos=i,this.lineStart=this.input.lastIndexOf("\n",i-1)+1,this.curLine=this.input.slice(0,this.lineStart).split(M).length):(this.pos=this.lineStart=0,this.curLine=1),this.type=F.eof,this.value=null,this.start=this.end=this.pos,this.startLoc=this.endLoc=this.curPosition(),this.lastTokEndLoc=this.lastTokStartLoc=null,this.lastTokStart=this.lastTokEnd=this.pos,this.context=this.initialContext(),this.exprAllowed=!0,this.inModule="module"===t.sourceType,this.strict=this.inModule||this.strictDirective(this.pos),this.potentialArrowAt=-1,this.yieldPos=this.awaitPos=this.awaitIdentPos=0,this.labels=[],this.undefinedExports={},0===this.pos&&t.allowHashBang&&"#!"===this.input.slice(0,2)&&this.skipLineComment(2),this.scopeStack=[],this.enterScope(1),this.regexpState=null},rt={inFunction:{configurable:!0},inGenerator:{configurable:!0},inAsync:{configurable:!0},allowSuper:{configurable:!0},allowDirectSuper:{configurable:!0},treatFunctionsAsVar:{configurable:!0}};it.prototype.parse=function(){var t=this.options.program||this.startNode();return this.nextToken(),this.parseTopLevel(t)},rt.inFunction.get=function(){return(2&this.currentVarScope().flags)>0},rt.inGenerator.get=function(){return(8&this.currentVarScope().flags)>0},rt.inAsync.get=function(){return(4&this.currentVarScope().flags)>0},rt.allowSuper.get=function(){return(64&this.currentThisScope().flags)>0},rt.allowDirectSuper.get=function(){return(128&this.currentThisScope().flags)>0},rt.treatFunctionsAsVar.get=function(){return this.treatFunctionsAsVarInScope(this.currentScope())},it.prototype.inNonArrowFunction=function(){return(2&this.currentThisScope().flags)>0},it.extend=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];for(var i=this,r=0;r -1&&this.raiseRecoverable(t.trailingComma,"Comma is not permitted after the rest element");var i=e?t.parenthesizedAssign:t.parenthesizedBind;i>-1&&this.raiseRecoverable(i,"Parenthesized pattern")}},nt.checkExpressionErrors=function(t,e){if(!t)return!1;var i=t.shorthandAssign,r=t.doubleProto;if(!e)return i>=0||r>=0;i>=0&&this.raise(i,"Shorthand property assignments are valid only in destructuring patterns"),r>=0&&this.raiseRecoverable(r,"Redefinition of __proto__ property")},nt.checkYieldAwaitInDefaultParams=function(){this.yieldPos&&(!this.awaitPos||this.yieldPos =6&&(t.sourceType=this.options.sourceType),this.finishNode(t,"Program")};var pt={kind:"loop"},ht={kind:"switch"};ot.isLet=function(t){if(this.options.ecmaVersion<6||!this.isContextual("let"))return!1;W.lastIndex=this.pos;var e=W.exec(this.input),i=this.pos+e[0].length,r=this.input.charCodeAt(i);if(91===r)return!0;if(t)return!1;if(123===r)return!0;if(P(r,!0)){for(var n=i+1;T(this.input.charCodeAt(n),!0);)++n;var s=this.input.slice(i,n);if(!S.test(s))return!0}return!1},ot.isAsyncFunction=function(){if(this.options.ecmaVersion<8||!this.isContextual("async"))return!1;W.lastIndex=this.pos;var t=W.exec(this.input),e=this.pos+t[0].length;return!(M.test(this.input.slice(this.pos,e))||"function"!==this.input.slice(e,e+8)||e+8!==this.input.length&&T(this.input.charAt(e+8)))},ot.parseStatement=function(t,e,i){var r,n=this.type,s=this.startNode();switch(this.isLet(t)&&(n=F._var,r="let"),n){case F._break:case F._continue:return this.parseBreakContinueStatement(s,n.keyword);case F._debugger:return this.parseDebuggerStatement(s);case F._do:return this.parseDoStatement(s);case F._for:return this.parseForStatement(s);case F._function:return t&&(this.strict||"if"!==t&&"label"!==t)&&this.options.ecmaVersion>=6&&this.unexpected(),this.parseFunctionStatement(s,!1,!t);case F._class:return t&&this.unexpected(),this.parseClass(s,!0);case F._if:return this.parseIfStatement(s);case F._return:return this.parseReturnStatement(s);case F._switch:return this.parseSwitchStatement(s);case F._throw:return this.parseThrowStatement(s);case F._try:return this.parseTryStatement(s);case F._const:case F._var:return r=r||this.value,t&&"var"!==r&&this.unexpected(),this.parseVarStatement(s,r);case F._while:return this.parseWhileStatement(s);case F._with:return this.parseWithStatement(s);case F.braceL:return this.parseBlock(!0,s);case F.semi:return this.parseEmptyStatement(s);case F._export:case F._import:return this.options.allowImportExportEverywhere||(e||this.raise(this.start,"'import' and 'export' may only appear at the top level"),this.inModule||this.raise(this.start,"'import' and 'export' may appear only with 'sourceType: module'")),n===F._import?this.parseImport(s):this.parseExport(s,i);default:if(this.isAsyncFunction())return t&&this.unexpected(),this.next(),this.parseFunctionStatement(s,!0,!t);var a=this.value,o=this.parseExpression();return n===F.name&&"Identifier"===o.type&&this.eat(F.colon)?this.parseLabeledStatement(s,a,o,t):this.parseExpressionStatement(s,o)}},ot.parseBreakContinueStatement=function(t,e){var i="break"===e;this.next(),this.eat(F.semi)||this.insertSemicolon()?t.label=null:this.type!==F.name?this.unexpected():(t.label=this.parseIdent(),this.semicolon());for(var r=0;r =6?this.eat(F.semi):this.semicolon(),this.finishNode(t,"DoWhileStatement")},ot.parseForStatement=function(t){this.next();var e=this.options.ecmaVersion>=9&&(this.inAsync||!this.inFunction&&this.options.allowAwaitOutsideFunction)&&this.eatContextual("await")?this.lastTokStart:-1;if(this.labels.push(pt),this.enterScope(0),this.expect(F.parenL),this.type===F.semi)return e>-1&&this.unexpected(e),this.parseFor(t,null);var i=this.isLet();if(this.type===F._var||this.type===F._const||i){var r=this.startNode(),n=i?"let":this.value;return this.next(),this.parseVar(r,!0,n),this.finishNode(r,"VariableDeclaration"),!(this.type===F._in||this.options.ecmaVersion>=6&&this.isContextual("of"))||1!==r.declarations.length||"var"!==n&&r.declarations[0].init?(e>-1&&this.unexpected(e),this.parseFor(t,r)):(this.options.ecmaVersion>=9&&(this.type===F._in?e>-1&&this.unexpected(e):t.await=e>-1),this.parseForIn(t,r))}var s=new at,a=this.parseExpression(!0,s);return this.type===F._in||this.options.ecmaVersion>=6&&this.isContextual("of")?(this.options.ecmaVersion>=9&&(this.type===F._in?e>-1&&this.unexpected(e):t.await=e>-1),this.toAssignable(a,!1,s),this.checkLVal(a),this.parseForIn(t,a)):(this.checkExpressionErrors(s,!0),e>-1&&this.unexpected(e),this.parseFor(t,a))},ot.parseFunctionStatement=function(t,e,i){return this.next(),this.parseFunction(t,lt|(i?0:ut),!1,e)},ot.parseIfStatement=function(t){return this.next(),t.test=this.parseParenExpression(),t.consequent=this.parseStatement("if"),t.alternate=this.eat(F._else)?this.parseStatement("if"):null,this.finishNode(t,"IfStatement")},ot.parseReturnStatement=function(t){return this.inFunction||this.options.allowReturnOutsideFunction||this.raise(this.start,"'return' outside of function"),this.next(),this.eat(F.semi)||this.insertSemicolon()?t.argument=null:(t.argument=this.parseExpression(),this.semicolon()),this.finishNode(t,"ReturnStatement")},ot.parseSwitchStatement=function(t){var e;this.next(),t.discriminant=this.parseParenExpression(),t.cases=[],this.expect(F.braceL),this.labels.push(ht),this.enterScope(0);for(var i=!1;this.type!==F.braceR;)if(this.type===F._case||this.type===F._default){var r=this.type===F._case;e&&this.finishNode(e,"SwitchCase"),t.cases.push(e=this.startNode()),e.consequent=[],this.next(),r?e.test=this.parseExpression():(i&&this.raiseRecoverable(this.lastTokStart,"Multiple default clauses"),i=!0,e.test=null),this.expect(F.colon)}else e||this.unexpected(),e.consequent.push(this.parseStatement(null));return this.exitScope(),e&&this.finishNode(e,"SwitchCase"),this.next(),this.labels.pop(),this.finishNode(t,"SwitchStatement")},ot.parseThrowStatement=function(t){return this.next(),M.test(this.input.slice(this.lastTokEnd,this.start))&&this.raise(this.lastTokEnd,"Illegal newline after throw"),t.argument=this.parseExpression(),this.semicolon(),this.finishNode(t,"ThrowStatement")};var ct=[];ot.parseTryStatement=function(t){if(this.next(),t.block=this.parseBlock(),t.handler=null,this.type===F._catch){var e=this.startNode();if(this.next(),this.eat(F.parenL)){e.param=this.parseBindingAtom();var i="Identifier"===e.param.type;this.enterScope(i?32:0),this.checkLVal(e.param,i?4:2),this.expect(F.parenR)}else this.options.ecmaVersion<10&&this.unexpected(),e.param=null,this.enterScope(0);e.body=this.parseBlock(!1),this.exitScope(),t.handler=this.finishNode(e,"CatchClause")}return t.finalizer=this.eat(F._finally)?this.parseBlock():null,t.handler||t.finalizer||this.raise(t.start,"Missing catch or finally clause"),this.finishNode(t,"TryStatement")},ot.parseVarStatement=function(t,e){return this.next(),this.parseVar(t,!1,e),this.semicolon(),this.finishNode(t,"VariableDeclaration")},ot.parseWhileStatement=function(t){return this.next(),t.test=this.parseParenExpression(),this.labels.push(pt),t.body=this.parseStatement("while"),this.labels.pop(),this.finishNode(t,"WhileStatement")},ot.parseWithStatement=function(t){return this.strict&&this.raise(this.start,"'with' in strict mode"),this.next(),t.object=this.parseParenExpression(),t.body=this.parseStatement("with"),this.finishNode(t,"WithStatement")},ot.parseEmptyStatement=function(t){return this.next(),this.finishNode(t,"EmptyStatement")},ot.parseLabeledStatement=function(t,e,i,r){for(var n=0,s=this.labels;n =0;o--){var p=this.labels[o];if(p.statementStart!==t.start)break;p.statementStart=this.start,p.kind=a}return this.labels.push({name:e,kind:a,statementStart:this.start}),t.body=this.parseStatement(r?-1===r.indexOf("label")?r+"label":r:"label"),this.labels.pop(),t.label=i,this.finishNode(t,"LabeledStatement")},ot.parseExpressionStatement=function(t,e){return t.expression=e,this.semicolon(),this.finishNode(t,"ExpressionStatement")},ot.parseBlock=function(t,e){for(void 0===t&&(t=!0),void 0===e&&(e=this.startNode()),e.body=[],this.expect(F.braceL),t&&this.enterScope(0);!this.eat(F.braceR);){var i=this.parseStatement(null);e.body.push(i)}return t&&this.exitScope(),this.finishNode(e,"BlockStatement")},ot.parseFor=function(t,e){return t.init=e,this.expect(F.semi),t.test=this.type===F.semi?null:this.parseExpression(),this.expect(F.semi),t.update=this.type===F.parenR?null:this.parseExpression(),this.expect(F.parenR),t.body=this.parseStatement("for"),this.exitScope(),this.labels.pop(),this.finishNode(t,"ForStatement")},ot.parseForIn=function(t,e){var i=this.type===F._in?"ForInStatement":"ForOfStatement";return this.next(),"ForInStatement"===i&&("AssignmentPattern"===e.type||"VariableDeclaration"===e.type&&null!=e.declarations[0].init&&(this.strict||"Identifier"!==e.declarations[0].id.type))&&this.raise(e.start,"Invalid assignment in for-in loop head"),t.left=e,t.right="ForInStatement"===i?this.parseExpression():this.parseMaybeAssign(),this.expect(F.parenR),t.body=this.parseStatement("for"),this.exitScope(),this.labels.pop(),this.finishNode(t,i)},ot.parseVar=function(t,e,i){for(t.declarations=[],t.kind=i;;){var r=this.startNode();if(this.parseVarId(r,i),this.eat(F.eq)?r.init=this.parseMaybeAssign(e):"const"!==i||this.type===F._in||this.options.ecmaVersion>=6&&this.isContextual("of")?"Identifier"===r.id.type||e&&(this.type===F._in||this.isContextual("of"))?r.init=null:this.raise(this.lastTokEnd,"Complex binding patterns require an initialization value"):this.unexpected(),t.declarations.push(this.finishNode(r,"VariableDeclarator")),!this.eat(F.comma))break}return t},ot.parseVarId=function(t,e){"const"!==e&&"let"!==e||!this.isContextual("let")||this.raiseRecoverable(this.start,"let is disallowed as a lexically bound name"),t.id=this.parseBindingAtom(),this.checkLVal(t.id,"var"===e?1:2,!1)};var lt=1,ut=2;ot.parseFunction=function(t,e,i,r){this.initFunction(t),(this.options.ecmaVersion>=9||this.options.ecmaVersion>=6&&!r)&&(this.type===F.star&&e&ut&&this.unexpected(),t.generator=this.eat(F.star)),this.options.ecmaVersion>=8&&(t.async=!!r),e<&&(t.id=4&e&&this.type!==F.name?null:this.parseIdent(),!t.id||e&ut||this.checkLVal(t.id,this.strict||t.generator||t.async?this.treatFunctionsAsVar?1:2:3));var n=this.yieldPos,s=this.awaitPos,a=this.awaitIdentPos;return this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,this.enterScope(et(t.async,t.generator)),e<||(t.id=this.type===F.name?this.parseIdent():null),this.parseFunctionParams(t),this.parseFunctionBody(t,i,!1),this.yieldPos=n,this.awaitPos=s,this.awaitIdentPos=a,this.finishNode(t,e<?"FunctionDeclaration":"FunctionExpression")},ot.parseFunctionParams=function(t){this.expect(F.parenL),t.params=this.parseBindingList(F.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams()},ot.parseClass=function(t,e){this.next();var i=this.strict;this.strict=!0,this.parseClassId(t,e),this.parseClassSuper(t);var r=this.startNode(),n=!1;for(r.body=[],this.expect(F.braceL);!this.eat(F.braceR);){var s=this.parseClassElement(null!==t.superClass);s&&(r.body.push(s),"MethodDefinition"===s.type&&"constructor"===s.kind&&(n&&this.raise(s.start,"Duplicate constructor in the same class"),n=!0))}return t.body=this.finishNode(r,"ClassBody"),this.strict=i,this.finishNode(t,e?"ClassDeclaration":"ClassExpression")},ot.parseClassElement=function(t){var e=this;if(this.eat(F.semi))return null;var i=this.startNode(),r=function(t,r){void 0===r&&(r=!1);var n=e.start,s=e.startLoc;return!(!e.eatContextual(t)||(e.type===F.parenL||r&&e.canInsertSemicolon())&&(i.key&&e.unexpected(),i.computed=!1,i.key=e.startNodeAt(n,s),i.key.name=t,e.finishNode(i.key,"Identifier"),1))};i.kind="method",i.static=r("static");var n=this.eat(F.star),s=!1;n||(this.options.ecmaVersion>=8&&r("async",!0)?(s=!0,n=this.options.ecmaVersion>=9&&this.eat(F.star)):r("get")?i.kind="get":r("set")&&(i.kind="set")),i.key||this.parsePropertyName(i);var a=i.key,o=!1;return i.computed||i.static||!("Identifier"===a.type&&"constructor"===a.name||"Literal"===a.type&&"constructor"===a.value)?i.static&&"Identifier"===a.type&&"prototype"===a.name&&this.raise(a.start,"Classes may not have a static property named prototype"):("method"!==i.kind&&this.raise(a.start,"Constructor can't have get/set modifier"),n&&this.raise(a.start,"Constructor can't be a generator"),s&&this.raise(a.start,"Constructor can't be an async method"),i.kind="constructor",o=t),this.parseClassMethod(i,n,s,o),"get"===i.kind&&0!==i.value.params.length&&this.raiseRecoverable(i.value.start,"getter should have no params"),"set"===i.kind&&1!==i.value.params.length&&this.raiseRecoverable(i.value.start,"setter should have exactly one param"),"set"===i.kind&&"RestElement"===i.value.params[0].type&&this.raiseRecoverable(i.value.params[0].start,"Setter cannot use rest params"),i},ot.parseClassMethod=function(t,e,i,r){return t.value=this.parseMethod(e,i,r),this.finishNode(t,"MethodDefinition")},ot.parseClassId=function(t,e){this.type===F.name?(t.id=this.parseIdent(),e&&this.checkLVal(t.id,2,!1)):(!0===e&&this.unexpected(),t.id=null)},ot.parseClassSuper=function(t){t.superClass=this.eat(F._extends)?this.parseExprSubscripts():null},ot.parseExport=function(t,e){if(this.next(),this.eat(F.star))return this.expectContextual("from"),this.type!==F.string&&this.unexpected(),t.source=this.parseExprAtom(),this.semicolon(),this.finishNode(t,"ExportAllDeclaration");if(this.eat(F._default)){var i;if(this.checkExport(e,"default",this.lastTokStart),this.type===F._function||(i=this.isAsyncFunction())){var r=this.startNode();this.next(),i&&this.next(),t.declaration=this.parseFunction(r,4|lt,!1,i)}else if(this.type===F._class){var n=this.startNode();t.declaration=this.parseClass(n,"nullableID")}else t.declaration=this.parseMaybeAssign(),this.semicolon();return this.finishNode(t,"ExportDefaultDeclaration")}if(this.shouldParseExportStatement())t.declaration=this.parseStatement(null),"VariableDeclaration"===t.declaration.type?this.checkVariableExport(e,t.declaration.declarations):this.checkExport(e,t.declaration.id.name,t.declaration.id.start),t.specifiers=[],t.source=null;else{if(t.declaration=null,t.specifiers=this.parseExportSpecifiers(e),this.eatContextual("from"))this.type!==F.string&&this.unexpected(),t.source=this.parseExprAtom();else{for(var s=0,a=t.specifiers;s =6&&t)switch(t.type){case"Identifier":this.inAsync&&"await"===t.name&&this.raise(t.start,"Cannot use 'await' as identifier inside an async function");break;case"ObjectPattern":case"ArrayPattern":case"RestElement":break;case"ObjectExpression":t.type="ObjectPattern",i&&this.checkPatternErrors(i,!0);for(var r=0,n=t.properties;r =6)switch(this.type){case F.bracketL:var t=this.startNode();return this.next(),t.elements=this.parseBindingList(F.bracketR,!0,!0),this.finishNode(t,"ArrayPattern");case F.braceL:return this.parseObj(!0)}return this.parseIdent()},dt.parseBindingList=function(t,e,i){for(var r=[],n=!0;!this.eat(t);)if(n?n=!1:this.expect(F.comma),e&&this.type===F.comma)r.push(null);else{if(i&&this.afterTrailingComma(t))break;if(this.type===F.ellipsis){var s=this.parseRestBinding();this.parseBindingListItem(s),r.push(s),this.type===F.comma&&this.raise(this.start,"Comma is not permitted after the rest element"),this.expect(t);break}var a=this.parseMaybeDefault(this.start,this.startLoc);this.parseBindingListItem(a),r.push(a)}return r},dt.parseBindingListItem=function(t){return t},dt.parseMaybeDefault=function(t,e,i){if(i=i||this.parseBindingAtom(),this.options.ecmaVersion<6||!this.eat(F.eq))return i;var r=this.startNodeAt(t,e);return r.left=i,r.right=this.parseMaybeAssign(),this.finishNode(r,"AssignmentPattern")},dt.checkLVal=function(t,e,i){switch(void 0===e&&(e=0),t.type){case"Identifier":this.strict&&this.reservedWordsStrictBind.test(t.name)&&this.raiseRecoverable(t.start,(e?"Binding ":"Assigning to ")+t.name+" in strict mode"),i&&(K(i,t.name)&&this.raiseRecoverable(t.start,"Argument name clash"),i[t.name]=!0),0!==e&&5!==e&&this.declareName(t.name,e,t.start);break;case"MemberExpression":e&&this.raiseRecoverable(t.start,"Binding member expression");break;case"ObjectPattern":for(var r=0,n=t.properties;r =9&&"SpreadElement"===t.type||this.options.ecmaVersion>=6&&(t.computed||t.method||t.shorthand))){var r,n=t.key;switch(n.type){case"Identifier":r=n.name;break;case"Literal":r=String(n.value);break;default:return}var s=t.kind;if(this.options.ecmaVersion>=6)"__proto__"===r&&"init"===s&&(e.proto&&(i&&i.doubleProto<0?i.doubleProto=n.start:this.raiseRecoverable(n.start,"Redefinition of __proto__ property")),e.proto=!0);else{var a=e[r="$"+r];a?("init"===s?this.strict&&a.init||a.get||a.set:a.init||a[s])&&this.raiseRecoverable(n.start,"Redefinition of property"):a=e[r]={init:!1,get:!1,set:!1},a[s]=!0}}},ft.parseExpression=function(t,e){var i=this.start,r=this.startLoc,n=this.parseMaybeAssign(t,e);if(this.type===F.comma){var s=this.startNodeAt(i,r);for(s.expressions=[n];this.eat(F.comma);)s.expressions.push(this.parseMaybeAssign(t,e));return this.finishNode(s,"SequenceExpression")}return n},ft.parseMaybeAssign=function(t,e,i){if(this.isContextual("yield")){if(this.inGenerator)return this.parseYield(t);this.exprAllowed=!1}var r=!1,n=-1,s=-1,a=-1;e?(n=e.parenthesizedAssign,s=e.trailingComma,a=e.shorthandAssign,e.parenthesizedAssign=e.trailingComma=e.shorthandAssign=-1):(e=new at,r=!0);var o=this.start,p=this.startLoc;this.type!==F.parenL&&this.type!==F.name||(this.potentialArrowAt=this.start);var h=this.parseMaybeConditional(t,e);if(i&&(h=i.call(this,h,o,p)),this.type.isAssign){var c=this.startNodeAt(o,p);return c.operator=this.value,c.left=this.type===F.eq?this.toAssignable(h,!1,e):h,r||at.call(e),e.shorthandAssign=-1,this.checkLVal(h),this.next(),c.right=this.parseMaybeAssign(t),this.finishNode(c,"AssignmentExpression")}return r&&this.checkExpressionErrors(e,!0),n>-1&&(e.parenthesizedAssign=n),s>-1&&(e.trailingComma=s),a>-1&&(e.shorthandAssign=a),h},ft.parseMaybeConditional=function(t,e){var i=this.start,r=this.startLoc,n=this.parseExprOps(t,e);if(this.checkExpressionErrors(e))return n;if(this.eat(F.question)){var s=this.startNodeAt(i,r);return s.test=n,s.consequent=this.parseMaybeAssign(),this.expect(F.colon),s.alternate=this.parseMaybeAssign(t),this.finishNode(s,"ConditionalExpression")}return n},ft.parseExprOps=function(t,e){var i=this.start,r=this.startLoc,n=this.parseMaybeUnary(e,!1);return this.checkExpressionErrors(e)||n.start===i&&"ArrowFunctionExpression"===n.type?n:this.parseExprOp(n,i,r,-1,t)},ft.parseExprOp=function(t,e,i,r,n){var s=this.type.binop;if(null!=s&&(!n||this.type!==F._in)&&s>r){var a=this.type===F.logicalOR||this.type===F.logicalAND,o=this.value;this.next();var p=this.start,h=this.startLoc,c=this.parseExprOp(this.parseMaybeUnary(null,!1),p,h,s,n),l=this.buildBinary(e,i,t,c,o,a);return this.parseExprOp(l,e,i,r,n)}return t},ft.buildBinary=function(t,e,i,r,n,s){var a=this.startNodeAt(t,e);return a.left=i,a.operator=n,a.right=r,this.finishNode(a,s?"LogicalExpression":"BinaryExpression")},ft.parseMaybeUnary=function(t,e){var i,r=this.start,n=this.startLoc;if(this.isContextual("await")&&(this.inAsync||!this.inFunction&&this.options.allowAwaitOutsideFunction))i=this.parseAwait(),e=!0;else if(this.type.prefix){var s=this.startNode(),a=this.type===F.incDec;s.operator=this.value,s.prefix=!0,this.next(),s.argument=this.parseMaybeUnary(null,!0),this.checkExpressionErrors(t,!0),a?this.checkLVal(s.argument):this.strict&&"delete"===s.operator&&"Identifier"===s.argument.type?this.raiseRecoverable(s.start,"Deleting local variable in strict mode"):e=!0,i=this.finishNode(s,a?"UpdateExpression":"UnaryExpression")}else{if(i=this.parseExprSubscripts(t),this.checkExpressionErrors(t))return i;for(;this.type.postfix&&!this.canInsertSemicolon();){var o=this.startNodeAt(r,n);o.operator=this.value,o.prefix=!1,o.argument=i,this.checkLVal(i),this.next(),i=this.finishNode(o,"UpdateExpression")}}return!e&&this.eat(F.starstar)?this.buildBinary(r,n,i,this.parseMaybeUnary(null,!1),"**",!1):i},ft.parseExprSubscripts=function(t){var e=this.start,i=this.startLoc,r=this.parseExprAtom(t),n="ArrowFunctionExpression"===r.type&&")"!==this.input.slice(this.lastTokStart,this.lastTokEnd);if(this.checkExpressionErrors(t)||n)return r;var s=this.parseSubscripts(r,e,i);return t&&"MemberExpression"===s.type&&(t.parenthesizedAssign>=s.start&&(t.parenthesizedAssign=-1),t.parenthesizedBind>=s.start&&(t.parenthesizedBind=-1)),s},ft.parseSubscripts=function(t,e,i,r){for(var n=this.options.ecmaVersion>=8&&"Identifier"===t.type&&"async"===t.name&&this.lastTokEnd===t.end&&!this.canInsertSemicolon()&&"async"===this.input.slice(t.start,t.end);;){var s=this.parseSubscript(t,e,i,r,n);if(s===t||"ArrowFunctionExpression"===s.type)return s;t=s}},ft.parseSubscript=function(t,e,i,r,n){var s=this.eat(F.bracketL);if(s||this.eat(F.dot)){var a=this.startNodeAt(e,i);a.object=t,a.property=s?this.parseExpression():this.parseIdent(!0),a.computed=!!s,s&&this.expect(F.bracketR),t=this.finishNode(a,"MemberExpression")}else if(!r&&this.eat(F.parenL)){var o=new at,p=this.yieldPos,h=this.awaitPos,c=this.awaitIdentPos;this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0;var l=this.parseExprList(F.parenR,this.options.ecmaVersion>=8,!1,o);if(n&&!this.canInsertSemicolon()&&this.eat(F.arrow))return this.checkPatternErrors(o,!1),this.checkYieldAwaitInDefaultParams(),this.awaitIdentPos>0&&this.raise(this.awaitIdentPos,"Cannot use 'await' as identifier inside an async function"),this.yieldPos=p,this.awaitPos=h,this.awaitIdentPos=c,this.parseArrowExpression(this.startNodeAt(e,i),l,!0);this.checkExpressionErrors(o,!0),this.yieldPos=p||this.yieldPos,this.awaitPos=h||this.awaitPos,this.awaitIdentPos=c||this.awaitIdentPos;var u=this.startNodeAt(e,i);u.callee=t,u.arguments=l,t=this.finishNode(u,"CallExpression")}else if(this.type===F.backQuote){var d=this.startNodeAt(e,i);d.tag=t,d.quasi=this.parseTemplate({isTagged:!0}),t=this.finishNode(d,"TaggedTemplateExpression")}return t},ft.parseExprAtom=function(t){this.type===F.slash&&this.readRegexp();var e,i=this.potentialArrowAt===this.start;switch(this.type){case F._super:return this.allowSuper||this.raise(this.start,"'super' keyword outside a method"),e=this.startNode(),this.next(),this.type!==F.parenL||this.allowDirectSuper||this.raise(e.start,"super() call outside constructor of a subclass"),this.type!==F.dot&&this.type!==F.bracketL&&this.type!==F.parenL&&this.unexpected(),this.finishNode(e,"Super");case F._this:return e=this.startNode(),this.next(),this.finishNode(e,"ThisExpression");case F.name:var r=this.start,n=this.startLoc,s=this.containsEsc,a=this.parseIdent(!1);if(this.options.ecmaVersion>=8&&!s&&"async"===a.name&&!this.canInsertSemicolon()&&this.eat(F._function))return this.parseFunction(this.startNodeAt(r,n),0,!1,!0);if(i&&!this.canInsertSemicolon()){if(this.eat(F.arrow))return this.parseArrowExpression(this.startNodeAt(r,n),[a],!1);if(this.options.ecmaVersion>=8&&"async"===a.name&&this.type===F.name&&!s)return a=this.parseIdent(!1),!this.canInsertSemicolon()&&this.eat(F.arrow)||this.unexpected(),this.parseArrowExpression(this.startNodeAt(r,n),[a],!0)}return a;case F.regexp:var o=this.value;return(e=this.parseLiteral(o.value)).regex={pattern:o.pattern,flags:o.flags},e;case F.num:case F.string:return this.parseLiteral(this.value);case F._null:case F._true:case F._false:return(e=this.startNode()).value=this.type===F._null?null:this.type===F._true,e.raw=this.type.keyword,this.next(),this.finishNode(e,"Literal");case F.parenL:var p=this.start,h=this.parseParenAndDistinguishExpression(i);return t&&(t.parenthesizedAssign<0&&!this.isSimpleAssignTarget(h)&&(t.parenthesizedAssign=p),t.parenthesizedBind<0&&(t.parenthesizedBind=p)),h;case F.bracketL:return e=this.startNode(),this.next(),e.elements=this.parseExprList(F.bracketR,!0,!0,t),this.finishNode(e,"ArrayExpression");case F.braceL:return this.parseObj(!1,t);case F._function:return e=this.startNode(),this.next(),this.parseFunction(e,0);case F._class:return this.parseClass(this.startNode(),!1);case F._new:return this.parseNew();case F.backQuote:return this.parseTemplate();default:this.unexpected()}},ft.parseLiteral=function(t){var e=this.startNode();return e.value=t,e.raw=this.input.slice(this.start,this.end),this.next(),this.finishNode(e,"Literal")},ft.parseParenExpression=function(){this.expect(F.parenL);var t=this.parseExpression();return this.expect(F.parenR),t},ft.parseParenAndDistinguishExpression=function(t){var e,i=this.start,r=this.startLoc,n=this.options.ecmaVersion>=8;if(this.options.ecmaVersion>=6){this.next();var s,a=this.start,o=this.startLoc,p=[],h=!0,c=!1,l=new at,u=this.yieldPos,d=this.awaitPos;for(this.yieldPos=0,this.awaitPos=0;this.type!==F.parenR;){if(h?h=!1:this.expect(F.comma),n&&this.afterTrailingComma(F.parenR,!0)){c=!0;break}if(this.type===F.ellipsis){s=this.start,p.push(this.parseParenItem(this.parseRestBinding())),this.type===F.comma&&this.raise(this.start,"Comma is not permitted after the rest element");break}p.push(this.parseMaybeAssign(!1,l,this.parseParenItem))}var f=this.start,m=this.startLoc;if(this.expect(F.parenR),t&&!this.canInsertSemicolon()&&this.eat(F.arrow))return this.checkPatternErrors(l,!1),this.checkYieldAwaitInDefaultParams(),this.yieldPos=u,this.awaitPos=d,this.parseParenArrowList(i,r,p);p.length&&!c||this.unexpected(this.lastTokStart),s&&this.unexpected(s),this.checkExpressionErrors(l,!0),this.yieldPos=u||this.yieldPos,this.awaitPos=d||this.awaitPos,p.length>1?((e=this.startNodeAt(a,o)).expressions=p,this.finishNodeAt(e,"SequenceExpression",f,m)):e=p[0]}else e=this.parseParenExpression();if(this.options.preserveParens){var g=this.startNodeAt(i,r);return g.expression=e,this.finishNode(g,"ParenthesizedExpression")}return e},ft.parseParenItem=function(t){return t},ft.parseParenArrowList=function(t,e,i){return this.parseArrowExpression(this.startNodeAt(t,e),i)};var mt=[];ft.parseNew=function(){var t=this.startNode(),e=this.parseIdent(!0);if(this.options.ecmaVersion>=6&&this.eat(F.dot)){t.meta=e;var i=this.containsEsc;return t.property=this.parseIdent(!0),("target"!==t.property.name||i)&&this.raiseRecoverable(t.property.start,"The only valid meta property for new is new.target"),this.inNonArrowFunction()||this.raiseRecoverable(t.start,"new.target can only be used in functions"),this.finishNode(t,"MetaProperty")}var r=this.start,n=this.startLoc;return t.callee=this.parseSubscripts(this.parseExprAtom(),r,n,!0),t.arguments=this.eat(F.parenL)?this.parseExprList(F.parenR,this.options.ecmaVersion>=8,!1):mt,this.finishNode(t,"NewExpression")},ft.parseTemplateElement=function(t){var e=t.isTagged,i=this.startNode();return this.type===F.invalidTemplate?(e||this.raiseRecoverable(this.start,"Bad escape sequence in untagged template literal"),i.value={raw:this.value,cooked:null}):i.value={raw:this.input.slice(this.start,this.end).replace(/\r\n?/g,"\n"),cooked:this.value},this.next(),i.tail=this.type===F.backQuote,this.finishNode(i,"TemplateElement")},ft.parseTemplate=function(t){void 0===t&&(t={});var e=t.isTagged;void 0===e&&(e=!1);var i=this.startNode();this.next(),i.expressions=[];var r=this.parseTemplateElement({isTagged:e});for(i.quasis=[r];!r.tail;)this.type===F.eof&&this.raise(this.pos,"Unterminated template literal"),this.expect(F.dollarBraceL),i.expressions.push(this.parseExpression()),this.expect(F.braceR),i.quasis.push(r=this.parseTemplateElement({isTagged:e}));return this.next(),this.finishNode(i,"TemplateLiteral")},ft.isAsyncProp=function(t){return!t.computed&&"Identifier"===t.key.type&&"async"===t.key.name&&(this.type===F.name||this.type===F.num||this.type===F.string||this.type===F.bracketL||this.type.keyword||this.options.ecmaVersion>=9&&this.type===F.star)&&!M.test(this.input.slice(this.lastTokEnd,this.start))},ft.parseObj=function(t,e){var i=this.startNode(),r=!0,n={};for(i.properties=[],this.next();!this.eat(F.braceR);){if(r)r=!1;else if(this.expect(F.comma),this.afterTrailingComma(F.braceR))break;var s=this.parseProperty(t,e);t||this.checkPropClash(s,n,e),i.properties.push(s)}return this.finishNode(i,t?"ObjectPattern":"ObjectExpression")},ft.parseProperty=function(t,e){var i,r,n,s,a=this.startNode();if(this.options.ecmaVersion>=9&&this.eat(F.ellipsis))return t?(a.argument=this.parseIdent(!1),this.type===F.comma&&this.raise(this.start,"Comma is not permitted after the rest element"),this.finishNode(a,"RestElement")):(this.type===F.parenL&&e&&(e.parenthesizedAssign<0&&(e.parenthesizedAssign=this.start),e.parenthesizedBind<0&&(e.parenthesizedBind=this.start)),a.argument=this.parseMaybeAssign(!1,e),this.type===F.comma&&e&&e.trailingComma<0&&(e.trailingComma=this.start),this.finishNode(a,"SpreadElement"));this.options.ecmaVersion>=6&&(a.method=!1,a.shorthand=!1,(t||e)&&(n=this.start,s=this.startLoc),t||(i=this.eat(F.star)));var o=this.containsEsc;return this.parsePropertyName(a),!t&&!o&&this.options.ecmaVersion>=8&&!i&&this.isAsyncProp(a)?(r=!0,i=this.options.ecmaVersion>=9&&this.eat(F.star),this.parsePropertyName(a,e)):r=!1,this.parsePropertyValue(a,t,i,r,n,s,e,o),this.finishNode(a,"Property")},ft.parsePropertyValue=function(t,e,i,r,n,s,a,o){(i||r)&&this.type===F.colon&&this.unexpected(),this.eat(F.colon)?(t.value=e?this.parseMaybeDefault(this.start,this.startLoc):this.parseMaybeAssign(!1,a),t.kind="init"):this.options.ecmaVersion>=6&&this.type===F.parenL?(e&&this.unexpected(),t.kind="init",t.method=!0,t.value=this.parseMethod(i,r)):e||o||!(this.options.ecmaVersion>=5)||t.computed||"Identifier"!==t.key.type||"get"!==t.key.name&&"set"!==t.key.name||this.type===F.comma||this.type===F.braceR?this.options.ecmaVersion>=6&&!t.computed&&"Identifier"===t.key.type?((i||r)&&this.unexpected(),this.checkUnreserved(t.key),"await"!==t.key.name||this.awaitIdentPos||(this.awaitIdentPos=n),t.kind="init",e?t.value=this.parseMaybeDefault(n,s,t.key):this.type===F.eq&&a?(a.shorthandAssign<0&&(a.shorthandAssign=this.start),t.value=this.parseMaybeDefault(n,s,t.key)):t.value=t.key,t.shorthand=!0):this.unexpected():((i||r)&&this.unexpected(),t.kind=t.key.name,this.parsePropertyName(t),t.value=this.parseMethod(!1),t.value.params.length!==("get"===t.kind?0:1)?this.raiseRecoverable(t.value.start,"get"===t.kind?"getter should have no params":"setter should have exactly one param"):"set"===t.kind&&"RestElement"===t.value.params[0].type&&this.raiseRecoverable(t.value.params[0].start,"Setter cannot use rest params"))},ft.parsePropertyName=function(t){if(this.options.ecmaVersion>=6){if(this.eat(F.bracketL))return t.computed=!0,t.key=this.parseMaybeAssign(),this.expect(F.bracketR),t.key;t.computed=!1}return t.key=this.type===F.num||this.type===F.string?this.parseExprAtom():this.parseIdent(!0)},ft.initFunction=function(t){t.id=null,this.options.ecmaVersion>=6&&(t.generator=t.expression=!1),this.options.ecmaVersion>=8&&(t.async=!1)},ft.parseMethod=function(t,e,i){var r=this.startNode(),n=this.yieldPos,s=this.awaitPos,a=this.awaitIdentPos;return this.initFunction(r),this.options.ecmaVersion>=6&&(r.generator=t),this.options.ecmaVersion>=8&&(r.async=!!e),this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,this.enterScope(64|et(e,r.generator)|(i?128:0)),this.expect(F.parenL),r.params=this.parseBindingList(F.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams(),this.parseFunctionBody(r,!1,!0),this.yieldPos=n,this.awaitPos=s,this.awaitIdentPos=a,this.finishNode(r,"FunctionExpression")},ft.parseArrowExpression=function(t,e,i){var r=this.yieldPos,n=this.awaitPos,s=this.awaitIdentPos;return this.enterScope(16|et(i,!1)),this.initFunction(t),this.options.ecmaVersion>=8&&(t.async=!!i),this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,t.params=this.toAssignableList(e,!0),this.parseFunctionBody(t,!0,!1),this.yieldPos=r,this.awaitPos=n,this.awaitIdentPos=s,this.finishNode(t,"ArrowFunctionExpression")},ft.parseFunctionBody=function(t,e,i){var r=this.strict,n=!1;if(e&&this.type!==F.braceL)t.body=this.parseMaybeAssign(),t.expression=!0,this.checkParams(t,!1);else{var s=this.options.ecmaVersion>=7&&!this.isSimpleParamList(t.params);r&&!s||(n=this.strictDirective(this.end))&&s&&this.raiseRecoverable(t.start,"Illegal 'use strict' directive in function with non-simple parameter list");var a=this.labels;this.labels=[],n&&(this.strict=!0),this.checkParams(t,!r&&!n&&!e&&!i&&this.isSimpleParamList(t.params)),t.body=this.parseBlock(!1),t.expression=!1,this.adaptDirectivePrologue(t.body.body),this.labels=a}this.exitScope(),this.strict&&t.id&&this.checkLVal(t.id,5),this.strict=r},ft.isSimpleParamList=function(t){for(var e=0,i=t;e -1||n.functions.indexOf(t)>-1||n.var.indexOf(t)>-1,n.lexical.push(t),this.inModule&&1&n.flags&&delete this.undefinedExports[t]}else if(4===e)this.currentScope().lexical.push(t);else if(3===e){var s=this.currentScope();r=this.treatFunctionsAsVar?s.lexical.indexOf(t)>-1:s.lexical.indexOf(t)>-1||s.var.indexOf(t)>-1,s.functions.push(t)}else for(var a=this.scopeStack.length-1;a>=0;--a){var o=this.scopeStack[a];if(o.lexical.indexOf(t)>-1&&!(32&o.flags&&o.lexical[0]===t)||!this.treatFunctionsAsVarInScope(o)&&o.functions.indexOf(t)>-1){r=!0;break}if(o.var.push(t),this.inModule&&1&o.flags&&delete this.undefinedExports[t],3&o.flags)break}r&&this.raiseRecoverable(i,"Identifier '"+t+"' has already been declared")},yt.checkLocalExport=function(t){-1===this.scopeStack[0].lexical.indexOf(t.name)&&-1===this.scopeStack[0].var.indexOf(t.name)&&(this.undefinedExports[t.name]=t)},yt.currentScope=function(){return this.scopeStack[this.scopeStack.length-1]},yt.currentVarScope=function(){for(var t=this.scopeStack.length-1;;t--){var e=this.scopeStack[t];if(3&e.flags)return e}},yt.currentThisScope=function(){for(var t=this.scopeStack.length-1;;t--){var e=this.scopeStack[t];if(3&e.flags&&!(16&e.flags))return e}};var xt=function(t,e,i){this.type="",this.start=e,this.end=0,t.options.locations&&(this.loc=new Q(t,i)),t.options.directSourceFile&&(this.sourceFile=t.options.directSourceFile),t.options.ranges&&(this.range=[e,0])},bt=it.prototype;function _t(t,e,i,r){return t.type=e,t.end=i,this.options.locations&&(t.loc.end=r),this.options.ranges&&(t.range[1]=i),t}bt.startNode=function(){return new xt(this,this.start,this.startLoc)},bt.startNodeAt=function(t,e){return new xt(this,t,e)},bt.finishNode=function(t,e){return _t.call(this,t,e,this.lastTokEnd,this.lastTokEndLoc)},bt.finishNodeAt=function(t,e,i,r){return _t.call(this,t,e,i,r)};var kt=function(t,e,i,r,n){this.token=t,this.isExpr=!!e,this.preserveSpace=!!i,this.override=r,this.generator=!!n},St={b_stat:new kt("{",!1),b_expr:new kt("{",!0),b_tmpl:new kt("${",!1),p_stat:new kt("(",!1),p_expr:new kt("(",!0),q_tmpl:new kt("`",!0,!0,(function(t){return t.tryReadTemplateToken()})),f_stat:new kt("function",!1),f_expr:new kt("function",!0),f_expr_gen:new kt("function",!0,!1,null,!0),f_gen:new kt("function",!1,!1,null,!0)},Et=it.prototype;Et.initialContext=function(){return[St.b_stat]},Et.braceIsBlock=function(t){var e=this.curContext();return e===St.f_expr||e===St.f_stat||(t!==F.colon||e!==St.b_stat&&e!==St.b_expr?t===F._return||t===F.name&&this.exprAllowed?M.test(this.input.slice(this.lastTokEnd,this.start)):t===F._else||t===F.semi||t===F.eof||t===F.parenR||t===F.arrow||(t===F.braceL?e===St.b_stat:t!==F._var&&t!==F._const&&t!==F.name&&!this.exprAllowed):!e.isExpr)},Et.inGeneratorContext=function(){for(var t=this.context.length-1;t>=1;t--){var e=this.context[t];if("function"===e.token)return e.generator}return!1},Et.updateContext=function(t){var e,i=this.type;i.keyword&&t===F.dot?this.exprAllowed=!1:(e=i.updateContext)?e.call(this,t):this.exprAllowed=i.beforeExpr},F.parenR.updateContext=F.braceR.updateContext=function(){if(1!==this.context.length){var t=this.context.pop();t===St.b_stat&&"function"===this.curContext().token&&(t=this.context.pop()),this.exprAllowed=!t.isExpr}else this.exprAllowed=!0},F.braceL.updateContext=function(t){this.context.push(this.braceIsBlock(t)?St.b_stat:St.b_expr),this.exprAllowed=!0},F.dollarBraceL.updateContext=function(){this.context.push(St.b_tmpl),this.exprAllowed=!0},F.parenL.updateContext=function(t){this.context.push(t===F._if||t===F._for||t===F._with||t===F._while?St.p_stat:St.p_expr),this.exprAllowed=!0},F.incDec.updateContext=function(){},F._function.updateContext=F._class.updateContext=function(t){!t.beforeExpr||t===F.semi||t===F._else||t===F._return&&M.test(this.input.slice(this.lastTokEnd,this.start))||(t===F.colon||t===F.braceL)&&this.curContext()===St.b_stat?this.context.push(St.f_stat):this.context.push(St.f_expr),this.exprAllowed=!1},F.backQuote.updateContext=function(){this.curContext()===St.q_tmpl?this.context.pop():this.context.push(St.q_tmpl),this.exprAllowed=!1},F.star.updateContext=function(t){if(t===F._function){var e=this.context.length-1;this.context[e]=this.context[e]===St.f_expr?St.f_expr_gen:St.f_gen}this.exprAllowed=!0},F.name.updateContext=function(t){var e=!1;this.options.ecmaVersion>=6&&t!==F.dot&&("of"===this.value&&!this.exprAllowed||"yield"===this.value&&this.inGeneratorContext())&&(e=!0),this.exprAllowed=e};var wt="ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS",Ct={9:wt,10:wt+" Extended_Pictographic"},At="Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu",It="Adlam Adlm Ahom Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb",Lt={9:It,10:It+" Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd"},Nt={};function Pt(t){var e=Nt[t]={binary:Z(Ct[t]+" "+At),nonBinary:{General_Category:Z(At),Script:Z(Lt[t])}};e.nonBinary.Script_Extensions=e.nonBinary.Script,e.nonBinary.gc=e.nonBinary.General_Category,e.nonBinary.sc=e.nonBinary.Script,e.nonBinary.scx=e.nonBinary.Script_Extensions}Pt(9),Pt(10);var Tt=it.prototype,jt=function(t){this.parser=t,this.validFlags="gim"+(t.options.ecmaVersion>=6?"uy":"")+(t.options.ecmaVersion>=9?"s":""),this.unicodeProperties=Nt[t.options.ecmaVersion>=10?10:t.options.ecmaVersion],this.source="",this.flags="",this.start=0,this.switchU=!1,this.switchN=!1,this.pos=0,this.lastIntValue=0,this.lastStringValue="",this.lastAssertionIsQuantifiable=!1,this.numCapturingParens=0,this.maxBackReference=0,this.groupNames=[],this.backReferenceNames=[]};function Ot(t){return t<=65535?String.fromCharCode(t):(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t)))}function Rt(t){return 36===t||t>=40&&t<=43||46===t||63===t||t>=91&&t<=94||t>=123&&t<=125}function Vt(t){return t>=65&&t<=90||t>=97&&t<=122}function Bt(t){return Vt(t)||95===t}function Dt(t){return Bt(t)||Ft(t)}function Ft(t){return t>=48&&t<=57}function Mt(t){return t>=48&&t<=57||t>=65&&t<=70||t>=97&&t<=102}function Ut(t){return t>=65&&t<=70?t-65+10:t>=97&&t<=102?t-97+10:t-48}function qt(t){return t>=48&&t<=55}jt.prototype.reset=function(t,e,i){var r=-1!==i.indexOf("u");this.start=0|t,this.source=e+"",this.flags=i,this.switchU=r&&this.parser.options.ecmaVersion>=6,this.switchN=r&&this.parser.options.ecmaVersion>=9},jt.prototype.raise=function(t){this.parser.raiseRecoverable(this.start,"Invalid regular expression: /"+this.source+"/: "+t)},jt.prototype.at=function(t){var e=this.source,i=e.length;if(t>=i)return-1;var r=e.charCodeAt(t);return!this.switchU||r<=55295||r>=57344||t+1>=i?r:(r<<10)+e.charCodeAt(t+1)-56613888},jt.prototype.nextIndex=function(t){var e=this.source,i=e.length;if(t>=i)return i;var r=e.charCodeAt(t);return!this.switchU||r<=55295||r>=57344||t+1>=i?t+1:t+2},jt.prototype.current=function(){return this.at(this.pos)},jt.prototype.lookahead=function(){return this.at(this.nextIndex(this.pos))},jt.prototype.advance=function(){this.pos=this.nextIndex(this.pos)},jt.prototype.eat=function(t){return this.current()===t&&(this.advance(),!0)},Tt.validateRegExpFlags=function(t){for(var e=t.validFlags,i=t.flags,r=0;r -1&&this.raise(t.start,"Duplicate regular expression flag")}},Tt.validateRegExpPattern=function(t){this.regexp_pattern(t),!t.switchN&&this.options.ecmaVersion>=9&&t.groupNames.length>0&&(t.switchN=!0,this.regexp_pattern(t))},Tt.regexp_pattern=function(t){t.pos=0,t.lastIntValue=0,t.lastStringValue="",t.lastAssertionIsQuantifiable=!1,t.numCapturingParens=0,t.maxBackReference=0,t.groupNames.length=0,t.backReferenceNames.length=0,this.regexp_disjunction(t),t.pos!==t.source.length&&(t.eat(41)&&t.raise("Unmatched ')'"),(t.eat(93)||t.eat(125))&&t.raise("Lone quantifier brackets")),t.maxBackReference>t.numCapturingParens&&t.raise("Invalid escape");for(var e=0,i=t.backReferenceNames;e =9&&(i=t.eat(60)),t.eat(61)||t.eat(33))return this.regexp_disjunction(t),t.eat(41)||t.raise("Unterminated group"),t.lastAssertionIsQuantifiable=!i,!0}return t.pos=e,!1},Tt.regexp_eatQuantifier=function(t,e){return void 0===e&&(e=!1),!!this.regexp_eatQuantifierPrefix(t,e)&&(t.eat(63),!0)},Tt.regexp_eatQuantifierPrefix=function(t,e){return t.eat(42)||t.eat(43)||t.eat(63)||this.regexp_eatBracedQuantifier(t,e)},Tt.regexp_eatBracedQuantifier=function(t,e){var i=t.pos;if(t.eat(123)){var r=0,n=-1;if(this.regexp_eatDecimalDigits(t)&&(r=t.lastIntValue,t.eat(44)&&this.regexp_eatDecimalDigits(t)&&(n=t.lastIntValue),t.eat(125)))return-1!==n&&n =9?this.regexp_groupSpecifier(t):63===t.current()&&t.raise("Invalid group"),this.regexp_disjunction(t),t.eat(41))return t.numCapturingParens+=1,!0;t.raise("Unterminated group")}return!1},Tt.regexp_eatExtendedAtom=function(t){return t.eat(46)||this.regexp_eatReverseSolidusAtomEscape(t)||this.regexp_eatCharacterClass(t)||this.regexp_eatUncapturingGroup(t)||this.regexp_eatCapturingGroup(t)||this.regexp_eatInvalidBracedQuantifier(t)||this.regexp_eatExtendedPatternCharacter(t)},Tt.regexp_eatInvalidBracedQuantifier=function(t){return this.regexp_eatBracedQuantifier(t,!0)&&t.raise("Nothing to repeat"),!1},Tt.regexp_eatSyntaxCharacter=function(t){var e=t.current();return!!Rt(e)&&(t.lastIntValue=e,t.advance(),!0)},Tt.regexp_eatPatternCharacters=function(t){for(var e=t.pos,i=0;-1!==(i=t.current())&&!Rt(i);)t.advance();return t.pos!==e},Tt.regexp_eatExtendedPatternCharacter=function(t){var e=t.current();return!(-1===e||36===e||e>=40&&e<=43||46===e||63===e||91===e||94===e||124===e||(t.advance(),0))},Tt.regexp_groupSpecifier=function(t){if(t.eat(63)){if(this.regexp_eatGroupName(t))return-1!==t.groupNames.indexOf(t.lastStringValue)&&t.raise("Duplicate capture group name"),void t.groupNames.push(t.lastStringValue);t.raise("Invalid group")}},Tt.regexp_eatGroupName=function(t){if(t.lastStringValue="",t.eat(60)){if(this.regexp_eatRegExpIdentifierName(t)&&t.eat(62))return!0;t.raise("Invalid capture group name")}return!1},Tt.regexp_eatRegExpIdentifierName=function(t){if(t.lastStringValue="",this.regexp_eatRegExpIdentifierStart(t)){for(t.lastStringValue+=Ot(t.lastIntValue);this.regexp_eatRegExpIdentifierPart(t);)t.lastStringValue+=Ot(t.lastIntValue);return!0}return!1},Tt.regexp_eatRegExpIdentifierStart=function(t){var e=t.pos,i=t.current();return t.advance(),92===i&&this.regexp_eatRegExpUnicodeEscapeSequence(t)&&(i=t.lastIntValue),function(t){return P(t,!0)||36===t||95===t}(i)?(t.lastIntValue=i,!0):(t.pos=e,!1)},Tt.regexp_eatRegExpIdentifierPart=function(t){var e=t.pos,i=t.current();return t.advance(),92===i&&this.regexp_eatRegExpUnicodeEscapeSequence(t)&&(i=t.lastIntValue),function(t){return T(t,!0)||36===t||95===t||8204===t||8205===t}(i)?(t.lastIntValue=i,!0):(t.pos=e,!1)},Tt.regexp_eatAtomEscape=function(t){return!!(this.regexp_eatBackReference(t)||this.regexp_eatCharacterClassEscape(t)||this.regexp_eatCharacterEscape(t)||t.switchN&&this.regexp_eatKGroupName(t))||(t.switchU&&(99===t.current()&&t.raise("Invalid unicode escape"),t.raise("Invalid escape")),!1)},Tt.regexp_eatBackReference=function(t){var e=t.pos;if(this.regexp_eatDecimalEscape(t)){var i=t.lastIntValue;if(t.switchU)return i>t.maxBackReference&&(t.maxBackReference=i),!0;if(i<=t.numCapturingParens)return!0;t.pos=e}return!1},Tt.regexp_eatKGroupName=function(t){if(t.eat(107)){if(this.regexp_eatGroupName(t))return t.backReferenceNames.push(t.lastStringValue),!0;t.raise("Invalid named reference")}return!1},Tt.regexp_eatCharacterEscape=function(t){return this.regexp_eatControlEscape(t)||this.regexp_eatCControlLetter(t)||this.regexp_eatZero(t)||this.regexp_eatHexEscapeSequence(t)||this.regexp_eatRegExpUnicodeEscapeSequence(t)||!t.switchU&&this.regexp_eatLegacyOctalEscapeSequence(t)||this.regexp_eatIdentityEscape(t)},Tt.regexp_eatCControlLetter=function(t){var e=t.pos;if(t.eat(99)){if(this.regexp_eatControlLetter(t))return!0;t.pos=e}return!1},Tt.regexp_eatZero=function(t){return 48===t.current()&&!Ft(t.lookahead())&&(t.lastIntValue=0,t.advance(),!0)},Tt.regexp_eatControlEscape=function(t){var e=t.current();return 116===e?(t.lastIntValue=9,t.advance(),!0):110===e?(t.lastIntValue=10,t.advance(),!0):118===e?(t.lastIntValue=11,t.advance(),!0):102===e?(t.lastIntValue=12,t.advance(),!0):114===e&&(t.lastIntValue=13,t.advance(),!0)},Tt.regexp_eatControlLetter=function(t){var e=t.current();return!!Vt(e)&&(t.lastIntValue=e%32,t.advance(),!0)},Tt.regexp_eatRegExpUnicodeEscapeSequence=function(t){var e,i=t.pos;if(t.eat(117)){if(this.regexp_eatFixedHexDigits(t,4)){var r=t.lastIntValue;if(t.switchU&&r>=55296&&r<=56319){var n=t.pos;if(t.eat(92)&&t.eat(117)&&this.regexp_eatFixedHexDigits(t,4)){var s=t.lastIntValue;if(s>=56320&&s<=57343)return t.lastIntValue=1024*(r-55296)+(s-56320)+65536,!0}t.pos=n,t.lastIntValue=r}return!0}if(t.switchU&&t.eat(123)&&this.regexp_eatHexDigits(t)&&t.eat(125)&&(e=t.lastIntValue)>=0&&e<=1114111)return!0;t.switchU&&t.raise("Invalid unicode escape"),t.pos=i}return!1},Tt.regexp_eatIdentityEscape=function(t){if(t.switchU)return!!this.regexp_eatSyntaxCharacter(t)||!!t.eat(47)&&(t.lastIntValue=47,!0);var e=t.current();return!(99===e||t.switchN&&107===e||(t.lastIntValue=e,t.advance(),0))},Tt.regexp_eatDecimalEscape=function(t){t.lastIntValue=0;var e=t.current();if(e>=49&&e<=57){do{t.lastIntValue=10*t.lastIntValue+(e-48),t.advance()}while((e=t.current())>=48&&e<=57);return!0}return!1},Tt.regexp_eatCharacterClassEscape=function(t){var e=t.current();if(function(t){return 100===t||68===t||115===t||83===t||119===t||87===t}(e))return t.lastIntValue=-1,t.advance(),!0;if(t.switchU&&this.options.ecmaVersion>=9&&(80===e||112===e)){if(t.lastIntValue=-1,t.advance(),t.eat(123)&&this.regexp_eatUnicodePropertyValueExpression(t)&&t.eat(125))return!0;t.raise("Invalid property name")}return!1},Tt.regexp_eatUnicodePropertyValueExpression=function(t){var e=t.pos;if(this.regexp_eatUnicodePropertyName(t)&&t.eat(61)){var i=t.lastStringValue;if(this.regexp_eatUnicodePropertyValue(t))return this.regexp_validateUnicodePropertyNameAndValue(t,i,t.lastStringValue),!0}return t.pos=e,!!this.regexp_eatLoneUnicodePropertyNameOrValue(t)&&(this.regexp_validateUnicodePropertyNameOrValue(t,t.lastStringValue),!0)},Tt.regexp_validateUnicodePropertyNameAndValue=function(t,e,i){K(t.unicodeProperties.nonBinary,e)||t.raise("Invalid property name"),t.unicodeProperties.nonBinary[e].test(i)||t.raise("Invalid property value")},Tt.regexp_validateUnicodePropertyNameOrValue=function(t,e){t.unicodeProperties.binary.test(e)||t.raise("Invalid property name")},Tt.regexp_eatUnicodePropertyName=function(t){var e=0;for(t.lastStringValue="";Bt(e=t.current());)t.lastStringValue+=Ot(e),t.advance();return""!==t.lastStringValue},Tt.regexp_eatUnicodePropertyValue=function(t){var e=0;for(t.lastStringValue="";Dt(e=t.current());)t.lastStringValue+=Ot(e),t.advance();return""!==t.lastStringValue},Tt.regexp_eatLoneUnicodePropertyNameOrValue=function(t){return this.regexp_eatUnicodePropertyValue(t)},Tt.regexp_eatCharacterClass=function(t){if(t.eat(91)){if(t.eat(94),this.regexp_classRanges(t),t.eat(93))return!0;t.raise("Unterminated character class")}return!1},Tt.regexp_classRanges=function(t){for(;this.regexp_eatClassAtom(t);){var e=t.lastIntValue;if(t.eat(45)&&this.regexp_eatClassAtom(t)){var i=t.lastIntValue;!t.switchU||-1!==e&&-1!==i||t.raise("Invalid character class"),-1!==e&&-1!==i&&e>i&&t.raise("Range out of order in character class")}}},Tt.regexp_eatClassAtom=function(t){var e=t.pos;if(t.eat(92)){if(this.regexp_eatClassEscape(t))return!0;if(t.switchU){var i=t.current();(99===i||qt(i))&&t.raise("Invalid class escape"),t.raise("Invalid escape")}t.pos=e}var r=t.current();return 93!==r&&(t.lastIntValue=r,t.advance(),!0)},Tt.regexp_eatClassEscape=function(t){var e=t.pos;if(t.eat(98))return t.lastIntValue=8,!0;if(t.switchU&&t.eat(45))return t.lastIntValue=45,!0;if(!t.switchU&&t.eat(99)){if(this.regexp_eatClassControlLetter(t))return!0;t.pos=e}return this.regexp_eatCharacterClassEscape(t)||this.regexp_eatCharacterEscape(t)},Tt.regexp_eatClassControlLetter=function(t){var e=t.current();return!(!Ft(e)&&95!==e||(t.lastIntValue=e%32,t.advance(),0))},Tt.regexp_eatHexEscapeSequence=function(t){var e=t.pos;if(t.eat(120)){if(this.regexp_eatFixedHexDigits(t,2))return!0;t.switchU&&t.raise("Invalid escape"),t.pos=e}return!1},Tt.regexp_eatDecimalDigits=function(t){var e=t.pos,i=0;for(t.lastIntValue=0;Ft(i=t.current());)t.lastIntValue=10*t.lastIntValue+(i-48),t.advance();return t.pos!==e},Tt.regexp_eatHexDigits=function(t){var e=t.pos,i=0;for(t.lastIntValue=0;Mt(i=t.current());)t.lastIntValue=16*t.lastIntValue+Ut(i),t.advance();return t.pos!==e},Tt.regexp_eatLegacyOctalEscapeSequence=function(t){if(this.regexp_eatOctalDigit(t)){var e=t.lastIntValue;if(this.regexp_eatOctalDigit(t)){var i=t.lastIntValue;t.lastIntValue=e<=3&&this.regexp_eatOctalDigit(t)?64*e+8*i+t.lastIntValue:8*e+i}else t.lastIntValue=e;return!0}return!1},Tt.regexp_eatOctalDigit=function(t){var e=t.current();return qt(e)?(t.lastIntValue=e-48,t.advance(),!0):(t.lastIntValue=0,!1)},Tt.regexp_eatFixedHexDigits=function(t,e){var i=t.pos;t.lastIntValue=0;for(var r=0;r >10),56320+(1023&t)))}Wt.next=function(){this.options.onToken&&this.options.onToken(new zt(this)),this.lastTokEnd=this.end,this.lastTokStart=this.start,this.lastTokEndLoc=this.endLoc,this.lastTokStartLoc=this.startLoc,this.nextToken()},Wt.getToken=function(){return this.next(),new zt(this)},"undefined"!=typeof Symbol&&(Wt[Symbol.iterator]=function(){var t=this;return{next:function(){var e=t.getToken();return{done:e.type===F.eof,value:e}}}}),Wt.curContext=function(){return this.context[this.context.length-1]},Wt.nextToken=function(){var t=this.curContext();return t&&t.preserveSpace||this.skipSpace(),this.start=this.pos,this.options.locations&&(this.startLoc=this.curPosition()),this.pos>=this.input.length?this.finishToken(F.eof):t.override?t.override(this):void this.readToken(this.fullCharCodeAtPos())},Wt.readToken=function(t){return P(t,this.options.ecmaVersion>=6)||92===t?this.readWord():this.getTokenFromCode(t)},Wt.fullCharCodeAtPos=function(){var t=this.input.charCodeAt(this.pos);return t<=55295||t>=57344?t:(t<<10)+this.input.charCodeAt(this.pos+1)-56613888},Wt.skipBlockComment=function(){var t,e=this.options.onComment&&this.curPosition(),i=this.pos,r=this.input.indexOf("*/",this.pos+=2);if(-1===r&&this.raise(this.pos-2,"Unterminated comment"),this.pos=r+2,this.options.locations)for(U.lastIndex=i;(t=U.exec(this.input))&&t.index 8&&t<14||t>=5760&&z.test(String.fromCharCode(t))))break t;++this.pos}}},Wt.finishToken=function(t,e){this.end=this.pos,this.options.locations&&(this.endLoc=this.curPosition());var i=this.type;this.type=t,this.value=e,this.updateContext(i)},Wt.readToken_dot=function(){var t=this.input.charCodeAt(this.pos+1);if(t>=48&&t<=57)return this.readNumber(!0);var e=this.input.charCodeAt(this.pos+2);return this.options.ecmaVersion>=6&&46===t&&46===e?(this.pos+=3,this.finishToken(F.ellipsis)):(++this.pos,this.finishToken(F.dot))},Wt.readToken_slash=function(){var t=this.input.charCodeAt(this.pos+1);return this.exprAllowed?(++this.pos,this.readRegexp()):61===t?this.finishOp(F.assign,2):this.finishOp(F.slash,1)},Wt.readToken_mult_modulo_exp=function(t){var e=this.input.charCodeAt(this.pos+1),i=1,r=42===t?F.star:F.modulo;return this.options.ecmaVersion>=7&&42===t&&42===e&&(++i,r=F.starstar,e=this.input.charCodeAt(this.pos+2)),61===e?this.finishOp(F.assign,i+1):this.finishOp(r,i)},Wt.readToken_pipe_amp=function(t){var e=this.input.charCodeAt(this.pos+1);return e===t?this.finishOp(124===t?F.logicalOR:F.logicalAND,2):61===e?this.finishOp(F.assign,2):this.finishOp(124===t?F.bitwiseOR:F.bitwiseAND,1)},Wt.readToken_caret=function(){return 61===this.input.charCodeAt(this.pos+1)?this.finishOp(F.assign,2):this.finishOp(F.bitwiseXOR,1)},Wt.readToken_plus_min=function(t){var e=this.input.charCodeAt(this.pos+1);return e===t?45!==e||this.inModule||62!==this.input.charCodeAt(this.pos+2)||0!==this.lastTokEnd&&!M.test(this.input.slice(this.lastTokEnd,this.pos))?this.finishOp(F.incDec,2):(this.skipLineComment(3),this.skipSpace(),this.nextToken()):61===e?this.finishOp(F.assign,2):this.finishOp(F.plusMin,1)},Wt.readToken_lt_gt=function(t){var e=this.input.charCodeAt(this.pos+1),i=1;return e===t?(i=62===t&&62===this.input.charCodeAt(this.pos+2)?3:2,61===this.input.charCodeAt(this.pos+i)?this.finishOp(F.assign,i+1):this.finishOp(F.bitShift,i)):33!==e||60!==t||this.inModule||45!==this.input.charCodeAt(this.pos+2)||45!==this.input.charCodeAt(this.pos+3)?(61===e&&(i=2),this.finishOp(F.relational,i)):(this.skipLineComment(4),this.skipSpace(),this.nextToken())},Wt.readToken_eq_excl=function(t){var e=this.input.charCodeAt(this.pos+1);return 61===e?this.finishOp(F.equality,61===this.input.charCodeAt(this.pos+2)?3:2):61===t&&62===e&&this.options.ecmaVersion>=6?(this.pos+=2,this.finishToken(F.arrow)):this.finishOp(61===t?F.eq:F.prefix,1)},Wt.getTokenFromCode=function(t){switch(t){case 46:return this.readToken_dot();case 40:return++this.pos,this.finishToken(F.parenL);case 41:return++this.pos,this.finishToken(F.parenR);case 59:return++this.pos,this.finishToken(F.semi);case 44:return++this.pos,this.finishToken(F.comma);case 91:return++this.pos,this.finishToken(F.bracketL);case 93:return++this.pos,this.finishToken(F.bracketR);case 123:return++this.pos,this.finishToken(F.braceL);case 125:return++this.pos,this.finishToken(F.braceR);case 58:return++this.pos,this.finishToken(F.colon);case 63:return++this.pos,this.finishToken(F.question);case 96:if(this.options.ecmaVersion<6)break;return++this.pos,this.finishToken(F.backQuote);case 48:var e=this.input.charCodeAt(this.pos+1);if(120===e||88===e)return this.readRadixNumber(16);if(this.options.ecmaVersion>=6){if(111===e||79===e)return this.readRadixNumber(8);if(98===e||66===e)return this.readRadixNumber(2)}case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return this.readNumber(!1);case 34:case 39:return this.readString(t);case 47:return this.readToken_slash();case 37:case 42:return this.readToken_mult_modulo_exp(t);case 124:case 38:return this.readToken_pipe_amp(t);case 94:return this.readToken_caret();case 43:case 45:return this.readToken_plus_min(t);case 60:case 62:return this.readToken_lt_gt(t);case 61:case 33:return this.readToken_eq_excl(t);case 126:return this.finishOp(F.prefix,1)}this.raise(this.pos,"Unexpected character '"+Ht(t)+"'")},Wt.finishOp=function(t,e){var i=this.input.slice(this.pos,this.pos+e);return this.pos+=e,this.finishToken(t,i)},Wt.readRegexp=function(){for(var t,e,i=this.pos;;){this.pos>=this.input.length&&this.raise(i,"Unterminated regular expression");var r=this.input.charAt(this.pos);if(M.test(r)&&this.raise(i,"Unterminated regular expression"),t)t=!1;else{if("["===r)e=!0;else if("]"===r&&e)e=!1;else if("/"===r&&!e)break;t="\\"===r}++this.pos}var n=this.input.slice(i,this.pos);++this.pos;var s=this.pos,a=this.readWord1();this.containsEsc&&this.unexpected(s);var o=this.regexpState||(this.regexpState=new jt(this));o.reset(i,n,a),this.validateRegExpFlags(o),this.validateRegExpPattern(o);var p=null;try{p=new RegExp(n,a)}catch(t){}return this.finishToken(F.regexp,{pattern:n,flags:a,value:p})},Wt.readInt=function(t,e){for(var i=this.pos,r=0,n=0,s=null==e?1/0:e;n =97?o-97+10:o>=65?o-65+10:o>=48&&o<=57?o-48:1/0)>=t)break;++this.pos,r=r*t+a}return this.pos===i||null!=e&&this.pos-i!==e?null:r},Wt.readRadixNumber=function(t){this.pos+=2;var e=this.readInt(t);return null==e&&this.raise(this.start+2,"Expected number in radix "+t),P(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number"),this.finishToken(F.num,e)},Wt.readNumber=function(t){var e=this.pos;t||null!==this.readInt(10)||this.raise(e,"Invalid number");var i=this.pos-e>=2&&48===this.input.charCodeAt(e);i&&this.strict&&this.raise(e,"Invalid number"),i&&/[89]/.test(this.input.slice(e,this.pos))&&(i=!1);var r=this.input.charCodeAt(this.pos);46!==r||i||(++this.pos,this.readInt(10),r=this.input.charCodeAt(this.pos)),69!==r&&101!==r||i||(43!==(r=this.input.charCodeAt(++this.pos))&&45!==r||++this.pos,null===this.readInt(10)&&this.raise(e,"Invalid number")),P(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number");var n=this.input.slice(e,this.pos),s=i?parseInt(n,8):parseFloat(n);return this.finishToken(F.num,s)},Wt.readCodePoint=function(){var t;if(123===this.input.charCodeAt(this.pos)){this.options.ecmaVersion<6&&this.unexpected();var e=++this.pos;t=this.readHexChar(this.input.indexOf("}",this.pos)-this.pos),++this.pos,t>1114111&&this.invalidStringToken(e,"Code point out of bounds")}else t=this.readHexChar(4);return t},Wt.readString=function(t){for(var e="",i=++this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated string constant");var r=this.input.charCodeAt(this.pos);if(r===t)break;92===r?(e+=this.input.slice(i,this.pos),e+=this.readEscapedChar(!1),i=this.pos):(q(r,this.options.ecmaVersion>=10)&&this.raise(this.start,"Unterminated string constant"),++this.pos)}return e+=this.input.slice(i,this.pos++),this.finishToken(F.string,e)};var Xt={};Wt.tryReadTemplateToken=function(){this.inTemplateElement=!0;try{this.readTmplToken()}catch(b){if(b!==Xt)throw b;this.readInvalidTemplateToken()}this.inTemplateElement=!1},Wt.invalidStringToken=function(t,e){if(this.inTemplateElement&&this.options.ecmaVersion>=9)throw Xt;this.raise(t,e)},Wt.readTmplToken=function(){for(var t="",e=this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated template");var i=this.input.charCodeAt(this.pos);if(96===i||36===i&&123===this.input.charCodeAt(this.pos+1))return this.pos!==this.start||this.type!==F.template&&this.type!==F.invalidTemplate?(t+=this.input.slice(e,this.pos),this.finishToken(F.template,t)):36===i?(this.pos+=2,this.finishToken(F.dollarBraceL)):(++this.pos,this.finishToken(F.backQuote));if(92===i)t+=this.input.slice(e,this.pos),t+=this.readEscapedChar(!0),e=this.pos;else if(q(i)){switch(t+=this.input.slice(e,this.pos),++this.pos,i){case 13:10===this.input.charCodeAt(this.pos)&&++this.pos;case 10:t+="\n";break;default:t+=String.fromCharCode(i)}this.options.locations&&(++this.curLine,this.lineStart=this.pos),e=this.pos}else++this.pos}},Wt.readInvalidTemplateToken=function(){for(;this.pos=48&&e<=55){var i=this.input.substr(this.pos-1,3).match(/^[0-7]+/)[0],r=parseInt(i,8);return r>255&&(i=i.slice(0,-1),r=parseInt(i,8)),this.pos+=i.length-1,e=this.input.charCodeAt(this.pos),"0"===i&&56!==e&&57!==e||!this.strict&&!t||this.invalidStringToken(this.pos-1-i.length,t?"Octal literal in template string":"Octal literal in strict mode"),String.fromCharCode(r)}return q(e)?"":String.fromCharCode(e)}},Wt.readHexChar=function(t){var e=this.pos,i=this.readInt(16,t);return null===i&&this.invalidStringToken(e,"Bad character escape sequence"),i},Wt.readWord1=function(){this.containsEsc=!1;for(var t="",e=!0,i=this.pos,r=this.options.ecmaVersion>=6;this.pos ",nbsp:"\xa0",iexcl:"\xa1",cent:"\xa2",pound:"\xa3",curren:"\xa4",yen:"\xa5",brvbar:"\xa6",sect:"\xa7",uml:"\xa8",copy:"\xa9",ordf:"\xaa",laquo:"\xab",not:"\xac",shy:"\xad",reg:"\xae",macr:"\xaf",deg:"\xb0",plusmn:"\xb1",sup2:"\xb2",sup3:"\xb3",acute:"\xb4",micro:"\xb5",para:"\xb6",middot:"\xb7",cedil:"\xb8",sup1:"\xb9",ordm:"\xba",raquo:"\xbb",frac14:"\xbc",frac12:"\xbd",frac34:"\xbe",iquest:"\xbf",Agrave:"\xc0",Aacute:"\xc1",Acirc:"\xc2",Atilde:"\xc3",Auml:"\xc4",Aring:"\xc5",AElig:"\xc6",Ccedil:"\xc7",Egrave:"\xc8",Eacute:"\xc9",Ecirc:"\xca",Euml:"\xcb",Igrave:"\xcc",Iacute:"\xcd",Icirc:"\xce",Iuml:"\xcf",ETH:"\xd0",Ntilde:"\xd1",Ograve:"\xd2",Oacute:"\xd3",Ocirc:"\xd4",Otilde:"\xd5",Ouml:"\xd6",times:"\xd7",Oslash:"\xd8",Ugrave:"\xd9",Uacute:"\xda",Ucirc:"\xdb",Uuml:"\xdc",Yacute:"\xdd",THORN:"\xde",szlig:"\xdf",agrave:"\xe0",aacute:"\xe1",acirc:"\xe2",atilde:"\xe3",auml:"\xe4",aring:"\xe5",aelig:"\xe6",ccedil:"\xe7",egrave:"\xe8",eacute:"\xe9",ecirc:"\xea",euml:"\xeb",igrave:"\xec",iacute:"\xed",icirc:"\xee",iuml:"\xef",eth:"\xf0",ntilde:"\xf1",ograve:"\xf2",oacute:"\xf3",ocirc:"\xf4",otilde:"\xf5",ouml:"\xf6",divide:"\xf7",oslash:"\xf8",ugrave:"\xf9",uacute:"\xfa",ucirc:"\xfb",uuml:"\xfc",yacute:"\xfd",thorn:"\xfe",yuml:"\xff",OElig:"\u0152",oelig:"\u0153",Scaron:"\u0160",scaron:"\u0161",Yuml:"\u0178",fnof:"\u0192",circ:"\u02c6",tilde:"\u02dc",Alpha:"\u0391",Beta:"\u0392",Gamma:"\u0393",Delta:"\u0394",Epsilon:"\u0395",Zeta:"\u0396",Eta:"\u0397",Theta:"\u0398",Iota:"\u0399",Kappa:"\u039a",Lambda:"\u039b",Mu:"\u039c",Nu:"\u039d",Xi:"\u039e",Omicron:"\u039f",Pi:"\u03a0",Rho:"\u03a1",Sigma:"\u03a3",Tau:"\u03a4",Upsilon:"\u03a5",Phi:"\u03a6",Chi:"\u03a7",Psi:"\u03a8",Omega:"\u03a9",alpha:"\u03b1",beta:"\u03b2",gamma:"\u03b3",delta:"\u03b4",epsilon:"\u03b5",zeta:"\u03b6",eta:"\u03b7",theta:"\u03b8",iota:"\u03b9",kappa:"\u03ba",lambda:"\u03bb",mu:"\u03bc",nu:"\u03bd",xi:"\u03be",omicron:"\u03bf",pi:"\u03c0",rho:"\u03c1",sigmaf:"\u03c2",sigma:"\u03c3",tau:"\u03c4",upsilon:"\u03c5",phi:"\u03c6",chi:"\u03c7",psi:"\u03c8",omega:"\u03c9",thetasym:"\u03d1",upsih:"\u03d2",piv:"\u03d6",ensp:"\u2002",emsp:"\u2003",thinsp:"\u2009",zwnj:"\u200c",zwj:"\u200d",lrm:"\u200e",rlm:"\u200f",ndash:"\u2013",mdash:"\u2014",lsquo:"\u2018",rsquo:"\u2019",sbquo:"\u201a",ldquo:"\u201c",rdquo:"\u201d",bdquo:"\u201e",dagger:"\u2020",Dagger:"\u2021",bull:"\u2022",hellip:"\u2026",permil:"\u2030",prime:"\u2032",Prime:"\u2033",lsaquo:"\u2039",rsaquo:"\u203a",oline:"\u203e",frasl:"\u2044",euro:"\u20ac",image:"\u2111",weierp:"\u2118",real:"\u211c",trade:"\u2122",alefsym:"\u2135",larr:"\u2190",uarr:"\u2191",rarr:"\u2192",darr:"\u2193",harr:"\u2194",crarr:"\u21b5",lArr:"\u21d0",uArr:"\u21d1",rArr:"\u21d2",dArr:"\u21d3",hArr:"\u21d4",forall:"\u2200",part:"\u2202",exist:"\u2203",empty:"\u2205",nabla:"\u2207",isin:"\u2208",notin:"\u2209",ni:"\u220b",prod:"\u220f",sum:"\u2211",minus:"\u2212",lowast:"\u2217",radic:"\u221a",prop:"\u221d",infin:"\u221e",ang:"\u2220",and:"\u2227",or:"\u2228",cap:"\u2229",cup:"\u222a",int:"\u222b",there4:"\u2234",sim:"\u223c",cong:"\u2245",asymp:"\u2248",ne:"\u2260",equiv:"\u2261",le:"\u2264",ge:"\u2265",sub:"\u2282",sup:"\u2283",nsub:"\u2284",sube:"\u2286",supe:"\u2287",oplus:"\u2295",otimes:"\u2297",perp:"\u22a5",sdot:"\u22c5",lceil:"\u2308",rceil:"\u2309",lfloor:"\u230a",rfloor:"\u230b",lang:"\u2329",rang:"\u232a",loz:"\u25ca",spades:"\u2660",clubs:"\u2663",hearts:"\u2665",diams:"\u2666"},Kt={version:"6.1.1",parse:function(t,e){return it.parse(t,e)},parseExpressionAt:function(t,e,i){return it.parseExpressionAt(t,e,i)},tokenizer:function(t,e){return it.tokenizer(t,e)},Parser:it,defaultOptions:tt,Position:$,SourceLocation:Q,getLineInfo:Y,Node:xt,TokenType:j,tokTypes:F,keywordTypes:B,TokContext:kt,tokContexts:St,isIdentifierChar:T,isIdentifierStart:P,Token:zt,isNewLine:q,lineBreak:M,lineBreakG:U,nonASCIIwhitespace:z};const Gt=/^[\da-fA-F]+$/,Zt=/^\d+$/,$t=Kt.tokTypes,Qt=Kt.TokContext,Yt=Kt.tokContexts,te=Kt.TokenType,ee=Kt.isNewLine,ie=Kt.isIdentifierStart,re=Kt.isIdentifierChar,ne=new Qt(" ... ",!0,!0),oe={jsxName:new te("jsxName"),jsxText:new te("jsxText",{beforeExpr:!0}),jsxTagStart:new te("jsxTagStart"),jsxTagEnd:new te("jsxTagEnd")};function pe(t){return t?"JSXIdentifier"===t.type?t.name:"JSXNamespacedName"===t.type?t.namespace.name+":"+t.name.name:"JSXMemberExpression"===t.type?pe(t.object)+"."+pe(t.property):void 0:t}oe.jsxTagStart.updateContext=function(){this.context.push(ae),this.context.push(ne),this.exprAllowed=!1},oe.jsxTagEnd.updateContext=function(t){let e=this.context.pop();e===ne&&t===$t.slash||e===se?(this.context.pop(),this.exprAllowed=this.curContext()===ae):this.exprAllowed=!0};var he=function(t){return t=t||{},function(e){return function(t,e){return class extends e{jsx_readToken(){let t="",e=this.pos;for(;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated JSX contents");let i=this.input.charCodeAt(this.pos);switch(i){case 60:case 123:return this.pos===this.start?60===i&&this.exprAllowed?(++this.pos,this.finishToken(oe.jsxTagStart)):this.getTokenFromCode(i):(t+=this.input.slice(e,this.pos),this.finishToken(oe.jsxText,t));case 38:t+=this.input.slice(e,this.pos),t+=this.jsx_readEntity(),e=this.pos;break;default:ee(i)?(t+=this.input.slice(e,this.pos),t+=this.jsx_readNewLine(!0),e=this.pos):++this.pos}}}jsx_readNewLine(t){let e,i=this.input.charCodeAt(this.pos);return++this.pos,13===i&&10===this.input.charCodeAt(this.pos)?(++this.pos,e=t?"\n":"\r\n"):e=String.fromCharCode(i),this.options.locations&&(++this.curLine,this.lineStart=this.pos),e}jsx_readString(t){let e="",i=++this.pos;for(;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated string constant");let r=this.input.charCodeAt(this.pos);if(r===t)break;38===r?(e+=this.input.slice(i,this.pos),e+=this.jsx_readEntity(),i=this.pos):ee(r)?(e+=this.input.slice(i,this.pos),e+=this.jsx_readNewLine(!1),i=this.pos):++this.pos}return e+=this.input.slice(i,this.pos++),this.finishToken($t.string,e)}jsx_readEntity(){let t,e="",i=0,r=this.input[this.pos];"&"!==r&&this.raise(this.pos,"Entity must start with an ampersand");let n=++this.pos;for(;this.pos")}let a=n.name?"Element":"Fragment";return i["opening"+a]=n,i["closing"+a]=s,i.children=r,this.type===$t.relational&&"<"===this.value&&this.raise(this.start,"Adjacent JSX elements must be wrapped in an enclosing tag"),this.finishNode(i,"JSX"+a)}jsx_parseText(t){let e=this.parseLiteral(t);return e.type="JSXText",e}jsx_parseElement(){let t=this.start,e=this.startLoc;return this.next(),this.jsx_parseElementAt(t,e)}parseExprAtom(t){return this.type===oe.jsxText?this.jsx_parseText(this.value):this.type===oe.jsxTagStart?this.jsx_parseElement():super.parseExprAtom(t)}readToken(t){let e=this.curContext();if(e===ae)return this.jsx_readToken();if(e===ne||e===se){if(ie(t))return this.jsx_readWord();if(62==t)return++this.pos,this.finishToken(oe.jsxTagEnd);if((34===t||39===t)&&e==ne)return this.jsx_readString(t)}return 60===t&&this.exprAllowed&&33!==this.input.charCodeAt(this.pos+1)?(++this.pos,this.finishToken(oe.jsxTagStart)):super.readToken(t)}updateContext(t){if(this.type==$t.braceL){var e=this.curContext();e==ne?this.context.push(Yt.b_expr):e==ae?this.context.push(Yt.b_tmpl):super.updateContext(t),this.exprAllowed=!0}else{if(this.type!==$t.slash||t!==oe.jsxTagStart)return super.updateContext(t);this.context.length-=2,this.context.push(se),this.exprAllowed=!1}}}}({allowNamespaces:!1!==t.allowNamespaces,allowNamespacedObjects:!!t.allowNamespacedObjects},e)}};he.tokTypes=oe;var ce,le,ue=(function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.DynamicImportKey=void 0;var i=function(){function t(t,e){for(var i=0;i >=5)>0&&(i|=32),e+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[i]}while(t>0);return e}var xe=function(t,e,i){this.start=t,this.end=e,this.original=i,this.intro="",this.outro="",this.content=i,this.storeName=!1,this.edited=!1,Object.defineProperties(this,{previous:{writable:!0,value:null},next:{writable:!0,value:null}})};xe.prototype.appendLeft=function(t){this.outro+=t},xe.prototype.appendRight=function(t){this.intro=this.intro+t},xe.prototype.clone=function(){var t=new xe(this.start,this.end,this.original);return t.intro=this.intro,t.outro=this.outro,t.content=this.content,t.storeName=this.storeName,t.edited=this.edited,t},xe.prototype.contains=function(t){return this.start 0&&(s+=";"),0!==o.length){for(var p=0,h=[],c=0,l=o;c 1&&(d+=ve(u[1]-e)+ve(u[2]-i)+ve(u[3]-r),e=u[1],i=u[2],r=u[3]),5===u.length&&(d+=ve(u[4]-n),n=u[4]),h.push(d)}s+=h.join(",")}}return s}(t.mappings)};function ke(t){var e=t.split("\n"),i=e.filter((function(t){return/^\t+/.test(t)})),r=e.filter((function(t){return/^ {2,}/.test(t)}));if(0===i.length&&0===r.length)return null;if(i.length>=r.length)return"\t";var n=r.reduce((function(t,e){var i=/^ +/.exec(e)[0].length;return Math.min(i,t)}),1/0);return new Array(n+1).join(" ")}function Se(t,e){var i=t.split(/[\/\\]/),r=e.split(/[\/\\]/);for(i.pop();i[0]===r[0];)i.shift(),r.shift();if(i.length)for(var n=i.length;n--;)i[n]="..";return i.concat(r).join("/")}_e.prototype.toString=function(){return JSON.stringify(this)},_e.prototype.toUrl=function(){return"data:application/json;charset=utf-8;base64,"+be(this.toString())};var Ee=Object.prototype.toString;function we(t){for(var e=t.split("\n"),i=[],r=0,n=0;r >1;t=0&&n.push(r),this.rawSegments.push(n)}else this.pending&&this.rawSegments.push(this.pending);this.advance(e),this.pending=null},Ce.prototype.addUneditedChunk=function(t,e,i,r,n){for(var s=e.start,a=!0;s 1){for(var i=0;i =t&&i<=e)throw new Error("Cannot move a selection inside itself");this._split(t),this._split(e),this._split(i);var r=this.byStart[t],n=this.byEnd[e],s=r.previous,a=n.next,o=this.byStart[i];if(!o&&n===this.lastChunk)return this;var p=o?o.previous:this.lastChunk;return s&&(s.next=a),a&&(a.previous=s),p&&(p.next=r),o&&(o.previous=n),r.previous||(this.firstChunk=n.next),n.next||(this.lastChunk=r.previous,this.lastChunk.next=null),r.previous=p,n.next=o||null,p||(this.firstChunk=r),o||(this.lastChunk=n),this},Le.prototype.overwrite=function(t,e,i,r){if("string"!=typeof i)throw new TypeError("replacement content must be a string");for(;t<0;)t+=this.original.length;for(;e<0;)e+=this.original.length;if(e>this.original.length)throw new Error("end is out of bounds");if(t===e)throw new Error("Cannot overwrite a zero-length range \u2013 use appendLeft or prependRight instead");this._split(t),this._split(e),!0===r&&(Ie.storeName||(console.warn("The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string"),Ie.storeName=!0),r={storeName:!0});var n=void 0!==r&&r.storeName,s=void 0!==r&&r.contentOnly;if(n){var a=this.original.slice(t,e);this.storedNames[a]=!0}var o=this.byStart[t],p=this.byEnd[e];if(o){if(e>o.end&&o.next!==this.byStart[o.end])throw new Error("Cannot overwrite across a split point");if(o.edit(i,n,s),o!==p){for(var h=o.next;h!==p;)h.edit("",!1),h=h.next;h.edit("",!1)}}else{var c=new xe(t,e,"").edit(i,n);p.next=c,c.previous=p}return this},Le.prototype.prepend=function(t){if("string"!=typeof t)throw new TypeError("outro content must be a string");return this.intro=t+this.intro,this},Le.prototype.prependLeft=function(t,e){if("string"!=typeof e)throw new TypeError("inserted content must be a string");this._split(t);var i=this.byEnd[t];return i?i.prependLeft(e):this.intro=e+this.intro,this},Le.prototype.prependRight=function(t,e){if("string"!=typeof e)throw new TypeError("inserted content must be a string");this._split(t);var i=this.byStart[t];return i?i.prependRight(e):this.outro=e+this.outro,this},Le.prototype.remove=function(t,e){for(;t<0;)t+=this.original.length;for(;e<0;)e+=this.original.length;if(t===e)return this;if(t<0||e>this.original.length)throw new Error("Character is out of bounds");if(t>e)throw new Error("end must be greater than start");this._split(t),this._split(e);for(var i=this.byStart[t];i;)i.intro="",i.outro="",i.edit(""),i=e>i.end?this.byStart[i.end]:null;return this},Le.prototype.lastChar=function(){if(this.outro.length)return this.outro[this.outro.length-1];var t=this.lastChunk;do{if(t.outro.length)return t.outro[t.outro.length-1];if(t.content.length)return t.content[t.content.length-1];if(t.intro.length)return t.intro[t.intro.length-1]}while(t=t.previous);return this.intro.length?this.intro[this.intro.length-1]:""},Le.prototype.lastLine=function(){var t=this.outro.lastIndexOf(Ae);if(-1!==t)return this.outro.substr(t+1);var e=this.outro,i=this.lastChunk;do{if(i.outro.length>0){if(-1!==(t=i.outro.lastIndexOf(Ae)))return i.outro.substr(t+1)+e;e=i.outro+e}if(i.content.length>0){if(-1!==(t=i.content.lastIndexOf(Ae)))return i.content.substr(t+1)+e;e=i.content+e}if(i.intro.length>0){if(-1!==(t=i.intro.lastIndexOf(Ae)))return i.intro.substr(t+1)+e;e=i.intro+e}}while(i=i.previous);return-1!==(t=this.intro.lastIndexOf(Ae))?this.intro.substr(t+1)+e:this.intro+e},Le.prototype.slice=function(t,e){for(void 0===t&&(t=0),void 0===e&&(e=this.original.length);t<0;)t+=this.original.length;for(;e<0;)e+=this.original.length;for(var i="",r=this.firstChunk;r&&(r.start>t||r.end<=t);){if(r.start =e)return i;r=r.next}if(r&&r.edited&&r.start!==t)throw new Error("Cannot use replaced character "+t+" as slice start anchor.");for(var n=r;r;){!r.intro||n===r&&r.start!==t||(i+=r.intro);var s=r.start =e;if(s&&r.edited&&r.end!==e)throw new Error("Cannot use replaced character "+e+" as slice end anchor.");if(i+=r.content.slice(n===r?t-r.start:0,s?r.content.length+e-r.end:r.content.length),!r.outro||s&&r.end!==e||(i+=r.outro),s)break;r=r.next}return i},Le.prototype.snip=function(t,e){var i=this.clone();return i.remove(0,t),i.remove(e,i.original.length),i},Le.prototype._split=function(t){if(!this.byStart[t]&&!this.byEnd[t])for(var e=this.lastSearchedChunk,i=t>e.end;e;){if(e.contains(t))return this._splitChunk(e,t);e=i?this.byStart[e.end]:this.byEnd[e.start]}},Le.prototype._splitChunk=function(t,e){if(t.edited&&t.content.length){var i=we(this.original)(e);throw new Error("Cannot split a chunk that has already been edited ("+i.line+":"+i.column+' \u2013 "'+t.original+'")')}var r=t.split(e);return this.byEnd[e]=t,this.byStart[e]=r,this.byEnd[r.end]=r,t===this.lastChunk&&(this.lastChunk=r),this.lastSearchedChunk=t,!0},Le.prototype.toString=function(){for(var t=this.intro,e=this.firstChunk;e;)t+=e.toString(),e=e.next;return t+this.outro},Le.prototype.isEmpty=function(){var t=this.firstChunk;do{if(t.intro.length&&t.intro.trim()||t.content.length&&t.content.trim()||t.outro.length&&t.outro.trim())return!1}while(t=t.next);return!0},Le.prototype.length=function(){var t=this.firstChunk,e=0;do{e+=t.intro.length+t.content.length+t.outro.length}while(t=t.next);return e},Le.prototype.trimLines=function(){return this.trim("[\\r\\n]")},Le.prototype.trim=function(t){return this.trimStart(t).trimEnd(t)},Le.prototype.trimEndAborted=function(t){var e=new RegExp((t||"\\s")+"+$");if(this.outro=this.outro.replace(e,""),this.outro.length)return!0;var i=this.lastChunk;do{var r=i.end,n=i.trimEnd(e);if(i.end!==r&&(this.lastChunk===i&&(this.lastChunk=i.next),this.byEnd[i.end]=i,this.byStart[i.next.start]=i.next,this.byEnd[i.next.end]=i.next),n)return!0;i=i.previous}while(i);return!1},Le.prototype.trimEnd=function(t){return this.trimEndAborted(t),this},Le.prototype.trimStartAborted=function(t){var e=new RegExp("^"+(t||"\\s")+"+");if(this.intro=this.intro.replace(e,""),this.intro.length)return!0;var i=this.firstChunk;do{var r=i.end,n=i.trimStart(e);if(i.end!==r&&(i===this.lastChunk&&(this.lastChunk=i.next),this.byEnd[i.end]=i,this.byStart[i.next.start]=i.next,this.byEnd[i.next.end]=i.next),n)return!0;i=i.next}while(i);return!1},Le.prototype.trimStart=function(t){return this.trimStartAborted(t),this};var Pe=function(){};function Te(t){var e=[];return je[t.type](e,t),e}Pe.prototype.ancestor=function(t){for(var e=this;t--;)if(!(e=e.parent))return null;return e},Pe.prototype.contains=function(t){for(;t;){if(t===this)return!0;t=t.parent}return!1},Pe.prototype.findLexicalBoundary=function(){return this.parent.findLexicalBoundary()},Pe.prototype.findNearest=function(t){return"string"==typeof t&&(t=new RegExp("^"+t+"$")),t.test(this.type)?this:this.parent.findNearest(t)},Pe.prototype.unparenthesizedParent=function(){for(var t=this.parent;t&&"ParenthesizedExpression"===t.type;)t=t.parent;return t},Pe.prototype.unparenthesize=function(){for(var t=this;"ParenthesizedExpression"===t.type;)t=t.expression;return t},Pe.prototype.findScope=function(t){return this.parent.findScope(t)},Pe.prototype.getIndentation=function(){return this.parent.getIndentation()},Pe.prototype.initialise=function(t){for(var e=0,i=this.keys;e e)return{line:i+1,column:e-s,char:i};s=a}throw new Error("Could not determine location of character")}(r,i.start);this.message=e+" ("+n.line+":"+n.column+")",this.stack=(new t).stack.replace(new RegExp(".+new "+this.name+".+\\n","m"),""),this.loc=n,this.snippet=Be(r,n,i.end-i.start)}}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.toString=function(){return this.name+": "+this.message+"\n"+this.snippet},e.missingTransform=function(t,i,r,n){throw void 0===n&&(n=null),new e("Transforming "+t+" is not "+(n?"fully supported":"implemented")+". Use `transforms: { "+i+": false }` to skip transformation and disable this error"+(n?", or `transforms: { "+n+": true }` if you know what you're doing":"")+".",r)},e}(Error);function Fe(t,e){for(var i=0;i 1&&(c=e(s),o.push((function(e,i,o){t.prependRight(n.start,(a?"":i+"var ")+c+" = "),t.overwrite(n.start,r=n.start+1,s),t.appendLeft(r,o),t.overwrite(n.start,r=n.start+1,(a?"":i+"var ")+c+" = "+s+o),t.move(n.start,r,e)}))),ze(t,e,i,n,c,a,o);break;case"ArrayPattern":if(t.remove(r,r=n.start),n.elements.filter(Boolean).length>1){var l=e(s);o.push((function(e,i,o){t.prependRight(n.start,(a?"":i+"var ")+l+" = "),t.overwrite(n.start,r=n.start+1,s,{contentOnly:!0}),t.appendLeft(r,o),t.move(n.start,r,e)})),n.elements.forEach((function(n,s){n&&("RestElement"===n.type?We(t,e,i,r,n.argument,l+".slice("+s+")",a,o):We(t,e,i,r,n,l+"["+s+"]",a,o),r=n.end)}))}else{var u=Fe(n.elements,Boolean),d=n.elements[u];"RestElement"===d.type?We(t,e,i,r,d.argument,s+".slice("+u+")",a,o):We(t,e,i,r,d,s+"["+u+"]",a,o),r=d.end}t.remove(r,n.end);break;default:throw new Error("Unexpected node type in destructuring ("+n.type+")")}}var He=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.createScope=function(){var t=this;this.parentIsFunction=/Function/.test(this.parent.type),this.isFunctionBlock=this.parentIsFunction||"Root"===this.parent.type,this.scope=new Re({block:!this.isFunctionBlock,parent:this.parent.findScope(!1),declare:function(e){return t.createdDeclarations.push(e)}}),this.parentIsFunction&&this.parent.params.forEach((function(e){t.scope.addDeclaration(e,"param")}))},e.prototype.initialise=function(t){this.thisAlias=null,this.argumentsAlias=null,this.defaultParameters=[],this.createdDeclarations=[],this.scope||this.createScope(),this.body.forEach((function(e){return e.initialise(t)})),this.scope.consolidate()},e.prototype.findLexicalBoundary=function(){return"Program"===this.type||/^Function/.test(this.parent.type)?this:this.parent.findLexicalBoundary()},e.prototype.findScope=function(t){return t&&!this.isFunctionBlock?this.parent.findScope(t):this.scope},e.prototype.getArgumentsAlias=function(){return this.argumentsAlias||(this.argumentsAlias=this.scope.createIdentifier("arguments")),this.argumentsAlias},e.prototype.getArgumentsArrayAlias=function(){return this.argumentsArrayAlias||(this.argumentsArrayAlias=this.scope.createIdentifier("argsArray")),this.argumentsArrayAlias},e.prototype.getThisAlias=function(){return this.thisAlias||(this.thisAlias=this.scope.createIdentifier("this")),this.thisAlias},e.prototype.getIndentation=function(){if(void 0===this.indentation){for(var t=this.program.magicString.original,e=this.synthetic||!this.body.length,i=e?this.start:this.body[0].start;i&&"\n"!==t[i];)i-=1;for(this.indentation="";;){var r=t[i+=1];if(" "!==r&&"\t"!==r)break;this.indentation+=r}for(var n=this.program.magicString.getIndentString(),s=this.parent;s;)"constructor"!==s.kind||s.parent.parent.superClass||(this.indentation=this.indentation.replace(n,"")),s=s.parent;e&&(this.indentation+=n)}return this.indentation},e.prototype.transpile=function(e,i){var r,n,s=this,a=this.getIndentation(),o=[];if(this.argumentsAlias&&o.push((function(t,i,r){e.appendLeft(t,i+"var "+s.argumentsAlias+" = arguments"+r)})),this.thisAlias&&o.push((function(t,i,r){e.appendLeft(t,i+"var "+s.thisAlias+" = this"+r)})),this.argumentsArrayAlias&&o.push((function(t,i,r){var n=s.scope.createIdentifier("i");e.appendLeft(t,i+"var "+n+" = arguments.length, "+s.argumentsArrayAlias+" = Array("+n+");\n"+a+"while ( "+n+"-- ) "+s.argumentsArrayAlias+"["+n+"] = arguments["+n+"]"+r)})),/Function/.test(this.parent.type)?this.transpileParameters(this.parent.params,e,i,a,o):"CatchClause"===this.parent.type&&this.transpileParameters([this.parent.param],e,i,a,o),i.letConst&&this.isFunctionBlock&&this.transpileBlockScopedIdentifiers(e),t.prototype.transpile.call(this,e,i),this.createdDeclarations.length&&o.push((function(t,i,r){var n=i+"var "+s.createdDeclarations.join(", ")+r;e.appendLeft(t,n)})),this.synthetic)if("ArrowFunctionExpression"===this.parent.type){var p=this.body[0];o.length?(e.appendLeft(this.start,"{").prependRight(this.end,this.parent.getIndentation()+"}"),e.prependRight(p.start,"\n"+a+"return "),e.appendLeft(p.end,";\n")):i.arrow&&(e.prependRight(p.start,"{ return "),e.appendLeft(p.end,"; }"))}else o.length&&e.prependRight(this.start,"{").appendLeft(this.end,"}");r=(n=this.body[0])&&"ExpressionStatement"===n.type&&"Literal"===n.expression.type&&"use strict"===n.expression.value?this.body[0].end:this.synthetic||"Root"===this.parent.type?this.start:this.start+1;var h="\n"+a,c=";";o.forEach((function(t,e){e===o.length-1&&(c=";\n"),t(r,h,c)}))},e.prototype.transpileParameters=function(t,e,i,r,n){var s=this;t.forEach((function(a){if("AssignmentPattern"===a.type&&"Identifier"===a.left.type)i.defaultParameter&&n.push((function(t,i,r){e.prependRight(a.left.end,i+"if ( "+a.left.name+" === void 0 ) "+a.left.name).move(a.left.end,a.right.end,t).appendLeft(a.right.end,r)}));else if("RestElement"===a.type)i.spreadRest&&n.push((function(i,n,o){var p=t[t.length-2];if(p)e.remove(p?p.end:a.start,a.end);else{for(var h=a.start,c=a.end;/\s/.test(e.original[h-1]);)h-=1;for(;/\s/.test(e.original[c]);)c+=1;e.remove(h,c)}var l=a.argument.name,u=s.scope.createIdentifier("len"),d=t.length-1;e.prependRight(i,d?n+"var "+l+" = [], "+u+" = arguments.length - "+d+";\n"+r+"while ( "+u+"-- > 0 ) "+l+"[ "+u+" ] = arguments[ "+u+" + "+d+" ]"+o:n+"var "+l+" = [], "+u+" = arguments.length;\n"+r+"while ( "+u+"-- ) "+l+"[ "+u+" ] = arguments[ "+u+" ]"+o)}));else if("Identifier"!==a.type&&i.parameterDestructuring){var o=s.scope.createIdentifier("ref");Ue(e,(function(t){return s.scope.createIdentifier(t)}),(function(t){return s.scope.resolveName(t.name)}),a,o,!1,n),e.prependRight(a.start,o)}}))},e.prototype.transpileBlockScopedIdentifiers=function(t){var e=this;Object.keys(this.scope.blockScopedDeclarations).forEach((function(i){for(var r=0,n=e.scope.blockScopedDeclarations[i];r this.start?e.overwrite(this.start,o,h):e.prependRight(this.start,h)}else t.prototype.transpile.call(this,e,i);i.trailingFunctionCommas&&this.params.length&&!s&&$e(e,this.params[this.params.length-1].end)},e.prototype.needsArguments=function(t){return t.spreadRest&&this.params.filter((function(t){return"RestElement"===t.type})).length>0},e}(Pe);function Ye(t,e){var i=e.findDeclaration(t.name);if(i&&"const"===i.kind)throw new De(t.name+" is read-only",t)}var ti=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){if("Identifier"===this.left.type){var i=this.findScope(!1).findDeclaration(this.left.name),r=i&&i.node.ancestor(3);r&&"ForStatement"===r.type&&r.body.contains(this)&&(r.reassigned[this.left.name]=!0)}t.prototype.initialise.call(this,e)},e.prototype.transpile=function(e,i){"Identifier"===this.left.type&&Ye(this.left,this.findScope(!1)),"**="===this.operator&&i.exponentiation?this.transpileExponentiation(e,i):/Pattern/.test(this.left.type)&&i.destructuring&&this.transpileDestructuring(e),t.prototype.transpile.call(this,e,i)},e.prototype.transpileDestructuring=function(t){var e=this,i=this.findScope(!0),r=this.findScope(!1),n=i.createDeclaration("assign");t.appendRight(this.left.end,"("+n),t.appendLeft(this.right.end,", ");var s=[];Ue(t,(function(t){return i.createDeclaration(t)}),(function(t){var e=r.resolveName(t.name);return Ye(t,r),e}),this.left,n,!0,s);var a=", ";s.forEach((function(t,i){i===s.length-1&&(a=""),t(e.end,"",a)})),"ExpressionStatement"===this.unparenthesizedParent().type?t.prependRight(this.end,")"):t.appendRight(this.end,", "+n+")")},e.prototype.transpileExponentiation=function(t){for(var e,i=this.findScope(!1),r=this.left.end;"*"!==t.original[r];)r+=1;t.remove(r,r+2);var n=this.left.unparenthesize();if("Identifier"===n.type)e=i.resolveName(n.name);else if("MemberExpression"===n.type){var s,a,o=!1,p=!1,h=this.findNearest(/(?:Statement|Declaration)$/),c=h.getIndentation();"Identifier"===n.property.type?a=n.computed?i.resolveName(n.property.name):n.property.name:(a=i.createDeclaration("property"),p=!0),"Identifier"===n.object.type?s=i.resolveName(n.object.name):(s=i.createDeclaration("object"),o=!0),n.start===h.start?o&&p?(t.prependRight(h.start,s+" = "),t.overwrite(n.object.end,n.property.start,";\n"+c+a+" = "),t.overwrite(n.property.end,n.end,";\n"+c+s+"["+a+"]")):o?(t.prependRight(h.start,s+" = "),t.appendLeft(n.object.end,";\n"+c),t.appendLeft(n.object.end,s)):p&&(t.prependRight(n.property.start,a+" = "),t.appendLeft(n.property.end,";\n"+c),t.move(n.property.start,n.property.end,this.start),t.appendLeft(n.object.end,"["+a+"]"),t.remove(n.object.end,n.property.start),t.remove(n.property.end,n.end)):(o&&p?(t.prependRight(n.start,"( "+s+" = "),t.overwrite(n.object.end,n.property.start,", "+a+" = "),t.overwrite(n.property.end,n.end,", "+s+"["+a+"]")):o?(t.prependRight(n.start,"( "+s+" = "),t.appendLeft(n.object.end,", "+s)):p&&(t.prependRight(n.property.start,"( "+a+" = "),t.appendLeft(n.property.end,", "),t.move(n.property.start,n.property.end,n.start),t.overwrite(n.object.end,n.property.start,"["+a+"]"),t.remove(n.property.end,n.end)),p&&t.appendLeft(this.end," )")),e=s+(n.computed||p?"["+a+"]":"."+a)}t.prependRight(this.right.start,"Math.pow( "+e+", "),t.appendLeft(this.right.end," )")},e}(Pe),ei=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){e.asyncAwait&&De.missingTransform("await","asyncAwait",this),t.prototype.initialise.call(this,e)},e}(Pe),ii=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i){"**"===this.operator&&i.exponentiation&&(e.prependRight(this.start,"Math.pow( "),e.overwrite(this.left.end,this.right.start,", "),e.appendLeft(this.end," )")),t.prototype.transpile.call(this,e,i)},e}(Pe),ri=/(?:For(?:In|Of)?|While)Statement/,ni=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(){var t=this.findNearest(ri),e=this.findNearest("SwitchCase");t&&(!e||t.depth>e.depth)&&(t.canBreak=!0,this.loop=t)},e.prototype.transpile=function(t){if(this.loop&&this.loop.shouldRewriteAsFunction){if(this.label)throw new De("Labels are not currently supported in a loop with locally-scoped variables",this);t.overwrite(this.start,this.start+5,"return 'break'")}},e}(Pe),si=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){if(e.spreadRest&&this.arguments.length>1)for(var i=this.findLexicalBoundary(),r=this.arguments.length;r--;){var n=this.arguments[r];"SpreadElement"===n.type&&Xe(n.argument)&&(this.argumentsArrayAlias=i.getArgumentsArrayAlias())}t.prototype.initialise.call(this,e)},e.prototype.transpile=function(e,i){if(i.spreadRest&&this.arguments.length&&Je(e,this,this.arguments),i.spreadRest&&this.arguments.length){var r,n=!1,s=this.arguments[0];if(1===this.arguments.length?"SpreadElement"===s.type&&(e.remove(s.start,s.argument.start),n=!0):n=Ge(e,this.arguments,s.start,this.argumentsArrayAlias),n){var a=null;if("Super"===this.callee.type?a=this.callee:"MemberExpression"===this.callee.type&&"Super"===this.callee.object.type&&(a=this.callee.object),a||"MemberExpression"!==this.callee.type)r="void 0";else if("Identifier"===this.callee.object.type)r=this.callee.object.name;else{r=this.findScope(!0).createDeclaration("ref");var o=this.callee.object;e.prependRight(o.start,"("+r+" = "),e.appendLeft(o.end,")")}e.appendLeft(this.callee.end,".apply"),a?(a.noCall=!0,this.arguments.length>1&&("SpreadElement"===s.type?Ke(s.argument)&&e.prependRight(s.start,"( "):e.prependRight(s.start,"[ "),e.appendLeft(this.arguments[this.arguments.length-1].end," )"))):1===this.arguments.length?e.prependRight(s.start,r+", "):("SpreadElement"===s.type?Ke(s.argument)?e.appendLeft(s.start,r+", ( "):e.appendLeft(s.start,r+", "):e.appendLeft(s.start,r+", [ "),e.appendLeft(this.arguments[this.arguments.length-1].end," )"))}}i.trailingFunctionCommas&&this.arguments.length&&$e(e,this.arguments[this.arguments.length-1].end),t.prototype.transpile.call(this,e,i)},e}(Pe),ai=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i,r,n){var s=this;if(i.classes){var a=this.parent.name,o=e.getIndentString(),p=this.getIndentation()+(r?o:""),h=p+o,c=Fe(this.body,(function(t){return"constructor"===t.kind})),l=this.body[c],u="",d="";if(this.body.length?(e.remove(this.start,this.body[0].start),e.remove(this.body[this.body.length-1].end,this.end)):e.remove(this.start,this.end),l){l.value.body.isConstructorBody=!0;var f=this.body[c+1];c>0&&(e.remove(this.body[c-1].end,l.start),e.move(l.start,f?f.start:this.end-1,this.body[0].start)),r||e.appendLeft(l.end,";")}var m=[];this.body.forEach((function(t){if("FieldDefinition"===t.type&&(m.push(t.computed?"this"+e.slice(t.start,t.end)+";":"this."+e.slice(t.start,t.end)+";"),e.remove(t.start,t.end),""!==e.byStart[t.end].content)){for(var i=0;i 0&&e.remove(t.end,t.end+i)}}));var g=!1!==this.program.options.namedFunctionExpressions,y=g||this.parent.superClass||"ClassDeclaration"!==this.parent.type;if(this.parent.superClass){var v="if ( "+n+" ) "+a+".__proto__ = "+n+";\n"+p+a+".prototype = Object.create( "+n+" && "+n+".prototype );\n"+p+a+".prototype.constructor = "+a+";";u+=l?"\n\n"+p+v:(v="function "+a+" () {"+(m.length?"\n"+h+m.join("\n"+h)+"\n"+h:"")+(n?"\n"+h+n+".apply(this, arguments);\n"+p+"}":"}")+(r?"":";")+(this.body.length?"\n\n"+p:"")+v)+"\n\n"+p}else if(!l){var x="function "+(y?a+" ":"")+"() {"+(m.length?"\n"+h+m.join("\n"+h)+"\n"+p:"")+"}";"ClassDeclaration"===this.parent.type&&(x+=";"),this.body.length&&(x+="\n\n"+p),u+=x}l&&m.length&&e.appendLeft(l.value.body.start+1,"\n"+h+m.join("\n"+h));var b,_,k=this.findScope(!1),S=[],E=[];if(this.body.forEach((function(t,r){if("get"!==t.kind&&"set"!==t.kind||!i.getterSetter||De.missingTransform("getters and setters","getterSetter",t),"FieldDefinition"!==t.type)if("constructor"!==t.kind){t.static&&e.remove(t.start,t.start+(" "==e.original[t.start+6]?7:6));var n,o="method"!==t.kind,h=t.key.name;(Oe[h]||t.value.body.scope.references[h])&&(h=k.createIdentifier(h));var l=!1;if(t.computed||"Literal"!==t.key.type||(l=!0,t.computed=!0),o){if(t.computed)throw new Error("Computed accessor properties are not currently supported");e.remove(t.start,t.key.start),t.static?(~E.indexOf(t.key.name)||E.push(t.key.name),_||(_=k.createIdentifier("staticAccessors")),n=""+_):(~S.indexOf(t.key.name)||S.push(t.key.name),b||(b=k.createIdentifier("prototypeAccessors")),n=""+b)}else n=t.static?""+a:a+".prototype";t.computed||(n+="."),(c>0&&r===c+1||0===r&&c===s.body.length-1)&&(n="\n\n"+p+n);var u=t.key.end;if(t.computed)if(l)e.prependRight(t.key.start,"["),e.appendLeft(t.key.end,"]");else{for(;"]"!==e.original[u];)u+=1;u+=1}var d=(o?"."+t.kind:"")+" = "+(t.value.async?"async ":"")+"function"+(t.value.generator?"* ":" ")+(t.computed||o||!g?"":h+" ");e.remove(u,t.value.start),e.prependRight(t.value.start,d),e.appendLeft(t.end,";"),t.value.generator&&e.remove(t.start,t.key.start);var f=t.key.start;if(t.computed&&!l)for(;"["!=e.original[f];)--f;t.start this.depth){this.shouldRewriteAsFunction=!0;for(var o=0,p=this.thisRefs;o e.depth&&(this.alias=e.getArgumentsAlias()),r&&r.body.contains(this)&&r.depth>e.depth&&(this.alias=e.getArgumentsAlias())}this.findScope(!1).addReference(this)}},e.prototype.transpile=function(t){this.alias&&t.overwrite(this.start,this.end,this.alias,{storeName:!0,contentOnly:!0})},e}(Pe),xi=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){t.prototype.initialise.call(this,e)},e.prototype.transpile=function(e,i){("BlockStatement"!==this.consequent.type||"BlockStatement"===this.consequent.type&&this.consequent.synthetic)&&(e.appendLeft(this.consequent.start,"{ "),e.prependRight(this.consequent.end," }")),this.alternate&&"IfStatement"!==this.alternate.type&&("BlockStatement"!==this.alternate.type||"BlockStatement"===this.alternate.type&&this.alternate.synthetic)&&(e.appendLeft(this.alternate.start,"{ "),e.prependRight(this.alternate.end," }")),t.prototype.transpile.call(this,e,i)},e}(Pe),bi=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){e.moduleImport&&De.missingTransform("dynamic import expressions","moduleImport",this),t.prototype.initialise.call(this,e)},e}(Pe),_i=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){e.moduleImport&&De.missingTransform("import","moduleImport",this),t.prototype.initialise.call(this,e)},e}(Pe),ki=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){this.findScope(!0).addDeclaration(this.local,"import"),t.prototype.initialise.call(this,e)},e}(Pe),Si=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){this.findScope(!0).addDeclaration(this.local,"import"),t.prototype.initialise.call(this,e)},e}(Pe),Ei=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i){var r,n=this.name;e.overwrite(n.start,this.value?this.value.start:this.name.end,(/-/.test(r=n.name)?"'"+r+"'":r)+": "+(this.value?"":"true")),t.prototype.transpile.call(this,e,i)},e}(Pe),wi=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(t){var e,i=!0,r=this.parent.children[this.parent.children.length-1];(r&&"JSXText"===(e=r).type&&!/\S/.test(e.value)&&/\n/.test(e.value)||this.parent.openingElement.attributes.length)&&(i=!1),t.overwrite(this.start,this.end,i?" )":")")},e}(Pe),Ci=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(t){var e,i=!0,r=this.parent.children[this.parent.children.length-1];r&&"JSXText"===(e=r).type&&!/\S/.test(e.value)&&/\n/.test(e.value)&&(i=!1),t.overwrite(this.start,this.end,i?" )":")")},e}(Pe);function Ai(t,e){return t=t.replace(/\u00a0/g," "),e&&/\n/.test(t)&&(t=t.replace(/\s+$/,"")),t=t.replace(/^\n\r?\s+/,"").replace(/\s*\n\r?\s*/gm," "),JSON.stringify(t)}var Ii=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i){t.prototype.transpile.call(this,e,i);var r=this.children.filter((function(t){return"JSXText"!==t.type||/\S/.test(t.raw)||!/\n/.test(t.raw)}));if(r.length){var n,s=(this.openingElement||this.openingFragment).end;for(n=0;n 0&&(c.start===s?e.prependRight(s,", "):e.overwrite(s,c.start,", ")),h&&"JSXSpreadAttribute"!==c.type){var l=this.attributes[a-1],u=this.attributes[a+1];l&&"JSXSpreadAttribute"!==l.type||e.prependRight(c.start,"{ "),u&&"JSXSpreadAttribute"!==u.type||e.appendLeft(c.end," }")}s=c.end}if(h)if(1===n)p=r?"',":",";else{if(!this.program.options.objectAssign)throw new De("Mixed JSX attributes ending in spread requires specified objectAssign option with 'Object.assign' or polyfill helper.",this);p=r?"', "+this.program.options.objectAssign+"({},":", "+this.program.options.objectAssign+"({},",o=")"}else p=r?"', {":", {",o=" }";e.prependRight(this.name.end,p),o&&e.appendLeft(this.attributes[n-1].end,o)}else e.appendLeft(this.name.end,r?"', null":", null"),s=this.name.end;this.selfClosing?e.overwrite(s,this.end,this.attributes.length?")":" )"):e.remove(s,this.end)},e}(Pe),JSXOpeningFragment:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(t){t.overwrite(this.start,this.end,this.program.jsx+"( React.Fragment, null")},e}(Pe),JSXSpreadAttribute:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i){e.remove(this.start,this.argument.start),e.remove(this.argument.end,this.end),t.prototype.transpile.call(this,e,i)},e}(Pe),Literal:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(){"string"==typeof this.value&&this.program.indentExclusionElements.push(this)},e.prototype.transpile=function(t,e){e.numericLiteral&&this.raw.match(/^0[bo]/i)&&t.overwrite(this.start,this.end,String(this.value),{storeName:!0,contentOnly:!0}),"string"==typeof this.value&&this.value.match(Li)&&t.overwrite(this.start,this.end,this.raw.replace(Li,(function(t){return"\u2028"==t?"\\u2028":"\\u2029"})),{contentOnly:!0})},e}(Pe),MemberExpression:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i){i.reservedProperties&&Oe[this.property.name]&&(e.overwrite(this.object.end,this.property.start,"['"),e.appendLeft(this.property.end,"']")),t.prototype.transpile.call(this,e,i)},e}(Pe),NewExpression:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){if(e.spreadRest&&this.arguments.length)for(var i=this.findLexicalBoundary(),r=this.arguments.length;r--;){var n=this.arguments[r];if("SpreadElement"===n.type&&Xe(n.argument)){this.argumentsArrayAlias=i.getArgumentsArrayAlias();break}}t.prototype.initialise.call(this,e)},e.prototype.transpile=function(e,i){if(t.prototype.transpile.call(this,e,i),i.spreadRest&&this.arguments.length&&Je(e,this,this.arguments),i.spreadRest&&this.arguments.length){var r=this.arguments[0];Ge(e,this.arguments,r.start,this.argumentsArrayAlias,!0)&&(e.prependRight(this.start+"new".length," (Function.prototype.bind.apply("),e.overwrite(this.callee.end,r.start,", [ null ].concat( "),e.appendLeft(this.end," ))"))}this.arguments.length&&$e(e,this.arguments[this.arguments.length-1].end)},e}(Pe),ObjectExpression:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i){var r;t.prototype.transpile.call(this,e,i);for(var n=this.start+1,s=0,a=0,o=null,p=null,h=0;h 0?(e.remove(c.start,l.properties[0].start),e.remove(l.properties[l.properties.length-1].end,c.end),(r=this.properties).splice.apply(r,[h,1].concat(l.properties)),h--):(e.remove(c.start,h===this.properties.length-1?c.end:this.properties[h+1].start),this.properties.splice(h,1),h--):(s+=1,null===o&&(o=h))}else c.computed&&i.computedProperty&&(a+=1,null===p&&(p=h))}if(!s||i.objectRestSpread||a&&i.computedProperty){if(s){if(!this.program.options.objectAssign)throw new De("Object spread operator requires specified objectAssign option with 'Object.assign' or polyfill helper.",this);for(var u=this.properties.length;u--;){var d=this.properties[u];if("Property"===d.type&&!a){var f=this.properties[u-1],m=this.properties[u+1];f&&"Property"===f.type||e.prependRight(d.start,"{"),m&&"Property"===m.type||e.appendLeft(d.end,"}")}"SpreadElement"===d.type&&(e.remove(d.start,d.argument.start),e.remove(d.argument.end,d.end))}n=this.properties[0].start,a?"SpreadElement"===this.properties[0].type?(e.overwrite(this.start,n,this.program.options.objectAssign+"({}, "),e.remove(this.end-1,this.end),e.appendRight(this.end,")")):(e.prependLeft(this.start,this.program.options.objectAssign+"("),e.appendRight(this.end,")")):(e.overwrite(this.start,n,this.program.options.objectAssign+"({}, "),e.overwrite(this.properties[this.properties.length-1].end,this.end,")"))}}else s=0,o=null;if(a&&i.computedProperty){var g,y,v=this.getIndentation();"VariableDeclarator"===this.parent.type&&1===this.parent.parent.declarations.length&&"Identifier"===this.parent.id.type?(g=!0,y=this.parent.id.alias||this.parent.id.name):("AssignmentExpression"===this.parent.type&&"ExpressionStatement"===this.parent.parent.type&&"Identifier"===this.parent.left.type||"AssignmentPattern"===this.parent.type&&"Identifier"===this.parent.left.type)&&(g=!0,y=this.parent.left.alias||this.parent.left.name),s&&(g=!1),y=this.findScope(!1).resolveName(y);var x=n,b=this.end;g||(null===o||p 0?this.properties[w-1].end:x;if("Property"===C.type&&(C.computed||_&&!s)){if(0===w&&(A=this.start+1),_=C,y){var I=(g?";\n"+v+y:", "+y)+("Literal"===C.key.type||C.computed?"":".");A L&&e.remove(L,C.value.start),e.prependLeft(L," = ")):e.overwrite(C.start,C.key.end+1,"["+e.slice(C.start,C.key.end)+"] = "),!C.method||!C.computed&&i.conciseMethodProperty||(C.value.generator&&e.remove(C.start,C.key.start),e.prependRight(C.value.start,"function"+(C.value.generator?"*":"")+" "))}else"SpreadElement"===C.type?y&&w>0&&(_||(_=this.properties[w-1]),e.appendLeft(_.end,", "+y+" )"),_=null,y=null):(!E&&s&&(e.prependRight(C.start,"{"),e.appendLeft(C.end,"}")),S=!0);if(E&&("SpreadElement"===C.type||C.computed)){var N=S?this.properties[this.properties.length-1].end:this.end-1;","==e.original[N]&&++N;var P=e.slice(N,b);e.prependLeft(A,P),e.remove(N,b),E=!1}var T=C.end;if(w this.nearestFunction.depth)&&(this.loop.canReturn=!0,this.shouldWrap=!0),this.argument&&this.argument.initialise(t)},e.prototype.transpile=function(t,e){var i=this.shouldWrap&&this.loop&&this.loop.shouldRewriteAsFunction;this.argument?(i&&t.prependRight(this.argument.start,"{ v: "),this.argument.transpile(t,e),i&&t.appendLeft(this.argument.end," }")):i&&t.appendLeft(this.start+6," {}")},e}(Pe),Super:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(t){if(t.classes){if(this.method=this.findNearest("MethodDefinition"),!this.method)throw new De("use of super outside class method",this);var e=this.findNearest("ClassBody").parent;if(this.superClassName=e.superClass&&(e.superClass.name||"superclass"),!this.superClassName)throw new De("super used in base class",this);if(this.isCalled="CallExpression"===this.parent.type&&this===this.parent.callee,"constructor"!==this.method.kind&&this.isCalled)throw new De("super() not allowed outside class constructor",this);if(this.isMember="MemberExpression"===this.parent.type,!this.isCalled&&!this.isMember)throw new De("Unexpected use of `super` (expected `super(...)` or `super.*`)",this)}if(t.arrow){var i=this.findLexicalBoundary(),r=this.findNearest("ArrowFunctionExpression"),n=this.findNearest(ri);r&&r.depth>i.depth&&(this.thisAlias=i.getThisAlias()),n&&n.body.contains(this)&&n.depth>i.depth&&(this.thisAlias=i.getThisAlias())}},e.prototype.transpile=function(t,e){if(e.classes){t.overwrite(this.start,this.end,this.isCalled||this.method.static?this.superClassName:this.superClassName+".prototype",{storeName:!0,contentOnly:!0});var i=this.isCalled?this.parent:this.parent.parent;if(i&&"CallExpression"===i.type){this.noCall||t.appendLeft(i.callee.end,".call");var r=this.thisAlias||"this";i.arguments.length?t.appendLeft(i.arguments[0].start,r+", "):t.appendLeft(i.end-1,""+r)}}},e}(Pe),TaggedTemplateExpression:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){e.templateString&&!e.dangerousTaggedTemplateString&&De.missingTransform("tagged template strings","templateString",this,"dangerousTaggedTemplateString"),t.prototype.initialise.call(this,e)},e.prototype.transpile=function(e,i){if(i.templateString&&i.dangerousTaggedTemplateString){var r=this.quasi.expressions.concat(this.quasi.quasis).sort((function(t,e){return t.start-e.start})),n=this.program.body.scope,s=this.quasi.quasis.map((function(t){return JSON.stringify(t.value.cooked)})).join(", "),a=this.program.templateLiteralQuasis[s];a||(a=n.createIdentifier("templateObject"),e.prependLeft(this.program.prependAt,"var "+a+" = Object.freeze(["+s+"]);\n"),this.program.templateLiteralQuasis[s]=a),e.overwrite(this.tag.end,r[0].start,"("+a);var o=r[0].start;r.forEach((function(t){"TemplateElement"===t.type?e.remove(o,t.end):e.overwrite(o,t.start,", "),o=t.end})),e.overwrite(o,this.end,")")}t.prototype.transpile.call(this,e,i)},e}(Pe),TemplateElement:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(){this.program.indentExclusionElements.push(this)},e}(Pe),TemplateLiteral:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.transpile=function(e,i){if(t.prototype.transpile.call(this,e,i),i.templateString&&"TaggedTemplateExpression"!==this.parent.type){var r=this.expressions.concat(this.quasis).sort((function(t,e){return t.start-e.start||t.end-e.end})).filter((function(t,e){return"TemplateElement"!==t.type||!!t.value.raw||!e}));if(r.length>=3){var n=r[0];"TemplateElement"===n.type&&""===n.value.raw&&"TemplateElement"===r[2].type&&r.shift()}var s=!(1===this.quasis.length&&0===this.expressions.length||"TemplateLiteral"===this.parent.type||"AssignmentExpression"===this.parent.type||"AssignmentPattern"===this.parent.type||"VariableDeclarator"===this.parent.type||"BinaryExpression"===this.parent.type&&"+"===this.parent.operator);s&&e.appendRight(this.start,"(");var a=this.start;r.forEach((function(t,i){var r=0===i?s?"(":"":" + ";if("TemplateElement"===t.type)e.overwrite(a,t.end,r+JSON.stringify(t.value.cooked));else{var n="Identifier"!==t.type;n&&(r+="("),e.remove(a,t.start),r&&e.prependRight(t.start,r),n&&e.appendLeft(t.end,")")}a=t.end})),s&&e.appendLeft(a,")"),e.overwrite(a,this.end,"",{contentOnly:!0})}},e}(Pe),ThisExpression:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(t){var e=this.findLexicalBoundary();if(t.letConst)for(var i=this.findNearest(ri);i&&i.depth>e.depth;)i.thisRefs.push(this),i=i.parent.findNearest(ri);if(t.arrow){var r=this.findNearest("ArrowFunctionExpression");r&&r.depth>e.depth&&(this.alias=e.getThisAlias())}},e.prototype.transpile=function(t){this.alias&&t.overwrite(this.start,this.end,this.alias,{storeName:!0,contentOnly:!0})},e}(Pe),UpdateExpression:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){if("Identifier"===this.argument.type){var i=this.findScope(!1).findDeclaration(this.argument.name),r=i&&i.node.ancestor(3);r&&"ForStatement"===r.type&&r.body.contains(this)&&(r.reassigned[this.argument.name]=!0)}t.prototype.initialise.call(this,e)},e.prototype.transpile=function(e,i){"Identifier"===this.argument.type&&Ye(this.argument,this.findScope(!1)),t.prototype.transpile.call(this,e,i)},e}(Pe),VariableDeclaration:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(t){this.scope=this.findScope("var"===this.kind),this.declarations.forEach((function(e){return e.initialise(t)}))},e.prototype.transpile=function(t,e){var i=this,r=this.getIndentation(),n=this.kind;if(e.letConst&&"var"!==n&&t.overwrite(this.start,this.start+this.kind.length,n="var",{contentOnly:!0,storeName:!0}),e.destructuring&&"ForOfStatement"!==this.parent.type&&"ForInStatement"!==this.parent.type){var s,a=this.start;this.declarations.forEach((function(n,o){if(n.transpile(t,e),"Identifier"===n.id.type)o>0&&"Identifier"!==i.declarations[o-1].id.type&&t.overwrite(a,n.id.start,"var ");else{var p=ri.test(i.parent.type);0===o?t.remove(a,n.id.start):t.overwrite(a,n.id.start,";\n"+r);var h="Identifier"===n.init.type&&!n.init.rewritten,c=h?n.init.alias||n.init.name:n.findScope(!0).createIdentifier("ref");a=n.start;var l=[];h?t.remove(n.id.end,n.end):l.push((function(e,i,r){t.prependRight(n.id.end,"var "+c),t.appendLeft(n.init.end,""+r),t.move(n.id.end,n.end,e)}));var u=n.findScope(!1);Ue(t,(function(t){return u.createIdentifier(t)}),(function(t){return u.resolveName(t.name)}),n.id,c,p,l);var d=p?"var ":"",f=p?", ":";\n"+r;l.forEach((function(t,e){o===i.declarations.length-1&&e===l.length-1&&(f=p?"":";"),t(n.start,0===e?d:"",f)}))}a=n.end,s="Identifier"!==n.id.type})),s&&this.end>a&&t.overwrite(a,this.end,"",{contentOnly:!0})}else this.declarations.forEach((function(i){i.transpile(t,e)}))},e}(Pe),VariableDeclarator:function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e,e.prototype.initialise=function(e){var i=this.parent.kind;"let"===i&&"ForStatement"===this.parent.parent.type&&(i="for.let"),this.parent.scope.addDeclaration(this.id,i),t.prototype.initialise.call(this,e)},e.prototype.transpile=function(t,e){if(!this.init&&e.letConst&&"var"!==this.parent.kind){var i=this.findNearest(/Function|^For(In|Of)?Statement|^(?:Do)?WhileStatement/);!i||/Function/.test(i.type)||this.isLeftDeclaratorOfLoop()||t.appendLeft(this.id.end," = (void 0)")}this.id&&this.id.transpile(t,e),this.init&&this.init.transpile(t,e)},e.prototype.isLeftDeclaratorOfLoop=function(){return this.parent&&"VariableDeclaration"===this.parent.type&&this.parent.parent&&("ForInStatement"===this.parent.parent.type||"ForOfStatement"===this.parent.parent.type)&&this.parent.parent.left&&this.parent.parent.left.declarations[0]===this},e}(Pe),WhileStatement:ui},Pi={Program:["body"],Literal:[]},Ti={IfStatement:"consequent",ForStatement:"body",ForInStatement:"body",ForOfStatement:"body",WhileStatement:"body",DoWhileStatement:"body",ArrowFunctionExpression:"body"};function ji(t,e,i,r){this.type="Root",this.jsx=r.jsx||"React.createElement",this.options=r,this.source=t,this.magicString=new Le(t),this.ast=e,this.depth=0,function t(e,i){if(e)if("length"in e)for(var r=e.length;r--;)t(e[r],i);else if(!e.__wrapped){e.__wrapped=!0,Pi[e.type]||(Pi[e.type]=Object.keys(e).filter((function(t){return"object"==typeof e[t]})));var n=Ti[e.type];if(n&&"BlockStatement"!==e[n].type){var s=e[n];e[n]={start:s.start,end:s.end,type:"BlockStatement",body:[s],synthetic:!0}}e.parent=i,e.program=i.program||i,e.depth=i.depth+1,e.keys=Pi[e.type],e.indentation=void 0;for(var a=0,o=Pi[e.type];a r[t]-r[e])),this.raise(r[t[0]],"Usage of undeclared private name"))}return n}parseClassElement(t){if(this.eat(fe.semi))return null;const e=this.startNode();if(!(this.options.ecmaVersion>=8)||this.type!=ye){if(this.isContextual("async")){de.lastIndex=this.pos;let t=de.exec(this.input),i=this.input.charAt(this.pos+t[0].length);if(";"===i||"="===i)return e.key=this.parseIdent(!0),e.computed=!1,me.call(this,e),this.finishNode(e,"FieldDefinition"),this.semicolon(),e}return super.parseClassElement.apply(this,arguments)}return e.key=ge.call(this),e.computed=!1,"constructor"==e.key.name&&this.raise(e.start,"Classes may not have a field named constructor"),Object.prototype.hasOwnProperty.call(this._privateBoundNamesStack[this._privateBoundNamesStack.length-1],e.key.name)&&this.raise(e.start,"Duplicate private element"),this._privateBoundNamesStack[this._privateBoundNamesStack.length-1][e.key.name]=!0,delete this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length-1][e.key.name],me.call(this,e),this.finishNode(e,"FieldDefinition"),this.semicolon(),e}parseClassMethod(t,e,i,r){return e||i||"method"!=t.kind||t.static||this.options.ecmaVersion<8||this.type==fe.parenL?super.parseClassMethod.apply(this,arguments):(me.call(this,t),delete t.kind,delete t.static,t=this.finishNode(t,"FieldDefinition"),this.semicolon(),t)}parseSubscripts(t,e,i,r){for(let n;;){if(!(n=this.eat(fe.bracketL))&&!this.eat(fe.dot))return super.parseSubscripts(t,e,i,r);{let r=this.startNodeAt(e,i);r.object=t,n?r.property=this.parseExpression():this.type==ye?(r.property=ge.call(this),this._privateBoundNamesStack.length&&this._privateBoundNamesStack[this._privateBoundNamesStack.length-1][r.property.name]||(this._unresolvedPrivateNamesStack[this._unresolvedPrivateNamesStack.length-1][r.property.name]=r.property.start)):r.property=this.parseIdent(!0),r.computed=Boolean(n),n&&this.expect(fe.bracketR),t=this.finishNode(r,"MemberExpression")}}}parseMaybeUnary(t,e){const i=super.parseMaybeUnary(t,e);return"delete"==i.operator&&"MemberExpression"==i.argument.type&&"PrivateName"==i.argument.property.type&&this.raise(i.start,"Private elements may not be deleted"),i}parseIdent(t,e){const i=super.parseIdent(t,e);return this._inFieldValue&&"arguments"==i.name&&this.raise(i.start,"A class field initializer may not contain arguments"),i}parseExprAtom(t){const e=super.parseExprAtom(t);return this._inFieldValue&&"Super"==e.type&&this.raise(e.start,"A class field initializer may not contain super"),e}}})),Ri=["getterSetter","arrow","classes","computedProperty","conciseMethodProperty","defaultParameter","destructuring","forOf","generator","letConst","moduleExport","moduleImport","numericLiteral","parameterDestructuring","spreadRest","stickyRegExp","templateString","exponentiation","reservedProperties","trailingFunctionCommas","asyncAwait","objectRestSpread"],Vi=["dangerousTaggedTemplateString","dangerousForOf"];function Bi(t,e){return function(t,e){var i;void 0===e&&(e={});var r=null;try{i=Oi.parse(t,{ecmaVersion:10,preserveParens:!0,sourceType:"module",allowAwaitOutsideFunction:!0,allowReturnOutsideFunction:!0,allowHashBang:!0,onComment:function(t,e){if(!r){var i=/@jsx\s+([^\s]+)/.exec(e);i&&(r=i[1])}}}),e.jsx=r||e.jsx}catch(e){throw e.snippet=Be(t,e.loc),e.toString=function(){return e.name+": "+e.message+"\n"+e.snippet},e}var n=Object.create(null);return Ri.forEach((function(t){n[t]=!0})),Vi.forEach((function(t){n[t]=!0})),Object.keys(e.transforms||{}).forEach((function(t){if("modules"===t)return"moduleImport"in e.transforms||(n.moduleImport=e.transforms.modules),void("moduleExport"in e.transforms||(n.moduleExport=e.transforms.modules));if(!(t in n))throw new Error("Unknown transform '"+t+"'");n[t]=e.transforms[t]})),!0===e.objectAssign&&(e.objectAssign="Object.assign"),new ji(t,i,n,e).export(e)}(t,{...e,transforms:{asyncAwait:!1,classes:!1,getterSetter:!1,...e.transforms}})}var Di=i(2817),Fi=i.n(Di),Mi={plain:{color:"#C5C8C6",backgroundColor:"#1D1F21"},styles:[{types:["prolog","comment","doctype","cdata"],style:{color:"hsl(30, 20%, 50%)"}},{types:["property","tag","boolean","number","constant","symbol"],style:{color:"hsl(350, 40%, 70%)"}},{types:["attr-name","string","char","builtin","insterted"],style:{color:"hsl(75, 70%, 60%)"}},{types:["operator","entity","url","string","variable","language-css"],style:{color:"hsl(40, 90%, 60%)"}},{types:["deleted"],style:{color:"rgb(255, 85, 85)"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["regex","important"],style:{color:"#e90"}},{types:["atrule","attr-value","keyword"],style:{color:"hsl(350, 40%, 70%)"}},{types:["punctuation","symbol"],style:{opacity:"0.7"}}]},Ui="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},qi=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},zi=Object.assign||function(t){for(var e=1;e =0||Object.prototype.hasOwnProperty.call(t,r)&&(i[r]=t[r]);return i},Xi=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e},Ji=function(t){function e(){var i,r;qi(this,e);for(var s=arguments.length,a=Array(s),o=0;o n.createElement(n.Fragment,null,n.createElement(sr,null),n.createElement(nr,null))))))}function xr(){const t=(0,a.Z)();return n.createElement(rr,{key:String(t),className:fr})}function br(){return n.createElement(n.Fragment,null,n.createElement(gr,null,n.createElement(ar.Z,{id:"theme.Playground.liveEditor",description:"The live editor label of the live codeblocks"},"Live Editor")),n.createElement(xr,null))}function _r(t){let{children:e,transformCode:i,...s}=t;const{siteConfig:{themeConfig:a}}=(0,or.Z)(),{liveCodeBlock:{playgroundPosition:o}}=a,p=lr(),h=s.metastring?.includes("noInline")??!1;return n.createElement("div",{className:ur},n.createElement(ir,(0,r.Z)({code:e.replace(/\n$/,""),noInline:h,transformCode:i??(t=>`${t};`),theme:p},s),"top"===o?n.createElement(n.Fragment,null,n.createElement(vr,null),n.createElement(br,null)):n.createElement(n.Fragment,null,n.createElement(br,null),n.createElement(vr,null))))}var kr=i(6922),Sr=i(5281),Er=i(7594),wr=i.n(Er);const Cr=/title=(?["'])(?.*?)\1/,Ar=/\{(? [\d,-]+)\}/,Ir={js:{start:"\\/\\/",end:""},jsBlock:{start:"\\/\\*",end:"\\*\\/"},jsx:{start:"\\{\\s*\\/\\*",end:"\\*\\/\\s*\\}"},bash:{start:"#",end:""},html:{start:"\x3c!--",end:"--\x3e"}};function Lr(t,e){const i=t.map((t=>{const{start:i,end:r}=Ir[t];return`(?:${i}\\s*(${e.flatMap((t=>[t.line,t.block?.start,t.block?.end].filter(Boolean))).join("|")})\\s*${r})`})).join("|");return new RegExp(`^\\s*(?:${i})\\s*$`)}function Nr(t,e){let i=t.replace(/\n$/,"");const{language:r,magicComments:n,metastring:s}=e;if(s&&Ar.test(s)){const t=s.match(Ar).groups.range;if(0===n.length)throw new Error(`A highlight range has been given in code block's metastring (\`\`\` ${s}), but no magic comment config is available. Docusaurus applies the first magic comment entry's className for metastring ranges.`);const e=n[0].className,r=wr()(t).filter((t=>t>0)).map((t=>[t-1,[e]]));return{lineClassNames:Object.fromEntries(r),code:i}}if(void 0===r)return{lineClassNames:{},code:i};const a=function(t,e){switch(t){case"js":case"javascript":case"ts":case"typescript":return Lr(["js","jsBlock"],e);case"jsx":case"tsx":return Lr(["js","jsBlock","jsx"],e);case"html":return Lr(["js","jsBlock","html"],e);case"python":case"py":case"bash":return Lr(["bash"],e);case"markdown":case"md":return Lr(["html","jsx","bash"],e);default:return Lr(Object.keys(Ir),e)}}(r,n),o=i.split("\n"),p=Object.fromEntries(n.map((t=>[t.className,{start:0,range:""}]))),h=Object.fromEntries(n.filter((t=>t.line)).map((t=>{let{className:e,line:i}=t;return[i,e]}))),c=Object.fromEntries(n.filter((t=>t.block)).map((t=>{let{className:e,block:i}=t;return[i.start,e]}))),l=Object.fromEntries(n.filter((t=>t.block)).map((t=>{let{className:e,block:i}=t;return[i.end,e]})));for(let d=0;d void 0!==t));h[e]?p[h[e]].range+=`${d},`:c[e]?p[c[e]].start=d:l[e]&&(p[l[e]].range+=`${p[l[e]].start}-${d-1},`),o.splice(d,1)}i=o.join("\n");const u={};return Object.entries(p).forEach((t=>{let[e,{range:i}]=t;wr()(i).forEach((t=>{u[t]??=[],u[t].push(e)}))})),{lineClassNames:u,code:i}}const Pr="codeBlockContainer_Ckt0";function Tr(t){let{as:e,...i}=t;const a=function(t){const e={color:"--prism-color",backgroundColor:"--prism-background-color"},i={};return Object.entries(t.plain).forEach((t=>{let[r,n]=t;const s=e[r];s&&"string"==typeof n&&(i[s]=n)})),i}(lr());return n.createElement(e,(0,r.Z)({},i,{style:a,className:(0,s.Z)(i.className,Pr,Sr.k.common.codeBlock)}))}const jr={codeBlockContent:"codeBlockContent_biex",codeBlockTitle:"codeBlockTitle_Ktv7",codeBlock:"codeBlock_bY9V",codeBlockStandalone:"codeBlockStandalone_MEMb",codeBlockLines:"codeBlockLines_e6Vv",codeBlockLinesWithNumbering:"codeBlockLinesWithNumbering_o6Pm",buttonGroup:"buttonGroup__atx"};function Or(t){let{children:e,className:i}=t;return n.createElement(Tr,{as:"pre",tabIndex:0,className:(0,s.Z)(jr.codeBlockStandalone,"thin-scrollbar",i)},n.createElement("code",{className:jr.codeBlockLines},e))}var Rr=i(902);const Vr={attributes:!0,characterData:!0,childList:!0,subtree:!0};function Br(t,e){const[i,r]=(0,n.useState)(),s=(0,n.useCallback)((()=>{r(t.current?.closest("[role=tabpanel][hidden]"))}),[t,r]);(0,n.useEffect)((()=>{s()}),[s]),function(t,e,i){void 0===i&&(i=Vr);const r=(0,Rr.zX)(e),s=(0,Rr.Ql)(i);(0,n.useEffect)((()=>{const e=new MutationObserver(r);return t&&e.observe(t,s),()=>e.disconnect()}),[t,r,s])}(i,(t=>{t.forEach((t=>{"attributes"===t.type&&"hidden"===t.attributeName&&(e(),s())}))}),{attributes:!0,characterData:!1,childList:!1,subtree:!1})}const Dr="codeLine_lJS_",Fr="codeLineNumber_Tfdd",Mr="codeLineContent_feaV";function Ur(t){let{line:e,classNames:i,showLineNumbers:a,getLineProps:o,getTokenProps:p}=t;1===e.length&&"\n"===e[0].content&&(e[0].content="");const h=o({line:e,className:(0,s.Z)(i,a&&Dr)}),c=e.map(((t,e)=>n.createElement("span",(0,r.Z)({key:e},p({token:t,key:e})))));return n.createElement("span",h,a?n.createElement(n.Fragment,null,n.createElement("span",{className:Fr}),n.createElement("span",{className:Mr},c)):c,n.createElement("br",null))}const qr={copyButtonCopied:"copyButtonCopied_obH4",copyButtonIcons:"copyButtonIcons_eSgA",copyButtonIcon:"copyButtonIcon_y97N",copyButtonSuccessIcon:"copyButtonSuccessIcon_LjdS"};function zr(t){let{code:e,className:i}=t;const[r,a]=(0,n.useState)(!1),o=(0,n.useRef)(void 0),p=(0,n.useCallback)((()=>{!function(t,e){let{target:i=document.body}=void 0===e?{}:e;const r=document.createElement("textarea"),n=document.activeElement;r.value=t,r.setAttribute("readonly",""),r.style.contain="strict",r.style.position="absolute",r.style.left="-9999px",r.style.fontSize="12pt";const s=document.getSelection();let a=!1;s.rangeCount>0&&(a=s.getRangeAt(0)),i.append(r),r.select(),r.selectionStart=0,r.selectionEnd=t.length;let o=!1;try{o=document.execCommand("copy")}catch{}r.remove(),a&&(s.removeAllRanges(),s.addRange(a)),n&&n.focus()}(e),a(!0),o.current=window.setTimeout((()=>{a(!1)}),1e3)}),[e]);return(0,n.useEffect)((()=>()=>window.clearTimeout(o.current)),[]),n.createElement("button",{type:"button","aria-label":r?(0,ar.I)({id:"theme.CodeBlock.copied",message:"Copied",description:"The copied button label on code blocks"}):(0,ar.I)({id:"theme.CodeBlock.copyButtonAriaLabel",message:"Copy code to clipboard",description:"The ARIA label for copy code blocks button"}),title:(0,ar.I)({id:"theme.CodeBlock.copy",message:"Copy",description:"The copy button label on code blocks"}),className:(0,s.Z)("clean-btn",i,qr.copyButton,r&&qr.copyButtonCopied),onClick:p},n.createElement("span",{className:qr.copyButtonIcons,"aria-hidden":"true"},n.createElement("svg",{className:qr.copyButtonIcon,viewBox:"0 0 24 24"},n.createElement("path",{d:"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"})),n.createElement("svg",{className:qr.copyButtonSuccessIcon,viewBox:"0 0 24 24"},n.createElement("path",{d:"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"}))))}const Wr="wordWrapButtonIcon_Bwma",Hr="wordWrapButtonEnabled_EoeP";function Xr(t){let{className:e,onClick:i,isEnabled:r}=t;const a=(0,ar.I)({id:"theme.CodeBlock.wordWrapToggle",message:"Toggle word wrap",description:"The title attribute for toggle word wrapping button of code block lines"});return n.createElement("button",{type:"button",onClick:i,className:(0,s.Z)("clean-btn",e,r&&Hr),"aria-label":a,title:a},n.createElement("svg",{className:Wr,viewBox:"0 0 24 24","aria-hidden":"true"},n.createElement("path",{fill:"currentColor",d:"M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"})))}function Jr(t){let{children:e,className:i="",metastring:a,title:o,showLineNumbers:p,language:h}=t;const{prism:{defaultLanguage:l,magicComments:u}}=(0,cr.L)(),d=h??i.split(" ").find((t=>t.startsWith("language-")))?.replace(/language-/,"")??l;const f=lr(),m=function(){const[t,e]=(0,n.useState)(!1),[i,r]=(0,n.useState)(!1),s=(0,n.useRef)(null),a=(0,n.useCallback)((()=>{const i=s.current.querySelector("code");t?i.removeAttribute("style"):(i.style.whiteSpace="pre-wrap",i.style.overflowWrap="anywhere"),e((t=>!t))}),[s,t]),o=(0,n.useCallback)((()=>{const{scrollWidth:t,clientWidth:e}=s.current,i=t>e||s.current.querySelector("code").hasAttribute("style");r(i)}),[s]);return Br(s,o),(0,n.useEffect)((()=>{o()}),[t,o]),(0,n.useEffect)((()=>(window.addEventListener("resize",o,{passive:!0}),()=>{window.removeEventListener("resize",o)})),[o]),{codeBlockRef:s,isEnabled:t,isCodeScrollable:i,toggle:a}}(),g=function(t){return t?.match(Cr)?.groups.title??""}(a)||o,{lineClassNames:y,code:v}=Nr(e,{metastring:a,language:d,magicComments:u}),b=p??function(t){return Boolean(t?.includes("showLineNumbers"))}(a);return n.createElement(Tr,{as:"div",className:(0,s.Z)(i,d&&!i.includes(`language-${d}`)&&`language-${d}`)},g&&n.createElement("div",{className:jr.codeBlockTitle},g),n.createElement("div",{className:jr.codeBlockContent},n.createElement(x,(0,r.Z)({},c,{theme:f,code:v,language:d??"text"}),(t=>{let{className:e,tokens:i,getLineProps:r,getTokenProps:a}=t;return n.createElement("pre",{tabIndex:0,ref:m.codeBlockRef,className:(0,s.Z)(e,jr.codeBlock,"thin-scrollbar")},n.createElement("code",{className:(0,s.Z)(jr.codeBlockLines,b&&jr.codeBlockLinesWithNumbering)},i.map(((t,e)=>n.createElement(Ur,{key:e,line:t,getLineProps:r,getTokenProps:a,classNames:y[e],showLineNumbers:b})))))})),n.createElement("div",{className:jr.buttonGroup},(m.isEnabled||m.isCodeScrollable)&&n.createElement(Xr,{className:jr.codeButton,onClick:()=>m.toggle(),isEnabled:m.isEnabled}),n.createElement(zr,{className:jr.codeButton,code:v}))))}const Kr=(t=>function(e){return e.live?n.createElement(_r,(0,r.Z)({scope:kr.Z},e)):n.createElement(t,e)})((function(t){let{children:e,...i}=t;const s=(0,a.Z)(),o=function(t){return n.Children.toArray(t).some((t=>(0,n.isValidElement)(t)))?t:Array.isArray(t)?t.join(""):t}(e),p="string"==typeof o?Jr:Or;return n.createElement(p,(0,r.Z)({key:String(s)},i),o)}))},8985:(t,e,i)=>{"use strict";i.d(e,{Z:()=>n});var r=i(7294);const n={React:r,...r}},7594:(t,e)=>{function i(t){let e,i=[];for(let r of t.split(",").map((t=>t.trim())))if(/^-?\d+$/.test(r))i.push(parseInt(r,10));else if(e=r.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)){let[t,r,n,s]=e;if(r&&s){r=parseInt(r),s=parseInt(s);const t=r {i(9119),t.exports=i(8040).Object.assign},6773:t=>{t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},5920:(t,e,i)=>{var r=i(3436);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},8652:(t,e,i)=>{var r=i(5721),n=i(1846),s=i(7584);t.exports=function(t){return function(e,i,a){var o,p=r(e),h=n(p.length),c=s(a,h);if(t&&i!=i){for(;h>c;)if((o=p[c++])!=o)return!0}else for(;h>c;c++)if((t||c in p)&&p[c]===i)return t||c||0;return!t&&-1}}},1813:t=>{var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},8040:t=>{var e=t.exports={version:"2.6.12"};"number"==typeof __e&&(__e=e)},2117:(t,e,i)=>{var r=i(6773);t.exports=function(t,e,i){if(r(t),void 0===e)return t;switch(i){case 1:return function(i){return t.call(e,i)};case 2:return function(i,r){return t.call(e,i,r)};case 3:return function(i,r,n){return t.call(e,i,r,n)}}return function(){return t.apply(e,arguments)}}},8299:t=>{t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},7871:(t,e,i)=>{t.exports=!i(5287)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},6039:(t,e,i)=>{var r=i(3436),n=i(221).document,s=r(n)&&r(n.createElement);t.exports=function(t){return s?n.createElement(t):{}}},7305:t=>{t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},3930:(t,e,i)=>{var r=i(221),n=i(8040),s=i(5235),a=i(4093),o=i(2117),p="prototype",h=function(t,e,i){var c,l,u,d,f=t&h.F,m=t&h.G,g=t&h.S,y=t&h.P,v=t&h.B,x=m?r:g?r[e]||(r[e]={}):(r[e]||{})[p],b=m?n:n[e]||(n[e]={}),_=b[p]||(b[p]={});for(c in m&&(i=e),i)u=((l=!f&&x&&void 0!==x[c])?x:i)[c],d=v&&l?o(u,r):y&&"function"==typeof u?o(Function.call,u):u,x&&a(x,c,u,t&h.U),b[c]!=u&&s(b,c,d),y&&_[c]!=u&&(_[c]=u)};r.core=n,h.F=1,h.G=2,h.S=4,h.P=8,h.B=16,h.W=32,h.U=64,h.R=128,t.exports=h},5287:t=>{t.exports=function(t){try{return!!t()}catch(e){return!0}}},5118:(t,e,i)=>{t.exports=i(6695)("native-function-to-string",Function.toString)},221:t=>{var e=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},1014:t=>{var e={}.hasOwnProperty;t.exports=function(t,i){return e.call(t,i)}},5235:(t,e,i)=>{var r=i(2823),n=i(8613);t.exports=i(7871)?function(t,e,i){return r.f(t,e,n(1,i))}:function(t,e,i){return t[e]=i,t}},6804:(t,e,i)=>{t.exports=!i(7871)&&!i(5287)((function(){return 7!=Object.defineProperty(i(6039)("div"),"a",{get:function(){return 7}}).a}))},8618:(t,e,i)=>{var r=i(1813);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},3436:t=>{t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},173:t=>{t.exports=!1},4254:(t,e,i)=>{"use strict";var r=i(7871),n=i(4453),s=i(5862),a=i(6466),o=i(572),p=i(8618),h=Object.assign;t.exports=!h||i(5287)((function(){var t={},e={},i=Symbol(),r="abcdefghijklmnopqrst";return t[i]=7,r.split("").forEach((function(t){e[t]=t})),7!=h({},t)[i]||Object.keys(h({},e)).join("")!=r}))?function(t,e){for(var i=o(t),h=arguments.length,c=1,l=s.f,u=a.f;h>c;)for(var d,f=p(arguments[c++]),m=l?n(f).concat(l(f)):n(f),g=m.length,y=0;g>y;)d=m[y++],r&&!u.call(f,d)||(i[d]=f[d]);return i}:h},2823:(t,e,i)=>{var r=i(5920),n=i(6804),s=i(8980),a=Object.defineProperty;e.f=i(7871)?Object.defineProperty:function(t,e,i){if(r(t),e=s(e,!0),r(i),n)try{return a(t,e,i)}catch(o){}if("get"in i||"set"in i)throw TypeError("Accessors not supported!");return"value"in i&&(t[e]=i.value),t}},5862:(t,e)=>{e.f=Object.getOwnPropertySymbols},6528:(t,e,i)=>{var r=i(1014),n=i(5721),s=i(8652)(!1),a=i(7148)("IE_PROTO");t.exports=function(t,e){var i,o=n(t),p=0,h=[];for(i in o)i!=a&&r(o,i)&&h.push(i);for(;e.length>p;)r(o,i=e[p++])&&(~s(h,i)||h.push(i));return h}},4453:(t,e,i)=>{var r=i(6528),n=i(7305);t.exports=Object.keys||function(t){return r(t,n)}},6466:(t,e)=>{e.f={}.propertyIsEnumerable},8613:t=>{t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},4093:(t,e,i)=>{var r=i(221),n=i(5235),s=i(1014),a=i(607)("src"),o=i(5118),p="toString",h=(""+o).split(p);i(8040).inspectSource=function(t){return o.call(t)},(t.exports=function(t,e,i,o){var p="function"==typeof i;p&&(s(i,"name")||n(i,"name",e)),t[e]!==i&&(p&&(s(i,a)||n(i,a,t[e]?""+t[e]:h.join(String(e)))),t===r?t[e]=i:o?t[e]?t[e]=i:n(t,e,i):(delete t[e],n(t,e,i)))})(Function.prototype,p,(function(){return"function"==typeof this&&this[a]||o.call(this)}))},7148:(t,e,i)=>{var r=i(6695)("keys"),n=i(607);t.exports=function(t){return r[t]||(r[t]=n(t))}},6695:(t,e,i)=>{var r=i(8040),n=i(221),s="__core-js_shared__",a=n[s]||(n[s]={});(t.exports=function(t,e){return a[t]||(a[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:i(173)?"pure":"global",copyright:"\xa9 2020 Denis Pushkarev (zloirock.ru)"})},7584:(t,e,i)=>{var r=i(7276),n=Math.max,s=Math.min;t.exports=function(t,e){return(t=r(t))<0?n(t+e,0):s(t,e)}},7276:t=>{var e=Math.ceil,i=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?i:e)(t)}},5721:(t,e,i)=>{var r=i(8618),n=i(8299);t.exports=function(t){return r(n(t))}},1846:(t,e,i)=>{var r=i(7276),n=Math.min;t.exports=function(t){return t>0?n(r(t),9007199254740991):0}},572:(t,e,i)=>{var r=i(8299);t.exports=function(t){return Object(r(t))}},8980:(t,e,i)=>{var r=i(3436);t.exports=function(t,e){if(!r(t))return t;var i,n;if(e&&"function"==typeof(i=t.toString)&&!r(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!r(n=i.call(t)))return n;if(!e&&"function"==typeof(i=t.toString)&&!r(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")}},607:t=>{var e=0,i=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++e+i).toString(36))}},9119:(t,e,i)=>{var r=i(3930);r(r.S+r.F,"Object",{assign:i(4254)})},460:(t,e,i)=>{"use strict";var r=Object.assign||function(t){for(var e=1;e1&&void 0!==arguments[1]&&arguments[1],i=n._history,s=i.stack,a=i.offset;if(s.length&&a>-1){n._history.stack=s.slice(0,a+1);var o=n._history.stack.length;if(o>u){var p=o-u;n._history.stack=s.slice(p,o),n._history.offset=Math.max(n._history.offset-p,0)}}var h=Date.now();if(e){var c=n._history.stack[n._history.offset];if(c&&h-c.timestamp =x&&e<=b&&t.startsWith(y)?t.substring(y.length):t})).join("\n");if(u!==_){var k=v[x];n._applyEdits({value:_,selectionStart:k.startsWith(y)?d-y.length:d,selectionEnd:g-(u.length-_.length)})}}else if(d!==g){var S=n._getLines(u,d),E=S.length-1,w=n._getLines(u,g).length-1,C=S[E];n._applyEdits({value:u.split("\n").map((function(t,e){return e>=E&&e<=w?y+t:t})).join("\n"),selectionStart:/\S/.test(C)?d+y.length:d,selectionEnd:g+y.length*(w-E+1)})}else{var A=d+y.length;n._applyEdits({value:u.substring(0,d)+y+u.substring(g),selectionStart:A,selectionEnd:A})}else if(8===t.keyCode){var I=d!==g;if(u.substring(0,d).endsWith(y)&&!I){t.preventDefault();var L=d-y.length;n._applyEdits({value:u.substring(0,d-y.length)+u.substring(g),selectionStart:L,selectionEnd:L})}}else if(13===t.keyCode){if(d===g){var N=n._getLines(u,d).pop().match(/^\s+/);if(N&&N[0]){t.preventDefault();var P="\n"+N[0],T=d+P.length;n._applyEdits({value:u.substring(0,d)+P+u.substring(g),selectionStart:T,selectionEnd:T})}}}else if(57===t.keyCode||t.keyCode===h||t.keyCode===c||t.keyCode===l){var j=void 0;57===t.keyCode&&t.shiftKey?j=["(",")"]:t.keyCode===h?j=t.shiftKey?["{","}"]:["[","]"]:t.keyCode===c?j=t.shiftKey?['"','"']:["'","'"]:t.keyCode!==l||t.shiftKey||(j=["`","`"]),d!==g&&j&&(t.preventDefault(),n._applyEdits({value:u.substring(0,d)+j[0]+u.substring(d,g)+j[1]+u.substring(g),selectionStart:d,selectionEnd:g+2}))}else!(m?t.metaKey&&t.keyCode===p:t.ctrlKey&&t.keyCode===p)||t.shiftKey||t.altKey?(m?t.metaKey&&t.keyCode===p&&t.shiftKey:f?t.ctrlKey&&89===t.keyCode:t.ctrlKey&&t.keyCode===p&&t.shiftKey)&&!t.altKey?(t.preventDefault(),n._redoEdit()):77!==t.keyCode||!t.ctrlKey||m&&!t.shiftKey||(t.preventDefault(),n.setState((function(t){return{capture:!t.capture}}))):(t.preventDefault(),n._undoEdit())}},n._handleChange=function(t){var e=t.target,i=e.value,r=e.selectionStart,s=e.selectionEnd;n._recordChange({value:i,selectionStart:r,selectionEnd:s},!0),n.props.onValueChange(i)},n._history={stack:[],offset:-1},o(n,i)}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),n(e,[{key:"componentDidMount",value:function(){this._recordCurrentState()}},{key:"render",value:function(){var t=this,e=this.props,i=e.value,n=e.style,a=e.padding,o=e.highlight,p=e.textareaId,h=e.autoFocus,c=e.disabled,l=e.form,u=e.maxLength,d=e.minLength,f=e.name,m=e.placeholder,v=e.readOnly,b=e.required,_=e.onClick,k=e.onFocus,S=e.onBlur,E=e.onKeyUp,w=(e.onKeyDown,e.onValueChange,e.tabSize,e.insertSpaces,e.ignoreTabKey,function(t,e){var i={};for(var r in t)e.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(t,r)&&(i[r]=t[r]);return i}(e,["value","style","padding","highlight","textareaId","autoFocus","disabled","form","maxLength","minLength","name","placeholder","readOnly","required","onClick","onFocus","onBlur","onKeyUp","onKeyDown","onValueChange","tabSize","insertSpaces","ignoreTabKey"])),C={paddingTop:a,paddingRight:a,paddingBottom:a,paddingLeft:a},A=o(i);return s.createElement("div",r({},w,{style:r({},x.container,n)}),s.createElement("textarea",{ref:function(e){return t._input=e},style:r({},x.editor,x.textarea,C),className:g,id:p,value:i,onChange:this._handleChange,onKeyDown:this._handleKeyDown,onClick:_,onKeyUp:E,onFocus:k,onBlur:S,disabled:c,form:l,maxLength:u,minLength:d,name:f,placeholder:m,readOnly:v,required:b,autoFocus:h,autoCapitalize:"off",autoComplete:"off",autoCorrect:"off",spellCheck:!1,"data-gramm":!1}),s.createElement("pre",r({"aria-hidden":"true",style:r({},x.editor,x.highlight,C)},"string"==typeof A?{dangerouslySetInnerHTML:{__html:A+"
"}}:{children:A})),s.createElement("style",{type:"text/css",dangerouslySetInnerHTML:{__html:y}}))}},{key:"session",get:function(){return{history:this._history}},set:function(t){this._history=t.history}}]),e}(s.Component);v.defaultProps={tabSize:2,insertSpaces:!0,ignoreTabKey:!1,padding:0},e.Z=v;var x={container:{position:"relative",textAlign:"left",boxSizing:"border-box",padding:0,overflow:"hidden"},textarea:{position:"absolute",top:0,left:0,height:"100%",width:"100%",resize:"none",color:"inherit",overflow:"hidden",MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",WebkitTextFillColor:"transparent"},highlight:{position:"relative",pointerEvents:"none"},editor:{margin:0,border:0,background:"none",boxSizing:"inherit",display:"inherit",fontFamily:"inherit",fontSize:"inherit",fontStyle:"inherit",fontVariantLigatures:"inherit",fontWeight:"inherit",letterSpacing:"inherit",lineHeight:"inherit",tabSize:"inherit",textIndent:"inherit",textRendering:"inherit",textTransform:"inherit",whiteSpace:"pre-wrap",wordBreak:"keep-all",overflowWrap:"break-word"}}}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/89cd71cf.290e111f.js b/pr-preview/pr-346/assets/js/89cd71cf.290e111f.js new file mode 100644 index 00000000..bae31abf --- /dev/null +++ b/pr-preview/pr-346/assets/js/89cd71cf.290e111f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[8959],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var r=a.createContext({}),h=function(e){var t=a.useContext(r),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=h(e.components);return a.createElement(r.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,r=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=h(n),u=i,d=c["".concat(r,".").concat(u)]||c[u]||m[u]||o;return n?a.createElement(d,l(l({ref:t},p),{},{components:n})):a.createElement(d,l({ref:t},p))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,l=new Array(o);l[0]=u;var s={};for(var r in t)hasOwnProperty.call(t,r)&&(s[r]=t[r]);s.originalType=e,s[c]="string"==typeof e?e:i,l[1]=s;for(var h=2;h {n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>l,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>h});var a=n(7462),i=(n(7294),n(3905));const o={title:"Shell Script Essentials",slug:"/part-3-manipulating-text/shell-script-essentials/"},l=void 0,s={unversionedId:"shell-scripting/shell-script-essentials/index",id:"shell-scripting/shell-script-essentials/index",title:"Shell Script Essentials",description:"In this chapter we're going to look at how to write shell scripts and the different ways we can execute them. We'll look at how shell script files should be structured and how to use 'shebangs' to define how a shell script will run.",source:"@site/docs/04-shell-scripting/18-shell-script-essentials/index.md",sourceDirName:"04-shell-scripting/18-shell-script-essentials",slug:"/part-3-manipulating-text/shell-script-essentials/",permalink:"/part-3-manipulating-text/shell-script-essentials/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/04-shell-scripting/18-shell-script-essentials/index.md",tags:[],version:"current",frontMatter:{title:"Shell Script Essentials",slug:"/part-3-manipulating-text/shell-script-essentials/"},sidebar:"sidebar",previous:{title:"Part 4 - Shell Scripting",permalink:"/part-4-shell-scripting/"},next:{title:"Variables, Reading Input, and Mathematics",permalink:"/part-3-manipulating-text/variables-reading-input-and-mathematics/"}},r={},h=[{value:"What is a Shell Script?",id:"what-is-a-shell-script",level:2},{value:"Creating a Basic Shell Script",id:"creating-a-basic-shell-script",level:2},{value:"The 'common' Command",id:"the-common-command",level:3},{value:"Creating a Simple Script",id:"creating-a-simple-script",level:3},{value:"Commentsindex",id:"commentsindex",level:2},{value:"Building and Testing the Script",id:"building-and-testing-the-script",level:2},{value:"Multi-line Commands",id:"multi-line-commands",level:2},{value:"Running a Shell Script",id:"running-a-shell-script",level:2},{value:"Using Shebangs",id:"using-shebangs",level:2},{value:"Shebangs - Dealing with Paths",id:"shebangs---dealing-with-paths",level:3},{value:"Sourcing Shell Scripts",id:"sourcing-shell-scripts",level:2},{value:"Dot Sourcingindex",id:"dot-sourcingindex",level:3},{value:"Installing Your Script",id:"installing-your-script",level:2},{value:"Summary",id:"summary",level:2},{value:"Appendix - How the Script Works",id:"appendix---how-the-script-works",level:3}],p={toc:h};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"In this chapter we're going to look at how to write shell scripts and the different ways we can execute them. We'll look at how shell script files should be structured and how to use 'shebangs' to define how a shell script will run."),(0,i.kt)("p",null,"We'll learn the essential techniques that will help you build your own scripts. Even if you are familiar with shell scripts I would suggest skimming this chapter to make sure you understand each of the concepts, particularly the later section where we talk about the ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," command in shebangs."),(0,i.kt)("h2",{id:"what-is-a-shell-script"},"What is a Shell Script?"),(0,i.kt)("p",null,"A shell script is just a text file which contains a set of commands. As soon as you find yourself repeating the same sequence of commands in a shell, it might be worth saving these commands to a file and running the file instead."),(0,i.kt)("p",null,"Saving your commands to a file has a number of benefits. It saves time - you don't need to type the commands out each time you want to run them! You can use your favourite editor to build the script file, and you can add 'comments' to describe what you are trying to achieve (which will make it far easier to update the script over time). Files can also easily be shared - meaning you can copy these scripts to other machines or share them with others who might find them useful."),(0,i.kt)("h2",{id:"creating-a-basic-shell-script"},"Creating a Basic Shell Script"),(0,i.kt)("p",null,"Let's create a simple shell script that shows us our most commonly used shell commands."),(0,i.kt)("p",null,"Almost every command that is needed to build the script has been discussed in the book already, so it shouldn't be too unfamiliar. But I'll still break it down blow-by-blow to help us understand it."),(0,i.kt)("p",null,"As we go through this section of the book, we're going to extend this script and make it more useful!"),(0,i.kt)("h3",{id:"the-common-command"},"The 'common' Command"),(0,i.kt)("p",null,"We're going to create a new command, called 'common', that shows the most commonly used shell commands."),(0,i.kt)("p",null,"We should be able to do this using techniques we've seen so far. We'll do it like this:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Read a large number of commands from the history"),(0,i.kt)("li",{parentName:"ol"},"Sort the commands, then count the number of duplicates"),(0,i.kt)("li",{parentName:"ol"},"Sort this list showing the most commonly run commands first"),(0,i.kt)("li",{parentName:"ol"},"Print the results to the screen.")),(0,i.kt)("p",null,"Let's get started!"),(0,i.kt)("h3",{id:"creating-a-simple-script"},"Creating a Simple Script"),(0,i.kt)("p",null,"It's going to take some trial and error to get our commands right. So let's start by creating a shell script, which we'll run again and again."),(0,i.kt)("p",null,"In your favourite editor, create a file called ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v1.sh")," and put it somewhere on your system. As an example, I'm going to create a folder called ",(0,i.kt)("inlineCode",{parentName:"p"},"scripts")," in my home directory and create the ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v1.sh")," file there:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Create a directory called 'scripts'.\n# Using the '-p' flag means we won't get an error if the folder exists.\nmkdir -p ~/scripts\n\n# Create the script file.\ntouch ~/scripts/common.v1.sh\n\n# Open the script file in my favourite editor.\nvi ~/scripts.sh\n")),(0,i.kt)("p",null,"I have called the script ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v1.sh")," rather than ",(0,i.kt)("inlineCode",{parentName:"p"},"common.sh")," because in each chapter of this section we are going to improve upon the script and change the version number. So in later chapters we will create ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v2.sh"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v3.sh")," and so on."),(0,i.kt)("p",null,"These commands should be familiar. The ",(0,i.kt)("inlineCode",{parentName:"p"},"mkdir")," command creates a directory. The ",(0,i.kt)("inlineCode",{parentName:"p"},"-p")," (create parent directories if needed) flag stops the command from returning an error if the directory already exists."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"touch")," command creates an empty file with the given name. Finally, I open the file in an editor. I am using Vim, but you can open this file in any editor you like. If you would like to see how to use Vim you can also jump to ",(0,i.kt)("a",{parentName:"p",href:"/part-6-advanced-techniques/a-vim-crash-course/"},"Chapter 32 - A Vim Crash Course"),"."),(0,i.kt)("p",null,"Before we run the script, let's quickly talk about ",(0,i.kt)("em",{parentName:"p"},"comments"),"."),(0,i.kt)("h2",{id:"commentsindex"},"Comments"),(0,i.kt)("p",null,"Comments are lines of text that you add to a script or program to help the reader understand what is going on. Comments are not interpreted by the shell \u2013 they are purely for the benefit of the reader."),(0,i.kt)("p",null,"Any text that follows a ",(0,i.kt)("inlineCode",{parentName:"p"},"#")," hash symbol is treated as a comment. Whether this is text you type into a shell, or text in a shell script, the shell will ignore anything after the hash and not try to interpret it."),(0,i.kt)("p",null,"Using comments effectively can be a huge time-saver \u2013 it is amazing how quickly you can forget what a certain piece of code means, or why you solved a problem in a particular way."),(0,i.kt)("p",null,"Let\u2019s look at some examples of comments."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'# This is a comment - we can use this to describe what we\'re trying to do.\n\necho "Hello Shell" # Comments can go at the end of a line...\n\n# You can also use a comment symbol to \'comment out\' a line:\n# echo "Goodbye Shell"\n')),(0,i.kt)("p",null,"There are three comments in this sample. The first comment takes up a whole line, the second comment is at the end of a line to add some explanation, and the third comment is some 'commented out' code \u2013 we've just put a hash in front of some commands so that they will not be executed."),(0,i.kt)("p",null,"From this point on we'll use comments a lot to explain what we are trying to accomplish with each section of a script. It is generally good practice to use comments to describe your ",(0,i.kt)("em",{parentName:"p"},"intent")," - why you are doing something. This is far more useful for the reader than ",(0,i.kt)("em",{parentName:"p"},"what")," you are doing. The 'what' should be clear from the commands - the 'why' is the thing readers will likely want to understand."),(0,i.kt)("p",null,"Here's an example of a bad comment:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Write the CSV file, reverse it, cut it, reverse it.\ncat ~/effective-shell/data/top100.csv | rev | cut -d',' -f1 | rev\n")),(0,i.kt)("p",null,"The comment just describes what the script is doing. But it doesn't explain ",(0,i.kt)("em",{parentName:"p"},"why"),". A better comment would be:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Get the last field of each line in the csv file.\ncat ~/effective-shell/data/top100.csv | rev | cut -d',' -f1 | rev\n")),(0,i.kt)("p",null,"If you ",(0,i.kt)("em",{parentName:"p"},"don't")," come from a programming background, you might think that many of these comments are a little obvious. But as you write more and more code, you'll realise that something that seemed obvious when you wrote it a while ago can look surprisingly baffling even just a few days later!"),(0,i.kt)("h2",{id:"building-and-testing-the-script"},"Building and Testing the Script"),(0,i.kt)("p",null,"Add the following commands to the ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v1.sh")," file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"# Write the title of our command.\necho \"common commands:\"\n\n# Show the most commonly used commands.\ntail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10\n")),(0,i.kt)("p",null,"This is a short script, but there is quite a lot going on. Let's look at it blow-by-blow:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"First we take the last 1000 lines of the ",(0,i.kt)("inlineCode",{parentName:"li"},"~/.bash_history")," file using the ",(0,i.kt)("inlineCode",{parentName:"li"},"tail")," command",(0,i.kt)("sup",{parentName:"li",id:"fnref-1"},(0,i.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1"))),(0,i.kt)("li",{parentName:"ol"},"Then we sort the commands. This will put all of the duplicates next to each other"),(0,i.kt)("li",{parentName:"ol"},"Then we remove all duplicates and use the ",(0,i.kt)("inlineCode",{parentName:"li"},"-c")," (",(0,i.kt)("em",{parentName:"li"},"show count"),") flag to count the duplicates"),(0,i.kt)("li",{parentName:"ol"},"Then we remove the leading spaces from the output (which we need to do so that we can sort properly)"),(0,i.kt)("li",{parentName:"ol"},"Then we sort ",(0,i.kt)("em",{parentName:"li"},"numerically")," and in reverse - the highest count first"),(0,i.kt)("li",{parentName:"ol"},"Finally, we limit the results to the first ten items")),(0,i.kt)("p",null,"If you need a refresher on the shell history, ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"uniq")," the check the ",(0,i.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/slice-and-dice-text/"},"Slice and Dice Text")," chapter. If the ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," command doesn't look familiar then check the ",(0,i.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/advanced-text-manipulation/"},"Advanced Text Manipulation with Sed")," chapter."),(0,i.kt)("p",null,"If you want to see a more detailed breakdown of how the script works, check ",(0,i.kt)("a",{parentName:"p",href:"#appendix-how-the-script-works"},"Appendix - How the Script Works"),". But this is not necessary for you to follow the content in this chapter."),(0,i.kt)("p",null,"Now save the file. In your shell, run the following command to execute the file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"sh ~/scripts/common.v1.sh\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"sh")," (shell) command starts a new shell. When we pass the path of a shell script, the shell command will run the script and then exit. The output you see will look something like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"common commands:\n463 vi\n267 gc\n238 ga .\n212 ls\n169 gpo\n122 make dev\n112 gl\n97 gcm\n96 gpr\n")),(0,i.kt)("p",null,"You can see my most common commands are short aliases for Git commands (the ones that start with 'g'), opening Vim, running a makefile command and a few others."),(0,i.kt)("p",null,"We now have a basic shell script. Let's look at a few different ways we can run the script."),(0,i.kt)("h2",{id:"multi-line-commands"},"Multi-line Commands"),(0,i.kt)("p",null,"You can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"\\")," backslash character to create a 'continuation' that tells the shell it needs to join lines up. This allows you to break long commands into multiple lines."),(0,i.kt)("p",null,"As an example, we could re-write our pipeline command to look like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Show the most commonly used commands.\ntail ~/.bash_history -n 1000 \\\n | sort \\\n | uniq -c \\\n | sed 's/^ *//' \\\n | sort -n -r \\\n | head -n 10\n")),(0,i.kt)("p",null,"This will probably look very familiar to anyone with a background in functional programming!"),(0,i.kt)("p",null,"Be careful when you split lines up - the continuation character ",(0,i.kt)("em",{parentName:"p"},"must")," be the last character on the line. If you add something after it (such as a comment) then the command will fail."),(0,i.kt)("h2",{id:"running-a-shell-script"},"Running a Shell Script"),(0,i.kt)("p",null,"There are a few different ways we can run shell scripts."),(0,i.kt)("p",null,"The first is to run a shell program and pass the script as a parameter. This is what we did in the earlier example. Here's another example of how we could run the script we created:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"bash ~/scripts/common.v1.sh\n")),(0,i.kt)("p",null,"This is a perfectly valid technique. Now let's see the other ways we can run a script."),(0,i.kt)("p",null,"The next way we can run a script is to make it 'executable'. This means we change the file permissions of the script file, adding the 'executable bit'. This tells the systems we can run the file. We use the ",(0,i.kt)("inlineCode",{parentName:"p"},"chmod")," (",(0,i.kt)("em",{parentName:"p"},"change file mode"),") command to do this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"chmod +x ~/scripts/common.v1.sh\n")),(0,i.kt)("p",null,"If the ",(0,i.kt)("inlineCode",{parentName:"p"},"chmod")," command looks unfamiliar then check the ",(0,i.kt)("a",{parentName:"p",href:"/part-2-core-skills/understanding-commands"},"Understanding Commands")," chapter. Now that the file has been made executable, we can simply enter the path to the file and run it, as if it was any other command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"~/scripts/common.v1.sh\n")),(0,i.kt)("p",null,"There is a problem with this approach though. How this file is executed is going to vary depending on how your system is set up",(0,i.kt)("sup",{parentName:"p",id:"fnref-2"},(0,i.kt)("a",{parentName:"sup",href:"#fn-2",className:"footnote-ref"},"2")),". For example, if you are using Bash, then the script will run in a new instance of the Bash shell. However, if you are using the Z shell, then the script will most likely run in the ",(0,i.kt)("inlineCode",{parentName:"p"},"sh")," program (and depending on your system, this program might just be a link to ",(0,i.kt)("em",{parentName:"p"},"another")," type of shell)."),(0,i.kt)("p",null,"We want to avoid any ambiguity and be explicit about ",(0,i.kt)("em",{parentName:"p"},"what")," program should run our script. We can do this using a special construct called a ",(0,i.kt)("em",{parentName:"p"},"shebang"),"."),(0,i.kt)("h2",{id:"using-shebangs"},"Using Shebangs"),(0,i.kt)("p",null,"A ",(0,i.kt)("em",{parentName:"p"},"shebang")," is a special set of symbols at the beginning of a file that tells the system what program should be used to run the file."),(0,i.kt)("p",null,"If we were to add a shebang to our ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v1.sh")," file, it would look like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"#!/usr/bin/sh\n\n# Write the title of our command.\necho \"common commands:\"\n\n# Show the most commonly used commands.\ntail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10\n")),(0,i.kt)("p",null,"The shebang is the two characters - ",(0,i.kt)("inlineCode",{parentName:"p"},"#!"),". The name 'shebang' comes from the names of the symbols. The first symbol is a 'sharp' symbol (sometimes it is called a hash, it depends a little on context). The second symbol is an exclamation point. In programming the exclamation point is sometimes called the 'bang' symbol. When we put the two together, we get 'sharp bang', which is shortened to 'shebang'."),(0,i.kt)("p",null,"Immediately after the shebang you write the full path to the program which should be used to open the file."),(0,i.kt)("p",null,"For example, if you wanted to write a script that is run in Python, you could do this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-python"},"#!/usr/bin/python3\n\nprint('Hello from Python')\n")),(0,i.kt)("p",null,"If we wanted to explicitly use the Bash shell to run a script, we might use a shebang like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'#!/usr/bin/bash\n\necho "Hello from Bash"\n')),(0,i.kt)("p",null,"What about Node.js? Easy!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'#!/usr/bin/node\n\nconsole.log("Hello from Node.js");\n')),(0,i.kt)("h3",{id:"shebangs---dealing-with-paths"},"Shebangs - Dealing with Paths"),(0,i.kt)("p",null,"When we use a shebang we need to provide the full path to the executable that runs the script."),(0,i.kt)("p",null,"For example, if we want to use Ruby to run a script we could write a script like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ruby"},"#!/usr/bin/ruby\n\nputs 'Hello from Ruby'\n")),(0,i.kt)("p",null,"But there is a problem here. This will only work if you have the Ruby program installed in the location specified after the shebang (i.e. ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/bin/ruby"),"). If you do not have the Ruby program in this location the script will fail to run."),(0,i.kt)("p",null,"How can we know where a specific program is installed?"),(0,i.kt)("p",null,"There is a common trick for dealing with this issue. We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," (",(0,i.kt)("em",{parentName:"p"},"set environment and execute command"),") command to run a command and it will work out the path for us."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," command is often used to show environment variables, but you can also use it to execute an arbitrary command (often with a modified environment). One handy feature of the ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," command is that it looks through the ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH")," variable to find the path of the command to execute."),(0,i.kt)("p",null,"You can see this by running a command like the below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ env python3\nPython 3.8.5 (default, Jan 27 2021, 15:41:15)\n[GCC 9.3.0] on linux\nType "help", "copyright", "credits" or "license" for more information.\n>>>\n')),(0,i.kt)("p",null,"We've used the ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," command to run the ",(0,i.kt)("inlineCode",{parentName:"p"},"python3")," command - and it worked out the correct path for us."),(0,i.kt)("p",null,"To use ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," in a shebang, specify the full path to ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," (which should be the same on all Unix-like systems) and then provide the name of the command to run:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'#!/usr/bin/env bash\n\necho "Hello from Bash"\n')),(0,i.kt)("p",null,"Or another example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"#!/usr/bin/env ruby\n\nputs 'Hello from Ruby'\n")),(0,i.kt)("p",null,"Using a shebang to specify the exact command to run, and then using the ",(0,i.kt)("inlineCode",{parentName:"p"},"env")," command to allow the ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH")," to be searched is generally the safest and most portable way to specify how a shell script should run."),(0,i.kt)("h2",{id:"sourcing-shell-scripts"},"Sourcing Shell Scripts"),(0,i.kt)("p",null,"We have discussed how to ",(0,i.kt)("em",{parentName:"p"},"run")," shell scripts. You can also use the ",(0,i.kt)("em",{parentName:"p"},"source")," (",(0,i.kt)("em",{parentName:"p"},"execute commands from a file"),") command"," to load the contents of a file into the ",(0,i.kt)("em",{parentName:"p"},"current")," shell."),(0,i.kt)("p",null,"Remember that when we run a shell script, a new shell is created as a child process of the current shell. This means that if you change something in the environment, such as a variable, it will not affect the environment of the shell that ran the script."),(0,i.kt)("p",null,"Let's see an example. We'll create a script called ",(0,i.kt)("em",{parentName:"p"},"set_editor.sh")," that sets the ",(0,i.kt)("inlineCode",{parentName:"p"},"EDITOR")," environment variable to ",(0,i.kt)("inlineCode",{parentName:"p"},"nano"),". The script's contents are below (can also find it in the samples at ",(0,i.kt)("em",{parentName:"p"},"~/effective-shell/scripts/set_editor.sh"),"):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},'EDITOR=nano\necho "Editor changed to: $EDITOR"\n')),(0,i.kt)("p",null,"Let's run this script and see what editor looks like before and after:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo $EDITOR\nvim\n$ ~/effective-shell/scripts/set_editor.sh\nEditor changed to: nano\n$ echo $EDITOR\nvim\n")),(0,i.kt)("p",null,"Notice that although we changed the ",(0,i.kt)("inlineCode",{parentName:"p"},"EDITOR")," environment variable in our script, the change has not persisted in the current shell. This is because each shell (and in fact, each process) gets its own ",(0,i.kt)("em",{parentName:"p"},"copy")," of the environment."),(0,i.kt)("p",null,"If we want to run the commands in the file in the context of the current shell, we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"source")," command to load the file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo $EDITOR\nvim\n$ source ~/effective-shell/scripts/set_editor.sh\nEditor changed to: nano\n$ echo $EDITOR\nnano\n")),(0,i.kt)("p",null,"Our existing environment has been changed. When we use ",(0,i.kt)("inlineCode",{parentName:"p"},"source"),", the commands in the file are executed in the current shell, rather than in a new shell."),(0,i.kt)("p",null,"We can see this even more clearly if we use the ",(0,i.kt)("em",{parentName:"p"},"showpstree.sh")," file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ ~/effective-shell/scripts/show-info.sh\nbash\n \u2514\u2500sh /home/ubuntu/effective-shell/scripts/showpstree.sh\n \u2514\u2500pstree -l -a -s 2240\n")),(0,i.kt)("p",null,"This script shows the current 'process tree', using the ",(0,i.kt)("inlineCode",{parentName:"p"},"pstree")," (",(0,i.kt)("em",{parentName:"p"},"show process tree"),") command. We can see that the ",(0,i.kt)("inlineCode",{parentName:"p"},"pstree")," command was run as a child of the ",(0,i.kt)("inlineCode",{parentName:"p"},"sh")," program. This program was run with the script path, by the shell I was using, ",(0,i.kt)("inlineCode",{parentName:"p"},"bash"),". This is a nice visualisation of what is going on - our ",(0,i.kt)("inlineCode",{parentName:"p"},"bash")," shell has run the ",(0,i.kt)("em",{parentName:"p"},"showpstree.sh")," script in a child shell."),(0,i.kt)("p",null,"If we source the same file, we'll see that we do ",(0,i.kt)("em",{parentName:"p"},"not")," create a new shell:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ source ~/effective-shell/scripts/show-info.sh\nbash\n \u2514\u2500pstree -l -a -s 2169\n")),(0,i.kt)("h3",{id:"dot-sourcingindex"},"Dot Sourcing"),(0,i.kt)("p",null,"There is a slightly more concise syntax to source a script - the ",(0,i.kt)("em",{parentName:"p"},"dot sourcing")," notation. When the shell sees a ",(0,i.kt)("inlineCode",{parentName:"p"},".")," dot character, it will source the file that follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ . ~/effective-shell/scripts/show-info.sh\nbash\n \u2514\u2500pstree -l -a -s 2169\n")),(0,i.kt)("p",null,"You may encounter this syntax as you look at things like shell configuration files, which we will discuss in ",(0,i.kt)("a",{parentName:"p",href:"../../part-5-building-your-toolkit/configuring-the-shell"},"Chapter 24 - Configuring the Shell"),"."),(0,i.kt)("h2",{id:"installing-your-script"},"Installing Your Script"),(0,i.kt)("p",null,"Before we finish with our shell script fundamentals, we'll take a look at one final commonly used pattern to run shell scripts - installing them as a local binary."),(0,i.kt)("p",null,"Our ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v1.sh")," script (with the added shebang) looks like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"#!/usr/bin/env sh\n\n# Write the title of our command.\necho \"common commands:\"\n\n# Show the most commonly used commands.\ntail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10\n")),(0,i.kt)("p",null,"If we have made the script executable with the ",(0,i.kt)("inlineCode",{parentName:"p"},"chmod")," command, then we can run the script by simply typing the location of the script in the shell:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ ~/scripts/common.v1.sh\ncommon commands:\n97 gcm\n96 gpr\n...\n")),(0,i.kt)("p",null,"If we want to 'install' this script as a local command which we can run easily, we can create a ",(0,i.kt)("em",{parentName:"p"},"symbolic link")," to the shell script in our ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/local/bin")," folder:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"ln -s ~/scripts/common.v1.sh /usr/local/bin/common\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"ln")," (",(0,i.kt)("em",{parentName:"p"},"create link"),") command creates a link (which is like a shortcut in Windows and other desktop systems) in our ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/local/bin")," folder, with the name ",(0,i.kt)("inlineCode",{parentName:"p"},"common"),", which points to the script we have written. We can now run the ",(0,i.kt)("inlineCode",{parentName:"p"},"common")," command without specifying its path:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ common\ncommon commands:\n97 gcm\n96 gpr\n...\n")),(0,i.kt)("p",null,"This works because when the shell sees a command, it searches through the folders in the $PATH environment variable to find out where the command is. And the ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/local/bin")," folder is in this list of paths."),(0,i.kt)("p",null,"Why do we use the ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/local/bin")," folder rather than the ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/bin")," folder? This is just a convention. In general, the ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/bin")," folder is for commands which are installed with package manager tools like ",(0,i.kt)("inlineCode",{parentName:"p"},"apt")," or Homebrew (on MacOS). The ",(0,i.kt)("inlineCode",{parentName:"p"},"/usr/local/bin")," folder is used for commands which you create for yourself on your local machine and manage yourself",(0,i.kt)("sup",{parentName:"p",id:"fnref-3"},(0,i.kt)("a",{parentName:"sup",href:"#fn-3",className:"footnote-ref"},"3")),"."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"In this chapter we've covered quite a few of the fundamentals of shell scripts:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"How to create a shell script"),(0,i.kt)("li",{parentName:"ul"},"How comments work in shell scripts"),(0,i.kt)("li",{parentName:"ul"},"How to handle long lines with continuations"),(0,i.kt)("li",{parentName:"ul"},"How to run a shell script"),(0,i.kt)("li",{parentName:"ul"},"How to make a shell script executable"),(0,i.kt)("li",{parentName:"ul"},"How shebangs work"),(0,i.kt)("li",{parentName:"ul"},"How to use the ",(0,i.kt)("inlineCode",{parentName:"li"},"env")," command to make our shebangs more portable"),(0,i.kt)("li",{parentName:"ul"},"How to 'install' scripts for the current user")),(0,i.kt)("p",null,"In the next chapter we'll look at how to add logic to our shell scripts."),(0,i.kt)("hr",null),(0,i.kt)("h3",{id:"appendix---how-the-script-works"},"Appendix - How the Script Works"),(0,i.kt)("p",null,"This section briefly covers how the ",(0,i.kt)("inlineCode",{parentName:"p"},"common.v1.sh")," script works. Assuming we have a history that looks like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"vi README.md\ngit status\ngit checkout main\ngit status\nrestart-shell\ngit status\nopen .\nvi README.md\nopen .\n")),(0,i.kt)("p",null,"First we sort, putting duplicate lines next to each other:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"git checkout main\ngit status\ngit status\ngit status\nopen .\nopen .\nrestart-shell\nvi README.md\nvi README.md\n")),(0,i.kt)("p",null,"Then we use ",(0,i.kt)("inlineCode",{parentName:"p"},"uniq")," to remove duplicate adjacent lines, passing the ",(0,i.kt)("inlineCode",{parentName:"p"},"-c")," flag to include a count:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"}," 1 git checkout main\n 3 git status\n 2 open .\n 1 restart-shell\n 2 vi README.md\n")),(0,i.kt)("p",null,"Now we remove the leading whitespace:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"1 git checkout main\n3 git status\n2 open .\n1 restart-shell\n2 vi README.md\n")),(0,i.kt)("p",null,"Finally we sort numerically (by using the ",(0,i.kt)("inlineCode",{parentName:"p"},"-n")," flag) and in descending order (by using the ",(0,i.kt)("inlineCode",{parentName:"p"},"-r")," flag):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"3 git status\n2 vi README.md\n2 open .\n1 restart-shell\n1 git checkout main\n")),(0,i.kt)("p",null,"Why the numeric sort? If we didn't sort numerically and instead performed the default lexographic sort and have more than single digit results, the output would look like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"1 git checkout main\n1 restart-shell\n13 git status\n2 open .\n2 vi README.md\n")),(0,i.kt)("p",null,"This is a lexographic sort - the line starting with 13 comes after the line starting with 2. We want to sort by the value of the number."),(0,i.kt)("div",{className:"footnotes"},(0,i.kt)("hr",{parentName:"div"}),(0,i.kt)("ol",{parentName:"div"},(0,i.kt)("li",{parentName:"ol",id:"fn-1"},"The path to the shell history file is normally available in the ",(0,i.kt)("inlineCode",{parentName:"li"},"$HISTFILE")," environment variable. However, in a non-interactive shell this variable is not set (and when we run a shell script, it is run in a non-interactive shell). We'll see more about interactive and non-interactive shells later, this is just a note in case you are wondering why we don't use the ",(0,i.kt)("inlineCode",{parentName:"li"},"$HISTFILE")," variable or ",(0,i.kt)("inlineCode",{parentName:"li"},"history")," command!",(0,i.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-2"},"Try putting the command ",(0,i.kt)("inlineCode",{parentName:"li"},"pstree -p $$")," in a shell script and running the script - you'll see exactly what process is run.",(0,i.kt)("a",{parentName:"li",href:"#fnref-2",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-3"},"If you want to know more about these folders and the conventions behind them then check back soon, I am going to be adding an entire section on Linux Fundamentals, and one of the chapters will specifically be on the Linux Filesystem. This will cover 'The Linux Filesystem Hierarchy Standard' which defines how folders like this should be used.",(0,i.kt)("a",{parentName:"li",href:"#fnref-3",className:"footnote-backref"},"\u21a9")))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/8e52dcb9.d883960e.js b/pr-preview/pr-346/assets/js/8e52dcb9.d883960e.js new file mode 100644 index 00000000..852d3d96 --- /dev/null +++ b/pr-preview/pr-346/assets/js/8e52dcb9.d883960e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[2616],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>u});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t =0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},m="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,l=e.originalType,s=e.parentName,d=r(e,["components","mdxType","originalType","parentName"]),m=p(n),h=i,u=m["".concat(s,".").concat(h)]||m[h]||c[h]||l;return n?a.createElement(u,o(o({ref:t},d),{},{components:n})):a.createElement(u,o({ref:t},d))}));function u(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var l=n.length,o=new Array(l);o[0]=h;var r={};for(var s in t)hasOwnProperty.call(t,s)&&(r[s]=t[s]);r.originalType=e,r[m]="string"==typeof e?e:i,o[1]=r;for(var p=2;p {n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>l,metadata:()=>r,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const l={title:"Managing Your Files",slug:"/part-1-transitioning-to-the-shell/managing-your-files/"},o=void 0,r={unversionedId:"transitioning-to-the-shell/managing-your-files/index",id:"transitioning-to-the-shell/managing-your-files/index",title:"Managing Your Files",description:"Downloading, unzipping, copying, moving, renaming and deleting files in a graphical user interface is normally fairly intuitive. Now we'll learn how to perform the same operations in a shell. Once you can organise your files, you are well on your way to being able to use the shell more effectively for day to day tasks.",source:"@site/docs/01-transitioning-to-the-shell/03-managing-your-files/index.md",sourceDirName:"01-transitioning-to-the-shell/03-managing-your-files",slug:"/part-1-transitioning-to-the-shell/managing-your-files/",permalink:"/part-1-transitioning-to-the-shell/managing-your-files/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/01-transitioning-to-the-shell/03-managing-your-files/index.md",tags:[],version:"current",frontMatter:{title:"Managing Your Files",slug:"/part-1-transitioning-to-the-shell/managing-your-files/"},sidebar:"sidebar",previous:{title:"Navigating Your System",permalink:"/part-1-transitioning-to-the-shell/navigating-your-system/"},next:{title:"Becoming a Clipboard Gymnast",permalink:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/"}},s={},p=[{value:"Creating a Playground",id:"creating-a-playground",level:2},{value:"Finding out about files",id:"finding-out-about-files",level:2},{value:"Extracting the Zip",id:"extracting-the-zip",level:2},{value:"Deleting Files",id:"deleting-files",level:2},{value:"Examining the Contents of a Folder",id:"examining-the-contents-of-a-folder",level:2},{value:"Copying a File",id:"copying-a-file",level:2},{value:"Saving Some Keystrokes",id:"saving-some-keystrokes",level:2},{value:"Renaming or Moving Files",id:"renaming-or-moving-files",level:2},{value:"Creating a New Folder",id:"creating-a-new-folder",level:2},{value:"Copying or Moving Multiple Files with Wildcards",id:"copying-or-moving-multiple-files-with-wildcards",level:2},{value:"Deleting a Folder",id:"deleting-a-folder",level:2},{value:"Looking at Text Files",id:"looking-at-text-files",level:2},{value:"Zipping up Files",id:"zipping-up-files",level:2},{value:"Summary",id:"summary",level:2}],d={toc:p};function m(e){let{components:t,...l}=e;return(0,i.kt)("wrapper",(0,a.Z)({},d,l,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Downloading, unzipping, copying, moving, renaming and deleting files in a graphical user interface is normally fairly intuitive. Now we'll learn how to perform the same operations in a shell. Once you can organise your files, you are well on your way to being able to use the shell more effectively for day to day tasks."),(0,i.kt)("p",null,"Now that we know how to organise the files in our computer, we'll take a look at how to download files, create new files, preview the contents of files, open files, copy, move and delete files."),(0,i.kt)("p",null,"This chapter will introduce the ",(0,i.kt)("inlineCode",{parentName:"p"},"wget"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"unzip"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"cp"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"mv"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"rm"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"mkdir"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"zip")," commands. We'll also briefly look at ",(0,i.kt)("em",{parentName:"p"},"wildcards")," and ",(0,i.kt)("em",{parentName:"p"},"redirection"),"."),(0,i.kt)("h2",{id:"creating-a-playground"},"Creating a Playground"),(0,i.kt)("p",null,"Before we start copying, deleting, moving and renaming files, we should create a 'playground' area we can work in. We don't want to test all of this on our own personal files until we know exactly what we're doing! "),(0,i.kt)("p",null,"To help with this, I've created a zipped up 'samples' which has a lot of files in it which we can use to play with. Now the file itself is available on the ",(0,i.kt)("a",{parentName:"p",href:"https://effective-shell.com"},"effective-shell.com")," website, right here:"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://effective-shell.com/downloads/effective-shell-samples.zip"},"effective-shell.com/downloads/effective-shell-samples.zip")),(0,i.kt)("p",null,"We ",(0,i.kt)("em",{parentName:"p"},"could")," open up a web browser, download the file, unzip it and then start from there, but this book is all about how to deal with every day tasks in your shell, so let's skip the browser and do it in the shell instead!"),(0,i.kt)("p",null,"Open your shell - if you've not yet got set up with your shell, that's OK, just check ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/getting-started/"},"Chapter 1 - Getting Started"),"."),(0,i.kt)("p",null,"Now that you have your shell open, we can run the ",(0,i.kt)("inlineCode",{parentName:"p"},"wget")," command (",(0,i.kt)("em",{parentName:"p"},"Web Get"),") to download the zip file. Let's download it to our Home folder. If you are not sure what the Home folder is, check ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/navigating-your-system/"},"Chapter 2- Navigating Your System"),"."),(0,i.kt)("p",null,"First, we'll move to our home directory, then download the file."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cd\nwget https://effective-shell.com/downloads/effective-shell-samples.zip\n")),(0,i.kt)("p",null,"You'll see something like this:"),(0,i.kt)("img",{alt:"Screenshot: wget",src:n(2434).Z,width:"800px"}),(0,i.kt)("p",null,"When you call the ",(0,i.kt)("inlineCode",{parentName:"p"},"wget")," command, you can give it any web address and it'll download it to your current folder. It also shows the progress of the download interactively (particularly useful if it's a big file!)."),(0,i.kt)("p",null,"As an aside, if we were not in our home folder when we called the ",(0,i.kt)("inlineCode",{parentName:"p"},"wget")," command, we'd download the file to wherever we are currently working in. If we wanted to be explicit about where we download the file, we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-O")," (",(0,i.kt)("em",{parentName:"p"},"Output File"),") flag to say explicitly where we want to download the file."),(0,i.kt)("p",null,"As an example, if were not in the home folder, but wanted to download there, we'd just call:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cd\nwget -O ~/playground.zip https://effective-shell.com/downloads/effective-shell-samples.zip\n")),(0,i.kt)("p",null,"Now that we've downloaded the file, let's look at our home directory now, with a quick call to ",(0,i.kt)("inlineCode",{parentName:"p"},"ls ~"),":"),(0,i.kt)("img",{alt:"Screenshot: ls home",src:n(9074).Z,width:"800px"}),(0,i.kt)("p",null,"Cool - we have the zip file downloaded! Now we need to work out how to unzip it so we can get to the files in the zip archive."),(0,i.kt)("h2",{id:"finding-out-about-files"},"Finding out about files"),(0,i.kt)("p",null,"One of the interesting things you can do in a shell is ask it to tell you more about a file. This can be useful if we've got a file, and we're not sure what it might be. Let's try it out now:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"file ~/effective-shell-samples.zip\n")),(0,i.kt)("img",{alt:"Screenshot: file",src:n(6470).Z,width:"800px"}),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"file")," command is showing us we have a zip file - now it's time to unzip it!"),(0,i.kt)("h2",{id:"extracting-the-zip"},"Extracting the Zip"),(0,i.kt)("p",null,"Right now we have a zip file. We need to extract it, unpack the files so that we can play with them. Again, in a system with a graphical user interface, this is easy, generally you just double click on it. But we're going to use the shell for this!"),(0,i.kt)("p",null,"Run the command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"unzip ~/effective-shell-samples.zip\n")),(0,i.kt)("p",null,"Now let's look at what we've got with the ",(0,i.kt)("inlineCode",{parentName:"p"},"ls")," command:"),(0,i.kt)("img",{alt:"Screenshot: unzip",src:n(868).Z,width:"800px"}),(0,i.kt)("p",null,"Excellent - we've now got a ",(0,i.kt)("em",{parentName:"p"},"folder")," which contains all of the files in the zip archive."),(0,i.kt)("h2",{id:"deleting-files"},"Deleting Files"),(0,i.kt)("p",null,"Now that we've downloaded and unzipped the file, we don't need the zipped version any more. So let's delete this file."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," (",(0,i.kt)("em",{parentName:"p"},"Remove"),") command can be used to delete a file. If we run:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"rm ~/effective-shell-samples.zip\nls | grep samples\n")),(0,i.kt)("p",null,"Then we'll see the following:"),(0,i.kt)("img",{alt:"Screenshot: rm",src:n(7257).Z,width:"800px"}),(0,i.kt)("p",null,"Notice that the zip file is gone - just the folder is left."),(0,i.kt)("p",null,"By the way - be really careful with the ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," command. Unlike in a graphical interface, it won't put files you delete into a recycle bin, they are blatted forever! In a later chapter we'll see some ways to change this behaviour for your local machine, but always remember ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," is a little risky!"),(0,i.kt)("p",null,"However one thing it ",(0,i.kt)("em",{parentName:"p"},"will")," do to try and help you not make mistakes is let you know if you are trying to delete a ",(0,i.kt)("em",{parentName:"p"},"folder"),", not a file."),(0,i.kt)("p",null,"Run the following command to try and delete the unzipped folder:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"rm ~/effective-shell-samples\n")),(0,i.kt)("img",{alt:"Screenshot: rm error directory",src:n(7318).Z,width:"800px"}),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," command has not succeeded in this case - it's warning us that we're not deleting a file, but a whole directory."),(0,i.kt)("p",null,"Now we can get around this by adding the ",(0,i.kt)("inlineCode",{parentName:"p"},"-r")," flag, which means 'recursive' - i.e. not just the folder but everything in it. But use this with caution!"),(0,i.kt)("h2",{id:"examining-the-contents-of-a-folder"},"Examining the Contents of a Folder"),(0,i.kt)("p",null,"Let's take a look at what is in the samples. By the way, the output you see on your computer might have a few more files in it as I might have added some after writing this article!"),(0,i.kt)("p",null,"In a graphical user interface, we'd open the folders and look at the files. In the shell, we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," command to show the contents of a folder."),(0,i.kt)("p",null,"Now the ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," command is ",(0,i.kt)("em",{parentName:"p"},"not")," installed by default on all systems. So if you are on a Mac, run:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"brew install tree\n")),(0,i.kt)("p",null,"If you are on Linux, you will likely already have it. If you don't, use your distributions package manager to get it (e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"apt-get install -y tree"),")."),(0,i.kt)("p",null,"Using a non-universal command is generally ",(0,i.kt)("em",{parentName:"p"},"not")," our goal in this book, but in these early stages while we are transitioning from the graphical user interface, the ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," command can be really helpful. Later on we'll see how to use the more universal ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command to give a similar output."),(0,i.kt)("p",null,"Try it out with:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"tree ~/effective-shell-samples\n")),(0,i.kt)("img",{alt:"Screenshot: tree",src:n(4).Z,width:"800px"}),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," command shows you all of the folders and files in a location. If we are unsure what one of the files is, we can ask the shell to give us more info. For example, I could find out more about the ",(0,i.kt)("inlineCode",{parentName:"p"},"loas-gch.JPG")," file by running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"file ~/effective-shell-samples/pictures/loas-gch.JPG\n")),(0,i.kt)("img",{alt:"Screenshot: file info for JPEG file",src:n(6392).Z,width:"800px"}),(0,i.kt)("p",null,"Note that the ",(0,i.kt)("inlineCode",{parentName:"p"},"file")," command is already showing it is a bit more clever. It knows that the file is a ",(0,i.kt)("inlineCode",{parentName:"p"},"JPEG")," file (a picture), but is giving other details as well."),(0,i.kt)("h2",{id:"copying-a-file"},"Copying a File"),(0,i.kt)("p",null,"Let's say we really love that photo, and we want to make a copy of it. We can do that easily by using the ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," (_Copy) command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cp ~/effective-shell-samples/pictures/laos-gch.JPG ~/effective-shell-playground/pictures/laos-gch-copy.JPG\n")),(0,i.kt)("p",null,"This makes a copy of the file - if you are not sure if it has worked, just run:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"tree ~/effective-shell-samples\n")),(0,i.kt)("img",{alt:"Screenshot: cp command",src:n(3).Z,width:"800px"}),(0,i.kt)("p",null,"We can see we've made a copy."),(0,i.kt)("h2",{id:"saving-some-keystrokes"},"Saving Some Keystrokes"),(0,i.kt)("p",null,"Wow, it's painful putting ",(0,i.kt)("inlineCode",{parentName:"p"},"~/effective-shell-samples")," before everything! From ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/navigating-your-system/"},"Chapter 2- Navigating Your System")," we already know how to change directory, so let's do that now:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cd ~/effective-shell-samples\n")),(0,i.kt)("p",null,"Remember - ",(0,i.kt)("inlineCode",{parentName:"p"},"cd")," is ",(0,i.kt)("em",{parentName:"p"},"change directory"),". Excellent - until we tell our shell otherwise, this our new working directory."),(0,i.kt)("h2",{id:"renaming-or-moving-files"},"Renaming or Moving Files"),(0,i.kt)("p",null,"You might have noticed that the photos have different endings - one of them ends in ",(0,i.kt)("inlineCode",{parentName:"p"},".JPG"),". Let's rename it so that it has the ending ",(0,i.kt)("inlineCode",{parentName:"p"},".jpeg")," to be consistent with the others."),(0,i.kt)("p",null,"To do this, we use the ",(0,i.kt)("inlineCode",{parentName:"p"},"mv")," (",(0,i.kt)("em",{parentName:"p"},"Move"),") command. When it comes down to it, moving a file or renaming a file amount to the same kind of operation, so one command can do both."),(0,i.kt)("p",null,"Rename the copy we made of the photo by running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"mv pictures/loas-gch-copy.JPG pictures/loas-gch-copy.jpeg\n")),(0,i.kt)("p",null,"Let's run ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," to see what happened. Remember - now that our working folder is the playground, we don't even need to tell ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," where to look, if we give it no arguments it'll assume we're looking at the working directory:"),(0,i.kt)("img",{alt:"Screenshot: mv command",src:n(5714).Z,width:"800px"}),(0,i.kt)("p",null,"Much nicer! Now our copied file has been moved to have a new name. It's in the same folder still, but you can use ",(0,i.kt)("inlineCode",{parentName:"p"},"mv")," to also change what folder a file is in."),(0,i.kt)("h2",{id:"creating-a-new-folder"},"Creating a New Folder"),(0,i.kt)("p",null,"Perhaps we're not happy with the name ",(0,i.kt)("inlineCode",{parentName:"p"},"pictures")," for our folder we've been playing with, maybe we'd prefer to have them all in a folder called ",(0,i.kt)("inlineCode",{parentName:"p"},"photos"),"?"),(0,i.kt)("p",null,"Probably the first thing we'd do in a graphical environment is create a new folder - so let's do thee same here!"),(0,i.kt)("p",null,"Run the commands:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir photos\ntree\n")),(0,i.kt)("p",null,"And we should see:"),(0,i.kt)("img",{alt:"Screenshot: mkdir command",src:n(5704).Z,width:"800px"}),(0,i.kt)("p",null,"We've use the ",(0,i.kt)("inlineCode",{parentName:"p"},"mkdir")," command, which is short for ",(0,i.kt)("em",{parentName:"p"},"Make Directory"),". This is how we create a new folder in the shell."),(0,i.kt)("p",null,"Now let's say we wanted to be ",(0,i.kt)("em",{parentName:"p"},"really")," organised, and create a photos folder by year and topic, perhaps ",(0,i.kt)("inlineCode",{parentName:"p"},"2019/outdoors/pictures"),". In a graphical user interface, we'd have to create each folder one at a time. In the shell, it's easy!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir -p 2019/outdoors/pictures\ntree\n")),(0,i.kt)("p",null,"Let's see how it looks:"),(0,i.kt)("img",{alt:"Screenshot: mkdirp command",src:n(8960).Z,width:"800px"}),(0,i.kt)("p",null,"All we had to do was add the ",(0,i.kt)("inlineCode",{parentName:"p"},"-p")," flag (which means \"make the parent folder if it doesn't already exist) and we can create a whole set of subfolders. Now we're starting to see why knowing the shell can be powerful - if you know you have this trick up your sleeve you can be doing things like re-organising files ",(0,i.kt)("em",{parentName:"p"},"more effectively")," in a shell than in your graphical user interface!"),(0,i.kt)("h2",{id:"copying-or-moving-multiple-files-with-wildcards"},"Copying or Moving Multiple Files with Wildcards"),(0,i.kt)("p",null,"Let's copy the photos that we have in the ",(0,i.kt)("inlineCode",{parentName:"p"},"pictures")," folder into the ",(0,i.kt)("inlineCode",{parentName:"p"},"photos/2019/outdoor/climbing")," folder."),(0,i.kt)("p",null,"When we run the ",(0,i.kt)("inlineCode",{parentName:"p"},"cp")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"mv")," command, we can use a ",(0,i.kt)("em",{parentName:"p"},"wildcard")," to specify the files we are copying and moving. A wildcard is a simple pattern which can be used to select multiple files. Here's how we can copy the photos over:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cp pictures/* photos/2019/outdoor/climbing\n")),(0,i.kt)("p",null,"Here's how it works for\nNow we need to copy over our files from the ",(0,i.kt)("inlineCode",{parentName:"p"},"pictures")," folder to the ",(0,i.kt)("inlineCode",{parentName:"p"},"2019/outdoor/photos")," folder. We'll use exactly the command we used before to copy a file - ",(0,i.kt)("inlineCode",{parentName:"p"},"cp"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ cp pictures/* photos/2019/outdoors/climbing/\n\n$ tree photos\nphotos\n\u251c\u2500\u2500 2019\n\u2502\xa0\xa0 \u2514\u2500\u2500 outdoors\n\u2502\xa0\xa0 \u2514\u2500\u2500 climbing\n\u2502\xa0\xa0 \u251c\u2500\u2500 laos-gch-copy.jpeg\n\u2502\xa0\xa0 \u251c\u2500\u2500 laos-gch.JPG\n\u2502\xa0\xa0 \u2514\u2500\u2500 nepal-mardi-himal.jpeg\n\u2514\u2500\u2500 2020\n \u2514\u2500\u2500 outdoors\n \u2514\u2500\u2500 climbing\n\n6 directories, 3 files\n")),(0,i.kt)("p",null,"Here we've used the ",(0,i.kt)("em",{parentName:"p"},"wildcard")," symbol, which is ",(0,i.kt)("inlineCode",{parentName:"p"},"*"),', to say "everything in the folder". Many commands can take wildcards as inputs. We\'ll see much more about them later!'),(0,i.kt)("h2",{id:"deleting-a-folder"},"Deleting a Folder"),(0,i.kt)("p",null,"Now that we have our more organise ",(0,i.kt)("inlineCode",{parentName:"p"},"2019/outdoors/photos")," folder, we don't need the ",(0,i.kt)("inlineCode",{parentName:"p"},"photos")," folder we created. So let's delete it! Remember how ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," removes a file, and ",(0,i.kt)("inlineCode",{parentName:"p"},"mkdir")," creates a folder? Well ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir")," will remove a folder!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"rmdir photos\ntree\n")),(0,i.kt)("img",{alt:"Screenshot: rmdir command",src:n(6729).Z,width:"800px"}),(0,i.kt)("p",null,"As an important sidenote, just how ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," doesn't move files to your recycle bin, so you cannot undo the operation, ",(0,i.kt)("inlineCode",{parentName:"p"},"rmdir")," works the same way. So if we try to remove a directory which has things in it, such as the ",(0,i.kt)("inlineCode",{parentName:"p"},"pictures")," directory, it will fail:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"rmdir pictures\n")),(0,i.kt)("img",{alt:"Screenshot: rmdir fail command",src:n(4195).Z,width:"800px"}),(0,i.kt)("p",null,"In this case, it is actually easier to just call ",(0,i.kt)("inlineCode",{parentName:"p"},"rm -r pictures"),". Why is that? Well it's just like we saw in the earlier example - ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," can delete files or directories. And if the directory is not empty, we just add the ",(0,i.kt)("inlineCode",{parentName:"p"},"-r")," (",(0,i.kt)("em",{parentName:"p"},"Recursive"),") flag to tell it to delete the directory and everything it contains."),(0,i.kt)("h2",{id:"looking-at-text-files"},"Looking at Text Files"),(0,i.kt)("p",null,"Run ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," and you'll see we have a ",(0,i.kt)("inlineCode",{parentName:"p"},"quotes")," folder:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"tree\n")),(0,i.kt)("img",{alt:"Screenshot: tree of new quotes folder",src:n(6227).Z,width:"800px"}),(0,i.kt)("p",null,"We're going to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," (",(0,i.kt)("em",{parentName:"p"},"Concatenate"),") command to look at the Ursula Le Guin quote. Run the following command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cat quotes/ursula-le-guin.txt\n")),(0,i.kt)("img",{alt:"Screenshot: cat",src:n(1718).Z,width:"800px"}),(0,i.kt)("p",null,"In the screenshot we snuck in a quick ",(0,i.kt)("inlineCode",{parentName:"p"},"file")," call to see what the shell thinks the file is."),(0,i.kt)("p",null,"Why ",(0,i.kt)("em",{parentName:"p"},"Concatenate"),"? We're just showing the text in the terminal, not concatenating (i.e. joining) anything! Well the reason is that the ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," command ",(0,i.kt)("em",{parentName:"p"},"does")," concatenate files (i.e. puts them together), it's just that we only gave it one file, so it had nothing to join it to. By default, ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," writes the output to the screen, so this is one of the most common ways you'll see to quickly look at the contents of a file."),(0,i.kt)("p",null,"We'll see a ",(0,i.kt)("em",{parentName:"p"},"lot")," more about how this works later, and how you can then take that output and put it somewhere else. But for now, let's finish with a couple of tricks."),(0,i.kt)("p",null,"First, let's just ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," the whole folder:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cat quotes/*\n")),(0,i.kt)("img",{alt:"Screenshot: cat wildcard",src:n(9391).Z,width:"800px"}),(0,i.kt)("p",null,"There we see the ",(0,i.kt)("inlineCode",{parentName:"p"},"*")," wildcard again. We could be more specific and use something like ",(0,i.kt)("inlineCode",{parentName:"p"},"cat quotes/*.txt")," to only show files ending in ",(0,i.kt)("inlineCode",{parentName:"p"},".txt"),"."),(0,i.kt)("p",null,"Notice how the output from all of the files has been ",(0,i.kt)("em",{parentName:"p"},"concatenated together")," into a single output? That's where the ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," name comes from - it ",(0,i.kt)("em",{parentName:"p"},"concatenates"),", i.e. ",(0,i.kt)("em",{parentName:"p"},"joins")," files."),(0,i.kt)("p",null,"As one last trick, let's use this output but instead of showing it on the screen, put it into a single ",(0,i.kt)("inlineCode",{parentName:"p"},"all-quotes.txt")," file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cat quotes/* > quotes/all-quotes.txt\ntree\ncat quotes/all-quotes.txt\n")),(0,i.kt)("img",{alt:"Screenshot: cat redirect",src:n(1415).Z,width:"800px"}),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},">")," part of this is called a ",(0,i.kt)("em",{parentName:"p"},"redirect operator")," - in short it's telling the shell not to write to the screen, ",(0,i.kt)("em",{parentName:"p"},"but to write to a file"),". We've concatenated all of the individual quotes and made a single file from them."),(0,i.kt)("p",null,"We'll look at wildcards and redirection in a lot more detail as we continue through the book!"),(0,i.kt)("h2",{id:"zipping-up-files"},"Zipping up Files"),(0,i.kt)("p",null,"Let' say that we want to zip up the new ",(0,i.kt)("inlineCode",{parentName:"p"},"2019/outdoors/pictures")," folder. We've already seen the ",(0,i.kt)("inlineCode",{parentName:"p"},"unzip")," command, let's see how to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"zip")," command to zip up a folder:"),(0,i.kt)("p",null,"Run the command below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"zip -r 2019-outdoor-pictures.zip 2019\n")),(0,i.kt)("p",null,"This is how it will look - there's a ",(0,i.kt)("inlineCode",{parentName:"p"},"tree")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"ls")," command before and after so we can see what's happening!"),(0,i.kt)("img",{alt:"Screenshot: zip",src:n(7860).Z,width:"800px"}),(0,i.kt)("p",null,"Great! We've created a zip. Let's dissect the command a bit:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"zip")," just means call the ",(0,i.kt)("inlineCode",{parentName:"li"},"zip")," executable"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"-r")," means ",(0,i.kt)("em",{parentName:"li"},"recursive")," we don't just want to zip the 2019 folder, we want to zip everything inside it as well!"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"2019-outdoor-pictures.zip")," is the name of the file we want to create, we put this first..."),(0,i.kt)("li",{parentName:"ul"},"...because everything which follows (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"2019"),") is going to be zipped, and we can specify many files and folders if we want")),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"In this chapter we introduced the following: "),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"wget")," (",(0,i.kt)("em",{parentName:"li"},"web get"),") command can download a file from the web."),(0,i.kt)("li",{parentName:"ul"},"If we use the ",(0,i.kt)("inlineCode",{parentName:"li"},"-O")," (",(0,i.kt)("em",{parentName:"li"},"output location"),") flag, we can specify ",(0,i.kt)("em",{parentName:"li"},"where")," we want to download the file to."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"file")," command can be used to ask the shell what it thinks a file is (this is quite useful because unlike on some systems, not all files in Linux have a file ending)."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"unzip")," command can unzip a file for us."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"rm")," (",(0,i.kt)("em",{parentName:"li"},"remove"),") command can delete a file."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"rm")," command won't delete a folder which has files in it, unless you tell it to by adding the ",(0,i.kt)("inlineCode",{parentName:"li"},"-r")," (",(0,i.kt)("em",{parentName:"li"},"recursive"),") flag."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"tree")," command can show the files and folders in a given directory, or the current directory by default."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"cp")," (",(0,i.kt)("em",{parentName:"li"},"copy"),") command can copy a file."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"cp")," can also be given wildcards like ",(0,i.kt)("inlineCode",{parentName:"li"},"*")," to copy many files."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"mv")," (",(0,i.kt)("em",{parentName:"li"},"move"),") command can move or rename a file."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"mkdir")," command can create a folder - it can even create a whole tree of folders if you pass the ",(0,i.kt)("inlineCode",{parentName:"li"},"-p")," (",(0,i.kt)("em",{parentName:"li"},"create parent directories"),") flag."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"rmdir")," command can delete a folder - but just like ",(0,i.kt)("inlineCode",{parentName:"li"},"rm")," it will fail if the folder is not empty!"),(0,i.kt)("li",{parentName:"ul"},"When we delete files in the shell with ",(0,i.kt)("inlineCode",{parentName:"li"},"rm")," or ",(0,i.kt)("inlineCode",{parentName:"li"},"rmdir")," they are gone forever, no recycle bin!"),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"cat")," command (",(0,i.kt)("em",{parentName:"li"},"concatenated"),") can be used to write the contents of a file to the screen."),(0,i.kt)("li",{parentName:"ul"},"We can pass multiple files to commands like ",(0,i.kt)("inlineCode",{parentName:"li"},"cat")," if we use wildcards, such as ",(0,i.kt)("inlineCode",{parentName:"li"},"quotes/*"),"."),(0,i.kt)("li",{parentName:"ul"},"We can write the output to a file instead of the screen, if we use the ",(0,i.kt)("inlineCode",{parentName:"li"},">")," (",(0,i.kt)("em",{parentName:"li"},"redirect to file"),") operator.")))}m.isMDXComponent=!0},1415:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cat-redirect-f1b5c975936a1b619eef1198efcb51bd.png"},9391:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cat-wildcard-14d0eefcb7dd7221857c5f39db5f0dfc.png"},1718:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cat-a81e2704f0cdbb0212bd5de6b3c53cf8.png"},3:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/cp-da8c2c5c8cea67d43451d876d4bb86ba.png"},6392:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/file-jpeg-info-8ff5a2ca4bb1ba0916f224ce204a8e3d.png"},6470:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/file-a824267fcbcf509a0f197686e40ad111.png"},9074:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/ls-home-90c668ffbc2eb7ac22715c623fdb576c.png"},5704:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mkdir-57c2af124ac85f07cb18f8b00fcfde1f.png"},8960:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mkdirp-4684ca958e3795dedccfed26e56dae22.png"},5714:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mv-9f1b888690e88b5ae75a1c2cbc4954a3.png"},7318:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/rm-error-directory-eb8abf1bd4997caee5d3879c35c1f2f0.png"},7257:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/rm-17de3ec6fc67be43dd6e437f9264f2df.png"},4195:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/rmdir-fail-2cdc29322732719ab601280bd1765b39.png"},6729:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/rmdir-a7fc9d107815c1f7b53a2907efd78643.png"},6227:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/tree-quotes-621beb5563167c8af53e79a6c01e2e2b.png"},4:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/tree-cbe0b2feb4e9d491bc323895971f8a8b.png"},868:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/unzip-f1a9a5d5eed1b92f90fda4b358e447f7.png"},2434:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/wget-68b61c0c8b06c80bf345ce94a7a69b0b.png"},7860:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/zip-0f8e0ab92362d59195ccb5ceb432063d.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/8f309a18.1ed1aba0.js b/pr-preview/pr-346/assets/js/8f309a18.1ed1aba0.js new file mode 100644 index 00000000..3b9c3948 --- /dev/null +++ b/pr-preview/pr-346/assets/js/8f309a18.1ed1aba0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[8817],{3905:(e,t,n)=>{n.d(t,{Zo:()=>h,kt:()=>c});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t =0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},h=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,h=l(e,["components","mdxType","originalType","parentName"]),d=p(n),u=i,c=d["".concat(s,".").concat(u)]||d[u]||m[u]||r;return n?a.createElement(c,o(o({ref:t},h),{},{components:n})):a.createElement(c,o({ref:t},h))}));function c(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:i,o[1]=l;for(var p=2;p {n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var a=n(7462),i=(n(7294),n(3905));const r={title:"Thinking in Pipelines",slug:"/part-2-core-skills/thinking-in-pipelines/"},o=void 0,l={unversionedId:"core-skills/thinking-in-pipelines/index",id:"core-skills/thinking-in-pipelines/index",title:"Thinking in Pipelines",description:"Understanding the concept of pipelines in the shell, as well as how input and output work for command line programs is critical to be able to use the shell effectively.",source:"@site/docs/02-core-skills/07-thinking-in-pipelines/index.md",sourceDirName:"02-core-skills/07-thinking-in-pipelines",slug:"/part-2-core-skills/thinking-in-pipelines/",permalink:"/part-2-core-skills/thinking-in-pipelines/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/02-core-skills/07-thinking-in-pipelines/index.md",tags:[],version:"current",frontMatter:{title:"Thinking in Pipelines",slug:"/part-2-core-skills/thinking-in-pipelines/"},sidebar:"sidebar",previous:{title:"Part 2 - Core Skills",permalink:"/part-2-core-skill/"},next:{title:"Fly on the Command Line",permalink:"/part-2-core-skills/fly-on-the-command-line"}},s={},p=[{value:"Input and Output",id:"input-and-output",level:2},{value:"Standard Input, Output and Error",id:"standard-input-output-and-error",level:2},{value:"A Pipeline in Action",id:"a-pipeline-in-action",level:2},{value:"Common Patterns - Standard Input",id:"common-patterns---standard-input",level:2},{value:"Common Patterns - Standard Output",id:"common-patterns---standard-output",level:2},{value:"Common Patterns - Standard Error",id:"common-patterns---standard-error",level:2},{value:"One Last Trick - The T Pipe",id:"one-last-trick---the-t-pipe",level:2},{value:"Thinking in Pipelines",id:"thinking-in-pipelines",level:2},{value:"Summary",id:"summary",level:2}],h={toc:p};function d(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,a.Z)({},h,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Understanding the concept of ",(0,i.kt)("em",{parentName:"p"},"pipelines")," in the shell, as well as how input and output work for command line programs is critical to be able to use the shell effectively."),(0,i.kt)("p",null,"In this chapter, we'll look at the ways programs handle input and output, then we'll look at how we can chain multiple commands together with pipelines. We'll also look at some really common ways to use pipelines which should hopefully make your life easier!"),(0,i.kt)("p",null,"When you understand these concepts, it will open up a new world in terms of what you can do with in the shell. We'll briefly touch on the 'The Unix Philosophy', which is a concept which allows us to perform highly complex tasks by composing together small, simple components."),(0,i.kt)("h2",{id:"input-and-output"},"Input and Output"),(0,i.kt)("p",null,"Many of the programs we have seen so far follow a very similar pattern:"),(0,i.kt)("img",{src:n(7007).Z,alt:"Diagram: Input -> Program -> Output",width:"480px"}),(0,i.kt)("p",null,"In fact, when you get down to the details, there are very few programs which ",(0,i.kt)("em",{parentName:"p"},"don't")," do something like this! As a more concrete example, we can look at the ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," program - which sorts the input in alphabetic order:"),(0,i.kt)("img",{src:n(1023).Z,alt:"Diagram: Sort",width:"480px"}),(0,i.kt)("p",null,"We can easily see this in action by just running ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," in a shell. Start the ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," program, enter some text, then press ",(0,i.kt)("inlineCode",{parentName:"p"},"Ctrl+D"),". ",(0,i.kt)("inlineCode",{parentName:"p"},"Ctrl+D")," (which is normally written as ",(0,i.kt)("inlineCode",{parentName:"p"},"^D")," is a special ",(0,i.kt)("em",{parentName:"p"},"control character")," which means 'end of transmission' - in this case we use it to tell ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," we've finished writing text. If you were to use ",(0,i.kt)("inlineCode",{parentName:"p"},"^C")," (which is the ",(0,i.kt)("em",{parentName:"p"},"interrupt")," command) it would closes the ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," program instead)."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ sort\nDogs\nchase\ncats\nand\ncats\nchase\nmice\n")),(0,i.kt)("p",null,"Once you've entered the text you want to sort, hit ",(0,i.kt)("inlineCode",{parentName:"p"},"^D")," and you'll see the sorted output:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"Dogs\nand\ncats\ncats\nchase\nchase\nmice\n")),(0,i.kt)("p",null,"So by default, the ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," command is reading ",(0,i.kt)("em",{parentName:"p"},"input")," from the keyboard (until we send it a special message saying we're done), then writing the ",(0,i.kt)("em",{parentName:"p"},"output")," to the terminal."),(0,i.kt)("p",null,"In fact, ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," is using two special ",(0,i.kt)("em",{parentName:"p"},"files")," - ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," - but what does this mean?"),(0,i.kt)("h2",{id:"standard-input-output-and-error"},"Standard Input, Output and Error"),(0,i.kt)("p",null,"Every program has access to three 'special' files, ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr"),":"),(0,i.kt)("img",{src:n(6699).Z,alt:"Diagram: stdin, stdout, stderr",width:"480px"}),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stdin")," is short for 'standard input' - it's where ",(0,i.kt)("em",{parentName:"li"},"many")," programs read their input from"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stdout")," is short for 'standard output' - it's where ",(0,i.kt)("em",{parentName:"li"},"many")," programs write their output to"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stderr")," is short for 'standard error' - it's where ",(0,i.kt)("em",{parentName:"li"},"some")," programs write error messages to")),(0,i.kt)("p",null,"Why do I say 'many' and 'some'? Well the reason is that while this is ",(0,i.kt)("em",{parentName:"p"},"convention"),", it is not adhered to universally. Anyone who writes a program is free to choose how they read input and write output, so some programs might not follow these conventions. In Chapter 29 we'll look at how to write tools which follow these conventions, as well as others which are useful."),(0,i.kt)("p",null,"Each of these files has a special number which is shown in grey in the diagram. This is known as the ",(0,i.kt)("em",{parentName:"p"},"file descriptor")," and we'll see it later on. Each of these files also has a special location in the system which you can access directly - you can see these files by running ",(0,i.kt)("inlineCode",{parentName:"p"},"ls -al /dev/std*"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ ls -al /dev/std*\nlr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stderr -> fd/2\nlr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stdin -> fd/0\nlr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stdout -> fd/1\n")),(0,i.kt)("p",null,"If you are not familiar with ",(0,i.kt)("inlineCode",{parentName:"p"},"ls")," (the ",(0,i.kt)("em",{parentName:"p"},"list directory contents")," command) then check ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/navigating-your-system/"},"Chapter 2 - Navigating Your System"),". The first part of the output isn't too important - but we can see we have three files in the special ",(0,i.kt)("inlineCode",{parentName:"p"},"/dev/")," (short for ",(0,i.kt)("em",{parentName:"p"},"device")," folder). We can also see the associated file descriptors."),(0,i.kt)("p",null,"As an aside - this is a really fundamental thing we'll see again and again in Unix and Linux - almost everything can be represented as a file. This is a core concept and one we'll touch on regularly."),(0,i.kt)("p",null,"When you are running programs in a shell, the shell attaches your keyboard to the program's standard input",(0,i.kt)("sup",{parentName:"p",id:"fnref-1"},(0,i.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),", and attaches the standard output and standard error to the terminal display:"),(0,i.kt)("img",{src:n(4956).Z,alt:"Diagram: Shell, Keyboard, Terminal",width:"640px"}),(0,i.kt)("p",null,"This means when we're in a shell, we can type on the keyboard, which goes to the input of the program and then as the program outputs information and errors they show up on the screen."),(0,i.kt)("p",null,"We can already see the beginnings of a ",(0,i.kt)("strong",{parentName:"p"},"pipeline")," here. There's a clear flow of data from the keyboard, through the ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin")," file, through the program, then through the output files, then to the display."),(0,i.kt)("p",null,"Looking at some real programs in action will hopefully make this clearer!"),(0,i.kt)("h2",{id:"a-pipeline-in-action"},"A Pipeline in Action"),(0,i.kt)("p",null,"Do you remember the ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," command? It's the one which writes the contents of a file to the screen. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cat ~/effective-shell/text/simpsons-characters.txt\nArtie Ziff\nKirk Van Houten\nTimothy Lovejoy\nArtie Ziff\nNick Riviera\nSeymore Skinner\nHank Scorpio\nTimothy Lovejoy\nJohn Frink\nCletus Spuckler\nRuth Powers\nArtie Ziff\nAgnes Skinner\nHelen Lovejoy\n")),(0,i.kt)("p",null,"We saw in ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/"},"Chapter 4 - Becoming a Clipboard Gymnast")," that we could ",(0,i.kt)("strong",{parentName:"p"},"pipe")," the output of this command into the ",(0,i.kt)("inlineCode",{parentName:"p"},"sort")," command to order it and then into the ",(0,i.kt)("inlineCode",{parentName:"p"},"uniq")," command to remove duplicates, like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cat ~/effective-shell/text/simpsons-characters.txt | sort | uniq\nAgnes Skinner\nArtie Ziff\nCletus Spuckler\nHank Scorpio\nHelen Lovejoy\nJohn Frink\nKirk Van Houten\nNick Riviera\nRuth Powers\nSeymore Skinner\nTimothy Lovejoy\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("strong",{parentName:"p"},"pipe")," operator (which is the vertical pipe symbol or ",(0,i.kt)("inlineCode",{parentName:"p"},"|"),") has a very specific meaning in the shell - it attaches the ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," of the first program to the ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin")," of the second. This means we can now visualise the entire pipeline and see exactly what is going on:"),(0,i.kt)("img",{src:n(586).Z,alt:"Diagram: cat-sort-uniq pipeline",width:"1024px"}),(0,i.kt)("p",null,"That's it! If you can follow what is going on here then you have the key information you need to know to understand how pipelines work. The pipe operator just connects the output of one program to the input of another. A pipeline is just a set of connected programs. Easy!"),(0,i.kt)("p",null,"We could do the same thing by writing the output of each step as a file, then reading that file with the next step, but that would mean we'd have a lot of intermediate files to clean up (and if we're processing a ",(0,i.kt)("em",{parentName:"p"},"big")," file, it also uses a lot of space). Pipelines let us create complex sequences of operations which work well even on very large files."),(0,i.kt)("p",null,"Now we'll look at ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," in a little more detail. We'll be seeing these special streams a lot as we go through the book. Knowing more about them is really going to help you when working in the shell or with Linux-like systems."),(0,i.kt)("h2",{id:"common-patterns---standard-input"},"Common Patterns - Standard Input"),(0,i.kt)("p",null,"Let's have a quick look at some of the common things we might see as sources of inputs for other programs. Each one illustrates an interesting point about how the shell or the standard input stream works."),(0,i.kt)("img",{src:n(3657).Z,alt:"Diagram: Input Examples",width:"1024px"}),(0,i.kt)("p",null,"This list is by no means exhaustive, in fact with a bit of tinkering you can make almost anything the input to anything else, but let's check each example."),(0,i.kt)("p",null,"These examples will use some new programs to transform the output - don't worry about the details of them, each will be described as we go through the book!"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"The Shell")),(0,i.kt)("p",null,"You might just use code in the shell as input, for example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo \"I am in the $PWD folder\" | sed 's/folder/directory/'\nI am in the /Users/dwmkerr/repos/github/dwmkerr/effective-shell directory\n")),(0,i.kt)("p",null,"Here we've just used ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," to write out message including a variable and then used the ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," (",(0,i.kt)("em",{parentName:"p"},"stream editor"),") program to replace the word ",(0,i.kt)("inlineCode",{parentName:"p"},"folder")," with ",(0,i.kt)("inlineCode",{parentName:"p"},"directory"),". We'll get a lot of practice with ",(0,i.kt)("inlineCode",{parentName:"p"},"sed")," as we go through this book!"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Files")),(0,i.kt)("p",null,"We've already seen a few examples of using ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," to write a file to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),"."),(0,i.kt)("p",null,"A lot of the time we don't need to use ",(0,i.kt)("inlineCode",{parentName:"p"},"cat")," many programs accept the path of a file as a parameter, meaning we can just tell the program to open the file directly. For example, we could count the number of lines in a file like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cat ~/effective-shell/text/simpsons-characters.txt | wc -l\n\n 14\n")),(0,i.kt)("p",null,"Or more simply, like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ wc -l ~/effective-shell/text/simpsons-characters.txt\n 14 /Users/dwmkerr/effective-shell/text/simpsons-characters.txt\n")),(0,i.kt)("p",null,"In this case, we've passed the file path as an argument to the ",(0,i.kt)("inlineCode",{parentName:"p"},"wc")," (",(0,i.kt)("em",{parentName:"p"},"word, line, character and byte count"),") program. But be aware - not all programs use the same convention or parameter names!"),(0,i.kt)("p",null,"Now here's a cool trick. Type ",(0,i.kt)("inlineCode",{parentName:"p"},"rev < /dev/stdin"),", then enter some text and hit ",(0,i.kt)("inlineCode",{parentName:"p"},"^D")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"^C")," when done. You should see something like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ rev < /dev/stdin\nRed Rum\nmuR deR\n")),(0,i.kt)("p",null,"What's going on here? Remember we mentioned that ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin")," is a special stream which represents input and that it lives at ",(0,i.kt)("inlineCode",{parentName:"p"},"/dev/stdin"),"? This little trick uses ",(0,i.kt)("em",{parentName:"p"},"redirection")," to redirect the ",(0,i.kt)("inlineCode",{parentName:"p"},"stdin")," file to the ",(0,i.kt)("inlineCode",{parentName:"p"},"rev")," (",(0,i.kt)("em",{parentName:"p"},"reverse"),") command."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"<")," operator redirects the standard input of a program to come from the given file. We could also have written ",(0,i.kt)("inlineCode",{parentName:"p"},"cat /dev/stdin | rev"),". Or just enter ",(0,i.kt)("inlineCode",{parentName:"p"},"rev")," and type in the input we want to reverse!"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"The Clipboard")),(0,i.kt)("p",null,"In ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/"},"Chapter 4 - Becoming a Clipboard Gymnast")," we saw a trick to remove formatting from text in the clipboard. Here's a similar trick to reverse the contents of the clipboard:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ pbpaste | rev | pbcopy\n")),(0,i.kt)("p",null,"This pipeline pastes the contents of the clipboard to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),", which is piped to ",(0,i.kt)("inlineCode",{parentName:"p"},"rev")," (reversing the text) and then pipes the output to ",(0,i.kt)("inlineCode",{parentName:"p"},"pbcopy"),", which copies the results to the clipboard",(0,i.kt)("sup",{parentName:"p",id:"fnref-2"},(0,i.kt)("a",{parentName:"sup",href:"#fn-2",className:"footnote-ref"},"2")),"."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Filtered Input")),(0,i.kt)("p",null,"This is a trick a friend shared with me. He works with data scientists and whenever he shows them this command they love it!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ head -n 100 100GBFile.csv > 100linefile.csv\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"head")," (",(0,i.kt)("em",{parentName:"p"},"display first lines of a file"),") command in this case just grabs the first 100 lines of a file and puts it straight into a smaller, more manageable file. We'll see what the ",(0,i.kt)("inlineCode",{parentName:"p"},">")," symbol (the ",(0,i.kt)("em",{parentName:"p"},"redirection")," symbol) means in the section lower down on Standard Output."),(0,i.kt)("p",null,"You can also use ",(0,i.kt)("inlineCode",{parentName:"p"},"tail")," in the same way to get the ",(0,i.kt)("em",{parentName:"p"},"last")," lines from a file. And if you are a more advanced user, you might use something like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ grep -C 5 error /var/log/mylogfile.txt | less\n")),(0,i.kt)("p",null,"We'll see all of these commands as we go through the book, but this very cool trick uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"grep")," (",(0,i.kt)("em",{parentName:"p"},"file pattern searcher"),") command to search for the text ",(0,i.kt)("inlineCode",{parentName:"p"},"error")," in the file ",(0,i.kt)("inlineCode",{parentName:"p"},"/var/log/mylogfile.txt"),", shows five lines of ",(0,i.kt)("em",{parentName:"p"},"context")," (",(0,i.kt)("inlineCode",{parentName:"p"},"-C 5"),"), which are the lines before and after the match, then puts the result into your pager! We'll see the pager just below. We'll do a lot of ",(0,i.kt)("inlineCode",{parentName:"p"},"grep"),"-ing as we go through the book so don't worry if this looks a little confusing for now."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Many More!")),(0,i.kt)("p",null,"We've only scratched the surface - almost any program will write to the standard output, meaning it can be the input for any pipeline you can imagine!"),(0,i.kt)("h2",{id:"common-patterns---standard-output"},"Common Patterns - Standard Output"),(0,i.kt)("p",null,"Now let's look at some of the things we can do with the standard output:"),(0,i.kt)("img",{src:n(4146).Z,alt:"Diagram: Output Examples",width:"1024px"}),(0,i.kt)("p",null,"Some of these outputs are things we've seen before, but let's do a quick revision."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Display")),(0,i.kt)("p",null,"This is what we've been doing a lot of so far. When you are working with the shell ",(0,i.kt)("em",{parentName:"p"},"interactively")," this makes a lot of sense."),(0,i.kt)("p",null,"If you have jobs which run in the ",(0,i.kt)("em",{parentName:"p"},"background")," (or on a timer, such as backup jobs which run nightly), you might not actually have a terminal attached to the program to see the output, in which case you'll likely write to a file."),(0,i.kt)("p",null,"What about if you have a ",(0,i.kt)("em",{parentName:"p"},"lot")," of output? It can be quite inconvenient to have to scroll through the terminal (or impossible, depending on the system you are on). In this case use a ",(0,i.kt)("em",{parentName:"p"},"pager"),". A pager is a program which makes it possible to interactively ",(0,i.kt)("em",{parentName:"p"},"page")," through output in the shell, scrolling up and down, searching and so on."),(0,i.kt)("p",null,"Try this out as an example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"ls /usr/bin /usr/local/bin /usr/sbin | less\n")),(0,i.kt)("p",null,"You'll see something like this:"),(0,i.kt)("img",{src:n(1377).Z,alt:"Screenshot: Less Example",width:"800px"}),(0,i.kt)("p",null,"This long list of files would be hard to search through if it was printed directly to the shell, but in the pager we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"d")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"u")," keys to go ",(0,i.kt)("em",{parentName:"p"},"down")," and ",(0,i.kt)("em",{parentName:"p"},"up"),", or the ",(0,i.kt)("inlineCode",{parentName:"p"},"/")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"?")," keys to search forwards or backwards."),(0,i.kt)("p",null,"Piping into your pager is a really useful trick - you can read more about pagers in ",(0,i.kt)("a",{parentName:"p",href:"/part-1-transitioning-to-the-shell/getting-help/"},"Chapter 5 - Getting Help"),"."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"File")),(0,i.kt)("p",null,"The shell has a built in operator which will pipe the standard output of a program and write it to a file. It is the ",(0,i.kt)("inlineCode",{parentName:"p"},">")," or ",(0,i.kt)("em",{parentName:"p"},"redirection")," operator:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ echo "Here\'s some data" > some_file.txt\n')),(0,i.kt)("p",null,"It's as easy as that! Note that this will ",(0,i.kt)("em",{parentName:"p"},"overwrite")," anything already in the file."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Append")),(0,i.kt)("p",null,"What if you don't want to overwrite a file, but instead just add a new line? The ",(0,i.kt)("inlineCode",{parentName:"p"},">>")," or ",(0,i.kt)("em",{parentName:"p"},"append redirection")," operator:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ echo "Tuesday was good" >> diary.txt\n$ echo "Wednesday was better!" >> diary.txt\n$ echo "Thursday suuucks" >> diary.txt\n$ cat diary.txt\nTuesday was good\nWednesday was better!\nThursday suuucks\n')),(0,i.kt)("p",null,"This example writes each line in turn to the ",(0,i.kt)("inlineCode",{parentName:"p"},"diary.txt")," file, appending the text to the end of the file (and creating it if it doesn't already exist)."),(0,i.kt)("p",null,"Appending to a file is extremely useful for circumstances where you might want to build or update a log of events over time."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Pipe")),(0,i.kt)("p",null,"This is what we've spent most of this chapter looking at - to simply pipe the standard output to the standard input of another program!"),(0,i.kt)("p",null,"In this case, the output of our program becomes the input of the next one in the pipeline."),(0,i.kt)("h2",{id:"common-patterns---standard-error"},"Common Patterns - Standard Error"),(0,i.kt)("p",null,"We haven't actually seen ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," in action yet. Let's see how it works."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ mkdir ~/effective-shell/new-folder\n$ mkdir ~/effective-shell/new-folder\nmkdir: /home/dwmkerr/effective-shell/new-folder: File exists\n")),(0,i.kt)("p",null,"In the first call to ",(0,i.kt)("inlineCode",{parentName:"p"},"mkdir"),", the folder is created successfully. In the second call, we get an error. Now let's try and use this output and make it louder - making all of the text uppercase."),(0,i.kt)("p",null,"There are lots of ways to make text uppercase in the shell, let's use the ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," (",(0,i.kt)("em",{parentName:"p"},"translate characters"),") program. Here's an example of how it works:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo 'Be quiet, this is a library!' | tr '[:lower:]' '[:upper:]'\nBE QUIET, THIS IS A LIBRARY!\n")),(0,i.kt)("p",null,"Now let's use it to shout out our error message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ mkdir ~/effective-shell/new-folder | tr '[:lower:]' '[:upper:]'\nmkdir: /home/dwmkerr/effective-shell/new-folder: File exists\n")),(0,i.kt)("p",null,"In this case the output has not been made uppercase. What's going on?"),(0,i.kt)("p",null,"To understand, let's quickly review the three streams:"),(0,i.kt)("img",{src:n(6699).Z,alt:"Diagram: stdin/stdout/stderr",width:"1024px"}),(0,i.kt)("p",null,"When we are in the shell, the shell automatically writes the ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," stream to the screen. But the shell's ",(0,i.kt)("em",{parentName:"p"},"pipe")," operator pipes ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," only - it is ",(0,i.kt)("em",{parentName:"p"},"not")," piping our error output. And the ",(0,i.kt)("inlineCode",{parentName:"p"},"mkdir")," command is writing this error message to ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr"),"."),(0,i.kt)("p",null,"The command we ran before:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"mkdir ~/effective-shell/new-folder | tr '[:lower:]' '[:upper:]'\n")),(0,i.kt)("p",null,"Actually looks like this:"),(0,i.kt)("img",{src:n(930).Z,alt:"Diagram: Standard Error",width:"1024px"}),(0,i.kt)("p",null,"The pipe ",(0,i.kt)("em",{parentName:"p"},"has")," piped the standard output to the ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," program. But there is no standard output - the error message was written to ",(0,i.kt)("em",{parentName:"p"},"standard error")," instead. The shell has still written it to the screen for us, but has not piped it to the ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," program."),(0,i.kt)("p",null,"So how do we deal with ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr"),"? Here are some common options:"),(0,i.kt)("img",{src:n(3523).Z,alt:"Diagram: Standard Error Options",width:"1024px"}),(0,i.kt)("p",null,"Now this might be the ",(0,i.kt)("strong",{parentName:"p"},"ah-ha!")," moment if you have done some shell scripting before - some of these obscure sequences like ",(0,i.kt)("inlineCode",{parentName:"p"},"2>&1")," might look familiar (even if it is just the thing you know you always have to Google to get right!)."),(0,i.kt)("p",null,"Let's take a quick look at some of these options."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"To Standard Output")),(0,i.kt)("p",null,"If we want to be able to pipe the error message to another command, we can use another redirection trick - we can redirect ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),"."),(0,i.kt)("p",null,"The characters ",(0,i.kt)("inlineCode",{parentName:"p"},"2>&1")," look really obscure - let's break it down:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Take the file with descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"2")," - which is ",(0,i.kt)("em",{parentName:"li"},"standard error")),(0,i.kt)("li",{parentName:"ul"},"Redirect it with the redirect symbol ",(0,i.kt)("inlineCode",{parentName:"li"},">")," - we saw this in the earlier section"),(0,i.kt)("li",{parentName:"ul"},"Redirect it into the file with descriptor (",(0,i.kt)("inlineCode",{parentName:"li"},"&"),") ",(0,i.kt)("inlineCode",{parentName:"li"},"1")," - which is ",(0,i.kt)("em",{parentName:"li"},"standard output"))),(0,i.kt)("p",null,"Remember, there are three 'magic' files each process has access to:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stdin"),", the standard input, which has the file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"0")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stdout"),", the standard output, which has the file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"1")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"stderr"),", the standard error, which has the file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"2"))),(0,i.kt)("p",null,"File descriptors are just numbers the operating system uses to keep track of files. When a program opens a 'normal' file, it'll get a new file descriptor. Here's a little example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"python < &1 | tr '[:lower:]' '[:upper:]'\nMKDIR: /HOME/DWMKERR/PLAYGROUND/NEW-FOLDER: FILE EXISTS\n")),(0,i.kt)("p",null,"Visually, what is happening is this:"),(0,i.kt)("img",{src:n(9387).Z,alt:"Diagram: stderr redirect",width:"1024px"}),(0,i.kt)("p",null,"If you can wrap your head around this, the other options we showed for ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," might start to make a little more sense."),(0,i.kt)("p",null,"A nice trick to remember the slightly obscure ampersand ",(0,i.kt)("inlineCode",{parentName:"p"},"&")," which references a file descriptor - if you were to write this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cat some-file-that-might-not-exist 2>1\n")),(0,i.kt)("p",null,"What would happen is that the shell would write ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," to a ",(0,i.kt)("em",{parentName:"p"},"new file")," with the name ",(0,i.kt)("inlineCode",{parentName:"p"},"1"),"! Why don't we need an ampersand ",(0,i.kt)("em",{parentName:"p"},"before")," the ",(0,i.kt)("inlineCode",{parentName:"p"},">")," symbol, only for the file descriptor afterwards? This is just because the shell only supports redirecting file descriptors, so an additional ampersand would be superfluous."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"To a File")),(0,i.kt)("p",null,"Before, we redirected to ",(0,i.kt)("inlineCode",{parentName:"p"},"&2"),", which is 'the file with descriptor ",(0,i.kt)("inlineCode",{parentName:"p"},"2"),". We can also use a similar trick to redirect to any arbitrary file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"mkdir ~/effective-shell/new-folder 2>./errors.txt\n")),(0,i.kt)("p",null,"This command just redirects all of the errors (remember, ",(0,i.kt)("inlineCode",{parentName:"p"},"2")," is ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr"),") to a file called ",(0,i.kt)("inlineCode",{parentName:"p"},"./errors.txt"),"."),(0,i.kt)("p",null,"This is quite a common trick - run the program, but log the errors to a file for later review."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"To Nowhere")),(0,i.kt)("p",null,"What if we just don't want to see the errors at all? Well there's a special file called ",(0,i.kt)("inlineCode",{parentName:"p"},"/dev/null")," which we can use for this. When we write to this file, the operating system just discards the input. In fact, it exists for just this kind of purpose!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"mkdir ~/effective-shell/new-folder 2>/dev/null\n")),(0,i.kt)("p",null,"This just redirects all errors to the black hole of ",(0,i.kt)("inlineCode",{parentName:"p"},"/dev/null")," - we won't see them on the screen or anywhere else. This is a common way to 'silence' errors",(0,i.kt)("sup",{parentName:"p",id:"fnref-3"},(0,i.kt)("a",{parentName:"sup",href:"#fn-3",className:"footnote-ref"},"3"))," in shell commands."),(0,i.kt)("p",null,"Notice how we're starting to see patterns? This is just redirection, the same tricks we saw for ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),", but we're explicitly redirecting ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," (file descriptor ",(0,i.kt)("inlineCode",{parentName:"p"},"2"),"). If we don't tell the shell ",(0,i.kt)("em",{parentName:"p"},"what")," to redirect, it assumes ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," by default."),(0,i.kt)("p",null,"So if we can redirect, can we append too?"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"Append")),(0,i.kt)("p",null,"Yes! Just like we did with ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),", there's nothing stopping us ",(0,i.kt)("em",{parentName:"p"},"appending")," to a file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"mkdir ~/effective-shell/new-folder 2>>./all-errors.log\n")),(0,i.kt)("p",null,"Just like before, we use ",(0,i.kt)("inlineCode",{parentName:"p"},">>")," which means ",(0,i.kt)("em",{parentName:"p"},"append")," (rather than ",(0,i.kt)("em",{parentName:"p"},"overwrite or create"),")."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"All to a File")),(0,i.kt)("p",null,"This is a really important subtlety. If you want to write ",(0,i.kt)("em",{parentName:"p"},"both")," ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," to a file, you might try this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"ls /usr/bin /nothing 2>&1 > all-output.txt\n")),(0,i.kt)("p",null,"If you run this command, you'll get ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," written to ",(0,i.kt)("inlineCode",{parentName:"p"},"all-output.txt"),", but the error message ",(0,i.kt)("inlineCode",{parentName:"p"},"cannot access '/nothing'")," is written to the screen, not the file. Why is this?"),(0,i.kt)("p",null,"Bash (and most bash-like shells) process redirections from ",(0,i.kt)("em",{parentName:"p"},"left to right"),", and when we redirect we ",(0,i.kt)("em",{parentName:"p"},"duplicate")," the source. So breaking this down:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"2>&1")," - duplicate file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"2")," (",(0,i.kt)("inlineCode",{parentName:"li"},"stderr"),") and write it to ",(0,i.kt)("inlineCode",{parentName:"li"},"1")," - which is ",(0,i.kt)("em",{parentName:"li"},"currently the terminal"),"!"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"> all-output.txt")," - duplicate file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"1")," (",(0,i.kt)("inlineCode",{parentName:"li"},"stdout"),") and write it to a file called ",(0,i.kt)("inlineCode",{parentName:"li"},"all-output.txt"))),(0,i.kt)("p",null,"To write ",(0,i.kt)("em",{parentName:"p"},"everything")," to the file, try do this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"ls /usr/bin /nothing > all-output.txt 2>&1\n")),(0,i.kt)("p",null,"This will work. Breaking it down:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Redirect ",(0,i.kt)("inlineCode",{parentName:"li"},"stdout")," to the file ",(0,i.kt)("inlineCode",{parentName:"li"},"all-output.txt")),(0,i.kt)("li",{parentName:"ul"},"Now redirect ",(0,i.kt)("inlineCode",{parentName:"li"},"stderr")," to ",(0,i.kt)("inlineCode",{parentName:"li"},"stdout")," - which by this point ",(0,i.kt)("em",{parentName:"li"},"has already been redirected to a file"))),(0,i.kt)("p",null,"This can be tough to remember so it's worth trying it out",(0,i.kt)("sup",{parentName:"p",id:"fnref-4"},(0,i.kt)("a",{parentName:"sup",href:"#fn-4",className:"footnote-ref"},"4")),". There are many variations you can play with and we'll see more as we go through the book."),(0,i.kt)("h2",{id:"one-last-trick---the-t-pipe"},"One Last Trick - The T Pipe"),(0,i.kt)("p",null,"This is a long chapter, but I can't talk about pipelines without briefly mentioning the T pipe. Check out this command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cat ~/effective-shell/text/simpsons-characters.txt | sort | tee sorted.txt | uniq | grep '^A'\n")),(0,i.kt)("p",null,"This command sorts the list of Simpsons characters, removes duplicates and filters down to ones which start with the letter ",(0,i.kt)("inlineCode",{parentName:"p"},"A"),". And it has the ",(0,i.kt)("inlineCode",{parentName:"p"},"tee")," command in the middle. What does this do?"),(0,i.kt)("p",null,"Well the ",(0,i.kt)("inlineCode",{parentName:"p"},"tee")," command is like a T-pipe in plumbing - it lets the stream of data go in two directions! The ",(0,i.kt)("inlineCode",{parentName:"p"},"sorted.txt")," file contains the sets of characters ",(0,i.kt)("em",{parentName:"p"},"after")," the sort operation, but before the unique and filter operation. Visually, it does this:"),(0,i.kt)("img",{src:n(1658).Z,alt:"Diagram: Tee",width:"1024px"}),(0,i.kt)("p",null,"As soon as you visualise a T-pipe it's easy to remember this useful command! You might use it in more complex pipelines or other scenarios to write things to a file which would otherwise go straight to another program or just the display."),(0,i.kt)("h2",{id:"thinking-in-pipelines"},"Thinking in Pipelines"),(0,i.kt)("p",null,"Once you get comfortable with pipelines, a whole world of possibilities open up."),(0,i.kt)("p",null,"Just the day before I wrote this chapter, I had to find out how many unique data points were in a data file, which also included empty lines and comments, it took less than a minute to quickly build this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cat data.dat | sort | uniq | grep -v '^#' | wc -l\n")),(0,i.kt)("p",null,"I didn't have to find a special program which does exactly what I needed",(0,i.kt)("sup",{parentName:"p",id:"fnref-5"},(0,i.kt)("a",{parentName:"sup",href:"#fn-5",className:"footnote-ref"},"5"))," - I just incrementally built a pipeline. Each section I added one by one, writing to the screen each time, until I had it working. The thought process was:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cat data.dat")," - OK, first I need to write out the file"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sort")," - now I can sort it, that'll put all the blank lines together"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"uniq")," - this'll remove all of those ",(0,i.kt)("em",{parentName:"li"},"duplicate")," blank lines, although it still leaves one blank one at the top!"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"grep -v '^#'")," - this should get rid of all the lines which start with ",(0,i.kt)("inlineCode",{parentName:"li"},"#")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"wc -l")," - this'll count the number of lines I'm left with")),(0,i.kt)("p",null,"Now there's probably better ways, and this has an oddity which is that if there are blank lines it'll remove all but one of them (although that would be quick to fix), but it gave me my quick and dirty answer in less than a minute."),(0,i.kt)("p",null,"Of course, as things get more complex you might want to build scripts, or use a programming language, or other methods, but this ",(0,i.kt)("em",{parentName:"p"},"Unix Philosophy")," (which we'll talk about more as we continue) of having lots of small, simple programs which we can chain together can be immensely powerful."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"We'll see pipelines again and again. The standard streams, redirection, pipelines and all of the tricks we've introduced in this chapter are fundamental not only to using the shell effectively, but really understanding how computer programs work."),(0,i.kt)("p",null,"Don't be worried if this feels like a lot to take in - we'll see more and more examples in later chapters which will help reinforce these concepts. If you find yourself struggling later you might want to quickly review this chapter, because we introduced a lot!"),(0,i.kt)("p",null,"In this chapter we looked at:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"How each program has access to three 'standard' streams - one for input, one for output and one for reporting errors"),(0,i.kt)("li",{parentName:"ul"},"The standard input stream is available as a file at ",(0,i.kt)("inlineCode",{parentName:"li"},"/dev/stdin"),", is often called ",(0,i.kt)("inlineCode",{parentName:"li"},"stdin")," in programming languages, and always has the special file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"0")),(0,i.kt)("li",{parentName:"ul"},"The standard output stream is available as a file at ",(0,i.kt)("inlineCode",{parentName:"li"},"/dev/stdout"),", is often called ",(0,i.kt)("inlineCode",{parentName:"li"},"stdout")," in programming languages, and always has the special file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"1")),(0,i.kt)("li",{parentName:"ul"},"The standard error stream is available as a file at ",(0,i.kt)("inlineCode",{parentName:"li"},"/dev/stderr"),", is often called ",(0,i.kt)("inlineCode",{parentName:"li"},"stderr")," in programming languages, and always has the special file descriptor ",(0,i.kt)("inlineCode",{parentName:"li"},"2")),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("inlineCode",{parentName:"li"},"Ctrl+D")," sequence means 'end of transmission' - we can use it to signal that we have completed putting our input into ",(0,i.kt)("inlineCode",{parentName:"li"},"stdin"),"..."),(0,i.kt)("li",{parentName:"ul"},"...but the ",(0,i.kt)("inlineCode",{parentName:"li"},"Ctrl+C")," sequence means 'interrupt' and is normally used to force a program to close"),(0,i.kt)("li",{parentName:"ul"},"We can ",(0,i.kt)("em",{parentName:"li"},"pipe")," the output of one program to the input of another with the pipe ",(0,i.kt)("inlineCode",{parentName:"li"},"|")," symbol"),(0,i.kt)("li",{parentName:"ul"},"We can ",(0,i.kt)("em",{parentName:"li"},"redirect")," a file to the standard input of a program with the ",(0,i.kt)("inlineCode",{parentName:"li"},"<")," operator"),(0,i.kt)("li",{parentName:"ul"},"We can ",(0,i.kt)("em",{parentName:"li"},"redirect")," the standard output of a program to create or overwrite a file with the ",(0,i.kt)("inlineCode",{parentName:"li"},">")," operator"),(0,i.kt)("li",{parentName:"ul"},"We can ",(0,i.kt)("em",{parentName:"li"},"redirect")," the standard output of a program to create or append to a file with the ",(0,i.kt)("inlineCode",{parentName:"li"},">>")," operator"),(0,i.kt)("li",{parentName:"ul"},"We can redirect the standard error of a program to its standard output with ",(0,i.kt)("inlineCode",{parentName:"li"},"2>&1")),(0,i.kt)("li",{parentName:"ul"},"We can redirect the standard error of a program to another file (such as the 'null' file) with ",(0,i.kt)("inlineCode",{parentName:"li"},"2>/dev/null")),(0,i.kt)("li",{parentName:"ul"},"We can redirect the standard error of a program to create or append to a file, just like with standard output, using the ",(0,i.kt)("inlineCode",{parentName:"li"},">>")," operator")),(0,i.kt)("p",null,"We also briefly saw some commands:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sort")," sorts text"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sed")," can replace content in text"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tr")," can replace parts of text"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"wc")," can count words or lines of text"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"tee")," takes the input stream and sends it straight to the output, but also to a file (like a T-pipe in plumbing)"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"grep")," can filter lines")),(0,i.kt)("p",null,"These programs can do a lot more and are workhorses we'll see in more detail through the book."),(0,i.kt)("p",null,"There are a few chapters which are planned to come later which go into detail on some of the concepts we only briefly touched on:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Writing Good Programs - How to write programs which use ",(0,i.kt)("inlineCode",{parentName:"li"},"stdin"),", ",(0,i.kt)("inlineCode",{parentName:"li"},"stdout")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"stderr")," sensibly"),(0,i.kt)("li",{parentName:"ul"},"The Unix Philosophy - Why we have so many small simple programs which we can pipe together"),(0,i.kt)("li",{parentName:"ul"},"Streams in Detail - How streams like ",(0,i.kt)("inlineCode",{parentName:"li"},"stdin")," actually work, especially with things like line endings, command sequences like ",(0,i.kt)("inlineCode",{parentName:"li"},"^D")," and so on"),(0,i.kt)("li",{parentName:"ul"},"Signals - A little more on Signals (such as ",(0,i.kt)("inlineCode",{parentName:"li"},"^C")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"^D"),")")),(0,i.kt)("p",null,"When these chapters are published I'll update the links here. If you want to be updated when new chapters are published, you can ",(0,i.kt)("a",{parentName:"p",href:"https://effective-shell.com"},"Join the Mailing Lits on the Homepage"),"."),(0,i.kt)("div",{className:"footnotes"},(0,i.kt)("hr",{parentName:"div"}),(0,i.kt)("ol",{parentName:"div"},(0,i.kt)("li",{parentName:"ol",id:"fn-1"},"Technically there is another layer here, which is the ",(0,i.kt)("inlineCode",{parentName:"li"},"tty"),". You can see this by running ",(0,i.kt)("inlineCode",{parentName:"li"},"tty")," in the shell. We'll talk more about this in the ",(0,i.kt)("a",{parentName:"li",href:"/part-2-core-skills/what-is-a-shell"},"Interlude - What is a Shell")," section.",(0,i.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-2"},"Check ",(0,i.kt)("a",{parentName:"li",href:"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/"},"Chapter 4 - Becoming a Clipboard Gymnast")," for how to do this on a Linux or Windows machine.",(0,i.kt)("a",{parentName:"li",href:"#fnref-2",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-3"},"Although always use tricks like this with caution! If we had a ",(0,i.kt)("em",{parentName:"li"},"different")," error, perhaps one we really do want to know about, we would lose the message in this case.",(0,i.kt)("a",{parentName:"li",href:"#fnref-3",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-4"},"There is a very detailed explanation of this behaviour at ",(0,i.kt)("a",{parentName:"li",href:"https://linuxnewbieguide.org/21-and-understanding-other-shell-scripts-idioms/"},"https://linuxnewbieguide.org/21-and-understanding-other-shell-scripts-idioms/"),".",(0,i.kt)("a",{parentName:"li",href:"#fnref-4",className:"footnote-backref"},"\u21a9")),(0,i.kt)("li",{parentName:"ol",id:"fn-5"},"With the correct options, ",(0,i.kt)("inlineCode",{parentName:"li"},"sed")," could likely do this in a single operation, but I'd probably spend a lot longer Googling the right options for it!",(0,i.kt)("a",{parentName:"li",href:"#fnref-5",className:"footnote-backref"},"\u21a9")))))}d.isMDXComponent=!0},586:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-cat-sort-uniq-pipeline-8c8d76566f351b4b9b900dde52af86b3.png"},3657:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-input-examples-e014dd4998bee0b50a94849ad55b01ce.png"},7007:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-input-program-output-fb71da951178e72bf4504bd292743d1c.png"},4146:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-output-examples-27e30c4a4036b2591e10e8c4fca7dc73.png"},4956:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-shell-keyboard-terminal-0475940cdf40bbcc8a329c090aa9e76a.png"},1023:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-sort-040db6e92c70e60612c9f711e81c9612.png"},3523:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-stderr-options-a2cde4aa6177249c25dd9e5c0c62667a.png"},9387:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-stderr-redirect-c7d8fe2d93a8cdb248924cc13027b59e.png"},930:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-stderr-d0845508087975a7d58ebac63e3a8cd5.png"},6699:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-stdin-stdout-stderr-702e578630d8d39c813d7d88c270c339.png"},1658:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/diagram-tee-6ad6dadcfa804f75f96b36807ffd688b.png"},1377:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/screenshot-less-65730c042d9a734b6f50168219fb70f7.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/935f2afb.417fef32.js b/pr-preview/pr-346/assets/js/935f2afb.417fef32.js new file mode 100644 index 00000000..32750512 --- /dev/null +++ b/pr-preview/pr-346/assets/js/935f2afb.417fef32.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"sidebar":[{"type":"link","label":"Introduction","href":"/","docId":"index"},{"type":"category","label":"Transitioning to the Shell","items":[{"type":"link","label":"Getting Started","href":"/part-1-transitioning-to-the-shell/getting-started/","docId":"transitioning-to-the-shell/getting-started/index"},{"type":"link","label":"Navigating Your System","href":"/part-1-transitioning-to-the-shell/navigating-your-system/","docId":"transitioning-to-the-shell/navigating-your-system/index"},{"type":"link","label":"Managing Your Files","href":"/part-1-transitioning-to-the-shell/managing-your-files/","docId":"transitioning-to-the-shell/managing-your-files/index"},{"type":"link","label":"Becoming a Clipboard Gymnast","href":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","docId":"transitioning-to-the-shell/clipboard-gymnastics/index"},{"type":"link","label":"Getting Help","href":"/part-1-transitioning-to-the-shell/getting-help/","docId":"transitioning-to-the-shell/getting-help/index"},{"type":"link","label":"The Renaissance of the Shell","href":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","docId":"transitioning-to-the-shell/the-renaissance-of-the-shell/index"}],"collapsed":true,"collapsible":true,"href":"/part-1-transitioning-to-the-shell/"},{"type":"category","label":"Core Skills","items":[{"type":"link","label":"Thinking in Pipelines","href":"/part-2-core-skills/thinking-in-pipelines/","docId":"core-skills/thinking-in-pipelines/index"},{"type":"link","label":"Fly on the Command Line","href":"/part-2-core-skills/fly-on-the-command-line","docId":"core-skills/fly-on-the-command-line/index"},{"type":"link","label":"Job Control","href":"/part-2-core-skills/job-control","docId":"core-skills/job-control/index"},{"type":"link","label":"Understanding Commands","href":"/part-2-core-skills/understanding-commands","docId":"core-skills/understanding-commands/index"},{"type":"link","label":"Finding Files","href":"/part-2-core-skills/finding-files","docId":"core-skills/finding-files/index"},{"type":"link","label":"What is a Shell?","href":"/part-2-core-skills/what-is-a-shell","docId":"core-skills/what-is-a-shell/index"}],"collapsed":true,"collapsible":true,"href":"/part-2-core-skill/"},{"type":"category","label":"Manipulating Text and Streams","items":[{"type":"link","label":"Regex Essentials","href":"/part-3-manipulating-text/regex-essentials/","docId":"manipulating-text/regex-essentials/index"},{"type":"link","label":"Get to Grips with Grep","href":"/part-3-manipulating-text/get-to-grips-with-grep/","docId":"manipulating-text/get-to-grips-with-grep/index"},{"type":"link","label":"Slice and Dice Text","href":"/part-3-manipulating-text/slice-and-dice-text/","docId":"manipulating-text/slice-and-dice-text/index"},{"type":"link","label":"Advanced Text Manipulation","href":"/part-3-manipulating-text/advanced-text-manipulation/","docId":"manipulating-text/advanced-text-manipulation/index"},{"type":"link","label":"Build Commands on the Fly","href":"/part-3-manipulating-text/build-commands-on-the-fly/","docId":"manipulating-text/build-commands-on-the-fly/index"}],"collapsed":true,"collapsible":true,"href":"/part-3-manipulating-text/"},{"type":"category","label":"Shell Scripting","items":[{"type":"link","label":"Shell Script Essentials","href":"/part-3-manipulating-text/shell-script-essentials/","docId":"shell-scripting/shell-script-essentials/index"},{"type":"link","label":"Variables, Reading Input, and Mathematics","href":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","docId":"shell-scripting/variables-reading-input-and-mathematics/index"},{"type":"link","label":"Mastering Conditional Logic","href":"/part-4-shell-scripting/mastering-conditional-logic","docId":"shell-scripting/mastering-conditional-logic/index"},{"type":"link","label":"Loops and working with Files and Folders","href":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","docId":"shell-scripting/loops-and-working-with-files-and-folders/index"},{"type":"link","label":"Functions, Parameters and Error Handling","href":"/part-4-shell-scripting/functions-parameters-and-error-handling","docId":"shell-scripting/functions-parameters-and-error-handling/index"},{"type":"link","label":"Useful Patterns for Shell Scripts","href":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","docId":"shell-scripting/useful-patterns-for-shell-scripts/index"}],"collapsed":true,"collapsible":true,"href":"/part-4-shell-scripting/"},{"type":"category","label":"Building Your Toolkit","items":[{"type":"link","label":"Configuring the Shell","href":"/part-5-building-your-toolkit/configuring-the-shell","docId":"building-your-toolkit/configuring-the-shell/index"},{"type":"link","label":"Customising Your Command Prompt","href":"/part-5-building-your-toolkit/customising-your-command-prompt","docId":"building-your-toolkit/customising-your-command-prompt/index"},{"type":"link","label":"Managing your Dotfiles","href":"/part-5-building-your-toolkit/managing-your-dotfiles","docId":"building-your-toolkit/managing-your-dotfiles/index"},{"type":"link","label":"Controlling Changes with Git","href":"/part-5-building-your-toolkit/controlling-changes-with-git","docId":"building-your-toolkit/controlling-changes-with-git/index"},{"type":"link","label":"Managing Remote Git Repositories and Sharing Your Dotfiles","href":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","docId":"building-your-toolkit/managing-remote-git-repositories/index"}],"collapsed":true,"collapsible":true,"href":"/part-5-building-your-toolkit/"},{"type":"category","label":"Advanced Techniques","items":[{"type":"link","label":"Understanding Shell Expansion","href":"/part-6-advanced-techniques/understanding-shell-expansion/","docId":"advanced-techniques/understanding-shell-expansion/index"},{"type":"link","label":"How to Avoid Scripting - A Dictionary Lookup in Python","href":"/part-6-advanced-techniques/how-to-avoid-scripting/","docId":"advanced-techniques/how-to-avoid-scripting/index"},{"type":"link","label":"The Secure Shell","href":"/part-6-advanced-techniques/the-secure-shell/","docId":"advanced-techniques/the-secure-shell/index"},{"type":"link","label":"A Vim Crash Course","href":"/part-6-advanced-techniques/a-vim-crash-course/","docId":"advanced-techniques/a-vim-crash-course/index"},{"type":"link","label":"Master the Multiplexer","href":"/part-6-advanced-techniques/master-the-multiplexer/","docId":"advanced-techniques/master-the-multiplexer/index"}],"collapsed":true,"collapsible":true,"href":"/part-6-advanced-techniques/"},{"type":"category","label":"Appendices","items":[{"type":"link","label":"Installing the Samples","href":"/xx-appendices/installing-samples/","docId":"xx-appendices/installing-samples/index"},{"type":"link","label":"Recommended Reading","href":"/xx-appendices/recommended-reading/","docId":"xx-appendices/recommended-reading/index"},{"type":"link","label":"Thanks","href":"/appendices/thanks/","docId":"xx-appendices/thanks"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Developer Guide","items":[{"type":"link","label":"Components","href":"/zz-developer-guide/components","docId":"zz-developer-guide/components"},{"type":"link","label":"Images and Diagrams","href":"/zz-developer-guide/images-and-diagrams","docId":"zz-developer-guide/images-and-diagrams"},{"type":"link","label":"Markdown","href":"/zz-developer-guide/markdown-guide","docId":"zz-developer-guide/markdown-guide"},{"type":"link","label":"Recording Terminal Sessions","href":"/zz-developer-guide/recording-terminal-sessions","docId":"zz-developer-guide/recording-terminal-sessions"}],"collapsed":true,"collapsible":true}]},"docs":{"advanced-techniques/a-vim-crash-course/index":{"id":"advanced-techniques/a-vim-crash-course/index","title":"A Vim Crash Course","description":"In this chapter we\'re going to introduce the popular Terminal Editor Vim. A Terminal Editor is a text editor that you can open directly in your terminal, normally from the shell. Many users find these editors hard to use, and some of them have a reputation for being highly complex.","sidebar":"sidebar"},"advanced-techniques/how-to-avoid-scripting/index":{"id":"advanced-techniques/how-to-avoid-scripting/index","title":"How to Avoid Scripting - A Dictionary Lookup in Python","description":"This book is about being an effective shell user. This is not a book about shell scripting. And sometimes being an effective shell user means knowing when to not use a shell script to solve a problem, but to use an alternative tool such as a programming language.","sidebar":"sidebar"},"advanced-techniques/index":{"id":"advanced-techniques/index","title":"Part 6 - Advanced Techniques","description":"At this stage you are well on your way to shell mastery. Each of the chapters in this section goes introduces either an advanced technique to help you be more effective in the shell, or goes considerably deeper into one of the topics that we have already covered so that you can really understand the fundamentals.","sidebar":"sidebar"},"advanced-techniques/master-the-multiplexer/index":{"id":"advanced-techniques/master-the-multiplexer/index","title":"Master the Multiplexer","description":"If you are regularly using a shell, then learning how to use a terminal multiplexer like screen or tmux can greatly improve your productivity. In this chapter we\'ll see how what a terminal multiplexer is, what it is used for, learn how to perform some common tasks and configure a multiplexer to make it even more useful.","sidebar":"sidebar"},"advanced-techniques/the-secure-shell/index":{"id":"advanced-techniques/the-secure-shell/index","title":"The Secure Shell","description":"So far we have been using the shell to operate on our local machine. We can use Secure Shell Protocol, or SSH, to open a secure network connection to a remote machine and use the shell to work on that machine.","sidebar":"sidebar"},"advanced-techniques/understanding-shell-expansion/index":{"id":"advanced-techniques/understanding-shell-expansion/index","title":"Understanding Shell Expansion","description":"When you are working with the shell there are a number of techniques that you can use to take simple commands and make more useful. For example, if we wanted to create three files, we could run touch file1 file2 file3, or we could use \'brace expansion\' and just run touch file{1..3}. Another example would be to delete all files that have names that start with file - like this rm file*, this is wildcard expansion.","sidebar":"sidebar"},"building-your-toolkit/configuring-the-shell/index":{"id":"building-your-toolkit/configuring-the-shell/index","title":"Configuring the Shell","description":"There are a number of different ways to configure your shell. In this chapter we\'ll take a look at the different configuration files for the shell and how they work, and how you can change your shell configuration with options.","sidebar":"sidebar"},"building-your-toolkit/controlling-changes-with-git/index":{"id":"building-your-toolkit/controlling-changes-with-git/index","title":"Controlling Changes with Git","description":"Git is an extremely popular version control tool. You can use Git to track changes to text, code, or any type of files you might be working with. Many popular projects use Git as a tool to manage changes, allow others to contribute and collaborate, and to publish their projects.","sidebar":"sidebar"},"building-your-toolkit/customising-your-command-prompt/index":{"id":"building-your-toolkit/customising-your-command-prompt/index","title":"Customising Your Command Prompt","description":"The shell has a large number of options available that you can use to customise the command prompt - the text shown in front of your cursor as you type commands. In this chapter we will look at how you can change the command prompt to show the information that you would like to see.","sidebar":"sidebar"},"building-your-toolkit/index":{"id":"building-your-toolkit/index","title":"Part 5 - Building Your Toolkit","description":"We\'ve now seen many of the core features and some of the more advanced capabilities of the shell. In this part of the book are are going to look at how the shell is configured, different ways a shell can run, and effective ways to manage your shell configuration.","sidebar":"sidebar"},"building-your-toolkit/managing-remote-git-repositories/index":{"id":"building-your-toolkit/managing-remote-git-repositories/index","title":"Managing Remote Git Repositories and Sharing Your Dotfiles","description":"In this chapter we\'ll take a look at how to take a local Git repository, like the one created in the previous chapter, and upload it to a remote repository.","sidebar":"sidebar"},"building-your-toolkit/managing-your-dotfiles/index":{"id":"building-your-toolkit/managing-your-dotfiles/index","title":"Managing your Dotfiles","description":"As you build up more and more customisations for your shell and environment, it becomes important to find a way to manage these changes and files effectively.","sidebar":"sidebar"},"core-skills/finding-files/index":{"id":"core-skills/finding-files/index","title":"Finding Files","description":"Searching through a system to find files or folders can be complex and time consuming, even with a graphical user interface. In this chapter we\'ll look at how to use the shell to search for files and folders and some quick ways to accomplish common tasks.","sidebar":"sidebar"},"core-skills/fly-on-the-command-line/index":{"id":"core-skills/fly-on-the-command-line/index","title":"Fly on the Command Line","description":"This is my favourite chapter of the book! The tricks I picked up on rapidly moving around in the command line have saved me an enormous amount of time over the years.","sidebar":"sidebar"},"core-skills/index":{"id":"core-skills/index","title":"Part 2 - Core Skills","description":"In the first part of this book we look at the shell from the perspective of someone who is familiar with a graphical user interface. We studied how to transition from a GUI to the shell, introducing the \'shell way\' of performing tasks which you might have previously performed using a GUI.","sidebar":"sidebar"},"core-skills/job-control/index":{"id":"core-skills/job-control/index","title":"Job Control","description":"Job control is a feature of most shells which can often be somewhat complicated to work with. However, knowing the basics can help prevent you from getting yourself into a tangle and can from time to time make certain tasks a little easier.","sidebar":"sidebar"},"core-skills/thinking-in-pipelines/index":{"id":"core-skills/thinking-in-pipelines/index","title":"Thinking in Pipelines","description":"Understanding the concept of pipelines in the shell, as well as how input and output work for command line programs is critical to be able to use the shell effectively.","sidebar":"sidebar"},"core-skills/understanding-commands/index":{"id":"core-skills/understanding-commands/index","title":"Understanding Commands","description":"In this chapter, we\'ll take a look at the various different types of shell commands that exist and how this can affect your work. Commands are far more subtle than you might think and in this chapter we\'ll look at some of the nuances of commands and the practical consequences for your work.","sidebar":"sidebar"},"core-skills/what-is-a-shell/hack-on":{"id":"core-skills/what-is-a-shell/hack-on","title":"hack-on","description":"Hack On! See those system calls!"},"core-skills/what-is-a-shell/index":{"id":"core-skills/what-is-a-shell/index","title":"What is a Shell?","description":"This is the second of the \\"interludes\\" which end each section of the book. These interludes give flavour, concepts, context and the history of some of the concepts we\'re dealing with. These interlude are not essential to mastering the skills of the shell, but you might find them interesting.","sidebar":"sidebar"},"index":{"id":"index","title":"Effective Shell","description":"Investing just a few hours in your ability to use text based interfaces for your computer can have an enormous impact on your productivity. It can also make your work more fun, allowing you to maintain that creative \'flow\' state that can make technology so exciting.","sidebar":"sidebar"},"manipulating-text/advanced-text-manipulation/index":{"id":"manipulating-text/advanced-text-manipulation/index","title":"Advanced Text Manipulation","description":"In Chapter 15 we introduced some simple commands to work with text - specifically head, tail, tr and cut. Now we are going to introduce sed the Stream Editor command.","sidebar":"sidebar"},"manipulating-text/build-commands-on-the-fly/index":{"id":"manipulating-text/build-commands-on-the-fly/index","title":"Build Commands on the Fly","description":"In the earlier chapters of this part of the book we\'ve seen a number of ways to manipulate text. Now we\'re going to introduce the xargs command and show how to use our text manipulation skills to dynamically build complex commands on the fly.","sidebar":"sidebar"},"manipulating-text/get-to-grips-with-grep/index":{"id":"manipulating-text/get-to-grips-with-grep/index","title":"Get to Grips with Grep","description":"The grep tool is a real workhorse for shell users - once you\'ve learned how to use it you will find yourself using it again and again. In this chapter we\'ll see how you can use grep for common tasks, and how to use it in combination with other tools.","sidebar":"sidebar"},"manipulating-text/index":{"id":"manipulating-text/index","title":"Part 3 - Manipulating Text and Streams","description":"A key part of how Linux and Unix systems work is that almost everything is represented as a text file in the system. Almost everything can be configured with simple text file.","sidebar":"sidebar"},"manipulating-text/regex-essentials/index":{"id":"manipulating-text/regex-essentials/index","title":"Regex Essentials","description":"Many of the tools we\'re going to introduce in this part of the book support regular expressions or regexes - a sophisticated language which allows us to describe different patterns of text.","sidebar":"sidebar"},"manipulating-text/slice-and-dice-text/index":{"id":"manipulating-text/slice-and-dice-text/index","title":"Slice and Dice Text","description":"In Chapter 14 we looked at how to use the grep command to search through text and filter text. In this chapter we\'re going to look at some of the basic commands which we can use to manipulate text. There are a whole raft of commands and options available.","sidebar":"sidebar"},"shell-scripting/functions-parameters-and-error-handling/index":{"id":"shell-scripting/functions-parameters-and-error-handling/index","title":"Functions, Parameters and Error Handling","description":"The shell allows you to create functions - a set of commands that you can call at any time. In this chapter we\'ll see how to create functions and how function parameters and script parameters are handled. We will also look at status codes for commands and scripts and error handling.","sidebar":"sidebar"},"shell-scripting/index":{"id":"shell-scripting/index","title":"Part 4 - Shell Scripting","description":"Now that we\'ve looked at the core skills will help you be more effective, as well as the fundamentals of managing text, it is time to look at shell scripting.","sidebar":"sidebar"},"shell-scripting/loops-and-working-with-files-and-folders/index":{"id":"shell-scripting/loops-and-working-with-files-and-folders/index","title":"Loops and working with Files and Folders","description":"Loops allow us to perform a set of operations over multiple items, such as a set of files or folders or the results of a command. In this chapter we\'ll look at loops and how to operate on many files and folders.","sidebar":"sidebar"},"shell-scripting/mastering-conditional-logic/index":{"id":"shell-scripting/mastering-conditional-logic/index","title":"Mastering Conditional Logic","description":"In this chapter we\'ll introduce \'conditional logic\', a set of powerful features that allow us to run operations only when certain conditions are met. We\'ll look at the if statement and the different ways we can evaluate conditions. We\'ll also look at more sophisticated conditional constructs such as the case statement and the select statement, and how to \'chain\' commands based on conditions.","sidebar":"sidebar"},"shell-scripting/shell-script-essentials/index":{"id":"shell-scripting/shell-script-essentials/index","title":"Shell Script Essentials","description":"In this chapter we\'re going to look at how to write shell scripts and the different ways we can execute them. We\'ll look at how shell script files should be structured and how to use \'shebangs\' to define how a shell script will run.","sidebar":"sidebar"},"shell-scripting/useful-patterns-for-shell-scripts/index":{"id":"shell-scripting/useful-patterns-for-shell-scripts/index","title":"Useful Patterns for Shell Scripts","description":"To close this the section on shell scripting we\'re going to look at some common patterns you will see in shell scripts. These are an assortment of techniques you may find useful when building your scripts - you may come across them in scripts others have written as well.","sidebar":"sidebar"},"shell-scripting/variables-reading-input-and-mathematics/index":{"id":"shell-scripting/variables-reading-input-and-mathematics/index","title":"Variables, Reading Input, and Mathematics","description":"We\'ve seen variables a few times in our journey so far. In this chapter we\'ll look at variables in a bit more detail. We\'ll then see how to read input from the user and also look at how to perform basic mathematical operations in the shell.","sidebar":"sidebar"},"transitioning-to-the-shell/clipboard-gymnastics/index":{"id":"transitioning-to-the-shell/clipboard-gymnastics/index","title":"Becoming a Clipboard Gymnast","description":"For those who are new to the shell, we\'ve covered a lot. In this chapter we\'ll slow down the pace of new commands a bit and instead focus on a core skill which you will already be familiar with from Graphical User Interfaces - using the clipboard.","sidebar":"sidebar"},"transitioning-to-the-shell/getting-help/index":{"id":"transitioning-to-the-shell/getting-help/index","title":"Getting Help","description":"In the earlier chapters I\'ve introduced quite a few commands. Having to remember all of these commands and their parameters would be very hard. Fortunately there are built-in capabilities in the shell to help.","sidebar":"sidebar"},"transitioning-to-the-shell/getting-started/index":{"id":"transitioning-to-the-shell/getting-started/index","title":"Getting Started","description":"This section is for those who are completely new to this topic. In this section we\'ll introduce just what the shell is, who this book is useful for, and what you can expect to learn.","sidebar":"sidebar"},"transitioning-to-the-shell/index":{"id":"transitioning-to-the-shell/index","title":"Part 1 - Transitioning to the Shell","description":"These are the key skills which everyone should know. Without them, you might struggle to perform certain tasks at all. Experienced users can probably skip this section, or just review the summary. But if you are new to the shell, this is the best place to start! This section focuses on helping you quickly get up to speed with how to perform the same kind of tasks you might have performed in a GUI (Graphical User Interface) with the shell.","sidebar":"sidebar"},"transitioning-to-the-shell/managing-your-files/index":{"id":"transitioning-to-the-shell/managing-your-files/index","title":"Managing Your Files","description":"Downloading, unzipping, copying, moving, renaming and deleting files in a graphical user interface is normally fairly intuitive. Now we\'ll learn how to perform the same operations in a shell. Once you can organise your files, you are well on your way to being able to use the shell more effectively for day to day tasks.","sidebar":"sidebar"},"transitioning-to-the-shell/navigating-your-system/index":{"id":"transitioning-to-the-shell/navigating-your-system/index","title":"Navigating Your System","description":"Switching from a graphical user interface to the shell can take some getting used to. First we\'ll take a look at how to navigate your system using the shell, and get information on files and folders in the system.","sidebar":"sidebar"},"transitioning-to-the-shell/the-renaissance-of-the-shell/index":{"id":"transitioning-to-the-shell/the-renaissance-of-the-shell/index","title":"The Renaissance of the Shell","description":"This is the first of the \\"interludes\\" which end each section of the book. They don\'t teach any specific skills but instead give a little flavour and background about the world of the shell, Linux and modern computing.","sidebar":"sidebar"},"work-in-progress":{"id":"work-in-progress","title":"Work in Progress!","description":"If you have landed here, then most likely you have clicked a link to a chapter which has not yet been completed. Sorry about that!"},"xx-appendices/essential-manpages":{"id":"xx-appendices/essential-manpages","title":"essential-manpages","description":"The Most Important Manpages"},"xx-appendices/exercises":{"id":"xx-appendices/exercises","title":"Good Scripts to write as exercises","description":"- recent - a better version of history, which deduplicates and sorts based on the most commonly used items"},"xx-appendices/index-of-commands":{"id":"xx-appendices/index-of-commands","title":"index-of-commands","description":"| Command | Description |"},"xx-appendices/installing-samples/index":{"id":"xx-appendices/installing-samples/index","title":"Installing the Samples","description":"How to install the Effective Shell Samples","sidebar":"sidebar"},"xx-appendices/posix":{"id":"xx-appendices/posix","title":"Posix","description":"Posix Shells"},"xx-appendices/recommended-reading/index":{"id":"xx-appendices/recommended-reading/index","title":"Recommended Reading","description":"Great books and articles you might enjoy if you like this book!","sidebar":"sidebar"},"xx-appendices/shell-parameter-expansion":{"id":"xx-appendices/shell-parameter-expansion","title":"shell-parameter-expansion","description":"| Variable | Description |"},"xx-appendices/thanks":{"id":"xx-appendices/thanks","title":"Thanks","description":"Many people have contributed their time and support to help build this book. Thank-you to everyone who has helped on this journey!","sidebar":"sidebar"},"xx-appendices/the-future":{"id":"xx-appendices/the-future","title":"the-future","description":"The shell, in particular the Bourne-Again Shell has been popular for many years. But what does the future hold? With the advent of the Linux Subsystem for Windows, new shells like Nushell, and the latest version of MacOS switching from Bash to Z-Shell, we finish off by looking at some of the trends which might shape how we use shells in the future."},"zz-developer-guide/components":{"id":"zz-developer-guide/components","title":"Components","description":"This page shows the custom components that are available for Effective Shell. These are primarily used to improve the reading experience for code samples.","sidebar":"sidebar"},"zz-developer-guide/images-and-diagrams":{"id":"zz-developer-guide/images-and-diagrams","title":"Images and Diagrams","description":"Images","sidebar":"sidebar"},"zz-developer-guide/markdown-guide":{"id":"zz-developer-guide/markdown-guide","title":"Markdown","description":"The contents of this book are primarily written in MDX. Some key concepts to be aware of:","sidebar":"sidebar"},"zz-developer-guide/recording-terminal-sessions":{"id":"zz-developer-guide/recording-terminal-sessions","title":"Recording Terminal Sessions","description":"There are a couple of techniques that can be useful to record terminal sessions. The first is the asciinema too. The second is the script and scriptreplay commands, which can be used to record the actual keystrokes typed and then replay them.","sidebar":"sidebar"}}}')}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/93d63416.3c403bd8.js b/pr-preview/pr-346/assets/js/93d63416.3c403bd8.js new file mode 100644 index 00000000..2893bb12 --- /dev/null +++ b/pr-preview/pr-346/assets/js/93d63416.3c403bd8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[3361],{3905:(t,e,l)=>{l.d(e,{Zo:()=>o,kt:()=>m});var r=l(7294);function i(t,e,l){return e in t?Object.defineProperty(t,e,{value:l,enumerable:!0,configurable:!0,writable:!0}):t[e]=l,t}function s(t,e){var l=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),l.push.apply(l,r)}return l}function n(t){for(var e=1;e =0||(i[l]=t[l]);return i}(t,e);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);for(r=0;r =0||Object.prototype.propertyIsEnumerable.call(t,l)&&(i[l]=t[l])}return i}var u=r.createContext({}),h=function(t){var e=r.useContext(u),l=e;return t&&(l="function"==typeof t?t(e):n(n({},e),t)),l},o=function(t){var e=h(t.components);return r.createElement(u.Provider,{value:e},t.children)},c="mdxType",k={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},p=r.forwardRef((function(t,e){var l=t.components,i=t.mdxType,s=t.originalType,u=t.parentName,o=a(t,["components","mdxType","originalType","parentName"]),c=h(l),p=i,m=c["".concat(u,".").concat(p)]||c[p]||k[p]||s;return l?r.createElement(m,n(n({ref:e},o),{},{components:l})):r.createElement(m,n({ref:e},o))}));function m(t,e){var l=arguments,i=e&&e.mdxType;if("string"==typeof t||i){var s=l.length,n=new Array(s);n[0]=p;var a={};for(var u in e)hasOwnProperty.call(e,u)&&(a[u]=e[u]);a.originalType=t,a[c]="string"==typeof t?t:i,n[1]=a;for(var h=2;h {l.r(e),l.d(e,{assets:()=>u,contentTitle:()=>n,default:()=>c,frontMatter:()=>s,metadata:()=>a,toc:()=>h});var r=l(7462),i=(l(7294),l(3905));const s={title:"Thanks",slug:"/appendices/thanks/"},n=void 0,a={unversionedId:"xx-appendices/thanks",id:"xx-appendices/thanks",title:"Thanks",description:"Many people have contributed their time and support to help build this book. Thank-you to everyone who has helped on this journey!",source:"@site/docs/xx-appendices/thanks.md",sourceDirName:"xx-appendices",slug:"/appendices/thanks/",permalink:"/appendices/thanks/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/xx-appendices/thanks.md",tags:[],version:"current",frontMatter:{title:"Thanks",slug:"/appendices/thanks/"},sidebar:"sidebar",previous:{title:"Recommended Reading",permalink:"/xx-appendices/recommended-reading/"},next:{title:"Components",permalink:"/zz-developer-guide/components"}},u={},h=[],o={toc:h};function c(t){let{components:e,...l}=t;return(0,i.kt)("wrapper",(0,r.Z)({},o,l,{components:e,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Many people have contributed their time and support to help build this book. Thank-you to everyone who has helped on this journey!"),(0,i.kt)("table",null,(0,i.kt)("tbody",null,(0,i.kt)("tr",null,(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/xiaoyou-elsie-jiang"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/101381124?v=4?s=100",width:"100px;",alt:"Xiaoyou Elsie Jiang"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Xiaoyou Elsie Jiang"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=xiaoyou-elsie-jiang",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Axiaoyou-elsie-jiang",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"http://linkedin.com/in/tbueschel"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/13087421?v=4?s=100",width:"100px;",alt:"Tobias B\xfcschel"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Tobias B\xfcschel"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Atobiasbueschel",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"http://foostack.ai"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/15166953?v=4?s=100",width:"100px;",alt:"Doug Foo"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Doug Foo"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=dougfoo",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Adougfoo",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/skokaina"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/2756985?v=4?s=100",width:"100px;",alt:"Sallah Kokaina"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Sallah Kokaina"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=skokaina",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Askokaina",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"http://www.fetch-template.com"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/26925206?v=4?s=100",width:"100px;",alt:"samhinton88"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"samhinton88"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=samhinton88",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Asamhinton88",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://www.alexvinall.com"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/5629393?v=4?s=100",width:"100px;",alt:"Alex Vinall"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Alex Vinall"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=alexvinall",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Aalexvinall",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/JosephFKnight"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/45918817?v=4?s=100",width:"100px;",alt:"Joseph Knight"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Joseph Knight"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=JosephFKnight",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3AJosephFKnight",title:"Reviewed Pull Requests"},"\ud83d\udc40"))),(0,i.kt)("tr",null,(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"http://bit.ly/doug-todd"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/53582591?v=4?s=100",width:"100px;",alt:"Doug Todd"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Doug Todd"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=Zambrella",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3AZambrella",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/jdhzzz"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/1476690?v=4?s=100",width:"100px;",alt:"jdhzzz"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"jdhzzz"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=jdhzzz",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Ajdhzzz",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/valankar"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/31250800?v=4?s=100",width:"100px;",alt:"valankar"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"valankar"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=valankar",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Avalankar",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/Denpeer"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/5969147?v=4?s=100",width:"100px;",alt:"Denpeer"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Denpeer"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3ADenpeer",title:"Reviewed Pull Requests"},"\ud83d\udc40")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=Denpeer",title:"Documentation"},"\ud83d\udcd6")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/mbogatzki"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/39946827?v=4?s=100",width:"100px;",alt:"Marek Bogatzki"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Marek Bogatzki"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=mbogatzki",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Ambogatzki",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/MichaelWarnecke"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/7615963?v=4?s=100",width:"100px;",alt:"MWarnecke"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"MWarnecke"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=MichaelWarnecke",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3AMichaelWarnecke",title:"Reviewed Pull Requests"},"\ud83d\udc40")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/issues?q=author%3AMichaelWarnecke",title:"Bug reports"},"\ud83d\udc1b")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://taxodium.ink/"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/30440218?v=4?s=100",width:"100px;",alt:"Spike"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Spike"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3ASpike-Leung",title:"Reviewed Pull Requests"},"\ud83d\udc40")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/issues?q=author%3ASpike-Leung",title:"Bug reports"},"\ud83d\udc1b"))),(0,i.kt)("tr",null,(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://nosarthur.github.io/"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/1400272?v=4?s=100",width:"100px;",alt:"Dong Zhou"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Dong Zhou"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Anosarthur",title:"Reviewed Pull Requests"},"\ud83d\udc40")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/issues?q=author%3Anosarthur",title:"Bug reports"},"\ud83d\udc1b")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=nosarthur",title:"Documentation"},"\ud83d\udcd6")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/drormaman"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/7041612?v=4?s=100",width:"100px;",alt:"Dror Maman"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Dror Maman"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/issues?q=author%3Adrormaman",title:"Bug reports"},"\ud83d\udc1b")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=drormaman",title:"Documentation"},"\ud83d\udcd6")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Adrormaman",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/saraid"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/40923?v=4?s=100",width:"100px;",alt:"Michael Chui"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Michael Chui"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Asaraid",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/nimid"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/4145121?v=4?s=100",width:"100px;",alt:"Saroj Sangphongamphai"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Saroj Sangphongamphai"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Animid",title:"Reviewed Pull Requests"},"\ud83d\udc40")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/linjielig"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/11633940?v=4?s=100",width:"100px;",alt:"Lee Li"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Lee Li"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Alinjielig",title:"Reviewed Pull Requests"},"\ud83d\udc40")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/issues?q=author%3Alinjielig",title:"Bug reports"},"\ud83d\udc1b")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/commits?author=linjielig",title:"Documentation"},"\ud83d\udcd6")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://github.com/leeli0"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/11633940?v=4?s=100",width:"100px;",alt:"Lee Li"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Lee Li"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/issues?q=author%3Aleeli0",title:"Bug reports"},"\ud83d\udc1b")),(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://stratus3d.com"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/1520926?v=4?s=100",width:"100px;",alt:"Trevor Brown"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Trevor Brown"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/issues?q=author%3AStratus3D",title:"Bug reports"},"\ud83d\udc1b")," ",(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3AStratus3D",title:"Reviewed Pull Requests"},"\ud83d\udc40"))),(0,i.kt)("tr",null,(0,i.kt)("td",{align:"center",valign:"top",width:"14.28%"},(0,i.kt)("a",{href:"https://twitter.com/pfrischmuth"},(0,i.kt)("img",{src:"https://avatars.githubusercontent.com/u/351542?v=4?s=100",width:"100px;",alt:"Philipp Frischmuth"}),(0,i.kt)("br",null),(0,i.kt)("sub",null,(0,i.kt)("b",null,"Philipp Frischmuth"))),(0,i.kt)("br",null),(0,i.kt)("a",{href:"https://github.com/dwmkerr/effective-shell/pulls?q=is%3Apr+reviewed-by%3Apfrischmuth",title:"Reviewed Pull Requests"},"\ud83d\udc40"))))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/9423fe57.8c4a381f.js b/pr-preview/pr-346/assets/js/9423fe57.8c4a381f.js new file mode 100644 index 00000000..d97927c6 --- /dev/null +++ b/pr-preview/pr-346/assets/js/9423fe57.8c4a381f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[4919],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a =0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},c="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),c=u(n),m=r,d=c["".concat(s,".").concat(m)]||c[m]||h[m]||o;return n?a.createElement(d,i(i({ref:t},p),{},{components:n})):a.createElement(d,i({ref:t},p))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[c]="string"==typeof e?e:r,i[1]=l;for(var u=2;u {n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>u});var a=n(7462),r=(n(7294),n(3905));const o={title:"Functions, Parameters and Error Handling",slug:"/part-4-shell-scripting/functions-parameters-and-error-handling"},i=void 0,l={unversionedId:"shell-scripting/functions-parameters-and-error-handling/index",id:"shell-scripting/functions-parameters-and-error-handling/index",title:"Functions, Parameters and Error Handling",description:"The shell allows you to create functions - a set of commands that you can call at any time. In this chapter we'll see how to create functions and how function parameters and script parameters are handled. We will also look at status codes for commands and scripts and error handling.",source:"@site/docs/04-shell-scripting/22-functions-parameters-and-error-handling/index.md",sourceDirName:"04-shell-scripting/22-functions-parameters-and-error-handling",slug:"/part-4-shell-scripting/functions-parameters-and-error-handling",permalink:"/part-4-shell-scripting/functions-parameters-and-error-handling",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/04-shell-scripting/22-functions-parameters-and-error-handling/index.md",tags:[],version:"current",frontMatter:{title:"Functions, Parameters and Error Handling",slug:"/part-4-shell-scripting/functions-parameters-and-error-handling"},sidebar:"sidebar",previous:{title:"Loops and working with Files and Folders",permalink:"/part-4-shell-scripting/loops-and-working-with-files-and-folders"},next:{title:"Useful Patterns for Shell Scripts",permalink:"/part-4-shell-scripting/useful-patterns-for-shell-scripts"}},s={},u=[{value:"Creating a Function index ",id:"creating-a-function--index-",level:2},{value:"Variables in Functions",id:"variables-in-functions",level:2},{value:"Variable Scoping index",id:"variable-scoping-index",level:3},{value:"Passing Parameters to Functions",id:"passing-parameters-to-functions",level:2},{value:"Parameter Variables",id:"parameter-variables",level:3},{value:"Parameter Shifting",id:"parameter-shifting",level:3},{value:"Return Values",id:"return-values",level:2},{value:"Writing Results to Stdout",id:"writing-results-to-stdout",level:2},{value:"Dealing with Output in Commands",id:"dealing-with-output-in-commands",level:3},{value:"Returning Status Codes",id:"returning-status-codes",level:3},{value:"Error Handling",id:"error-handling",level:2},{value:"The Function Keyword",id:"the-function-keyword",level:2},{value:"Parameters and Status Codes for Scripts",id:"parameters-and-status-codes-for-scripts",level:2},{value:"Updating the 'common' Command",id:"updating-the-common-command",level:2},{value:"Summary",id:"summary",level:2}],p={toc:u};function c(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"The shell allows you to create ",(0,r.kt)("em",{parentName:"p"},"functions")," - a set of commands that you can call at any time. In this chapter we'll see how to create functions and how function parameters and script parameters are handled. We will also look at status codes for commands and scripts and error handling."),(0,r.kt)("h2",{id:"creating-a-function--index-"},"Creating a Function "),(0,r.kt)("p",null,"A ",(0,r.kt)("em",{parentName:"p"},"function")," has the following structure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"}," {\n \n \n \n}\n")),(0,r.kt)("p",null,"First we specify the name of the function. Then between a set of opening and closing curly braces, we list the commands that should be executed when we call the function."),(0,r.kt)("p",null,"Let's take a look at a very simple function in action:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'title() {\n echo "My Script version 1.0"\n}\n')),(0,r.kt)("p",null,"This script defines a very simple function called ",(0,r.kt)("em",{parentName:"p"},"title")," that prints out a message. We call the function in the same way we would call any command in the shell, by simply writing the name of the command and hitting enter. Here's how we would call the function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ title\nMy Script version 1.0"\n')),(0,r.kt)("p",null,"Easy! Functions let you structure commands into logical blocks and can help make your scripts easier to read and manage."),(0,r.kt)("h2",{id:"variables-in-functions"},"Variables in Functions"),(0,r.kt)("p",null,"A function can read and write to any variables in the current shell session. Here's an example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'# Set some variables.\ntitle="My Cool Script"\nversion="1.2"\nsucceeded=0\n\n# Create a function that writes a message and changes a variable.\ntitle() {\n # Note that we can read variables...\n title_message="${title} - version ${version}"\n echo "${title_message}"\n\n # ...and set them as well.\n succeeded=1\n}\n\n# Show the value of \'succeeded\' before and after the function call.\necho "Succeeded: ${succeeded}"\ntitle\necho "Succeeded: ${succeeded}"\necho "Title Message: ${title_message}"\n')),(0,r.kt)("p",null,"The output of this script will be:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Succeeded: 0\nMy Cool Script - version 1.2\nSucceeded: 1\nTitle Message: My Cool Script - version 1.2\n")),(0,r.kt)("p",null,"This demonstrates that functions can use the variables that are available in the shell. They can also set variables. We can also create new variables in functions."),(0,r.kt)("h3",{id:"variable-scoping-index"},"Variable Scoping "),(0,r.kt)("p",null,"If you come from a programming background you might find it odd that you can create a variable in a function and use it outside of the function. This is a feature known as ",(0,r.kt)("em",{parentName:"p"},"dynamic scoping"),". Many common programming languages like Python, JavaScript, C, Java and others use an alternative mechanism called ",(0,r.kt)("em",{parentName:"p"},"lexical scoping"),"."),(0,r.kt)("p",null,"Lexical scoping is a feature that ensures that you can only use a variable from within the 'scope' that it is defined. This can reduce errors - because it means that if you define a variable in a function you don't accidentally 'overwrite' the value of another variable that is used elsewhere."),(0,r.kt)("p",null,"You can use the 'local' keyword to define a variable that is only available in the 'local' scope, i.e. the function that it is defined in. This allows you to use lexical scoping and can reduce the risk of errors. Here's an example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'run_loop() {\n local count=0\n for i in {1..10}; do\n # Update our counter.\n count=$((count + 1))\n done\n echo "Count is: ${count}"\n}\n')),(0,r.kt)("p",null,"Let's see what happens if we run this function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ run_loop\nCount is: 10\n$ echo "Count: ${count}"\nCount:\n')),(0,r.kt)("p",null,"Notice that because we declared the ",(0,r.kt)("em",{parentName:"p"},"count")," variable using the 'local' keyword, it is only available inside the ",(0,r.kt)("em",{parentName:"p"},"run_loop")," function. If we try and access it outside of the function it is undefined."),(0,r.kt)("p",null,"In general, you should use 'local' variables inside functions. This can help to avoid problems where calling a function can have an unintended side effects:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"# Set a count variable somewhere in our script...\ncount=3\n\n# Call our 'run_loop' function.\nrun_loop\n\n# Write out the value of 'count'.\necho \"The 'count' variable is: ${count}\"\n")),(0,r.kt)("p",null,"The output of this script is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Count is: 10\nThe 'count' variable is: 3\n")),(0,r.kt)("p",null,"Notice that even though we used a variable named ",(0,r.kt)("em",{parentName:"p"},"count")," in the ",(0,r.kt)("em",{parentName:"p"},"run_loop")," function, we did not overwrite the value that was set outside of the function. If we were to run the same script but not use the 'local' keyword for the count variable, we would get the following output:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Count is: 10\nThe 'count' variable is: 10\n")),(0,r.kt)("p",null,"In this case calling the function changes the 'count' variable that is outside of the function. In most cases this is not going to be what you want and will just lead to unexpected behaviour later on."),(0,r.kt)("h2",{id:"passing-parameters-to-functions"},"Passing Parameters to Functions"),(0,r.kt)("p",null,"You can pass any number of parameters to a shell function. To get the value of a parameter, we can use special built-in variables that represent each parameter. Let's take a look at an example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'sum() {\n local value1=$1\n local value2=$2\n local result=$((value1 + value2))\n echo "The sum of ${value1} and ${value2} is ${result}"\n}\n')),(0,r.kt)("p",null,"Let's see how we can pass parameters to this function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ sum 3 6\nThe sum of 3 and 6 is 9\n$ sum 10 33\nThe sum of 10 and 33 is 43\n")),(0,r.kt)("p",null,"In this script we have used the special ",(0,r.kt)("inlineCode",{parentName:"p"},"$1")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"$2")," built-in variables to get the value of the first and second parameters. At the beginning of the function I have put these variables into local variables that have more descriptive names. This is purely to make the script more readable, I could also have written the function like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'# Create a function that calculates the sum of two numbers.\nsum() {\n echo "The sum of $1 and $2 is $(($1 + $2))"\n}\n')),(0,r.kt)("p",null,"For a short and simple function you might just use the special parameter variables directly like above. However for anything more complex than a one-line script I think that it is generally more readable to create a local variable with a more descriptive name."),(0,r.kt)("h3",{id:"parameter-variables"},"Parameter Variables"),(0,r.kt)("p",null,"There are a number of special parameter variables that the shell provides. Let's see a few in action:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'# Create a function that sums a set of numbers.\nsum() {\n local total=0\n for value in $@; do\n total=$((total + value))\n done\n\n # Write out the result.\n echo "Summed $# values for a total of: ${total}"\n}\n')),(0,r.kt)("p",null,"We can call this function with any number of parameters:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ sum 1 2 3 4 5\nSummed 5 values for a total of: 15\n")),(0,r.kt)("p",null,"In this script we've used two special variables. The ",(0,r.kt)("inlineCode",{parentName:"p"},"$@")," variable is expanded into a list of all of the function parameters. The ",(0,r.kt)("inlineCode",{parentName:"p"},"$#")," variable contains the number of parameters provided to the function."),(0,r.kt)("p",null,"You might recognise that these variables look quite similar to the syntax that is used to get the members of an array or the length of an array as described in ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/variables-reading-input-and-mathematics/"},"Chapter 19 - Variables, Reading Input, and Mathematics"),". You can actually use some of the array-style operators with the special parameters variables:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"# Show the top 'n' values of a set.\nshow_top() {\n local n=$1\n local values=${@:2:n}\n echo \"Top ${n} values: ${values}\"\n}\n")),(0,r.kt)("p",null,"We can call this function with any number of parameters. The first parameter specifies how many of the subsequent parameters we will show:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ show_top 3 10 20 30 40 50\nTop 3 values: 10 20 30\n")),(0,r.kt)("p",null,"We have used the 'range' operator on the ",(0,r.kt)("inlineCode",{parentName:"p"},"$@")," variable to get a subset of the parameters. This script is a little odd to read because when we set the 'values' parameter we need to 'skip' past the first positional parameter, because the first positional parameter is the number of values to show."),(0,r.kt)("p",null,"The table below shows some of the common variables you can use when working with function parameters:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Variable"),(0,r.kt)("th",{parentName:"tr",align:null},"Description"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"$1")),(0,r.kt)("td",{parentName:"tr",align:null},"The first parameter")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"$2")),(0,r.kt)("td",{parentName:"tr",align:null},"The second parameter")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"${11}")),(0,r.kt)("td",{parentName:"tr",align:null},"The 11th parameter - if the parameter is more than one digit you must surround it with braces")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"$#")),(0,r.kt)("td",{parentName:"tr",align:null},"The number of parameters")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"$@")),(0,r.kt)("td",{parentName:"tr",align:null},"The full set of parameters as an array")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"$*")),(0,r.kt)("td",{parentName:"tr",align:null},"The full set of parameters as a string separated by the first value in the ",(0,r.kt)("inlineCode",{parentName:"td"},"$IFS")," variable")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"${@:start:count}")),(0,r.kt)("td",{parentName:"tr",align:null},"A subset of 'count' parameters starting at parameter number 'start'")))),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"$@")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"@*")," parameters look quite similar. The first one is an array, just like we saw in ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/variables-reading-input-and-mathematics/"},"Chapter 19 - Variables, Reading Input, and Mathematics"),". The second version is the parameters collected together into a single string separated by spaces (actually, separated by the first character in the ",(0,r.kt)("inlineCode",{parentName:"p"},"$IFS")," variable)."),(0,r.kt)("h3",{id:"parameter-shifting"},"Parameter Shifting"),(0,r.kt)("p",null,"We can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"shift")," (",(0,r.kt)("em",{parentName:"p"},"shift positional parameters"),")"," to remove a number of parameters from the beginning of the position parameters list and 'shift' the remaining parameters to take their place."),(0,r.kt)("p",null,"This is a little hard to describe so let's see how we can use it to simplify our ",(0,r.kt)("em",{parentName:"p"},"show_top")," function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"# Show the top 'n' values of a set.\nshow_top() {\n # Grab the number of values to show, then shift.\n local n=$1\n shift\n\n # Get the set of values to show. Notice that we start in position 1 now.\n local values=${@:1:n}\n echo \"Top ${n} values: ${values}\"\n}\n")),(0,r.kt)("p",null,"After we get the value of the first parameter, we 'shift', removing it from the list of positional parameters so that we can deal with the remaining parameters. I would avoid using 'shift' too much - if you find that you are having to write complex code to shift parameters around you might be better using a programming language rather than the shell for the task you are performing!"),(0,r.kt)("h2",{id:"return-values"},"Return Values"),(0,r.kt)("p",null,"You can return a value from a shell function in two ways. The first is to simply set the value of a variable, like so:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"is_even() {\n local number=$1\n\n # A number is even if when we divide it by 2 there is no remainder.\n # Set 'result' to 1 if the parameter is even and 0 otherwise.\n if [ $((number % 2)) -eq 0 ]; then\n result=1\n else\n result=0\n fi\n}\n")),(0,r.kt)("p",null,"A function could set any number of variables to provide output. Here's how we could use the ",(0,r.kt)("em",{parentName:"p"},"is_even")," function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ number=33\n$ is_even $number\n$ echo "Result is: $result"\nResult is: 0\n')),(0,r.kt)("p",null,"In general, this method of returning values from a function should be avoided, for the reasons we've discussed already in this chapter. It overwrites the value of a global variable and that can be confusing for the operator."),(0,r.kt)("p",null,"A more common way to return a value from a function is to write its result to ",(0,r.kt)("em",{parentName:"p"},"stdout")," - let's look at this in detail."),(0,r.kt)("h2",{id:"writing-results-to-stdout"},"Writing Results to Stdout"),(0,r.kt)("p",null,"If we write our result to ",(0,r.kt)("em",{parentName:"p"},"stdout"),", then we can capture the result of a function in a far more readable way:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"lowercase() {\n local params=\"$@\"\n # Translate all uppercase characters to lowercase characters.\n echo \"$params\" | tr '[:upper:]' '[:lower:]' \n}\n")),(0,r.kt)("p",null,"In this example we write the result of the function to ",(0,r.kt)("em",{parentName:"p"},"stdout"),". This means that we can capture the result and put it in another variable by simply executing the command in a subshell:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ result=$(lowercase "Don\'t SHOUT!")\n$ echo "$result"\ndon\'t shout!\n')),(0,r.kt)("p",null,"If you have a programming background it might seem very strange that you write results in a function by writing to ",(0,r.kt)("em",{parentName:"p"},"stdout"),". Remember - the shell is a text based interface to the computer system. The majority of commands that we have seen so far that provide output write their output to the screen. This is what ",(0,r.kt)("inlineCode",{parentName:"p"},"ls")," does, what ",(0,r.kt)("inlineCode",{parentName:"p"},"find")," does, what ",(0,r.kt)("inlineCode",{parentName:"p"},"cat")," does and so on. When we ",(0,r.kt)("inlineCode",{parentName:"p"},"echo")," a result from a function, we are really just following the Unix standard of writing the results of a program to the screen."),(0,r.kt)("p",null,"This is important - if we run our function directly in a shell, we can see the result written to the screen:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ lowercase \"PLEASE don't SHOUT!\"\nplease don't shout!\n")),(0,r.kt)("p",null,"Remember - shell functions are designed to behave in a similar way to shell commands. They write their output to ",(0,r.kt)("em",{parentName:"p"},"stdout"),"."),(0,r.kt)("h3",{id:"dealing-with-output-in-commands"},"Dealing with Output in Commands"),(0,r.kt)("p",null,"Although it might feel a bit clunky, writing the results of a command to ",(0,r.kt)("em",{parentName:"p"},"stdout")," is a tried and tested method of returning results. However, we need to be careful. Let's take a look at an example to see why!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'# This function creates a temporary folder for today and returns its path.\ntemp_today() {\n # Get today\'s date in the format YYYY-MM-DD.\n local today=$(date +"%Y-%m-%d")\n\n # Create a temporary directory for today and return it.\n tmpdir_today="/tmp/${today}"\n echo "Creating folder \'${tmpdir_today}\'..."\n mkdir -p "${tmpdir_today}"\n echo "${tmpdir_today}"\n}\n')),(0,r.kt)("p",null,"This function creates a temporary folder that is based on the current date. If we try and grab the result of the function and change to that folder then the script will fail:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'# Go to today\'s temporary folder.\nfolder=$(temp_today)\ncd "${folder}"\n')),(0,r.kt)("p",null,"This script fails, with the output:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"'Creating folder \\'/tmp/2021-05-28\\'...\\n/tmp/2021-05-28': No such file or directory\n")),(0,r.kt)("p",null,"What's going on here?"),(0,r.kt)("p",null,"Well in the ",(0,r.kt)("em",{parentName:"p"},"temp_today")," function we wrote a message halfway through the function, showing the name of the folder that would be created. This message is part of the output of the function. Even though in the last line we echo the path to the folder, the output of the command is ",(0,r.kt)("em",{parentName:"p"},"all")," of the text we have written."),(0,r.kt)("p",null,"It is important to remember that any command you call in a function that might write to ",(0,r.kt)("em",{parentName:"p"},"stdout")," could cause problems as it could write text to your output:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'command_exists() {\n if type "$1"; then\n echo "1"\n else\n echo "0"\n fi\n}\n')),(0,r.kt)("p",null,"What happens when we try and store the result of the function in a variable?"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ result=$(command_exists "touch")\n$ echo "Result is: ${result}"\nResult is: touch is hashed (/usr/bin/touch)\n1\n')),(0,r.kt)("p",null,"This is not a well written function, we'll look at a better way to write it next. But it shows an important challenge to be aware of - when ",(0,r.kt)("inlineCode",{parentName:"p"},"type")," is used to find out whether a command exists it returns success if the command exists but also writes to ",(0,r.kt)("em",{parentName:"p"},"stdout"),"."),(0,r.kt)("p",null,"In ",(0,r.kt)("a",{parentName:"p",href:"/part-2-core-skills/thinking-in-pipelines/"},"Chapter 7 - Thinking in Pipelines")," we saw that we can send the output of a command to the 'null' device to silence its output. We can use this trick in our functions to stop commands from 'polluting' our result:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'command_exists() {\n if type "$1" >> /dev/null; then\n echo "1"\n else\n echo "0"\n fi\n}\n')),(0,r.kt)("p",null,"Now if we run this command we will not get the output from the ",(0,r.kt)("inlineCode",{parentName:"p"},"type")," command in our result - the output was redirected to the null device."),(0,r.kt)("h3",{id:"returning-status-codes"},"Returning Status Codes"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"return")," (",(0,r.kt)("em",{parentName:"p"},"return from shell function"),")"," command causes a function to exit with a given status code."),(0,r.kt)("p",null,"This is something that often causes confusion in shell scripts. The reason is that in most programming languages, you would use a 'return' statement to return the result of a function. But in the shell, when we return, we set the ",(0,r.kt)("em",{parentName:"p"},"status code")," of the function."),(0,r.kt)("p",null,"What is a status code? We actually touched on this in ",(0,r.kt)("a",{parentName:"p",href:"/part-4-shell-scripting/mastering-conditional-logic"},"Chapter 20 - Mastering Conditional Logic"),". When a command runs, we expect it to return a ",(0,r.kt)("em",{parentName:"p"},"status code")," of 'zero' to indicate success. Any non-zero status code is used to specify an ",(0,r.kt)("em",{parentName:"p"},"error code"),"."),(0,r.kt)("p",null,"Let's see how we could re-write the ",(0,r.kt)("em",{parentName:"p"},"command_exists")," function to set a status code:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'command_exists() {\n if type "$1" >> /dev/null; then\n return 0\n else\n return 1\n fi\n}\n')),(0,r.kt)("p",null,"Now that our command sets a status code properly, we can use it in an 'if statement' like so:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'if command_exists "common"; then\n echo "The \'common\' command is installed on your system"\nelse\n echo "The \'common\' command is not installed on your system"\nfi\n')),(0,r.kt)("p",null,"Remember - only use the 'return' command to set a status code. Many shells will only allow values from 0-255 to be set, and most users will expect that a command should return zero for success and that any non-zero value is an error code. If you need to provide output for a command that is not just a status code, you should write it to ",(0,r.kt)("em",{parentName:"p"},"stdout")," or if you must, set the value of a global variable."),(0,r.kt)("p",null,"The result of the last executed command is always available in the special variable ",(0,r.kt)("inlineCode",{parentName:"p"},"$?"),". Here's how you could use it:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ type "test"\ntest is a shell builtin\n$ echo "Result: $?"\nResult: 0\n')),(0,r.kt)("h2",{id:"error-handling"},"Error Handling"),(0,r.kt)("p",null,"When you run a shell script, if a command in the script fails, the script will continue to run. Like many other points in this chapter this might seem unintuitive if you come from a programming background, but this makes sense in the shell - if the shell was to terminate whenever a command fails it would be very difficult to use interactively."),(0,r.kt)("p",null,"Let's create a script called 'today' that makes a new temporary folder each day, then puts a link to that folder in our home directory:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'#!/usr/bin/env sh\n\n# Get today\'s date in the format YYYY-MM-DD.\ntoday=$(date +"%Y-%m-%d")\n\n# Create the path to today\'s temp folder and then make sure the folder exists.\ntemp_path="/tmp/${today}"\nmkdir -p "${temp_path}"\n\n# Now that we\'ve created the folder, make a symlink to it in our homedir.\nln -sf "${temp_path}" "${HOME}/today" \n\n# Write out the path we created.\necho "${temp_path}"\n')),(0,r.kt)("p",null,"Now we can run the script to create temporary folder for the current day and a link to it in our home directory:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ chmod +x ./today.sh\n$ ./today.sh\n/tmp/2021-05-28\n$ cd ~/today\n")),(0,r.kt)("p",null,"In this example we created a new directory in the ",(0,r.kt)("em",{parentName:"p"},"tmp")," folder and created a link to it in our home directory. But what happens if we cause one of the commands to fail?"),(0,r.kt)("p",null,"First, let's clean up the folder we created:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ rm -rf $(./today.sh)\n$ rm ~/today\n")),(0,r.kt)("p",null,"Now we'll create a file where we want to put our 'today' folder:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ touch "/tmp/$(date +"%Y-%m-%d")"\n')),(0,r.kt)("p",null,"If we run our script now, we can see a problem:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ ./today.sh\nmkdir: /tmp/2021-05-28: Not a directory\n/tmp/2021-05-28\n$ cd ~/today\nbash: cd: /home/dwmkerr/today: Not a directory\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"mkdir")," command failed - because there was a ",(0,r.kt)("em",{parentName:"p"},"file")," in the location where we wanted to create the folder. But the script kept on running - meaning that it created a symlink to this file. Now when we try to move to the ",(0,r.kt)("inlineCode",{parentName:"p"},"today")," folder we get another error - it is a link to a file not a folder."),(0,r.kt)("p",null,"In general in your shell scripts if a command fails you probably want the entire script to stop executing. Otherwise you can get this cascading effect as commands continue to return even after there was a failure, which can lead to all sorts of unexpected behaviour."),(0,r.kt)("p",null,"You can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"set")," (",(0,r.kt)("em",{parentName:"p"},"set option"),") command to set an option in the shell. There is an option that tells the shell to exit when a command fails. Here's how we would use it:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"#!/usr/bin/env sh\n\n# Exit if any command fails.\nset -e\n\n# ...\n")),(0,r.kt)("p",null,"The 'set' command allows you to turn on and turn off shell options. The 'e' option means 'exit if any command exits with a non-zero status'."),(0,r.kt)("p",null,"Now let's clean up again:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ rm -rf $(./today.sh)\n$ rm ~/today\n")),(0,r.kt)("p",null,"And finally, we'll run the same script after creating the file that will cause a failure:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ touch "/tmp/$(date +"%Y-%m-%d")"\n$ ./today.sh\nmkdir: /tmp/2021-05-28: Not a directory\n')),(0,r.kt)("p",null,"In this case the script stopped running as soon as there was a failure - after the ",(0,r.kt)("inlineCode",{parentName:"p"},"mkdir")," command failed."),(0,r.kt)("p",null,"One thing to be aware of is that the ",(0,r.kt)("inlineCode",{parentName:"p"},"set -e")," option only affects the ",(0,r.kt)("em",{parentName:"p"},"final")," command of a pipeline. This means that if you have a pipeline such as the below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"grep '[:space:]*#' ~/effective-shell/scripts/common.sh | tr 'a-z' 'A-Z'\n")),(0,r.kt)("p",null,"Then the script will still run if the ",(0,r.kt)("inlineCode",{parentName:"p"},"grep")," command fails. To ensure that the shell terminates if a command in a pipeline fails we must set the ",(0,r.kt)("inlineCode",{parentName:"p"},"pipefail")," option:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"set -o pipefail\n")),(0,r.kt)("p",null,"If you set your scripts up so that they fail on errors (and this is probably something you should always do), then remember to make sure that commands that you expect ",(0,r.kt)("em",{parentName:"p"},"might")," fail are properly handled."),(0,r.kt)("p",null,"For example, if we want to delete a file in script but don't want to stop if the deletion fails for some reason, we could use an ",(0,r.kt)("inlineCode",{parentName:"p"},"if")," block to 'catch' the error and show a warning:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'if ! [ rm ~/my-file.text ]; then\n echo "warning: unable to delete file"\nfi\n')),(0,r.kt)("p",null,"Another option would be to use a conditional expression:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"rm ~/my-file.txt || true\n")),(0,r.kt)("p",null,"This expression always evaluates to 'true' so will not stop the script if an error occurs when running the ",(0,r.kt)("inlineCode",{parentName:"p"},"rm")," command."),(0,r.kt)("h2",{id:"the-function-keyword"},"The Function Keyword"),(0,r.kt)("p",null,"In some scripts you might see functions defined using the ",(0,r.kt)("inlineCode",{parentName:"p"},"function")," keyword, as below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'function title() {\n echo "My Script version 1.0"\n}\n')),(0,r.kt)("p",null,"The 'function' keyword is not required. The keyword is available in Bash and similar shells. Using the function keyword has a minor benefit that it does not lead to an error if you have already defined an ",(0,r.kt)("em",{parentName:"p"},"alias")," with the same name as the function you are declaring. However, the drawback is that it is less standard and therefore less portable."),(0,r.kt)("p",null,"I would recommend that you do not use the 'function' keyword. Firstly, this will make your scripts more portable. Secondly, if your function is going to clash with the name of an alias that has already been defined, I would actually think that it is better that the script fails. Better to fail early and realise there is clash than to silently overwrite the alias which may cause unexpected errors later on when something else tries to call the alias and calls your function instead!"),(0,r.kt)("h2",{id:"parameters-and-status-codes-for-scripts"},"Parameters and Status Codes for Scripts"),(0,r.kt)("p",null,"Everything we have learned about parameters applies to scripts themselves. We can pass parameters to scripts and read them with the special variables such as ",(0,r.kt)("inlineCode",{parentName:"p"},"$1"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"$2")," and so on."),(0,r.kt)("p",null,"The only difference is that instead of using the ",(0,r.kt)("inlineCode",{parentName:"p"},"return")," command when we want to exit a script with a status code, we use the ",(0,r.kt)("inlineCode",{parentName:"p"},"exit")," (",(0,r.kt)("em",{parentName:"p"},"exit the shell"),")"," command. The exit command exits the current shell with the provided status code."),(0,r.kt)("p",null,"Be careful when using the ",(0,r.kt)("inlineCode",{parentName:"p"},"exit")," command - if you are running a script then it is fine to use ",(0,r.kt)("inlineCode",{parentName:"p"},"exit"),", it will simply close the subshell that the script is running in. But if you type ",(0,r.kt)("inlineCode",{parentName:"p"},"exit")," in your shell that you are using interactively, it will close it."),(0,r.kt)("h2",{id:"updating-the-common-command"},"Updating the 'common' Command"),(0,r.kt)("p",null,"In the previous chapter we created the ",(0,r.kt)("inlineCode",{parentName:"p"},"common.v4.sh")," command, that shows common commands from the users shell history."),(0,r.kt)("p",null,"If you need a refresher on what is in the script, you can view it in your pager with:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"less ~/effective-shell/scripts/common.v4.sh\n")),(0,r.kt)("p",null,"The output of the command will look something like:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"1: 280 gst\n2: 144 vi\n3: 84 gc\n4: 72 ga .\n5: 62 gl\n6: 54 ls\n7: 50 gpo\n8: 48 gcm\n9: 45 make dev\n10: 44 gpr\n")),(0,r.kt)("p",null,"Let's make a couple of changes."),(0,r.kt)("p",null,"First, let's make sure we exit the script if one of the commands fails:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"# Exit if any command fails.\nset -e\n")),(0,r.kt)("p",null,"Next, we will update the script on line 7 so that we use the first parameter as the command count. If the first parameter is not set, we default to ten:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"# ...\ncommand_count=${1:-10} # The number of common commands to show\n# ...\n")),(0,r.kt)("p",null,"Here we are using the ",(0,r.kt)("inlineCode",{parentName:"p"},"$1")," variable. But we are also using ",(0,r.kt)("em",{parentName:"p"},"Shell Parameter Expansion")," as described in ",(0,r.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/variables-reading-input-and-mathematics/"},"Chapter 19 - Variables, Reading Input, and Mathematics")," to provide a default value to use if the parameter is not set."),(0,r.kt)("p",null,"Next, let's change the line that writes out the count and the name of the command. At the moment, the count is shown and then the command name. Let's write a function that takes a number and line of text and writes it as a line of text with the number ",(0,r.kt)("em",{parentName:"p"},"after")," the text and in brackets:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'write_command_then_count() {\n # Get the command and count, this will be text that looks like:\n # \'43 git commit\'\n # Then write the command and the count afterwards.\n local line="$1"\n local count=$(echo "${line}" | cut -d\' \' -f1)\n local command=$(echo "${line}" | cut -d\' \' -f2-)\n echo "${command} (${count})"\n}\n')),(0,r.kt)("p",null,"We can now re-write our loop to make it a little cleaner:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'for command in $commands\ndo\n echo "$counter: $(write_command_then_count "$command")"\n counter=$((counter + 1))\ndone\n')),(0,r.kt)("p",null,"The updated script is in the samples folder at ",(0,r.kt)("em",{parentName:"p"},"~/effective-shell/scripts/common.v5.sh"),", you can update your link to point to this version by running the ",(0,r.kt)("inlineCode",{parentName:"p"},"ln")," command:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"ln -s ~/effective-shell/scripts/common.v5.sh /usr/local/bin/common\n")),(0,r.kt)("p",null,"Now when we run this command we can optionally provide the number of commands to show as a parameter. The output also is shown with the number of times the command has been called ",(0,r.kt)("em",{parentName:"p"},"after")," the command text itself:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ common 5\ncommon commands:\n1: gst (139)\n2: vi (74)\n3: gc (42)\n4: ga . (36)\n5: gl (31)\n")),(0,r.kt)("h2",{id:"summary"},"Summary"),(0,r.kt)("p",null,"In this chapter we looked at how to use functions to provide more structure to our shell scripts, and also how to use parameters, return values and status codes."),(0,r.kt)("p",null,"In the next and final chapter of this section, we'll look at some more advanced techniques that can be useful when writing shell scripts."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/9446f79a.88b6ac45.js b/pr-preview/pr-346/assets/js/9446f79a.88b6ac45.js new file mode 100644 index 00000000..e778a05b --- /dev/null +++ b/pr-preview/pr-346/assets/js/9446f79a.88b6ac45.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[5368],{3905:(e,t,a)=>{a.d(t,{Zo:()=>m,kt:()=>p});var n=a(7294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t =0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n =0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var l=n.createContext({}),h=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},m=function(e){var t=h(e.components);return n.createElement(l.Provider,{value:t},e.children)},d="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},g=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),d=h(a),g=i,p=d["".concat(l,".").concat(g)]||d[g]||c[g]||o;return a?n.createElement(p,r(r({ref:t},m),{},{components:a})):n.createElement(p,r({ref:t},m))}));function p(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=g;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:i,r[1]=s;for(var h=2;h {a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>h});var n=a(7462),i=(a(7294),a(3905));const o={title:"Controlling Changes with Git",slug:"/part-5-building-your-toolkit/controlling-changes-with-git"},r=void 0,s={unversionedId:"building-your-toolkit/controlling-changes-with-git/index",id:"building-your-toolkit/controlling-changes-with-git/index",title:"Controlling Changes with Git",description:"Git is an extremely popular version control tool. You can use Git to track changes to text, code, or any type of files you might be working with. Many popular projects use Git as a tool to manage changes, allow others to contribute and collaborate, and to publish their projects.",source:"@site/docs/05-building-your-toolkit/27-controlling-changes-with-git/index.md",sourceDirName:"05-building-your-toolkit/27-controlling-changes-with-git",slug:"/part-5-building-your-toolkit/controlling-changes-with-git",permalink:"/part-5-building-your-toolkit/controlling-changes-with-git",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/05-building-your-toolkit/27-controlling-changes-with-git/index.md",tags:[],version:"current",frontMatter:{title:"Controlling Changes with Git",slug:"/part-5-building-your-toolkit/controlling-changes-with-git"},sidebar:"sidebar",previous:{title:"Managing your Dotfiles",permalink:"/part-5-building-your-toolkit/managing-your-dotfiles"},next:{title:"Managing Remote Git Repositories and Sharing Your Dotfiles",permalink:"/part-5-building-your-toolkit/managing-rempte-git-repositories/"}},l={},h=[{value:"What is Git",id:"what-is-git",level:2},{value:"Creating a Git Repository",id:"creating-a-git-repository",level:2},{value:"Adding and Resetting Changes to the Index",id:"adding-and-resetting-changes-to-the-index",level:2},{value:"Committing Changes",id:"committing-changes",level:2},{value:"Commit Messages",id:"commit-messages",level:3},{value:"Creating Branches",id:"creating-branches",level:2},{value:"Merging",id:"merging",level:2},{value:"Merging Diverged Branches",id:"merging-diverged-branches",level:3},{value:"The Git Log",id:"the-git-log",level:3},{value:"Merge Conflicts",id:"merge-conflicts",level:3},{value:"Other Merge Strategies",id:"other-merge-strategies",level:3},{value:"Deleting and Renaming Files",id:"deleting-and-renaming-files",level:2},{value:"Restoring Your Working Tree",id:"restoring-your-working-tree",level:2},{value:"Summary",id:"summary",level:2}],m={toc:h};function d(e){let{components:t,...o}=e;return(0,i.kt)("wrapper",(0,n.Z)({},m,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"Git is an extremely popular version control tool. You can use Git to track changes to text, code, or any type of files you might be working with. Many popular projects use Git as a tool to manage changes, allow others to contribute and collaborate, and to publish their projects."),(0,i.kt)("p",null,"In this chapter we'll look at the most common operations for Git to allow us to quickly and easily work with Git repositories. We'll learn Git on the command line by taking our 'dotfiles' folder (a simple set of shell configuration files) and showing how we can track and manage changes."),(0,i.kt)("p",null,"Being able to use Git from the command line is essential to being an effective shell user. In this chapter we'll look at setting up a local Git repository - in the next chapter we'll see how to share it online with the popular GitHub website."),(0,i.kt)("h2",{id:"what-is-git"},"What is Git"),(0,i.kt)("p",null,"Any files or folders that we work with over time, such as our 'dotfiles' (i.e. our personal configuration files) will change. Sometimes new files get added, old files get deleted, files get changed, things get moved around and so on."),(0,i.kt)("p",null,"Git is a ",(0,i.kt)("em",{parentName:"p"},"version control system")," that allows you to track changes to files and folders. This means that you can maintain a history of all of the changes that have been made, when they were made, who made them and why. You can also maintain multiple 'branches' of your files and folders - these branches can be used as working environments where you can make changes, without affecting the current 'main' set of files."),(0,i.kt)("p",null,'Git was written by Linus Torvalds (the creator of Linux) in 2006. "Git" is slang for an annoying person - Linus joked that he always names projects after himself, first Linux and now Git. There were many version control systems around before Git, such as ',(0,i.kt)("em",{parentName:"p"},"CVS")," (Concurrent Version System) and ",(0,i.kt)("em",{parentName:"p"},"SVN")," (Subversion, an system similar to CVS but with some improvements). There were also a number of proprietary and commercial solutions."),(0,i.kt)("p",null,"In recent years Git has become without a doubt the most popular version control system globally, and many highly popular software collaboration systems such as GitHub, GitLab and BitBucket use Git as their underlying version control system, adding additional features on top."),(0,i.kt)("h2",{id:"creating-a-git-repository"},"Creating a Git Repository"),(0,i.kt)("p",null,"All of the information about a set of files or folders that you are tracking the changes for is stored in a ",(0,i.kt)("em",{parentName:"p"},"Git repository"),". We can create a Git repository by running the ",(0,i.kt)("inlineCode",{parentName:"p"},"git init")," (",(0,i.kt)("em",{parentName:"p"},"initialise Git repository"),")"," command."),(0,i.kt)("p",null,"Let's see this in action be creating a Git repository to track changes to our 'dotfiles' folder. Our 'dotfiles' folder is a folder where we keep simple shell configuration - you can read about how to create a folder like this in ",(0,i.kt)("a",{parentName:"p",href:"../../part-5-building-your-toolkit/managing-your-dotfiles"},"Chapter 27 - Managing Your Dotfiles")," or you can download the Effective Shell samples to get a copy of the 'dotfiles' folder."),(0,i.kt)("admonition",{title:"Downloading the Samples",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"Run the following command in your shell to download the samples:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"curl effective.sh | sh\n"))),(0,i.kt)("p",null,"If you have installed the samples, you can copy the ",(0,i.kt)("em",{parentName:"p"},"~/effective-shell/dotfiles")," folder to your home directory - this is where we will create our Git repository and start using the Git commands:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ cp -r ~/effective-shell/dotfiles ~/dotfiles\n$ cd ~/dotfiles\n")),(0,i.kt)("p",null,"We have created the ",(0,i.kt)("em",{parentName:"p"},"~/dotfiles")," folder in our home directory from the samples and moved into it."),(0,i.kt)("p",null,"Now we will initialise a Git repository with the ",(0,i.kt)("inlineCode",{parentName:"p"},"git init")," command, and choose a 'branch' name with the ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git init\nInitialized empty Git repository in /home/dwmkerr/dotfiles/.git/\n$ git checkout -b main\n")),(0,i.kt)("p",null,"We'll see the ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," command in detail soon. For now it is enough to know that we have initialised a new Git repository and chosen the name of our 'main' branch."),(0,i.kt)("admonition",{title:"Initialising a Git Repository with a Branch Name",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"If you are using Git 2.2 or later, you can initialise a repository and set the branch name with a single command:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"$ git init -b main\n")),(0,i.kt)("p",{parentName:"admonition"},"If you see the error message ",(0,i.kt)("em",{parentName:"p"},"error: unknown switch 'b'")," then this means that you are using a version of Git that does not have the ",(0,i.kt)("inlineCode",{parentName:"p"},"-b")," (",(0,i.kt)("em",{parentName:"p"},"initial branch name"),") flag (any version of Git lower than 2.2).")),(0,i.kt)("h2",{id:"adding-and-resetting-changes-to-the-index"},"Adding and Resetting Changes to the Index"),(0,i.kt)("p",null,"We now have an empty Git repository. We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," (",(0,i.kt)("em",{parentName:"p"},"show the working tree status"),") command"," to show some information on the files in the ",(0,i.kt)("em",{parentName:"p"},"working tree"),". The working tree is the folder that we are using Git to track changes for, in our case ",(0,i.kt)("em",{parentName:"p"},"~/dotfiles"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git status\nOn branch main\n\nNo commits yet\n\nUntracked files:\n (use "git add ..." to include in what will be committed)\n install.sh\n shell.d/\n shell.sh\n\nnothing added to commit but untracked files present (use "git add" to track)\n')),(0,i.kt)("p",null,"The first thing that ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," tells us is the name of the ",(0,i.kt)("em",{parentName:"p"},"branch")," we are on. We'll look at branches in detail shortly. The next thing we see is that there are no ",(0,i.kt)("em",{parentName:"p"},"commits")," - commits are sets of changes that we track. Finally, git is telling us that there are three files that are 'untracked'. These are the ",(0,i.kt)("em",{parentName:"p"},"install.sh")," and ",(0,i.kt)("em",{parentName:"p"},"shell.sh")," files as well as the ",(0,i.kt)("em",{parentName:"p"},"shell.d")," folder."),(0,i.kt)("p",null,"If we are going to use Git to track changes to these files, we need to add them to the repository. We can do that with the ",(0,i.kt)("em",{parentName:"p"},"git add")," (",(0,i.kt)("em",{parentName:"p"},"add file contents to index"),") command",":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git add .\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"git add")," command takes a list of file paths. We have used the special ",(0,i.kt)("em",{parentName:"p"},"dot")," folder to represent the entire current directory. Let's take a look at the status again:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git status\nOn branch main\n\nNo commits yet\n\nChanges to be committed:\n (use "git rm --cached ..." to unstage)\n new file: install.sh\n new file: shell.d/set_ps1.sh\n new file: shell.sh\n')),(0,i.kt)("p",null,"Git is now telling us that we have three new files which are ready to be ",(0,i.kt)("em",{parentName:"p"},"committed"),". At the moment these files are in the ",(0,i.kt)("em",{parentName:"p"},"index"),". The index, or staging area, is the set of changes that we are preparing to commit. These changes are not yet stored in the repository. When we add files to the index we are 'staging' changes. When we remove changes from the index, we are 'unstaging' changes."),(0,i.kt)("p",null,"Think of the index as a working area where we can build up a set of changes that we would later like to record in the repository. We could add more files to the index before we actually save them to the repository in a commit."),(0,i.kt)("p",null,"If we were to visualise what we've done so far, it would look like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram: A diagram showing how the 'git add' command tells Git to track changes to items in the working tree and adds them to the index",src:a(9126).Z,width:"1672",height:"1046"})),(0,i.kt)("p",null,"Our ",(0,i.kt)("em",{parentName:"p"},"working tree")," is the folder associated with our Git repository, this is the ",(0,i.kt)("em",{parentName:"p"},"~/dotfiles")," folder. Our ",(0,i.kt)("em",{parentName:"p"},"index")," is initially empty. When we run the ",(0,i.kt)("inlineCode",{parentName:"p"},"git add")," command, we have told Git we want to add three files to the repository. Our 'staging area' has three files in it. Our Git repository does not have any commits recorded yet."),(0,i.kt)("p",null,"What if we realised that we don't want to add one of these files to the repository? To remove a file from the index we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git reset")," (",(0,i.kt)("em",{parentName:"p"},"reset changes"),") command. Let's reset the ",(0,i.kt)("em",{parentName:"p"},"~/dotfiles/shell.d/set_ps1.sh")," file and check the status:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git reset shell.d/set_ps1.sh\n$ git status\nOn branch main\n\nNo commits yet\n\nChanges to be committed:\n (use "git rm --cached ..." to unstage)\n new file: install.sh\n new file: shell.sh\n\nUntracked files:\n (use "git add ..." to include in what will be committed)\n shell.d/\n')),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"git reset")," command has removed a change from the index - telling Git that we don't want to 'stage' one of the files. Git now tells us there are two files in the index and one that is not tracked."),(0,i.kt)("p",null,"Here's how we can visualise the changes we've made:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram: The 'git reset' command removes items from the index",src:a(7878).Z,width:"2060",height:"968"})),(0,i.kt)("p",null,"You can also reset changes by using the ",(0,i.kt)("inlineCode",{parentName:"p"},"git rm --cached")," (",(0,i.kt)("em",{parentName:"p"},"remove changes from index"),") command. However, I think this is a little harder to work with as you have to remember to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"--cached")," flag to tell Git that you are removing from the index and not the repository. We'll see the ",(0,i.kt)("inlineCode",{parentName:"p"},"git rm")," command a little later in the chapter."),(0,i.kt)("p",null,"Remember - at this stage we have not changed a single file! Nothing we have done has changed the content of any of the files in the working tree, and the only thing that has changed in the Git repository is the 'index' - the current set of files that we are 'staging'."),(0,i.kt)("p",null,"Now let's look at how we can commit our changes with the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," command."),(0,i.kt)("h2",{id:"committing-changes"},"Committing Changes"),(0,i.kt)("p",null,"Once we are happy with the set of changes in the index, we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," (",(0,i.kt)("em",{parentName:"p"},"record changes to the repository"),") command","."),(0,i.kt)("p",null,"Now run the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git commit \n")),(0,i.kt)("p",null,"At this stage your shell editor will open up, with the text shown below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"\n# Please enter the commit message for your changes. Lines staring\n# with '#' will be ignored, and an empty message aborts the commit.\n#\n# On branch main\n#\n# Initial commit\n#\n# Changes to be committed:\n# new file: install.sh\n# new file: shell.sh\n#\n# Untracked files:\n# shell.d/\n#\n")),(0,i.kt)("p",null,"The reason you shell editor opens is that the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," command would like you to provide a message describing your changes. Type a short description, such as:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"add the 'install' and 'shell' scripts\n# Please enter the commit message for your changes. Lines staring\n# with '#' will be ignored, and an empty message aborts the commit.\n#\n# On branch main\n#\n# Initial commit\n#\n# Changes to be committed:\n# new file: install.sh\n# new file: shell.sh\n#\n# Untracked files:\n# shell.d/\n#\n")),(0,i.kt)("p",null,"Note that below the cursor there is some information that starts with the ",(0,i.kt)("inlineCode",{parentName:"p"},"#")," hash symbol. This is provided as a convenience - Git is telling you the status of the index. Anything that starts with a hash symbol is a comment and will not be stored in the commit message."),(0,i.kt)("p",null,"Save the file by pressing Ctrl+W and close the editor with Ctrl+X (these are the commands for the ",(0,i.kt)("inlineCode",{parentName:"p"},"nano")," editor, if you are using a different editor use whatever commands are needed to save and close)."),(0,i.kt)("p",null,"When the editor closes you'll see a confirmation below the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git commit\n[main (root-commit) 01e7a10] add the 'install' and 'shell' scripts\n 2 files changed, 90 insertions(+)\n create mode 100755 install.sh\n create mode 100644 shell.sh\n")),(0,i.kt)("p",null,"This message tells us that two files have changed and 90 lines have been added. It also lists the files we have added. At this point we have created our first commit. We can visualise the process we have gone through like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing how 'git commit' commits changes to the repository",src:a(9395).Z,width:"2116",height:"1030"})),(0,i.kt)("p",null,"We 'staged' a set of changes and then 'committed' these changes. We now have a single commit in our repository. Our files are ",(0,i.kt)("em",{parentName:"p"},"still")," unchanged, but our Git repository now has a single commit in it that tracks the two files we added."),(0,i.kt)("p",null,"Let's run ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," again:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git status\nOn branch main\nUntracked files:\n (use "git add ..." to include in what will be committed)\n shell.d/\n\nnothing added to commit but untracked files present (use "git add" to track)\n')),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," command tells us we're still on the 'main' branch, and that there is one file which is not tracked. Let's create a second commit by adding this file. When you run the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," command below, enter a message to describe the commit:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git add .\n$ git commit\n[main d7e1bb9] add the 'shell.d' folder\n 1 file changed, 228 insertions(+)\n create mode 100644 shell.d/set_ps1.sh\n")),(0,i.kt)("p",null,"We have now created a second commit - our timeline will look like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing our second git commit",src:a(8853).Z,width:"2582",height:"1022"})),(0,i.kt)("p",null,"If we run the ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," command one last time we will see that everything in the working tree is tracked in Git:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git status\nOn branch main\nnothing to commit, working tree clean\n")),(0,i.kt)("p",null,"The concepts of the 'index', the 'working tree' and the Git repository itself can take a bit of getting used to! If you have not used Git before and this seems like a lot to take on board, don't worry, people often find Git quite hard at first. As you use it more this will all become familiar and make more sense."),(0,i.kt)("h3",{id:"commit-messages"},"Commit Messages"),(0,i.kt)("p",null,"You can use any text you like for a commit message. However, there are a couple of things that you should bear in mind:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Try to keep the first line to 50 characters or less. This is known as the 'subject' line - keeping this short will make it easier to look through the log of changes later and see what each commit means."),(0,i.kt)("li",{parentName:"ol"},"If you want to add more detail, leave a blank line after the subject line and then include as much text as you like. Common convention is to wrap the text at 80 characters so that it will fit in a typical shell window.")),(0,i.kt)("p",null,"There are many articles available online that suggest conventions for how to write your commit messages. You can explore these as you get more familiar with Git. The only convention I would strongly recommend that you follow is to make sure that your Git message makes sense - it should describe what the change is and ideally why you have made it. It is easy to forget why changes were made after time has passed - writing good commit messages will save you a lot of time in the long run and also make it easier for others to work with your repository!"),(0,i.kt)("p",null,"Now let's take a look at how we can work on changes to our files with ",(0,i.kt)("em",{parentName:"p"},"branches"),"."),(0,i.kt)("h2",{id:"creating-branches"},"Creating Branches"),(0,i.kt)("p",null,"The commits that we have made so far have been on a 'branch' named 'main'. We can create new 'branches' and put commits on them to allow us to make a series of changes that are isolated from each other."),(0,i.kt)("p",null,"We can create branches using the ",(0,i.kt)("inlineCode",{parentName:"p"},"git branch")," (",(0,i.kt)("em",{parentName:"p"},"list, create or delete branches"),") or ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," (",(0,i.kt)("em",{parentName:"p"},"switch branches or restore working tree"),") command",". To show these features in action, we'll create a new branch called ",(0,i.kt)("em",{parentName:"p"},"aliases")," and add some files to it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout -b aliases\nSwitched to a new branch 'aliases'\n$ git status\nOn branch aliases\nnothing to commit, working tree clean\n")),(0,i.kt)("p",null,"We have used the ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," command to 'switch' to another branch. The ",(0,i.kt)("inlineCode",{parentName:"p"},"-b")," (",(0,i.kt)("em",{parentName:"p"},"new branch"),") option tells Git that we want to create a new branch. The ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," command now shows the new branch name when we run it."),(0,i.kt)("p",null,"Let's create a new file which includes an alias for the ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," command, then let's see what ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," tells us about the status of the working tree:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ echo \'alias gs="git status"\' >> ./shell.d/git_aliases.sh\n$ git status\nOn branch aliases\nUntracked files:\n (use "git add ..." to include in what will be committed)\n shell.d/git_aliases.sh\n\nnothing added to commit but untracked files present (use "git add" to track)\n')),(0,i.kt)("p",null,"Excellent - we have a new file and Git knows that it is not currently tracked. Let's stage this file and then commit it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git add .\n$ git commit -m \"add alias 'gs' for 'git status'\"\n\n[aliases f61369d] add alias 'gs' for 'git status'\n 1 file changed, 1 insertion(+)\n create mode 100644 shell.d/git_aliases.sh\n")),(0,i.kt)("p",null,"In the example above I used the ",(0,i.kt)("inlineCode",{parentName:"p"},"-m")," (",(0,i.kt)("em",{parentName:"p"},"commit message"),") parameter for the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," command, this means my editor will not open up as we've already provided a commit message."),(0,i.kt)("p",null,"We now have a series of commits that looks like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing how 'git checkout -b' creates a new branch",src:a(7685).Z,width:"2136",height:"560"})),(0,i.kt)("p",null,"Our new ",(0,i.kt)("em",{parentName:"p"},"~/dotfiles/shell.d/git_aliases.sh")," file has been committed to the ",(0,i.kt)("em",{parentName:"p"},"aliases")," branch."),(0,i.kt)("p",null,"We can switch back to the ",(0,i.kt)("em",{parentName:"p"},"main")," branch at any time with ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout main\nSwitched to branch 'main'\n$ tree\n.\n\u251c\u2500\u2500 install.sh\n\u251c\u2500\u2500 shell.d\n\u2502\xa0\xa0 \u2514\u2500\u2500 set_ps1.sh\n\u2514\u2500\u2500 shell.sh\n")),(0,i.kt)("p",null,"When we switch back to the ",(0,i.kt)("em",{parentName:"p"},"main")," branch and look at our working tree we can see that the ",(0,i.kt)("em",{parentName:"p"},"git_aliases.sh")," file is not present. This is very cool - by passing the name of the branch we want to switch to as the parameter to the ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," command we can switch branches. If we are on the ",(0,i.kt)("em",{parentName:"p"},"main")," branch we don't see the ",(0,i.kt)("em",{parentName:"p"},"git_aliases.sh")," file, because the commit that added is was not on the ",(0,i.kt)("em",{parentName:"p"},"main")," branch. To go back to the ",(0,i.kt)("em",{parentName:"p"},"aliases")," branch we can just checkout again:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout aliases\nSwitched to branch 'aliases'\n$ tree\n.\n\u251c\u2500\u2500 install.sh\n\u251c\u2500\u2500 shell.d\n\u2502\xa0\xa0 \u251c\u2500\u2500 git_aliases.sh\n\u2502\xa0\xa0 \u2514\u2500\u2500 set_ps1.sh\n\u2514\u2500\u2500 shell.sh\n")),(0,i.kt)("p",null,"As a nice little tip, you can always go back to the ",(0,i.kt)("em",{parentName:"p"},"last")," branch you were on by running ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout -")," just like you can use ",(0,i.kt)("inlineCode",{parentName:"p"},"cd -")," to change to the last directory you visited! The dash character is a shortcut for the last branch you were on."),(0,i.kt)("p",null,"Let's add another alias to the file and create another commit:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo 'alias gcm=\"git checkout main\"' >> ./shell.d/git_aliases.sh\n$ git add .\n$ git commit -m \"add alias 'gcm' for 'git checkout main'\"\n[aliases b9ae0ad] add alias 'gcm' for 'git checkout main'\n 1 file changed, 1 insertion(+)\n")),(0,i.kt)("p",null,"Our branches will now look like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing two commits on the 'aliases' branch",src:a(7745).Z,width:"2140",height:"552"})),(0,i.kt)("p",null,"You can create as many branches as you like - just remember that when you run ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout -b"),", you branch from the ",(0,i.kt)("em",{parentName:"p"},"current")," branch (and in fact, the current ",(0,i.kt)("em",{parentName:"p"},"HEAD"),", which we will see a little later)."),(0,i.kt)("p",null,"If you want to create a branch, but don't want to switch to it, you can run ",(0,i.kt)("inlineCode",{parentName:"p"},"git branch "),". This command will create a branch from your current position, but will not move to it."),(0,i.kt)("h2",{id:"merging"},"Merging"),(0,i.kt)("p",null,"You can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git merge")," (",(0,i.kt)("em",{parentName:"p"},"join two or more branches"),") command"," to take the changes from one branch and bring them into another."),(0,i.kt)("p",null,"We can merge the changes from our ",(0,i.kt)("em",{parentName:"p"},"aliases")," branch into the ",(0,i.kt)("em",{parentName:"p"},"main")," branch of the repository by first checking out the branch we want to merge into, and then running ",(0,i.kt)("inlineCode",{parentName:"p"},"git merge"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout main\n$ git merge aliases\nUpdating d7e1bb9..b9ae0ad\nFast-forward\n shell.d/git_aliases.sh | 2 ++\n 1 file changed, 2 insertions(+)\n create mode 100644 shell.d/git_aliases.sh\n")),(0,i.kt)("p",null,"When we run the ",(0,i.kt)("inlineCode",{parentName:"p"},"git merge")," command Git tells us what ",(0,i.kt)("em",{parentName:"p"},"type")," of merge it has performed. In this case we have a ",(0,i.kt)("em",{parentName:"p"},"fast forward")," ","merge, which is the most simple type of merge. When Git tries to merge the two branches, it sees that each of the commits on the ",(0,i.kt)("em",{parentName:"p"},"aliases")," branch can be applied sequentially to the ",(0,i.kt)("em",{parentName:"p"},"main")," branch:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing how Git prepares for a 'fast forwards' merge",src:a(8506).Z,width:"2210",height:"492"})),(0,i.kt)("p",null,"One the merge is complete, our branches look like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing a fast forward merge result",src:a(4983).Z,width:"2120",height:"364"})),(0,i.kt)("p",null,"The ",(0,i.kt)("em",{parentName:"p"},"main")," branch and ",(0,i.kt)("em",{parentName:"p"},"aliases")," branch contain the exact same set of commits."),(0,i.kt)("p",null,"Let's look at a more common merge scenario - merging branches that have diverged."),(0,i.kt)("h3",{id:"merging-diverged-branches"},"Merging Diverged Branches"),(0,i.kt)("p",null,"Let's create set of commits that show a case where we our branches have ",(0,i.kt)("em",{parentName:"p"},"diverged")," - the branches both have their own new commits:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Create a branch called 'more_aliases', add a file to it, then commit.\ngit checkout -b more_aliases\ntouch ./shell.d/bash_aliases.sh\ngit add .\ngit commit -m \"add a file to store 'bash' aliases\"\n\n# Create another file, add it, then commit.\ntouch ./shell.d/zsh_aliases.sh\ngit add .\ngit commit -m \"add a file to store 'zsh' aliases\"\n")),(0,i.kt)("p",null,"This snippet checks out a new branch called ",(0,i.kt)("em",{parentName:"p"},"more_aliases")," and adds two new empty files, as two separate commits. Now we'll go back to our ",(0,i.kt)("em",{parentName:"p"},"main")," branch and change a file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Go back to the 'main' branch, add and commit another file.\ngit checkout main\necho 'alias gm=\"git merge\"' >> ./shell.d/git_aliases.sh\ngit commit -a -m \"add the 'gm' alias for 'git merge'\"\n")),(0,i.kt)("p",null,"I have added a new alias to the ",(0,i.kt)("em",{parentName:"p"},"shell.d/git_aliases.sh")," file. By adding the ",(0,i.kt)("inlineCode",{parentName:"p"},"-a")," (",(0,i.kt)("em",{parentName:"p"},"all changes"),") flag to the ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," command I was able to add the changes to the index and commit them with a single command."),(0,i.kt)("p",null,"Our branches now look like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"A diagram showing two diverged branches",src:a(3763).Z,width:"2136",height:"548"})),(0,i.kt)("p",null,"Let's merge the ",(0,i.kt)("em",{parentName:"p"},"more_aliases")," branch into the ",(0,i.kt)("em",{parentName:"p"},"main")," branch:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git merge more_aliases\n")),(0,i.kt)("p",null,"At this point your shell editor will open up with a request for a commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"Merge branch 'more_aliases'\n# Please enter a commit message to explain why this merge is needed\n# especially if it merges an updated upstream into a topic branch.\n#\n# Lines starting with '#' will be ignored, and an empty message will abort\n# the commit.\n")),(0,i.kt)("p",null,"Git is going to create a new commit on the ",(0,i.kt)("em",{parentName:"p"},"main")," branch that brings in the changes from the ",(0,i.kt)("em",{parentName:"p"},"bash_aliases")," branch. Because a new commit is going to be created, Git asks us to provide a message. The default message simply explains that this commit merges the branch named ",(0,i.kt)("em",{parentName:"p"},"more_aliases"),"."),(0,i.kt)("p",null,"You can change the message or leave it as is. Save the file when you have entered the message and the output below will be shown:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git merge more_aliases\nMerge made by the 'recursive' strategy.\n shell.d/bash_aliases.sh | 0\n shell.d/zsh_aliases.sh | 0\n 2 files changed, 0 insertions(+), 0 deletions(-)\n create mode 100644 shell.d/bash_aliases.sh\n create mode 100644 shell.d/zsh_aliases.sh\n")),(0,i.kt)("p",null,"Git now tells us that we have made a ",(0,i.kt)("em",{parentName:"p"},"recursive")," merge and our branches will look like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing the results of a recursive merge of two diverged branches",src:a(4876).Z,width:"2352",height:"502"})),(0,i.kt)("p",null,"At this stage both the ",(0,i.kt)("em",{parentName:"p"},"main")," branch and the ",(0,i.kt)("em",{parentName:"p"},"more_aliases")," branch have the full set of changes that we made to ",(0,i.kt)("em",{parentName:"p"},"both")," of the branches. Git merged the two branches together and created a new commit that joins them."),(0,i.kt)("h3",{id:"the-git-log"},"The Git Log"),(0,i.kt)("p",null,"In the diagrams we've shown we've given each commit a number. This was just to make things easier to read - Git doesn't use a number for commits. Git uses a ",(0,i.kt)("em",{parentName:"p"},"SHA"),", which is a hash. A hash is a sequence of letters and numbers that uniquely identify each commit."),(0,i.kt)("p",null,"You can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git log")," (",(0,i.kt)("em",{parentName:"p"},"show commit logs"),") command"," to see the log of commits and their SHAs:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git log\ncommit 138b40418d5658bc64421e7bcf2680c8339f8350 (HEAD)\nMerge: a95bd90 a51ae1a\nAuthor: Dave Kerr \nDate: Tue Jun 15 21:00:28 2021 +0800\n\n Merge branch 'more_aliases'\n\ncommit a95bd90e3656b2e55b8708193d387c80c282a6ad\nAuthor: Dave Kerr \nDate: Tue Jun 15 21:00:22 2021 +0800\n\n add the 'gm' alias for 'git merge'\n\ncommit a51ae1aa42432c2f391ca782c1c20b3793c232ab (more_aliases)\nAuthor: Dave Kerr \nDate: Tue Jun 15 20:53:01 2021 +0800\n\n add a file to store 'zsh' aliases\n")),(0,i.kt)("p",null,"You can see that the log shows each of the commits, the branch the commit was on, the message, the date and so on."),(0,i.kt)("p",null,"If you want to see a more compact log you can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"--oneline")," (",(0,i.kt)("em",{parentName:"p"},"show one line per commit"),") flag:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git log --oneline\n138b404 (HEAD) Merge branch 'more_aliases'\na95bd90 add the 'gm' alias for 'git merge'\na51ae1a (more_aliases) add a file to store 'zsh' aliases\n63ea74f add a file to store 'bash' aliases\nb9ae0ad (aliases) add alias 'gcm' for 'git checkout main'\nf61369d add alias 'gs' for 'git status'\nd7e1bb9 add the 'shell.d' folder\n01e7a10 add the 'install' and 'shell' scripts\n")),(0,i.kt)("p",null,"When you run this command yourself it will be a little easier to read as the output uses different colours for the SHAs and the branch names."),(0,i.kt)("p",null,"We can even see a 'graph' view, showing the branches we have made and when they branched off and were merged back. To do this, pass the ",(0,i.kt)("inlineCode",{parentName:"p"},"--graph")," (",(0,i.kt)("em",{parentName:"p"},"show commit graph"),") flag:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git log --oneline --graph\n* 138b404 (HEAD) Merge branch 'more_aliases'\n| \\\n| * a51ae1a (more_aliases) add a file to store 'zsh' aliases\n| * 63ea74f add a file to store 'bash' aliases\n* | a95bd90 add the 'gm' alias for 'git merge'\n|/\n* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'\n* f61369d add alias 'gs' for 'git status'\n* d7e1bb9 add the 'shell.d' folder\n* 01e7a10 add the 'install' and 'shell' scripts\n")),(0,i.kt)("p",null,"Each commit is shown with an ",(0,i.kt)("inlineCode",{parentName:"p"},"*")," asterisk symbol - we can also see when we created the ",(0,i.kt)("em",{parentName:"p"},"more_aliases")," branch and when we merged it back in."),(0,i.kt)("p",null,"The Git log is very useful to help you understand the changes that have happened in the repository."),(0,i.kt)("h3",{id:"merge-conflicts"},"Merge Conflicts"),(0,i.kt)("p",null,"One of the most important features of any version control system is the ability to manage ",(0,i.kt)("em",{parentName:"p"},"conflicts"),". Conflicts occur when a set of changes are made that cannot be merged without some kind of manual intervention to decide ",(0,i.kt)("em",{parentName:"p"},"which")," of the changes are correct."),(0,i.kt)("p",null,"Here are a few common scenarios that might lead to merge conflicts:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"In one branch a file is deleted and in another branch the file is changed - when we merge should we delete the file or keep the version with the changes?"),(0,i.kt)("li",{parentName:"ul"},"In one branch a file is edited and in another branch the ",(0,i.kt)("em",{parentName:"li"},"same")," part of the file is edited in a different way - which edit should we keep? Should we keep both?"),(0,i.kt)("li",{parentName:"ul"},"In one branch we add content to the end of a file and in another branch we add different content - which of these changes should come first?")),(0,i.kt)("p",null,"A lot of the time you can avoid conflicts by making sure that you don't keep branches for too long - if other people are merging changes into the ",(0,i.kt)("em",{parentName:"p"},"main")," branch while you are working on another branch, you are ",(0,i.kt)("em",{parentName:"p"},"drifting")," from the main branch. You should either regularly update your branch with the changes in ",(0,i.kt)("em",{parentName:"p"},"main")," or merge your changes into ",(0,i.kt)("em",{parentName:"p"},"main"),"."),(0,i.kt)("p",null,"There are many different ways for version control systems to manage conflicts. Let's see how Git does it by creating a conflict."),(0,i.kt)("p",null,"First, we will create a branch that adds a new alias to the ",(0,i.kt)("em",{parentName:"p"},"git_aliases.sh")," file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Create a 'glog_alias' branch and commit a file.\ngit checkout -b glog_alias\necho 'alias glog=\"git log --graph --oneline\"' >> ./shell.d/git_aliases.sh\ngit commit -a -m \"add the 'glog' alias\"\n")),(0,i.kt)("p",null,"Now we'll go back to the ",(0,i.kt)("em",{parentName:"p"},"main")," branch and add another alias:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# Go back to 'main' and commit a change to the same file as the last one.\ngit checkout main\necho 'alias glog=\"git log\"' >> ./shell.d/git_aliases.sh\ngit commit -a -m \"add the 'glog' alias\"\n")),(0,i.kt)("p",null,"We've changed the same file in two branches - if we try to merge we will get a conflict:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git merge glog_alias\n\nAuto-merging shell.d/git_aliases.sh\nCONFLICT (content): Merge conflict in shell.d/git_aliases.sh\nAutomatic merge failed; fix conflicts and then commit the result.\n")),(0,i.kt)("p",null,"When Git cannot automatically consolidate the changes into a single merge commit, it aborts the merge process. No new commits have been made - the conflicted files with their changes are in the ",(0,i.kt)("em",{parentName:"p"},"index"),". We have to now manually fix these files."),(0,i.kt)("p",null,"Let's see what the status shows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git status\nOn branch main\nYou have unmerged paths.\n (fix conflicts and run "git commit")\n (use "git merge --abort" to abort the merge)\n\nUnmerged paths:\n (use "git add ..." to mark resolution)\n both modified: shell.d/git_aliases.sh\n\nno changes added to commit (use "git add" and/or "git commit -a")\n')),(0,i.kt)("p",null,"Git is telling us that we are currently in the process of trying to fix a merge conflict. It is telling us that we need to fix the ",(0,i.kt)("em",{parentName:"p"},"shell.d/git_aliases.sh")," file, then use ",(0,i.kt)("inlineCode",{parentName:"p"},"git add")," to stage the changes and commit the result."),(0,i.kt)("p",null,"To resolve the conflict, we need to edit the file. If you open the file in an editor it will look like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'alias gs="git status"\nalias gcm="git checkout main"\nalias gm="git merge"\n<<<<<<< HEAD\nalias glog="git log"\n=======\nalias glog="git log --graph --oneline"\n>>>>>>> glog_alias\n')),(0,i.kt)("p",null,"Many modern editors will immediately recognise the sequence of symbols that Git uses to indicate conflicts and highlight them. The ",(0,i.kt)("inlineCode",{parentName:"p"},"<<<<<< HEAD")," line indicates the changes in the current branch, the ",(0,i.kt)("inlineCode",{parentName:"p"},"=======")," line is a separator, and the ",(0,i.kt)("inlineCode",{parentName:"p"},">>>>>> glog_alias")," line indicates that everything since the ",(0,i.kt)("inlineCode",{parentName:"p"},"=======")," line is the changes in the ",(0,i.kt)("em",{parentName:"p"},"glog_alias")," branch."),(0,i.kt)("p",null,"We can see why Git has not been able to merge these changes - each branch has a new line and Git doesn't know which one is correct. So rather than make an assumption (such as the most recent change should 'win'), Git is asking us to choose."),(0,i.kt)("p",null,"In your editor, update the file to look like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'alias gs="git status"\nalias gcm="git checkout main"\nalias gm="git merge"\nalias glog="git log --graph --oneline"\n')),(0,i.kt)("p",null,"We have chosen the version of the ",(0,i.kt)("inlineCode",{parentName:"p"},"glog")," alias that was in the ",(0,i.kt)("em",{parentName:"p"},"glog_alias")," branch. But you could choose either - or replace the content with a new line. You can make any changes you like - just be sure to remove the lines that start with ",(0,i.kt)("inlineCode",{parentName:"p"},"<<<"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"===")," and ",(0,i.kt)("inlineCode",{parentName:"p"},">>>"),". We don't even have to use the changes from one of the branches - we could remove the lines or add completely new ones. We change the file to make sure that the merged result makes sense."),(0,i.kt)("p",null,"In the example above we deleted one of the ",(0,i.kt)("inlineCode",{parentName:"p"},"glog")," aliases, but we could have also just changed the name of one of them:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'alias gs="git status"\nalias gcm="git checkout main"\nalias gm="git merge"\nalias glog="git log"\nalias ggraph="git log --graph --oneline"\n')),(0,i.kt)("p",null,"We can now use ",(0,i.kt)("inlineCode",{parentName:"p"},"git add")," to mark the file as resolved, and run ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git add shell.d/git_aliases.sh\n")),(0,i.kt)("p",null,"The editor will open showing a sensible commit message:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"Merge branch 'glog_alias'\n\n# Conflicts:\n# shell.d/git_aliases.sh\n#\n# It looks like you may be committing a merge.\n# If this is not correct, please run\n# git update-ref -d MERGE_HEAD\n# and try again.\n\n\n# Please enter the commit message for your changes. Lines starting\n# with '#' will be ignored, and an empty message aborts the commit.\n#\n# On branch main\n")),(0,i.kt)("p",null,"The message is just like the earlier merge commit message, but the comments show a little more information (the files that were conflicted). Save the file and close the editor to complete the commit."),(0,i.kt)("p",null,"Our Git log will now show this new merge commit:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git log --graph --oneline\n* 2532277 (HEAD -> main) Merge branch 'glog_alias'\n|\\\n| * a8cbb15 (glog_alias) add the 'glog' alias\n* | 31548e4 add the 'glog' alias\n|/\n* 138b404 Merge branch 'more_aliases'\n|\\\n| * a51ae1a (more_aliases) add a file to store 'zsh' aliases\n| * 63ea74f add a file to store 'bash' aliases\n* | a95bd90 add the 'gm' alias for 'git merge'\n|/\n* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'\n* f61369d add alias 'gs' for 'git status'\n* d7e1bb9 add the 'shell.d' folder\n* 01e7a10 add the 'install' and 'shell' scripts\n")),(0,i.kt)("p",null,"Dealing with conflicts can be extremely complicated. We have only scratched the surface here, but there is a wealth of information online if you'd like to go deeper."),(0,i.kt)("h3",{id:"other-merge-strategies"},"Other Merge Strategies"),(0,i.kt)("p",null,"Git has a number of merge strategies that can be used to combine the changes across branches. Going into them is beyond the scope of this book. However, it is useful to know that you have a lot of functionality to control how branches are merged available to you if you want to explore further."),(0,i.kt)("p",null,"There are merge strategies that allow you to try and create a single, coherent history between two branches, rather than creating a merge commit, there are options to 'squash' all of the commits from one branch into another and more."),(0,i.kt)("p",null,'I would suggest that as you become more familiar with the basics of how Git works, you get a little deeper on this topic - searching online for "Git Merge Strategies" will bring up many articles going into detail.'),(0,i.kt)("h2",{id:"deleting-and-renaming-files"},"Deleting and Renaming Files"),(0,i.kt)("p",null,"It is quite simple to remove files from your Git Repository. You can either ask Git to remove the file for you, or just delete the file yourself and then tell Git that you have removed it."),(0,i.kt)("p",null,"Let's see both ways in action. First we'll use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git rm")," (",(0,i.kt)("em",{parentName:"p"},"remove files from the working tree and index"),") command",":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git rm install.sh\nrm 'install.sh'\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"git rm")," command removes the file from your working tree and stages the deletion. We can see that the file is staged for deletion if we run ",(0,i.kt)("inlineCode",{parentName:"p"},"git status"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git status\nOn branch main\nChanges to be committed:\n (use "git restore --staged ..." to unstage)\n deleted: install.sh\n')),(0,i.kt)("p",null,"The deletion is only in the ",(0,i.kt)("em",{parentName:"p"},"index")," at this stage - we need to run ",(0,i.kt)("inlineCode",{parentName:"p"},"git commit")," to commit the deletion."),(0,i.kt)("p",null,"If we want to restore the file, we can use ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," to get the file back from Git:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout HEAD install.sh\n")),(0,i.kt)("p",null,"We use ",(0,i.kt)("inlineCode",{parentName:"p"},"HEAD")," to tell Git what commit we want to checkout the file from - head means the current commit we are on (which is the most recent commit on ",(0,i.kt)("em",{parentName:"p"},"main"),")."),(0,i.kt)("p",null,"Another common way to delete a file is to simply remove it from the file-system:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ rm install.sh\n$ git status\nOn branch main\nChanges not staged for commit:\n (use "git add/rm ..." to update what will be committed)\n (use "git restore ..." to discard changes in working directory)\n deleted: install.sh\n')),(0,i.kt)("p",null,"At this point Git knows the file is missing, but the deletion is not yet ",(0,i.kt)("em",{parentName:"p"},"staged")," - because we haven't explicitly told Git that we want to remove this file as part of the commit we are creating. To confirm that we want to remove the file we can use:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git rm install.sh\n")),(0,i.kt)("p",null,"We could also run ",(0,i.kt)("inlineCode",{parentName:"p"},"git add install.sh")," - think of this like saying 'stage my change to ",(0,i.kt)("em",{parentName:"p"},"index.sh"),"' - we are ",(0,i.kt)("em",{parentName:"p"},"adding")," a change to the ",(0,i.kt)("em",{parentName:"p"},"index"),". I often just run ",(0,i.kt)("inlineCode",{parentName:"p"},"git add .")," to add all of my changes (including deletions) to the stage. At this point we can make any other changes we like, stage them, and then commit."),(0,i.kt)("p",null,"Let's restore the file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git reset .\nUnstaged changes after reset:\nD install.sh\n$ git checkout .\nUpdated 1 path from the index\n")),(0,i.kt)("p",null,"This is another way to restore the file - first we reset all of the changes to the index with ",(0,i.kt)("inlineCode",{parentName:"p"},"git reset ."),", then we checkout all of the files in the current commit with ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout ."),". But be careful with this method - it will also reset any other changes you have made. I prefer the ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout HEAD ")," method as it is more explicit and only restores one file."),(0,i.kt)("p",null,"What about if we rename a file? Let's rename the install file and see what the status is:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ mv install.sh install_dotfiles.sh\nChanges not staged for commit:\n (use "git add/rm ..." to update what will be committed)\n (use "git restore ..." to discard changes in working directory)\n deleted: install.sh\n\nUntracked files:\n (use "git add ..." to include in what will be committed)\n install_dotfiles.sh\n\nno changes added to commit (use "git add" and/or "git commit -a")\n')),(0,i.kt)("p",null,"We've renamed the file, but when we run ",(0,i.kt)("inlineCode",{parentName:"p"},"git status")," it is telling us that the file is missing, and there is a new untracked file. But Git is smart enough to know when we move a file - if we run ",(0,i.kt)("inlineCode",{parentName:"p"},"git add .")," to add all changes in the working tree to the index, Git will recognise that we have not deleted and added a file, but instead renamed it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git add .\n$ git status\nOn branch main\nChanges to be committed:\n (use "git restore --staged ..." to unstage)\n renamed: install.sh -> install_dotfiles.sh\n')),(0,i.kt)("p",null,"Let's restore the file by renaming it back to what it was and adding these changes:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ mv install_dotfiles.sh install.sh\n$ git add .\n$ git status\nOn branch main\nnothing to commit, working tree clean\n")),(0,i.kt)("p",null,"We can also use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git mv")," (",(0,i.kt)("em",{parentName:"p"},"move or rename a file"),") command"," to move or rename a file and stage the changes in one go:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ git mv install.sh install_dotfiles.sh\nChanges to be committed:\n (use "git restore --staged ..." to unstage)\n renamed: install.sh -> install_dotfiles.sh\n')),(0,i.kt)("p",null,"We can also restore the changes by using ",(0,i.kt)("inlineCode",{parentName:"p"},"git mv"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git mv install_dotfiles.sh install.sh\n$ git status\nOn branch main\nnothing to commit, working tree clean\n")),(0,i.kt)("p",null,"These commands work equally well with folders, or lists of files and folders."),(0,i.kt)("h2",{id:"restoring-your-working-tree"},"Restoring Your Working Tree"),(0,i.kt)("p",null,"We've already seen how the ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," command can be used to switch branches. We can also use the ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout")," command to restore our working tree to a certain point in our commit history. This is extremely useful if you want to see how your files looked at an earlier point in time, or restore your files to a previous state."),(0,i.kt)("p",null,"Let's take a quick look at our Git log so far."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git log --graph --oneline\n* 2532277 (HEAD -> main) Merge branch 'glog_alias'\n|\\\n| * a8cbb15 (glog_alias) add the 'glog' alias\n* | 31548e4 add the 'glog' alias\n|/\n* 138b404 Merge branch 'more_aliases'\n|\\\n| * a51ae1a (more_aliases) add a file to store 'zsh' aliases\n| * 63ea74f add a file to store 'bash' aliases\n* | a95bd90 add the 'gm' alias for 'git merge'\n|/\n* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'\n* f61369d add alias 'gs' for 'git status'\n* d7e1bb9 add the 'shell.d' folder\n* 01e7a10 add the 'install' and 'shell' scripts\n")),(0,i.kt)("p",null,"The Git log is showing the first seven digits of the SHA for each commit. We can checkout any commit by providing its SHA. We don't need to provide the entire SHA (which is good, because they are normally quite long), we only need to provide enough letters to uniquely identify the commit, so normally four or five digits is plenty."),(0,i.kt)("p",null,"Notice that the most recent commit is marked with the text ",(0,i.kt)("inlineCode",{parentName:"p"},"HEAD"),". The ",(0,i.kt)("em",{parentName:"p"},"HEAD")," is where Git is currently 'pointing' to - we will see how to move the head shortly (the term ",(0,i.kt)("em",{parentName:"p"},"HEAD")," also refers to the most recent commit of a branch)."),(0,i.kt)("p",null,"At the moment we can visualise our Git log, with the commits, branches and HEAD like so:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing our Git log with HEAD indicated",src:a(5524).Z,width:"2992",height:"854"})),(0,i.kt)("p",null,"We can restore the working tree to the state of any of the commits by running ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout