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 @@ + + + + + +Page Not Found | Effective Shell + + + + + + + + + + + + + + +
+
Skip to main content

Page Not Found

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.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/CNAME b/pr-preview/pr-346/CNAME new file mode 100644 index 00000000..8da851d3 --- /dev/null +++ b/pr-preview/pr-346/CNAME @@ -0,0 +1 @@ +effective-shell.com diff --git a/pr-preview/pr-346/ads.txt b/pr-preview/pr-346/ads.txt new file mode 100644 index 00000000..0ba6f9b4 --- /dev/null +++ b/pr-preview/pr-346/ads.txt @@ -0,0 +1 @@ +google.com, pub-6181461532532600, DIRECT, f08c47fec0942fa0 diff --git a/pr-preview/pr-346/appendices/thanks/index.html b/pr-preview/pr-346/appendices/thanks/index.html new file mode 100644 index 00000000..d3db8eba --- /dev/null +++ b/pr-preview/pr-346/appendices/thanks/index.html @@ -0,0 +1,26 @@ + + + + + +Thanks | Effective Shell + + + + + + + + + + + + + + +
+
Skip to main content
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/assets/css/styles.51892cac.css b/pr-preview/pr-346/assets/css/styles.51892cac.css new file mode 100644 index 00000000..18abce0c --- /dev/null +++ b/pr-preview/pr-346/assets/css/styles.51892cac.css @@ -0,0 +1 @@ +.col,.container{padding:0 var(--ifm-spacing-horizontal);width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}.menu,.navbar-sidebar{overflow-x:hidden}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.button,code{vertical-align:middle}.toggleButton_gllP,html{-webkit-tap-highlight-color:transparent}.clean-list,.containsTaskList_mC6p,.details_lb9f>summary,.dropdown__menu,.menu__list{list-style:none}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:#0000;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:#0000000d;--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 #0000001a;--ifm-global-shadow-md:0 5px 40px #0003;--ifm-global-shadow-tl:0 12px 28px 0 #0003,0 2px 4px 0 #0000001a;--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:#0000;--ifm-table-stripe-background:#00000008;--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem;--docusaurus-progress-bar-color:var(--ifm-color-primary);--docusaurus-announcement-bar-height:auto;--docusaurus-tag-list-border:var(--ifm-color-emphasis-300);--docusaurus-collapse-button-bg:#0000;--docusaurus-collapse-button-bg-hover:#0000001a;--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:#0000}*,.subscribe-container,.subscribe-container *{box-sizing:border-box}html{-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base);text-rendering:optimizelegibility}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_NmtK,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid #0000001a;border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:initial;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_EoeP .wordWrapButtonIcon_Bwma{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_tbUL,.alert__heading,.playgroundHeader_dyrN,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.asciinema-terminal .italic,.text--italic{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:#3578e526;--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:#ebedf026;--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:#00a40026;--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:#54c7ec26;--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:#ffba0026;--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:#fa383e26;--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.asciinema-player .start-prompt .play-button div,.asciinema-player .start-prompt .play-button div span svg,.card--full-height,.navbar__logo img,body,html{height:100%}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.asciinema-terminal .line .cursor-a,.asciinema-terminal.cursor .line .cursor-b,.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after,.searchBarContainer_NW3z.searchIndexLoading_EJ1f .searchBarLoadingRing_YnHq{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:any-link:hover,.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area.breadcrumbs__link[href]:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;-webkit-user-select:none;user-select:none;white-space:nowrap}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:#0000;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus,.theme-code-block-highlighted-line .codeLineNumber_Tfdd:before{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;pointer-events:none;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor #0000;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.menu,.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.docItemContainer_Djhp article>:first-child,.docItemContainer_Djhp header+*,.footer__item{margin-top:0}.admonitionContent_S0QG>:last-child,.collapsibleContent_i85q>:last-child,.footer__items,.searchResultItem_U687>h2,.subscribe-container form{margin-bottom:0}.codeBlockStandalone_MEMb,[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_lb9f[data-collapsed=false].isBrowser_bmU9>summary:before,.details_lb9f[open]:not(.isBrowser_bmU9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.navbar-sidebar,.navbar-sidebar__backdrop{bottom:0;opacity:0;transition-timing-function:ease-in-out;top:0;left:0;visibility:hidden}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist-caret:after{height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast) linear;width:1.25rem;filter:var(--ifm-menu-link-sublist-icon-filter);content:""}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.docsWrapper_BCFX,.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.announcementBarContent_xLdY,.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}#nprogress,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:#ffffff1a;--ifm-navbar-search-input-placeholder-color:#ffffff80;color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:#ffffff0d;--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);position:fixed;transform:translate3d(-100%,0,0);transition-duration:.25s;transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:#0009;position:fixed;right:0;transition-duration:.1s;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.asciinema-player-wrapper .title-bar a:hover,.pagination__link:hover{text-decoration:none}.pagination-nav{grid-gap:var(--ifm-spacing-horizontal);display:grid;gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.asciinema-terminal .underline,.content_knG7 a,.hitFooter_E9YW a,.suggestion_fB_2.cursor_eG29 mark{text-decoration:underline}.pagination-nav__link--next{grid-column:2/3;text-align:right}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"Β« "}.pagination-nav__link--next .pagination-nav__label:after{content:" Β»"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs,:not(.containsTaskList_mC6p>li)>.containsTaskList_mC6p{padding-left:0}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto}.tabs__item{border-bottom:3px solid #0000;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:#ffffff0d;--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:#ffffff1a;--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:#ffffff12;--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}.subscribe-container{background:#f9fafb;border:1px solid #e6e6e6;border-radius:4px;box-shadow:0 2px 15px 0 #d2d6dc4d;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-size:16px;line-height:1.5;margin-bottom:2rem;max-width:600px;padding:1.25rem 1rem;width:100%}.subscribe-container__title{color:#000;display:inline-block;font-size:1.75rem;line-height:1.2;margin-bottom:.5rem}.subscribe-container__subtitle{color:#777;font-size:1rem;margin-block-start:0;margin-bottom:1rem;padding-bottom:0}.subscribe-container__email{background:#fff;border:1px solid #e6e6e6;border-radius:4px;color:#000;flex:1 0 auto;font-size:15px;font-weight:400;line-height:1.4;margin:0 0 1rem;overflow:visible;padding:12px;transition:border-color .3s ease-out;width:100%}.subscribe-container__submit{background-color:#4caf50;border-radius:8px;color:#fff;cursor:pointer;font-size:1.2rem;font-weight:700;padding:12px 18px;transition:background-color .3s ease-in-out;width:100%}.subscribe-container__submit:hover{background-color:#3d8b40}.asciinema-player-wrapper{display:flex;height:100%;justify-content:center;outline:0}.asciinema-player-wrapper .title-bar{background-color:#000c;box-sizing:initial;color:#fff;display:none;font-family:sans-serif;font-size:20px;left:0;line-height:1em;padding:15px;position:absolute;right:0;top:-78px;transition:top .15s linear}.asciinema-player-wrapper .title-bar img{height:48px;margin-right:16px;vertical-align:middle}.asciinema-player-wrapper .title-bar a{color:#fff;text-decoration:underline}.asciinema-player-wrapper:fullscreen{align-items:center;background-color:#000;width:100%}.asciinema-player-wrapper:fullscreen .asciinema-player{position:static}.asciinema-player-wrapper:fullscreen .title-bar{display:initial}.asciinema-player-wrapper:fullscreen.hud .title-bar{top:0}.asciinema-player-wrapper:-webkit-full-screen{align-items:center;background-color:#000;width:100%}.asciinema-player-wrapper:-webkit-full-screen .asciinema-player{position:static}.asciinema-player-wrapper:-webkit-full-screen .title-bar{display:initial}.asciinema-player-wrapper:-webkit-full-screen.hud .title-bar{top:0}.asciinema-player-wrapper:-moz-full-screen{align-items:center;background-color:#000;width:100%}.asciinema-player-wrapper:-moz-full-screen .asciinema-player{position:static}.asciinema-player-wrapper:-moz-full-screen .title-bar{display:initial}.asciinema-player-wrapper:-moz-full-screen.hud .title-bar{top:0}.asciinema-player-wrapper:-ms-fullscreen{align-items:center;background-color:#000;width:100%}.asciinema-player-wrapper:-ms-fullscreen .asciinema-player{position:static}.asciinema-player-wrapper:-ms-fullscreen .title-bar{display:initial}.asciinema-player-wrapper:-ms-fullscreen.hud .title-bar{top:0}.asciinema-player-wrapper .asciinema-player{border-radius:4px;display:inline-block;font-size:12px;max-width:100%;position:relative;text-align:left}.asciinema-player-wrapper .asciinema-player,.asciinema-terminal{box-sizing:initial;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;overflow:hidden;padding:0}.asciinema-terminal{word-wrap:normal;border:.75em solid;border-radius:0;cursor:text;display:block;font-family:Consolas,Menlo,Bitstream Vera Sans Mono,monospace,Powerline Symbols;margin:0;white-space:pre;word-break:normal}.asciinema-terminal .line{letter-spacing:normal;overflow:hidden;display:block;width:200%}.asciinema-terminal .line span{display:inline-block;height:100%;padding:0}.asciinema-terminal .line .cursor-b{border-radius:.05em;display:none}.asciinema-terminal .line .blink{visibility:hidden}.asciinema-player .control-bar .fullscreen-button svg:last-child,.asciinema-player .control-bar .timer .time-remaining,.asciinema-player .control-bar .timer:hover .time-elapsed,.asciinema-terminal.cursor .line .cursor-a{display:none}.asciinema-terminal.blink .line .blink{visibility:visible}.asciinema-terminal .bright,.asciinema-theme-asciinema .fg-10,.asciinema-theme-asciinema .fg-11,.asciinema-theme-asciinema .fg-12,.asciinema-theme-asciinema .fg-13,.asciinema-theme-asciinema .fg-14,.asciinema-theme-asciinema .fg-15,.asciinema-theme-asciinema .fg-8,.asciinema-theme-asciinema .fg-9,.asciinema-theme-monokai .fg-10,.asciinema-theme-monokai .fg-11,.asciinema-theme-monokai .fg-12,.asciinema-theme-monokai .fg-13,.asciinema-theme-monokai .fg-14,.asciinema-theme-monokai .fg-15,.asciinema-theme-monokai .fg-8,.asciinema-theme-monokai .fg-9,.asciinema-theme-seti .fg-10,.asciinema-theme-seti .fg-11,.asciinema-theme-seti .fg-12,.asciinema-theme-seti .fg-13,.asciinema-theme-seti .fg-14,.asciinema-theme-seti .fg-15,.asciinema-theme-seti .fg-8,.asciinema-theme-seti .fg-9,.asciinema-theme-tango .fg-10,.asciinema-theme-tango .fg-11,.asciinema-theme-tango .fg-12,.asciinema-theme-tango .fg-13,.asciinema-theme-tango .fg-14,.asciinema-theme-tango .fg-15,.asciinema-theme-tango .fg-8,.asciinema-theme-tango .fg-9{font-weight:700}.asciinema-terminal .strikethrough{text-decoration:line-through}.asciinema-player .control-bar{-webkit-touch-callout:none;background:#000c;background:linear-gradient(180deg,#00000080 0,#000 25%,#000);bottom:-35px;box-sizing:initial;color:#bbb;height:32px;left:0;line-height:1;position:absolute;transition:bottom .15s linear;-webkit-user-select:none;user-select:none;width:100%;z-index:30}.asciinema-player .control-bar *{box-sizing:inherit;font-size:0}.asciinema-player .control-bar svg.icon path{fill:#bbb}.asciinema-player .control-bar .playback-button{cursor:pointer;display:block;float:left;height:12px;padding:10px;width:12px}.asciinema-player .control-bar .playback-button svg{height:12px;width:12px}.asciinema-player .control-bar .timer{cursor:default;display:block;float:left;font-family:Helvetica,Arial,sans-serif;font-size:11px;font-weight:700;height:100%;line-height:32px;text-align:center;width:50px}.asciinema-player .control-bar .timer span{display:inline-block;font-size:inherit}.asciinema-player .control-bar .fullscreen-button svg:first-child,.asciinema-player .control-bar .timer:hover .time-remaining{display:inline}.asciinema-player .control-bar .progressbar{display:block;height:100%;overflow:hidden;padding:0 10px}.asciinema-player .control-bar .progressbar .bar{cursor:default;display:block;font-size:0;height:100%;padding-top:15px}.asciinema-player .control-bar .progressbar .bar .gutter{background-color:#333;display:block;height:3px}.asciinema-player .control-bar .progressbar .bar .gutter span{background-color:#bbb;border-radius:3px;display:inline-block;height:100%}.asciinema-player .control-bar.seekable .progressbar .bar{cursor:pointer}.asciinema-player .control-bar .fullscreen-button{cursor:pointer;display:block;float:right;height:14px;padding:9px;width:14px}.asciinema-player .control-bar .fullscreen-button svg{height:14px;width:14px}.asciinema-player-wrapper.hud .control-bar{bottom:0}.asciinema-player-wrapper:fullscreen .fullscreen-button svg:first-child{display:none}.asciinema-player-wrapper:fullscreen .fullscreen-button svg:last-child{display:inline}.asciinema-player-wrapper:-webkit-full-screen .fullscreen-button svg:first-child{display:none}.asciinema-player-wrapper:-webkit-full-screen .fullscreen-button svg:last-child{display:inline}.asciinema-player-wrapper:-moz-full-screen .fullscreen-button svg:first-child{display:none}.asciinema-player-wrapper:-moz-full-screen .fullscreen-button svg:last-child{display:inline}.asciinema-player-wrapper:-ms-fullscreen .fullscreen-button svg:first-child{display:none}.asciinema-player-wrapper:-ms-fullscreen .fullscreen-button svg:last-child{display:inline}.asciinema-player .loading{background-color:#00000080}.asciinema-player .loading,.asciinema-player .start-prompt{background-position:50%;background-repeat:no-repeat;bottom:0;left:0;position:absolute;right:0;top:0;z-index:10}.asciinema-player .start-prompt{cursor:pointer;z-index:20}.asciinema-player .start-prompt .play-button{bottom:0;color:#fff;font-size:0;height:80px;left:0;margin:auto;max-height:66%;position:absolute;right:0;text-align:center;top:0}.asciinema-player .start-prompt .play-button div span{display:block;height:100%}@keyframes a{0%{transform:scale(0)}50%{transform:scale(1)}to{z-index:1}}.loader{background-color:#fff;box-shadow:0 0 0 6.66667px #141414;height:40px;margin:-20px 0 0 -20px;width:40px}.loader,.loader:after,.loader:before{border-radius:50%;left:50%;position:absolute;top:50%}.loader:after,.loader:before{content:"";display:block;height:42px;margin:-21px 0 0 -21px;width:42px;z-index:2}.loader:before{animation:1.6s linear infinite both a;background-color:#141414}.loader:after{animation:1.6s linear .8s infinite both a;background-color:#fff}.asciinema-terminal .fg-16,.asciinema-theme-asciinema .fg-0,.asciinema-theme-tango .fg-0{color:#000}.asciinema-terminal .bg-16,.asciinema-theme-asciinema .bg-0,.asciinema-theme-tango .bg-0{background-color:#000}.asciinema-terminal .fg-17{color:#00005f}.asciinema-terminal .bg-17{background-color:#00005f}.asciinema-terminal .fg-18{color:#000087}.asciinema-terminal .bg-18{background-color:#000087}.asciinema-terminal .fg-19{color:#0000af}.asciinema-terminal .bg-19{background-color:#0000af}.asciinema-terminal .fg-20{color:#0000d7}.asciinema-terminal .bg-20{background-color:#0000d7}.asciinema-terminal .fg-21{color:#00f}.asciinema-terminal .bg-21{background-color:#00f}.asciinema-terminal .fg-22{color:#005f00}.asciinema-terminal .bg-22{background-color:#005f00}.asciinema-terminal .fg-23{color:#005f5f}.asciinema-terminal .bg-23{background-color:#005f5f}.asciinema-terminal .fg-24{color:#005f87}.asciinema-terminal .bg-24{background-color:#005f87}.asciinema-terminal .fg-25{color:#005faf}.asciinema-terminal .bg-25{background-color:#005faf}.asciinema-terminal .fg-26{color:#005fd7}.asciinema-terminal .bg-26{background-color:#005fd7}.asciinema-terminal .fg-27{color:#005fff}.asciinema-terminal .bg-27{background-color:#005fff}.asciinema-terminal .fg-28{color:#008700}.asciinema-terminal .bg-28{background-color:#008700}.asciinema-terminal .fg-29{color:#00875f}.asciinema-terminal .bg-29{background-color:#00875f}.asciinema-terminal .fg-30{color:#008787}.asciinema-terminal .bg-30{background-color:#008787}.asciinema-terminal .fg-31{color:#0087af}.asciinema-terminal .bg-31{background-color:#0087af}.asciinema-terminal .fg-32{color:#0087d7}.asciinema-terminal .bg-32{background-color:#0087d7}.asciinema-terminal .fg-33{color:#0087ff}.asciinema-terminal .bg-33{background-color:#0087ff}.asciinema-terminal .fg-34{color:#00af00}.asciinema-terminal .bg-34{background-color:#00af00}.asciinema-terminal .fg-35{color:#00af5f}.asciinema-terminal .bg-35{background-color:#00af5f}.asciinema-terminal .fg-36{color:#00af87}.asciinema-terminal .bg-36{background-color:#00af87}.asciinema-terminal .fg-37{color:#00afaf}.asciinema-terminal .bg-37{background-color:#00afaf}.asciinema-terminal .fg-38{color:#00afd7}.asciinema-terminal .bg-38{background-color:#00afd7}.asciinema-terminal .fg-39{color:#00afff}.asciinema-terminal .bg-39{background-color:#00afff}.asciinema-terminal .fg-40{color:#00d700}.asciinema-terminal .bg-40{background-color:#00d700}.asciinema-terminal .fg-41{color:#00d75f}.asciinema-terminal .bg-41{background-color:#00d75f}.asciinema-terminal .fg-42{color:#00d787}.asciinema-terminal .bg-42{background-color:#00d787}.asciinema-terminal .fg-43{color:#00d7af}.asciinema-terminal .bg-43{background-color:#00d7af}.asciinema-terminal .fg-44{color:#00d7d7}.asciinema-terminal .bg-44{background-color:#00d7d7}.asciinema-terminal .fg-45{color:#00d7ff}.asciinema-terminal .bg-45{background-color:#00d7ff}.asciinema-terminal .fg-46{color:#0f0}.asciinema-terminal .bg-46{background-color:#0f0}.asciinema-terminal .fg-47{color:#00ff5f}.asciinema-terminal .bg-47{background-color:#00ff5f}.asciinema-terminal .fg-48{color:#00ff87}.asciinema-terminal .bg-48{background-color:#00ff87}.asciinema-terminal .fg-49{color:#00ffaf}.asciinema-terminal .bg-49{background-color:#00ffaf}.asciinema-terminal .fg-50{color:#00ffd7}.asciinema-terminal .bg-50{background-color:#00ffd7}.asciinema-terminal .fg-51{color:#0ff}.asciinema-terminal .bg-51{background-color:#0ff}.asciinema-terminal .fg-52{color:#5f0000}.asciinema-terminal .bg-52{background-color:#5f0000}.asciinema-terminal .fg-53{color:#5f005f}.asciinema-terminal .bg-53{background-color:#5f005f}.asciinema-terminal .fg-54{color:#5f0087}.asciinema-terminal .bg-54{background-color:#5f0087}.asciinema-terminal .fg-55{color:#5f00af}.asciinema-terminal .bg-55{background-color:#5f00af}.asciinema-terminal .fg-56{color:#5f00d7}.asciinema-terminal .bg-56{background-color:#5f00d7}.asciinema-terminal .fg-57{color:#5f00ff}.asciinema-terminal .bg-57{background-color:#5f00ff}.asciinema-terminal .fg-58{color:#5f5f00}.asciinema-terminal .bg-58{background-color:#5f5f00}.asciinema-terminal .fg-59{color:#5f5f5f}.asciinema-terminal .bg-59{background-color:#5f5f5f}.asciinema-terminal .fg-60{color:#5f5f87}.asciinema-terminal .bg-60{background-color:#5f5f87}.asciinema-terminal .fg-61{color:#5f5faf}.asciinema-terminal .bg-61{background-color:#5f5faf}.asciinema-terminal .fg-62{color:#5f5fd7}.asciinema-terminal .bg-62{background-color:#5f5fd7}.asciinema-terminal .fg-63{color:#5f5fff}.asciinema-terminal .bg-63{background-color:#5f5fff}.asciinema-terminal .fg-64{color:#5f8700}.asciinema-terminal .bg-64{background-color:#5f8700}.asciinema-terminal .fg-65{color:#5f875f}.asciinema-terminal .bg-65{background-color:#5f875f}.asciinema-terminal .fg-66{color:#5f8787}.asciinema-terminal .bg-66{background-color:#5f8787}.asciinema-terminal .fg-67{color:#5f87af}.asciinema-terminal .bg-67{background-color:#5f87af}.asciinema-terminal .fg-68{color:#5f87d7}.asciinema-terminal .bg-68{background-color:#5f87d7}.asciinema-terminal .fg-69{color:#5f87ff}.asciinema-terminal .bg-69{background-color:#5f87ff}.asciinema-terminal .fg-70{color:#5faf00}.asciinema-terminal .bg-70{background-color:#5faf00}.asciinema-terminal .fg-71{color:#5faf5f}.asciinema-terminal .bg-71{background-color:#5faf5f}.asciinema-terminal .fg-72{color:#5faf87}.asciinema-terminal .bg-72{background-color:#5faf87}.asciinema-terminal .fg-73{color:#5fafaf}.asciinema-terminal .bg-73{background-color:#5fafaf}.asciinema-terminal .fg-74{color:#5fafd7}.asciinema-terminal .bg-74{background-color:#5fafd7}.asciinema-terminal .fg-75{color:#5fafff}.asciinema-terminal .bg-75{background-color:#5fafff}.asciinema-terminal .fg-76{color:#5fd700}.asciinema-terminal .bg-76{background-color:#5fd700}.asciinema-terminal .fg-77{color:#5fd75f}.asciinema-terminal .bg-77{background-color:#5fd75f}.asciinema-terminal .fg-78{color:#5fd787}.asciinema-terminal .bg-78{background-color:#5fd787}.asciinema-terminal .fg-79{color:#5fd7af}.asciinema-terminal .bg-79{background-color:#5fd7af}.asciinema-terminal .fg-80{color:#5fd7d7}.asciinema-terminal .bg-80{background-color:#5fd7d7}.asciinema-terminal .fg-81{color:#5fd7ff}.asciinema-terminal .bg-81{background-color:#5fd7ff}.asciinema-terminal .fg-82{color:#5fff00}.asciinema-terminal .bg-82{background-color:#5fff00}.asciinema-terminal .fg-83{color:#5fff5f}.asciinema-terminal .bg-83{background-color:#5fff5f}.asciinema-terminal .fg-84{color:#5fff87}.asciinema-terminal .bg-84{background-color:#5fff87}.asciinema-terminal .fg-85{color:#5fffaf}.asciinema-terminal .bg-85{background-color:#5fffaf}.asciinema-terminal .fg-86{color:#5fffd7}.asciinema-terminal .bg-86{background-color:#5fffd7}.asciinema-terminal .fg-87{color:#5fffff}.asciinema-terminal .bg-87{background-color:#5fffff}.asciinema-terminal .fg-88{color:#870000}.asciinema-terminal .bg-88{background-color:#870000}.asciinema-terminal .fg-89{color:#87005f}.asciinema-terminal .bg-89{background-color:#87005f}.asciinema-terminal .fg-90{color:#870087}.asciinema-terminal .bg-90{background-color:#870087}.asciinema-terminal .fg-91{color:#8700af}.asciinema-terminal .bg-91{background-color:#8700af}.asciinema-terminal .fg-92{color:#8700d7}.asciinema-terminal .bg-92{background-color:#8700d7}.asciinema-terminal .fg-93{color:#8700ff}.asciinema-terminal .bg-93{background-color:#8700ff}.asciinema-terminal .fg-94{color:#875f00}.asciinema-terminal .bg-94{background-color:#875f00}.asciinema-terminal .fg-95{color:#875f5f}.asciinema-terminal .bg-95{background-color:#875f5f}.asciinema-terminal .fg-96{color:#875f87}.asciinema-terminal .bg-96{background-color:#875f87}.asciinema-terminal .fg-97{color:#875faf}.asciinema-terminal .bg-97{background-color:#875faf}.asciinema-terminal .fg-98{color:#875fd7}.asciinema-terminal .bg-98{background-color:#875fd7}.asciinema-terminal .fg-99{color:#875fff}.asciinema-terminal .bg-99{background-color:#875fff}.asciinema-terminal .fg-100{color:#878700}.asciinema-terminal .bg-100{background-color:#878700}.asciinema-terminal .fg-101{color:#87875f}.asciinema-terminal .bg-101{background-color:#87875f}.asciinema-terminal .fg-102{color:#878787}.asciinema-terminal .bg-102{background-color:#878787}.asciinema-terminal .fg-103{color:#8787af}.asciinema-terminal .bg-103{background-color:#8787af}.asciinema-terminal .fg-104{color:#8787d7}.asciinema-terminal .bg-104{background-color:#8787d7}.asciinema-terminal .fg-105{color:#8787ff}.asciinema-terminal .bg-105{background-color:#8787ff}.asciinema-terminal .fg-106{color:#87af00}.asciinema-terminal .bg-106{background-color:#87af00}.asciinema-terminal .fg-107{color:#87af5f}.asciinema-terminal .bg-107{background-color:#87af5f}.asciinema-terminal .fg-108{color:#87af87}.asciinema-terminal .bg-108{background-color:#87af87}.asciinema-terminal .fg-109{color:#87afaf}.asciinema-terminal .bg-109{background-color:#87afaf}.asciinema-terminal .fg-110{color:#87afd7}.asciinema-terminal .bg-110{background-color:#87afd7}.asciinema-terminal .fg-111{color:#87afff}.asciinema-terminal .bg-111{background-color:#87afff}.asciinema-terminal .fg-112{color:#87d700}.asciinema-terminal .bg-112{background-color:#87d700}.asciinema-terminal .fg-113{color:#87d75f}.asciinema-terminal .bg-113{background-color:#87d75f}.asciinema-terminal .fg-114{color:#87d787}.asciinema-terminal .bg-114{background-color:#87d787}.asciinema-terminal .fg-115{color:#87d7af}.asciinema-terminal .bg-115{background-color:#87d7af}.asciinema-terminal .fg-116{color:#87d7d7}.asciinema-terminal .bg-116{background-color:#87d7d7}.asciinema-terminal .fg-117{color:#87d7ff}.asciinema-terminal .bg-117{background-color:#87d7ff}.asciinema-terminal .fg-118{color:#87ff00}.asciinema-terminal .bg-118{background-color:#87ff00}.asciinema-terminal .fg-119{color:#87ff5f}.asciinema-terminal .bg-119{background-color:#87ff5f}.asciinema-terminal .fg-120{color:#87ff87}.asciinema-terminal .bg-120{background-color:#87ff87}.asciinema-terminal .fg-121{color:#87ffaf}.asciinema-terminal .bg-121{background-color:#87ffaf}.asciinema-terminal .fg-122{color:#87ffd7}.asciinema-terminal .bg-122{background-color:#87ffd7}.asciinema-terminal .fg-123{color:#87ffff}.asciinema-terminal .bg-123{background-color:#87ffff}.asciinema-terminal .fg-124{color:#af0000}.asciinema-terminal .bg-124{background-color:#af0000}.asciinema-terminal .fg-125{color:#af005f}.asciinema-terminal .bg-125{background-color:#af005f}.asciinema-terminal .fg-126{color:#af0087}.asciinema-terminal .bg-126{background-color:#af0087}.asciinema-terminal .fg-127{color:#af00af}.asciinema-terminal .bg-127{background-color:#af00af}.asciinema-terminal .fg-128{color:#af00d7}.asciinema-terminal .bg-128{background-color:#af00d7}.asciinema-terminal .fg-129{color:#af00ff}.asciinema-terminal .bg-129{background-color:#af00ff}.asciinema-terminal .fg-130{color:#af5f00}.asciinema-terminal .bg-130{background-color:#af5f00}.asciinema-terminal .fg-131{color:#af5f5f}.asciinema-terminal .bg-131{background-color:#af5f5f}.asciinema-terminal .fg-132{color:#af5f87}.asciinema-terminal .bg-132{background-color:#af5f87}.asciinema-terminal .fg-133{color:#af5faf}.asciinema-terminal .bg-133{background-color:#af5faf}.asciinema-terminal .fg-134{color:#af5fd7}.asciinema-terminal .bg-134{background-color:#af5fd7}.asciinema-terminal .fg-135{color:#af5fff}.asciinema-terminal .bg-135{background-color:#af5fff}.asciinema-terminal .fg-136{color:#af8700}.asciinema-terminal .bg-136{background-color:#af8700}.asciinema-terminal .fg-137{color:#af875f}.asciinema-terminal .bg-137{background-color:#af875f}.asciinema-terminal .fg-138{color:#af8787}.asciinema-terminal .bg-138{background-color:#af8787}.asciinema-terminal .fg-139{color:#af87af}.asciinema-terminal .bg-139{background-color:#af87af}.asciinema-terminal .fg-140{color:#af87d7}.asciinema-terminal .bg-140{background-color:#af87d7}.asciinema-terminal .fg-141{color:#af87ff}.asciinema-terminal .bg-141{background-color:#af87ff}.asciinema-terminal .fg-142{color:#afaf00}.asciinema-terminal .bg-142{background-color:#afaf00}.asciinema-terminal .fg-143{color:#afaf5f}.asciinema-terminal .bg-143{background-color:#afaf5f}.asciinema-terminal .fg-144{color:#afaf87}.asciinema-terminal .bg-144{background-color:#afaf87}.asciinema-terminal .fg-145{color:#afafaf}.asciinema-terminal .bg-145{background-color:#afafaf}.asciinema-terminal .fg-146{color:#afafd7}.asciinema-terminal .bg-146{background-color:#afafd7}.asciinema-terminal .fg-147{color:#afafff}.asciinema-terminal .bg-147{background-color:#afafff}.asciinema-terminal .fg-148{color:#afd700}.asciinema-terminal .bg-148{background-color:#afd700}.asciinema-terminal .fg-149{color:#afd75f}.asciinema-terminal .bg-149{background-color:#afd75f}.asciinema-terminal .fg-150{color:#afd787}.asciinema-terminal .bg-150{background-color:#afd787}.asciinema-terminal .fg-151{color:#afd7af}.asciinema-terminal .bg-151{background-color:#afd7af}.asciinema-terminal .fg-152{color:#afd7d7}.asciinema-terminal .bg-152{background-color:#afd7d7}.asciinema-terminal .fg-153{color:#afd7ff}.asciinema-terminal .bg-153{background-color:#afd7ff}.asciinema-terminal .fg-154{color:#afff00}.asciinema-terminal .bg-154{background-color:#afff00}.asciinema-terminal .fg-155{color:#afff5f}.asciinema-terminal .bg-155{background-color:#afff5f}.asciinema-terminal .fg-156{color:#afff87}.asciinema-terminal .bg-156{background-color:#afff87}.asciinema-terminal .fg-157{color:#afffaf}.asciinema-terminal .bg-157{background-color:#afffaf}.asciinema-terminal .fg-158{color:#afffd7}.asciinema-terminal .bg-158{background-color:#afffd7}.asciinema-terminal .fg-159{color:#afffff}.asciinema-terminal .bg-159{background-color:#afffff}.asciinema-terminal .fg-160{color:#d70000}.asciinema-terminal .bg-160{background-color:#d70000}.asciinema-terminal .fg-161{color:#d7005f}.asciinema-terminal .bg-161{background-color:#d7005f}.asciinema-terminal .fg-162{color:#d70087}.asciinema-terminal .bg-162{background-color:#d70087}.asciinema-terminal .fg-163{color:#d700af}.asciinema-terminal .bg-163{background-color:#d700af}.asciinema-terminal .fg-164{color:#d700d7}.asciinema-terminal .bg-164{background-color:#d700d7}.asciinema-terminal .fg-165{color:#d700ff}.asciinema-terminal .bg-165{background-color:#d700ff}.asciinema-terminal .fg-166{color:#d75f00}.asciinema-terminal .bg-166{background-color:#d75f00}.asciinema-terminal .fg-167{color:#d75f5f}.asciinema-terminal .bg-167{background-color:#d75f5f}.asciinema-terminal .fg-168{color:#d75f87}.asciinema-terminal .bg-168{background-color:#d75f87}.asciinema-terminal .fg-169{color:#d75faf}.asciinema-terminal .bg-169{background-color:#d75faf}.asciinema-terminal .fg-170{color:#d75fd7}.asciinema-terminal .bg-170{background-color:#d75fd7}.asciinema-terminal .fg-171{color:#d75fff}.asciinema-terminal .bg-171{background-color:#d75fff}.asciinema-terminal .fg-172{color:#d78700}.asciinema-terminal .bg-172{background-color:#d78700}.asciinema-terminal .fg-173{color:#d7875f}.asciinema-terminal .bg-173{background-color:#d7875f}.asciinema-terminal .fg-174{color:#d78787}.asciinema-terminal .bg-174{background-color:#d78787}.asciinema-terminal .fg-175{color:#d787af}.asciinema-terminal .bg-175{background-color:#d787af}.asciinema-terminal .fg-176{color:#d787d7}.asciinema-terminal .bg-176{background-color:#d787d7}.asciinema-terminal .fg-177{color:#d787ff}.asciinema-terminal .bg-177{background-color:#d787ff}.asciinema-terminal .fg-178{color:#d7af00}.asciinema-terminal .bg-178{background-color:#d7af00}.asciinema-terminal .fg-179{color:#d7af5f}.asciinema-terminal .bg-179{background-color:#d7af5f}.asciinema-terminal .fg-180{color:#d7af87}.asciinema-terminal .bg-180{background-color:#d7af87}.asciinema-terminal .fg-181{color:#d7afaf}.asciinema-terminal .bg-181{background-color:#d7afaf}.asciinema-terminal .fg-182{color:#d7afd7}.asciinema-terminal .bg-182{background-color:#d7afd7}.asciinema-terminal .fg-183{color:#d7afff}.asciinema-terminal .bg-183{background-color:#d7afff}.asciinema-terminal .fg-184{color:#d7d700}.asciinema-terminal .bg-184{background-color:#d7d700}.asciinema-terminal .fg-185{color:#d7d75f}.asciinema-terminal .bg-185{background-color:#d7d75f}.asciinema-terminal .fg-186{color:#d7d787}.asciinema-terminal .bg-186{background-color:#d7d787}.asciinema-terminal .fg-187{color:#d7d7af}.asciinema-terminal .bg-187{background-color:#d7d7af}.asciinema-terminal .fg-188{color:#d7d7d7}.asciinema-terminal .bg-188{background-color:#d7d7d7}.asciinema-terminal .fg-189{color:#d7d7ff}.asciinema-terminal .bg-189{background-color:#d7d7ff}.asciinema-terminal .fg-190{color:#d7ff00}.asciinema-terminal .bg-190{background-color:#d7ff00}.asciinema-terminal .fg-191{color:#d7ff5f}.asciinema-terminal .bg-191{background-color:#d7ff5f}.asciinema-terminal .fg-192{color:#d7ff87}.asciinema-terminal .bg-192{background-color:#d7ff87}.asciinema-terminal .fg-193{color:#d7ffaf}.asciinema-terminal .bg-193{background-color:#d7ffaf}.asciinema-terminal .fg-194{color:#d7ffd7}.asciinema-terminal .bg-194{background-color:#d7ffd7}.asciinema-terminal .fg-195{color:#d7ffff}.asciinema-terminal .bg-195{background-color:#d7ffff}.asciinema-terminal .fg-196{color:red}.asciinema-terminal .bg-196{background-color:red}.asciinema-terminal .fg-197{color:#ff005f}.asciinema-terminal .bg-197{background-color:#ff005f}.asciinema-terminal .fg-198{color:#ff0087}.asciinema-terminal .bg-198{background-color:#ff0087}.asciinema-terminal .fg-199{color:#ff00af}.asciinema-terminal .bg-199{background-color:#ff00af}.asciinema-terminal .fg-200{color:#ff00d7}.asciinema-terminal .bg-200{background-color:#ff00d7}.asciinema-terminal .fg-201{color:#f0f}.asciinema-terminal .bg-201{background-color:#f0f}.asciinema-terminal .fg-202{color:#ff5f00}.asciinema-terminal .bg-202{background-color:#ff5f00}.asciinema-terminal .fg-203{color:#ff5f5f}.asciinema-terminal .bg-203{background-color:#ff5f5f}.asciinema-terminal .fg-204{color:#ff5f87}.asciinema-terminal .bg-204{background-color:#ff5f87}.asciinema-terminal .fg-205{color:#ff5faf}.asciinema-terminal .bg-205{background-color:#ff5faf}.asciinema-terminal .fg-206{color:#ff5fd7}.asciinema-terminal .bg-206{background-color:#ff5fd7}.asciinema-terminal .fg-207{color:#ff5fff}.asciinema-terminal .bg-207{background-color:#ff5fff}.asciinema-terminal .fg-208{color:#ff8700}.asciinema-terminal .bg-208{background-color:#ff8700}.asciinema-terminal .fg-209{color:#ff875f}.asciinema-terminal .bg-209{background-color:#ff875f}.asciinema-terminal .fg-210{color:#ff8787}.asciinema-terminal .bg-210{background-color:#ff8787}.asciinema-terminal .fg-211{color:#ff87af}.asciinema-terminal .bg-211{background-color:#ff87af}.asciinema-terminal .fg-212{color:#ff87d7}.asciinema-terminal .bg-212{background-color:#ff87d7}.asciinema-terminal .fg-213{color:#ff87ff}.asciinema-terminal .bg-213{background-color:#ff87ff}.asciinema-terminal .fg-214{color:#ffaf00}.asciinema-terminal .bg-214{background-color:#ffaf00}.asciinema-terminal .fg-215{color:#ffaf5f}.asciinema-terminal .bg-215{background-color:#ffaf5f}.asciinema-terminal .fg-216{color:#ffaf87}.asciinema-terminal .bg-216{background-color:#ffaf87}.asciinema-terminal .fg-217{color:#ffafaf}.asciinema-terminal .bg-217{background-color:#ffafaf}.asciinema-terminal .fg-218{color:#ffafd7}.asciinema-terminal .bg-218{background-color:#ffafd7}.asciinema-terminal .fg-219{color:#ffafff}.asciinema-terminal .bg-219{background-color:#ffafff}.asciinema-terminal .fg-220{color:gold}.asciinema-terminal .bg-220{background-color:gold}.asciinema-terminal .fg-221{color:#ffd75f}.asciinema-terminal .bg-221{background-color:#ffd75f}.asciinema-terminal .fg-222{color:#ffd787}.asciinema-terminal .bg-222{background-color:#ffd787}.asciinema-terminal .fg-223{color:#ffd7af}.asciinema-terminal .bg-223{background-color:#ffd7af}.asciinema-terminal .fg-224{color:#ffd7d7}.asciinema-terminal .bg-224{background-color:#ffd7d7}.asciinema-terminal .fg-225{color:#ffd7ff}.asciinema-terminal .bg-225{background-color:#ffd7ff}.asciinema-terminal .fg-226{color:#ff0}.asciinema-terminal .bg-226{background-color:#ff0}.asciinema-terminal .fg-227{color:#ffff5f}.asciinema-terminal .bg-227{background-color:#ffff5f}.asciinema-terminal .fg-228{color:#ffff87}.asciinema-terminal .bg-228{background-color:#ffff87}.asciinema-terminal .fg-229{color:#ffffaf}.asciinema-terminal .bg-229{background-color:#ffffaf}.asciinema-terminal .fg-230{color:#ffffd7}.asciinema-terminal .bg-230{background-color:#ffffd7}.asciinema-terminal .fg-231,.asciinema-theme-asciinema .fg-15,.asciinema-theme-seti .fg-15{color:#fff}.asciinema-terminal .bg-231,.asciinema-theme-asciinema .bg-15,.asciinema-theme-seti .bg-15{background-color:#fff}.asciinema-terminal .fg-232{color:#080808}.asciinema-terminal .bg-232{background-color:#080808}.asciinema-terminal .fg-233{color:#121212}.asciinema-terminal .bg-233{background-color:#121212}.asciinema-terminal .fg-234{color:#1c1c1c}.asciinema-terminal .bg-234{background-color:#1c1c1c}.asciinema-terminal .fg-235{color:#262626}.asciinema-terminal .bg-235{background-color:#262626}.asciinema-terminal .fg-236{color:#303030}.asciinema-terminal .bg-236{background-color:#303030}.asciinema-terminal .fg-237{color:#3a3a3a}.asciinema-terminal .bg-237{background-color:#3a3a3a}.asciinema-terminal .fg-238{color:#444}.asciinema-terminal .bg-238{background-color:#444}.asciinema-terminal .fg-239{color:#4e4e4e}.asciinema-terminal .bg-239{background-color:#4e4e4e}.asciinema-terminal .fg-240{color:#585858}.asciinema-terminal .bg-240{background-color:#585858}.asciinema-terminal .fg-241{color:#626262}.asciinema-terminal .bg-241{background-color:#626262}.asciinema-terminal .fg-242{color:#6c6c6c}.asciinema-terminal .bg-242{background-color:#6c6c6c}.asciinema-terminal .fg-243{color:#767676}.asciinema-terminal .bg-243{background-color:#767676}.asciinema-terminal .fg-244{color:grey}.asciinema-terminal .bg-244{background-color:grey}.asciinema-terminal .fg-245{color:#8a8a8a}.asciinema-terminal .bg-245{background-color:#8a8a8a}.asciinema-terminal .fg-246{color:#949494}.asciinema-terminal .bg-246{background-color:#949494}.asciinema-terminal .fg-247{color:#9e9e9e}.asciinema-terminal .bg-247{background-color:#9e9e9e}.asciinema-terminal .fg-248{color:#a8a8a8}.asciinema-terminal .bg-248{background-color:#a8a8a8}.asciinema-terminal .fg-249{color:#b2b2b2}.asciinema-terminal .bg-249{background-color:#b2b2b2}.asciinema-terminal .fg-250{color:#bcbcbc}.asciinema-terminal .bg-250{background-color:#bcbcbc}.asciinema-terminal .fg-251{color:#c6c6c6}.asciinema-terminal .bg-251{background-color:#c6c6c6}.asciinema-terminal .fg-252{color:#d0d0d0}.asciinema-terminal .bg-252{background-color:#d0d0d0}.asciinema-terminal .fg-253{color:#dadada}.asciinema-terminal .bg-253{background-color:#dadada}.asciinema-terminal .fg-254{color:#e4e4e4}.asciinema-terminal .bg-254{background-color:#e4e4e4}.asciinema-terminal .fg-255,.asciinema-theme-seti .fg-7{color:#eee}.asciinema-terminal .bg-255,.asciinema-theme-seti .bg-7{background-color:#eee}.asciinema-theme-asciinema,.asciinema-theme-tango{background-color:#121314}.asciinema-theme-asciinema .asciinema-terminal,.asciinema-theme-tango .asciinema-terminal{background-color:#121314;border-color:#121314;color:#ccc}.asciinema-theme-asciinema .fg-bg,.asciinema-theme-tango .fg-bg{color:#121314}.asciinema-theme-asciinema .bg-fg,.asciinema-theme-tango .bg-fg{background-color:#ccc}.asciinema-theme-asciinema .fg-1,.asciinema-theme-asciinema .fg-9{color:#dd3c69}.asciinema-theme-asciinema .bg-1,.asciinema-theme-asciinema .bg-9{background-color:#dd3c69}.asciinema-theme-asciinema .fg-10,.asciinema-theme-asciinema .fg-2{color:#4ebf22}.asciinema-theme-asciinema .bg-10,.asciinema-theme-asciinema .bg-2{background-color:#4ebf22}.asciinema-theme-asciinema .fg-11,.asciinema-theme-asciinema .fg-3{color:#ddaf3c}.asciinema-theme-asciinema .bg-11,.asciinema-theme-asciinema .bg-3{background-color:#ddaf3c}.asciinema-theme-asciinema .fg-12,.asciinema-theme-asciinema .fg-4{color:#26b0d7}.asciinema-theme-asciinema .bg-12,.asciinema-theme-asciinema .bg-4{background-color:#26b0d7}.asciinema-theme-asciinema .fg-13,.asciinema-theme-asciinema .fg-5{color:#b954e1}.asciinema-theme-asciinema .bg-13,.asciinema-theme-asciinema .bg-5{background-color:#b954e1}.asciinema-theme-asciinema .fg-14,.asciinema-theme-asciinema .fg-6{color:#54e1b9}.asciinema-theme-asciinema .bg-14,.asciinema-theme-asciinema .bg-6{background-color:#54e1b9}.asciinema-theme-asciinema .fg-7{color:#d9d9d9}.asciinema-theme-asciinema .bg-7{background-color:#d9d9d9}.asciinema-theme-asciinema .fg-8{color:#4d4d4d}.asciinema-theme-asciinema .bg-8{background-color:#4d4d4d}.asciinema-theme-tango .fg-1{color:#c00}.asciinema-theme-tango .bg-1{background-color:#c00}.asciinema-theme-tango .fg-2{color:#4e9a06}.asciinema-theme-tango .bg-2{background-color:#4e9a06}.asciinema-theme-tango .fg-3{color:#c4a000}.asciinema-theme-tango .bg-3{background-color:#c4a000}.asciinema-theme-tango .fg-4{color:#3465a4}.asciinema-theme-tango .bg-4{background-color:#3465a4}.asciinema-theme-tango .fg-5{color:#75507b}.asciinema-theme-tango .bg-5{background-color:#75507b}.asciinema-theme-tango .fg-6{color:#06989a}.asciinema-theme-tango .bg-6{background-color:#06989a}.asciinema-theme-tango .fg-7{color:#d3d7cf}.asciinema-theme-tango .bg-7{background-color:#d3d7cf}.asciinema-theme-tango .fg-8{color:#555753}.asciinema-theme-tango .bg-8{background-color:#555753}.asciinema-theme-tango .fg-9{color:#ef2929}.asciinema-theme-tango .bg-9{background-color:#ef2929}.asciinema-theme-tango .fg-10{color:#8ae234}.asciinema-theme-tango .bg-10{background-color:#8ae234}.asciinema-theme-tango .fg-11{color:#fce94f}.asciinema-theme-tango .bg-11{background-color:#fce94f}.asciinema-theme-tango .fg-12{color:#729fcf}.asciinema-theme-tango .bg-12{background-color:#729fcf}.asciinema-theme-tango .fg-13{color:#ad7fa8}.asciinema-theme-tango .bg-13{background-color:#ad7fa8}.asciinema-theme-tango .fg-14{color:#34e2e2}.asciinema-theme-tango .bg-14{background-color:#34e2e2}.asciinema-theme-tango .fg-15{color:#eeeeec}.asciinema-theme-tango .bg-15{background-color:#eeeeec}.asciinema-theme-solarized-dark,.asciinema-theme-solarized-dark .bg-8,.asciinema-theme-solarized-light .bg-8{background-color:#002b36}.asciinema-theme-solarized-dark .asciinema-terminal{background-color:#002b36;border-color:#002b36;color:#839496}.asciinema-theme-solarized-dark .fg-8,.asciinema-theme-solarized-dark .fg-bg,.asciinema-theme-solarized-light .fg-8{color:#002b36}.asciinema-theme-solarized-dark .bg-12,.asciinema-theme-solarized-dark .bg-fg,.asciinema-theme-solarized-light .bg-12{background-color:#839496}.asciinema-theme-solarized-dark .fg-0,.asciinema-theme-solarized-light .fg-0{color:#073642}.asciinema-theme-solarized-dark .bg-0,.asciinema-theme-solarized-light .bg-0{background-color:#073642}.asciinema-theme-solarized-dark .fg-1,.asciinema-theme-solarized-light .fg-1{color:#dc322f}.asciinema-theme-solarized-dark .bg-1,.asciinema-theme-solarized-light .bg-1{background-color:#dc322f}.asciinema-theme-solarized-dark .fg-2,.asciinema-theme-solarized-light .fg-2{color:#859900}.asciinema-theme-solarized-dark .bg-2,.asciinema-theme-solarized-light .bg-2{background-color:#859900}.asciinema-theme-solarized-dark .fg-3,.asciinema-theme-solarized-light .fg-3{color:#b58900}.asciinema-theme-solarized-dark .bg-3,.asciinema-theme-solarized-light .bg-3{background-color:#b58900}.asciinema-theme-solarized-dark .fg-4,.asciinema-theme-solarized-light .fg-4{color:#268bd2}.asciinema-theme-solarized-dark .bg-4,.asciinema-theme-solarized-light .bg-4{background-color:#268bd2}.asciinema-theme-solarized-dark .fg-5,.asciinema-theme-solarized-light .fg-5{color:#d33682}.asciinema-theme-solarized-dark .bg-5,.asciinema-theme-solarized-light .bg-5{background-color:#d33682}.asciinema-theme-solarized-dark .fg-6,.asciinema-theme-solarized-light .fg-6{color:#2aa198}.asciinema-theme-solarized-dark .bg-6,.asciinema-theme-solarized-light .bg-6{background-color:#2aa198}.asciinema-theme-solarized-dark .fg-7,.asciinema-theme-solarized-light .fg-7{color:#eee8d5}.asciinema-theme-solarized-dark .bg-7,.asciinema-theme-solarized-light .bg-7{background-color:#eee8d5}.asciinema-theme-solarized-dark .fg-9,.asciinema-theme-solarized-light .fg-9{color:#cb4b16}.asciinema-theme-solarized-dark .bg-9,.asciinema-theme-solarized-light .bg-9{background-color:#cb4b16}.asciinema-theme-solarized-dark .fg-10,.asciinema-theme-solarized-light .fg-10{color:#586e75}.asciinema-theme-solarized-dark .bg-10,.asciinema-theme-solarized-light .bg-10{background-color:#586e75}.asciinema-theme-solarized-dark .fg-11{color:#657b83}.asciinema-theme-solarized-dark .bg-11,.asciinema-theme-solarized-light .bg-fg{background-color:#657b83}.asciinema-theme-solarized-dark .fg-12,.asciinema-theme-solarized-light .fg-12{color:#839496}.asciinema-theme-solarized-dark .fg-13,.asciinema-theme-solarized-light .fg-13{color:#6c71c4}.asciinema-theme-solarized-dark .bg-13,.asciinema-theme-solarized-light .bg-13{background-color:#6c71c4}.asciinema-theme-solarized-dark .fg-14,.asciinema-theme-solarized-light .fg-14{color:#93a1a1}.asciinema-theme-solarized-dark .bg-14,.asciinema-theme-solarized-light .bg-14{background-color:#93a1a1}.asciinema-theme-solarized-dark .fg-15,.asciinema-theme-solarized-light .fg-15,.asciinema-theme-solarized-light .fg-bg{color:#fdf6e3}.asciinema-theme-solarized-dark .bg-15,.asciinema-theme-solarized-light,.asciinema-theme-solarized-light .bg-15{background-color:#fdf6e3}.asciinema-theme-solarized-light .asciinema-terminal{background-color:#fdf6e3;border-color:#fdf6e3;color:#657b83}.asciinema-theme-solarized-light .fg-11{color:#657c83}.asciinema-theme-solarized-light .bg-11{background-color:#657c83}.asciinema-theme-solarized-light .start-prompt .play-button svg .play-btn-fill{fill:#dc322f}.asciinema-theme-solarized-light .start-prompt .play-button svg .play-btn-stroke{stroke:#dc322f}.asciinema-theme-seti{background-color:#111213}.asciinema-theme-seti .asciinema-terminal{background-color:#111213;border-color:#111213;color:#cacecd}.asciinema-theme-seti .fg-bg{color:#111213}.asciinema-theme-seti .bg-fg{background-color:#cacecd}.asciinema-theme-seti .fg-0,.asciinema-theme-seti .fg-8{color:#323232}.asciinema-theme-seti .bg-0,.asciinema-theme-seti .bg-8{background-color:#323232}.asciinema-theme-seti .fg-1,.asciinema-theme-seti .fg-9{color:#c22832}.asciinema-theme-seti .bg-1,.asciinema-theme-seti .bg-9{background-color:#c22832}.asciinema-theme-seti .fg-10,.asciinema-theme-seti .fg-14,.asciinema-theme-seti .fg-2,.asciinema-theme-seti .fg-6{color:#8ec43d}.asciinema-theme-seti .bg-10,.asciinema-theme-seti .bg-14,.asciinema-theme-seti .bg-2,.asciinema-theme-seti .bg-6{background-color:#8ec43d}.asciinema-theme-seti .fg-11,.asciinema-theme-seti .fg-3{color:#e0c64f}.asciinema-theme-seti .bg-11,.asciinema-theme-seti .bg-3{background-color:#e0c64f}.asciinema-theme-seti .fg-12,.asciinema-theme-seti .fg-4{color:#43a5d5}.asciinema-theme-seti .bg-12,.asciinema-theme-seti .bg-4{background-color:#43a5d5}.asciinema-theme-seti .fg-13,.asciinema-theme-seti .fg-5{color:#8b57b5}.asciinema-theme-seti .bg-13,.asciinema-theme-seti .bg-5{background-color:#8b57b5}.asciinema-theme-monokai,.asciinema-theme-monokai .bg-0{background-color:#272822}.asciinema-theme-monokai .asciinema-terminal{background-color:#272822;border-color:#272822;color:#f8f8f2}.asciinema-theme-monokai .fg-0,.asciinema-theme-monokai .fg-bg{color:#272822}.asciinema-theme-monokai .bg-7,.asciinema-theme-monokai .bg-fg{background-color:#f8f8f2}.asciinema-theme-monokai .fg-1,.asciinema-theme-monokai .fg-9{color:#f92672}.asciinema-theme-monokai .bg-1,.asciinema-theme-monokai .bg-9{background-color:#f92672}.asciinema-theme-monokai .fg-10,.asciinema-theme-monokai .fg-2{color:#a6e22e}.asciinema-theme-monokai .bg-10,.asciinema-theme-monokai .bg-2{background-color:#a6e22e}.asciinema-theme-monokai .fg-11,.asciinema-theme-monokai .fg-3{color:#f4bf75}.asciinema-theme-monokai .bg-11,.asciinema-theme-monokai .bg-3{background-color:#f4bf75}.asciinema-theme-monokai .fg-12,.asciinema-theme-monokai .fg-4{color:#66d9ef}.asciinema-theme-monokai .bg-12,.asciinema-theme-monokai .bg-4{background-color:#66d9ef}.asciinema-theme-monokai .fg-13,.asciinema-theme-monokai .fg-5{color:#ae81ff}.asciinema-theme-monokai .bg-13,.asciinema-theme-monokai .bg-5{background-color:#ae81ff}.asciinema-theme-monokai .fg-14,.asciinema-theme-monokai .fg-6{color:#a1efe4}.asciinema-theme-monokai .bg-14,.asciinema-theme-monokai .bg-6{background-color:#a1efe4}.asciinema-theme-monokai .fg-7{color:#f8f8f2}.asciinema-theme-monokai .fg-8{color:#75715e}.asciinema-theme-monokai .bg-8{background-color:#75715e}.asciinema-theme-monokai .fg-15{color:#f9f8f5}.asciinema-theme-monokai .bg-15{background-color:#f9f8f5}body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#docusaurus-base-url-issue-banner-container,.hideAction_vcyE>svg,.themedImage_ToTc,[data-theme=dark] .lightToggleIcon_pyhR,[data-theme=light] .darkToggleIcon_wfgR,html[data-announcement-bar-initially-dismissed=true] .announcementBar_mb4j{display:none}.skipToContent_fXgn{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_fXgn:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.closeButton_CVFx{line-height:0;padding:0}.content_knG7{font-size:85%;padding:5px 0;text-align:center}.content_knG7 a{color:inherit}.announcementBar_mb4j{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_vyr4{flex:0 0 10px}.announcementBarClose_gvF7{align-self:stretch;flex:0 0 30px}.toggle_vylO{height:2rem;width:2rem}.toggleButton_gllP{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_gllP:hover{background:var(--ifm-color-emphasis-200)}.toggleButtonDisabled_aARS{cursor:not-allowed}[data-theme=dark] .themedImage--dark_i4oU,[data-theme=light] .themedImage--light_HNdA{display:initial}.iconExternalLink_nPIU{margin-left:.3rem}.iconLanguage_nlXk{margin-right:5px;vertical-align:text-bottom}.searchBar_RVTs .dropdownMenu_qbY6{background:var(--search-local-modal-background,#f5f6f7);border-radius:6px;box-shadow:var(--search-local-modal-shadow,inset 1px 1px 0 0 #ffffff80,0 3px 8px 0 #555a64);left:auto!important;margin-top:8px;padding:var(--search-local-spacing,12px);position:relative;right:0!important;width:var(--search-local-modal-width,560px)}html[data-theme=dark] .searchBar_RVTs .dropdownMenu_qbY6{background:var(--search-local-modal-background,var(--ifm-background-color));box-shadow:var(--search-local-modal-shadow,inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309)}.searchBar_RVTs .dropdownMenu_qbY6 .suggestion_fB_2{align-items:center;background:var(--search-local-hit-background,#fff);border-radius:4px;box-shadow:var(--search-local-hit-shadow,0 1px 3px 0 #d4d9e1);color:var(--search-local-hit-color,#444950);cursor:pointer;display:flex;flex-direction:row;height:var(--search-local-hit-height,56px);padding:0 var(--search-local-spacing,12px);width:100%}.hitTree_kk6K,.noResults_l6Q3{align-items:center;display:flex}html[data-theme=dark] .dropdownMenu_qbY6 .suggestion_fB_2{background:var(--search-local-hit-background,var(--ifm-color-emphasis-100));box-shadow:var(--search-local-hit-shadow,none);color:var(--search-local-hit-color,var(--ifm-font-color-base))}.searchBar_RVTs .dropdownMenu_qbY6 .suggestion_fB_2:not(:last-child){margin-bottom:4px}.searchBar_RVTs .dropdownMenu_qbY6 .suggestion_fB_2.cursor_eG29{background-color:var(--search-local-highlight-color,var(--ifm-color-primary))}.hitFooter_E9YW a,.hitIcon_a7Zy,.hitPath_ieM4,.hitTree_kk6K,.noResultsIcon_EBY5{color:var(--search-local-muted-color,#969faf)}html[data-theme=dark] .hitIcon_a7Zy,html[data-theme=dark] .hitPath_ieM4,html[data-theme=dark] .hitTree_kk6K,html[data-theme=dark] .noResultsIcon_EBY5{color:var(--search-local-muted-color,var(--ifm-color-secondary-darkest))}.hitTree_kk6K>svg{height:var(--search-local-hit-height,56px);opacity:.5;width:24px}.hitIcon_a7Zy,.hitTree_kk6K>svg{stroke-width:var(--search-local-icon-stroke-width,1.4)}.hitAction_NqkB,.hitIcon_a7Zy{height:20px;width:20px}.hitWrapper_sAK8{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;margin:0 8px;overflow-x:hidden;width:80%}.hitWrapper_sAK8 mark{background:none;color:var(--search-local-highlight-color,var(--ifm-color-primary))}.hitTitle_vyVt{font-size:.9em}.hitPath_ieM4{font-size:.75em}.hitPath_ieM4,.hitTitle_vyVt{overflow-x:hidden;text-overflow:ellipsis;white-space:nowrap}.noResults_l6Q3{flex-direction:column;justify-content:center;padding:var(--search-local-spacing,12px) 0}.noResultsIcon_EBY5{margin-bottom:var(--search-local-spacing,12px)}.hitFooter_E9YW{font-size:.85em;margin-top:var(--search-local-spacing,12px);text-align:center}.cursor_eG29 .hideAction_vcyE>svg,.tocCollapsibleContent_vkbj a{display:block}.suggestion_fB_2.cursor_eG29,.suggestion_fB_2.cursor_eG29 .hitIcon_a7Zy,.suggestion_fB_2.cursor_eG29 .hitPath_ieM4,.suggestion_fB_2.cursor_eG29 .hitTree_kk6K,.suggestion_fB_2.cursor_eG29 mark{color:var(--search-local-hit-active-color,var(--ifm-color-white))!important}.searchBarContainer_NW3z{margin-left:16px}.searchBarContainer_NW3z .searchBarLoadingRing_YnHq{display:none;left:10px;position:absolute;top:6px}.navbar__search{position:relative}.searchIndexLoading_EJ1f .navbar__search-input{background-image:none}.searchHintContainer_Pkmr{align-items:center;display:flex;gap:4px;height:100%;justify-content:center;pointer-events:none;position:absolute;right:10px;top:0}.searchHint_iIMx{background-color:var(--ifm-navbar-background-color);border:1px solid var(--ifm-color-emphasis-500);box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-500);color:var(--ifm-navbar-search-input-placeholder-color)}.loadingRing_RJI3{display:inline-block;height:20px;opacity:var(--search-local-loading-icon-opacity,.5);position:relative;width:20px}.loadingRing_RJI3 div{animation:1.2s cubic-bezier(.5,0,.5,1) infinite b;border:2px solid var(--search-load-loading-icon-color,var(--ifm-navbar-search-input-color));border-color:var(--search-load-loading-icon-color,var(--ifm-navbar-search-input-color)) #0000 #0000 #0000;border-radius:50%;box-sizing:border-box;display:block;height:16px;margin:2px;position:absolute;width:16px}.loadingRing_RJI3 div:first-child{animation-delay:-.45s}.loadingRing_RJI3 div:nth-child(2){animation-delay:-.3s}.loadingRing_RJI3 div:nth-child(3){animation-delay:-.15s}@keyframes b{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.navbarHideable_m1mJ{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_jGov{transform:translate3d(0,calc(-100% - 2px),0)}.footerLogoLink_BH7S{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.footerLogoLink_BH7S:hover,.hash-link:focus,:hover>.hash-link{opacity:1}.mainWrapper_z2l0{flex:1 0 auto}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{display:flex;flex-direction:column;min-height:100%}.iconEdit_Z9Sw{margin-right:.3em;vertical-align:sub}.tag_zVej{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_zVej:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_sFm0{border-radius:var(--ifm-global-radius);font-size:90%;padding:.2rem .5rem .3rem}.tagWithCount_h2kH{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_h2kH:after,.tagWithCount_h2kH:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.playgroundContainer_X_Ta,.tagWithCount_h2kH span,.tocCollapsible_ETCw{border-radius:var(--ifm-global-radius)}.tagWithCount_h2kH:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_h2kH:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_h2kH span{background:var(--ifm-color-secondary);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tags_jXut{display:inline}.tag_QGVx{display:inline-block;margin:0 .4rem .5rem 0}.lastUpdated_vwxv{font-size:smaller;font-style:italic;margin-top:.2rem}.tocCollapsibleButton_TO0P{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_TO0P:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_MG3E:after,.tocCollapsibleExpanded_sAul{transform:none}.tocCollapsible_ETCw{background-color:var(--ifm-menu-color-background-active);margin:1rem 0}.codeBlockContainer_Ckt0,.playgroundContainer_X_Ta{margin-bottom:var(--ifm-leading);box-shadow:var(--ifm-global-shadow-lw)}.tocCollapsibleContent_vkbj>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_vkbj ul li{margin:.4rem .8rem}.tableOfContents_bqdL{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.anchorWithStickyNavbar_LWe7{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_WYt5{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.hash-link:before{content:"#"}.playgroundContainer_X_Ta{overflow:hidden}.playgroundHeader_dyrN{background:var(--ifm-color-emphasis-200);color:var(--ifm-color-content);font-size:var(--ifm-code-font-size);font-weight:700;letter-spacing:.08rem;padding:.75rem}.playgroundHeader_dyrN:first-of-type{background:var(--ifm-color-emphasis-600);color:var(--ifm-color-content-inverse)}.playgroundEditor_Q6Y7{direction:ltr;font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace)!important}.playgroundPreview_DzOI{background-color:var(--ifm-pre-background);padding:1rem}.buttonGroup__atx button,.codeBlockContainer_Ckt0{background:var(--prism-background-color);color:var(--prism-color)}.codeBlockContainer_Ckt0{border-radius:var(--ifm-code-border-radius)}.codeBlockContent_biex{border-radius:inherit;direction:ltr;position:relative}.codeBlockTitle_Ktv7{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:inherit;border-top-right-radius:inherit;font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_bY9V{--ifm-pre-background:var(--prism-background-color);margin:0;padding:0}.codeBlockTitle_Ktv7+.codeBlockContent_biex .codeBlock_bY9V{border-top-left-radius:0;border-top-right-radius:0}.codeBlockLines_e6Vv{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.codeBlockLinesWithNumbering_o6Pm{display:table;padding:var(--ifm-pre-padding) 0}.buttonGroup__atx{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup__atx button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity .2s ease-in-out}.buttonGroup__atx button:focus-visible,.buttonGroup__atx button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup__atx button{opacity:.4}:where(:root){--docusaurus-highlighted-code-line-bg:#484d5b}:where([data-theme=dark]){--docusaurus-highlighted-code-line-bg:#646464}.theme-code-block-highlighted-line{background-color:var(--docusaurus-highlighted-code-line-bg);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.codeLine_lJS_{counter-increment:a;display:table-row}.codeLineNumber_Tfdd{background:var(--ifm-pre-background);display:table-cell;left:0;overflow-wrap:normal;padding:0 var(--ifm-pre-padding);position:sticky;text-align:right;width:1%}.codeLineNumber_Tfdd:before{content:counter(a);opacity:.4}.codeLineContent_feaV{padding-right:var(--ifm-pre-padding)}.theme-code-block:hover .copyButtonCopied_obH4{opacity:1!important}.copyButtonIcons_eSgA{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_y97N,.copyButtonSuccessIcon_LjdS{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:.15s;width:inherit}.copyButtonSuccessIcon_LjdS{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.copyButtonCopied_obH4 .copyButtonIcon_y97N{opacity:0;transform:scale(.33)}.copyButtonCopied_obH4 .copyButtonSuccessIcon_LjdS{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.wordWrapButtonIcon_Bwma{height:1.2rem;width:1.2rem}.details_lb9f{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_lb9f>summary{cursor:pointer;padding-left:1rem;position:relative}.details_lb9f>summary::-webkit-details-marker{display:none}.details_lb9f>summary:before{border-color:#0000 #0000 #0000 var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_i85q{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.details_b_Ee{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.img_ev3q{height:auto}.admonition_LlT9{margin-bottom:1em}.admonitionHeading_tbUL{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.3rem}.admonitionHeading_tbUL code{text-transform:none}.admonitionIcon_kALy{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_kALy svg{fill:var(--ifm-alert-foreground-color);display:inline-block;height:1.6em;width:1.6em}.breadcrumbsContainer_Z_bl{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}.breadcrumbHomeIcon_OVgt{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}.searchQueryInput_CFBF{background:var(--ifm-background-color);border:var(--ifm-global-border-width) solid var(--ifm-color-content-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-font-color-base);font-size:var(--ifm-font-size-base);margin-bottom:1rem;padding:.5rem;width:100%}.searchResultItem_U687{border-bottom:1px solid #dfe3e8;padding:1rem 0}.searchResultItemPath_uIbk{color:var(--ifm-color-content-secondary);font-size:.8rem;margin:.5rem 0 0}.searchResultItemSummary_oZHr{font-style:italic;margin:.5rem 0 0}.backToTopButton_sjWU{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.backToTopButton_sjWU:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_xfvO{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark]:root{--docusaurus-collapse-button-bg:#ffffff0d;--docusaurus-collapse-button-bg-hover:#ffffff1a}.collapseSidebarButton_PEFL,.docSidebarContainer_b6E3,.sidebarLogo_isFc{display:none}.docMainContainer_gTbr,.docPage__5DB{display:flex;width:100%}@media (min-width:997px){.collapseSidebarButton_PEFL,.expandButton_m80_{background-color:var(--docusaurus-collapse-button-bg);position:sticky}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_gvF7,.announcementBarPlaceholder_vyr4{flex-basis:50px}.searchBox_ZlJk{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.lastUpdated_vwxv{text-align:right}.tocMobile_ITEo{display:none}.docItemCol_VOVn{max-width:75%!important}.collapseSidebarButton_PEFL{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px}.collapseSidebarButtonIcon_kv0_{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_BlDH,[dir=rtl] .collapseSidebarButtonIcon_kv0_{transform:rotate(0)}.collapseSidebarButton_PEFL:focus,.collapseSidebarButton_PEFL:hover,.expandButton_m80_:focus,.expandButton_m80_:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.menuHtmlItem_M9Kj{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_SIkG{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_SIkG{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_GW3s{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_njMd{display:flex;flex-direction:column;height:100%;max-height:100vh;padding-top:var(--ifm-navbar-height);position:sticky;top:0;transition:opacity 50ms;width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_wUlq{padding-top:0}.sidebarHidden_VK0M{height:0;opacity:0;overflow:hidden;visibility:hidden}.sidebarLogo_isFc{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_isFc img{height:2rem;margin-right:.5rem}.expandButton_m80_{align-items:center;display:flex;height:100%;justify-content:center;max-height:100vh;top:0;transition:background-color var(--ifm-transition-fast) ease}[dir=rtl] .expandButtonIcon_BlDH{transform:rotate(180deg)}.docSidebarContainer_b6E3{border-right:1px solid var(--ifm-toc-border-color);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_b3ry{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.docMainContainer_gTbr{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_Uz_u{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_czyv{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_DEke,.footer__link-separator,.navbar__item,.tableOfContents_bqdL{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.searchBox_ZlJk{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_F8PC{padding:0 .3rem}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}.navbar__search-input:not(:focus){width:2rem}.searchBar_RVTs .dropdownMenu_qbY6{max-width:calc(100vw - var(--ifm-navbar-padding-horizontal)*2);width:var(--search-local-modal-width-sm,340px)}.searchHintContainer_Pkmr{display:none}}@media (hover:hover){.backToTopButton_sjWU:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media print{.announcementBar_mb4j,.footer,.menu,.navbar,.pagination-nav,.table-of-contents,.tocMobile_ITEo{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_e6Vv{white-space:pre-wrap}} \ No newline at end of file diff --git a/pr-preview/pr-346/assets/images/applied-cryptography-41972400cd69dc964208e0c1b09f5110.jpeg b/pr-preview/pr-346/assets/images/applied-cryptography-41972400cd69dc964208e0c1b09f5110.jpeg new file mode 100644 index 00000000..5d1a9772 Binary files /dev/null and b/pr-preview/pr-346/assets/images/applied-cryptography-41972400cd69dc964208e0c1b09f5110.jpeg differ diff --git a/pr-preview/pr-346/assets/images/atat-the-unix-operating-system-69b2792f90350605dcf31c0faaebce37.png b/pr-preview/pr-346/assets/images/atat-the-unix-operating-system-69b2792f90350605dcf31c0faaebce37.png new file mode 100644 index 00000000..abab472f Binary files /dev/null and b/pr-preview/pr-346/assets/images/atat-the-unix-operating-system-69b2792f90350605dcf31c0faaebce37.png differ diff --git a/pr-preview/pr-346/assets/images/aws-congratulations-4cded50a75870c1f24e22e09cc889449.png b/pr-preview/pr-346/assets/images/aws-congratulations-4cded50a75870c1f24e22e09cc889449.png new file mode 100644 index 00000000..4d992d7e Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-congratulations-4cded50a75870c1f24e22e09cc889449.png differ diff --git a/pr-preview/pr-346/assets/images/aws-connect-to-instance-f39a3426fa9dcdd818e7856f1d9a4423.png b/pr-preview/pr-346/assets/images/aws-connect-to-instance-f39a3426fa9dcdd818e7856f1d9a4423.png new file mode 100644 index 00000000..9c8dd3ca Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-connect-to-instance-f39a3426fa9dcdd818e7856f1d9a4423.png differ diff --git a/pr-preview/pr-346/assets/images/aws-dashboard-keypairs-6fb245665dd23809d3e537f2d1851878.png b/pr-preview/pr-346/assets/images/aws-dashboard-keypairs-6fb245665dd23809d3e537f2d1851878.png new file mode 100644 index 00000000..14680647 Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-dashboard-keypairs-6fb245665dd23809d3e537f2d1851878.png differ diff --git a/pr-preview/pr-346/assets/images/aws-import-key-pair-details-1d4cf6592734dd97d05033b372153cbb.png b/pr-preview/pr-346/assets/images/aws-import-key-pair-details-1d4cf6592734dd97d05033b372153cbb.png new file mode 100644 index 00000000..33f80b7f Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-import-key-pair-details-1d4cf6592734dd97d05033b372153cbb.png differ diff --git a/pr-preview/pr-346/assets/images/aws-import-key-pair-e66e68e77de3159f03ff19fa4440bdd0.png b/pr-preview/pr-346/assets/images/aws-import-key-pair-e66e68e77de3159f03ff19fa4440bdd0.png new file mode 100644 index 00000000..ab7a75fd Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-import-key-pair-e66e68e77de3159f03ff19fa4440bdd0.png differ diff --git a/pr-preview/pr-346/assets/images/aws-instance-ssh-details-44d96c55f359b471be06ed14b77b092b.png b/pr-preview/pr-346/assets/images/aws-instance-ssh-details-44d96c55f359b471be06ed14b77b092b.png new file mode 100644 index 00000000..06aa47f9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-instance-ssh-details-44d96c55f359b471be06ed14b77b092b.png differ diff --git a/pr-preview/pr-346/assets/images/aws-launch-instance-a27d37a4beaab0cea4fb49b32799f890.png b/pr-preview/pr-346/assets/images/aws-launch-instance-a27d37a4beaab0cea4fb49b32799f890.png new file mode 100644 index 00000000..831066a0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-launch-instance-a27d37a4beaab0cea4fb49b32799f890.png differ diff --git a/pr-preview/pr-346/assets/images/aws-select-instance-476ce57f7b478a9145f29b226ddda177.png b/pr-preview/pr-346/assets/images/aws-select-instance-476ce57f7b478a9145f29b226ddda177.png new file mode 100644 index 00000000..11cb389d Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-select-instance-476ce57f7b478a9145f29b226ddda177.png differ diff --git a/pr-preview/pr-346/assets/images/aws-select-key-pair-acd5584bd22e7a93f66f4cf2bd026465.png b/pr-preview/pr-346/assets/images/aws-select-key-pair-acd5584bd22e7a93f66f4cf2bd026465.png new file mode 100644 index 00000000..9e1ec67a Binary files /dev/null and b/pr-preview/pr-346/assets/images/aws-select-key-pair-acd5584bd22e7a93f66f4cf2bd026465.png differ diff --git a/pr-preview/pr-346/assets/images/bash-version-14d5b16e4a01db7b421bc9128a1f35e3.png b/pr-preview/pr-346/assets/images/bash-version-14d5b16e4a01db7b421bc9128a1f35e3.png new file mode 100644 index 00000000..e68b412b Binary files /dev/null and b/pr-preview/pr-346/assets/images/bash-version-14d5b16e4a01db7b421bc9128a1f35e3.png differ diff --git a/pr-preview/pr-346/assets/images/begin-end-47e0bb8e42e059d45f5d8727cb430333.gif b/pr-preview/pr-346/assets/images/begin-end-47e0bb8e42e059d45f5d8727cb430333.gif new file mode 100644 index 00000000..5ca7cd9f Binary files /dev/null and b/pr-preview/pr-346/assets/images/begin-end-47e0bb8e42e059d45f5d8727cb430333.gif differ diff --git a/pr-preview/pr-346/assets/images/blocked-shell-206a1fc7743e30d0246b56500f4174a7.gif b/pr-preview/pr-346/assets/images/blocked-shell-206a1fc7743e30d0246b56500f4174a7.gif new file mode 100644 index 00000000..25d10856 Binary files /dev/null and b/pr-preview/pr-346/assets/images/blocked-shell-206a1fc7743e30d0246b56500f4174a7.gif differ diff --git a/pr-preview/pr-346/assets/images/cat-a81e2704f0cdbb0212bd5de6b3c53cf8.png b/pr-preview/pr-346/assets/images/cat-a81e2704f0cdbb0212bd5de6b3c53cf8.png new file mode 100644 index 00000000..5bb53c45 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cat-a81e2704f0cdbb0212bd5de6b3c53cf8.png differ diff --git a/pr-preview/pr-346/assets/images/cat-redirect-f1b5c975936a1b619eef1198efcb51bd.png b/pr-preview/pr-346/assets/images/cat-redirect-f1b5c975936a1b619eef1198efcb51bd.png new file mode 100644 index 00000000..fd1b9ef0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cat-redirect-f1b5c975936a1b619eef1198efcb51bd.png differ diff --git a/pr-preview/pr-346/assets/images/cat-wildcard-14d0eefcb7dd7221857c5f39db5f0dfc.png b/pr-preview/pr-346/assets/images/cat-wildcard-14d0eefcb7dd7221857c5f39db5f0dfc.png new file mode 100644 index 00000000..3a90838b Binary files /dev/null and b/pr-preview/pr-346/assets/images/cat-wildcard-14d0eefcb7dd7221857c5f39db5f0dfc.png differ diff --git a/pr-preview/pr-346/assets/images/cd-11037ff9f63cacc208544113f72c2910.png b/pr-preview/pr-346/assets/images/cd-11037ff9f63cacc208544113f72c2910.png new file mode 100644 index 00000000..b7902e3e Binary files /dev/null and b/pr-preview/pr-346/assets/images/cd-11037ff9f63cacc208544113f72c2910.png differ diff --git a/pr-preview/pr-346/assets/images/cd-dash-53e29f0f8230e4ec479620a71dc61a77.png b/pr-preview/pr-346/assets/images/cd-dash-53e29f0f8230e4ec479620a71dc61a77.png new file mode 100644 index 00000000..cd230870 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cd-dash-53e29f0f8230e4ec479620a71dc61a77.png differ diff --git a/pr-preview/pr-346/assets/images/cd-dot-dot-c778020499ddc2c49509232cdc15c9f1.png b/pr-preview/pr-346/assets/images/cd-dot-dot-c778020499ddc2c49509232cdc15c9f1.png new file mode 100644 index 00000000..a7e7db41 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cd-dot-dot-c778020499ddc2c49509232cdc15c9f1.png differ diff --git a/pr-preview/pr-346/assets/images/cd-dot-dot-scripts-4fddc6579b157b2474429d88080cdad5.png b/pr-preview/pr-346/assets/images/cd-dot-dot-scripts-4fddc6579b157b2474429d88080cdad5.png new file mode 100644 index 00000000..e6eeac45 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cd-dot-dot-scripts-4fddc6579b157b2474429d88080cdad5.png differ diff --git a/pr-preview/pr-346/assets/images/cd-home-cb88b95f55c0104849cff3bdd76f0e6a.png b/pr-preview/pr-346/assets/images/cd-home-cb88b95f55c0104849cff3bdd76f0e6a.png new file mode 100644 index 00000000..144a9237 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cd-home-cb88b95f55c0104849cff3bdd76f0e6a.png differ diff --git a/pr-preview/pr-346/assets/images/cht-7a5f65ba5518dfb1dcd61899d9f8d0d6.sh b/pr-preview/pr-346/assets/images/cht-7a5f65ba5518dfb1dcd61899d9f8d0d6.sh new file mode 100644 index 00000000..caa7f3cf Binary files /dev/null and b/pr-preview/pr-346/assets/images/cht-7a5f65ba5518dfb1dcd61899d9f8d0d6.sh differ diff --git a/pr-preview/pr-346/assets/images/cht.sh-question-ff2730c7b1d7834b6ac3d8f6d5babaa7.png b/pr-preview/pr-346/assets/images/cht.sh-question-ff2730c7b1d7834b6ac3d8f6d5babaa7.png new file mode 100644 index 00000000..b2f0cc99 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cht.sh-question-ff2730c7b1d7834b6ac3d8f6d5babaa7.png differ diff --git a/pr-preview/pr-346/assets/images/clear-screen-e040f209848e4b4e038e9e2cc0071e65.gif b/pr-preview/pr-346/assets/images/clear-screen-e040f209848e4b4e038e9e2cc0071e65.gif new file mode 100644 index 00000000..6965a6ff Binary files /dev/null and b/pr-preview/pr-346/assets/images/clear-screen-e040f209848e4b4e038e9e2cc0071e65.gif differ diff --git a/pr-preview/pr-346/assets/images/command-line-a47c08acd86b732173b3f6dfc1955bb1.png b/pr-preview/pr-346/assets/images/command-line-a47c08acd86b732173b3f6dfc1955bb1.png new file mode 100644 index 00000000..6cfafc57 Binary files /dev/null and b/pr-preview/pr-346/assets/images/command-line-a47c08acd86b732173b3f6dfc1955bb1.png differ diff --git a/pr-preview/pr-346/assets/images/cp-da8c2c5c8cea67d43451d876d4bb86ba.png b/pr-preview/pr-346/assets/images/cp-da8c2c5c8cea67d43451d876d4bb86ba.png new file mode 100644 index 00000000..9a41bff9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cp-da8c2c5c8cea67d43451d876d4bb86ba.png differ diff --git a/pr-preview/pr-346/assets/images/create-free-account-b40baad1adf05123d62f7572e097ea8f.png b/pr-preview/pr-346/assets/images/create-free-account-b40baad1adf05123d62f7572e097ea8f.png new file mode 100644 index 00000000..c7eecdbb Binary files /dev/null and b/pr-preview/pr-346/assets/images/create-free-account-b40baad1adf05123d62f7572e097ea8f.png differ diff --git a/pr-preview/pr-346/assets/images/ctrl-v-on-ubuntu-6dd4bf41757a440a10f149f01068e3a6.png b/pr-preview/pr-346/assets/images/ctrl-v-on-ubuntu-6dd4bf41757a440a10f149f01068e3a6.png new file mode 100644 index 00000000..55e441d8 Binary files /dev/null and b/pr-preview/pr-346/assets/images/ctrl-v-on-ubuntu-6dd4bf41757a440a10f149f01068e3a6.png differ diff --git a/pr-preview/pr-346/assets/images/cygwin-1-c2222807ad8531efa8f904ebd7d47ac5.png b/pr-preview/pr-346/assets/images/cygwin-1-c2222807ad8531efa8f904ebd7d47ac5.png new file mode 100644 index 00000000..fd9aedaa Binary files /dev/null and b/pr-preview/pr-346/assets/images/cygwin-1-c2222807ad8531efa8f904ebd7d47ac5.png differ diff --git a/pr-preview/pr-346/assets/images/cygwin-2-9a538f448ea5789e7fbdcaecb5c85c88.png b/pr-preview/pr-346/assets/images/cygwin-2-9a538f448ea5789e7fbdcaecb5c85c88.png new file mode 100644 index 00000000..7cf56532 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cygwin-2-9a538f448ea5789e7fbdcaecb5c85c88.png differ diff --git a/pr-preview/pr-346/assets/images/cygwin-3-089db6ba17fc27105f49bfeb2b32394f.png b/pr-preview/pr-346/assets/images/cygwin-3-089db6ba17fc27105f49bfeb2b32394f.png new file mode 100644 index 00000000..5d1ab198 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cygwin-3-089db6ba17fc27105f49bfeb2b32394f.png differ diff --git a/pr-preview/pr-346/assets/images/cygwin-6-99fce814e1395c8a33c78e3c5a47718a.png b/pr-preview/pr-346/assets/images/cygwin-6-99fce814e1395c8a33c78e3c5a47718a.png new file mode 100644 index 00000000..1bdfe0a4 Binary files /dev/null and b/pr-preview/pr-346/assets/images/cygwin-6-99fce814e1395c8a33c78e3c5a47718a.png differ diff --git a/pr-preview/pr-346/assets/images/cygwin-7-12d4c00e0534c45cbfa33c0243730157.png b/pr-preview/pr-346/assets/images/cygwin-7-12d4c00e0534c45cbfa33c0243730157.png new file mode 100644 index 00000000..455528bf Binary files /dev/null and b/pr-preview/pr-346/assets/images/cygwin-7-12d4c00e0534c45cbfa33c0243730157.png differ diff --git a/pr-preview/pr-346/assets/images/cygwin-8-68d2071d9e5ad3122291f85c3c977252.png b/pr-preview/pr-346/assets/images/cygwin-8-68d2071d9e5ad3122291f85c3c977252.png new file mode 100644 index 00000000..183cf93f Binary files /dev/null and b/pr-preview/pr-346/assets/images/cygwin-8-68d2071d9e5ad3122291f85c3c977252.png differ diff --git a/pr-preview/pr-346/assets/images/delete-next-word-0598cce2c528dd0bf427bd22a0d35640.gif b/pr-preview/pr-346/assets/images/delete-next-word-0598cce2c528dd0bf427bd22a0d35640.gif new file mode 100644 index 00000000..a5b708cb Binary files /dev/null and b/pr-preview/pr-346/assets/images/delete-next-word-0598cce2c528dd0bf427bd22a0d35640.gif differ diff --git a/pr-preview/pr-346/assets/images/delete-to-end-ee91dd9badf75df28dbc5c53c95ce7e7.gif b/pr-preview/pr-346/assets/images/delete-to-end-ee91dd9badf75df28dbc5c53c95ce7e7.gif new file mode 100644 index 00000000..ff0714c0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/delete-to-end-ee91dd9badf75df28dbc5c53c95ce7e7.gif differ diff --git a/pr-preview/pr-346/assets/images/delete-undo-3445c75c45b36ac50992818f4df86c6d.gif b/pr-preview/pr-346/assets/images/delete-undo-3445c75c45b36ac50992818f4df86c6d.gif new file mode 100644 index 00000000..cf9076aa Binary files /dev/null and b/pr-preview/pr-346/assets/images/delete-undo-3445c75c45b36ac50992818f4df86c6d.gif differ diff --git a/pr-preview/pr-346/assets/images/diagram-cat-sort-uniq-pipeline-8c8d76566f351b4b9b900dde52af86b3.png b/pr-preview/pr-346/assets/images/diagram-cat-sort-uniq-pipeline-8c8d76566f351b4b9b900dde52af86b3.png new file mode 100644 index 00000000..f8c11dd2 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-cat-sort-uniq-pipeline-8c8d76566f351b4b9b900dde52af86b3.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-input-examples-e014dd4998bee0b50a94849ad55b01ce.png b/pr-preview/pr-346/assets/images/diagram-input-examples-e014dd4998bee0b50a94849ad55b01ce.png new file mode 100644 index 00000000..93ac050a Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-input-examples-e014dd4998bee0b50a94849ad55b01ce.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-input-program-output-fb71da951178e72bf4504bd292743d1c.png b/pr-preview/pr-346/assets/images/diagram-input-program-output-fb71da951178e72bf4504bd292743d1c.png new file mode 100644 index 00000000..d5db7bd7 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-input-program-output-fb71da951178e72bf4504bd292743d1c.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-output-examples-27e30c4a4036b2591e10e8c4fca7dc73.png b/pr-preview/pr-346/assets/images/diagram-output-examples-27e30c4a4036b2591e10e8c4fca7dc73.png new file mode 100644 index 00000000..399347b1 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-output-examples-27e30c4a4036b2591e10e8c4fca7dc73.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-shell-keyboard-terminal-0475940cdf40bbcc8a329c090aa9e76a.png b/pr-preview/pr-346/assets/images/diagram-shell-keyboard-terminal-0475940cdf40bbcc8a329c090aa9e76a.png new file mode 100644 index 00000000..4a8dd7b9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-shell-keyboard-terminal-0475940cdf40bbcc8a329c090aa9e76a.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-sort-040db6e92c70e60612c9f711e81c9612.png b/pr-preview/pr-346/assets/images/diagram-sort-040db6e92c70e60612c9f711e81c9612.png new file mode 100644 index 00000000..b55345aa Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-sort-040db6e92c70e60612c9f711e81c9612.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-stderr-d0845508087975a7d58ebac63e3a8cd5.png b/pr-preview/pr-346/assets/images/diagram-stderr-d0845508087975a7d58ebac63e3a8cd5.png new file mode 100644 index 00000000..23f85242 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-stderr-d0845508087975a7d58ebac63e3a8cd5.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-stderr-options-a2cde4aa6177249c25dd9e5c0c62667a.png b/pr-preview/pr-346/assets/images/diagram-stderr-options-a2cde4aa6177249c25dd9e5c0c62667a.png new file mode 100644 index 00000000..a35b302c Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-stderr-options-a2cde4aa6177249c25dd9e5c0c62667a.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-stderr-redirect-c7d8fe2d93a8cdb248924cc13027b59e.png b/pr-preview/pr-346/assets/images/diagram-stderr-redirect-c7d8fe2d93a8cdb248924cc13027b59e.png new file mode 100644 index 00000000..30a4b825 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-stderr-redirect-c7d8fe2d93a8cdb248924cc13027b59e.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-stdin-stdout-stderr-702e578630d8d39c813d7d88c270c339.png b/pr-preview/pr-346/assets/images/diagram-stdin-stdout-stderr-702e578630d8d39c813d7d88c270c339.png new file mode 100644 index 00000000..24a51ce6 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-stdin-stdout-stderr-702e578630d8d39c813d7d88c270c339.png differ diff --git a/pr-preview/pr-346/assets/images/diagram-tee-6ad6dadcfa804f75f96b36807ffd688b.png b/pr-preview/pr-346/assets/images/diagram-tee-6ad6dadcfa804f75f96b36807ffd688b.png new file mode 100644 index 00000000..aa84b01a Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram-tee-6ad6dadcfa804f75f96b36807ffd688b.png differ diff --git a/pr-preview/pr-346/assets/images/diagram1-operating-system-3da3b52f3ef73f3e08db4eaa1fd7d8c7.png b/pr-preview/pr-346/assets/images/diagram1-operating-system-3da3b52f3ef73f3e08db4eaa1fd7d8c7.png new file mode 100644 index 00000000..b046d5a7 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram1-operating-system-3da3b52f3ef73f3e08db4eaa1fd7d8c7.png differ diff --git a/pr-preview/pr-346/assets/images/diagram2-the-kernel-and-user-space-48ea8009fa452ddb13745e5747286c4f.png b/pr-preview/pr-346/assets/images/diagram2-the-kernel-and-user-space-48ea8009fa452ddb13745e5747286c4f.png new file mode 100644 index 00000000..ae73120c Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram2-the-kernel-and-user-space-48ea8009fa452ddb13745e5747286c4f.png differ diff --git a/pr-preview/pr-346/assets/images/diagram3-terminal-and-shell-31620f593a4c3838051a5a6dcea17577.png b/pr-preview/pr-346/assets/images/diagram3-terminal-and-shell-31620f593a4c3838051a5a6dcea17577.png new file mode 100644 index 00000000..a3f3f043 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram3-terminal-and-shell-31620f593a4c3838051a5a6dcea17577.png differ diff --git a/pr-preview/pr-346/assets/images/diagram4-command-prompt-d1a46f63387ec11ebeb2cebb76b30696.png b/pr-preview/pr-346/assets/images/diagram4-command-prompt-d1a46f63387ec11ebeb2cebb76b30696.png new file mode 100644 index 00000000..b078da80 Binary files /dev/null and b/pr-preview/pr-346/assets/images/diagram4-command-prompt-d1a46f63387ec11ebeb2cebb76b30696.png differ diff --git a/pr-preview/pr-346/assets/images/echo-hello-shell-f1d92f9c20f15c0e5741c07537284d18.png b/pr-preview/pr-346/assets/images/echo-hello-shell-f1d92f9c20f15c0e5741c07537284d18.png new file mode 100644 index 00000000..0bfd49b5 Binary files /dev/null and b/pr-preview/pr-346/assets/images/echo-hello-shell-f1d92f9c20f15c0e5741c07537284d18.png differ diff --git a/pr-preview/pr-346/assets/images/echo-home-45ee24e5927f9a6f2845504ae08ef800.png b/pr-preview/pr-346/assets/images/echo-home-45ee24e5927f9a6f2845504ae08ef800.png new file mode 100644 index 00000000..ccee74b3 Binary files /dev/null and b/pr-preview/pr-346/assets/images/echo-home-45ee24e5927f9a6f2845504ae08ef800.png differ diff --git a/pr-preview/pr-346/assets/images/echo-home-6c8165310a37202fc6efe3eaa14a8456.png b/pr-preview/pr-346/assets/images/echo-home-6c8165310a37202fc6efe3eaa14a8456.png new file mode 100644 index 00000000..5744d15d Binary files /dev/null and b/pr-preview/pr-346/assets/images/echo-home-6c8165310a37202fc6efe3eaa14a8456.png differ diff --git a/pr-preview/pr-346/assets/images/edit-in-place-fbbf09c4fcab87a69ba499c5f390360f.gif b/pr-preview/pr-346/assets/images/edit-in-place-fbbf09c4fcab87a69ba499c5f390360f.gif new file mode 100644 index 00000000..612da448 Binary files /dev/null and b/pr-preview/pr-346/assets/images/edit-in-place-fbbf09c4fcab87a69ba499c5f390360f.gif differ diff --git a/pr-preview/pr-346/assets/images/editor-vs-code-e929474e6290a2d26d0d5ef4e198db23.gif b/pr-preview/pr-346/assets/images/editor-vs-code-e929474e6290a2d26d0d5ef4e198db23.gif new file mode 100644 index 00000000..a05f6fda Binary files /dev/null and b/pr-preview/pr-346/assets/images/editor-vs-code-e929474e6290a2d26d0d5ef4e198db23.gif differ diff --git a/pr-preview/pr-346/assets/images/effective-shell-dotfiles-showing-fork-7300a8e41f8e617d12fa74175e86141d.png b/pr-preview/pr-346/assets/images/effective-shell-dotfiles-showing-fork-7300a8e41f8e617d12fa74175e86141d.png new file mode 100644 index 00000000..b14082b7 Binary files /dev/null and b/pr-preview/pr-346/assets/images/effective-shell-dotfiles-showing-fork-7300a8e41f8e617d12fa74175e86141d.png differ diff --git a/pr-preview/pr-346/assets/images/email_list_excel-c952ce039f3d370ecb4bc9c17d8ab815.png b/pr-preview/pr-346/assets/images/email_list_excel-c952ce039f3d370ecb4bc9c17d8ab815.png new file mode 100644 index 00000000..558f3e1c Binary files /dev/null and b/pr-preview/pr-346/assets/images/email_list_excel-c952ce039f3d370ecb4bc9c17d8ab815.png differ diff --git a/pr-preview/pr-346/assets/images/example-bash-c0f87d553dded0884b96787b2a631d94.png b/pr-preview/pr-346/assets/images/example-bash-c0f87d553dded0884b96787b2a631d94.png new file mode 100644 index 00000000..b922dfa9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/example-bash-c0f87d553dded0884b96787b2a631d94.png differ diff --git a/pr-preview/pr-346/assets/images/example-bash-root-073a27146aa9bfbbcafc6464d071fa46.png b/pr-preview/pr-346/assets/images/example-bash-root-073a27146aa9bfbbcafc6464d071fa46.png new file mode 100644 index 00000000..1273f40a Binary files /dev/null and b/pr-preview/pr-346/assets/images/example-bash-root-073a27146aa9bfbbcafc6464d071fa46.png differ diff --git a/pr-preview/pr-346/assets/images/example-cmd-899ba21f4f211c9670c85bf64cb8d426.png b/pr-preview/pr-346/assets/images/example-cmd-899ba21f4f211c9670c85bf64cb8d426.png new file mode 100644 index 00000000..cb586ebb Binary files /dev/null and b/pr-preview/pr-346/assets/images/example-cmd-899ba21f4f211c9670c85bf64cb8d426.png differ diff --git a/pr-preview/pr-346/assets/images/example-explorer-086bee5a143984e5a134d1b73dcc3d3f.png b/pr-preview/pr-346/assets/images/example-explorer-086bee5a143984e5a134d1b73dcc3d3f.png new file mode 100644 index 00000000..06966dba Binary files /dev/null and b/pr-preview/pr-346/assets/images/example-explorer-086bee5a143984e5a134d1b73dcc3d3f.png differ diff --git a/pr-preview/pr-346/assets/images/example-iterm-zsh-0410a3717ef4f0e5b437ae51e1d62d2d.png b/pr-preview/pr-346/assets/images/example-iterm-zsh-0410a3717ef4f0e5b437ae51e1d62d2d.png new file mode 100644 index 00000000..4e1a7f08 Binary files /dev/null and b/pr-preview/pr-346/assets/images/example-iterm-zsh-0410a3717ef4f0e5b437ae51e1d62d2d.png differ diff --git a/pr-preview/pr-346/assets/images/example-powershell-ca476a5744e3e0feb12489ea113ad238.png b/pr-preview/pr-346/assets/images/example-powershell-ca476a5744e3e0feb12489ea113ad238.png new file mode 100644 index 00000000..5e269185 Binary files /dev/null and b/pr-preview/pr-346/assets/images/example-powershell-ca476a5744e3e0feb12489ea113ad238.png differ diff --git a/pr-preview/pr-346/assets/images/example-wsl-d00882cbdb7343c88834a9a3df92ae84.png b/pr-preview/pr-346/assets/images/example-wsl-d00882cbdb7343c88834a9a3df92ae84.png new file mode 100644 index 00000000..57d5b9a4 Binary files /dev/null and b/pr-preview/pr-346/assets/images/example-wsl-d00882cbdb7343c88834a9a3df92ae84.png differ diff --git a/pr-preview/pr-346/assets/images/file-a824267fcbcf509a0f197686e40ad111.png b/pr-preview/pr-346/assets/images/file-a824267fcbcf509a0f197686e40ad111.png new file mode 100644 index 00000000..da6b2ddf Binary files /dev/null and b/pr-preview/pr-346/assets/images/file-a824267fcbcf509a0f197686e40ad111.png differ diff --git a/pr-preview/pr-346/assets/images/file-jpeg-info-8ff5a2ca4bb1ba0916f224ce204a8e3d.png b/pr-preview/pr-346/assets/images/file-jpeg-info-8ff5a2ca4bb1ba0916f224ce204a8e3d.png new file mode 100644 index 00000000..df960a7e Binary files /dev/null and b/pr-preview/pr-346/assets/images/file-jpeg-info-8ff5a2ca4bb1ba0916f224ce204a8e3d.png differ diff --git a/pr-preview/pr-346/assets/images/forward-backwards-b24add7c23fc12c802be09c0f84a4676.gif b/pr-preview/pr-346/assets/images/forward-backwards-b24add7c23fc12c802be09c0f84a4676.gif new file mode 100644 index 00000000..3b62a989 Binary files /dev/null and b/pr-preview/pr-346/assets/images/forward-backwards-b24add7c23fc12c802be09c0f84a4676.gif differ diff --git a/pr-preview/pr-346/assets/images/git-add-273352897de39c41175ab1570deba674.png b/pr-preview/pr-346/assets/images/git-add-273352897de39c41175ab1570deba674.png new file mode 100644 index 00000000..7e8938ca Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-add-273352897de39c41175ab1570deba674.png differ diff --git a/pr-preview/pr-346/assets/images/git-checkout-b-6475f62943d9c57369f6e44991d92efe.png b/pr-preview/pr-346/assets/images/git-checkout-b-6475f62943d9c57369f6e44991d92efe.png new file mode 100644 index 00000000..7c24af22 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-checkout-b-6475f62943d9c57369f6e44991d92efe.png differ diff --git a/pr-preview/pr-346/assets/images/git-checkout-detached-head-1a94781840cf8e5c7a4fb3fd4b59ce1b.png b/pr-preview/pr-346/assets/images/git-checkout-detached-head-1a94781840cf8e5c7a4fb3fd4b59ce1b.png new file mode 100644 index 00000000..b6a6d2e9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-checkout-detached-head-1a94781840cf8e5c7a4fb3fd4b59ce1b.png differ diff --git a/pr-preview/pr-346/assets/images/git-commands-summary-61cc5e172ff5f185b40941120cd77518.png b/pr-preview/pr-346/assets/images/git-commands-summary-61cc5e172ff5f185b40941120cd77518.png new file mode 100644 index 00000000..89e418e3 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-commands-summary-61cc5e172ff5f185b40941120cd77518.png differ diff --git a/pr-preview/pr-346/assets/images/git-commit-910cd46c3ad343660e47a0b60cd70934.png b/pr-preview/pr-346/assets/images/git-commit-910cd46c3ad343660e47a0b60cd70934.png new file mode 100644 index 00000000..423464a4 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-commit-910cd46c3ad343660e47a0b60cd70934.png differ diff --git a/pr-preview/pr-346/assets/images/git-commit-number-2-9dba4c3b9c31e2268ea9ecbbfb1ed196.png b/pr-preview/pr-346/assets/images/git-commit-number-2-9dba4c3b9c31e2268ea9ecbbfb1ed196.png new file mode 100644 index 00000000..0e5e8560 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-commit-number-2-9dba4c3b9c31e2268ea9ecbbfb1ed196.png differ diff --git a/pr-preview/pr-346/assets/images/git-commit-on-aliases-84e8cf5ab03a662ab391896ba9438b7f.png b/pr-preview/pr-346/assets/images/git-commit-on-aliases-84e8cf5ab03a662ab391896ba9438b7f.png new file mode 100644 index 00000000..e0b57415 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-commit-on-aliases-84e8cf5ab03a662ab391896ba9438b7f.png differ diff --git a/pr-preview/pr-346/assets/images/git-diverged-branches-d7921427ff846f43f168d004001862fb.png b/pr-preview/pr-346/assets/images/git-diverged-branches-d7921427ff846f43f168d004001862fb.png new file mode 100644 index 00000000..18425e81 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-diverged-branches-d7921427ff846f43f168d004001862fb.png differ diff --git a/pr-preview/pr-346/assets/images/git-local-and-upstream-main-branch-0e3be26028c755377700a49fe606f4ea.png b/pr-preview/pr-346/assets/images/git-local-and-upstream-main-branch-0e3be26028c755377700a49fe606f4ea.png new file mode 100644 index 00000000..6601a669 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-local-and-upstream-main-branch-0e3be26028c755377700a49fe606f4ea.png differ diff --git a/pr-preview/pr-346/assets/images/git-log-showing-head-5a0e2000cb1a17a77297bbb98dd64a12.png b/pr-preview/pr-346/assets/images/git-log-showing-head-5a0e2000cb1a17a77297bbb98dd64a12.png new file mode 100644 index 00000000..b20bfa90 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-log-showing-head-5a0e2000cb1a17a77297bbb98dd64a12.png differ diff --git a/pr-preview/pr-346/assets/images/git-merge-fast-forwards-9cea41f3cdc7252424a0d46eb8aae96e.png b/pr-preview/pr-346/assets/images/git-merge-fast-forwards-9cea41f3cdc7252424a0d46eb8aae96e.png new file mode 100644 index 00000000..d83997e6 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-merge-fast-forwards-9cea41f3cdc7252424a0d46eb8aae96e.png differ diff --git a/pr-preview/pr-346/assets/images/git-merge-prepare-fast-forwards-62b89b1712bd04787afe47b3c955bc96.png b/pr-preview/pr-346/assets/images/git-merge-prepare-fast-forwards-62b89b1712bd04787afe47b3c955bc96.png new file mode 100644 index 00000000..3f3c67dd Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-merge-prepare-fast-forwards-62b89b1712bd04787afe47b3c955bc96.png differ diff --git a/pr-preview/pr-346/assets/images/git-merge-recursive-e40ed73171c316ca6754a895d84beb07.png b/pr-preview/pr-346/assets/images/git-merge-recursive-e40ed73171c316ca6754a895d84beb07.png new file mode 100644 index 00000000..d50ad134 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-merge-recursive-e40ed73171c316ca6754a895d84beb07.png differ diff --git a/pr-preview/pr-346/assets/images/git-reset-ff25fe1fd1489ef9f82cf1b0cc5e7e73.png b/pr-preview/pr-346/assets/images/git-reset-ff25fe1fd1489ef9f82cf1b0cc5e7e73.png new file mode 100644 index 00000000..285fafd8 Binary files /dev/null and b/pr-preview/pr-346/assets/images/git-reset-ff25fe1fd1489ef9f82cf1b0cc5e7e73.png differ diff --git a/pr-preview/pr-346/assets/images/github-add-readme-contents-8bfa1c5b08ca9b861c081bc6e8317947.png b/pr-preview/pr-346/assets/images/github-add-readme-contents-8bfa1c5b08ca9b861c081bc6e8317947.png new file mode 100644 index 00000000..6e0ec52e Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-add-readme-contents-8bfa1c5b08ca9b861c081bc6e8317947.png differ diff --git a/pr-preview/pr-346/assets/images/github-create-new-repository-ee5623925a3de2d4fa391aae37b14b42.png b/pr-preview/pr-346/assets/images/github-create-new-repository-ee5623925a3de2d4fa391aae37b14b42.png new file mode 100644 index 00000000..4cc12c63 Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-create-new-repository-ee5623925a3de2d4fa391aae37b14b42.png differ diff --git a/pr-preview/pr-346/assets/images/github-dotfiles-initial-view-6e5f9edeb6dff11ee5ae64f841ae1f9b.png b/pr-preview/pr-346/assets/images/github-dotfiles-initial-view-6e5f9edeb6dff11ee5ae64f841ae1f9b.png new file mode 100644 index 00000000..b6e16003 Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-dotfiles-initial-view-6e5f9edeb6dff11ee5ae64f841ae1f9b.png differ diff --git a/pr-preview/pr-346/assets/images/github-dotfiles-repository-with-readme-cb524f459cdb29d48a9933189bd3bc4d.png b/pr-preview/pr-346/assets/images/github-dotfiles-repository-with-readme-cb524f459cdb29d48a9933189bd3bc4d.png new file mode 100644 index 00000000..5f79b519 Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-dotfiles-repository-with-readme-cb524f459cdb29d48a9933189bd3bc4d.png differ diff --git a/pr-preview/pr-346/assets/images/github-open-pull-request-598b73e597294d5f00426c11d83ec597.png b/pr-preview/pr-346/assets/images/github-open-pull-request-598b73e597294d5f00426c11d83ec597.png new file mode 100644 index 00000000..0399fe9a Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-open-pull-request-598b73e597294d5f00426c11d83ec597.png differ diff --git a/pr-preview/pr-346/assets/images/github-readme-commit-9d7d429d3aa2fc6c7c38d08bd6fe02af.png b/pr-preview/pr-346/assets/images/github-readme-commit-9d7d429d3aa2fc6c7c38d08bd6fe02af.png new file mode 100644 index 00000000..642d3288 Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-readme-commit-9d7d429d3aa2fc6c7c38d08bd6fe02af.png differ diff --git a/pr-preview/pr-346/assets/images/github-repository-setup-47230fb424354935066e6267b2033332.png b/pr-preview/pr-346/assets/images/github-repository-setup-47230fb424354935066e6267b2033332.png new file mode 100644 index 00000000..b2cab939 Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-repository-setup-47230fb424354935066e6267b2033332.png differ diff --git a/pr-preview/pr-346/assets/images/github-what-do-you-want-to-do-first-0a76936acd3e228f156f540c4498f93c.png b/pr-preview/pr-346/assets/images/github-what-do-you-want-to-do-first-0a76936acd3e228f156f540c4498f93c.png new file mode 100644 index 00000000..3ac2eac0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/github-what-do-you-want-to-do-first-0a76936acd3e228f156f540c4498f93c.png differ diff --git a/pr-preview/pr-346/assets/images/image-asr-33-989ef8e0dc1a64b4d00052abeae25e40.jpg b/pr-preview/pr-346/assets/images/image-asr-33-989ef8e0dc1a64b4d00052abeae25e40.jpg new file mode 100644 index 00000000..1507abc0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/image-asr-33-989ef8e0dc1a64b4d00052abeae25e40.jpg differ diff --git a/pr-preview/pr-346/assets/images/image-ibm3486-1acadd2ab0ee18512fe79316bec727fd.jpg b/pr-preview/pr-346/assets/images/image-ibm3486-1acadd2ab0ee18512fe79316bec727fd.jpg new file mode 100644 index 00000000..52679d1b Binary files /dev/null and b/pr-preview/pr-346/assets/images/image-ibm3486-1acadd2ab0ee18512fe79316bec727fd.jpg differ diff --git a/pr-preview/pr-346/assets/images/image-ohmyzsh-a26c1a89776b0966d0577fb6f7e35576.jpg b/pr-preview/pr-346/assets/images/image-ohmyzsh-a26c1a89776b0966d0577fb6f7e35576.jpg new file mode 100644 index 00000000..5af278ff Binary files /dev/null and b/pr-preview/pr-346/assets/images/image-ohmyzsh-a26c1a89776b0966d0577fb6f7e35576.jpg differ diff --git a/pr-preview/pr-346/assets/images/image-psforest-689f9f137b2e62cfebe51023f8b35202.png b/pr-preview/pr-346/assets/images/image-psforest-689f9f137b2e62cfebe51023f8b35202.png new file mode 100644 index 00000000..db8182ab Binary files /dev/null and b/pr-preview/pr-346/assets/images/image-psforest-689f9f137b2e62cfebe51023f8b35202.png differ diff --git a/pr-preview/pr-346/assets/images/image-walnut-93cb9b0e2b015cbc5de26cd2c4483ecd.jpg b/pr-preview/pr-346/assets/images/image-walnut-93cb9b0e2b015cbc5de26cd2c4483ecd.jpg new file mode 100644 index 00000000..9460d3ee Binary files /dev/null and b/pr-preview/pr-346/assets/images/image-walnut-93cb9b0e2b015cbc5de26cd2c4483ecd.jpg differ diff --git a/pr-preview/pr-346/assets/images/kill-job-19fb1d0256beee272a6c317bd29c4c2e.gif b/pr-preview/pr-346/assets/images/kill-job-19fb1d0256beee272a6c317bd29c4c2e.gif new file mode 100644 index 00000000..173ca803 Binary files /dev/null and b/pr-preview/pr-346/assets/images/kill-job-19fb1d0256beee272a6c317bd29c4c2e.gif differ diff --git a/pr-preview/pr-346/assets/images/linux-shell-5ef723374126e0ea4c3c54483987a629.png b/pr-preview/pr-346/assets/images/linux-shell-5ef723374126e0ea4c3c54483987a629.png new file mode 100644 index 00000000..24c29430 Binary files /dev/null and b/pr-preview/pr-346/assets/images/linux-shell-5ef723374126e0ea4c3c54483987a629.png differ diff --git a/pr-preview/pr-346/assets/images/ls-620b55bfd11e8f1bbb53da22b569b2ee.png b/pr-preview/pr-346/assets/images/ls-620b55bfd11e8f1bbb53da22b569b2ee.png new file mode 100644 index 00000000..03057e5e Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-620b55bfd11e8f1bbb53da22b569b2ee.png differ diff --git a/pr-preview/pr-346/assets/images/ls-applications-95adadfe6a72bee754edda918dfdcac0.png b/pr-preview/pr-346/assets/images/ls-applications-95adadfe6a72bee754edda918dfdcac0.png new file mode 100644 index 00000000..14a19b30 Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-applications-95adadfe6a72bee754edda918dfdcac0.png differ diff --git a/pr-preview/pr-346/assets/images/ls-applications-windows-1d30de599a44ee545ad631eb5c3176b9.png b/pr-preview/pr-346/assets/images/ls-applications-windows-1d30de599a44ee545ad631eb5c3176b9.png new file mode 100644 index 00000000..dc38f7a0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-applications-windows-1d30de599a44ee545ad631eb5c3176b9.png differ diff --git a/pr-preview/pr-346/assets/images/ls-applications-windows-error-95571fda84ee478ee5678922b29fe185.png b/pr-preview/pr-346/assets/images/ls-applications-windows-error-95571fda84ee478ee5678922b29fe185.png new file mode 100644 index 00000000..018622f1 Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-applications-windows-error-95571fda84ee478ee5678922b29fe185.png differ diff --git a/pr-preview/pr-346/assets/images/ls-df7bd66eeae98e04b0422d10ccd6cb68.png b/pr-preview/pr-346/assets/images/ls-df7bd66eeae98e04b0422d10ccd6cb68.png new file mode 100644 index 00000000..20589948 Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-df7bd66eeae98e04b0422d10ccd6cb68.png differ diff --git a/pr-preview/pr-346/assets/images/ls-downloads-0dd8669df2ed45c5cced6b0c642b676d.png b/pr-preview/pr-346/assets/images/ls-downloads-0dd8669df2ed45c5cced6b0c642b676d.png new file mode 100644 index 00000000..8c0ee54f Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-downloads-0dd8669df2ed45c5cced6b0c642b676d.png differ diff --git a/pr-preview/pr-346/assets/images/ls-home-90c668ffbc2eb7ac22715c623fdb576c.png b/pr-preview/pr-346/assets/images/ls-home-90c668ffbc2eb7ac22715c623fdb576c.png new file mode 100644 index 00000000..85dd71c3 Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-home-90c668ffbc2eb7ac22715c623fdb576c.png differ diff --git a/pr-preview/pr-346/assets/images/ls-l-feba8ca271f4f43e1580bd6445adfba3.png b/pr-preview/pr-346/assets/images/ls-l-feba8ca271f4f43e1580bd6445adfba3.png new file mode 100644 index 00000000..856b250b Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-l-feba8ca271f4f43e1580bd6445adfba3.png differ diff --git a/pr-preview/pr-346/assets/images/ls-movies-apps-5f10acf5a3b015e153365771d99caf13.png b/pr-preview/pr-346/assets/images/ls-movies-apps-5f10acf5a3b015e153365771d99caf13.png new file mode 100644 index 00000000..fc7e23cd Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-movies-apps-5f10acf5a3b015e153365771d99caf13.png differ diff --git a/pr-preview/pr-346/assets/images/ls-music-8dfbb67185c5a33fb2f3fd8d2c6d8baa.png b/pr-preview/pr-346/assets/images/ls-music-8dfbb67185c5a33fb2f3fd8d2c6d8baa.png new file mode 100644 index 00000000..5c9088bd Binary files /dev/null and b/pr-preview/pr-346/assets/images/ls-music-8dfbb67185c5a33fb2f3fd8d2c6d8baa.png differ diff --git a/pr-preview/pr-346/assets/images/mac-shell-e2a527be5dd0a060b69e863b34e3573a.png b/pr-preview/pr-346/assets/images/mac-shell-e2a527be5dd0a060b69e863b34e3573a.png new file mode 100644 index 00000000..1f4cdd04 Binary files /dev/null and b/pr-preview/pr-346/assets/images/mac-shell-e2a527be5dd0a060b69e863b34e3573a.png differ diff --git a/pr-preview/pr-346/assets/images/man-sed-3636d879748281f9a8f2110a36bacac0.png b/pr-preview/pr-346/assets/images/man-sed-3636d879748281f9a8f2110a36bacac0.png new file mode 100644 index 00000000..81c87553 Binary files /dev/null and b/pr-preview/pr-346/assets/images/man-sed-3636d879748281f9a8f2110a36bacac0.png differ diff --git a/pr-preview/pr-346/assets/images/markdown-basic-b0fbc2ff6ef902c8fc1f794bed0fb12a.png b/pr-preview/pr-346/assets/images/markdown-basic-b0fbc2ff6ef902c8fc1f794bed0fb12a.png new file mode 100644 index 00000000..7e7bd8c9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/markdown-basic-b0fbc2ff6ef902c8fc1f794bed0fb12a.png differ diff --git a/pr-preview/pr-346/assets/images/mkdir-57c2af124ac85f07cb18f8b00fcfde1f.png b/pr-preview/pr-346/assets/images/mkdir-57c2af124ac85f07cb18f8b00fcfde1f.png new file mode 100644 index 00000000..58ba29b8 Binary files /dev/null and b/pr-preview/pr-346/assets/images/mkdir-57c2af124ac85f07cb18f8b00fcfde1f.png differ diff --git a/pr-preview/pr-346/assets/images/mkdirp-4684ca958e3795dedccfed26e56dae22.png b/pr-preview/pr-346/assets/images/mkdirp-4684ca958e3795dedccfed26e56dae22.png new file mode 100644 index 00000000..cca4f775 Binary files /dev/null and b/pr-preview/pr-346/assets/images/mkdirp-4684ca958e3795dedccfed26e56dae22.png differ diff --git a/pr-preview/pr-346/assets/images/moving-to-home-164dc619f0084c7e1ba79cd1b6486e89.png b/pr-preview/pr-346/assets/images/moving-to-home-164dc619f0084c7e1ba79cd1b6486e89.png new file mode 100644 index 00000000..1d1058bc Binary files /dev/null and b/pr-preview/pr-346/assets/images/moving-to-home-164dc619f0084c7e1ba79cd1b6486e89.png differ diff --git a/pr-preview/pr-346/assets/images/multiple-editors-bf231315b9a8296347112dff2d7f253b.png b/pr-preview/pr-346/assets/images/multiple-editors-bf231315b9a8296347112dff2d7f253b.png new file mode 100644 index 00000000..a1f53d24 Binary files /dev/null and b/pr-preview/pr-346/assets/images/multiple-editors-bf231315b9a8296347112dff2d7f253b.png differ diff --git a/pr-preview/pr-346/assets/images/multiplexer-screenshot-d99b6247558814ee9dc6842816d70257.png b/pr-preview/pr-346/assets/images/multiplexer-screenshot-d99b6247558814ee9dc6842816d70257.png new file mode 100644 index 00000000..550d7e76 Binary files /dev/null and b/pr-preview/pr-346/assets/images/multiplexer-screenshot-d99b6247558814ee9dc6842816d70257.png differ diff --git a/pr-preview/pr-346/assets/images/mv-9f1b888690e88b5ae75a1c2cbc4954a3.png b/pr-preview/pr-346/assets/images/mv-9f1b888690e88b5ae75a1c2cbc4954a3.png new file mode 100644 index 00000000..6d556274 Binary files /dev/null and b/pr-preview/pr-346/assets/images/mv-9f1b888690e88b5ae75a1c2cbc4954a3.png differ diff --git a/pr-preview/pr-346/assets/images/numbers-9748c81928b03074c4a74fc307d1b71b.png b/pr-preview/pr-346/assets/images/numbers-9748c81928b03074c4a74fc307d1b71b.png new file mode 100644 index 00000000..83e337fd Binary files /dev/null and b/pr-preview/pr-346/assets/images/numbers-9748c81928b03074c4a74fc307d1b71b.png differ diff --git a/pr-preview/pr-346/assets/images/open-command-596e1bd58a6b9244a6a653f0bad118bb.png b/pr-preview/pr-346/assets/images/open-command-596e1bd58a6b9244a6a653f0bad118bb.png new file mode 100644 index 00000000..c0b391e0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/open-command-596e1bd58a6b9244a6a653f0bad118bb.png differ diff --git a/pr-preview/pr-346/assets/images/open-current-directory-b657b2ed173070551c7f7301e4655876.png b/pr-preview/pr-346/assets/images/open-current-directory-b657b2ed173070551c7f7301e4655876.png new file mode 100644 index 00000000..43c098af Binary files /dev/null and b/pr-preview/pr-346/assets/images/open-current-directory-b657b2ed173070551c7f7301e4655876.png differ diff --git a/pr-preview/pr-346/assets/images/osx-search-terminal-7ac82426f49389bc7f8018a88258fbc5.png b/pr-preview/pr-346/assets/images/osx-search-terminal-7ac82426f49389bc7f8018a88258fbc5.png new file mode 100644 index 00000000..d253f795 Binary files /dev/null and b/pr-preview/pr-346/assets/images/osx-search-terminal-7ac82426f49389bc7f8018a88258fbc5.png differ diff --git a/pr-preview/pr-346/assets/images/osx-shell-whoami-c8395feac9a865fec59e4cb9ee037615.png b/pr-preview/pr-346/assets/images/osx-shell-whoami-c8395feac9a865fec59e4cb9ee037615.png new file mode 100644 index 00000000..8b581c02 Binary files /dev/null and b/pr-preview/pr-346/assets/images/osx-shell-whoami-c8395feac9a865fec59e4cb9ee037615.png differ diff --git a/pr-preview/pr-346/assets/images/output-c59dac752d60566d856c3f01b4ef0ffb.png b/pr-preview/pr-346/assets/images/output-c59dac752d60566d856c3f01b4ef0ffb.png new file mode 100644 index 00000000..a1666d02 Binary files /dev/null and b/pr-preview/pr-346/assets/images/output-c59dac752d60566d856c3f01b4ef0ffb.png differ diff --git a/pr-preview/pr-346/assets/images/pbcopy-5efdcf7ad96fdbc4276d68c28deac528.png b/pr-preview/pr-346/assets/images/pbcopy-5efdcf7ad96fdbc4276d68c28deac528.png new file mode 100644 index 00000000..eda78247 Binary files /dev/null and b/pr-preview/pr-346/assets/images/pbcopy-5efdcf7ad96fdbc4276d68c28deac528.png differ diff --git a/pr-preview/pr-346/assets/images/pbpaste-e53160a5f1cb41cd6b52db2407fc9bee.png b/pr-preview/pr-346/assets/images/pbpaste-e53160a5f1cb41cd6b52db2407fc9bee.png new file mode 100644 index 00000000..adab8087 Binary files /dev/null and b/pr-preview/pr-346/assets/images/pbpaste-e53160a5f1cb41cd6b52db2407fc9bee.png differ diff --git a/pr-preview/pr-346/assets/images/pro-git-2-52eab2fa942bda3675cf4d5498e0737d.png b/pr-preview/pr-346/assets/images/pro-git-2-52eab2fa942bda3675cf4d5498e0737d.png new file mode 100644 index 00000000..2319ec05 Binary files /dev/null and b/pr-preview/pr-346/assets/images/pro-git-2-52eab2fa942bda3675cf4d5498e0737d.png differ diff --git a/pr-preview/pr-346/assets/images/ps1-with-git-information-dbea469870f0485ea2bf7dd6a169aa5d.png b/pr-preview/pr-346/assets/images/ps1-with-git-information-dbea469870f0485ea2bf7dd6a169aa5d.png new file mode 100644 index 00000000..da926a57 Binary files /dev/null and b/pr-preview/pr-346/assets/images/ps1-with-git-information-dbea469870f0485ea2bf7dd6a169aa5d.png differ diff --git a/pr-preview/pr-346/assets/images/pushd-popd-f69004387269ce04326a8d62cc60a635.png b/pr-preview/pr-346/assets/images/pushd-popd-f69004387269ce04326a8d62cc60a635.png new file mode 100644 index 00000000..3cc89df3 Binary files /dev/null and b/pr-preview/pr-346/assets/images/pushd-popd-f69004387269ce04326a8d62cc60a635.png differ diff --git a/pr-preview/pr-346/assets/images/pushd-popd-stack-ccd34132d513841c5b1d97c842b0413f.png b/pr-preview/pr-346/assets/images/pushd-popd-stack-ccd34132d513841c5b1d97c842b0413f.png new file mode 100644 index 00000000..bf44f7a5 Binary files /dev/null and b/pr-preview/pr-346/assets/images/pushd-popd-stack-ccd34132d513841c5b1d97c842b0413f.png differ diff --git a/pr-preview/pr-346/assets/images/pwd-b4ec8f59e6ea3997888a4919fdd3dbb0.png b/pr-preview/pr-346/assets/images/pwd-b4ec8f59e6ea3997888a4919fdd3dbb0.png new file mode 100644 index 00000000..96abc52b Binary files /dev/null and b/pr-preview/pr-346/assets/images/pwd-b4ec8f59e6ea3997888a4919fdd3dbb0.png differ diff --git a/pr-preview/pr-346/assets/images/pwd-env-var-6f01c6f9ac876cd2d9f8c91b2ed141b6.png b/pr-preview/pr-346/assets/images/pwd-env-var-6f01c6f9ac876cd2d9f8c91b2ed141b6.png new file mode 100644 index 00000000..2d9be264 Binary files /dev/null and b/pr-preview/pr-346/assets/images/pwd-env-var-6f01c6f9ac876cd2d9f8c91b2ed141b6.png differ diff --git a/pr-preview/pr-346/assets/images/regex101-fea6481d5879238fd6a0ea825fef8eb1.png b/pr-preview/pr-346/assets/images/regex101-fea6481d5879238fd6a0ea825fef8eb1.png new file mode 100644 index 00000000..293fbd77 Binary files /dev/null and b/pr-preview/pr-346/assets/images/regex101-fea6481d5879238fd6a0ea825fef8eb1.png differ diff --git a/pr-preview/pr-346/assets/images/regex_v1-f91163e62ea21c34e399180ea629e8b3.png b/pr-preview/pr-346/assets/images/regex_v1-f91163e62ea21c34e399180ea629e8b3.png new file mode 100644 index 00000000..bf94fe2a Binary files /dev/null and b/pr-preview/pr-346/assets/images/regex_v1-f91163e62ea21c34e399180ea629e8b3.png differ diff --git a/pr-preview/pr-346/assets/images/rm-17de3ec6fc67be43dd6e437f9264f2df.png b/pr-preview/pr-346/assets/images/rm-17de3ec6fc67be43dd6e437f9264f2df.png new file mode 100644 index 00000000..82379c79 Binary files /dev/null and b/pr-preview/pr-346/assets/images/rm-17de3ec6fc67be43dd6e437f9264f2df.png differ diff --git a/pr-preview/pr-346/assets/images/rm-error-directory-eb8abf1bd4997caee5d3879c35c1f2f0.png b/pr-preview/pr-346/assets/images/rm-error-directory-eb8abf1bd4997caee5d3879c35c1f2f0.png new file mode 100644 index 00000000..620c361e Binary files /dev/null and b/pr-preview/pr-346/assets/images/rm-error-directory-eb8abf1bd4997caee5d3879c35c1f2f0.png differ diff --git a/pr-preview/pr-346/assets/images/rmdir-a7fc9d107815c1f7b53a2907efd78643.png b/pr-preview/pr-346/assets/images/rmdir-a7fc9d107815c1f7b53a2907efd78643.png new file mode 100644 index 00000000..22cc0844 Binary files /dev/null and b/pr-preview/pr-346/assets/images/rmdir-a7fc9d107815c1f7b53a2907efd78643.png differ diff --git a/pr-preview/pr-346/assets/images/rmdir-fail-2cdc29322732719ab601280bd1765b39.png b/pr-preview/pr-346/assets/images/rmdir-fail-2cdc29322732719ab601280bd1765b39.png new file mode 100644 index 00000000..f3f6656c Binary files /dev/null and b/pr-preview/pr-346/assets/images/rmdir-fail-2cdc29322732719ab601280bd1765b39.png differ diff --git a/pr-preview/pr-346/assets/images/screenshot-history-83f30c8929177b43237c2ff892bdede8.png b/pr-preview/pr-346/assets/images/screenshot-history-83f30c8929177b43237c2ff892bdede8.png new file mode 100644 index 00000000..fa6c22ab Binary files /dev/null and b/pr-preview/pr-346/assets/images/screenshot-history-83f30c8929177b43237c2ff892bdede8.png differ diff --git a/pr-preview/pr-346/assets/images/screenshot-less-65730c042d9a734b6f50168219fb70f7.png b/pr-preview/pr-346/assets/images/screenshot-less-65730c042d9a734b6f50168219fb70f7.png new file mode 100644 index 00000000..2e073c00 Binary files /dev/null and b/pr-preview/pr-346/assets/images/screenshot-less-65730c042d9a734b6f50168219fb70f7.png differ diff --git a/pr-preview/pr-346/assets/images/screenshot-nano-e6d15b516f2ea7678d1059fa29808791.png b/pr-preview/pr-346/assets/images/screenshot-nano-e6d15b516f2ea7678d1059fa29808791.png new file mode 100644 index 00000000..4bc497d2 Binary files /dev/null and b/pr-preview/pr-346/assets/images/screenshot-nano-e6d15b516f2ea7678d1059fa29808791.png differ diff --git a/pr-preview/pr-346/assets/images/screenshot-shell-c4e71ad86af3edb1ad3a967f78691842.png b/pr-preview/pr-346/assets/images/screenshot-shell-c4e71ad86af3edb1ad3a967f78691842.png new file mode 100644 index 00000000..336d9266 Binary files /dev/null and b/pr-preview/pr-346/assets/images/screenshot-shell-c4e71ad86af3edb1ad3a967f78691842.png differ diff --git a/pr-preview/pr-346/assets/images/screenshot-windows-evolution-8e669f56c5e4ccbff1dbf1c885f6cc2f.png b/pr-preview/pr-346/assets/images/screenshot-windows-evolution-8e669f56c5e4ccbff1dbf1c885f6cc2f.png new file mode 100644 index 00000000..a9ff31c7 Binary files /dev/null and b/pr-preview/pr-346/assets/images/screenshot-windows-evolution-8e669f56c5e4ccbff1dbf1c885f6cc2f.png differ diff --git a/pr-preview/pr-346/assets/images/screenshot1-example-shell-9be2c6d6c9bd91bb97bdf4b8b6ce0046.png b/pr-preview/pr-346/assets/images/screenshot1-example-shell-9be2c6d6c9bd91bb97bdf4b8b6ce0046.png new file mode 100644 index 00000000..bc9a9c2c Binary files /dev/null and b/pr-preview/pr-346/assets/images/screenshot1-example-shell-9be2c6d6c9bd91bb97bdf4b8b6ce0046.png differ diff --git a/pr-preview/pr-346/assets/images/search-backwards-and-forwards-64613715b4a7e53da52424c0417425f6.gif b/pr-preview/pr-346/assets/images/search-backwards-and-forwards-64613715b4a7e53da52424c0417425f6.gif new file mode 100644 index 00000000..39b8bbad Binary files /dev/null and b/pr-preview/pr-346/assets/images/search-backwards-and-forwards-64613715b4a7e53da52424c0417425f6.gif differ diff --git a/pr-preview/pr-346/assets/images/search-commands-backwards-and-forwards-ce18bdb04911c4f46ef6b85652f41815.gif b/pr-preview/pr-346/assets/images/search-commands-backwards-and-forwards-ce18bdb04911c4f46ef6b85652f41815.gif new file mode 100644 index 00000000..c87e8ab9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/search-commands-backwards-and-forwards-ce18bdb04911c4f46ef6b85652f41815.gif differ diff --git a/pr-preview/pr-346/assets/images/search-history-cancel-efc4cff2c4486095385e809edc761d5a.gif b/pr-preview/pr-346/assets/images/search-history-cancel-efc4cff2c4486095385e809edc761d5a.gif new file mode 100644 index 00000000..eb09acca Binary files /dev/null and b/pr-preview/pr-346/assets/images/search-history-cancel-efc4cff2c4486095385e809edc761d5a.gif differ diff --git a/pr-preview/pr-346/assets/images/search-history-edit-3985fc23d383297b448b7575e891404a.gif b/pr-preview/pr-346/assets/images/search-history-edit-3985fc23d383297b448b7575e891404a.gif new file mode 100644 index 00000000..0deb7e0c Binary files /dev/null and b/pr-preview/pr-346/assets/images/search-history-edit-3985fc23d383297b448b7575e891404a.gif differ diff --git a/pr-preview/pr-346/assets/images/search-history-execute-b05988030790ea222df25bd4ef7fa27b.gif b/pr-preview/pr-346/assets/images/search-history-execute-b05988030790ea222df25bd4ef7fa27b.gif new file mode 100644 index 00000000..69a7730f Binary files /dev/null and b/pr-preview/pr-346/assets/images/search-history-execute-b05988030790ea222df25bd4ef7fa27b.gif differ diff --git a/pr-preview/pr-346/assets/images/search-history-next-988cf6677fbdca229482ec74735fbdf7.gif b/pr-preview/pr-346/assets/images/search-history-next-988cf6677fbdca229482ec74735fbdf7.gif new file mode 100644 index 00000000..8d0b1732 Binary files /dev/null and b/pr-preview/pr-346/assets/images/search-history-next-988cf6677fbdca229482ec74735fbdf7.gif differ diff --git a/pr-preview/pr-346/assets/images/setup-osx-1-4fc7d2ebdf8a24d89935281dad1f0c07.png b/pr-preview/pr-346/assets/images/setup-osx-1-4fc7d2ebdf8a24d89935281dad1f0c07.png new file mode 100644 index 00000000..1d1d593a Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-osx-1-4fc7d2ebdf8a24d89935281dad1f0c07.png differ diff --git a/pr-preview/pr-346/assets/images/setup-osx-2-4d3098e3ad33dc2dce7788de368e6cf6.png b/pr-preview/pr-346/assets/images/setup-osx-2-4d3098e3ad33dc2dce7788de368e6cf6.png new file mode 100644 index 00000000..c4932ec8 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-osx-2-4d3098e3ad33dc2dce7788de368e6cf6.png differ diff --git a/pr-preview/pr-346/assets/images/setup-osx-3-93e47b2eafb69b28b7e912d73366a70e.png b/pr-preview/pr-346/assets/images/setup-osx-3-93e47b2eafb69b28b7e912d73366a70e.png new file mode 100644 index 00000000..4f3d9f3e Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-osx-3-93e47b2eafb69b28b7e912d73366a70e.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-1-41d6240f93fa7636937af9f31124bc68.png b/pr-preview/pr-346/assets/images/setup-ubuntu-1-41d6240f93fa7636937af9f31124bc68.png new file mode 100644 index 00000000..3c896cbc Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-1-41d6240f93fa7636937af9f31124bc68.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-11-95ad07511fb360ee6ad0e8d977c85cfb.png b/pr-preview/pr-346/assets/images/setup-ubuntu-11-95ad07511fb360ee6ad0e8d977c85cfb.png new file mode 100644 index 00000000..be0181e4 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-11-95ad07511fb360ee6ad0e8d977c85cfb.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-2-ee5741a5bb4d4faa4b0f7e9d6f23ffd7.png b/pr-preview/pr-346/assets/images/setup-ubuntu-2-ee5741a5bb4d4faa4b0f7e9d6f23ffd7.png new file mode 100644 index 00000000..8f451df5 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-2-ee5741a5bb4d4faa4b0f7e9d6f23ffd7.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-3-4e91f8a9fb849a66312fecb62b4b9bea.png b/pr-preview/pr-346/assets/images/setup-ubuntu-3-4e91f8a9fb849a66312fecb62b4b9bea.png new file mode 100644 index 00000000..6361600a Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-3-4e91f8a9fb849a66312fecb62b4b9bea.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-4-35bf528013ee3bef60ab7c3671c0bf45.png b/pr-preview/pr-346/assets/images/setup-ubuntu-4-35bf528013ee3bef60ab7c3671c0bf45.png new file mode 100644 index 00000000..f54a6ca2 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-4-35bf528013ee3bef60ab7c3671c0bf45.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-5-683a093ee97c0e298d809e1b68f07b0e.png b/pr-preview/pr-346/assets/images/setup-ubuntu-5-683a093ee97c0e298d809e1b68f07b0e.png new file mode 100644 index 00000000..6d7f77e9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-5-683a093ee97c0e298d809e1b68f07b0e.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-6-503dda8507f6f47c965e34a09f267fe0.png b/pr-preview/pr-346/assets/images/setup-ubuntu-6-503dda8507f6f47c965e34a09f267fe0.png new file mode 100644 index 00000000..c9a591cc Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-6-503dda8507f6f47c965e34a09f267fe0.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-7-8222d073de6fd7cd0f5379564f46b456.png b/pr-preview/pr-346/assets/images/setup-ubuntu-7-8222d073de6fd7cd0f5379564f46b456.png new file mode 100644 index 00000000..6f74b947 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-7-8222d073de6fd7cd0f5379564f46b456.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-8-4ae6fe35c79a7737ae8edf0232a8552e.png b/pr-preview/pr-346/assets/images/setup-ubuntu-8-4ae6fe35c79a7737ae8edf0232a8552e.png new file mode 100644 index 00000000..476d241c Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-8-4ae6fe35c79a7737ae8edf0232a8552e.png differ diff --git a/pr-preview/pr-346/assets/images/setup-ubuntu-9-463d9fdcd9eb20b6b7c9a81fdd0375ba.png b/pr-preview/pr-346/assets/images/setup-ubuntu-9-463d9fdcd9eb20b6b7c9a81fdd0375ba.png new file mode 100644 index 00000000..f837ef43 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-ubuntu-9-463d9fdcd9eb20b6b7c9a81fdd0375ba.png differ diff --git a/pr-preview/pr-346/assets/images/setup-wsl-1-dc11f93fb578549b24b935c4182b7ab6.png b/pr-preview/pr-346/assets/images/setup-wsl-1-dc11f93fb578549b24b935c4182b7ab6.png new file mode 100644 index 00000000..06eeb649 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-wsl-1-dc11f93fb578549b24b935c4182b7ab6.png differ diff --git a/pr-preview/pr-346/assets/images/setup-wsl-2-76bac1a1fc1a859ecfa64924141b48d9.png b/pr-preview/pr-346/assets/images/setup-wsl-2-76bac1a1fc1a859ecfa64924141b48d9.png new file mode 100644 index 00000000..032cad10 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-wsl-2-76bac1a1fc1a859ecfa64924141b48d9.png differ diff --git a/pr-preview/pr-346/assets/images/setup-wsl-3-f12276a493809ef2fec57c99ede1d8a7.png b/pr-preview/pr-346/assets/images/setup-wsl-3-f12276a493809ef2fec57c99ede1d8a7.png new file mode 100644 index 00000000..6ed22d23 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-wsl-3-f12276a493809ef2fec57c99ede1d8a7.png differ diff --git a/pr-preview/pr-346/assets/images/setup-wsl-4-9134675242a163fce181afc99b1c68c7.png b/pr-preview/pr-346/assets/images/setup-wsl-4-9134675242a163fce181afc99b1c68c7.png new file mode 100644 index 00000000..fbd20380 Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-wsl-4-9134675242a163fce181afc99b1c68c7.png differ diff --git a/pr-preview/pr-346/assets/images/setup-wsl-5-9687c657fd3f8d6c01bcc193b4d57918.png b/pr-preview/pr-346/assets/images/setup-wsl-5-9687c657fd3f8d6c01bcc193b4d57918.png new file mode 100644 index 00000000..2ab4033a Binary files /dev/null and b/pr-preview/pr-346/assets/images/setup-wsl-5-9687c657fd3f8d6c01bcc193b4d57918.png differ diff --git a/pr-preview/pr-346/assets/images/shell-commands-dd4418126f860c1bb692c2bf54b827d7.png b/pr-preview/pr-346/assets/images/shell-commands-dd4418126f860c1bb692c2bf54b827d7.png new file mode 100644 index 00000000..ecdc62ac Binary files /dev/null and b/pr-preview/pr-346/assets/images/shell-commands-dd4418126f860c1bb692c2bf54b827d7.png differ diff --git a/pr-preview/pr-346/assets/images/special-dot-folders-3e278df76dd750bfb6f0fec3d003bd97.png b/pr-preview/pr-346/assets/images/special-dot-folders-3e278df76dd750bfb6f0fec3d003bd97.png new file mode 100644 index 00000000..07192f05 Binary files /dev/null and b/pr-preview/pr-346/assets/images/special-dot-folders-3e278df76dd750bfb6f0fec3d003bd97.png differ diff --git a/pr-preview/pr-346/assets/images/strip-formatting-after-718d90c0c17b8c9c164bb4153944b7ad.png b/pr-preview/pr-346/assets/images/strip-formatting-after-718d90c0c17b8c9c164bb4153944b7ad.png new file mode 100644 index 00000000..65f0fe42 Binary files /dev/null and b/pr-preview/pr-346/assets/images/strip-formatting-after-718d90c0c17b8c9c164bb4153944b7ad.png differ diff --git a/pr-preview/pr-346/assets/images/strip-formatting-before-9ecde3dedc50fcea493f01062986dcc3.png b/pr-preview/pr-346/assets/images/strip-formatting-before-9ecde3dedc50fcea493f01062986dcc3.png new file mode 100644 index 00000000..b4ba6860 Binary files /dev/null and b/pr-preview/pr-346/assets/images/strip-formatting-before-9ecde3dedc50fcea493f01062986dcc3.png differ diff --git a/pr-preview/pr-346/assets/images/terminal-multiplexer-8708ba9a77b0dfd04fb93c617b4b755e.gif b/pr-preview/pr-346/assets/images/terminal-multiplexer-8708ba9a77b0dfd04fb93c617b4b755e.gif new file mode 100644 index 00000000..39ce9c1f Binary files /dev/null and b/pr-preview/pr-346/assets/images/terminal-multiplexer-8708ba9a77b0dfd04fb93c617b4b755e.gif differ diff --git a/pr-preview/pr-346/assets/images/the-mother-of-all-demos-236d7faa36662a621d079b0a6a772dfd.png b/pr-preview/pr-346/assets/images/the-mother-of-all-demos-236d7faa36662a621d079b0a6a772dfd.png new file mode 100644 index 00000000..30ba6e77 Binary files /dev/null and b/pr-preview/pr-346/assets/images/the-mother-of-all-demos-236d7faa36662a621d079b0a6a772dfd.png differ diff --git a/pr-preview/pr-346/assets/images/tldr-sed-7e8fd4e608991238dc64ab2de0bc12f6.png b/pr-preview/pr-346/assets/images/tldr-sed-7e8fd4e608991238dc64ab2de0bc12f6.png new file mode 100644 index 00000000..9bcda358 Binary files /dev/null and b/pr-preview/pr-346/assets/images/tldr-sed-7e8fd4e608991238dc64ab2de0bc12f6.png differ diff --git a/pr-preview/pr-346/assets/images/transpose-word-66dd6c66db8fc0318750fcf1081d359b.gif b/pr-preview/pr-346/assets/images/transpose-word-66dd6c66db8fc0318750fcf1081d359b.gif new file mode 100644 index 00000000..4db32ce1 Binary files /dev/null and b/pr-preview/pr-346/assets/images/transpose-word-66dd6c66db8fc0318750fcf1081d359b.gif differ diff --git a/pr-preview/pr-346/assets/images/tree-cbe0b2feb4e9d491bc323895971f8a8b.png b/pr-preview/pr-346/assets/images/tree-cbe0b2feb4e9d491bc323895971f8a8b.png new file mode 100644 index 00000000..27b26252 Binary files /dev/null and b/pr-preview/pr-346/assets/images/tree-cbe0b2feb4e9d491bc323895971f8a8b.png differ diff --git a/pr-preview/pr-346/assets/images/tree-quotes-621beb5563167c8af53e79a6c01e2e2b.png b/pr-preview/pr-346/assets/images/tree-quotes-621beb5563167c8af53e79a6c01e2e2b.png new file mode 100644 index 00000000..d39ba9d6 Binary files /dev/null and b/pr-preview/pr-346/assets/images/tree-quotes-621beb5563167c8af53e79a6c01e2e2b.png differ diff --git a/pr-preview/pr-346/assets/images/ubuntu-1-32da6aea9163bf8c0634b03e58690f08.png b/pr-preview/pr-346/assets/images/ubuntu-1-32da6aea9163bf8c0634b03e58690f08.png new file mode 100644 index 00000000..c37a792e Binary files /dev/null and b/pr-preview/pr-346/assets/images/ubuntu-1-32da6aea9163bf8c0634b03e58690f08.png differ diff --git a/pr-preview/pr-346/assets/images/unzip-f1a9a5d5eed1b92f90fda4b358e447f7.png b/pr-preview/pr-346/assets/images/unzip-f1a9a5d5eed1b92f90fda4b358e447f7.png new file mode 100644 index 00000000..37ed4263 Binary files /dev/null and b/pr-preview/pr-346/assets/images/unzip-f1a9a5d5eed1b92f90fda4b358e447f7.png differ diff --git a/pr-preview/pr-346/assets/images/vim-cheatsheet-30d6ba320441d1b700ea51eed7736835.png b/pr-preview/pr-346/assets/images/vim-cheatsheet-30d6ba320441d1b700ea51eed7736835.png new file mode 100644 index 00000000..b0173568 Binary files /dev/null and b/pr-preview/pr-346/assets/images/vim-cheatsheet-30d6ba320441d1b700ea51eed7736835.png differ diff --git a/pr-preview/pr-346/assets/images/virtualbox-1-b55e8eca96f9ad58a2d2b4b07c10d7f7.png b/pr-preview/pr-346/assets/images/virtualbox-1-b55e8eca96f9ad58a2d2b4b07c10d7f7.png new file mode 100644 index 00000000..c202bea3 Binary files /dev/null and b/pr-preview/pr-346/assets/images/virtualbox-1-b55e8eca96f9ad58a2d2b4b07c10d7f7.png differ diff --git a/pr-preview/pr-346/assets/images/virtualbox-2-56e9a56597638f636bb037cfecc9e979.png b/pr-preview/pr-346/assets/images/virtualbox-2-56e9a56597638f636bb037cfecc9e979.png new file mode 100644 index 00000000..4d53daa2 Binary files /dev/null and b/pr-preview/pr-346/assets/images/virtualbox-2-56e9a56597638f636bb037cfecc9e979.png differ diff --git a/pr-preview/pr-346/assets/images/virtualbox-3-8b9a8364c7a91467f0c3dbb792cb8fe3.png b/pr-preview/pr-346/assets/images/virtualbox-3-8b9a8364c7a91467f0c3dbb792cb8fe3.png new file mode 100644 index 00000000..4b6ea5eb Binary files /dev/null and b/pr-preview/pr-346/assets/images/virtualbox-3-8b9a8364c7a91467f0c3dbb792cb8fe3.png differ diff --git a/pr-preview/pr-346/assets/images/virtualbox-4-8937d9eb77a0f58c4e04887fbe21c903.png b/pr-preview/pr-346/assets/images/virtualbox-4-8937d9eb77a0f58c4e04887fbe21c903.png new file mode 100644 index 00000000..01fc53a0 Binary files /dev/null and b/pr-preview/pr-346/assets/images/virtualbox-4-8937d9eb77a0f58c4e04887fbe21c903.png differ diff --git a/pr-preview/pr-346/assets/images/website-screenshot-9a8faefcf54b8b8328422cca83a46c9e.png b/pr-preview/pr-346/assets/images/website-screenshot-9a8faefcf54b8b8328422cca83a46c9e.png new file mode 100644 index 00000000..5e3b9ea9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/website-screenshot-9a8faefcf54b8b8328422cca83a46c9e.png differ diff --git a/pr-preview/pr-346/assets/images/wget-68b61c0c8b06c80bf345ce94a7a69b0b.png b/pr-preview/pr-346/assets/images/wget-68b61c0c8b06c80bf345ce94a7a69b0b.png new file mode 100644 index 00000000..3015ecc9 Binary files /dev/null and b/pr-preview/pr-346/assets/images/wget-68b61c0c8b06c80bf345ce94a7a69b0b.png differ diff --git a/pr-preview/pr-346/assets/images/windows-bash-options-ee40590c7b84ed3883d04b278c6cc605.png b/pr-preview/pr-346/assets/images/windows-bash-options-ee40590c7b84ed3883d04b278c6cc605.png new file mode 100644 index 00000000..8e7a2162 Binary files /dev/null and b/pr-preview/pr-346/assets/images/windows-bash-options-ee40590c7b84ed3883d04b278c6cc605.png differ diff --git a/pr-preview/pr-346/assets/images/windows-search-command-prompt-3649f5245909e5d709626e6ae028234a.png b/pr-preview/pr-346/assets/images/windows-search-command-prompt-3649f5245909e5d709626e6ae028234a.png new file mode 100644 index 00000000..91971450 Binary files /dev/null and b/pr-preview/pr-346/assets/images/windows-search-command-prompt-3649f5245909e5d709626e6ae028234a.png differ diff --git a/pr-preview/pr-346/assets/images/windows-shell-ade4a68eb3fc37c74d58144f35e128ce.png b/pr-preview/pr-346/assets/images/windows-shell-ade4a68eb3fc37c74d58144f35e128ce.png new file mode 100644 index 00000000..f914c7b7 Binary files /dev/null and b/pr-preview/pr-346/assets/images/windows-shell-ade4a68eb3fc37c74d58144f35e128ce.png differ diff --git a/pr-preview/pr-346/assets/images/windows-shell-whoami-ade4a68eb3fc37c74d58144f35e128ce.png b/pr-preview/pr-346/assets/images/windows-shell-whoami-ade4a68eb3fc37c74d58144f35e128ce.png new file mode 100644 index 00000000..f914c7b7 Binary files /dev/null and b/pr-preview/pr-346/assets/images/windows-shell-whoami-ade4a68eb3fc37c74d58144f35e128ce.png differ diff --git a/pr-preview/pr-346/assets/images/youtube-where-grep-came-from-5519a145dbc3be578d877a23a5325bb0.png b/pr-preview/pr-346/assets/images/youtube-where-grep-came-from-5519a145dbc3be578d877a23a5325bb0.png new file mode 100644 index 00000000..ca03d292 Binary files /dev/null and b/pr-preview/pr-346/assets/images/youtube-where-grep-came-from-5519a145dbc3be578d877a23a5325bb0.png differ diff --git a/pr-preview/pr-346/assets/images/zip-0f8e0ab92362d59195ccb5ceb432063d.png b/pr-preview/pr-346/assets/images/zip-0f8e0ab92362d59195ccb5ceb432063d.png new file mode 100644 index 00000000..e2f05ae7 Binary files /dev/null and b/pr-preview/pr-346/assets/images/zip-0f8e0ab92362d59195ccb5ceb432063d.png differ diff --git a/pr-preview/pr-346/assets/js/00cc7322.3224192b.js b/pr-preview/pr-346/assets/js/00cc7322.3224192b.js new file mode 100644 index 00000000..18132266 --- /dev/null +++ b/pr-preview/pr-346/assets/js/00cc7322.3224192b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[8055],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,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 l(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||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var o=n.createContext({}),s=function(e){var t=n.useContext(o),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},d=function(e){var t=s(e.components);return n.createElement(o.Provider,{value:t},e.children)},m="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,o=e.parentName,d=p(e,["components","mdxType","originalType","parentName"]),m=s(r),u=a,h=m["".concat(o,".").concat(u)]||m[u]||c[u]||l;return r?n.createElement(h,i(i({ref:t},d),{},{components:r})):n.createElement(h,i({ref:t},d))}));function h(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,i=new Array(l);i[0]=u;var p={};for(var o in t)hasOwnProperty.call(t,o)&&(p[o]=t[o]);p.originalType=e,p[m]="string"==typeof e?e:a,i[1]=p;for(var s=2;s{r.r(t),r.d(t,{assets:()=>o,contentTitle:()=>i,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>s});var n=r(7462),a=(r(7294),r(3905));const l={},i=void 0,p={unversionedId:"xx-appendices/shell-parameter-expansion",id:"xx-appendices/shell-parameter-expansion",title:"shell-parameter-expansion",description:"| Variable | Description |",source:"@site/docs/xx-appendices/shell-parameter-expansion.md",sourceDirName:"xx-appendices",slug:"/xx-appendices/shell-parameter-expansion",permalink:"/xx-appendices/shell-parameter-expansion",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/xx-appendices/shell-parameter-expansion.md",tags:[],version:"current",frontMatter:{}},o={},s=[{value:"TODO",id:"todo",level:2}],d={toc:s};function m(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Variable"),(0,a.kt)("th",{parentName:"tr",align:null},"Description"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$$")),(0,a.kt)("td",{parentName:"tr",align:null},"This is the process ID of the script itself.")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$0")),(0,a.kt)("td",{parentName:"tr",align:null},"The first parameter to the shell, which is typically the path of the shell itself. If this parameter starts with ",(0,a.kt)("inlineCode",{parentName:"td"},"-")," then the shell is assumed to be a Login Shell.")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$-")),(0,a.kt)("td",{parentName:"tr",align:null},"The flags that were set for the shell, such as ",(0,a.kt)("inlineCode",{parentName:"td"},"i")," for 'interactive'.")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$0")),(0,a.kt)("td",{parentName:"tr",align:null},"The first parameter to the shell, which is typically the path of the shell itself.")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$1")),(0,a.kt)("td",{parentName:"tr",align:null},"The first parameter")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$2")),(0,a.kt)("td",{parentName:"tr",align:null},"The second parameter")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"${11}")),(0,a.kt)("td",{parentName:"tr",align:null},"The 11th parameter - if the parameter is more than one digit you must surround it with braces")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$#")),(0,a.kt)("td",{parentName:"tr",align:null},"The number of parameters")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$@")),(0,a.kt)("td",{parentName:"tr",align:null},"The full set of parameters as an array")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$*")),(0,a.kt)("td",{parentName:"tr",align:null},"The full set of parameters as a string separated by the first value in the ",(0,a.kt)("inlineCode",{parentName:"td"},"$IFS")," variable")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"${@:start:count}")),(0,a.kt)("td",{parentName:"tr",align:null},"A subset of 'count' parameters starting at parameter number 'start'")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"$?")),(0,a.kt)("td",{parentName:"tr",align:null},"The status code of the most recently called command.")))),(0,a.kt)("h2",{id:"todo"},"TODO"),(0,a.kt)("p",null,"Need to update Chapter 19 to link to this."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/0208c534.764d7469.js b/pr-preview/pr-346/assets/js/0208c534.764d7469.js new file mode 100644 index 00000000..0953d720 --- /dev/null +++ b/pr-preview/pr-346/assets/js/0208c534.764d7469.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[6879],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});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 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 a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},p=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},u="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,i=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=l(r),m=o,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||i;return r?n.createElement(f,a(a({ref:t},p),{},{components:r})):n.createElement(f,a({ref:t},p))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[u]="string"==typeof e?e:o,a[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var n=r(7462),o=(r(7294),r(3905));const i={},a="Good Scripts to write as exercises",s={unversionedId:"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",source:"@site/docs/xx-appendices/exercises.md",sourceDirName:"xx-appendices",slug:"/xx-appendices/exercises",permalink:"/xx-appendices/exercises",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/xx-appendices/exercises.md",tags:[],version:"current",frontMatter:{}},c={},l=[],p={toc:l};function u(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"good-scripts-to-write-as-exercises"},"Good Scripts to write as exercises"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"recent")," - a better version of ",(0,o.kt)("inlineCode",{parentName:"li"},"history"),", which deduplicates and sorts based on the most commonly used items"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"quickman")," - a quick link to the most common man pages"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"options")," - show and interactively toggle options"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"phonetic")," - take a sequence of letters/numbers and spell it out using NATO phonetic alphabet")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/0c6c849c.40753cad.js b/pr-preview/pr-346/assets/js/0c6c849c.40753cad.js new file mode 100644 index 00000000..9bf5c232 --- /dev/null +++ b/pr-preview/pr-346/assets/js/0c6c849c.40753cad.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[6194],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>c});var a=n(7294);function l(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 o(e){for(var t=1;t=0||(l[n]=e[n]);return l}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(l[n]=e[n])}return l}var r=a.createContext({}),h=function(e){var t=a.useContext(r),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=h(e.components);return a.createElement(r.Provider,{value:t},e.children)},u="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,l=e.mdxType,i=e.originalType,r=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=h(n),d=l,c=u["".concat(r,".").concat(d)]||u[d]||m[d]||i;return n?a.createElement(c,o(o({ref:t},p),{},{components:n})):a.createElement(c,o({ref:t},p))}));function c(e,t){var n=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var i=n.length,o=new Array(i);o[0]=d;var s={};for(var r in t)hasOwnProperty.call(t,r)&&(s[r]=t[r]);s.originalType=e,s[u]="string"==typeof e?e:l,o[1]=s;for(var h=2;h{n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>s,toc:()=>h});var a=n(7462),l=(n(7294),n(3905));const i={title:"Configuring the Shell",slug:"/part-5-building-your-toolkit/configuring-the-shell"},o=void 0,s={unversionedId:"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.",source:"@site/docs/05-building-your-toolkit/24-configuring-the-shell/index.md",sourceDirName:"05-building-your-toolkit/24-configuring-the-shell",slug:"/part-5-building-your-toolkit/configuring-the-shell",permalink:"/part-5-building-your-toolkit/configuring-the-shell",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/05-building-your-toolkit/24-configuring-the-shell/index.md",tags:[],version:"current",frontMatter:{title:"Configuring the Shell",slug:"/part-5-building-your-toolkit/configuring-the-shell"},sidebar:"sidebar",previous:{title:"Part 5 - Building Your Toolkit",permalink:"/part-5-building-your-toolkit/"},next:{title:"Customising Your Command Prompt",permalink:"/part-5-building-your-toolkit/customising-your-command-prompt"}},r={},h=[{value:"The Shell Configuration File",id:"the-shell-configuration-file",level:2},{value:"The Default Configuration File",id:"the-default-configuration-file",level:3},{value:"Common Shell Configurations",id:"common-shell-configurations",level:3},{value:"Aliases",id:"aliases",level:4},{value:"Functions",id:"functions",level:4},{value:"Shell Options",id:"shell-options",level:4},{value:"Changing the Command Prompt",id:"changing-the-command-prompt",level:4},{value:"Source Files",id:"source-files",level:4},{value:"Configure Your System",id:"configure-your-system",level:4},{value:"Configuration Tips",id:"configuration-tips",level:4},{value:"Shell Startup",id:"shell-startup",level:2},{value:"Different Types of Shells",id:"different-types-of-shells",level:2},{value:"Interactive Shells",id:"interactive-shells",level:3},{value:"Non-Interactive Shells",id:"non-interactive-shells",level:3},{value:"Login Shells",id:"login-shells",level:3},{value:"Shell Startup Files",id:"shell-startup-files",level:2},{value:"The Shell Profile Fileindex",id:"the-shell-profile-fileindex",level:3},{value:"The Shell Run Commands Fileindex",id:"the-shell-run-commands-fileindex",level:3},{value:"Startup Files for Non-Interactive Shells",id:"startup-files-for-non-interactive-shells",level:3},{value:"Login Shells and Desktop Managers",id:"login-shells-and-desktop-managers",level:3},{value:"Changing Your Shell",id:"changing-your-shell",level:2},{value:"Summary",id:"summary",level:2}],p={toc:h};function u(e){let{components:t,...n}=e;return(0,l.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"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."),(0,l.kt)("h2",{id:"the-shell-configuration-file"},"The Shell Configuration File"),(0,l.kt)("p",null,"There are a number of different files that the shell uses for configuration, and we're going to see all of them in this chapter. However, the file we will use most often is the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file."),(0,l.kt)("p",null,"When you log into a machine using the shell, or start a shell program in a terminal emulator like the Gnome Terminal or Konsole, you are running an ",(0,l.kt)("em",{parentName:"p"},"interactive shell"),". An interactive shell is one that is connected to your keyboard and screen."),(0,l.kt)("p",null,"When an interactive shell starts, one of the operations it performs is to run all of the commands in the file ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc"),". This is one of the 'shell startup' files."),(0,l.kt)("p",null,"The 'RC' in the file name stands for 'run commands' (sometimes people will also refer to this as 'run configuration'). This is a convention from the early days of Unix. Many tools on Unix and Linux have files that end in 'rc' that are loaded when a program starts up. For example, the ",(0,l.kt)("em",{parentName:"p"},"~/.vimrc")," run commands file is loaded by the ",(0,l.kt)("inlineCode",{parentName:"p"},"vim")," program when it starts."),(0,l.kt)("p",null,"The ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file is in your home directory - this means that it is your personal Bash configuration file. There is also a file that is normally at ",(0,l.kt)("em",{parentName:"p"},"/etc/bash.bashrc")," that is used to configure Bash for all users."),(0,l.kt)("p",null,"Again - this is a common convention for Unix and Linux systems - there is a 'global' configuration file that is used for all users, as well as a 'user' configuration file in the user's home directory that the user can edit to personalise things for themselves."),(0,l.kt)("admonition",{title:"Z-Shell",type:"tip"},(0,l.kt)("p",{parentName:"admonition"},"The ",(0,l.kt)("inlineCode",{parentName:"p"},"zsh")," shell uses a ",(0,l.kt)("em",{parentName:"p"},"~/.zshrc")," file for per-user configuration and ",(0,l.kt)("em",{parentName:"p"},"/etc/zsh/zshrc")," for global configuration. The paths are different but the concepts are the same. Other shells may use different paths as well - you should be able to find the paths in their manual pages.")),(0,l.kt)("h3",{id:"the-default-configuration-file"},"The Default Configuration File"),(0,l.kt)("p",null,"Let's take a look at some of the commands that are in the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," on a clean Ubuntu 20 installation (if you want to know how to set up a free Ubuntu 20 machine check ",(0,l.kt)("a",{parentName:"p",href:"../../work-in-progress"},"Appendix - Setting Up a Linux Virtual Machine"),"."),(0,l.kt)("p",null,"I've omitted parts of the file in the snippet below, we'll focus on some of the most interesting areas."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# If not running interactively, don't do anything\ncase $- in\n *i*) ;;\n *) return;;\nesac\n\n# 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\n# ...\n\n# some more ls aliases\nalias ll='ls -alF'\nalias la='ls -A'\nalias l='ls -CF'\n\n# ...\n")),(0,l.kt)("p",null,"It's very important to understand that this file is ",(0,l.kt)("em",{parentName:"p"},"sourced")," by the shell - so we have to use ",(0,l.kt)("inlineCode",{parentName:"p"},"return")," if we want to stop processing it. If we used ",(0,l.kt)("inlineCode",{parentName:"p"},"exit")," instead then the shell would close, which is definitely not what we want! If you need a reminder on sourcing, check ",(0,l.kt)("a",{parentName:"p",href:"/part-3-manipulating-text/shell-script-essentials/"},"Chapter 18 - Shell Script Essentials"),"."),(0,l.kt)("p",null,"The next section of the file sets up some of the configuration for the history features of the shell. Some variables are set, such as ",(0,l.kt)("inlineCode",{parentName:"p"},"HISTSIZE")," (the number of commands to store in the history), we also set some options using the ",(0,l.kt)("inlineCode",{parentName:"p"},"shopt")," (",(0,l.kt)("em",{parentName:"p"},"set shell option")," flag)."),(0,l.kt)("p",null,"Later on, we can see that some aliases are defined, for user convenience. For example, the ",(0,l.kt)("inlineCode",{parentName:"p"},"la")," alias is a shorthand for ",(0,l.kt)("inlineCode",{parentName:"p"},"ls -A"),", which can save a few keystrokes."),(0,l.kt)("p",null,"This exactly the sort of configuration that makes sense to keep in the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file. Users can modify this to suit their preferences."),(0,l.kt)("p",null,"Now let's look at some of the common features you might configure in the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file."),(0,l.kt)("h3",{id:"common-shell-configurations"},"Common Shell Configurations"),(0,l.kt)("p",null,"You can add any commands you like to the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file, these commands will be run when the shell starts up."),(0,l.kt)("p",null,"Let's see a few examples of what you might add to your ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc"),"."),(0,l.kt)("h4",{id:"aliases"},"Aliases"),(0,l.kt)("p",null,"If you find yourself typing the same series of keystrokes again and again, you might want to add some aliases to your configuration file:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# Start a web server\nalias serve=\"python3 -m SimpleHTTPServer 3000\"\n\n# Open vim without loading the vimrc.\nalias vimnilla='vi -u NONE'\n\n# Shortcut for 'kubectl', saves a lot of time!\nalias k='kubectl'\n\n# Quickly go to my GitHub repositories.\nalias gocode='cd ~/repos/github/dwmkerr'\n")),(0,l.kt)("p",null,"If you are not familiar with aliases, check ",(0,l.kt)("a",{parentName:"p",href:"../../part-2-core-skills/understanding-commands"},"Chapter 10 - Understanding Commands"),"."),(0,l.kt)("p",null,"You might also use aliases to change the behaviour of existing commands. For example, we can change the ",(0,l.kt)("inlineCode",{parentName:"p"},"rm")," command to automatically ask for confirmation before a file is deleted:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# Always run 'rm' in interactive mode.\nalias rm='rm -i'\n")),(0,l.kt)("p",null,"Be aware that the more you customise default commands the more that you run the risk that tutorials or samples you use may not work as expected, as those samples will expect the ",(0,l.kt)("em",{parentName:"p"},"default")," behaviour of the command."),(0,l.kt)("h4",{id:"functions"},"Functions"),(0,l.kt)("p",null,"If you have more complex operations that you regularly perform, you could add them to your ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," as a function:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# Restart the shell.\nrestart-shell() {\n exec -l $SHELL\n}\n\n# Make a directory (don\'t fail if it exists) and move into it in one line.\nfunction mkd {\n mkdir -p -- "$1" && cd -P -- "$1";\n}\n\n# Cut, but in reverse, e.g:\n# $ echo "One;Two;Three;Four;Five" | revcut -d\';\' -f2\n# -> Four\nfunction revcut {\n rev | cut "$@" | rev\n}\n')),(0,l.kt)("p",null,"You can find out more about functions in ",(0,l.kt)("a",{parentName:"p",href:"../../part-4-shell-scripting/functions-parameters-and-error-handling"},"Chapter 22 - Functions, Parameters and Error Handling"),"."),(0,l.kt)("h4",{id:"shell-options"},"Shell Options"),(0,l.kt)("p",null,"The ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file is the ideal place to configure shell options to suit your preferences:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# If we enter a directory name on its own, assume we want to 'cd' into it.\nshopt -s autocd\n")),(0,l.kt)("p",null,"In this example we use the ",(0,l.kt)("inlineCode",{parentName:"p"},"shopt")," (",(0,l.kt)("em",{parentName:"p"},"set shell option"),") command to set the ",(0,l.kt)("inlineCode",{parentName:"p"},"autocd")," option. This option allows you to enter the name of a directory as if it was a command, when you press 'enter' the shell will ",(0,l.kt)("inlineCode",{parentName:"p"},"cd")," into the directory."),(0,l.kt)("p",null,"You can set an option using the ",(0,l.kt)("inlineCode",{parentName:"p"},"-s")," (",(0,l.kt)("em",{parentName:"p"},"set option"),") flag and unset an option with the ",(0,l.kt)("inlineCode",{parentName:"p"},"-u")," (",(0,l.kt)("em",{parentName:"p"},"unset option"),") flag."),(0,l.kt)("p",null,"You can list the options available to set by running ",(0,l.kt)("inlineCode",{parentName:"p"},"shopt -p"),", or searching the ",(0,l.kt)("inlineCode",{parentName:"p"},"man bash")," page for ",(0,l.kt)("inlineCode",{parentName:"p"},"shopt"),". Some of the most useful options are:"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Option"),(0,l.kt)("th",{parentName:"tr",align:null},"Description"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"autocd")),(0,l.kt)("td",{parentName:"tr",align:null},"Enter a directory name as a command and the shell will ",(0,l.kt)("inlineCode",{parentName:"td"},"cd")," to it.")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"cdable_vars")),(0,l.kt)("td",{parentName:"tr",align:null},"Allows you to ",(0,l.kt)("inlineCode",{parentName:"td"},"cd")," into a variable, such as ",(0,l.kt)("inlineCode",{parentName:"td"},"repos=~/repos; cd repos"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"cdspell")),(0,l.kt)("td",{parentName:"tr",align:null},"The shell will try to fix typos to the ",(0,l.kt)("inlineCode",{parentName:"td"},"cd")," command.")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"checkjobs")),(0,l.kt)("td",{parentName:"tr",align:null},"Show the status of stopped and running jobs before exiting the shell.")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"cmdhist")),(0,l.kt)("td",{parentName:"tr",align:null},"Save multi-line commands in the shell history as single entries, rather than an entry per line.")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"dirspell")),(0,l.kt)("td",{parentName:"tr",align:null},"Try to correct typos when auto-completing directory names.")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"globstar")),(0,l.kt)("td",{parentName:"tr",align:null},"Support recursive globbing such as ",(0,l.kt)("inlineCode",{parentName:"td"},"**/*.py")," to find files in subdirectories.")),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"histappend")),(0,l.kt)("td",{parentName:"tr",align:null},"Append to the history file when the shell exists, rather than overwriting it.")))),(0,l.kt)("p",null,"As well as the options that can be set using the ",(0,l.kt)("inlineCode",{parentName:"p"},"shopt")," command, there are also many variables that are used to configure the shell. We've seen some of these variables already, such as the ",(0,l.kt)("inlineCode",{parentName:"p"},"EDITOR")," variable that defines what text editor to use and the ",(0,l.kt)("inlineCode",{parentName:"p"},"PAGER")," variable that defines what pager program to use."),(0,l.kt)("h4",{id:"changing-the-command-prompt"},"Changing the Command Prompt"),(0,l.kt)("p",null,"The command prompt is the information that is shown to the left of the caret in the shell where you enter commands. It will often look something like this:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"dwmkerr@ip-172-31-28-144:~/effective-shell$\n")),(0,l.kt)("p",null,"This command prompt in this example is made up of the following parts:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"ubuntu")," - the name of the current user"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"ip-172-31-28-144")," - the hostname of the machine"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"~/effective-shell")," - the current directory"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"$")," - an indicator showing that we are using Bash (this will be ",(0,l.kt)("inlineCode",{parentName:"li"},"#")," if we are a super user)")),(0,l.kt)("p",null,"The structure and format of the command prompt can be configured using the ",(0,l.kt)("inlineCode",{parentName:"p"},"PS1")," variable. This is a large enough topic that the whole of the next chapter is dedicated to customising the command prompt."),(0,l.kt)("h4",{id:"source-files"},"Source Files"),(0,l.kt)("p",null,"Another common pattern for the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file is to simply ",(0,l.kt)("inlineCode",{parentName:"p"},"source")," another file."),(0,l.kt)("p",null,"For example, you might want to create a set of common functions that you keep in a file called ",(0,l.kt)("em",{parentName:"p"},"shell-functions.sh"),". You could source this file as part of your shell configuration:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# Load my common shell functions.\nsource ~/shell-functions.sh\n")),(0,l.kt)("p",null,"In fact, a lot of the shell startup files do exactly this. For example, in the default ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file on Ubuntu 20, you will see these lines:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"if [ -f ~/.bash_aliases ]; then\n . ~/.bash_aliases\nfi\n")),(0,l.kt)("p",null,"This line uses the ",(0,l.kt)("inlineCode",{parentName:"p"},"-f")," test to see whether a file named ",(0,l.kt)("em",{parentName:"p"},"~/.bash_aliases")," exists. If it does, it is loaded (using ",(0,l.kt)("em",{parentName:"p"},"dot sourcing")," as the notation)."),(0,l.kt)("p",null,"There are lots of different ways to manage your shell configuration. This can range from the simple, such as adding an alias to the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file, to the complex, such as sourcing the contents of an entire directory, or configuring a shell dynamically based on what tools are installed on a system."),(0,l.kt)("h4",{id:"configure-your-system"},"Configure Your System"),(0,l.kt)("p",null,"You might have particular commands you want to ensure are run when you start a shell. For example, let's say that you want to always have a folder named ",(0,l.kt)("em",{parentName:"p"},"~/today")," that links to a temporary folder which is updated daily."),(0,l.kt)("p",null,"To do this, you could add the following commands to the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},'# 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')),(0,l.kt)("p",null,"If I add this code to my ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file then whenever I start a new shell, a folder will be created with today's date in the ",(0,l.kt)("em",{parentName:"p"},"/tmp/")," directory, and a link will be created to this folder at ",(0,l.kt)("em",{parentName:"p"},"~/today"),". This provides a convenient way to have a temporary working folder for the day. You can then go back and refer to old temporary folders if you need to."),(0,l.kt)("h4",{id:"configuration-tips"},"Configuration Tips"),(0,l.kt)("p",null,"There are a few things that you should pay attention to when working with startup files."),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Do not print output")),(0,l.kt)("p",null,"It is considered bad practice to print output during startup of the shell. Avoid running commands like ",(0,l.kt)("inlineCode",{parentName:"p"},"echo")," or ",(0,l.kt)("inlineCode",{parentName:"p"},"printf"),". If you call commands that write to ",(0,l.kt)("em",{parentName:"p"},"stdout")," then silence the output by piping it to ",(0,l.kt)("em",{parentName:"p"},"/dev/null"),"."),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Do not run long operations")),(0,l.kt)("p",null,"You might have written a cool scripts that pulls down information on stocks or weather from a website, ready to show in your shell. But avoid running anything in a startup file that can take a lot of time. Every time you start your shell you'll have a delay while the command runs and this can really slow you down!"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Be careful not to break things")),(0,l.kt)("p",null,"Don't run so many commands that you might cause errors or failures on startup. This can make your shell difficult to use or slow to start up. If your startup logic is failing it can be hard to debug, so try not to make it too complex!"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Clean up after yourself!")),(0,l.kt)("p",null,"Remember, any variables you set will be set for all shells that read the startup file. If there are variables that you only need during the processing of the file, consider using the ",(0,l.kt)("inlineCode",{parentName:"p"},"unset")," command to unset the variable at the end of startup."),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Expect commands to be run multiple times")),(0,l.kt)("p",null,"Write your startup files with the assumption that they will be run multiple times. If you start a new shell from your current shell, your configuration file will be loaded again. Your configuration should not cause errors if it is run multiple times!"),(0,l.kt)("h2",{id:"shell-startup"},"Shell Startup"),(0,l.kt)("p",null,"In most cases you will only need to work with the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file to configure your shell. However, the shell actually uses a number of different configuration files (which are called 'startup files') depending on how the shell is being used."),(0,l.kt)("p",null,"You may have seen references to files such as ",(0,l.kt)("em",{parentName:"p"},"/etc/profile"),", ",(0,l.kt)("em",{parentName:"p"},"~/.bash_profile"),", ",(0,l.kt)("em",{parentName:"p"},"~/.bash_logout")," and more. The different files that are used can be quite confusing. For the rest of this chapter we're going to go into the details of exactly how the shell uses these different files."),(0,l.kt)("h2",{id:"different-types-of-shells"},"Different Types of Shells"),(0,l.kt)("p",null,"For us to be able to understand how shells are configured, we need to understand the different types of shells that can run. This does not mean different shell programs, such as ",(0,l.kt)("inlineCode",{parentName:"p"},"bash"),", ",(0,l.kt)("inlineCode",{parentName:"p"},"zsh")," or ",(0,l.kt)("inlineCode",{parentName:"p"},"dash"),", but instead the differences between ",(0,l.kt)("em",{parentName:"p"},"interactive")," and ",(0,l.kt)("em",{parentName:"p"},"non-interactive")," shells, as well as ",(0,l.kt)("em",{parentName:"p"},"login")," shells."),(0,l.kt)("p",null,"A lot of people get confused by how the shell is configured because they don't fully understand what these different types of shells are. So let's introduce each one, what it is and how it is used."),(0,l.kt)("h3",{id:"interactive-shells"},"Interactive Shells"),(0,l.kt)("p",null,"An ",(0,l.kt)("em",{parentName:"p"},"interactive shell")," is any shell that has its input, output and error standard streams connected to a terminal. This sounds complicated, but it really just means that an interactive shell is one that you interact with via the keyboard and display!"),(0,l.kt)("p",null,"When we type commands into our shell, we're using an interactive shell."),(0,l.kt)("h3",{id:"non-interactive-shells"},"Non-Interactive Shells"),(0,l.kt)("p",null,"Any shell that does not have its standard input, output and error streams attached to a terminal is generally called a ",(0,l.kt)("em",{parentName:"p"},"non-interactive shell"),"."),(0,l.kt)("p",null,"The most common example we've seen so far for non interactive shells are the shells that run shell scripts! Let's run the ",(0,l.kt)("em",{parentName:"p"},"showpstree.sh")," script from the samples to show the process tree for the current process. This script shows the process tree for the shell process it is running in and looks like this:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# GNU pstree; use the long form (-l) show the command line (-a) and the\n# details for a specific process (-s).\npstree -a -s $$\n")),(0,l.kt)("p",null,"Here's the output when we run this script:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"~$ ./effective-shell/scripts/showpstree.sh\nsystemd\n \u2514\u2500sshd\n \u2514\u2500sshd\n \u2514\u2500sshd\n \u2514\u2500bash\n \u2514\u2500sh ./effective-shell/scripts/showpstree.sh\n \u2514\u2500pstree -a -s 1675\n")),(0,l.kt)("p",null,"The output will look different depending on what system you are using, but the key section to focus on are the final three processes:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"pstree -a -s 1675")," - this is just the ",(0,l.kt)("inlineCode",{parentName:"li"},"pstree")," (",(0,l.kt)("em",{parentName:"li"},"show process tree"),") command that is run in the ",(0,l.kt)("em",{parentName:"li"},"showpstree.sh")," script"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"sh ./effective-shell/scripts/showpstree.sh")," - this is a ",(0,l.kt)("em",{parentName:"li"},"non-interactive shell")," that is running our shell script"),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"bash")," - this is the ",(0,l.kt)("em",{parentName:"li"},"interactive shell")," that we used to invoke our shell script")),(0,l.kt)("p",null,"When you run a shell script, it runs in a ",(0,l.kt)("em",{parentName:"p"},"non-interactive")," shell. This is really important to remember! Shell scripts are run in non-interactive shells. This means that anything you define in ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," will not be loaded, so don't try and use aliases or other customisations that you have made."),(0,l.kt)("p",null,"In fact, on many distributions you will see the following lines in the default ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# If not running interactively, don't do anything\ncase $- in\n *i*) ;;\n *) return;;\nesac\n")),(0,l.kt)("p",null,"The first section of the script checks the current shell parameters (which are stored in the special ",(0,l.kt)("inlineCode",{parentName:"p"},"$-")," variable) to see whether the ",(0,l.kt)("inlineCode",{parentName:"p"},"i")," (",(0,l.kt)("em",{parentName:"p"},"interactive"),") parameter is present. If it is not present, the ",(0,l.kt)("inlineCode",{parentName:"p"},"return")," command runs. This check for the shell parameters ensures that even if a non-interactive shell does load the run commands file for some reason, it stops reading it right away."),(0,l.kt)("p",null,"If you need a refresher on how the ",(0,l.kt)("inlineCode",{parentName:"p"},"case")," statement works, check ",(0,l.kt)("a",{parentName:"p",href:"../../part-4-shell-scripting/mastering-conditional-logic"},"Chapter 20 - Mastering Conditional Logic"),"."),(0,l.kt)("p",null,"Another way to show a non-interactive shell in action is to simply invoke the shell program with a specified command from the command like:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'$ sh -c "echo $((5 + 5))"\n10\n')),(0,l.kt)("p",null,"In this example we started the ",(0,l.kt)("inlineCode",{parentName:"p"},"sh")," (",(0,l.kt)("em",{parentName:"p"},"shell"),") program and provided a command via the ",(0,l.kt)("inlineCode",{parentName:"p"},"-c")," (",(0,l.kt)("em",{parentName:"p"},"command"),") flag. This starts a non-interactive shell."),(0,l.kt)("p",null,"Why do non-interactive shells not load the configuration file? There are two reasons. The first is that it doesn't make sense for scripts to rely on user-level customisations. If one user has an alias and refers to it in a script, then the script will not run for another user unless they have the same alias. The second reason is for performance - when using a shell to run a script the shell can start much more quickly if it doesn't need to load configuration or customisations."),(0,l.kt)("h3",{id:"login-shells"},"Login Shells"),(0,l.kt)("p",null,"When you login to a computer with a shell, entering credentials such as a username and password, then you are using a ",(0,l.kt)("em",{parentName:"p"},"login shell"),". A login shell will normally run some initial setup of your environment and provide the bare minimum configuration required to work with the system. For example, most shells set up the ",(0,l.kt)("inlineCode",{parentName:"p"},"$PATH")," variable as part of the initialisation of the login shell."),(0,l.kt)("p",null,"For systems that don't have a graphical interface, any shell you create will be a child of the login shell, so will inherit the login shell's configuration. For graphical interfaces to systems, such as KDE or Gnome, when you log in with the graphical interface, the desktop manager normally configures the environment using the same configuration as is used for a login shell. The desktop manager process will therefore have variables like the ",(0,l.kt)("inlineCode",{parentName:"p"},"$PATH")," set up just as if you had logged in at the command line."),(0,l.kt)("p",null,"When you ",(0,l.kt)("inlineCode",{parentName:"p"},"ssh")," onto a remote machine, you will be running a login shell. In most cases, when you switch users with commands like ",(0,l.kt)("inlineCode",{parentName:"p"},"su")," (",(0,l.kt)("em",{parentName:"p"},"set user"),"), you will start a login shell as well",(0,l.kt)("sup",{parentName:"p",id:"fnref-1"},(0,l.kt)("a",{parentName:"sup",href:"#fn-1",className:"footnote-ref"},"1")),"."),(0,l.kt)("p",null,"The key thing to remember about login shells is that they are normally run once, when you start working with a computer, and all of the other shells you then run will be children of the login shell."),(0,l.kt)("p",null,"You can see whether your shell is a login shell by examining the ",(0,l.kt)("inlineCode",{parentName:"p"},"$0")," variable. This variable holds the parameters that were provided to start the shell. By convention, if the parameter starts with a ",(0,l.kt)("inlineCode",{parentName:"p"},"-")," dash symbol, you can assume that you are in a login shell."),(0,l.kt)("p",null,"Let's see an example of this in action by logging into a virtual machine (if you would like to set up your own Linux virtual machine you can follow the guide in ",(0,l.kt)("a",{parentName:"p",href:"../../work-in-progress"},"Appendix - Setting Up a Linux Virtual Machine"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'$ ssh effective-shell-ubuntu-20\nWelcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-1045-aws x86_64)\n...\nTo run a command as administrator (user "root"), use "sudo ".\nSee "man sudo_root" for details.\n\n~$ echo "$0"\n-bash\n')),(0,l.kt)("p",null,"Here we can see that the parameter that the shell was started with was ",(0,l.kt)("inlineCode",{parentName:"p"},"-bash"),". This starts with a ",(0,l.kt)("inlineCode",{parentName:"p"},"-")," dash symbol, indicating that it is a login shell."),(0,l.kt)("p",null,"Login shells are ",(0,l.kt)("em",{parentName:"p"},"normally")," interactive shells, but it is possible to run a non-interactive login shell (it's just quite an unusual thing to do)."),(0,l.kt)("p",null,"In the early days of Unix, executing any commands could be time consuming. The login shell would perform the most essential configuration only once when a user logs in and all subsequent shell processes could start more quickly as they would inherit the login configuration and then load the user specific configuration."),(0,l.kt)("h2",{id:"shell-startup-files"},"Shell Startup Files"),(0,l.kt)("p",null,"When the shell starts, it reads a set of ",(0,l.kt)("em",{parentName:"p"},"startup files"),". These files are shell scripts that are ",(0,l.kt)("em",{parentName:"p"},"sourced")," by the shell. A script that is sourced is loaded into the ",(0,l.kt)("em",{parentName:"p"},"current")," shell process, rather than running in a new shell process."),(0,l.kt)("p",null,"For many people, the various different files that are loaded can cause confusion. But as long as you understand the different types of shells that exist, it is actually quite straightforward to understand the process."),(0,l.kt)("p",null,"When a ",(0,l.kt)("em",{parentName:"p"},"login")," shell starts, the following steps are taken:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"The shell attempts to load the ",(0,l.kt)("em",{parentName:"li"},"profile file")),(0,l.kt)("li",{parentName:"ul"},"The ",(0,l.kt)("em",{parentName:"li"},"profile file")," will normally load the ",(0,l.kt)("em",{parentName:"li"},"run commands")," file")),(0,l.kt)("p",null,"When an ",(0,l.kt)("em",{parentName:"p"},"interactive")," shell starts, the following steps are taken:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"The shell attempts to load the ",(0,l.kt)("em",{parentName:"li"},"run commands")," file")),(0,l.kt)("p",null,"When a ",(0,l.kt)("em",{parentName:"p"},"non-interactive")," shell starts, it doesn't load any configuration files, unless one has been specified in the ",(0,l.kt)("inlineCode",{parentName:"p"},"BASH_ENV")," variable."),(0,l.kt)("p",null,"Let's take a look at these files in detail."),(0,l.kt)("h3",{id:"the-shell-profile-fileindex"},"The Shell Profile File"),(0,l.kt)("p",null,"When a login shell is started, the shell loads and executes commands from the ",(0,l.kt)("em",{parentName:"p"},"/etc/profile")," file."),(0,l.kt)("p",null,"The profile file contains the most essential configuration that is required. It is often used to set things like the ",(0,l.kt)("inlineCode",{parentName:"p"},"$PATH")," environment variable, which will sometimes have different values depending on the operating system you are using."),(0,l.kt)("p",null,"The shell will then attempt to read each of the following files. If the shell finds one that is readable, it reads it and executes its commands, and then does not attempt to read the others:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("em",{parentName:"li"},"~/.bash_profile")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("em",{parentName:"li"},"~/.bash_login")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("em",{parentName:"li"},"~/.profile"))),(0,l.kt)("p",null,"There are very few circumstances in which you should change any of these files. It is based to think of the profile files as essential operating system specific configuration that is needed to have a functional login shell."),(0,l.kt)("p",null,"When a login shell closes, it will run any commands in the ",(0,l.kt)("em",{parentName:"p"},"~/.bash_logout")," file. However, users might terminate the shell process forcibly, which means that you cannot be sure this file will always be sourced as the shell exits."),(0,l.kt)("p",null,"The configuration that the startup files perform varies from distribution to distribution, but in general they will do at least the following:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"Set the ",(0,l.kt)("inlineCode",{parentName:"li"},"$PATH")," variable to include the appropriate folders for tools for your distribution"),(0,l.kt)("li",{parentName:"ul"},"Set the shell prompt, the characters that are shown to the user to show they need to enter input (such as ",(0,l.kt)("inlineCode",{parentName:"li"},"~$")," when we are in the home folder, or ",(0,l.kt)("inlineCode",{parentName:"li"},"~#")," if we are in the home folder as a super user)"),(0,l.kt)("li",{parentName:"ul"},"Set up auto-completion (the feature that allows you to press 'tab' to see suggestions when entering commands)"),(0,l.kt)("li",{parentName:"ul"},"Load the ",(0,l.kt)("em",{parentName:"li"},"run commands")," file - we'll look at this file next")),(0,l.kt)("p",null,"The key thing to remember about the profile files is that you normally don't need to change them and they normally load the ",(0,l.kt)("em",{parentName:"p"},"run command")," file for you."),(0,l.kt)("h3",{id:"the-shell-run-commands-fileindex"},"The Shell Run Commands File"),(0,l.kt)("p",null,"When an interactive non-login shell is started, the shell loads and executes the commands from the ",(0,l.kt)("em",{parentName:"p"},"/etc/bash.bashrc")," file and then the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file (if they exist)."),(0,l.kt)("p",null,"It is also convention that you can have two ",(0,l.kt)("inlineCode",{parentName:"p"},"rc")," files - one that is for ",(0,l.kt)("em",{parentName:"p"},"all")," users (in this case, ",(0,l.kt)("em",{parentName:"p"},"/etc/bash.bashrc"),") and one that is user specific (in this case, ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc"),")."),(0,l.kt)("p",null,"The ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file is where you can put your own commands to configure the shell to suit how you want to use it. This is the file we spent the first half of the chapter looking at in detail."),(0,l.kt)("h3",{id:"startup-files-for-non-interactive-shells"},"Startup Files for Non-Interactive Shells"),(0,l.kt)("p",null,"If you need to load a startup file for a non-interactive shell, you can set the ",(0,l.kt)("inlineCode",{parentName:"p"},"BASH_ENV")," variable to the path of the file that you want to load. In general you should be very careful when doing this, as shell commands or shell scripts should be written so that they can operate without a startup file being loaded."),(0,l.kt)("h3",{id:"login-shells-and-desktop-managers"},"Login Shells and Desktop Managers"),(0,l.kt)("p",null,"Before the advent of the graphical user interface, almost all shell processes would be children of a login shell, as you had to use a login shell to access the system."),(0,l.kt)("p",null,"For modern systems that use a desktop environment such as Gnome or KDE, the desktop manager process normally loads the ",(0,l.kt)("inlineCode",{parentName:"p"},"/etc/profile")," file. This means that when you open a terminal program use as the Gnome Terminal or Konsole, the shell is a child of a process which has loaded the profile. Even if you don't use a login shell to access a system, you can normally be sure that the profile will have been loaded by the desktop manager."),(0,l.kt)("p",null,"Different distributions and operating systems may handle this in slightly different ways. For example, on MacOS when you run the Terminal program it actually starts a login shell",(0,l.kt)("sup",{parentName:"p",id:"fnref-2"},(0,l.kt)("a",{parentName:"sup",href:"#fn-2",className:"footnote-ref"},"2")),". Again, this means that you can be sure that the profile has been loaded (which in turn will load the RC files)."),(0,l.kt)("h2",{id:"changing-your-shell"},"Changing Your Shell"),(0,l.kt)("p",null,"You can see the shell that is currently set as the default shell for a user by checking the ",(0,l.kt)("em",{parentName:"p"},"/etc/passwd")," file. Here's how I could see what shell is used when the ",(0,l.kt)("inlineCode",{parentName:"p"},"dwmkerr")," user logs in:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ grep 'dwmkerr' /etc/passwd\ndwmkerr:x:1001:1001:Dave Kerr,,,:/home/dwmkerr:/bin/bash\n")),(0,l.kt)("p",null,"The ",(0,l.kt)("em",{parentName:"p"},"/etc/passwd")," file keeps track of the local user accounts on the system. The final item on a line is the shell that is used for the user. When a user logs in, their shell is set in the ",(0,l.kt)("inlineCode",{parentName:"p"},"SHELL")," environment variable, we can write this value out with the ",(0,l.kt)("inlineCode",{parentName:"p"},"echo")," command:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'$ echo "My shell is: $SHELL"\nMy shell is: /bin/bash\n')),(0,l.kt)("p",null,"There are a few ways that you can change your shell. However, before you change your shell, you need to make sure that the shell you want to use is listed in the 'available shells' file. This file is kept at ",(0,l.kt)("em",{parentName:"p"},"/etc/shells"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ cat /etc/shells\n# /etc/shells: valid login shells\n/bin/sh\n/bin/bash\n/usr/bin/bash\n/bin/rbash\n/usr/bin/rbash\n/bin/dash\n/usr/bin/dash\n/usr/bin/tmux\n")),(0,l.kt)("p",null,"If the shell you want to use is ",(0,l.kt)("em",{parentName:"p"},"not")," listed in ",(0,l.kt)("em",{parentName:"p"},"/etc/shells")," you will need to add it to the list."),(0,l.kt)("p",null,"Once you have installed the shell you want to use and added it to the ",(0,l.kt)("em",{parentName:"p"},"/etc/shells")," list you can run the ",(0,l.kt)("inlineCode",{parentName:"p"},"chsh")," (",(0,l.kt)("em",{parentName:"p"},"change shell"),")"," command to change the shell for a given user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ chsh -s /bin/sh dwmkerr\n")),(0,l.kt)("p",null,"The ",(0,l.kt)("inlineCode",{parentName:"p"},"-s")," (",(0,l.kt)("em",{parentName:"p"},"shell"),") parameter is used to specify the shell path. After this we provide the name of the user we are changing the shell for. On many systems users are allowed to change their own shell as long as it is in the ",(0,l.kt)("em",{parentName:"p"},"/etc/shells")," list. To change the shell for ",(0,l.kt)("em",{parentName:"p"},"another")," user, or to use a shell that is ",(0,l.kt)("em",{parentName:"p"},"not")," in the ",(0,l.kt)("em",{parentName:"p"},"/etc/shells")," list the ",(0,l.kt)("inlineCode",{parentName:"p"},"chsh")," command will need to be run as a super-user."),(0,l.kt)("p",null,"You can also change the shell for a user by editing the ",(0,l.kt)("em",{parentName:"p"},"/etc/passwd")," file."),(0,l.kt)("p",null,"Changing your shell is an advanced topic - if you prefer to use another shell you could also simply start the shell from your login shell, for example by running ",(0,l.kt)("inlineCode",{parentName:"p"},"sh")," from your Bash shell session."),(0,l.kt)("p",null,"As an end-to-end example, here's how you would install ",(0,l.kt)("inlineCode",{parentName:"p"},"zsh")," and set it for the current user on a Debian based system:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"# Elevate privileges to super-user.\nsudo su\n\n# Update the apt databases and install 'zsh'.\napt update -y\napt install zsh\n\n# Add 'zsh' to the list of shell.\necho \"/bin/zsh\" >> /etc/shells\n\n# Return to normal user mode.\nexit\n\n# Change the current user's shell to 'zsh'.\nchsh -s \"/bin/zsh\" $USER\n")),(0,l.kt)("p",null,"Be careful when changing your shell - if you get this wrong then you may inadvertently lock yourself out of your account, if logging in tries to start a shell that is not properly configured. Always test that the new shell works before you set it!"),(0,l.kt)("h2",{id:"summary"},"Summary"),(0,l.kt)("p",null,"In this chapter we saw how to customise shell configuration with the ",(0,l.kt)("em",{parentName:"p"},"~/.bashrc")," file. We also looked in detail at the differences between login and non-login shells, interactive and non-interactive shells, and how these different shells load startup files."),(0,l.kt)("p",null,"You can find all of the detail on how the shell starts up in the ",(0,l.kt)("inlineCode",{parentName:"p"},"man bash")," page, just search for ",(0,l.kt)("inlineCode",{parentName:"p"},"^INVOCATION"),"."),(0,l.kt)("p",null,"In the next chapter we will look at how you can set up your command prompt to suit your preferences."),(0,l.kt)("div",{className:"footnotes"},(0,l.kt)("hr",{parentName:"div"}),(0,l.kt)("ol",{parentName:"div"},(0,l.kt)("li",{parentName:"ol",id:"fn-1"},"There are three excellent discussions on login and non-login shells and interactive shells ",(0,l.kt)("a",{parentName:"li",href:"https://askubuntu.com/questions/247738/why-is-etc-profile-not-invoked-for-non-login-shells"},"here"),", ",(0,l.kt)("a",{parentName:"li",href:"https://askubuntu.com/questions/155865/what-are-login-and-non-login-shells"},"here")," and ",(0,l.kt)("a",{parentName:"li",href:"https://unix.stackexchange.com/questions/38175/difference-between-login-shell-and-non-login-shell"},"here"),".",(0,l.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")),(0,l.kt)("li",{parentName:"ol",id:"fn-2"},"There is a very good discussion on this topic at ",(0,l.kt)("a",{parentName:"li",href:"https://unix.stackexchange.com/questions/119627/why-are-interactive-shells-on-osx-login-shells-by-default"},"https://unix.stackexchange.com/questions/119627/why-are-interactive-shells-on-osx-login-shells-by-default"),".",(0,l.kt)("a",{parentName:"li",href:"#fnref-2",className:"footnote-backref"},"\u21a9")))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/0d95b4ed.d899b77f.js b/pr-preview/pr-346/assets/js/0d95b4ed.d899b77f.js new file mode 100644 index 00000000..908208ab --- /dev/null +++ b/pr-preview/pr-346/assets/js/0d95b4ed.d899b77f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[243],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>u});var o=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 r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,o)}return a}function i(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var s=o.createContext({}),h=function(e){var t=o.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},c=function(e){var t=h(e.components);return o.createElement(s.Provider,{value:t},e.children)},d="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},p=o.forwardRef((function(e,t){var a=e.components,n=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=h(a),p=n,u=d["".concat(s,".").concat(p)]||d[p]||m[p]||r;return a?o.createElement(u,i(i({ref:t},c),{},{components:a})):o.createElement(u,i({ref:t},c))}));function u(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=a.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[d]="string"==typeof e?e:n,i[1]=l;for(var h=2;h{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>h});var o=a(7462),n=(a(7294),a(3905));const r={title:"Recommended Reading",description:"Great books and articles you might enjoy if you like this book!"},i="Recommended Reading",l={unversionedId:"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!",source:"@site/docs/xx-appendices/recommended-reading/index.mdx",sourceDirName:"xx-appendices/recommended-reading",slug:"/xx-appendices/recommended-reading/",permalink:"/xx-appendices/recommended-reading/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/xx-appendices/recommended-reading/index.mdx",tags:[],version:"current",frontMatter:{title:"Recommended Reading",description:"Great books and articles you might enjoy if you like this book!"},sidebar:"sidebar",previous:{title:"Installing the Samples",permalink:"/xx-appendices/installing-samples/"},next:{title:"Thanks",permalink:"/appendices/thanks/"}},s={},h=[{value:"The Future of the Shell",id:"the-future-of-the-shell",level:2},{value:"General Shell Skills",id:"general-shell-skills",level:2},{value:"Fantastic Books",id:"fantastic-books",level:2},{value:"Shell Scripting by Jason Cannon",id:"shell-scripting-by-jason-cannon",level:3},{value:"Wicked Cool Shell Scripts - Dave Taylor & Brandon Perry",id:"wicked-cool-shell-scripts---dave-taylor--brandon-perry",level:3},{value:"Practical Vim: Edit Text at the Speed of Thought, Drew Niel",id:"practical-vim-edit-text-at-the-speed-of-thought-drew-niel",level:3},{value:"Joshua Levy - The Art of the Command Line",id:"joshua-levy---the-art-of-the-command-line",level:3},{value:"Essential Online Resources",id:"essential-online-resources",level:2},{value:"Command Line Interface Guidelines",id:"command-line-interface-guidelines",level:3},{value:"The Missing Semester of Your CS Education",id:"the-missing-semester-of-your-cs-education",level:3},{value:"Maarten Billemont - Bash Guide",id:"maarten-billemont---bash-guide",level:3},{value:"Great Books",id:"great-books",level:2},{value:"Great Videos",id:"great-videos",level:2},{value:"The UNIX Operating System",id:"the-unix-operating-system",level:3},{value:"The Mother of All Demos",id:"the-mother-of-all-demos",level:3},{value:"Where Grep Came From - Computerphile",id:"where-grep-came-from---computerphile",level:3},{value:"Advanced",id:"advanced",level:2},{value:"Research",id:"research",level:2},{value:"TODO",id:"todo",level:2}],c={toc:h};function d(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"recommended-reading"},"Recommended Reading"),(0,n.kt)("admonition",{type:"info"},(0,n.kt)("mdxAdmonitionTitle",{parentName:"admonition"},(0,n.kt)("strong",{parentName:"mdxAdmonitionTitle"},"Work In Progress")),(0,n.kt)("p",{parentName:"admonition"},"This section is being expanded regularly, so check back often to find more recommendations!")),(0,n.kt)("p",null,"Some great books and articles for specific topics are listed below."),(0,n.kt)("h2",{id:"the-future-of-the-shell"},"The Future of the Shell"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://changelog.com/gotime/222"},"The Changelog - Making the command line glamorous")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://changelog.com/podcast/487"},"The Changelog - Warp wants to be the terminal of the future")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://changelog.com/podcast/511"},"The Changelog - The terminal as a platform")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://github.com/readme/featured/future-of-the-command-line"},"GitHub The ReadME Project - Building the future of the command line")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://christianheilmann.com/2023/03/29/github-copilot-for-the-command-line-is-amazing/"},"GitHub Copilot for the Command Line is amazing!"))),(0,n.kt)("h2",{id:"general-shell-skills"},"General Shell Skills"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://changelog.com/podcast/547"},"The Changelog - Efficient Linux at the CLI"))),(0,n.kt)("h2",{id:"fantastic-books"},"Fantastic Books"),(0,n.kt)("p",null,"Each of these books would make a great addition to your bookshelf if you are a technologist. Many of these books I have read multiple times and still go back to as a reference."),(0,n.kt)("table",null,(0,n.kt)("tr",null,(0,n.kt)("td",null,(0,n.kt)("img",{style:{width:"480px"},alt:"Book Cover Image: Applied Cryptography",src:a(2841).Z})),(0,n.kt)("td",null,(0,n.kt)("p",null,(0,n.kt)("strong",null,(0,n.kt)("a",{href:"https://www.amazon.com/Applied-Cryptography-Protocols-Algorithms-Source/dp/0471117099"},"Applied Cryptography: Protocols, Algorithms, and Source Code in C - Bruce Schneier"))),(0,n.kt)("p",null,"This is the absolute best book around on cryptography - from concepts, protocols all the way to advanced topics. There are code examples in C that allow you to really see how these concepts work in practice. This is an excellent book for someone who wants to learn about cryptography but also have the option to go deep into the topics that interest them."))),(0,n.kt)("tr",null,(0,n.kt)("td",null,(0,n.kt)("img",{style:{width:"480px"},alt:"Book Cover Image: Pro Git",src:a(5543).Z})),(0,n.kt)("td",null,(0,n.kt)("p",null,(0,n.kt)("strong",null,(0,n.kt)("a",{href:"https://git-scm.com/book/en/v2"},"Pro Git - Scott Chacon and Ben Straub"))),(0,n.kt)("p",null,"Superb book on Git, that is essential reading for the new user, but also goes into great depth on topics that will be relevant for expert readers. Should be on everyone's bookshelf.")))),(0,n.kt)("h3",{id:"shell-scripting-by-jason-cannon"},(0,n.kt)("a",{parentName:"h3",href:"https://www.amazon.com/Shell-Scripting-Automate-Command-Programming-ebook/dp/B015FZAXU6"},"Shell Scripting by Jason Cannon")),(0,n.kt)("p",null,"Short and sweet, this is a good book for absolute shell scripting beginners."),(0,n.kt)("h3",{id:"wicked-cool-shell-scripts---dave-taylor--brandon-perry"},(0,n.kt)("a",{parentName:"h3",href:"https://nostarch.com/wcss2"},"Wicked Cool Shell Scripts - Dave Taylor & Brandon Perry")),(0,n.kt)("p",null,"Ideal for systems administrators and power users who can benefit from automating processes across many platforms. This book contains a lot of tips on how to standardise the behaviour of programs across Linux and Unix systems. If you find yourself regularly shell scripting and want to start to build a library of your own scripts to run across machines, this is a great book to read. It will be particularly useful for anyone who faces challenges on incompatibilities and inconsistencies between programs on different systems."),(0,n.kt)("h3",{id:"practical-vim-edit-text-at-the-speed-of-thought-drew-niel"},(0,n.kt)("a",{parentName:"h3",href:"https://www.amazon.com/Practical-Vim-Thought-Pragmatic-Programmers/dp/1934356980"},"Practical Vim: Edit Text at the Speed of Thought, Drew Niel")),(0,n.kt)("p",null,"Absolutely the best book I've read on Vim, perfect for users of all levels. Written by Drew Niel, who is the author of the amazing ",(0,n.kt)("a",{parentName:"p",href:"http://vimcasts.org/"},"Vimcasts")," series."),(0,n.kt)("h3",{id:"joshua-levy---the-art-of-the-command-line"},(0,n.kt)("a",{parentName:"h3",href:"https://github.com/jlevy/the-art-of-command-line"},"Joshua Levy - The Art of the Command Line")),(0,n.kt)("p",null,'This is a wonderful repository, which aims to help you "Master the command line, in one page". This page is full of useful resources and is a superb reference for users from novice all the way to advanced!'),(0,n.kt)("h2",{id:"essential-online-resources"},"Essential Online Resources"),(0,n.kt)("p",null,"These resources are available online and are particularly useful."),(0,n.kt)("h3",{id:"command-line-interface-guidelines"},(0,n.kt)("a",{parentName:"h3",href:"https://clig.dev/"},"Command Line Interface Guidelines")),(0,n.kt)("blockquote",null,(0,n.kt)("p",{parentName:"blockquote"},"An open-source guide to help you write better command-line programs, taking traditional UNIX principles and updating them for the modern day.")),(0,n.kt)("p",null,"This is an excellent online resource that describes the principles, but also practical patterns, that you can use to design CLI programs that interface well to other programs and make sense to human operators. This is great reading if you are building any kind of CLI app."),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://clig.dev/"},"https://clig.dev/")),(0,n.kt)("h3",{id:"the-missing-semester-of-your-cs-education"},"The Missing Semester of Your CS Education"),(0,n.kt)("p",null,"The introduction in the site says it better than I could!"),(0,n.kt)("p",null,"Classes teach you all about advanced topics within CS, from operating systems to machine learning, but there's one critical subject that's rarely covered, and is instead left to students to figure out on their own: proficiency with their tools. We\u2019ll teach you how to master the command-line, use a powerful text editor, use fancy features of version control systems, and much more!"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://missing.csail.mit.edu/"},"https://missing.csail.mit.edu/")),(0,n.kt)("p",null,"Thanks to ",(0,n.kt)("a",{parentName:"p",href:"https://www.linkedin.com/in/lrwilke/"},"Lennart R. Wilke")," for sharing this with me!"),(0,n.kt)("h3",{id:"maarten-billemont---bash-guide"},(0,n.kt)("a",{parentName:"h3",href:"http://mywiki.wooledge.org/BashGuide"},"Maarten Billemont - Bash Guide")),(0,n.kt)("p",null,"This is an excellent and very detailed resource on bash. It goes into a lot of detail on how things are implemented and is a great resource to find all of the low level details you might be interested in. There is also a more modern version being currently drafted at ",(0,n.kt)("a",{parentName:"p",href:"https://guide.bash.academy/"},"https://guide.bash.academy/"),"."),(0,n.kt)("h2",{id:"great-books"},"Great Books"),(0,n.kt)("h2",{id:"great-videos"},"Great Videos"),(0,n.kt)("h3",{id:"the-unix-operating-system"},"The UNIX Operating System"),(0,n.kt)("p",null,"This is a fascinating video from the late 60s - you might be amazed at how much of the stuff you see here is still fundamental to how we work with computers today. The shell, pipelines, the file system and more:"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://www.youtube.com/watch?v=tc4ROCJYbm0"},(0,n.kt)("img",{alt:"Screenshot: The Unix Operating System",src:a(3073).Z,width:"1784",height:"1254"}))),(0,n.kt)("h3",{id:"the-mother-of-all-demos"},"The Mother of All Demos"),(0,n.kt)("p",null,"Another fascinating video from the late 60s - see the mouse, hypertext, word processing and more:"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://www.youtube.com/watch?v=yJDv-zdhzMY"},(0,n.kt)("img",{alt:"Screenshot: The Mother of all Demos",src:a(8779).Z,width:"1780",height:"1246"}))),(0,n.kt)("h3",{id:"where-grep-came-from---computerphile"},"Where Grep Came From - Computerphile"),(0,n.kt)("p",null,"Professor Brian Kerninghan explains where the ",(0,n.kt)("inlineCode",{parentName:"p"},"grep")," tool came from:"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://www.youtube.com/watch?v=NTfOnGZUZDk"},(0,n.kt)("img",{alt:"Screenshot: YouTube Where Grep Came From",src:a(6013).Z,width:"2370",height:"1502"}))),(0,n.kt)("h2",{id:"advanced"},"Advanced"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://www.in-ulm.de/~mascheck/various/shebang"},"The #! magic, details about the shebang/hash-bang mechanism on various Unix flavours\n")," - Excellent article on the internals of how different Unix platforms handle shebangs.")),(0,n.kt)("h2",{id:"research"},"Research"),(0,n.kt)("p",null,"The following articles and links were particularly useful when writing this book:"),(0,n.kt)("p",null,"Great source of shell tricks and tips:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://twitter.com/krisnova/status/1109618657305333761?s=11"},"https://twitter.com/krisnova/status/1109618657305333761?s=11")),(0,n.kt)("li",{parentName:"ul"},"Useful Linux commands: ",(0,n.kt)("a",{parentName:"li",href:"https://www.thegeekstuff.com/2010/11/50-linux-commands/"},"https://www.thegeekstuff.com/2010/11/50-linux-commands/"))),(0,n.kt)("h2",{id:"todo"},"TODO"),(0,n.kt)("p",null,"Books I'm reading as part of the research for this book."),(0,n.kt)("ul",{className:"contains-task-list"},(0,n.kt)("li",{parentName:"ul",className:"task-list-item"},(0,n.kt)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ","How Linux Works, 2nd Edition: What Every Superuser Should Know - Brian Ward"),(0,n.kt)("li",{parentName:"ul",className:"task-list-item"},(0,n.kt)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ","Wicked Cool Shell Scripts, 2nd Edition: 101 Scripts for Linux, OS X, and UNIX Systems - Dave Taylor"),(0,n.kt)("li",{parentName:"ul",className:"task-list-item"},(0,n.kt)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ","The Linux Command Line: A Complete Introduction - William E. Shotts Jr."),(0,n.kt)("li",{parentName:"ul",className:"task-list-item"},(0,n.kt)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ",(0,n.kt)("a",{parentName:"li",href:"https://pragprog.com/book/modvim/modern-vim"},"Modern Vim: Craft Your Development Environment with Vim 8 and Neovim, Drew Niel")),(0,n.kt)("li",{parentName:"ul",className:"task-list-item"},(0,n.kt)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ",(0,n.kt)("a",{parentName:"li",href:"https://smile.amazon.com/Serious-Cryptography-Practical-Introduction-Encryption/dp/1593278268/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr="},"Serious Cryptography: A Practical Introduction to Modern Encryption by Jean-Philippe Aumasson"))))}d.isMDXComponent=!0},2841:(e,t,a)=>{a.d(t,{Z:()=>o});const o=a.p+"assets/images/applied-cryptography-41972400cd69dc964208e0c1b09f5110.jpeg"},5543:(e,t,a)=>{a.d(t,{Z:()=>o});const o=a.p+"assets/images/pro-git-2-52eab2fa942bda3675cf4d5498e0737d.png"},3073:(e,t,a)=>{a.d(t,{Z:()=>o});const o=a.p+"assets/images/atat-the-unix-operating-system-69b2792f90350605dcf31c0faaebce37.png"},8779:(e,t,a)=>{a.d(t,{Z:()=>o});const o=a.p+"assets/images/the-mother-of-all-demos-236d7faa36662a621d079b0a6a772dfd.png"},6013:(e,t,a)=>{a.d(t,{Z:()=>o});const o=a.p+"assets/images/youtube-where-grep-came-from-5519a145dbc3be578d877a23a5325bb0.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/0e67b310.af7fcee8.js b/pr-preview/pr-346/assets/js/0e67b310.af7fcee8.js new file mode 100644 index 00000000..b3c18ea1 --- /dev/null +++ b/pr-preview/pr-346/assets/js/0e67b310.af7fcee8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[5618],{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 p=a.createContext({}),s=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},m=function(e){var t=s(e.components);return a.createElement(p.Provider,{value:t},e.children)},d="mdxType",h={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,p=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),d=s(n),c=i,u=d["".concat(p,".").concat(c)]||d[c]||h[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 l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l[d]="string"==typeof e?e:i,r[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>r,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var a=n(7462),i=(n(7294),n(3905));const o={title:"Build Commands on the Fly",slug:"/part-3-manipulating-text/build-commands-on-the-fly/"},r=void 0,l={unversionedId:"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.",source:"@site/docs/03-manipulating-text/17-build-commands-on-the-fly/index.md",sourceDirName:"03-manipulating-text/17-build-commands-on-the-fly",slug:"/part-3-manipulating-text/build-commands-on-the-fly/",permalink:"/part-3-manipulating-text/build-commands-on-the-fly/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/03-manipulating-text/17-build-commands-on-the-fly/index.md",tags:[],version:"current",frontMatter:{title:"Build Commands on the Fly",slug:"/part-3-manipulating-text/build-commands-on-the-fly/"},sidebar:"sidebar",previous:{title:"Advanced Text Manipulation",permalink:"/part-3-manipulating-text/advanced-text-manipulation/"},next:{title:"Part 4 - Shell Scripting",permalink:"/part-4-shell-scripting/"}},p={},s=[{value:"Introducing Xargs",id:"introducing-xargs",level:2},{value:"Handling Whitespace, Special Characters and Tracing",id:"handling-whitespace-special-characters-and-tracing",level:2},{value:"One Command or Many Commands?",id:"one-command-or-many-commands",level:2},{value:"Constructing more complex commands with the 'I' Parameter",id:"constructing-more-complex-commands-with-the-i-parameter",level:2},{value:"Requesting Confirmation with the Prompt Option",id:"requesting-confirmation-with-the-prompt-option",level:2},{value:"Splitting up Input with a Delimiter",id:"splitting-up-input-with-a-delimiter",level:2},{value:"Summary",id:"summary",level:2}],m={toc:s};function d(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,"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 ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," command and show how to use our text manipulation skills to dynamically build complex commands on the fly."),(0,i.kt)("h2",{id:"introducing-xargs"},"Introducing Xargs"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," ","[",(0,i.kt)("em",{parentName:"p"},"build and execute commands"),"]",' command takes input, uses the input to create commands, then executes the commands. I tend to remember it as "Execute with Arguments" as the name ',(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," sounds a little odd!"),(0,i.kt)("p",null,"How ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," is used is probably easiest to see with an example. Let's use it to build a set of commands which will remove any empty files from a folder."),(0,i.kt)("p",null,"Before we show ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," let's create some empty files which we'll later clean up:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ mkdir -p ~/effective-shell/tmp\n$ cd ~/effective-shell/tmp\n$ touch file{1..100}.txt\n")),(0,i.kt)("p",null,"We're using a nice shell trick here called ",(0,i.kt)("em",{parentName:"p"},"Brace Expansion")," - the shell will expand ",(0,i.kt)("inlineCode",{parentName:"p"},"file{1..100}.txt")," into ",(0,i.kt)("inlineCode",{parentName:"p"},"file1.txt"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"file2.txt")," and so on, all the way to ",(0,i.kt)("inlineCode",{parentName:"p"},"file100.txt"),"."),(0,i.kt)("p",null,"We could just look for empty files in our ",(0,i.kt)("inlineCode",{parentName:"p"},"/tmp")," folder for this example, but a file in that folder might be in use, so a safer way to demonstrate ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," is to use some temporary files which we create ourselves."),(0,i.kt)("p",null,"We could search for empty files with the command below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -empty\nfile1.txt\nfile2.txt\nfile3.txt\nfile4.txt\nfile5.txt\n...\n")),(0,i.kt)("admonition",{title:"A refresher on Finding Files",type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"In this chapter we'll be using the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," (",(0,i.kt)("em",{parentName:"p"},"find files and folders"),") command a lot - if you need a refresher, check ",(0,i.kt)("a",{parentName:"p",href:"/part-2-core-skills/finding-files"},"Chapter 11 - Finding Files"),".")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command has outputted a list of files, now we want to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," (",(0,i.kt)("em",{parentName:"p"},"remove file"),") command to delete each one. Let's just ",(0,i.kt)("em",{parentName:"p"},"pipe")," the list of files to the ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," command, check ",(0,i.kt)("a",{parentName:"p",href:"/part-2-core-skills/thinking-in-pipelines/"},"Chapter 7 - Thinking in Pipelines")," if you need a reminder of how piping works:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -empty | rm\nrm: missing operand\nTry 'rm --help' for more information.\n")),(0,i.kt)("p",null,"What's going on here? Well basically the issue is that the ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," command doesn't actually read the list of files from ",(0,i.kt)("em",{parentName:"p"},"stdin"),", the list of files has to be passed as a parameter to the command. How can we take this list of files and pass it to ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," as a set of parameters?"),(0,i.kt)("p",null,"This is what ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," is for! Before we delete the files, let's just see what happens when we pass the list to ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -empty | xargs\n./file40.txt ./file8.txt ./file35.txt ./file81.txt ...\n")),(0,i.kt)("p",null,"By default ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," take the input, joins each line together with a space and then passes it to the ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," command. The ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," command writes it out to the screen."),(0,i.kt)("p",null,"We can change the command ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," passes the arguments to:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -empty | xargs echo rm\nrm ./file40.txt ./file8.txt ./file35.txt ./file81.txt ...\n")),(0,i.kt)("p",null,"Very interesting! Now we've told ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," to pass the output to the ",(0,i.kt)("inlineCode",{parentName:"p"},"echo rm")," command - this just writes out ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," followed by the list of files. Putting ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," before whatever command you want to run is a useful way to ",(0,i.kt)("em",{parentName:"p"},"check")," the command before we commit to running it."),(0,i.kt)("p",null,"Let's finish the job and delete each file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -empty | xargs rm\n")),(0,i.kt)("p",null,"Done! You can run ",(0,i.kt)("inlineCode",{parentName:"p"},"ls")," to confirm that the file has been deleted."),(0,i.kt)("p",null,"This is ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," - it ",(0,i.kt)("em",{parentName:"p"},"constructs and executes")," a command using ",(0,i.kt)("em",{parentName:"p"},"arguments")," from ",(0,i.kt)("em",{parentName:"p"},"standard input"),". Now let's see how we can take this further."),(0,i.kt)("h2",{id:"handling-whitespace-special-characters-and-tracing"},"Handling Whitespace, Special Characters and Tracing"),(0,i.kt)("p",null,"One common challenge with ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," is how to deal with spaces. To see what I mean, let's create three files with spaces in the names:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ touch "chapter "{1,2,3}.md\n$ find . -type f\n./chapter 1.md\n./chapter 2.md\n./chapter 3.md\n')),(0,i.kt)("p",null,"What if we wanted to delete these files? Let's try that with ",(0,i.kt)("inlineCode",{parentName:"p"},"rm"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type f | xargs rm\nrm: cannot remove './chapter': No such file or directory\nrm: cannot remove '1.md': No such file or directory\n...\n")),(0,i.kt)("p",null,"The file name has a space in it, which is confusing ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," as it thinks we're providing six paths rather than three."),(0,i.kt)("p",null,"We can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-t")," (",(0,i.kt)("em",{parentName:"p"},"trace"),") option to see what ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," actually tried to do:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type f | xargs -t rm\nrm ./chapter 1.md ./chapter 2.md ./chapter 3.md\n...\n")),(0,i.kt)("p",null,"Hopefully you can spot the error - the ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," command thinks it needs to remove six files, because there are spaces in the filenames and there are not quotes around the filenames to let ",(0,i.kt)("inlineCode",{parentName:"p"},"rm")," know this!"),(0,i.kt)("p",null,"Fortunately, ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," loves ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," - they are part of the same package of tools (which is called 'findutils'). And there's a special pair of options that can deal with this."),(0,i.kt)("p",null,"For find, we are going to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-print0")," action and for ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," we'll use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-0")," option. Let's see how it looks now, then describe what's going on under the hood:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type f -print0 | xargs -0 -t rm\nrm './chapter 1.md' './chapter 2.md' './chapter 3.md'\n")),(0,i.kt)("p",null,"In ",(0,i.kt)("a",{parentName:"p",href:"/part-2-core-skills/finding-files"},"Chapter 11 - Finding Files")," we saw that the ",(0,i.kt)("em",{parentName:"p"},"default")," action of the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command is ",(0,i.kt)("inlineCode",{parentName:"p"},"-print"),", which writes out the path of each item found. The ",(0,i.kt)("inlineCode",{parentName:"p"},"-print0")," action is very similar - but it instead it writes out each item followed by a special 'null' character",(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,"Now that we've told ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," to end each result with a special 'null' character, we just tell ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," that the 'null' character is what separates each line of input. We do this with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-0")," (",(0,i.kt)("em",{parentName:"p"},"use NUL as separators"),") option."),(0,i.kt)("p",null,"You don't need to really understand the internals - if you are a computer programmer it might make sense, this is how strings in things like the C Programming language work. All you need to know is that it means the ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," program won't get confused when it sees spaces, tabs, quotes, newlines, or anything else which might be goofy in a file name."),(0,i.kt)("p",null,"My recommendation would be to ",(0,i.kt)("em",{parentName:"p"},"always")," pair up the ",(0,i.kt)("inlineCode",{parentName:"p"},"-print0")," action with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-0")," option - it means you won't get caught out by odd file names. And definitely make use of the ",(0,i.kt)("inlineCode",{parentName:"p"},"-t")," (",(0,i.kt)("em",{parentName:"p"},"trace"),") option to see what ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," is actually doing!"),(0,i.kt)("h2",{id:"one-command-or-many-commands"},"One Command or Many Commands?"),(0,i.kt)("p",null,"By default ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," takes all of the input and passes it as a ",(0,i.kt)("em",{parentName:"p"},"set")," of arguments to the provided command. We can see this below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ touch file{1..5}\n$ find . -type f | xargs echo\n./file1 ./file2 ./file3 ./file4 ./file5\n")),(0,i.kt)("p",null,"We don't need to provide ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs"),", it is the default, but I have added it for clarity. But what is really important is that we have called ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," once and once only."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," has passed ",(0,i.kt)("em",{parentName:"p"},"all")," of the arguments it has been given to the command."),(0,i.kt)("p",null,"We can tell ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," how many lines of input it should use for the command with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-L")," (",(0,i.kt)("em",{parentName:"p"},"max lines"),") parameter:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type f | xargs -L 1 echo\n./file1\n./file2\n./file3\n./file4\n./file5\n")),(0,i.kt)("p",null,"We've now called the ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," command once for each line of input, meaning that ",(0,i.kt)("inlineCode",{parentName:"p"},"echo")," has been called five times."),(0,i.kt)("p",null,"In general if you can provide all of the arguments to a single command the system may be able to process the command slightly faster. However, if there are lots of arguments, the command itself might not be able to handle all of the arguments you give it."),(0,i.kt)("p",null,"You can set ",(0,i.kt)("inlineCode",{parentName:"p"},"-L")," to other values too - ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," will use up to the number of lines provided:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type f | xargs -L 3 echo\n./file1 ./file2 ./file3\n./file4 ./file5\n")),(0,i.kt)("p",null,"Here we've allowed up to three input lines per command."),(0,i.kt)("p",null,"You will probably ",(0,i.kt)("em",{parentName:"p"},"not")," use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-L")," parameter very often, but it is really important that you understand what it does. And that is because many of the other options we'll use ",(0,i.kt)("em",{parentName:"p"},"imply")," ",(0,i.kt)("inlineCode",{parentName:"p"},"-L 1")," - we'll see why in the next example."),(0,i.kt)("h2",{id:"constructing-more-complex-commands-with-the-i-parameter"},"Constructing more complex commands with the 'I' Parameter"),(0,i.kt)("p",null,"You have probably noticed by now that the ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," command puts the arguments it is given at the end of the command you write."),(0,i.kt)("p",null,"What if you need the arguments to go somewhere else? For example, what if I wanted to copy every text file in a folder to another location?"),(0,i.kt)("p",null,"Here's how we might start - and what'll go wrong!"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -name \"*.txt\" -print0 | xargs -0 -t cp ~/backups\ncp /home/dwmkerr/backups ./file2.txt ./file3.txt ./file1.txt\ncp: target './file1.txt' is not a directory\n")),(0,i.kt)("p",null,"The problem is that the destination location for where we copy the files has to be the ",(0,i.kt)("em",{parentName:"p"},"last")," parameter - but ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," puts the list of files at the end of the command."),(0,i.kt)("p",null,"And by the way - we've used the ",(0,i.kt)("inlineCode",{parentName:"p"},"-0")," parameter to make sure that funny filenames are handled properly (a good habit to get into) and the ",(0,i.kt)("inlineCode",{parentName:"p"},"-t")," parameter to trace - which means we see the command which will be run."),(0,i.kt)("p",null,"So we need to tell ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," where to put the list of arguments. We can do that with the ",(0,i.kt)("inlineCode",{parentName:"p"},"-I")," (",(0,i.kt)("em",{parentName:"p"},"replace string"),") parameter. This parameter lets us tell ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," exactly where we want to put the arguments:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "*.txt" -print0 | xargs -0 -t -I {} cp {} ~/backups\ncp ./file2.txt /home/dwmkerr/backups\ncp ./file3.txt /home/dwmkerr/backups\ncp ./file1.txt /home/dwmkerr/backups\n')),(0,i.kt)("p",null,"Here we have set the 'replacement string' to be ",(0,i.kt)("inlineCode",{parentName:"p"},"{}"),". This means when ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," sees ",(0,i.kt)("inlineCode",{parentName:"p"},"{}")," in the command it will replace it with the arguments we provide as its input."),(0,i.kt)("p",null,"The first observation you might make is that as soon as we use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-I")," parameter it ",(0,i.kt)("em",{parentName:"p"},"automatically")," implies that we use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-L 1")," parameter, i.e. we run the command once for each individual input line."),(0,i.kt)("p",null,"For the example we have shown above, this isn't really necessary, ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," could just write all of the arguments. The reason ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," does this is that we are not actually limited to using the replacement once only - we can use it multiple times."),(0,i.kt)("p",null,"Here's a similar example, but in this one we put ",(0,i.kt)("inlineCode",{parentName:"p"},".bak")," at the end of each filename as we copy it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ find . -name "*.txt" -print0 | xargs -0 -t -I {} cp {} ~/backups/{}.bak\ncp ./file2.txt /home/dwmkerr/backups/./file2.txt.bak\ncp ./file3.txt /home/dwmkerr/backups/./file3.txt.bak\ncp ./file1.txt /home/dwmkerr/backups/./file1.txt.bak\n')),(0,i.kt)("p",null,"Because we can use the replacement string multiple times, ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," splits up the commands so it is one command per input argument. If it didn't do this and we tried the command above it would not work properly."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-I")," parameter is incredibly powerful, it lets us construct complex commands."),(0,i.kt)("p",null,"You don't need to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"{}")," letters as the replacement string, any sequence of characters will work. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ env | xargs -I % echo "You have env var: % set!"\nYou have env var: SHELL=/bin/bash set!\nYou have env var: COLORTERM=truecolor set!\nYou have env var: EDITOR=vi set!\n')),(0,i.kt)("p",null,"In this example we used ",(0,i.kt)("inlineCode",{parentName:"p"},"%")," as the replacement string."),(0,i.kt)("p",null,"You might wonder why is ",(0,i.kt)("inlineCode",{parentName:"p"},"{}")," so commonly used in examples or the manpages. The reason is that this is the default replacement string used by ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," if we perform an action like ",(0,i.kt)("inlineCode",{parentName:"p"},"-exec"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ find . -type f -empty -exec stat {} \\;\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"{}")," characters are used as the placeholder for files found with the ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," command, so people often use the same placeholder for ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs"),", but you are not required to use these characters."),(0,i.kt)("h2",{id:"requesting-confirmation-with-the-prompt-option"},"Requesting Confirmation with the Prompt Option"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"-p")," (",(0,i.kt)("em",{parentName:"p"},"prompt"),") option tells ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," to ask the user to confirm each command before it is run."),(0,i.kt)("p",null,"Let's test this out by deleting a set of 'pods' from a Kubernetes cluster. You don't have to worry about what a Kubernetes cluster is, I'm just using this as an example to highlight that you don't have to be limited to using ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," as the input for ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs"),"!"),(0,i.kt)("p",null,"On my machine I can show the pods available to me in my cluster with this command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ kubectl get pods -o name\npod/my-app\npod/nginx\npod/postgres\n")),(0,i.kt)("p",null,"Three pods are shown. I could use this command to build input to ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," to let me to chose which pods to delete, interactively:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'$ kubectl get pods -o name | xargs -L 1 -p kubectl delete\nkubectl --context minikube delete pod/my-app?...n\nkubectl --context minikube delete pod/nginx?...y\npod "nginx" deleted\nkubectl --context minikube delete pod/postgres?...n\n')),(0,i.kt)("p",null,"This is fantastic! We've used ",(0,i.kt)("inlineCode",{parentName:"p"},"-L 1")," to make sure that we only deal with one pod at a time (rather than trying to delete all three at once) and the ",(0,i.kt)("inlineCode",{parentName:"p"},"-p")," flag to ask the user to press 'y' or 'n' in each case. The ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," command helpfully shows us what it is going to do and asks for confirmation first."),(0,i.kt)("p",null,"I think this really hints at the true power of ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," - yes it can be combined with ",(0,i.kt)("inlineCode",{parentName:"p"},"find")," to perform operations on files, but it can also be used with other tools to build more complex operations."),(0,i.kt)("h2",{id:"splitting-up-input-with-a-delimiter"},"Splitting up Input with a Delimiter"),(0,i.kt)("p",null,"We can ask the user whether they want to see files in ",(0,i.kt)("em",{parentName:"p"},"all")," of their 'path' locations with the command below:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ echo $PATH | xargs -d ':' -p -L 1 ls\nls /home/dwmkerr/.pyenv/shims ?...n\nls /home/dwmkerr/.nvm/versions/node/v14.15.1/bin ?...n\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH")," environment variable holds all of the folders the shell will search in for binaries - and each folder is separated by a ",(0,i.kt)("inlineCode",{parentName:"p"},":")," colon character (you can read more about ",(0,i.kt)("inlineCode",{parentName:"p"},"$PATH")," in ",(0,i.kt)("a",{parentName:"p",href:"/part-2-core-skills/finding-files"},"Chapter 10 - Understanding Commands"),"."),(0,i.kt)("p",null,"We use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-d")," (",(0,i.kt)("em",{parentName:"p"},"delimiter"),") parameter to tell ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," that each argument in the input is separated with a colon. We also use the ",(0,i.kt)("inlineCode",{parentName:"p"},"-L 1")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"-p")," parameters to process this input one folder at a time and ask the user if they want to see the contents of the folder."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"In this chapter we introduced ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs"),", a powerful command which allows us to build other commands on the fly. We can trace, showing how the resulting command will look, ask the user for confirmation, control how many commands we run and more."),(0,i.kt)("p",null,"There are more options for the ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," command, you can read all about them with ",(0,i.kt)("inlineCode",{parentName:"p"},"man xargs"),". But I think if you learn the key parameters we've shown in this chapter you'll be well equipped to use ",(0,i.kt)("inlineCode",{parentName:"p"},"xargs")," in your day to day wok."),(0,i.kt)("p",null,"In the next chapter we'll look at some of the advanced features which are built into most shells which allow us to manipulate text."),(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 character is ASCII NUL, which is the number zero. This is often used in programming to represent 'null' or 'nothing at all', not the ",(0,i.kt)("em",{parentName:"li"},"digit")," zero as is used when printing to the screen, which is actually represented by number 30. You can see the actual ASCII table with ",(0,i.kt)("inlineCode",{parentName:"li"},"man ascii"),".",(0,i.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/0f7c9ced.0429bb55.js b/pr-preview/pr-346/assets/js/0f7c9ced.0429bb55.js new file mode 100644 index 00000000..0a2b55da --- /dev/null +++ b/pr-preview/pr-346/assets/js/0f7c9ced.0429bb55.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[6179],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>c});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 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},m=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},h="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,o=e.originalType,s=e.parentName,m=i(e,["components","mdxType","originalType","parentName"]),h=p(n),u=r,c=h["".concat(s,".").concat(u)]||h[u]||d[u]||o;return n?a.createElement(c,l(l({ref:t},m),{},{components:n})):a.createElement(c,l({ref:t},m))}));function c(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 s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[h]="string"==typeof e?e:r,l[1]=i;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>o,metadata:()=>i,toc:()=>p});var a=n(7462),r=(n(7294),n(3905));const o={title:"Customising Your Command Prompt",slug:"/part-5-building-your-toolkit/customising-your-command-prompt"},l=void 0,i={unversionedId:"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.",source:"@site/docs/05-building-your-toolkit/25-customising-your-command-prompt/index.md",sourceDirName:"05-building-your-toolkit/25-customising-your-command-prompt",slug:"/part-5-building-your-toolkit/customising-your-command-prompt",permalink:"/part-5-building-your-toolkit/customising-your-command-prompt",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/05-building-your-toolkit/25-customising-your-command-prompt/index.md",tags:[],version:"current",frontMatter:{title:"Customising Your Command Prompt",slug:"/part-5-building-your-toolkit/customising-your-command-prompt"},sidebar:"sidebar",previous:{title:"Configuring the Shell",permalink:"/part-5-building-your-toolkit/configuring-the-shell"},next:{title:"Managing your Dotfiles",permalink:"/part-5-building-your-toolkit/managing-your-dotfiles"}},s={},p=[{value:"The Command Promptindex",id:"the-command-promptindex",level:2},{value:"Customising the Command Prompt",id:"customising-the-command-prompt",level:2},{value:"The Prompt String",id:"the-prompt-string",level:3},{value:"Special Characters",id:"special-characters",level:3},{value:"Changing the Colour and Text Formatting",id:"changing-the-colour-and-text-formatting",level:3},{value:"Adding Data to the Command Prompt",id:"adding-data-to-the-command-prompt",level:3},{value:"A Shell Script to Customise the Prompt",id:"a-shell-script-to-customise-the-prompt",level:2},{value:"Additional Prompt Configuration",id:"additional-prompt-configuration",level:2},{value:"Z-Shell and Oh-My-Zsh",id:"z-shell-and-oh-my-zsh",level:2},{value:"Summary",id:"summary",level:2}],m={toc:p};function h(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,"The shell has a large number of options available that you can use to customise the ",(0,r.kt)("em",{parentName:"p"},"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."),(0,r.kt)("p",null,"We will also create a script that allows us to set our own command prompt 'theme' from a list that we can extend over time. This script will also handle the differences between Bash-like shells and Z-Shell for us, allowing us to have a consistent command prompt across different types of shells!"),(0,r.kt)("h2",{id:"the-command-promptindex"},"The Command Prompt"),(0,r.kt)("p",null,"The ",(0,r.kt)("em",{parentName:"p"},"command prompt")," is the text that is shown to the left of your cursor to show that the shell is waiting for you to type a command. Each distribution comes with its own configuration for the command prompt, but the default is often similar to the one shown below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~$\n")),(0,r.kt)("p",null,"This is the prompt on an Ubuntu virtual machine I have set up. If you want to set up a free virtual machine yourself, you can follow the guide at ",(0,r.kt)("a",{parentName:"p",href:"../../work-in-progress"},"Appendix - Setting Up a Linux Virtual Machine"),"."),(0,r.kt)("p",null,"Let's take a look at each of the components that make up the prompt:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"dwmkerr")," - The first thing that is shown is the name of the current user"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"@")," - Next we have an 'at symbol' character that is used as a separator between the username field and the following field"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"effective-shell-ubuntu"),": This is the ",(0,r.kt)("em",{parentName:"li"},"hostname")," of the machine"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},":")," - A colon separates the hostname from the next field"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"~")," - Next we have the current working directory"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"$")," - Finally we have the prompt itself, the ",(0,r.kt)("inlineCode",{parentName:"li"},"$")," symbol shows we are a normal user, rather than a 'root' user")),(0,r.kt)("p",null,"If we change directory, our prompt will be updated:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~$ cd effective-shell\ndwmkerr@effective-shell-ubuntu-20:~/effective-shell$\n")),(0,r.kt)("p",null,"If we change to the 'super' user we can see that the username changes to ",(0,r.kt)("inlineCode",{parentName:"p"},"root")," and the ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," dollar symbol changes to a ",(0,r.kt)("inlineCode",{parentName:"p"},"#")," hash symbol:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ununtu-20:~/effective-shell$ sudo su\nroot@effective-shell-ununtu-20:/home/dwmkerr/effective-shell#\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"#")," symbol is a useful reminder that we are the root user. It is important to be careful when running commands as the root user as we could easily break things by changing system files."),(0,r.kt)("p",null,"So out-of-the box on most systems our command prompt shows a number of useful fields. But we can actually customise this prompt to include almost any kind of information we would like to see. Let's take a look!"),(0,r.kt)("h2",{id:"customising-the-command-prompt"},"Customising the Command Prompt"),(0,r.kt)("p",null,"The structure of the command prompt is specified in the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," shell variable. This stands for 'Prompt String 1'. The shell uses this variable to write out the command prompt."),(0,r.kt)("p",null,"We can see the contents of this variable by using ",(0,r.kt)("inlineCode",{parentName:"p"},"echo")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"printf")," to write it to the screen:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ echo $PS1\n\\[\\e]0;\\u@\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\[\\033[01;32m\\]\\u@\\h\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$\n")),(0,r.kt)("p",null,"This looks extremely complicated - but don't worry, by the time we've finished this chapter you'll be able to understand what this mess of special characters means!"),(0,r.kt)("p",null,"The easiest way to see how these special prompt strings work is to start using them, so let's get started and customise our prompt."),(0,r.kt)("h3",{id:"the-prompt-string"},"The Prompt String"),(0,r.kt)("p",null,"You can set your own prompt string by setting the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ PS1="---\x3e "\n---\x3e\n')),(0,r.kt)("p",null,"The shell will use the contents of the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable to display the prompt. We can use plan text as shown above, but there's also a lot more that we can do to customise this prompt!"),(0,r.kt)("h3",{id:"special-characters"},"Special Characters"),(0,r.kt)("p",null,"When the shell reads the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable, it allows certain special characters to be specified. These characters can be used to customise how the prompt string looks."),(0,r.kt)("p",null,"The special characters that the shell uses are listed below:"),(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},"Characters"),(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"},"\\a")),(0,r.kt)("td",{parentName:"tr",align:null},"The special 'beep' character, that tells the shell to play a beep sound through the speakers.")),(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},"The date in \"Weekday Month Date\" format, for example: 'Tue May 26')")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\D{format}")),(0,r.kt)("td",{parentName:"tr",align:null},"The date in a format specified by the ",(0,r.kt)("inlineCode",{parentName:"td"},"format")," value.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\e")),(0,r.kt)("td",{parentName:"tr",align:null},"An ASCII escape character (033). This is used to print special characters.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\h")),(0,r.kt)("td",{parentName:"tr",align:null},"The hostname up to the first ",(0,r.kt)("inlineCode",{parentName:"td"},".")," dot.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\H")),(0,r.kt)("td",{parentName:"tr",align:null},"The hostname.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\j")),(0,r.kt)("td",{parentName:"tr",align:null},"The number of jobs currently managed by the shell.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\l")),(0,r.kt)("td",{parentName:"tr",align:null},"The basename of the shell's terminal device.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\n")),(0,r.kt)("td",{parentName:"tr",align:null},"A newline character.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\r")),(0,r.kt)("td",{parentName:"tr",align:null},"A carriage return 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},"The name of the shell, the basename of ",(0,r.kt)("inlineCode",{parentName:"td"},"$0"),", for example: ",(0,r.kt)("inlineCode",{parentName:"td"},"-bash"),".")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\t")),(0,r.kt)("td",{parentName:"tr",align:null},"The current time in 24-hour HH:MM:SS format.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\T")),(0,r.kt)("td",{parentName:"tr",align:null},"The current time in 12-hour HH:MM:SS format.")),(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 current time in 12-hour am/pm format.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\A")),(0,r.kt)("td",{parentName:"tr",align:null},"The current time in 24-hour HH:MM format.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\u")),(0,r.kt)("td",{parentName:"tr",align:null},"The username of the current user.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\v")),(0,r.kt)("td",{parentName:"tr",align:null},"The version of bash, for example: '5.0'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\V")),(0,r.kt)("td",{parentName:"tr",align:null},"The release of bash, with the patch level, for example: '5.0.17'.")),(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},"The current working directory, with ",(0,r.kt)("inlineCode",{parentName:"td"},"$HOME")," abbreviated with a ",(0,r.kt)("inlineCode",{parentName:"td"},"~")," tilde symbol.")),(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},"The current working directory name (rather than the entire path as is used for ",(0,r.kt)("inlineCode",{parentName:"td"},"\\w"),").")),(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 history number of this command.")),(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 command number of this command")),(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 ",(0,r.kt)("inlineCode",{parentName:"td"},"$")," dollar symbol, unless we are a super-user, in which case the ",(0,r.kt)("inlineCode",{parentName:"td"},"#")," hash symbol is used.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\nnn")),(0,r.kt)("td",{parentName:"tr",align:null},"The character corresponding to the octal number ",(0,r.kt)("inlineCode",{parentName:"td"},"nnn"),", used to show special 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},"A ",(0,r.kt)("inlineCode",{parentName:"td"},"\\")," backslash 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},"The 'start of non-printing characters' sequence.")),(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 'end of non-printing characters' sequence.")))),(0,r.kt)("p",null,"Some of these sequences are reasonably self-explanatory, some are a little more complex. Let's use some of them now to see how we can customise the prompt."),(0,r.kt)("admonition",{title:"Z-Shell",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"The ",(0,r.kt)("inlineCode",{parentName:"p"},"zsh")," shell uses different sequences. However, I suggest that you follow this chapter through to understand how Bash-like shells work and then you can apply the same techniques using Z-Shell. The Z-Shell documentation links are at the end of the chapter."),(0,r.kt)("p",{parentName:"admonition"},"Later on in this chapter we will introduce a function to help set the prompt, this function automatically converts to the prompt into Z-Shell format if needed. So the techniques you learn here should still be able to be used in Z-Shell.")),(0,r.kt)("p",null,"To change the prompt, all we need to do is set the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable. Let's start by changing the prompt so that it shows the date, time and the ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"#")," prompt symbol:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~$ PS1='\\d \\@ \\$ '\nSun Jun 06 12:43 PM $\n")),(0,r.kt)("p",null,"In this example we've used the ",(0,r.kt)("inlineCode",{parentName:"p"},"\\d")," (current date), ",(0,r.kt)("inlineCode",{parentName:"p"},"\\@")," (current time in am/pm format) and ",(0,r.kt)("inlineCode",{parentName:"p"},"\\$")," (prompt) and a space for our prompt. Notice that once we set ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," in the shell, the prompt immediately changed."),(0,r.kt)("p",null,"How about if we want to show the number of jobs, then the command number, then the prompt? Easy!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"Sun Jun 06 04:43 AM $ PS1='[\\j] (\\#) \\$ '\n[0] (4) $ sleep 10 &\n[1] 27598\n[1] (5) $ sleep 10 &\n[2] 27600\n[2] (6) $ sleep 10 &\n[3] 27601\n[3] (7) $\n")),(0,r.kt)("p",null,"In this example we've used the ",(0,r.kt)("inlineCode",{parentName:"p"},"\\j")," (current job) sequence, and surrounded it with square brackets. Then we used ",(0,r.kt)("inlineCode",{parentName:"p"},"#")," (command number), surrounded by parentheses, then the ",(0,r.kt)("inlineCode",{parentName:"p"},"\\$")," shell prompt. I also started some background jobs, that just run the ",(0,r.kt)("inlineCode",{parentName:"p"},"sleep")," (",(0,r.kt)("em",{parentName:"p"},"wait for a number of seconds"),") command, so that we can see that the number of jobs is changing. If you need a refresher on jobs, check ",(0,r.kt)("a",{parentName:"p",href:"/part-2-core-skills/job-control"},"Chapter 9 - Job Control"),"."),(0,r.kt)("p",null,"Note that we are using single quotes when specifying the value of the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1"),". If we didn't use single quotes, then the shell would see the dollar symbol and think that we were trying to use a variable. For a reminder on how quoting works, check ",(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)("p",null,"If you are following along or trying this out in your own shell, you might have noticed that we don't have any colours for the new prompts we have set, everything is shown in white. To set the colour of the prompt we need to use some special characters."),(0,r.kt)("h3",{id:"changing-the-colour-and-text-formatting"},"Changing the Colour and Text Formatting"),(0,r.kt)("p",null,"In the earlier part of this chapter we saw that the default prompt on systems like Ubuntu contains lots of special characters. For reference, here is the value of the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable on a clean Ubuntu 20 installations: "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ echo $PS1\n\\[\\e]0;\\u@\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\[\\033[01;32m\\]\\u@\\h\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$\n")),(0,r.kt)("p",null,"Some of these characters we might now be able to recognise, such as ",(0,r.kt)("inlineCode",{parentName:"p"},"\\u")," for the username and ",(0,r.kt)("inlineCode",{parentName:"p"},"\\h")," for the host. The characters that start with the sequence ",(0,r.kt)("inlineCode",{parentName:"p"},"\\033")," are ",(0,r.kt)("em",{parentName:"p"},"ANSI color codes"),". ANSI stands for ",(0,r.kt)("em",{parentName:"p"},"American National Standards Institute"),", an organisation that was set up to attempt to set common standards for computing platforms."),(0,r.kt)("p",null,"In the early days of Unix, each vendor developed their own special characters that could be used to control the visual formatting of output. These characters would vary from platform to platform, which made trying to create scripts or functionality that worked across multiple platforms complex. To deal with this, the ANSI organisation defined a common set of codes that could be printed to a terminal to control the visual style of the output."),(0,r.kt)("p",null,"To tell a terminal that we want to use a special sequence to control the formatting or output of text, we can use these ",(0,r.kt)("em",{parentName:"p"},"ANSI Escape Sequences"),". First we write out the characters ",(0,r.kt)("inlineCode",{parentName:"p"},"\\033")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"\\e"),". This is the sequence that represents the 'escape' key. The first version is the 'escape' key code written in 'octal' format (octal is a format where numbers are written in base eight, rather than base ten). The second sequence is an alternative way of writing the 'escape' key."),(0,r.kt)("p",null,"When a terminal sees the escape sequence, it knows that the ",(0,r.kt)("em",{parentName:"p"},"following")," sequence is used to define the formatting. The table below shows some of the different formats that can be used:"),(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},"Sequence"),(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)("strong",{parentName:"td"},"Foreground Color")),(0,r.kt)("td",{parentName:"tr",align:null})),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[30m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'black'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[31m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'red'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[32m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'green'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[33m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'yellow'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[34m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'blue'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[35m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'magenta'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[36m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'cyan'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[37m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'white' (normally light grey)")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Foreground Color (Bold)")),(0,r.kt)("td",{parentName:"tr",align:null})),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;30m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright black' (grey, or bold black)")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;31m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright red' (or bold red).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;32m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright green' (or bold green).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;33m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright yellow' (or bold yellow).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;34m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright blue' (or bold blue).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;35m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright purple' (or bold purple).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;36m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright cyan' (or bold cyan).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[1;37m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set foreground to 'bright white' (or bold white).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Background Color")),(0,r.kt)("td",{parentName:"tr",align:null})),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;40m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'black'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;41m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'red'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;42m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'green'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;43m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'brown'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;44m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'blue'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;45m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'purple'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;46m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'cyan'.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0;47m")),(0,r.kt)("td",{parentName:"tr",align:null},"Set background to 'white' (normally light grey).")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("strong",{parentName:"td"},"Reset Colors")),(0,r.kt)("td",{parentName:"tr",align:null})),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"\\033[0m")),(0,r.kt)("td",{parentName:"tr",align:null},"Reset the text colors.")))),(0,r.kt)("p",null,"Notice that each sequence starts with the 'escape' character, followed by ",(0,r.kt)("inlineCode",{parentName:"p"},"[")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"[1;"),". ",(0,r.kt)("inlineCode",{parentName:"p"},"[")," will use the 'normal' colour, ",(0,r.kt)("inlineCode",{parentName:"p"},"[1;")," will use the 'bright' colour (how this is shown depends on your terminal emulator, in many modern emulators the text is shown in the same colour but is bold). You can also use ",(0,r.kt)("inlineCode",{parentName:"p"},"[0;")," to clear any changes to the foreground or background before you set the new one. After this there are one of eight colours that can be used, specified by the characters in the range from ",(0,r.kt)("inlineCode",{parentName:"p"},"30m")," to ",(0,r.kt)("inlineCode",{parentName:"p"},"37m"),". The sequences in the range ",(0,r.kt)("inlineCode",{parentName:"p"},"40m")," to ",(0,r.kt)("inlineCode",{parentName:"p"},"47m")," set the background color. The sequence ",(0,r.kt)("inlineCode",{parentName:"p"},"0m")," resets the colors."),(0,r.kt)("p",null,"With these codes we can print coloured text. When we write text that uses escape sequences, we need to tell the shell that our text needs to have these escape sequences processed properly. We can use the ",(0,r.kt)("inlineCode",{parentName:"p"},"printf")," command or ",(0,r.kt)("inlineCode",{parentName:"p"},"echo -e")," to do this. ",(0,r.kt)("inlineCode",{parentName:"p"},"printf")," should be preferred as not all systems support the ",(0,r.kt)("inlineCode",{parentName:"p"},"-e")," parameter for ",(0,r.kt)("inlineCode",{parentName:"p"},"echo"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'printf "\\033[31mRED\\033[0m\\n"\nprintf "\\033[1;31mLIGHT RED\\033[0m\\n"\nprintf "\\033[0;30m\\033[42mBLACK ON GREEN\\033[0m\\n"\n')),(0,r.kt)("p",null,"The output of these commands will be the text below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"RED\nLIGHT RED\nBLACK ON GREEN\n")),(0,r.kt)("p",null,"However, the colour of the foreground and background should change on each line. The exact formatting will change depending on the terminal emulator you use. Some terminal emulators use bold text for the 'bright' colours. "),(0,r.kt)("p",null,"With our new knowledge of how to use ANSI Escape Sequences to set the format of text, we can update our ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable to show a prompt in colour. As an example, the code below sets the prompt to show the username in blue and the name of the current working directory in green, followed by a white ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," prompt symbol, followed by the 'reset' sequence so that the text we type afterwards does not have its colour changed:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ PS1='\\033[34m\\u \\033[32m\\W \\033[37m\\$ '\ndwmkerr effective-shell $\n")),(0,r.kt)("p",null,"The prompt above will be shown in color on modern terminals."),(0,r.kt)("p",null,"There is one snag to this. If you set your prompt in this way and press the 'up' and 'down' keys to cycle through previously entered commands, you might see that your shell prompt gets overwritten. The reason for this is that we need to tell the shell that colour and formatting sequences are 'non-printing' characters - the sequences don't actually produce written text in the terminal."),(0,r.kt)("p",null,"To deal with this we need to surround each colour sequence with the special characters ",(0,r.kt)("inlineCode",{parentName:"p"},"\\[")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"\\]"),". This tells the shell when a 'non-printing' sequence starts and when it ends. To fix our ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable, we can use the value below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"PS1='\\[\\033[34m\\]\\u \\[\\033[32m\\]\\W \\[\\033[37m\\]\\$ \\[\\033[0m\\]'\n")),(0,r.kt)("p",null,"Phew! This is a lot of work to go to just to format the colour of the prompt. Later in this chapter we'll build a script that will make it far easier to work with colours and text formatting!"),(0,r.kt)("h3",{id:"adding-data-to-the-command-prompt"},"Adding Data to the Command Prompt"),(0,r.kt)("p",null,"When we set the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable, we are simply setting it to a string. This string could be anything, for example:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~$ PS1='-Ready?---\x3e '\n-Ready?---\x3e \n")),(0,r.kt)("p",null,"We don't need to limit ourselves to the special sequences we've seen so far in this chapter - we can run any commands we like to build a command prompt. For example, we could use the use the ",(0,r.kt)("inlineCode",{parentName:"p"},"ls")," (",(0,r.kt)("em",{parentName:"p"},"list directory contents"),") and ",(0,r.kt)("inlineCode",{parentName:"p"},"wc")," (",(0,r.kt)("em",{parentName:"p"},"count lines and words"),") commands to count the number of files and folders in the current directory and show that in the prompt:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"PS1=\"$(ls -al | wc -l | tr -d '[:space:]') \\\\$ \"\n")),(0,r.kt)("p",null,"When I run this command, my prompt will look something like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"32 $\n")),(0,r.kt)("p",null,"We have used the ",(0,r.kt)("inlineCode",{parentName:"p"},"$()")," notation to run a sub-shell that lists the contents of the current directory and then pipes them to ",(0,r.kt)("inlineCode",{parentName:"p"},"wc -l"),", which counts the number of lines. Finally we pipe the result into ",(0,r.kt)("inlineCode",{parentName:"p"},"tr -d '[:space:]")," to remove the whitespace around the line count."),(0,r.kt)("p",null,"To use the ",(0,r.kt)("inlineCode",{parentName:"p"},"$()")," notation, or any shell variable, we have to use double quotes in the string, otherwise the shell will write out those characters literally. And because we are using double quotes, we need an extra backslash before last ",(0,r.kt)("inlineCode",{parentName:"p"},"\\$")," character to escape it, so that the shell doesn't try to treat it as a variable."),(0,r.kt)("p",null,"However - there's a subtle bug in this ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," configuration! Let's see what happens when we change directories:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"32 $ cd effective-shell/\n32 $ touch newfile-{1..10}\n32 $ \n")),(0,r.kt)("p",null,"In the session above I changed to the ",(0,r.kt)("em",{parentName:"p"},"effective-shell")," directory. But the count is still showing as ",(0,r.kt)("inlineCode",{parentName:"p"},"32"),". This is suspicious. After creating ten new files with ",(0,r.kt)("inlineCode",{parentName:"p"},"touch newfile-{1..10}")," the count still shows ",(0,r.kt)("inlineCode",{parentName:"p"},"32"),"."),(0,r.kt)("p",null,"The reason for this is that ",(0,r.kt)("inlineCode",{parentName:"p"},"32")," was the number of files and folders in the current directory ",(0,r.kt)("em",{parentName:"p"},"at the time the ",(0,r.kt)("inlineCode",{parentName:"em"},"PS1")," variable was set"),". We changed the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable once - what we really need to do is have the prompt count the files each time the prompt is shown."),(0,r.kt)("p",null,"Fortunately, there is a special syntax for this! We just put a ",(0,r.kt)("inlineCode",{parentName:"p"},"\\")," backslash character in front of the ",(0,r.kt)("inlineCode",{parentName:"p"},"$")," dollar symbol for the sub-shell:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"PS1=\"\\$(ls -al | wc -l | tr -d '[:space:]') \\\\$ \"\n")),(0,r.kt)("p",null,"The backslash before the sub-shell tells the shell that it should evaluate the sub-shell ",(0,r.kt)("em",{parentName:"p"},"each time")," the prompt is shown:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"32 $ touch newfile-{1..10}\n42 $\n")),(0,r.kt)("p",null,"This is where the real power of the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable comes into play. Because we set it using the shell itself, we can run ",(0,r.kt)("em",{parentName:"p"},"any")," commands that we find useful and integrate their output into our command prompt."),(0,r.kt)("p",null,"Let's see this in action by creating a script to make customising our prompt far easier and more intuitive!"),(0,r.kt)("h2",{id:"a-shell-script-to-customise-the-prompt"},"A Shell Script to Customise the Prompt"),(0,r.kt)("p",null,"We can write a script to make it much easier to customise our shell prompt. Rather than having to remember each of the colour sequences, we can store them in variables to make them easier to refer to. We can also run any commands that we'd like to run to allow us to show extra information."),(0,r.kt)("p",null,"There is a script in the Effective Shell samples at ",(0,r.kt)("em",{parentName:"p"},"~/effective-shell/scripts/set_ps1.sh")," that we can use to set our ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable in a much more user-friendly way."),(0,r.kt)("admonition",{title:"Downloading the Samples",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"Run the following commands in your shell to download the samples:"),(0,r.kt)("pre",{parentName:"admonition"},(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"curl effective.sh | sh\n"))),(0,r.kt)("p",null,"The ",(0,r.kt)("em",{parentName:"p"},"set_ps1.sh")," script is quite long, so let's go through it bit-by-bit."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'# Keep track of the original PS1 value.\n_original_ps1="${PS1}"\n\nset_ps1() {\n # Foreground colours.\n local fg_black=$(tput setaf 0) # \\033[30m\n local fg_red=$(tput setaf 1) # \\033[31m\n local fg_green=$(tput setaf 2) # \\033[32m\n local fg_yellow=$(tput setaf 3) # \\033[33m\n local fg_blue=$(tput setaf 4) # \\033[34m\n local fg_magenta=$(tput setaf 5) # \\033[35m\n local fg_cyan=$(tput setaf 6) # \\033[36m\n local fg_white=$(tput setaf 7) # \\033[37m\n\n # Background colours.\n local bg_black=$(tput setab 0) # \\033[40m\n local bg_red=$(tput setab 1) # \\033[41m\n local bg_green=$(tput setab 2) # \\033[42m\n local bg_yellow=$(tput setab 3) # \\033[43m\n local bg_blue=$(tput setab 4) # \\033[44m\n local bg_magenta=$(tput setab 5) # \\033[45m\n local bg_cyan=$(tput setab 6) # \\033[46m\n local bg_white=$(tput setab 7) # \\033[47m\n')),(0,r.kt)("p",null,"First, we store the current value of ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," in a variable named ",(0,r.kt)("inlineCode",{parentName:"p"},"_original_ps1"),". This is so that later on if we have changed the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable, we can change it back to what it was set to originally. The ",(0,r.kt)("inlineCode",{parentName:"p"},"_")," underscore in the variable name is a convention that indicates that this variable is used internally in the script."),(0,r.kt)("p",null,"Next, we define a function called ",(0,r.kt)("inlineCode",{parentName:"p"},"set_ps1"),". Then we use the ",(0,r.kt)("inlineCode",{parentName:"p"},"tput")," command (",(0,r.kt)("em",{parentName:"p"},"query terminfo database"),") to get the exact escape sequences for the foreground and background colours. For easy reference the escape sequences are shown to the right of each command as a comment."),(0,r.kt)("p",null,"Next, we get the escape sequences for some of the other formatting options, such as 'bold' (which will be 'bright' on some terminals):"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"}," # Text styles and reset. Note that on some terminals 'bold' will produce\n # light colours for bright colours, on others it will actually show the text\n # in bold.\n local bold=$(tput bold) # \\033[1m\n local dim=$(tput dim) # \\033[2m\n local start_underline=$(tput smul) # \\033[4m\n local stop_underline=$(tput mmul) # \\033[24m\n local reset=$(tput sgr0) # \\033[0m\n")),(0,r.kt)("p",null,"You might recall the ",(0,r.kt)("inlineCode",{parentName:"p"},"tput")," command from the section 'colourising output' in ",(0,r.kt)("a",{parentName:"p",href:"../../part-4-shell-scripting/useful-patterns-for-shell-scripts"},"Chapter 23 - Useful Patterns for Shell Scripts"),"."),(0,r.kt)("p",null,"After this we use a ",(0,r.kt)("inlineCode",{parentName:"p"},"case")," statement to set the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable based on the value of the first parameter that was provided to the function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},' # Depending on the name of the theme provided, set the prompt.\n case $1 in\n debian)\n # Debian/Ubuntu style:\n # \\u@\\h - username@host (bold/green)\n # \\w - working directory (bold/blue)\n # \\$ - prompt (# if root, otherwise $) (bold/white)\n PS1="\\[${bold}${fg_green}\\]\\u@\\h:\\[${fg_blue}\\]\\w\\[${fg_white}\\]\\\\$\\[${reset}\\] "\n ;;\n\n datetime)\n # A style that shows the date and time:\n # \\D{%Y-%m-%d} - the year/month/date (in white)\n # \\@ - the time (in green)\n # \\$ - prompt (# if root, otherwise $) (bold/white)\n PS1="\\[${fg_white}\\]\\D{%Y-%m-%d} \\[${bold}${fg_green}\\]\\@\\[${fg_white}\\] \\\\$\\[${reset}\\] "\n ;;\n\n # Add your own themes here!\n\n *)\n # Restore PS1 to its original value.\n PS1="${_original_ps1}"\n ;; \n esac\n\n # If we are in Z-Shell convert the PS1 to use Z-Shell format.\n [ -n "$ZSH_VERSION" ] && PS1=$(_to_zsh "$PS1")\n}\n')),(0,r.kt)("p",null,"In this code we check the first parameter of the function ",(0,r.kt)("inlineCode",{parentName:"p"},"$1"),". If it matches the string ",(0,r.kt)("inlineCode",{parentName:"p"},"debian")," we set the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable to a format that is similar to what is used by Debian Linux distributions. If it matches the string ",(0,r.kt)("inlineCode",{parentName:"p"},"datetime")," we set ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," to a prompt that shows the current date and time. If any other value is used, we reset the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable back to its original value."),(0,r.kt)("p",null,"Before we complete the function, we check to see if ",(0,r.kt)("inlineCode",{parentName:"p"},"ZSH_VERSION")," is set - this is to check whether we are in a ",(0,r.kt)("inlineCode",{parentName:"p"},"zsh")," shell. If we are, then we use the ",(0,r.kt)("inlineCode",{parentName:"p"},"_to_zsh")," function to convert the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," string into the format used by Z-Shell."),(0,r.kt)("p",null,"Finally, we use the ",(0,r.kt)("inlineCode",{parentName:"p"},"}")," to complete the definition of the function."),(0,r.kt)("admonition",{title:"Z-Shell",type:"tip"},(0,r.kt)("p",{parentName:"admonition"},"The ",(0,r.kt)("inlineCode",{parentName:"p"},"zsh")," shell differs considerably from Bash and Bash-like shells in how it handles the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable. There is no need for the ",(0,r.kt)("inlineCode",{parentName:"p"},"\\[")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"\\]")," sequences, there are built in color variables such as ",(0,r.kt)("inlineCode",{parentName:"p"},"$fg[red]")," for 'red' and the special sequences are different (for example, rather than ",(0,r.kt)("inlineCode",{parentName:"p"},"\\u")," for username, Z-Shell uses ",(0,r.kt)("inlineCode",{parentName:"p"},"%n"),")."),(0,r.kt)("p",{parentName:"admonition"},"The ",(0,r.kt)("inlineCode",{parentName:"p"},"set_ps1")," function in the samples converts the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," string to Z-Shell format if it is running in Z-Shell. However, this conversion is not perfect as some of the sequences shown in this chapter do not have an equivalent in Z-Shell. If you want to customise a Z-Shell prompt you can check the manual page ",(0,r.kt)("inlineCode",{parentName:"p"},"man zshmisc")," and search for ",(0,r.kt)("inlineCode",{parentName:"p"},"PROMPT\\ SEQUENCES"),".")),(0,r.kt)("p",null,"Notice how much easier it is to specify the values for the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," string when we have the colours and formatting defined in variables! We still need to wrap the formatting characters with ",(0,r.kt)("inlineCode",{parentName:"p"},"\\[")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"\\]")," to make sure that the shell knows how long the command prompt is, but this is ",(0,r.kt)("em",{parentName:"p"},"far")," easier to read than the samples we saw before where we provide the ANSI Escape Sequences."),(0,r.kt)("p",null,"To use this script, we can simply ",(0,r.kt)("inlineCode",{parentName:"p"},"source")," it into our current session and then change the prompt by calling the ",(0,r.kt)("inlineCode",{parentName:"p"},"set_ps1")," function:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ \ndwmkerr@effective-shell-ubuntu-20:~$ source ~/effective-shell/scripts/set_ps1.sh\ndwmkerr@effective-shell-ubuntu-20:~$ set_ps1 datetime\n2021-06-06 04:10 PM $ set_ps1 debian\ndwmkerr@effective-shell-ubuntu-20:~$ \n")),(0,r.kt)("p",null,"This script has a placeholder in the ",(0,r.kt)("inlineCode",{parentName:"p"},"case")," statement for you to add your own 'themes' that you want to be able to use in your shell."),(0,r.kt)("p",null,"For example, one 'theme' I often use is below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'git)\n # A style that shows some git information.\n\n # Build a string that shows:\n # - The branch (underlined if \'main\') in green\n # - A red exclamation if there are any local changes not committed\n # - An indicator of the number of stashed items, if any.\n _git_info() {\n # Git details.\n local git_branch_name="$(git branch --show-current)"\n local git_any_local_changes="$(git status --porcelain=v1 2>/dev/null)"\n local git_stash_count="$(git rev-list --walk-reflogs --count \\\n refs/stash -- 2>/dev/null)" # Ignore error when no stashes\n local git_info=""\n if [ "${git_branch_name}" = "main" ]; then\n git_info="${bold}${fg_green}${start_underline}${git_branch_name}${reset}"\n else\n git_info="${bold}${fg_green}${git_branch_name}${reset}"\n fi\n if ! [ -z "${git_any_local_changes}" ]; then\n # Note that we have to be careful to put the exclamation mark\n # in single quotes so that it is not expanded to the last command!\n git_info="${git_info} ${bold}${fg_red}"\'!\'"${reset}"\n fi\n if [ "${git_stash_count:-0}" -gt 0 ]; then\n git_info="${git_info} ${bold}${fg_yellow}${git_stash_count} in stash${reset}"\n fi\n printf "${git_info}"\n }\n\n # Now show a Debian style prompt with the git info above it.\n PS1="\\$(_git_info)\\n\\\\[${bold}${fg_green}\\]\\u@\\h:\\[${fg_blue}\\]\\w\\[${fg_white}\\]\\\\$\\[${reset}\\] "\n;;\n')),(0,r.kt)("p",null,"Don't worry if you are not familiar with 'git', we will see it in a couple of chapters. The important thing is that this snippet shows that you can add almost any kind of information that you might find useful to your command prompt. When I run ",(0,r.kt)("inlineCode",{parentName:"p"},"set_ps1 git"),", my prompt looks like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"feat/chapter-26-customise-your-command-prompt ! 3 in stash\ndwmkerr@effective-shell-ubuntu-20:~/repos/github/dwmkerr/effective-shell$\n")),(0,r.kt)("p",null,"My prompt is now spread across two lines - the first shows me the branch I am on, a red exclamation point if I have made changes but not saved them, and the number of items I have in my 'stash'. The second line shows the standard Debian prompt."),(0,r.kt)("p",null,"The code shown above has been slightly simplified to make it more readable, you can see the exact version in the samples."),(0,r.kt)("p",null,"You can use the ",(0,r.kt)("em",{parentName:"p"},"~/effective-shell/scripts/set_ps1.sh")," file to build your own 'themes' and easily change between them in the shell."),(0,r.kt)("p",null,"If you want to always ",(0,r.kt)("inlineCode",{parentName:"p"},"source")," this file into your shell on startup, just add the following like to ",(0,r.kt)("em",{parentName:"p"},"~/.bashrc"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'source "~/effective-shell/scripts/set_ps1.sh"\n')),(0,r.kt)("p",null,"You could also set the default ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable immediately after sourcing the script if you like:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'# Source the set_ps1 function and set our \'theme\' to Debian.\nsource "~/effective-shell/scripts/set_ps1.sh"\nset_ps1 "debian"\n')),(0,r.kt)("p",null,"In the next chapter we will look at some sensible ways we can organise files like the ",(0,r.kt)("em",{parentName:"p"},"set_ps1.sh")," script and the ",(0,r.kt)("em",{parentName:"p"},"~/.bashrc")," file so that we can easily manage our customisations and share them across different machines."),(0,r.kt)("h2",{id:"additional-prompt-configuration"},"Additional Prompt Configuration"),(0,r.kt)("p",null,"There are some other variables that you might want to use to configure your prompt:"),(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"},"PS2")),(0,r.kt)("td",{parentName:"tr",align:null},"This is shown when performing 'continuation' and is normally set to ",(0,r.kt)("inlineCode",{parentName:"td"},">"),".")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"PS3")),(0,r.kt)("td",{parentName:"tr",align:null},"This is shown when the ",(0,r.kt)("inlineCode",{parentName:"td"},"select")," command is used and is normally not set, so the default ",(0,r.kt)("inlineCode",{parentName:"td"},"#?")," is used.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"PS4")),(0,r.kt)("td",{parentName:"tr",align:null},"This is shown when tracing with ",(0,r.kt)("inlineCode",{parentName:"td"},"set -x")," and is normally set to ",(0,r.kt)("inlineCode",{parentName:"td"},"+"),".")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"PROMPT_DIRTRIM")),(0,r.kt)("td",{parentName:"tr",align:null},"This can be set to limit the number of directories shown with using ",(0,r.kt)("inlineCode",{parentName:"td"},"\\w")," or ",(0,r.kt)("inlineCode",{parentName:"td"},"\\W")," in your prompt.")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"PROMPT_COMMAND")),(0,r.kt)("td",{parentName:"tr",align:null},"This can be set to limit the number of directories shown with using ",(0,r.kt)("inlineCode",{parentName:"td"},"\\w")," or ",(0,r.kt)("inlineCode",{parentName:"td"},"\\W")," in your prompt.")))),(0,r.kt)("p",null,"Let's take a look at how each one can be used."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"PS2")),(0,r.kt)("p",null,"If we have a long line of text in the shell, we can start a 'continuation'"," by entering the backlash symbol:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ echo "This is a really really \\\n> long \\\n> long line of text"\nThis is a really really long long line of text\n')),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},">")," symbol is shown when we press 'enter' after entering a ",(0,r.kt)("inlineCode",{parentName:"p"},"\\")," backslash symbol. This symbol is used to remind us that we are not entering a new command, we are just continuing the current command on a new line. You can change the text shown by setting ",(0,r.kt)("inlineCode",{parentName:"p"},"PS2"),"."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"PS3")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"PS3")," allows you to specify the prompt used by the ",(0,r.kt)("inlineCode",{parentName:"p"},"select")," command:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'$ PS3="Your choice? : "\n$ select fruit in Apples Pears; do echo "$fruit"; done\n1) Apples\n2) Pears\nYour choice? :\n')),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"PS3")," variable is not set by default. If it is not set, then the ",(0,r.kt)("inlineCode",{parentName:"p"},"select")," statement uses ",(0,r.kt)("inlineCode",{parentName:"p"},"#?")," for the prompt."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"PS4")),(0,r.kt)("p",null,"When you enable 'tracing' by setting the ",(0,r.kt)("inlineCode",{parentName:"p"},"-x")," option, each traced line starts with a ",(0,r.kt)("inlineCode",{parentName:"p"},"+")," symbol:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"$ set -x\n$ echo \"The date is $(date)\"\n++ date\n+ echo 'The date is Sun 06 Jun 2021 08:49:07 AM UTC'\nThe date is Sun 06 Jun 2021 08:49:07 AM UTC\n")),(0,r.kt)("p",null,"You can change this symbol by setting the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS4")," option."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"PROMPT_DIRTRIM")),(0,r.kt)("p",null,"If you set a value in the ",(0,r.kt)("inlineCode",{parentName:"p"},"PROMPT_DIRTRIM")," variable, the shell will not show the entire contents of the working directory when you use the special ",(0,r.kt)("inlineCode",{parentName:"p"},"\\w")," sequence in a prompt variable. Instead, it will limit the number of directories shown to the value in ",(0,r.kt)("inlineCode",{parentName:"p"},"PROPMT_DIRTRIM")," and use an 'ellipses' for the rest (and ellipses is written as three dots)."),(0,r.kt)("p",null,"For example, if I was in the folder ",(0,r.kt)("em",{parentName:"p"},"~/effective-shell/logs/apm-logs")," and had ",(0,r.kt)("inlineCode",{parentName:"p"},"PROMPT_DIRTRIM")," set to ",(0,r.kt)("inlineCode",{parentName:"p"},"2"),", then on Debian my command prompt would look like this:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"dwmkerr@effective-shell-ubuntu-20:~/.../logs/apm-logs$\n")),(0,r.kt)("p",null,"Note that only the last two parts of the path to the folder are shown."),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"PROMPT_COMMAND")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"PROMPT_COMMAND")," variable can be used to specify a command or set of commands to run before the prompt is shown."),(0,r.kt)("p",null,"A common use for the ",(0,r.kt)("inlineCode",{parentName:"p"},"PROMPT_COMMAND")," is to save and reload the shell command history before each command is run:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},'PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"\n')),(0,r.kt)("p",null,"In this example, we used the ",(0,r.kt)("inlineCode",{parentName:"p"},"history")," (",(0,r.kt)("em",{parentName:"p"},"display or manipulate history list"),") command three times. First with ",(0,r.kt)("inlineCode",{parentName:"p"},"-a")," to append the lines from the current session to the history file, then ",(0,r.kt)("inlineCode",{parentName:"p"},"-c")," to clear the shell history in the session, then ",(0,r.kt)("inlineCode",{parentName:"p"},"-r")," to reload it."),(0,r.kt)("p",null,"For many shells the history of commands is only updated when the shell is closed, this change means that even if the shell is terminated unexpectedly, each command we have executed will still have been written to the history."),(0,r.kt)("h2",{id:"z-shell-and-oh-my-zsh"},"Z-Shell and Oh-My-Zsh"),(0,r.kt)("p",null,"Z-Shell does not use the same sequences to format the prompt-string variables. However, the ",(0,r.kt)("em",{parentName:"p"},"set_ps1.sh")," script included in the Effective Shell samples will convert the Bash-style ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable into Z-Shell formatted prompt strings automatically."),(0,r.kt)("p",null,'For Z-Shell users, you might also consider the very popular "Oh-My-Zsh" project. This is a collection of themes and plugins that add many more aliases, functions, autocompletions and more to the shell. One of the most popular features of "Oh-My-Zsh" is its large collection of themes that customise how the prompt looks.'),(0,r.kt)("p",null,'However, just like with most things in computing, I would strongly recommend that you learn how the fundamentals work as they are described in this chapter before using "Oh-My-Zsh" themes. This will help you understand how things like "Oh-My-Zsh" actually work under the hood.'),(0,r.kt)("p",null,"You might also realise that you don't need to install an additional package to get the styling you want. For example, my own shell prompt includes information on Git, the working directory (trimmed to only show up to three entries), but only requires a few lines of setup and works consistently in Bash-like shells ",(0,r.kt)("em",{parentName:"p"},"and")," Z-Shell."),(0,r.kt)("p",null,"Enjoy playing around with the prompt customisation! It can be a lot of fun and the options are almost limitless!"),(0,r.kt)("h2",{id:"summary"},"Summary"),(0,r.kt)("p",null,"In this chapter we looked at how you can customise the command prompt with the ",(0,r.kt)("inlineCode",{parentName:"p"},"PS1")," variable, the shell's special sequences for useful information like ",(0,r.kt)("inlineCode",{parentName:"p"},"\\u")," for the current user, and how to configure the visual formatting of the prompt. We also looked at a script that makes configuring the command prompt a little easier to manage."),(0,r.kt)("p",null,"We've now seen quite a few ways to configure the shell, in the next chapter we'll look at some sensible practices that you can use to organise your shell configuration files."),(0,r.kt)("p",null,"To find all of the information on how to control the command prompt in the manual, run ",(0,r.kt)("inlineCode",{parentName:"p"},"man bash")," and search for ",(0,r.kt)("inlineCode",{parentName:"p"},"^PROMPTING"),". For Z-Shell, run ",(0,r.kt)("inlineCode",{parentName:"p"},"man zshmisc")," and search for ",(0,r.kt)("inlineCode",{parentName:"p"},"PROMPT\\ SEQUENCES"),"."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/103ed7fb.2bed8b08.js b/pr-preview/pr-346/assets/js/103ed7fb.2bed8b08.js new file mode 100644 index 00000000..c0ccecd2 --- /dev/null +++ b/pr-preview/pr-346/assets/js/103ed7fb.2bed8b08.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[9130],{2452:(t,e,a)=>{a.d(e,{Z:()=>r});var n=a(7294);const o={color:"#333333",fontStyle:"italic"},r=t=>{let{annotation:e,children:a}=t;return n.createElement("div",null,n.createElement("span",null,n.createElement("code",null,a)),n.createElement("br",null),n.createElement("br",null),n.createElement("small",{style:o},e))}},1789:(t,e,a)=>{a.d(e,{Z:()=>l});var n=a(7294),o=a(1262),r=a(412);const l=t=>{let{src:e,style:l,...i}=t;return n.createElement(o.default,null,(()=>{if(!r.Z.canUseDOM)return n.createElement("div",null,"ASCII Cinema Player Unavailable");const t=a(4828),o=(0,n.useRef)(null);return(0,n.useEffect)((()=>{const a=o.current;t.create(e,a,i)}),[e]),n.createElement("div",{ref:o,style:l})}))}},5626:(t,e,a)=>{a.d(e,{Z:()=>m});var n,o=a(7294);!function(t){t.default="block",t.block="block",t.line="line"}(n||(n={}));const r={color:"#FFFFFF",background:"#333333"},l={boxShadow:"inset 1px 0px #000000",background:"#FFFFFF11"},i=t=>{switch(t){case n.block:return r;case n.line:return l;default:return r}},m=t=>{let{caretStyle:e=n.block,children:a}=t;return o.createElement("span",{style:i(e)},a)}},6922:(t,e,a)=>{a.d(e,{Z:()=>d});var n=a(7294),o=a(8985),r=a(2452),l=a(1789),i=a(5626);const m=t=>n.createElement(n.Fragment,null,n.createElement(o.Z,t));m.AnnotatedCommand=r.Z,m.AsciinemaPlayer=l.Z,m.Caret=i.Z;const d=m},5896:(t,e,a)=>{a.r(e),a.d(e,{assets:()=>d,contentTitle:()=>i,default:()=>c,frontMatter:()=>l,metadata:()=>m,toc:()=>s});var n=a(7462),o=(a(7294),a(3905)),r=a(1413);const l={title:"A Vim Crash Course",slug:"/part-6-advanced-techniques/a-vim-crash-course/",chapterNumber:32},i=void 0,m={unversionedId:"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.",source:"@site/docs/06-advanced-techniques/32-a-vim-crash-course/index.mdx",sourceDirName:"06-advanced-techniques/32-a-vim-crash-course",slug:"/part-6-advanced-techniques/a-vim-crash-course/",permalink:"/part-6-advanced-techniques/a-vim-crash-course/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/06-advanced-techniques/32-a-vim-crash-course/index.mdx",tags:[],version:"current",frontMatter:{title:"A Vim Crash Course",slug:"/part-6-advanced-techniques/a-vim-crash-course/",chapterNumber:32},sidebar:"sidebar",previous:{title:"The Secure Shell",permalink:"/part-6-advanced-techniques/the-secure-shell/"},next:{title:"Master the Multiplexer",permalink:"/part-6-advanced-techniques/master-the-multiplexer/"}},d={},s=[{value:"What is a Terminal Editor",id:"what-is-a-terminal-editor",level:2},{value:"Why use a Terminal Editor?",id:"why-use-a-terminal-editor",level:2},{value:"Introducing Vim",id:"introducing-vim",level:2},{value:"What is Modal Editing?",id:"what-is-modal-editing",level:3},{value:"Modal Editing in Action",id:"modal-editing-in-action",level:3},{value:"Building a Vim Cheat Sheet",id:"building-a-vim-cheat-sheet",level:2},{value:"Creating a File in Vim",id:"creating-a-file-in-vim",level:2},{value:"Vim Motions and Movement Commands",id:"vim-motions-and-movement-commands",level:2},{value:"Vim Command Counts",id:"vim-command-counts",level:2},{value:"Vim Text Insert Commands",id:"vim-text-insert-commands",level:2},{value:"Vim Operators",id:"vim-operators",level:2},{value:"Search Motions and the 'In' and 'A' Operators",id:"search-motions-and-the-in-and-a-operators",level:2},{value:"The 'In' and 'A' Modifiers",id:"the-in-and-a-modifiers",level:2},{value:"Editing Commands in Vim",id:"editing-commands-in-vim",level:2},{value:"Next Steps with Vim",id:"next-steps-with-vim",level:2},{value:"Vimtutor",id:"vimtutor",level:3},{value:"Vimcasts",id:"vimcasts",level:3},{value:"Practical Vim & Modern Vim",id:"practical-vim--modern-vim",level:3},{value:"The Vim Shortcuts Wallpaper",id:"the-vim-shortcuts-wallpaper",level:3},{value:"Summary",id:"summary",level:2}],p=t=>function(e){return console.warn("Component "+t+" was not imported, exported, or provided by MDXProvider as global scope"),(0,o.kt)("div",e)},k=p("AnnotatedCommand"),h=p("Caret"),u={toc:s};function c(t){let{components:e,...l}=t;return(0,o.kt)("wrapper",(0,n.Z)({},u,l,{components:e,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"In this chapter we're going to introduce the popular Terminal Editor ",(0,o.kt)("em",{parentName:"p"},"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."),(0,o.kt)("p",null,"In this chapter we'll see why we might want to use or at least become familiar with Vim, and show just a few features that might excite you enough to want to dive in and learn more!"),(0,o.kt)("h2",{id:"what-is-a-terminal-editor"},"What is a Terminal Editor"),(0,o.kt)("p",null,"A Terminal Editor is any text editor that is designed to run inside a terminal, often from a shell. This means it is designed to be used without a mouse, on a smaller screen, and on a system that might have no graphical user interface or windowing or desktop environment."),(0,o.kt)("p",null,"Some of the most popular terminal editors you might have heard of are ",(0,o.kt)("em",{parentName:"p"},"GNU nano"),", ",(0,o.kt)("em",{parentName:"p"},"Vim")," and ",(0,o.kt)("em",{parentName:"p"},"Emacs"),". Let's take a look at why we might want to know how to use a terminal editor."),(0,o.kt)("h2",{id:"why-use-a-terminal-editor"},"Why use a Terminal Editor?"),(0,o.kt)("p",null,"Many technologists will be familiar with Graphical Text Editors, such as ",(0,o.kt)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"Visual Studio Code"),", ",(0,o.kt)("a",{parentName:"p",href:"https://atom.io/"},"Atom")," and ",(0,o.kt)("a",{parentName:"p",href:"https://www.sublimetext.com/"},"Sublime"),". Software engineers will likely be familiar with Integrated Development Environments such as ",(0,o.kt)("a",{parentName:"p",href:"https://visualstudio.microsoft.com/"},"Visual Studio")," or the JetBrains family of IDEs which include dedicated environments for many languages, such as ",(0,o.kt)("a",{parentName:"p",href:"https://www.jetbrains.com/idea/"},"IntelliJ IDEA")," for Java."),(0,o.kt)("p",null,"So, with such a selection of powerful and user-friendly text editing options available, why would you choose to use a Terminal Editor?"),(0,o.kt)("p",null,"There are many reasons, but perhaps the most compelling one is that you will not always have a graphical environment available! If you are working on a remote server using the ",(0,o.kt)("inlineCode",{parentName:"p"},"ssh")," tool as described in ",(0,o.kt)("a",{parentName:"p",href:"/part-6-advanced-techniques/the-secure-shell/"},"Chapter 31 - The Secure Shell"),", you will not have a desktop or windowed environment to run a graphical tool. The ",(0,o.kt)("em",{parentName:"p"},"only")," options available for you to edit text, code or scripts in such an environment are terminal editors - because the terminal is your only interface to the server."),(0,o.kt)("p",null,"There are other times where you might not have easy access to a graphical environment - for example if you are editing a file in a Docker container."),(0,o.kt)("p",null,"There are some other reasons that being able to use a terminal editor is powerful:"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Maintaining flow")," - if you are working in a shell already and need to edit text, being able to quickly do so without leaving the shell, and ideally without even having to touch the mouse, will allow you to maintain your 'flow' while you work."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Speed of editing")," - editors such as Vim and Emacs allow you to be ",(0,o.kt)("em",{parentName:"p"},"incredibly")," efficient at editing text, once you get over the initial learning curve. Vim in particular is designed to try and keep your fingers on the home row of the keyboard as much as possible to make it possible to manipulate text very quickly."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Customisation")," - terminal editors can be customised to suit your working style - you can then manage your customisations through dotfiles that you share across environments as described in ",(0,o.kt)("a",{parentName:"p",href:"/part-5-building-your-toolkit/managing-your-dotfiles"},"Chapter 26 - Managing Your Dotfiles"),"."),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"Improving Efficiency in Graphical Editors and other tools")," - if you learn how to use Vim or Emacs and find yourself able to edit text very efficiently using their idioms, then you can install plugins in your graphical editors or IDEs, allowing you to edit text using the same commands. There are even browser plugins that let you navigate webpages using Vim style movement commands."),(0,o.kt)("p",null,"All in all I believe it is extremely useful to get to grips with at least the basics of Vim - you will be more efficient when working with remote machines, when quickly editing text from the shell, and might even find it replaces your other tools for many tasks."),(0,o.kt)("h2",{id:"introducing-vim"},"Introducing Vim"),(0,o.kt)("p",null,"I have chosen Vim as it has the reputation for being the most complex of the terminal text editors to work with, but actually with an hour or two of practice you'll find that it is quite straightforward to use for simple tasks. With just a little knowledge you'll be effective - and as you invest more time in learning you'll become increasingly efficient."),(0,o.kt)("p",null,"Vim is installed on most Linux distributions out of the box and is an extremely popular editor. But it has a style of editing, called ",(0,o.kt)("em",{parentName:"p"},"Modal Editing")," that can be a bit confusing at first."),(0,o.kt)("h3",{id:"what-is-modal-editing"},"What is Modal Editing?"),(0,o.kt)("p",null,"Vim is a ",(0,o.kt)("em",{parentName:"p"},"modal text editor"),". This means that it runs in different 'modes' - and in each mode the keyboard has different functions. For example, when you are in ",(0,o.kt)("em",{parentName:"p"},"Command Mode")," the keyboard is used to enter sequences that manipulate text or move the cursor. In ",(0,o.kt)("em",{parentName:"p"},"Insert Mode")," your keyboard is used just like with a normal text editor - typing edits the text on the screen."),(0,o.kt)("h3",{id:"modal-editing-in-action"},"Modal Editing in Action"),(0,o.kt)("p",null,"First, we'll open Vim by running the ",(0,o.kt)("inlineCode",{parentName:"p"},"vi")," command:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ vi\n\n\u2588\n~\n~\n~\n~\n~ VIM - Vi IMproved\n~\n~ version 8.2.4314\n~ by Bram Moolenaar et al.\n~ Modified by \n~ Vim is open source and freely distributable\n~\n~ Help poor children in Uganda!\n~ type :help iccf for information\n~\n~ type :q to exit\n~ type :help or for on-line help\n~ type :help version8 for version info\n~\n~\n~\n~\n~\n 0,0-1 All \n")),(0,o.kt)("p",null,"When Vim is started it either displays the file that it has been asked to open or if no file has been provided it shows the welcome message above."),(0,o.kt)("p",null,"Vim starts in ",(0,o.kt)("strong",{parentName:"p"},"Command Mode"),". This means that the keys on the keyboard are not used to enter text - they are used to enter commands. One command that is going to be useful is the 'insert' command, which tells vim to enter ",(0,o.kt)("strong",{parentName:"p"},"Insert Mode"),", before the cursor. The position of the cursor in the code block above is shown with a full-block ",(0,o.kt)("inlineCode",{parentName:"p"},"\u2588")," symbol."),(0,o.kt)("p",null,"Let's insert some text. Enter the characters in the first column of the table below - the output should match what is shown on the right:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter insert mode",mdxType:"AnnotatedCommand"},"i")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text",mdxType:"AnnotatedCommand"},"Hello Vim!")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"Hello Vim!",(0,o.kt)(h,{mdxType:"Caret"}," ")))))),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"i")," command tells Vim to enter ",(0,o.kt)("strong",{parentName:"p"},"Insert Mode"),". Insert mode is the mode that will be most familiar to you - when Vim is in insert mode your keystrokes enter text in the current file. When Vim is in insert mode it behaves more like a non-modal editor. We can enter some text as shown above."),(0,o.kt)("p",null,"You might have noticed that when you entered insert mode Vim updated its status bar to show you what mode it is in:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"Hello Vim!\u2588\n~\n~\n~\n-- INSERT -- 1,11 All\n")),(0,o.kt)("p",null,"Vim will show you what mode you are in on the status line at the bottom of the screen. Vim's default mode is 'Command Mode' - when Vim is in command mode the mode is not shown - it's what you assume Vim is in unless it tells you otherwise."),(0,o.kt)("p",null,"To return to Command Mode, press Control and 'C', or 'Escape'. In Vim's documentation a 'chord', which is more than one key pressed at the same time, is shown in angled brackets, to differentiate it from a 'sequence' which is a set of keystrokes pressed one after the other. Control and C is written as ",(0,o.kt)("inlineCode",{parentName:"p"},"")," in the documentation for Vim. We will use the same style to describe chords and sequences."),(0,o.kt)("p",null,"Let's exit insert mode:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Command mode",mdxType:"AnnotatedCommand"},"<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"Hello Vim",(0,o.kt)(h,{mdxType:"Caret"},"!")))))),(0,o.kt)("p",null,"You will notice that the ",(0,o.kt)("inlineCode",{parentName:"p"},"-- INSERT --")," text in the status bar is no longer shown - meaning we are back in Command Mode."),(0,o.kt)("p",null,"Let's exit Vim. When we are in Command Mode, the keystrokes when enter are used to manipulate text or move around. If we want to perform an administrative task, such as saving a file or calling another program, we use what is called an ",(0,o.kt)("em",{parentName:"p"},"Ex Command"),". ",(0,o.kt)("em",{parentName:"p"},"Ex")," is short for 'execute'. When we press the colon ",(0,o.kt)("inlineCode",{parentName:"p"},":")," keystroke, we enter 'Ex Mode'. This mode is a bit like Insert Mode, but for writing commands:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Start ex command",mdxType:"AnnotatedCommand"},":")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},":",(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Quit without saving",mdxType:"AnnotatedCommand"},"q!")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},":q!",(0,o.kt)(h,{mdxType:"Caret"}," ")))))),(0,o.kt)("p",null,"Keystrokes we enter after entering Ex mode will show in the status line, showing the text for the command we are building. At this point the status bar for Vim will look like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"Hello Vim!\n~ \n~ \n~ \n:q!\u2588\n")),(0,o.kt)("p",null,"Vim is showing us that our current command is ",(0,o.kt)("inlineCode",{parentName:"p"},"q!"),". This command translates to 'quit without saving'. Press the Enter key to execute the command - Vim will now close."),(0,o.kt)("p",null,"We have seen the basics of modal editing - showing Vim's Command Mode, Insert Mode and Ex Mode. We can also close Vim - which is famously a task that people who haven't used Vim before struggle with! Now let's start building our own Vim Cheatsheet - using Vim - to show more of what it can do!"),(0,o.kt)("h2",{id:"building-a-vim-cheat-sheet"},"Building a Vim Cheat Sheet"),(0,o.kt)("p",null,"We're going to build a Vim Cheatsheet - this will be a great way to show what Vim can do at the same time as documenting our learnings as we go along. You'll then be able to extend your personal Cheatsheet over time with the commands that you find useful."),(0,o.kt)("p",null,"Let's start by creating a folder to keep the cheat sheet in and initialing an empty Git Repository in the folder so that we can track changes to the cheat sheet. If you need a reminder on how Git works, check ",(0,o.kt)("a",{parentName:"p",href:"/part-5-building-your-toolkit/controlling-changes-with-git"},"Chapter 27 - Controlling Changes with Git"),"."),(0,o.kt)("p",null,"First we'll create a new folder, move into it and then initialise an empty Git repository with the main branched named ",(0,o.kt)("inlineCode",{parentName:"p"},"main"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ mkdir ~/vim-cheatsheet`\n$ cd ~/vim-cheatsheet\n$ git init -b main\nInitialized empty Git repository in /home/dwmkerr/vim-cheatsheet/.git/\n")),(0,o.kt)("h2",{id:"creating-a-file-in-vim"},"Creating a File in Vim"),(0,o.kt)("p",null,"We now have a new repository ready to hold our cheatsheet. Let's open Vim and tell it that we want to work on a file named ",(0,o.kt)("inlineCode",{parentName:"p"},"cheatsheet.md"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'$ vi cheatsheet.md\n\n\u2588\n~ \n~ \n"cheatsheet.md" [New] 0,0-1 All\n')),(0,o.kt)("p",null,"The text we are editing in Vim is called a ",(0,o.kt)("em",{parentName:"p"},"buffer"),". A buffer can be thought of as a view on a file - when we open a file we load the context into a buffer - it is this buffer that Vim shows. When we want to create a new file, we enter text in a new buffer and then save it when we are ready."),(0,o.kt)("p",null,"Vim is showing us in the status line that we have a buffer named ",(0,o.kt)("em",{parentName:"p"},"cheatsheet.md")," and that this is a 'New' buffer - it has not been saved."),(0,o.kt)("p",null,"The extension ",(0,o.kt)("inlineCode",{parentName:"p"},"md")," at the end of the file is short for 'Markdown'. Markdown is a plain text format that is great for documentation. You write text as normal - you can also use some special characters such as Hash ",(0,o.kt)("inlineCode",{parentName:"p"},"#")," to format things like headings, or dash ",(0,o.kt)("inlineCode",{parentName:"p"},"-")," for bullets. Markdown will be rendered in a very reader-friendly way when we look at on things like GitHub."),(0,o.kt)("p",null,"Let's enter insert mode, and create a title for our cheatsheet, then exit insert mode."),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter insert mode",mdxType:"AnnotatedCommand"},"i")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text",mdxType:"AnnotatedCommand"},"# Vim Cheatsheet")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter command mode",mdxType:"AnnotatedCommand"},"<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatshee",(0,o.kt)(h,{mdxType:"Caret"},"t")))))),(0,o.kt)("p",null,"Now let's use the ",(0,o.kt)("inlineCode",{parentName:"p"},"w")," (",(0,o.kt)("em",{parentName:"p"},"write"),") ex-mode command to save the file. Enter ",(0,o.kt)("inlineCode",{parentName:"p"},":w")," - the Vim status line will to tell us it has written the buffer to disk:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'# Vim Cheatsheet\n~\n~\n~\n"cheatsheet.md" [New] 1L, 17B written 1,16 All\n')),(0,o.kt)("p",null,"Exit Vim by entering ",(0,o.kt)("inlineCode",{parentName:"p"},":q"),". Let's add this new cheatsheet file to our repository:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ git add cheatsheet.md\n")),(0,o.kt)("p",null,"Let's tell our shell to use Vim as our text editor. We're going to use Vim to work with Git for the duration of this chapter. When you are more familiar with Vim, being able to work with Git repositories and commands without leaving the shell and using Vim as the editor will start to feel extremely efficient:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ export EDITOR=vi\n$ git commit\n\n\u2588\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# Committer: dwmkerr\n#\n# On branch main\n#\n# Initial commit\n#\n# Changes to be committed:\n# new file: cheatsheet.md\n#\n~\n~\n\"~/vim-cheatsheet/.git/COMMIT_EDITMSG\" 13L, 325B 1,0-1 All\n")),(0,o.kt)("p",null,"Git has opened Vim to ask us to provide a commit message. Git has also provided some helpful information on the changes we are committing - it's told us that we have a new file called ",(0,o.kt)("em",{parentName:"p"},"cheatsheet.md")," in the commit. Let's use Vim to enter a commit message:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter insert mode",mdxType:"AnnotatedCommand"},"i")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text",mdxType:"AnnotatedCommand"},"add the cheatsheet")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"add the cheatsheet",(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter command mode",mdxType:"AnnotatedCommand"},"<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"add the cheatshee",(0,o.kt)(h,{mdxType:"Caret"},"t")))))),(0,o.kt)("p",null,"Now type ",(0,o.kt)("inlineCode",{parentName:"p"},":wq")," to ",(0,o.kt)("em",{parentName:"p"},"write")," and ",(0,o.kt)("em",{parentName:"p"},"quit")," - Git will use the message we have provided for the commit:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"}," 1 file changed, 1 insertion(+)\n create mode 100644 cheatsheet.md\n")),(0,o.kt)("p",null,"We've used Vim to create the initial cheatsheet file as well as to quickly set the Git commit message - all without leaving our shell!"),(0,o.kt)("h2",{id:"vim-motions-and-movement-commands"},"Vim Motions and Movement Commands"),(0,o.kt)("p",null,"Vim commands that move the cursor are called 'Motions'. Understanding how motions work is essential to becoming effective at moving around Vim buffers."),(0,o.kt)("p",null,"Let's open our cheatsheet in Vim:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ vi cheatsheet.md\n")),(0,o.kt)("p",null,"Now update the file so that it looks like the version below, this will give us some text to play with:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-sh"},"# Vim Cheatsheet\n\n## Vim Motions\n\nVim motions are commands used to move the cursor.\n")),(0,o.kt)("p",null,"Now that we have this text, let's see some of the motion commands in action."),(0,o.kt)("p",null,"The table below shows what I think are the most essential Vim motions:"),(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},"Motion"),(0,o.kt)("th",{parentName:"tr",align:null},"Usage"),(0,o.kt)("th",{parentName:"tr",align:null},"Motion"),(0,o.kt)("th",{parentName:"tr",align:null},"Usage"))),(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"},"gg")),(0,o.kt)("td",{parentName:"tr",align:null},"Go to beginning of buffer."),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"h")),(0,o.kt)("td",{parentName:"tr",align:null},"Go left.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"G")),(0,o.kt)("td",{parentName:"tr",align:null},"Go to end of buffer."),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"j")),(0,o.kt)("td",{parentName:"tr",align:null},"Go down.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"0")),(0,o.kt)("td",{parentName:"tr",align:null},"Go to beginning of line."),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"k")),(0,o.kt)("td",{parentName:"tr",align:null},"Go up.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"$")),(0,o.kt)("td",{parentName:"tr",align:null},"Go to end of line."),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"l")),(0,o.kt)("td",{parentName:"tr",align:null},"Go right.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"w")),(0,o.kt)("td",{parentName:"tr",align:null},"Go forwards one word."),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},")")),(0,o.kt)("td",{parentName:"tr",align:null},"Go forwards one sentence.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"b")),(0,o.kt)("td",{parentName:"tr",align:null},"Go backwards one word."),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"(")),(0,o.kt)("td",{parentName:"tr",align:null},"Go backwards one sentence.")))),(0,o.kt)("p",null,"Let's see a few of these motions in action. Make sure to use ",(0,o.kt)("inlineCode",{parentName:"p"},"")," to exit Insert Mode and enter Command Mode before entering the keystrokes 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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to beginning of buffer",mdxType:"AnnotatedCommand"},"gg")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions",(0,o.kt)("br",null),(0,o.kt)("br",null),"## Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go right",mdxType:"AnnotatedCommand"},"l")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"#",(0,o.kt)(h,{mdxType:"Caret"},"#")," Vim Motions",(0,o.kt)("br",null),(0,o.kt)("br",null),"## Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go left",mdxType:"AnnotatedCommand"},"h")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions",(0,o.kt)("br",null),(0,o.kt)("br",null),"## Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go down",mdxType:"AnnotatedCommand"},"j")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"## Vim Motions",(0,o.kt)("br",null),(0,o.kt)(h,{mdxType:"Caret"}," "),(0,o.kt)("br",null),"## Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go up",mdxType:"AnnotatedCommand"},"k")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions",(0,o.kt)("br",null),(0,o.kt)("br",null),"## Vim Motions"))))),(0,o.kt)("p",null,"The first motion is ",(0,o.kt)("inlineCode",{parentName:"p"},"gg")," - go to the beginning of the buffer."),(0,o.kt)("p",null,"The next four motions are ",(0,o.kt)("inlineCode",{parentName:"p"},"hjkl")," - move left, down, up and right. Vim uses these keys as they are all next to each other and on the home row for the right hand. Although it takes a little getting used to, once you use ",(0,o.kt)("inlineCode",{parentName:"p"},"hjkl")," instead of arrow keys to move around you'll wonder how you lived without them - being able to navigate without moving your right hand from the home row lets you navigate text incredibly quickly."),(0,o.kt)("p",null,"We can use the ",(0,o.kt)("inlineCode",{parentName:"p"},"0")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"$")," motions to go to the beginning and end of a line:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to beginning of line",mdxType:"AnnotatedCommand"},"0")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to end of line",mdxType:"AnnotatedCommand"},"$")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"## Vim Motion",(0,o.kt)(h,{mdxType:"Caret"},"s")))))),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"w")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"b")," motions move backwards and forwards by one word:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to beginning of line",mdxType:"AnnotatedCommand"},"0")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go forward one word",mdxType:"AnnotatedCommand"},"w")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"## ",(0,o.kt)(h,{mdxType:"Caret"},"V"),"im Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to back one word",mdxType:"AnnotatedCommand"},"b")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions"))))),(0,o.kt)("h2",{id:"vim-command-counts"},"Vim Command Counts"),(0,o.kt)("p",null,"Many Vim commands can be provided with a 'count', which indicates how many times the command should be run. This makes motions far more flexible - instead of pressing ",(0,o.kt)("inlineCode",{parentName:"p"},"jjjjj")," to move down five lines, we can just press ",(0,o.kt)("inlineCode",{parentName:"p"},"5j"),"."),(0,o.kt)("p",null,"Let's see how the cursor moves when we add counts to motion commands:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to beginning of buffer",mdxType:"AnnotatedCommand"},"gg")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions",(0,o.kt)("br",null),(0,o.kt)("br",null),"## Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go down four lines",mdxType:"AnnotatedCommand"},"4j")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"## Vim Motions",(0,o.kt)("br",null),(0,o.kt)("br",null),(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go forwards three words",mdxType:"AnnotatedCommand"},"3w")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"## Vim Motions",(0,o.kt)("br",null),(0,o.kt)("br",null),"## Vim ",(0,o.kt)(h,{mdxType:"Caret"},"M"),"otions"))))),(0,o.kt)("h2",{id:"vim-text-insert-commands"},"Vim Text Insert Commands"),(0,o.kt)("p",null,"Now that we know how to move the cursor with the movement commands, we can move to where we want to insert text and use the ",(0,o.kt)("inlineCode",{parentName:"p"},"i")," command to enter Insert Mode. However, Vim has a set of commands that enter Insert Mode in specific positions - and these can save us moving around unnecessarily!"),(0,o.kt)("p",null,"The most essential 'enter insert mode' commands are:"),(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},"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"},"i")),(0,o.kt)("td",{parentName:"tr",align:null},"Insert at cursor.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"I")),(0,o.kt)("td",{parentName:"tr",align:null},"Insert beginning of current line.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"a")),(0,o.kt)("td",{parentName:"tr",align:null},"Append text after the cursor.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"A")),(0,o.kt)("td",{parentName:"tr",align:null},"Append text at the end of the current line.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"o")),(0,o.kt)("td",{parentName:"tr",align:null},"Open a new line below the position.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"O")),(0,o.kt)("td",{parentName:"tr",align:null},"Open a new line above the current line.")))),(0,o.kt)("p",null,"These commands make it much faster to quickly enter Insert Mode just where you like. Let's see some of them in action:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to beginning of buffer",mdxType:"AnnotatedCommand"},"gg")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"#"),"# Vim Motions"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Open line below current line",mdxType:"AnnotatedCommand"},"o")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)("br",null),(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text, enter command mode",mdxType:"AnnotatedCommand"},"Hello","<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)("br",null),"Hello",(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Insert at beginning of line",mdxType:"AnnotatedCommand"},"I")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)("br",null),(0,o.kt)(h,{mdxType:"Caret"},"H"),"ello"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text, enter command mode",mdxType:"AnnotatedCommand"},"Welcome and ","<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)("br",null),"Welcome and ",(0,o.kt)(h,{mdxType:"Caret"},"H"),"ello"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Append at end of line",mdxType:"AnnotatedCommand"},"A")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)("br",null),"Welcome and Hell",(0,o.kt)(h,{mdxType:"Caret"},"o")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text, enter command mode",mdxType:"AnnotatedCommand"},"Vim!","<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)("br",null),"Welcome and Hello Vim",(0,o.kt)(h,{mdxType:"Caret"},"!")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Open line above current line, enter command mode",mdxType:"AnnotatedCommand"},"O","<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"# Vim Cheatsheet",(0,o.kt)("br",null),(0,o.kt)(h,{mdxType:"Caret"}," "),(0,o.kt)("br",null),"Welcome and Hello Vim"))))),(0,o.kt)("p",null,"We use the ",(0,o.kt)("inlineCode",{parentName:"p"},"")," chord to quickly go back into Command Mode when we have inserted text. This should start to become a habit as you use Vim - it is faster to enter small amounts of text, then go back to command mode and reposition the cursor, than to enter lots of text and use the arrow keys to move around in Insert Mode",(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,"Now that we have seen motions and text insert commands, let's look at some essential Vim operators."),(0,o.kt)("h2",{id:"vim-operators"},"Vim Operators"),(0,o.kt)("p",null,"A Vim operator is any command that can be applied to a range of text. We can combine ",(0,o.kt)("em",{parentName:"p"},"counts"),", ",(0,o.kt)("em",{parentName:"p"},"motions")," and ",(0,o.kt)("em",{parentName:"p"},"operators")," to rapidly manipulate a specific part of the buffer."),(0,o.kt)("p",null,"Some of the operators that are particularly useful are listed in the table below - we'll see them in action afterwards:"),(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},"Operator"),(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"},"c")),(0,o.kt)("td",{parentName:"tr",align:null},"Change the range, i.e. delete the characters and move into insert mode at the beginning of the range.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"d")),(0,o.kt)("td",{parentName:"tr",align:null},"Delete the range.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"y")),(0,o.kt)("td",{parentName:"tr",align:null},"Yank the range - copy it to a register ready to paste later.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"g~")),(0,o.kt)("td",{parentName:"tr",align:null},"Swap case.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"gu")),(0,o.kt)("td",{parentName:"tr",align:null},"Make lowercase ('go lower').")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"gU")),(0,o.kt)("td",{parentName:"tr",align:null},"Make uppercase ('go upper').")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"!")),(0,o.kt)("td",{parentName:"tr",align:null},"Send through external program.")))),(0,o.kt)("p",null,"We can use operators with a motion to alter a range of text. Let's see some of the basic operators:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to the beginning of the buffer",mdxType:"AnnotatedCommand"},"gg")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("pre",null,(0,o.kt)(h,{mdxType:"Caret"},"W"),"elcome to Vim!"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Move forwards one word",mdxType:"AnnotatedCommand"},"w")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("pre",null,"Welcome ",(0,o.kt)(h,{mdxType:"Caret"},"t"),"o Vim!"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Change two words",mdxType:"AnnotatedCommand"},"c2w")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("pre",null,"Welcome to ",(0,o.kt)(h,{mdxType:"Caret"}," ")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter new text, go to Command Mode",mdxType:"AnnotatedCommand"},"to Terminal Editing!","<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("pre",null,"Welcome to Terminal Editing",(0,o.kt)(h,{mdxType:"Caret"},"!")))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go uppercase to the beginning of the line",mdxType:"AnnotatedCommand"},"gU0")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("pre",null,(0,o.kt)(h,{mdxType:"Caret"},"W"),"ELCOME TO TERMINAL EDITING!"))))),(0,o.kt)("p",null,"Hopefully from these examples you are starting to get an idea of just how powerful modal editing is - you are able to express complex changes to text, ranges of text, and movements in the buffer with just a few keystrokes."),(0,o.kt)("p",null,"These operators can also quickly be applied to the current line - just type them twice!"),(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},"Operator"),(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"},"cc")),(0,o.kt)("td",{parentName:"tr",align:null},"Change current line, i.e. delete the line and move into insert mode at the beginning of the line.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"dd")),(0,o.kt)("td",{parentName:"tr",align:null},"Delete the current line.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"yy")),(0,o.kt)("td",{parentName:"tr",align:null},"Yank the current line - copy it to a register ready to paste later.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"g~~")),(0,o.kt)("td",{parentName:"tr",align:null},"Swap case for the current line.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"guu")),(0,o.kt)("td",{parentName:"tr",align:null},"Make lowercase ('go lower') for the current line.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"gUU")),(0,o.kt)("td",{parentName:"tr",align:null},"Make uppercase ('go upper') for the current line.")))),(0,o.kt)("h2",{id:"search-motions-and-the-in-and-a-operators"},"Search Motions and the 'In' and 'A' Operators"),(0,o.kt)("p",null,"Some of the most powerful motions are the ones that can be used to search for a specific character. These 'search' motions can be used to quickly select a range of characters for an operator to perform on. Let's see a few of the most essential search motions:"),(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},"Motion"),(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"},"f{character}")),(0,o.kt)("td",{parentName:"tr",align:null},"Find the next specified character.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"F{character}")),(0,o.kt)("td",{parentName:"tr",align:null},"Find the previous specified character.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"t{character}")),(0,o.kt)("td",{parentName:"tr",align:null},"'To' the next specified character.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},"T{character}")),(0,o.kt)("td",{parentName:"tr",align:null},"'To' the previous specified character.")))),(0,o.kt)("p",null,"Now let's see how these motions look in action, by quickly moving around a little bit of Python code:"),(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},"keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"go to next '(' character",mdxType:"AnnotatedCommand"},"f(")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word",(0,o.kt)(h,{mdxType:"Caret"},"("),"word):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"go to second previous 'd' character",mdxType:"AnnotatedCommand"},"2fd")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"d"),"ef search_for_word(word):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"go to first '_' character",mdxType:"AnnotatedCommand"},"t_")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def searc",(0,o.kt)(h,{mdxType:"Caret"},"h"),"_for_word(word):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"go to previous space character",mdxType:"AnnotatedCommand"},"t","\xa0")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def",(0,o.kt)(h,{mdxType:"Caret"}," "),"search_for_word(word):"))))),(0,o.kt)("p",null,"Just like any motion, we can add a ",(0,o.kt)("inlineCode",{parentName:"p"},"{count}")," to indicate how many times we want the motion to run."),(0,o.kt)("p",null,"Two other powerful motions are 'in' and 'a' - these motions are special because they come after the operator. Let's change some text using these motions:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Find first '(' character",mdxType:"AnnotatedCommand"},"f(")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word",(0,o.kt)(h,{mdxType:"Caret"},"("),"word):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go uppercase in brackets",mdxType:"AnnotatedCommand"},"gUi)")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word(",(0,o.kt)(h,{mdxType:"Caret"},"W"),"ORD):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Make entire line lowercase",mdxType:"AnnotatedCommand"},"guu")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"d"),"ef search_for_word(word):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Find first bracket, delete a 'brackets'",mdxType:"AnnotatedCommand"},"f(da(")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word",(0,o.kt)(h,{mdxType:"Caret"},":")))))),(0,o.kt)("p",null,"When you have searched for a character, you can use the following keystrokes to find the next or previous result:"),(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},"Motion"),(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"},";")),(0,o.kt)("td",{parentName:"tr",align:null},"Move to the next result for the search motion.")),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)("inlineCode",{parentName:"td"},",")),(0,o.kt)("td",{parentName:"tr",align:null},"Move to the previous result for the search motion.")))),(0,o.kt)("h2",{id:"the-in-and-a-modifiers"},"The 'In' and 'A' Modifiers"),(0,o.kt)("p",null,"The 'in' and 'a' modifiers for an operator are particularly useful when you want to change the contents of something like brackets, parenthesis, braces and so on. If you use ",(0,o.kt)("inlineCode",{parentName:"p"},"ca)")," for example, you are saying 'Change a Brackets' - the brackets are removed and you are put into insert mode at the position the brackets were in. In comparison, ",(0,o.kt)("inlineCode",{parentName:"p"},"ci{")," changes 'in' braces, removing the text inside the braces, but keeping the braces themselves."),(0,o.kt)("p",null,"If you use an open bracket or brace, a space is added before and after the text you insert, if you use a close bracket or brace, no spaces are used:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Go to first open bracket",mdxType:"AnnotatedCommand"},"f(")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word",(0,o.kt)(h,{mdxType:"Caret"},"("),"word):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Change in brackets",mdxType:"AnnotatedCommand"},"ci)")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word",(0,o.kt)(h,{mdxType:"Caret"},"("),"):"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text, return to Command Mode",mdxType:"AnnotatedCommand"},'word = "default"',"<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},'def search_for_word(word = "default"',(0,o.kt)(h,{mdxType:"Caret"},")"),":"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Change a quotes",mdxType:"AnnotatedCommand"},'ca"')),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word(word = ",(0,o.kt)(h,{mdxType:"Caret"},")"),":"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter text, return to Command Mode",mdxType:"AnnotatedCommand"},"'sample'","<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{language:"python",mdxType:"CodeBlock"},"def search_for_word(word = 'sample",(0,o.kt)(h,{mdxType:"Caret"},"'"),"):"))))),(0,o.kt)("h2",{id:"editing-commands-in-vim"},"Editing Commands in Vim"),(0,o.kt)("p",null,"We're going to update our cheatsheet with a few notes - at this stage you can add any notes that you think are the most relevant, here are the first few lines of what I have added:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-markdown"},"# Vim Cheatsheet\n\n## Vim Motions\n\nVim motions are commands used to move the cursor. Essential motions are:\n\n* `hjkl` - move the cursor left/down/up/right\n* `w` - forward one word\n* `b` - back one word\n* `(` - back one sentence\n* `)` - forwards one sentence\n* `0` - beginning of line\n* `$` - end of line\n* `gg` - beginning of buffer\n* `G` - end of buffer\n\nMotions can take a `{count}` - this indicates how many motions we want to make, such as `3w` to move forwards three words.\n")),(0,o.kt)("p",null,"Before we continue we should note that there are three new Markdown syntax features we have used:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"##")," characters, which indicate a sub-heading"),(0,o.kt)("li",{parentName:"ol"},"The ",(0,o.kt)("inlineCode",{parentName:"li"},"*")," character, which indicates an item in a bullet list (a dash ",(0,o.kt)("inlineCode",{parentName:"li"},"-")," can also be used for this)"),(0,o.kt)("li",{parentName:"ol"},"The backtick ",(0,o.kt)("inlineCode",{parentName:"li"},"`")," character, which can surround text to indicate that it is code and should be rendered as such")),(0,o.kt)("p",null,"These simple formatting features will make the cheatsheet look excellent when it is viewed on GitHub or in a suitable editor - here's how it looks on GitHub at the moment:"),(0,o.kt)("img",{alt:"Screenshot: Basic Markdown Rendered by GitHub",src:a(1196).Z,width:"800px"}),(0,o.kt)("p",null,"Let's look at another way we can use Vim in our day to day work. We'll enter a shell command that has a mistake, and quickly edit the command and fix the mistake in Vim. Let's add these changes to Git - and show one more way to use Vim. Enter the line of text below in the shell but don't press 'Enter' to execute it yet!"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ git commit -m 'added more detail on motions'\n")),(0,o.kt)("p",null,"This command will commit the changes - but we haven't added any yet! Rather than fixing this by moving around the command line, let's just open the current shell command line in Vim. Press ",(0,o.kt)("inlineCode",{parentName:"p"},"^x^e")," (Control X, Control E) - this is the shell command to open the current line in the editor:"),(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"g"),"it commit -m 'added more detail on motions'",(0,o.kt)("br",null),"~",(0,o.kt)("br",null),"~",(0,o.kt)("br",null),"~",(0,o.kt)("br",null),'"/tmp/bash-fc-5094983766" 1L, 45B 1,1 All',(0,o.kt)("br",null)),(0,o.kt)("p",null,"Let's fix up the command with our new Vim skills:"),(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},"Keystrokes"),(0,o.kt)("th",{parentName:"tr",align:null},"Output"))),(0,o.kt)("tbody",{parentName:"table"},(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Enter insert mode",mdxType:"AnnotatedCommand"},"i")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},(0,o.kt)(h,{mdxType:"Caret"},"g"),"it commit -m 'added more detail on motions'"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Add text, enter command mode",mdxType:"AnnotatedCommand"},"git add . && ","<","C-c",">")),(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(r.Z,{mdxType:"CodeBlock"},"git add . &&",(0,o.kt)(h,{mdxType:"Caret"}," "),"git commit -m 'added more detail on motions'"))),(0,o.kt)("tr",{parentName:"tbody"},(0,o.kt)("td",{parentName:"tr",align:null},(0,o.kt)(k,{annotation:"Write and quit",mdxType:"AnnotatedCommand"},":wq")),(0,o.kt)("td",{parentName:"tr",align:null},"(Vim closes and ",(0,o.kt)("inlineCode",{parentName:"td"},"git commit")," completes)")))),(0,o.kt)("p",null,"Being able to rapidly edit the current shell command in Vim is a huge time saver."),(0,o.kt)("h2",{id:"next-steps-with-vim"},"Next Steps with Vim"),(0,o.kt)("p",null,"Vim is a huge topic - we've barely scratched the surface but I hope have focused on the features that you can use most immediately. As you use Vim more, I would recommend learning about the following features in order:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"The dot command"),(0,o.kt)("li",{parentName:"ol"},"Visual mode"),(0,o.kt)("li",{parentName:"ol"},"Yank and paste and registers"),(0,o.kt)("li",{parentName:"ol"},"Search and replace"),(0,o.kt)("li",{parentName:"ol"},"Customising Vim with the ",(0,o.kt)("em",{parentName:"li"},"~/.vimrc")," file"),(0,o.kt)("li",{parentName:"ol"},"Macros")),(0,o.kt)("p",null,"There are a few resources that I would recommend. The first is ",(0,o.kt)("inlineCode",{parentName:"p"},"vimtutor")," and the next are some excellent online resources and books."),(0,o.kt)("h3",{id:"vimtutor"},"Vimtutor"),(0,o.kt)("p",null,"Vim comes installed with a very useful ",(0,o.kt)("inlineCode",{parentName:"p"},"vimtutor")," program. You can run this program to open Vim with a special file that describes the key functionality of Vim."),(0,o.kt)("p",null,"Use this program regularly while you are learning Vim to see how familiar you are getting and refresh your skills!"),(0,o.kt)("h3",{id:"vimcasts"},"Vimcasts"),(0,o.kt)("p",null,"The excellent website ",(0,o.kt)("a",{parentName:"p",href:"http://vimcasts.org/"},"Vimcasts")," by Drew Neil has some superb videos on how to use Vim - from the basics to some really advanced functionality."),(0,o.kt)("p",null,"Drew Neil is an extremely skilled Vim user and does a superb job of making a complex subject easy to follow."),(0,o.kt)("h3",{id:"practical-vim--modern-vim"},"Practical Vim & Modern Vim"),(0,o.kt)("p",null,'If you are enjoying using Vim, Drew Neil\'s books "Practical Vim" and "Modern" Vim should be on your bookshelf! They are truly excellent and again, just like his Vimcasts, make a complex subject much more user friendly.'),(0,o.kt)("h3",{id:"the-vim-shortcuts-wallpaper"},"The Vim Shortcuts Wallpaper"),(0,o.kt)("p",null,'Something I found useful when learning Vim was to have a wallpaper with the most common shortcuts. There are a number available on the web if you search for "Vim Wallpaper" - the one I currently use is below:'),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Vim Cheatsheet Wallpaper",src:a(706).Z,width:"2560",height:"1180"})),(0,o.kt)("p",null,"Unfortunately I have not been able to find the name of the original author. I found this file at:"),(0,o.kt)("p",null,(0,o.kt)("a",{parentName:"p",href:"https://wallha.com/wallpaper/vim-cheat-sheet-minimalism-linux-1250034"},"https://wallha.com/wallpaper/vim-cheat-sheet-minimalism-linux-1250034")),(0,o.kt)("h2",{id:"summary"},"Summary"),(0,o.kt)("p",null,"In this chapter we introduced the concept of the terminal editor. We looked at the essentials of Vim, Insert Mode, Command Mode, motions, operators and more. We also introduced some resources that will help with your learning journey."),(0,o.kt)("p",null,"In the next chapter we'll look at another essential tool - the Terminal Multiplexer."),(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"},"There is also another benefit to short commands - it makes them easier to repeat with the ",(0,o.kt)("em",{parentName:"li"},"Dot Command")," which is not covered in this chapter but something that you will learn about as you use Vim more.",(0,o.kt)("a",{parentName:"li",href:"#fnref-1",className:"footnote-backref"},"\u21a9")))))}c.isMDXComponent=!0},1196:(t,e,a)=>{a.d(e,{Z:()=>n});const n=a.p+"assets/images/markdown-basic-b0fbc2ff6ef902c8fc1f794bed0fb12a.png"},706:(t,e,a)=>{a.d(e,{Z:()=>n});const n=a.p+"assets/images/vim-cheatsheet-30d6ba320441d1b700ea51eed7736835.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/14e3d0c5.e6c1b0dc.js b/pr-preview/pr-346/assets/js/14e3d0c5.e6c1b0dc.js new file mode 100644 index 00000000..4edaa02a --- /dev/null +++ b/pr-preview/pr-346/assets/js/14e3d0c5.e6c1b0dc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[3351],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>d});var n=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(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||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},u="mdxType",f={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,i=e.mdxType,l=e.originalType,s=e.parentName,p=a(e,["components","mdxType","originalType","parentName"]),u=c(r),m=i,d=u["".concat(s,".").concat(m)]||u[m]||f[m]||l;return r?n.createElement(d,o(o({ref:t},p),{},{components:r})):n.createElement(d,o({ref:t},p))}));function d(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var l=r.length,o=new Array(l);o[0]=m;var a={};for(var s in t)hasOwnProperty.call(t,s)&&(a[s]=t[s]);a.originalType=e,a[u]="string"==typeof e?e:i,o[1]=a;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>l,metadata:()=>a,toc:()=>c});var n=r(7462),i=(r(7294),r(3905));const l={title:"Part 4 - Shell Scripting",slug:"/part-4-shell-scripting/"},o=void 0,a={unversionedId:"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.",source:"@site/docs/04-shell-scripting/00-index.md",sourceDirName:"04-shell-scripting",slug:"/part-4-shell-scripting/",permalink:"/part-4-shell-scripting/",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/04-shell-scripting/00-index.md",tags:[],version:"current",sidebarPosition:0,frontMatter:{title:"Part 4 - Shell Scripting",slug:"/part-4-shell-scripting/"},sidebar:"sidebar",previous:{title:"Build Commands on the Fly",permalink:"/part-3-manipulating-text/build-commands-on-the-fly/"},next:{title:"Shell Script Essentials",permalink:"/part-3-manipulating-text/shell-script-essentials/"}},s={},c=[],p={toc:c};function u(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"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."),(0,i.kt)("p",null,"Shell scripting is the process of writing re-usable scripts, which you can use to automate repetitive work, create new programs and manage your environment."),(0,i.kt)("p",null,"In this section we'll look at the fundamentals of how shell scripts work and how to write and structure shell scripts. We'll also look at some more advanced techniques to deal with more complex scenarios, tricks for debugging shell scripts and more."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/1599.f225e795.js b/pr-preview/pr-346/assets/js/1599.f225e795.js new file mode 100644 index 00000000..d75cff11 --- /dev/null +++ b/pr-preview/pr-346/assets/js/1599.f225e795.js @@ -0,0 +1,2 @@ +/*! For license information please see 1599.f225e795.js.LICENSE.txt */ +(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[1599],{3905:(A,g,I)=>{"use strict";I.d(g,{Zo:()=>n,kt:()=>a});var B=I(7294);function Q(A,g,I){return g in A?Object.defineProperty(A,g,{value:I,enumerable:!0,configurable:!0,writable:!0}):A[g]=I,A}function C(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 E(A){for(var g=1;g=0||(Q[I]=A[I]);return Q}(A,g);if(Object.getOwnPropertySymbols){var C=Object.getOwnPropertySymbols(A);for(B=0;B=0||Object.prototype.propertyIsEnumerable.call(A,I)&&(Q[I]=A[I])}return Q}var e=B.createContext({}),i=function(A){var g=B.useContext(e),I=g;return A&&(I="function"==typeof A?A(g):E(E({},g),A)),I},n=function(A){var g=i(A.components);return B.createElement(e.Provider,{value:g},A.children)},o="mdxType",r={inlineCode:"code",wrapper:function(A){var g=A.children;return B.createElement(B.Fragment,{},g)}},s=B.forwardRef((function(A,g){var I=A.components,Q=A.mdxType,C=A.originalType,e=A.parentName,n=t(A,["components","mdxType","originalType","parentName"]),o=i(I),s=Q,a=o["".concat(e,".").concat(s)]||o[s]||r[s]||C;return I?B.createElement(a,E(E({ref:g},n),{},{components:I})):B.createElement(a,E({ref:g},n))}));function a(A,g){var I=arguments,Q=g&&g.mdxType;if("string"==typeof A||Q){var C=I.length,E=new Array(C);E[0]=s;var t={};for(var e in g)hasOwnProperty.call(g,e)&&(t[e]=g[e]);t.originalType=A,t[o]="string"==typeof A?A:Q,E[1]=t;for(var i=2;i{"use strict";function B(A){return B="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},B(A)}function Q(A){var g=function(A,g){if("object"!==B(A)||null===A)return A;var I=A[Symbol.toPrimitive];if(void 0!==I){var Q=I.call(A,g||"default");if("object"!==B(Q))return Q;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===g?String:Number)(A)}(A,"string");return"symbol"===B(g)?g:String(g)}function C(A,g,I){return(g=Q(g))in A?Object.defineProperty(A,g,{value:I,enumerable:!0,configurable:!0,writable:!0}):A[g]=I,A}function E(A,g,I,B,Q,C,E){try{var t=A[C](E),e=t.value}catch(i){return void I(i)}t.done?g(e):Promise.resolve(e).then(B,Q)}function t(A){return function(){var g=this,I=arguments;return new Promise((function(B,Q){var C=A.apply(g,I);function t(A){E(C,B,Q,t,e,"next",A)}function e(A){E(C,B,Q,t,e,"throw",A)}t(void 0)}))}}function e(A,g){(null==g||g>A.length)&&(g=A.length);for(var I=0,B=new Array(g);Ijg});var n=I(4687),o=I.n(n);function r(A,g){if(!(A instanceof g))throw new TypeError("Cannot call a class as a function")}function s(A,g){for(var I=0;IA===g};let w=x;const D={},l={owned:null,cleanups:null,context:null,owner:null};var h=null;let f=null,y=null,G=null,k=null,N=0;function M(A,g){const I=f,B=h,Q=0===A.length?l:{owned:null,cleanups:null,context:null,owner:g||B};h=Q,f=null;try{return H((()=>A((()=>O(Q)))),!0)}finally{f=I,h=B}}function F(A,g){g=g?Object.assign({},u,g):u;const I={value:A,observers:null,observerSlots:null,pending:D,comparator:g.equals||void 0};return[U.bind(I),A=>("function"==typeof A&&(A=A(I.pending!==D?I.pending:I.value)),b(I,A))]}function d(A,g,I){K(m(A,g,!1,1))}function p(A,g,I){w=j;const B=m(A,g,!1,1);B.user=!0,k?k.push(B):queueMicrotask((()=>K(B)))}function R(A,g,I){I=I?Object.assign({},u,I):u;const B=m(A,g,!0,0);return B.pending=D,B.observers=null,B.observerSlots=null,B.comparator=I.equals||void 0,K(B),U.bind(B)}function J(A){if(y)return A();let g;const I=y=[];try{g=A()}finally{y=null}return H((()=>{for(let A=0;AX(g())))}function U(){if(this.sources&&this.state){const A=G;G=null,1===this.state?K(this):Z(this),G=A}if(f){const A=this.observers?this.observers.length:0;f.sources?(f.sources.push(this),f.sourceSlots.push(A)):(f.sources=[this],f.sourceSlots=[A]),this.observers?(this.observers.push(f),this.observerSlots.push(f.sources.length-1)):(this.observers=[f],this.observerSlots=[f.sources.length-1])}return this.value}function b(A,g,I){if(y)return A.pending===D&&y.push(A),A.pending=g,g;if(A.comparator&&A.comparator(A.value,g))return g;let B=!1;return A.value=g,A.observers&&A.observers.length&&H((()=>{for(let g=0;g1e6)throw G=[],new Error}),!1),g}function K(A){if(!A.fn)return;O(A);const g=h,I=f,B=N;f=h=A,function(A,g,I){let B;try{B=A.fn(g)}catch(Q){W(Q)}(!A.updatedAt||A.updatedAt<=I)&&(A.observers&&A.observers.length?b(A,B):A.value=B,A.updatedAt=I)}(A,A.value,B),f=I,h=g}function m(A,g,I,B=1,Q){const C={fn:A,state:B,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:g,owner:h,context:null,pure:I};return null===h||h!==l&&(h.owned?h.owned.push(C):h.owned=[C]),C}function q(A){if(0===A.state)return;if(2===A.state)return Z(A);if(A.suspense&&L(A.suspense.inFallback))return A.suspense.effects.push(A);const g=[A];for(;(A=A.owner)&&(!A.updatedAt||A.updatedAt=0;I--)if(1===(A=g[I]).state)K(A);else if(2===A.state){const I=G;G=null,Z(A,g[0]),G=I}}function H(A,g){if(G)return A();let I=!1;g||(G=[]),k?I=!0:k=[],N++;try{return A()}catch(B){W(B)}finally{!function(A){G&&(x(G),G=null);if(A)return;k.length?J((()=>{w(k),k=null})):k=null}(I)}}function x(A){for(let g=0;gA(g)))}function _(A){const g="fallback"in A&&{fallback:()=>A.fallback};return R(function(A,g,I={}){let B=[],Q=[],C=[],E=0,t=g.length>1?[]:null;return Y((()=>z(C))),()=>{let e,i,n=A()||[];return L((()=>{let A,g,r,s,a,c,u,w,D,l=n.length;if(0===l)0!==E&&(z(C),C=[],B=[],Q=[],E=0,t&&(t=[])),I.fallback&&(B=[P],Q[0]=M((A=>(C[0]=A,I.fallback()))),E=1);else if(0===E){for(Q=new Array(l),i=0;i=c&&w>=c&&B[u]===n[w];u--,w--)r[w]=Q[u],s[w]=C[u],t&&(a[w]=t[u]);for(A=new Map,g=new Array(w+1),i=w;i>=c;i--)D=n[i],e=A.get(D),g[i]=void 0===e?-1:e,A.set(D,i);for(e=c;e<=u;e++)D=B[e],i=A.get(D),void 0!==i&&-1!==i?(r[i]=Q[e],s[i]=C[e],t&&(a[i]=t[e]),i=g[i],A.set(D,i)):C[e]();for(i=c;iA.each),A.children,g||void 0))}function $(A){const g="fallback"in A&&{fallback:()=>A.fallback};return R(function(A,g,I={}){let B,Q=[],C=[],E=[],t=[],e=0;return Y((()=>z(E))),()=>{const i=A()||[];return L((()=>{if(0===i.length)return 0!==e&&(z(E),E=[],Q=[],C=[],e=0,t=[]),I.fallback&&(Q=[P],C[0]=M((A=>(E[0]=A,I.fallback()))),e=1),C;for(Q[0]===P&&(E[0](),E=[],Q=[],C=[],e=0),B=0;Bi[B])):B>=Q.length&&(C[B]=M(n));for(;BA.each),A.children,g||void 0))}function AA(A){let g=!1;const I=R((()=>A.when),void 0,{equals:(A,I)=>g?A===I:!A==!I});return R((()=>{const B=I();if(B){const I=A.children;return(g="function"==typeof I&&I.length>0)?L((()=>I(B))):I}return A.fallback}))}function gA(A){let g=!1;const I=v((()=>A.children)),B=R((()=>{let A=I();Array.isArray(A)||(A=[A]);for(let g=0;gA[0]===I[0]&&(g?A[1]===I[1]:!A[1]==!I[1])&&A[2]===I[2]});return R((()=>{const[I,Q,C]=B();if(I<0)return A.fallback;const E=C.children;return(g="function"==typeof E&&E.length>0)?L((()=>E(Q))):E}))}function IA(A){return A}const BA="_$DX_DELEGATE";function QA(A,g,I){let B;return M((Q=>{B=Q,g===document?A():iA(g,A(),g.firstChild?null:void 0,I)})),()=>{B(),g.textContent=""}}function CA(A,g,I){const B=document.createElement("template");B.innerHTML=A;let Q=B.content.firstChild;return I&&(Q=Q.firstChild),Q}function EA(A,g=window.document){const I=g[BA]||(g[BA]=new Set);for(let B=0,Q=A.length;BI[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(;EB-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;Q1)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(;IA.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;IA.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;t1))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;e127)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)}("AGFzbQEAAAABmwEXYAJ/fwF/YAJ/fwBgA39/fwF/YAN/f38AYAF/AGAEf39/fwBgAX8Bf2AFf39/f38AYAABf2AEf39/fwF/YAAAYAV/f39/fwF/YAF/AX5gBn9/f39/fwBgA39/fgBgBX9/fX9/AGAFf398f38AYAR/fX9/AGAEf3x/fwBgBn9/f39/fwF/YAd/f39/f39/AX9gAn5/AX9gAXwBfwKSAw0Dd2JnGl9fd2JpbmRnZW5fb2JqZWN0X2Ryb3BfcmVmAAQDd2JnFV9fd2JpbmRnZW5fbnVtYmVyX25ldwAWA3diZxVfX3diaW5kZ2VuX3N0cmluZ19uZXcAAAN3YmcaX193Ymdfc2V0X2YxYTRhYzhmM2E2MDViMTEAAwN3YmcaX193YmdfbmV3Xzk0OWJiYzExNDcxOTVjNGUACAN3YmcaX193YmdfbmV3X2FjMzIxNzlhNjYwZGI0YmIACAN3YmcaX193YmdfbmV3XzBiODNkM2RmNjdlY2IzM2UACAN3YmcUX193YmluZGdlbl9pc19zdHJpbmcABgN3YmcbX193YmdfcHVzaF8yODQ0ODZjYTI3YzZhYThiAAADd2JnGl9fd2JnX25ld18zNDJhMjRjYTY5OGVkZDg3AAADd2JnGl9fd2JnX3NldF9hNDYwOTFiMTIwY2M2M2U5AAIDd2JnF19fd2JpbmRnZW5fZGVidWdfc3RyaW5nAAEDd2JnEF9fd2JpbmRnZW5fdGhyb3cAAQOlAqMCBgEBAQMBBAcCCwkCAwQDAAIDAQEDAQAAFAEHBgADAQIBFQADBAEDAwUBAwQAAwgBBAABAwMEAAQABQUDAAMBAwMEAQMFBQUDAwMBAQQDAwMEAAABBAQEBQQAAwAAAAAAAAEEAAQDBAgDBgQOAQYNBAQBAwUDAQUBAwMEAAEHAwEBBgEBBAQBAQAEAQEDAwMDAAADAAAAAAAAAgECAgIFBQMCBgQEBAEAAgEAAQIDAwEDAQEDAQEDAQEJAAQBBAQGAwQAAQQBAQEABAEGBAYDAwMAAAAABQMDAQEEAQMEBAQEBAQCEwAHEA8LAQUCAAQABAkFAAAAAAAAAQICAAADAQACAwEKAAAAAwIAAAYEAAAAAAAAAAAAAQoKAAACAAwMDAQBBAUBcAFzcwUDAQARBgkBfwFBgIDAAAsH4AELBm1lbW9yeQIAFF9fd2JnX3Z0d3JhcHBlcl9mcmVlAIcBBmNyZWF0ZQDVAQ52dHdyYXBwZXJfZmVlZABREXZ0d3JhcHBlcl9pbnNwZWN0AEsSdnR3cmFwcGVyX2dldF9saW5lAMcBFHZ0d3JhcHBlcl9nZXRfY3Vyc29yAMwBEV9fd2JpbmRnZW5fbWFsbG9jANgBEl9fd2JpbmRnZW5fcmVhbGxvYwDwAR9fX3diaW5kZ2VuX2FkZF90b19zdGFja19wb2ludGVyAJkCD19fd2JpbmRnZW5fZnJlZQCGAgneAQEAQQELcpsCmgKOAj6kAa4C/AEc/QHPAZgCrgKcApoCjgI+rgL4AfUB8wH4AXvzAfEB8wHzAfMB8wHzAfQB8wH2AfMB8wH5AfMB9gGuAuEBrgKjAq4CqAKuAqcCrgKhAq4CgwKuAt4BrgKgAq4CgQKuAoQCrgKfAq4C+gGuAoACrgKdAq4CggKuAuABrgKuAp4CrgKFAq4CrgKuAqIC3wGuAocCOaABrwLuAawCrgKrAu8BPGDQAYwCuQEpoQGUAq4CuQGSAqIBkwKJAo0ClQEjrgKtAhhJpQGWAkWjAQrkrQOjAoohAgt/AX4jAEEQayIIJAACQAJAIABB9QFPBEBBzf97IABNDQIgAEELakF4cSEEQZi5wAAoAgBFDQFBACAEayECAkACQAJ/QQAgBEGAAkkNABpBHyAEQf///wdLDQAaIARBBiAEQQh2ZyIAa3ZBAXEgAEEBdGtBPmoLIgVBAnRBpLvAAGooAgAiAARAIARBAEEZIAVBAXZrIAVBH0YbdCEHA0ACQCAAKAIEQXhxIgYgBEkNACAGIARrIgYgAk8NACAAIQEgBiICDQBBACECDAMLIABBFGooAgAiBiADIAYgB0EddkEEcSAAakEQaigCACIARxsgAyAGGyEDIAdBAXQhByAADQALIAMEQCADIQAMAgsgAQ0CC0EAIQFBmLnAACgCAEEBIAV0QQF0IgBBACAAa3JxIgBFDQNBACAAayAAcWhBAnRBpLvAAGooAgAiAEUNAwsDQCAAIAEgACgCBEF4cSIBIARrIgMgAkkgASAET3EiBRshASADIAIgBRshAiAAKAIQIgMEfyADBSAAQRRqKAIACyIADQALIAFFDQILQaS8wAAoAgAiACAET0EAIAIgACAEa08bDQEgASAEaiEDIAEQOAJAIAJBEE8EQCABIARBA3I2AgQgAyACQQFyNgIEIAIgA2ogAjYCACACQYACTwRAIAMgAhA2DAILIAJBA3YiAkEDdEGcucAAaiEAAn9BlLnAACgCACIFQQEgAnQiAnEEQCAAKAIIDAELQZS5wAAgAiAFcjYCACAACyECIAAgAzYCCCACIAM2AgwgAyAANgIMIAMgAjYCCAwBCyABIAIgBGoiAEEDcjYCBCAAIAFqQQRqIgAgACgCAEEBcjYCAAsgAUEIaiICRQ0BDAILAkACQAJAAn8CQAJAQZS5wAAoAgAiBUEQIABBBGpBCyAASxtBB2pBeHEiBEEDdiIBdiIAQQNxRQRAIARBpLzAACgCAE0NByAADQFBmLnAACgCACIARQ0HQQAgAGsgAHFoQQJ0QaS7wABqKAIAIgEoAgRBeHEgBGshAiABKAIQIgBFBEAgAUEUaigCACEACyAABEADQCAAKAIEQXhxIARrIgUgAkkhAyAFIAIgAxshAiAAIAEgAxshASAAKAIQIgMEfyADBSAAQRRqKAIACyIADQALCyABIARqIQAgARA4IAJBEEkNBSABIARBA3I2AgQgACIFIAJBAXI2AgQgACACaiACNgIAQaS8wAAoAgAiAEUNBCAAQQN2IgNBA3RBnLnAAGohAEGsvMAAKAIAIQZBlLnAACgCACIHQQEgA3QiA3FFDQIgACgCCAwDCwJAIABBf3NBAXEgAWoiAUEDdCIDQaS5wABqKAIAIgBBCGooAgAiAiADQZy5wABqIgNHBEAgAiADNgIMIAMgAjYCCAwBC0GUucAAQX4gAXcgBXE2AgALIAAgAUEDdCIBQQNyNgIEIAAgAWpBBGoiASABKAIAQQFyNgIAIABBCGohAgwHCwJAQQEgAUEfcSIBdEEBdCICQQAgAmtyIAAgAXRxIgBBACAAa3FoIgBBA3QiAkGkucAAaigCACIDQQhqKAIAIgEgAkGcucAAaiICRwRAIAEgAjYCDCACIAE2AggMAQtBlLnAAEGUucAAKAIAQX4gAHdxNgIACyADIARBA3I2AgQgAyAEaiIBIQUgASAAQQN0IARrIgYiAEEBcjYCBCAAIAFqIAA2AgBBpLzAACgCACIABEAgAEEDdiIBQQN0QZy5wABqIQBBrLzAACgCACECAn9BlLnAACgCACIEQQEgAXQiAXEEQCAAKAIIDAELQZS5wAAgASAEcjYCACAACyEBIAAgAjYCCCABIAI2AgwgAiAANgIMIAIgATYCCAtBrLzAACAFNgIAQaS8wAAgBjYCACADQQhqIQIMBgtBlLnAACADIAdyNgIAIAALIQMgACAGNgIIIAMgBjYCDCAGIAA2AgwgBiADNgIIC0GsvMAAIAU2AgBBpLzAACACNgIADAELIAEgAiAEaiIAQQNyNgIEIAAgAWpBBGoiACAAKAIAQQFyNgIACyABQQhqIgINAQsCQAJAAkACQAJAAkACQAJAQaS8wAAoAgAiASAESQRAQai8wAAoAgAiACAESw0CIAggBEGvgARqQYCAfHEQ1AEgCCgCACICDQFBACECDAkLQay8wAAoAgAhACABIARrIgFBEEkEQEGsvMAAQQA2AgBBpLzAACgCACEBQaS8wABBADYCACAAIAFBA3I2AgQgACABakEEaiIBIAEoAgBBAXI2AgAgAEEIaiECDAkLQaS8wAAgATYCAEGsvMAAIAAgBGoiAjYCACACIAFBAXI2AgQgASACaiABNgIAIAAgBEEDcjYCBCAAQQhqIQIMCAsgCCgCCCEGQbS8wAAgCCgCBCIFQbS8wAAoAgBqIgA2AgBBuLzAAEG4vMAAKAIAIgEgACABIABLGzYCAAJAAkBBsLzAACgCAARAQby8wAAhAANAIAAoAgAgACgCBGogAkYNAiAAKAIIIgANAAsMAgtB0LzAACgCACIARQ0DIAIgAEkNAwwHCyAAKAIMQQFxDQAgACgCDEEBdiAGRw0AIAAoAgAiAUGwvMAAKAIAIgNNBH8gACgCBCABaiADSwVBAAsNAwtB0LzAAEHQvMAAKAIAIgAgAiACIABLGzYCACACIAVqIQFBvLzAACEAAkACQANAIAAoAgAgAUcEQCAAKAIIIgANAQwCCwsgACgCDEEBcQ0AIAAoAgxBAXYgBkYNAQtBsLzAACgCACEDQby8wAAhAAJAA0AgACgCACADTQRAIAAoAgAgACgCBGogA0sNAgsgACgCCCIADQALQQAhAAsgACgCACAAKAIEaiILQVFqIgBBCGoiAUEHakF4cSEHIAMgByABayAAaiIAIAAgA0EQakkbIgdBCGohASAHQRhqIQBBsLzAACACQQhqIglBB2pBeHEgCWsiCiACaiIJNgIAQai8wAAgBSAKa0FYaiIKNgIAIAkgCkEBcjYCBCAJIApqQSg2AgRBzLzAAEGAgIABNgIAIAdBGzYCBEG8vMAAKQIAIQwgAUEIakHEvMAAKQIANwIAIAEgDDcCAEHIvMAAIAY2AgBBwLzAACAFNgIAQby8wAAgAjYCAEHEvMAAIAE2AgADQCAAQQc2AgQgCyAAQQRqIgBBBGpLDQALIAMgB0YNByAHIANrIgAgA2oiASABKAIEQX5xNgIEIAMgAEEBcjYCBCAAIANqIAA2AgAgAEGAAk8EQCADIAAQNgwICyAAQQN2IgFBA3RBnLnAAGohAAJ/QZS5wAAoAgAiAkEBIAF0IgFxBEAgACgCCAwBC0GUucAAIAEgAnI2AgAgAAshASAAIAM2AgggASADNgIMIAMgADYCDCADIAE2AggMBwsgACgCACEBIAAgAjYCACAAIAAoAgQgBWo2AgQgAkEIaiIAQQdqQXhxIABrIAJqIgMgBGohAiADIARBA3I2AgQgAUEIaiIAQQdqQXhxIABrIAFqIgAgAyAEamshBEGwvMAAKAIAIABHBEBBrLzAACgCACAARg0EIAAoAgRBA3FBAUcNBQJAIAAoAgRBeHEiAUGAAk8EQCAAEDgMAQsgAEEMaigCACIFIABBCGooAgAiBkcEQCAGIAU2AgwgBSAGNgIIDAELQZS5wABBlLnAACgCAEF+IAFBA3Z3cTYCAAsgASAEaiEEIAAgAWohAAwFC0GwvMAAIAI2AgBBqLzAAEGovMAAKAIAIARqIgA2AgAgAiAAQQFyNgIEIANBCGohAgwHC0GovMAAIAAgBGsiATYCAEGwvMAAQbC8wAAoAgAiACAEaiICNgIAIAIgAUEBcjYCBCAAIARBA3I2AgQgAEEIaiECDAYLQdC8wAAgAjYCAAwDCyAAIAAoAgQgBWo2AgRBsLzAACgCAEGovMAAKAIAIAVqEJQBDAMLQay8wAAgAjYCAEGkvMAAQaS8wAAoAgAgBGoiADYCACACIABBAXI2AgQgACACaiAANgIAIANBCGohAgwDCyAAIAAoAgRBfnE2AgQgAiAEQQFyNgIEIAIgBGogBDYCACAEQYACTwRAIAIgBBA2IANBCGohAgwDCyAEQQN2IgFBA3RBnLnAAGohAAJ/QZS5wAAoAgAiBUEBIAF0IgFxBEAgACgCCAwBC0GUucAAIAEgBXI2AgAgAAshASAAIAI2AgggASACNgIMIAIgADYCDCACIAE2AgggA0EIaiECDAILQdS8wABB/x82AgBByLzAACAGNgIAQcC8wAAgBTYCAEG8vMAAIAI2AgBBqLnAAEGcucAANgIAQbC5wABBpLnAADYCAEGkucAAQZy5wAA2AgBBuLnAAEGsucAANgIAQay5wABBpLnAADYCAEHAucAAQbS5wAA2AgBBtLnAAEGsucAANgIAQci5wABBvLnAADYCAEG8ucAAQbS5wAA2AgBB0LnAAEHEucAANgIAQcS5wABBvLnAADYCAEHYucAAQcy5wAA2AgBBzLnAAEHEucAANgIAQeC5wABB1LnAADYCAEHUucAAQcy5wAA2AgBB6LnAAEHcucAANgIAQdy5wABB1LnAADYCAEHkucAAQdy5wAA2AgBB8LnAAEHkucAANgIAQey5wABB5LnAADYCAEH4ucAAQey5wAA2AgBB9LnAAEHsucAANgIAQYC6wABB9LnAADYCAEH8ucAAQfS5wAA2AgBBiLrAAEH8ucAANgIAQYS6wABB/LnAADYCAEGQusAAQYS6wAA2AgBBjLrAAEGEusAANgIAQZi6wABBjLrAADYCAEGUusAAQYy6wAA2AgBBoLrAAEGUusAANgIAQZy6wABBlLrAADYCAEGousAAQZy6wAA2AgBBsLrAAEGkusAANgIAQaS6wABBnLrAADYCAEG4usAAQay6wAA2AgBBrLrAAEGkusAANgIAQcC6wABBtLrAADYCAEG0usAAQay6wAA2AgBByLrAAEG8usAANgIAQby6wABBtLrAADYCAEHQusAAQcS6wAA2AgBBxLrAAEG8usAANgIAQdi6wABBzLrAADYCAEHMusAAQcS6wAA2AgBB4LrAAEHUusAANgIAQdS6wABBzLrAADYCAEHousAAQdy6wAA2AgBB3LrAAEHUusAANgIAQfC6wABB5LrAADYCAEHkusAAQdy6wAA2AgBB+LrAAEHsusAANgIAQey6wABB5LrAADYCAEGAu8AAQfS6wAA2AgBB9LrAAEHsusAANgIAQYi7wABB/LrAADYCAEH8usAAQfS6wAA2AgBBkLvAAEGEu8AANgIAQYS7wABB/LrAADYCAEGYu8AAQYy7wAA2AgBBjLvAAEGEu8AANgIAQaC7wABBlLvAADYCAEGUu8AAQYy7wAA2AgBBnLvAAEGUu8AANgIAQbC8wAAgAkEIaiIAQQdqQXhxIABrIgEgAmoiADYCAEGovMAAIAUgAWtBWGoiATYCACAAIAFBAXI2AgQgACABakEoNgIEQcy8wABBgICAATYCAAtBACECQai8wAAoAgAiACAETQ0AQai8wAAgACAEayIBNgIAQbC8wABBsLzAACgCACIAIARqIgI2AgAgAiABQQFyNgIEIAAgBEEDcjYCBCAAQQhqIQILIAhBEGokACACC9cKAQd/IwBB0ABrIgIkAAJAAkACQAJAAkACQAJAIABBFGooAgBFBEAgAUFgcUHAAEYNASABQUlqDgICAwQLIAAoAgwhAwJAIAFBMEcEQCABQThGDQEgAygCACEBDAcLIAMoAgAiAUEoRw0GIABBAToAoQEMBwsgAygCACIBQSNHDQUgACgCHCIFRQ0GIAJBEWohBCACQcMAaiIGQQRqIQdBACEDA0AgACgCGCIIBEBBACEBA0AgB0EAOwAAIAZBADYAACAEIAIpAEA3AAAgBEEIaiACQcgAai0AADoAACACQQI6ABAgAkECOgAMIAJBxQA2AgggACABIAMgAkEIahCDASAIIAFBAWoiAUcNAAsLIAAoAowBIgEgA00NBSAAKAKEASADakEBOgAAIAUgA0EBaiIDRw0ACwwGCyAAIAFBQGsQLQwFCyAAQdgAaiAAKAI8NgIAIABB3ABqIAApAJMBNwAAIABB6gBqIAAvAKMBOwEAIABB4gBqIABBmQFqKQAANwAAIAAgACgCGEF/aiIBIAAoAjgiACAAIAFLGzYCVAwECyAAQQA6AKYBIAAgACkCVDcCOCAAIABB3ABqKQAANwCTASAAQZkBaiAAQeIAaikAADcAACAAIABB6gBqLwEAOwCjAQwDCyABQeMARw0CIAJBIGoiASAAKAIYIAAoAhwQTCACQTBqIAEQWCAAQQA6AJABQfCSwAAoAgAhAQJAIAAoAgQiA0UNACADQQF0RQ0AIAAoAgAQEwsgAEIANwIEIAAgATYCACAAQQAQvgEgACgCACAAKAIIQQF0akEAOwEAIAAgACgCCEEBajYCCEHoksAAKAIAIQECQCAAQRBqKAIAIgNFDQAgA0ECdEUNACAAKAIMEBMLIABCADcCECAAIAE2AgwgAkEQaiIDIAJBKGooAgA2AgAgAiACKQMgNwMIIABBIGoiARCxAQJAIABBJGooAgAiBEUNACAEQQxsRQ0AIAEoAgAQEwsgASACKQMINwIAIAFBCGogAygCADYCACAAQSxqIgEQsQECQCAAQTBqKAIAIgNFDQAgA0EMbEUNACABKAIAEBMLIAEgAikDMDcCACAAQQA6AJEBIAFBCGogAkE4aigCADYCACACQQhqIAAoAhgQbiAAQUBrIQECQCAAQcQAaigCACIDRQ0AIANBAnRFDQAgASgCABATCyABIAIpAwg3AgAgAUEIaiACQQhqIgVBCGoiASgCADYCACAAQQE6AJIBIABCADcCOCACQQ9qIgNBADsAACAAQZcBakECOgAAIABBAjoAkwEgAkEANgALIABBmAFqIAIpAAg3AAAgAEGgAWogAS0AADoAACAAQQA7AKUBIABBgICACDYAoQEgAEEANgJMIAAgACgCHCIEQX9qNgJQIANBADsAACACQQA2AAsgAEHhAGogAikACDcAACAAQekAaiABLQAAOgAAIABB6gBqQYACOwEAIABB4ABqQQI6AAAgAEHcAGpBAjoAACAAQgA3AlQgA0EAOwAAIAJBADYACyAAQfkAaiACKQAINwAAIABBgQFqIAEtAAA6AAAgAEGCAWpBgAI7AQAgAEH4AGpBAjoAACAAQfQAakECOgAAIABCADcCbCACIAQQpwEgAUEANgIAIAIgAikDADcDCCAFIAQQhAEgAkHIAGogASgCADYCACACIAIpAwg3A0AgAEGEAWohASAAQYgBaigCAARAIAEoAgAQEwsgASACKQNANwIAIAFBCGogAkHIAGooAgA2AgAMAgsgAyABQdyMwAAQmQEACyABQShHDQAgAEEAOgChAQsgAkHQAGokAAuvDAEIfyMAQSBrIgMkAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIABBFGooAgBFBEAgAUFAag4zHAcbChoZGBcGFhUUExIfHxEfHxAPHx8ODR8MHx8fHx8LCgkfCAcGBQQfHx8DAh8fHx8BHwsgACgCDCECAkACQCABQZR/ag4FASAgIB4ACyABQegARg0eDB8LIAIoAgBBP0cNHiAAKAIAIQIgA0EIaiAAKAIIIgEQkwEgAygCDCEHIAMoAgggAiABQQF0IggQLCECIAEEQCAAQZMBaiEFIABB3ABqIQYgAiEBA0ACQAJAIAEvAQAiBEGWCEwEQAJAAkACQAJAIARBemoOAgECAAsgBEEZRg0CIARBL0YNBAwFCyAAQQA6AKYBIABCADcCOCAAQQA6AKMBDAQLIABBADoApAEMAwsgAEEAOgCSAQwCCwJAAkAgBEHpd2oOAwIBAAMLIAAQXSAAQQA6AKYBIAAgACkCVDcCOCAFIAYpAAA3AAAgBUEGaiAGQQZqKQAANwAAIAAgAC8BajsAowEMAgsgAEEAOgCmASAAIAApAlQ3AjggBSAGKQAANwAAIAAgAC8BajsAowEgBUEGaiAGQQZqKQAANwAADAELIAAQXQsgAUECaiEBIAhBfmoiCA0ACwsgB0UNHiAHQQF0RQ0eIAIQEwweCyAAEG8MHQsgABAaDBwLIAAQYQwbCyAAEGIMGgsgABDqAQwZCyAAEGMMGAsgABCSAQwXCyAAEH0MFgsgABBlDBULIAAQygEMFAsgAEEAOgCmASAAIAAoAgBBmIzAACAAKAIIGy8BACIBQQEgARtBf2oiASAAKAIYIgBBf2ogACABSxs2AjgMEwsgABAxDBILIAAQlgEMEQsgABDZAQwQCyAAIAAoAgBBmIzAACAAKAIIGy8BACIAQQEgABsQiQEMDwsgACAAKAIAQZiMwAAgACgCCBsvAQAiAEEBIAAbEIIBDA4LIAAQTgwNCyAAEEIMDAsgABBEDAsLIAAQfAwKCyAAEHMMCQsgACAAKAIAQZiMwAAgACgCCBsvAQAiAEEBIAAbED8MCAsgAEEAOgCmASAAIAAoAgBBmIzAACAAKAIIGy8BACIBQQEgARtBf2oiASAAKAIYIgBBf2ogACABSxs2AjgMBwsgABCyAQwGCyAAEOsBDAULIAAQywEMBAsgACAAKAIAQZiMwAAgACgCCBsvAQAiAEEBIAAbEJABDAMLIAAQPQwCCyACKAIAQSFHDQEgAEEANgJMIABBAToAkgEgAEEAOwGiASAAIAAoAhxBf2o2AlAgA0EeaiIBQQA7AAAgAEGXAWpBAjoAACAAQQI6AJMBIANBADYAGiAAQZgBaiADKQAXNwAAIABBoAFqIANBH2oiAi0AADoAACABQQA7AAAgA0EANgAaIABB4QBqIAMpABc3AAAgAEHpAGogAi0AADoAACAAQeoAakGAAjsBACAAQeAAakECOgAAIABB3ABqQQI6AAAgAEIANwJUDAELIAIoAgBBP0cNACAAKAIAIQIgAyAAKAIIIgEQkwEgAygCBCEHIAMoAgAgAiABQQF0IggQLCECIAEEQCAAQdwAaiEFIABBkwFqIQYgAiEBA0ACQAJAAkAgAS8BACIEQZYITARAAkACQAJAAkAgBEF6ag4CAQIACyAEQRlGDQIgBEEvRg0EDAYLIABBAToAowEgAEEAOgCmASAAQQA2AjggACAAKAJMNgI8DAULIABBAToApAEMBAsgAEEBOgCSAQwDCwJAIARB6XdqDgMBAgADCyAAIAAoAjw2AlggBSAGKQAANwAAIAAgAC8AowE7AWogBUEGaiAGQQZqKQAANwAAIAAgACgCGEF/aiIEIAAoAjgiCSAJIARLGzYCVAsgABBZDAELIAAgACgCPDYCWCAFIAYpAAA3AAAgACAALwCjATsBaiAFQQZqIAZBBmopAAA3AAAgACAAKAIYQX9qIgQgACgCOCIJIAkgBEsbNgJUCyABQQJqIQEgCEF+aiIIDQALCyAHRQ0AIAdBAXRFDQAgAhATCyADQSBqJAAL5gkBC38jAEHQAGsiAiQAAkACQCABKAIIIgNFBEAgAEIANwIEIABB6JLAACgCADYCAAwBCwJAAkACQEEEQQQQigIiBwRAIAcgASgCACIFKAIANgIAIAIgBUEKaikAADcBNiACIAUpAAQ3AzAgAkESaiACKQE2NwEAIAIgBzYCACACQoGAgIAQNwIEIAIgAikDMDcCDCACQgA3AiQgAkHoksAAKAIANgIgIANBAUYEQCACQTBqIgFBGGogAkEYaigCADYCACABQRBqIAJBEGopAwA3AwAgAUEIaiACQQhqKQMANwMAIAIgAikDADcDMEEAIQEMAwsgAkEMaiEIIANBFGxBbGohCkEAIQdBASEDA0ACQAJAAkAgBSAHaiIBQRhqIgktAAAiBEECRyACLQAMIgZBAkdzDQACQCAEQQJGDQAgBkECRg0AIAQgBkcNASAEQQFHBEAgAUEZai0AACACLQANRg0BDAILIAFBGWotAAAgAi0ADUcNASABQRpqLQAAIAItAA5HDQEgAUEbai0AACACLQAPRw0BCyABQRxqLQAAIgRBAkcgAi0AECIGQQJHcw0AAkAgBEECRg0AIAZBAkYNACAEIAZHDQEgBEEBRwRAIAFBHWotAAAgAi0AEUYNAQwCCyABQR1qLQAAIAItABFHDQEgAUEeai0AACACLQASRw0BIAFBH2otAAAgAi0AE0cNAQsgAUEgai0AAEUgAi0AFEEAR0YNACABQSFqLQAARSACLQAVQQBHRg0AIAFBImotAABFIAItABZBAEdGDQAgAUEjai0AAEUgAi0AF0EAR0YNACABQSRqLQAARSACLQAYQQBHRg0AIAFBJWotAABFIAItABlBAEdzDQELIAJBMGoiA0EYaiIGIAJBGGooAgA2AgAgA0EQaiILIAJBEGopAwA3AwAgA0EIaiIMIAJBCGopAwA3AwAgAiACKQMANwMwIAIoAigiAyACKAIkRgRAIAJBIGogAxC/ASACKAIoIQMLIAIoAiAgA0EcbGoiBCACKQMwNwIAIARBCGogDCkDADcCACAEQRBqIAspAwA3AgAgBEEYaiAGKAIANgIAIAIgA0EBajYCKEEEQQQQigIiA0UNCCADIAFBFGooAgA2AgAgAiAJKQIANwMwIAIgCUEGaikBADcBNiAIIAIpAzA3AgAgCEEGaiACKQE2NwEAIAIgAzYCACACQoGAgIAQNwIEQQEhAwwBCyABQRRqKAIAIQEgAigCBCADRgRAIAIgAxC8ASACKAIIIQMLIAIoAgAgA0ECdGogATYCACACIAIoAghBAWoiAzYCCAsgCiAHQRRqIgdHDQALDAELDAQLIAIoAiQhBSACKAIoIQEgAkEwaiIDQRhqIAJBGGooAgA2AgAgA0EQaiACQRBqKQMANwMAIANBCGogAkEIaikDADcDACACIAIpAwA3AzAgASAFRw0BCyACQSBqIAEQvwEgAigCKCEBCyACKAIgIAFBHGxqIgMgAikDMDcCACADQQhqIAJBMGoiBUEIaikDADcCACADQRBqIAVBEGopAwA3AgAgA0EYaiAFQRhqKAIANgIAIAJBKGogAUEBaiIBNgIAIABBCGogATYCACAAIAIpAyA3AgALIAJB0ABqJAAPC0EEQQRBgLnAACgCACIAQdQAIAAbEQEAAAueCQILfwR+IwBBkAFrIgYkAAJAIAJFDQAgAEUNAANAAkACQAJAIAAgAmpBGE8EQCACIAAgACACSxtBC0kNAyAAIAJJDQEgAkF0bCEKIAJBDGwhBwNAIAEgCmohBUEAIQMgB0EgTwRAQQAhBANAIAQgBWoiAykAACEOIAMpAAghDyADKQAQIRAgA0EYaiIIKQAAIREgCCABIARqIghBGGoiCSkAADcAACADQRBqIAhBEGoiCykAADcAACADQQhqIAhBCGoiDCkAADcAACADIAgpAAA3AAAgCSARNwAAIAsgEDcAACAMIA83AAAgCCAONwAAIARBQGsgBEEgaiIDIQQgB00NAAsLIAcgA0sEQCAGQRBqIgggAyAFaiIJIAcgA2siBBAsGiAJIAEgA2oiASAEECwaIAEgCCAEECwaCyAFIQEgACACayIAIAJPDQALDAILIAZBCGoiB0EAIABrIghBDGwgAWoiA0EIaigCADYCACAGIAMpAgA3AwAgAkEMbCEKIAIiASEEA0AgBEEMbCADaiEFA0AgBkEYaiIJIAVBCGoiCygCADYCACAGIAUpAgA3AxAgBygCACEMIAUgBikDADcCACALIAw2AgAgByAJKAIANgIAIAYgBikDEDcDACAEIABPRQRAIAUgCmohBSACIARqIQQMAQsLIAQgCGoiBARAIAQgASAEIAFJGyEBDAEFIAYpAwAhDiADQQhqIAZBCGoiBygCADYCACADIA43AgAgAUECSQ0GQQEhBANAIARBDGwgA2oiCCkCACEOIAcgCEEIaiIJKAIANgIAIAYgDjcDACACIARqIQUDQCAGQRhqIgsgBUEMbCADaiIKQQhqIgwoAgA2AgAgBiAKKQIANwMQIAcoAgAhDSAKIAYpAwA3AgAgDCANNgIAIAcgCygCADYCACAGIAYpAxA3AwAgBSAASQRAIAIgBWohBQwBCyAFIABrIgUgBEcNAAsgBikDACEOIAkgBygCADYCACAIIA43AgAgASAEQQFqIgRHDQALDAYLAAsACyAAQXRsIQggAEEMbCEHQQAgAGshCgNAQQAhAyAHQSBPBEAgASAIaiEJQQAhBANAIAQgCWoiBSkAACEOIAUpAAghDyAFKQAQIRAgBUEYaiIDKQAAIREgAyABIARqIgNBGGoiCykAADcAACAFQRBqIANBEGoiDCkAADcAACAFQQhqIANBCGoiDSkAADcAACAFIAMpAAA3AAAgCyARNwAAIAwgEDcAACANIA83AAAgAyAONwAAIARBQGsgBEEgaiIDIQQgB00NAAsLIAcgA0sEQCAGQRBqIgUgCkEMbCABaiADaiIJIAcgA2siBBAsGiAJIAEgA2oiAyAEECwaIAMgBSAEECwaCyABIAdqIQEgAiAAayICIABPDQALCyACRQ0CIAANAQwCCwtBACAAa0EMbCABaiIEIAJBDGwiBWohAyAAIAJLBEAgBkEQaiICIAEgBRAsGiADIAQgAEEMbBAZIAQgAiAFECwaDAELIAZBEGoiAiAEIABBDGwiABAsGiAEIAEgBRAZIAMgAiAAECwaCyAGQZABaiQAC9sIAQJ/AkACQAJAAkACQAJAAkACQAJAAkACQEHBACABIAFBnwFLGyICQbB/aiIDQQ9NQQBBASADdEGB/gNxGw0AAkACQAJAAkACQAJAAkACQCACQfB+ag4QCwEBAQEBAQEFAgIMDQQFBQALIAJBaGoOBAEFAQIACyACQZABSw0AIAJBcHFBgAFHDQULIABBADoAkAEMBgsgAEEBOgCQASAAEMgBDwsgAEEMOgCQAQ8LIABBDToAkAEPCyAALQCQAUUNAgwBCyAALQCQAQ0AIAJBGEkNASACQXxxQRxGDQELAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAALQCQAQ4NDAsKBwYFBAMCABUVARULIAJBcHEiA0EgRg0SIANBMEYNGSACQUBqQT9PDRQMGAsgAkEHRw0TDBYLIAJBcHFBIEYNCiACQVBqQQpJDQUCQCACQUZqDgIYBgALAkAgAkF8cUE8Rw0ADBgLIAJBQGpBPksNEgwWCyACQXBxQSBGDQoCQAJAIAJBUGpBCkkNACACQUZqDgIYAAELIABBCDoAkAEMBQsgAkF8cUE8Rg0LIAJBQGpBP08NEQwVCyACQRhJDQ8gAkEZRg0PIAJBfHFBHEYNDyACQUBqQT5LDRAMEwsgAkEYSQ0OIAJBGUYNDiACQXxxQRxGDQ4gAkFwcSIDQTBGDRYgA0EgRg0NIAJBQGpBP08NDwwVCyACQRdNDQ0CQCACQUZqDgIWAgALIAJBGUYNDSACQXxxIgNBHEYNDSACQXBxQSBGDQkgAkFQakEKSQ0BIANBPEYNFSACQUBqQT5LDQ4MFAsgAkEXTQ0MAkACQCACQUZqDgIWAQALIAJBGUYNDSACQXxxIgNBHEYNDSACQXBxQSBGDQogAkFQakEKTw0CCyAAQQQ6AJABCyAAIAEQeQ8LIANBPEYNCCACQUBqQT9PDQsMEQsgAkEYSQ0JIAJBGUYNCSACQXxxQRxGDQkgAkFwcUEgRg0IIAJBUGpBzwBPDQoMEgsgAkEXTQ0IAkACQAJAAkACQCACQbB/ag4QDwEBAQEBAQEDFhYQFgIDAwALIAJBGUYNDAsgAkF8cUEcRg0LIAJBcHFBIEYNAiACQVBqQSBJDRQgAkGvf2pBB0kNFCACQaB/akEfTw0MDBQLIABBDDoAkAEPCyAAQQ06AJABDwsgAEECOgCQAQwHCyACQWBqQeAATw0IIAAgARAiDwsgAEEJOgCQAQwFCyAAQQk6AJABDAQLIABBCDoAkAEMAwsgAEEFOgCQAQwCCyAAQQU6AJABDAELIABBBDoAkAELIAAgARDJAQ8LIAAgARAtCw8LIABBBzoAkAEgABDIAQ8LIABBAzoAkAEgABDIAQ8LIABBADoAkAEPCyAAQQo6AJABDwsgAEELOgCQAQ8LIABBADoAkAEgACABEA8PCyAAQQY6AJABDwsgAEEAOgCQASAAIAEQDgv+BgEFfyAAQXhqIgAoAgRBeHEhASAAIAFqIQICQAJAAkAgACgCBEEBcQ0AIAAoAgAhAwJAIAAtAARBA3EEQCABIANqIQEgACADayIAQay8wAAoAgBHDQEgAigCBEEDcUEDRw0CQaS8wAAgATYCACACIAIoAgRBfnE2AgQgACABQQFyNgIEIAAgAWogATYCAA8LDAILIANBgAJPBEAgABA4DAELIABBDGooAgAiBCAAQQhqKAIAIgVHBEAgBSAENgIMIAQgBTYCCAwBC0GUucAAQZS5wAAoAgBBfiADQQN2d3E2AgALAkAgAi0ABEECcUEBdgRAIAIgAigCBEF+cTYCBCAAIAFBAXI2AgQgACABaiABNgIADAELAkACQAJAQbC8wAAoAgAgAkcEQEGsvMAAKAIAIAJHDQFBrLzAACAANgIAQaS8wABBpLzAACgCACABaiIBNgIAIAAgAUEBcjYCBCAAIAFqIAE2AgAPC0GwvMAAIAA2AgBBqLzAAEGovMAAKAIAIAFqIgE2AgAgACABQQFyNgIEQay8wAAoAgAgAEYNAQwCCyACKAIEQXhxIgMgAWohAQJAIANBgAJPBEAgAhA4DAELIAJBDGooAgAiBCACQQhqKAIAIgJHBEAgAiAENgIMIAQgAjYCCAwBC0GUucAAQZS5wAAoAgBBfiADQQN2d3E2AgALIAAgAUEBcjYCBCAAIAFqIAE2AgBBrLzAACgCACAARw0CQaS8wAAgATYCAAwDC0GkvMAAQQA2AgBBrLzAAEEANgIAC0HMvMAAKAIAIAFPDQFBsLzAACgCAEUNAUEAIQECQEGovMAAKAIAQShNDQBBsLzAACgCACEBQby8wAAhAAJAA0AgACgCACABTQRAIAAoAgAgACgCBGogAUsNAgsgACgCCCIADQALQQAhAAtBACEBIAAoAgxBAXENACAAQQxqKAIAGgtBABA7aw0BQai8wAAoAgBBzLzAACgCAE0NAUHMvMAAQX82AgAPCyABQYACSQ0BIAAgARA2QdS8wABB1LzAACgCAEF/aiIANgIAIAANABA7Gg8LDwsgAUEDdiICQQN0QZy5wABqIQECf0GUucAAKAIAIgNBASACdCICcQRAIAEoAggMAQtBlLnAACACIANyNgIAIAELIQIgASAANgIIIAIgADYCDCAAIAE2AgwgACACNgIIC70IAQN/IwBB8ABrIgUkACAFIAM2AgwgBSACNgIIIAUCfwJAAn8CQAJAIAFBgQJPBEADQCAGQYACaiAAIAZqIgdBgAJqLAAAQb9/Sg0EGiAGQf8BaiAHQf8BaiwAAEG/f0oNBBogB0H+AWosAABBv39KDQMgB0H9AWosAABBv39KDQIgBkF8aiIGQYB+Rw0AC0EAIQYMBAsgBSABNgIUIAUgADYCECAFQdCdwAA2AhhBAAwECyAGQf0BagwBCyAGQf4BagsiByABSQRAIAchBgwBCyABIQYgASAHRg0AIAAgAUEAIAdBlKTAABAUAAsgBSAGNgIUIAUgADYCECAFQaSkwAA2AhhBBQs2AhwCQAJAAkACQAJAAkACQCACIAFLIgYNACADIAFLDQAgAiADSw0BIAJFDQICQCACIAFPBEAgASACRw0BDAQLIAAgAmosAABBv39KDQMLIAUgAjYCICACIQMMAwsgBSACIAMgBhs2AiggBUEwaiIAQRRqQQM2AgAgBUHIAGoiAUEUakHoADYCACAFQdQAakHoADYCACAFQgM3AjQgBUHMpMAANgIwIAVB4QA2AkwgBSABNgJAIAUgBUEYajYCWCAFIAVBEGo2AlAgBSAFQShqNgJIIAAgBBDoAQALIAVB5ABqQegANgIAIAVByABqIgBBFGpB6AA2AgAgBUHUAGpB4QA2AgAgBUEwaiIBQRRqQQQ2AgAgBUIENwI0IAVBiKXAADYCMCAFQeEANgJMIAUgADYCQCAFIAVBGGo2AmAgBSAFQRBqNgJYIAUgBUEMajYCUCAFIAVBCGo2AkggASAEEOgBAAsgBSADNgIgIANFDQELA0ACQCADIAFPBEAgASADRg0FDAELIAAgA2osAABBv39KDQMLIANBf2oiAw0ACwtBACEDCyABIANGDQAgACADaiIALAAAIgFB/wFxIQYCfwJAAkAgAUF/TARAIAAtAAFBP3EhByABQR9xIQIgBkHfAUsNASACQQZ0IAdyIQYMAgsgBSAGNgIkQQEMAgsgAC0AAkE/cSAHQQZ0ciEGIAFB/wFxQfABSQRAIAJBDHQgBnIhBgwBCyACQRJ0QYCA8ABxIAAtAANBP3EgBkEGdHJyIgZBgIDEAEYNAgsgBSAGNgIkQQEgBkGAAUkNABpBAiAGQYAQSQ0AGkEDQQQgBkGAgARJGwshByAFIAM2AiggBSADIAdqNgIsIAVBMGoiAEEUakEFNgIAIAVB7ABqQegANgIAIAVB5ABqQegANgIAIAVByABqIgFBFGpB6QA2AgAgBUHUAGpB6gA2AgAgBUIFNwI0IAVB3KXAADYCMCAFQeEANgJMIAUgATYCQCAFIAVBGGo2AmggBSAFQRBqNgJgIAUgBUEoajYCWCAFIAVBJGo2AlAgBSAFQSBqNgJIIAAgBBDoAQALQdydwABBKyAEEM0BAAviBgEGfyAAKAIQIQQCQAJAAkACQCAAKAIIIghBAUcEQCAEQQFGDQEgACgCGCABIAIgAEEcaigCACgCDBECACEDDAMLIARBAUcNAQsgASACaiEHAkACQCAAQRRqKAIAIgZFBEAgASEEDAELIAEhBANAIAQgB0YNAgJ/IAQiAywAACIEQX9KBEAgA0EBagwBCyADQQJqIARB/wFxIgRB4AFJDQAaIANBA2ogBEHwAUkNABogBEESdEGAgPAAcSADLQADQT9xIAMtAAJBP3FBBnQgAy0AAUE/cUEMdHJyckGAgMQARg0DIANBBGoLIgQgBSADa2ohBSAGQX9qIgYNAAsLIAQgB0YNACAELQAAIgNB8AFPBEAgA0ESdEGAgPAAcSAELQADQT9xIAQtAAJBP3FBBnQgBC0AAUE/cUEMdHJyckGAgMQARg0BCwJAAkAgBUUEQEEAIQQMAQsgBSACTwRAQQAhAyAFIAIiBEYNAQwCC0EAIQMgBSIEIAFqLAAAQUBIDQELIAQhBSABIQMLIAUgAiADGyECIAMgASADGyEBCyAIQQFGDQAMAgsgAEEMaigCACEHAkAgAkUEQEEAIQQMAQsgAkEDcSEFAkAgAkF/akEDSQRAQQAhBCABIQMMAQtBACEEQQAgAkF8cWshBiABIQMDQCADLAAAQb9/SiAEaiADQQFqLAAAQb9/SmogA0ECaiwAAEG/f0pqIANBA2osAABBv39KaiEEIANBBGohAyAGQQRqIgYNAAsLIAVFDQADQCADLAAAQb9/SiAEaiEEIANBAWohAyAFQX9qIgUNAAsLIAcgBEsEQEEAIQMgByAEayIEIQUCQAJAAkBBACAALQAgIgYgBkEDRhtBA3FBAWsOAgABAgtBACEFIAQhAwwBCyAEQQF2IQMgBEEBakEBdiEFCyADQQFqIQMgAEEcaigCACEEIAAoAgQhBiAAKAIYIQACQANAIANBf2oiA0UNASAAIAYgBCgCEBEAAEUNAAtBAQ8LQQEhAyAGQYCAxABGDQEgACABIAIgBCgCDBECAA0BQQAhAwNAIAMgBUYEQEEADwsgA0EBaiEDIAAgBiAEKAIQEQAARQ0ACyADQX9qIAVJDwsMAQsgAw8LIAAoAhggASACIABBHGooAgAoAgwRAgAL7AYBB39BK0GAgMQAIAAoAgAiCUEBcSIFGyEKIAQgBWohBwJAIAlBBHFFBEBBACEBDAELAkAgAkUNACACQQNxIQYCQCACQX9qQQNJBEAgASEFDAELQQAgAkF8cWshCyABIQUDQCAFLAAAQb9/SiAIaiAFQQFqLAAAQb9/SmogBUECaiwAAEG/f0pqIAVBA2osAABBv39KaiEIIAVBBGohBSALQQRqIgsNAAsLIAZFDQADQCAFLAAAQb9/SiAIaiEIIAVBAWohBSAGQX9qIgYNAAsLIAcgCGohBwtBASEFAkACQCAAKAIIQQFHBEAgACAKIAEgAhDGAQ0BDAILAkACQAJAAkAgAEEMaigCACIGIAdLBEAgCUEIcQ0EQQAhBSAGIAdrIgghB0EBIAAtACAiCSAJQQNGG0EDcUEBaw4CAQIDCyAAIAogASACEMYBDQQMBQtBACEHIAghBQwBCyAIQQF2IQUgCEEBakEBdiEHCyAFQQFqIQUgAEEcaigCACEJIAAoAgQhCCAAKAIYIQYCQANAIAVBf2oiBUUNASAGIAggCSgCEBEAAEUNAAtBAQ8LQQEhBSAIQYCAxABGDQEgACAKIAEgAhDGAQ0BIAAoAhggAyAEIAAoAhwoAgwRAgANASAAKAIcIQEgACgCGCECQQAhBQJ/A0AgByIAIAAgBUYNARogBUEBaiEFIAIgCCABKAIQEQAARQ0ACyAFQX9qCyAHSSEFDAELIAAoAgQhCCAAQTA2AgQgAC0AICEJIABBAToAICAAIAogASACEMYBDQBBACEFIAYgB2siASECAkACQAJAQQEgAC0AICIHIAdBA0YbQQNxQQFrDgIAAQILQQAhAiABIQUMAQsgAUEBdiEFIAFBAWpBAXYhAgsgBUEBaiEFIABBHGooAgAhByAAKAIEIQEgACgCGCEGAkADQCAFQX9qIgVFDQEgBiABIAcoAhARAABFDQALQQEPC0EBIQUgAUGAgMQARg0AIAAoAhggAyAEIAAoAhwoAgwRAgANACAAKAIcIQMgACgCGCEEQQAhBgJAA0AgAiAGRg0BIAZBAWohBiAEIAEgAygCEBEAAEUNAAsgBkF/aiACSQ0BCyAAIAk6ACAgACAINgIEQQAPCyAFDwsgACgCGCADIAQgAEEcaigCACgCDBECAAuvBQEGfwJAAkACQCACQQlPBEAgAyACECQiAg0BQQAPC0EAIQJBzf97IANNDQFBECADQQRqQQsgA0sbQQdqQXhxIQUgAEF4aiIBKAIEQXhxIQYgASAGaiEEAkACQAJAAkACQAJAAkAgAS0ABEEDcQRAIAYgBU8NAUGwvMAAKAIAIARGDQJBrLzAACgCACAERg0DIAQtAARBAnFBAXYNByAEKAIEQXhxIgcgBmoiCCAFSQ0HIAggBWshBiAHQYACSQ0EIAQQOAwFCyABKAIEQXhxIQQgBUGAAkkNBiAEIAVBBGpPQQAgBCAFa0GBgAhJGw0FIAEoAgAaDAYLIAYgBWsiBEEQSQ0EIAEgBRDmASABIAVqIgUgBBDmASAFIAQQHwwEC0GovMAAKAIAIAZqIgQgBU0NBCABIAUQ5gEgASAFaiIGIAQgBWsiBEEBcjYCBEGovMAAIAQ2AgBBsLzAACAGNgIADAMLQaS8wAAoAgAgBmoiBiAFSQ0DAkAgBiAFayIEQRBJBEAgASAGEOYBQQAhBEEAIQYMAQsgASAFaiIGIARqIQcgASAFEOYBIAYgBEEBcjYCBCAEIAZqIAQ2AgAgByAHKAIEQX5xNgIEC0GsvMAAIAY2AgBBpLzAACAENgIADAILIARBDGooAgAiCSAEQQhqKAIAIgRHBEAgBCAJNgIMIAkgBDYCCAwBC0GUucAAQZS5wAAoAgBBfiAHQQN2d3E2AgALIAZBEE8EQCABIAUQ5gEgASAFaiIEIAYQ5gEgBCAGEB8MAQsgASAIEOYBCyABDQMLIAMQDSIERQ0BIAQgACADIAEoAgRBeHFBfEF4IAEtAARBA3EbaiIBIAEgA0sbECwgABATDwsgAiAAIAMgASABIANLGxAsGiAAEBMLIAIPCyABLQAEGiABQQhqC+cFAQl/AkACQCACBEAgACgCBCEHIAAoAgAhCCAAKAIIIQoDQAJAIAotAABFDQAgCEGIn8AAQQQgBygCDBECAEUNAEEBDwtBACEFIAIhBAJAAkADQAJAIAEgBWohBgJAAkACQAJAIARBCE8EQCAGQQNqQXxxIAZrIgBFBEAgBEF4aiEDQQAhAAwDCyAEIAAgACAESxshAEEAIQMDQCADIAZqLQAAQQpGDQUgACADQQFqIgNHDQALDAELIARFDQRBACEDIAYtAABBCkYNA0EAIQAgBEEBRg0GQQEhAyAGLQABQQpGDQMgBEECRg0GQQIhAyAGLQACQQpGDQMgBEEDRg0GQQMhAyAGLQADQQpGDQMgBEEERg0GQQQhAyAGLQAEQQpGDQMgBEEFRg0GQQUhAyAGLQAFQQpGDQMgBEEGRg0GQQYhAyAGLQAGQQpHDQYMAwsgACAEQXhqIgNLDQELA0AgACAGaiIJKAIAIgtBf3MgC0GKlKjQAHNB//37d2pxIAlBBGooAgAiCUF/cyAJQYqUqNAAc0H//ft3anFyQYCBgoR4cUUEQCAAQQhqIgAgA00NAQsLIAAgBE0NACAAIARBnKLAABCaAQALIAAgBEYNASAEIABrIQQgACAFaiABaiEGQQAhAwNAIAMgBmotAABBCkcEQCADQQFqIgMgBEcNAQwDCwsgACADaiEDCwJAIAMgBWoiAEEBaiIFIABJDQAgAiAFSQ0AIAAgAWotAABBCkcNAEEBIQAMBAsgAiAFayEEIAIgBU8NAQsLQQAhAAsgAiEFCyAKIAA6AAACQCACIAVNBEAgAiAFRw0EIAggASAFIAcoAgwRAgBFDQFBAQ8LIAEgBWoiACwAAEG/f0wNAyAIIAEgBSAHKAIMEQIABEBBAQ8LIAAsAABBv39MDQQLIAEgBWohASACIAVrIgINAAsLQQAPCyABIAJBACAFQayfwAAQFAALIAEgAiAFIAJBvJ/AABAUAAuUBQEGfwJAAn8CQCAAIAFrIAJJBEAgACACaiEDIAEgAmoiBSACQQ9NDQIaIANBfHEhAEEAIANBA3EiBmshByAGBEAgASACakF/aiEEA0AgA0F/aiIDIAQtAAA6AAAgBEF/aiEEIAAgA0kNAAsLIAAgAiAGayIGQXxxIgJrIQNBACACayECIAUgB2oiBUEDcQRAIAJBf0oNAiAFQQN0IgFBGHEhB0EAIAFrQRhxIQggBUF8cSIEQXxqIQEgBCgCACEEA0AgAEF8aiIAIAQgCHQgASgCACIEIAd2cjYCACABQXxqIQEgACADSw0ACwwCCyACQX9KDQEgASAGakF8aiEBA0AgAEF8aiIAIAEoAgA2AgAgAUF8aiEBIAAgA0sNAAsMAQsCQCACQQ9NBEAgACEDDAELQQAgAGtBA3EiBSAAaiEEIAUEQCAAIQMgASEAA0AgAyAALQAAOgAAIABBAWohACADQQFqIgMgBEkNAAsLIAIgBWsiAkF8cSIGIARqIQMCQCABIAVqIgVBA3EEQCAGQQFIDQEgBUEDdCIAQRhxIQdBACAAa0EYcSEIIAVBfHEiAEEEaiEBIAAoAgAhAANAIAQgACAHdiABKAIAIgAgCHRyNgIAIAFBBGohASAEQQRqIgQgA0kNAAsMAQsgBkEBSA0AIAUhAQNAIAQgASgCADYCACABQQRqIQEgBEEEaiIEIANJDQALCyACQQNxIQIgBSAGaiEBCyACQQFIDQIgAiADaiEAA0AgAyABLQAAOgAAIAFBAWohASADQQFqIgMgAEkNAAsMAgsgBkEDcSIARQ0BIAMgAGshACACIAVqC0F/aiEBA0AgA0F/aiIDIAEtAAA6AAAgAUF/aiEBIAAgA0kNAAsLC9cGAQh/IwBBEGshBQJAIAAoAggiAUUNACAAQZgBaiEGIAAoAgAhAyAFQQpqIgdBBGohCANAAn8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAMvAQAiAg4cAAEMAgMEDAUMBgwMDAwMDAwMDAwMBwcICQoMCwwLIAdBADYAACAIQQA7AAAgAEECOgCXASAAQQI6AJMBIAYgBSkABzcAACAGQQhqIAVBD2otAAA6AAAMDAsgAEEBOgCbAQwLCyAAQQE6AJwBDAoLIABBAToAnQEMCQsgAEEBOgCfAQwICyAAQQE6AKABDAcLIABBAToAngEMBgsgAEEAOgCbAQwFCyAAQQA6AJwBDAQLIABBADoAnQEMAwsgAEEAOgCfAQwCCyAAQQA6AKABDAELAkACQAJAAkACQAJAAkACQAJAIAJBYmoiBEH//wNxQQhPBEAgAkFaag4CAQIDCyAAQQA6AJMBIAAgBDoAlAEMCQsgAUEBSw0CDAsLIABBAjoAkwEMBwsCQAJAAkAgAkH4/wNxQShHBEAgAkFQag4CAwECCyAAQQA6AJcBIAAgAkFYajoAmAEMCQsgAEECOgCXAQwICyACQaZ/akH//wNxQQhJDQIgAkGcf2pB//8DcUEITw0HIABBADoAlwEgACACQaR/ajoAmAEMBwsgAUEBTQ0JAkACQAJAIANBAmoiAi8BAEF+ag4EAgAAAQALIAFBf2ohASACDAkLIAFBA0kNCiAAIAMtAAQ6AJgBIABBADoAlwEMBgsgAUEESw0DDAILAkACQAJAIANBAmoiAi8BAEF+ag4EAgAAAQALIAFBf2ohASACDAgLIAFBA0kNCSAAIAMtAAQ6AJQBIABBADoAkwEMBQsgAUEETQ0BIAMtAAQhAiADLQAGIQQgACADLQAIOgCWASAAIAQ6AJUBIAAgAjoAlAEgAEEBOgCTAQwDCyAAQQA6AJMBIAAgAkGuf2o6AJQBDAQLIAFBfmohASADQQRqDAQLIAMtAAQhAiADLQAGIQQgACADLQAIOgCaASAAIAQ6AJkBIAAgAjoAmAEgAEEBOgCXAQsgAUF7aiEBIANBCmoMAgsgAUF9aiEBIANBBmoMAQsgAUF/aiEBIANBAmoLIQMgAQ0ACwsLzAUBCX8jAEGAAWsiAyQAIAEtAAAiBEECRiEFIANB6ABqIAIgAS0ADSIGIAEtAAwiByABLQALIgggAS0ACiIJIAEtAAkiCiABLQAIIgsgBEECR0EBQQIgBRsgAS0ABCIEQQJGG2pqampqahDDASADKAJsIQICfwJAAkACfwJAAkACQAJAIAMoAmhBAUcEQCADQdwAaiADQfgAaikDADcCACADIANB8ABqKQMANwJUIAMgAjYCUCAFRQRAIAMgASgAADYCaCADQcgAaiADQdAAakHOgcAAIANB6ABqEKsBIAMoAkgNAgsgBEECRwRAIAMgASgABDYCaCADQUBrIANB0ABqQdCBwAAgA0HoAGoQqwEgAygCQA0DCyALDQMMBAsMBQsgAygCTAwDCyADKAJEDAILIANBOGogA0HQAGpB0oHAAEEEEKwBIAMoAjhFDQAgAygCPAwBCwJAIApFDQAgA0EwaiADQdAAakHXgcAAQQYQrAEgAygCMEUNACADKAI0DAELAkAgCUUNACADQShqIANB0ABqQd2BwABBCRCsASADKAIoRQ0AIAMoAiwMAQsCQCAIRQ0AIANBIGogA0HQAGpB5oHAAEENEKwBIAMoAiBFDQAgAygCJAwBCwJAIAdFDQAgA0EYaiADQdAAakHzgcAAQQUQrAEgAygCGEUNACADKAIcDAELIAZFDQIgA0EQaiADQdAAakH4gcAAQQcQrAEgAygCEEUNAiADKAIUCyECIANB2ABqKAIAIgFBJE8EQCABEAALIAMoAlxFDQAgA0HgAGooAgAiAUEkSQ0AIAEQAAtBAQwBCyADQegAaiIBQRBqIANB0ABqIgJBEGooAgA2AgAgAUEIaiACQQhqKQMANwMAIAMgAykDUDcDaCADQQhqIAEQ0gEgAygCDCECIAMoAggLIQEgACACNgIEIAAgATYCACADQYABaiQAC/YFAQF/IwBBEGsiAiQAIAIgAa1CgICAgBBCACABKAIYQYyQwABBAiABQRxqKAIAKAIMEQIAG4Q3AwAgAiAAQZABajYCDCACQY6QwABBBSACQQxqIgFBlJDAABAnIAIgADYCDCACQaSQwABBBiABQayQwAAQJyACIABBDGo2AgwgAkG8kMAAQQ0gAUGkj8AAECcgAiAAQRhqNgIMIAJByZDAAEEHIAFB2I/AABAnIAIgAEEcajYCDCACQdCQwABBBCABQdiPwAAQJyACIABBIGo2AgwgAkHUkMAAQQYgAUHckMAAECcgAiAAQSxqNgIMIAJB7JDAAEEQIAFB3JDAABAnIAIgAEGRAWo2AgwgAkH8kMAAQRIgAUGQkcAAECcgAiAAQThqNgIMIAJB0I/AAEEIIAFB2I/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/IwBBIGsiAiQAAkAgAUEBaiIDIAFJDQAgAEEEaigCACIBQQF0IgQgAyAEIANLGyIDQQggA0EISxshAwJAIAEEQCACQRhqQQE2AgAgAiABNgIUIAIgACgCADYCEAwBCyACQQA2AhALIAIgAyACQRBqEGcgAigCAEEBRgRAIAJBCGooAgAiAEUNASACKAIEIABBgLnAACgCACIAQdQAIAAbEQEAAAsgAigCBCEBIABBBGogAzYCACAAIAE2AgAgAkEgaiQADwsQkQIAC8UBAQh/IwBBEGsiAiQAIAEoAgAhAyACQQhqIAEoAggiBxCNASACKAIIIQEgACACKAIMIgQ2AgQgACABNgIAAkAgBEUNACAHQQxsIQUDQCAFRQ0BIAMoAgAhBiACIAMoAggiCBCMASACKAIEIQkgAigCACAGIAhBFGwQLCEGIAFBCGogCDYCACABQQRqIAk2AgAgASAGNgIAIAFBDGohASAFQXRqIQUgA0EMaiEDIARBf2oiBA0ACwsgACAHNgIIIAJBEGokAAvYAQIDfwF+IwBBMGsiAiQAIAAtAJEBRQRAIABBAToAkQEgACkCbCEEIAAgACkCVDcCbCAAIAQ3AlQgAEH0AGoiASkCACEEIAEgAEHcAGoiASkCADcCACABIAQ3AgAgAEH8AGoiASkCACEEIAEgAEHkAGoiASkCADcCACABIAQ3AgAgACkCLCEEIAAgACkCIDcCLCAAIAQ3AiAgAEE0aiIBKAIAIQMgASAAQShqIgEoAgA2AgAgASADNgIAIABBACAAKAIcIgEQKiAAQQAgARCtAQsgAkEwaiQAC8MBAQJ/AkACQCAAQShqKAIAIgQgACgCPCIDSwRAIAIgAUkNASAAKAIgIANBDGxqIgMoAggiBCACSQ0CIAEgAkcEQCADKAIAIgMgAkEUbGohBCABQRRsIANqIQIgAEGTAWoiAEEGaiEBA0AgAkEgNgIAIAJBBGogACkAADcAACACQQpqIAEpAAA3AAAgBCACQRRqIgJHDQALCw8LIAMgBEH4i8AAEJkBAAsgASACQfiLwAAQnAEACyACIARB+IvAABCbAQALvgEBBX8jAEEQayIDJAACQAJAIAEoAgQgAk8EQCADIAEQ0wEgAygCACIEBEAgA0EIaigCACEFIAMoAgQhBgJAIAJBAnQiB0UEQCAGBEAgBBATCyAFIgRFDQEMBAsgBCAGIAUgBxD+ASIEDQMLIAAgBzYCBCAAQQE2AgAgAEEIaiAFNgIADAMLIABBADYCAAwCC0HchMAAQSRBzIXAABDNAQALIAEgAjYCBCABIAQ2AgAgAEEANgIACyADQRBqJAALnwEBAn8gAkEPSwRAQQAgAGtBA3EiAyAAaiEEIAMEQANAIAAgAToAACAAQQFqIgAgBEkNAAsLIAIgA2siAkF8cSIDIARqIQAgA0EBTgRAIAFB/wFxQYGChAhsIQMDQCAEIAM2AgAgBEEEaiIEIABJDQALCyACQQNxIQILIAJBAU4EQCAAIAJqIQIDQCAAIAE6AAAgAEEBaiIAIAJJDQALCwvNAQIDfwF+IwBBMGsiAiQAIAAtAJEBBEAgAEEAOgCRASAAKQJsIQQgACAAKQJUNwJsIAAgBDcCVCAAQfQAaiIBKQIAIQQgASAAQdwAaiIBKQIANwIAIAEgBDcCACAAQfwAaiIBKQIAIQQgASAAQeQAaiIBKQIANwIAIAEgBDcCACAAKQIsIQQgACAAKQIgNwIsIAAgBDcCICAAQTRqIgEoAgAhAyABIABBKGoiASgCADYCACABIAM2AgAgAEEAIAAoAhwQrQELIAJBMGokAAvTAQEBfyMAQRBrIgIkACACIAGtQoCAgIAQQgAgASgCGEHIj8AAQQggAUEcaigCACgCDBECABuENwMAIAIgADYCDCACQdCPwABBCCACQQxqIgFB2I/AABAnIAIgAEEEajYCDCACQeiPwABBCCABQdiPwAAQJyACIABBCGo2AgwgAkHwj8AAQQMgAUGUj8AAECcgAiAAQRZqNgIMIAJB84/AAEELIAFByI7AABAnIAIgAEEXajYCDCACQf6PwABBDiABQciOwAAQJyACEI4BIAJBEGokAAu9AwACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAAtAABBAWsODQECAwQFBgcICQoLDA0ACyABKAIYQfmNwABBBiABQRxqKAIAKAIMEQIADwsgASgCGEHzjcAAQQYgAUEcaigCACgCDBECAA8LIAEoAhhB4Y3AAEESIAFBHGooAgAoAgwRAgAPCyABKAIYQdmNwABBCCABQRxqKAIAKAIMEQIADwsgASgCGEHRjcAAQQggAUEcaigCACgCDBECAA8LIAEoAhhBwo3AAEEPIAFBHGooAgAoAgwRAgAPCyABKAIYQbmNwABBCSABQRxqKAIAKAIMEQIADwsgASgCGEGxjcAAQQggAUEcaigCACgCDBECAA8LIAEoAhhBqY3AAEEIIAFBHGooAgAoAgwRAgAPCyABKAIYQZqNwABBDyABQRxqKAIAKAIMEQIADwsgASgCGEGMjcAAQQ4gAUEcaigCACgCDBECAA8LIAEoAhhBg43AAEEJIAFBHGooAgAoAgwRAgAPCyABKAIYQfqMwABBCSABQRxqKAIAKAIMEQIADwsgASgCGEHsjMAAQQ4gAUEcaigCACgCDBECAAu0AQEEfyMAQTBrIgIkACABQQRqIQMgASgCBEUEQCABKAIAIQEgAkIANwIMIAJBiJnAACgCADYCCCACIAJBCGoiBTYCFCACQRhqIgRBEGogAUEQaikCADcDACAEQQhqIAFBCGopAgA3AwAgAiABKQIANwMYIAJBFGpBwJrAACAEEB0aIANBCGogBUEIaigCADYCACADIAIpAwg3AgALIABB+JnAADYCBCAAIAM2AgAgAkEwaiQAC6EBAQZ/IwBBEGsiAiQAIAAoAgAhAyACQQhqIAAoAggiARCTASACKAIMIQQgAigCCCADIAFBAXQiBRAsIQMgAQRAIAMhAQNAAkACQCABLwEAIgZBBEcEQCAGQRRGDQEMAgsgAEEAOgCiAQwBCyAAQQA6AKUBCyABQQJqIQEgBUF+aiIFDQALCwJAIARFDQAgBEEBdEUNACADEBMLIAJBEGokAAuhAQEGfyMAQRBrIgIkACAAKAIAIQMgAkEIaiAAKAIIIgEQkwEgAigCDCEEIAIoAgggAyABQQF0IgUQLCEDIAEEQCADIQEDQAJAAkAgAS8BACIGQQRHBEAgBkEURg0BDAILIABBAToAogEMAQsgAEEBOgClAQsgAUECaiEBIAVBfmoiBQ0ACwsCQCAERQ0AIARBAXRFDQAgAxATCyACQRBqJAALqwEBBX8gAEEAOgCmASAAIAAoAlAgACgCHEF/aiAALQCjASIBGyICIAAoAkxBACABGyIBIAAoAgAiA0GYjMAAIAAoAggiBRsvAQAiBEEBIAQbakF/aiIEIAEgBCABSxsiASABIAJLGzYCPCADQQJqQZiMwAAgBUEBSxsvAQAiAUEBIAEbQX9qIgIgACgCGCIDQX9qIgEgAyACSxshAiAAIAEgAiACIAFLGzYCOAuoAQECfwJAAkACQCACBEBBASEEIAFBAE4NAQwCCyAAIAE2AgRBASEEDAELAkACQAJAAkAgAygCACIFBEAgAygCBCIDRQRAIAENAgwECyAFIAMgAiABEP4BIgNFDQIMBAsgAUUNAgsgASACEIoCIgMNAgsgACABNgIEIAIhAQwDCyACIQMLIAAgAzYCBEEAIQQMAQtBACEBCyAAIAQ2AgAgAEEIaiABNgIAC6kBAQR/AkACQCAAKAI4IgIEQCAAQShqKAIAIgMgACgCPCIBTQ0BIAAoAiAgAUEMbGoiAygCCCIEIAJBf2oiAU0NAiAAKAIAQZiMwAAgACgCCBsvAQAiAkEBIAIbIQIgAygCACABQRRsaigCACEDQQAhAQNAIAAgAxAiIAFBAWoiAUH//wNxIAJJDQALCw8LIAEgA0HIi8AAEJkBAAsgASAEQciLwAAQmQEAC6EBAQJ/IwBBEGsiAiQAIABBAWohAwJAIAAtAABBAUcEQCACIAFBlI7AAEEHEOIBIAIgAzYCDCACIAJBDGpBhI7AABA3DAELIAIgAUH/jcAAQQMQ4gEgAiADNgIMIAIgAkEMaiIBQYSOwAAQNyACIABBAmo2AgwgAiABQYSOwAAQNyACIABBA2o2AgwgAiABQYSOwAAQNwsgAhB2IAJBEGokAAuUAQECfwJAAkACQAJAAn9BASEDAkACQCABQQBOBEAgAigCACIERQ0BIAIoAgQiAg0EIAENAkEBDAMLQQAhAQwGCyABDQBBAQwBCyABQQEQigILIgJFDQEMAgsgBCACQQEgARD+ASICDQELIAAgATYCBEEBIQEMAQsgACACNgIEQQAhAwsgACADNgIAIABBCGogATYCAAuVAQEDfyMAQYABayIDJAAgAC8BACECQQAhAANAIAAgA2pB/wBqIAJBD3EiBEEwQdcAIARBCkkbajoAACAAQX9qIQAgAkH//wNxIgRBBHYhAiAEQQ9LDQALIABBgAFqIgJBgQFPBEAgAkGAAUH8n8AAEJoBAAsgAUGMoMAAQQIgACADakGAAWpBACAAaxAWIANBgAFqJAALlAEBA38jAEGAAWsiAyQAIAAtAAAhAkEAIQADQCAAIANqQf8AaiACQQ9xIgRBMEHXACAEQQpJG2o6AAAgAEF/aiEAIAJB/wFxIgRBBHYhAiAEQQ9LDQALIABBgAFqIgJBgQFPBEAgAkGAAUH8n8AAEJoBAAsgAUGMoMAAQQIgACADakGAAWpBACAAaxAWIANBgAFqJAALkwEBA38jAEGAAWsiAyQAIAAtAAAhAkEAIQADQCAAIANqQf8AaiACQQ9xIgRBMEE3IARBCkkbajoAACAAQX9qIQAgAkH/AXEiBEEEdiECIARBD0sNAAsgAEGAAWoiAkGBAU8EQCACQYABQfyfwAAQmgEACyABQYygwABBAiAAIANqQYABakEAIABrEBYgA0GAAWokAAuUAQEDfyMAQYABayIDJAAgAC8BACECQQAhAANAIAAgA2pB/wBqIAJBD3EiBEEwQTcgBEEKSRtqOgAAIABBf2ohACACQf//A3EiBEEEdiECIARBD0sNAAsgAEGAAWoiAkGBAU8EQCACQYABQfyfwAAQmgEACyABQYygwABBAiAAIANqQYABakEAIABrEBYgA0GAAWokAAuKAQEDfyMAQYABayIDJAAgACgCACEAA0AgAiADakH/AGogAEEPcSIEQTBB1wAgBEEKSRtqOgAAIAJBf2ohAiAAQQ9LIABBBHYhAA0ACyACQYABaiIAQYEBTwRAIABBgAFB/J/AABCaAQALIAFBjKDAAEECIAIgA2pBgAFqQQAgAmsQFiADQYABaiQAC4kBAQN/IwBBgAFrIgMkACAAKAIAIQADQCACIANqQf8AaiAAQQ9xIgRBMEE3IARBCkkbajoAACACQX9qIQIgAEEPSyAAQQR2IQANAAsgAkGAAWoiAEGBAU8EQCAAQYABQfyfwAAQmgEACyABQYygwABBAiACIANqQYABakEAIAJrEBYgA0GAAWokAAuRAQEDfyAAQgA3AgQgAEHoksAAKAIANgIAQQghAgNAAkACQCAEQQFxRQRAIAIgAUkNAQwCCyACQQdqIgQgAkkNASAEIgIgAU8NAQsgAyAAKAIERgRAIAAgAxC8ASAAKAIIIQMLIAAoAgAgA0ECdGogAjYCAEEBIQQgACAAKAIIQQFqIgM2AgggAkEBaiECDAELCwuiAQEDfwJAIAAoAgAiAUGYjMAAIAAoAggiAhsvAQAiA0F/akEAIAMbIgNB//8DcSABQQJqQZiMwAAgAkEBSxsvAQAiASAAKAIcIgIgARtBf2pB//8DcSIBSUEAIAIgAUsbRQRAIAAoAkwhAQwBCyAAIAE2AlAgACADQf//A3EiATYCTAsgAEEAOgCmASAAQQA2AjggACABQQAgAC0AowEbNgI8C5oBAQJ/IwBBQGoiAiQAIAJCADcDECACQRBqIgMgACgCABALIAIgAigCFCIANgI4IAIgADYCNCACIAIoAhA2AjAgAkEIaiIAQc8ANgIEIAAgAkEwaiIANgIAIAJBJGpBATYCACACQgI3AhQgAkHslsAANgIQIAIgAikDCDcDKCACIAJBKGo2AiAgASADEJ4BIAAQ+wEgAkFAayQAC4kBAQZ/AkAgACgCOCIDRQ0AIAMgACgCGE8NAAJAIABByABqKAIAIgFFBEAMAQsgACgCQCEFIAEhBANAAkAgAUEBdiACaiIBQQJ0IAVqKAIAIgYgA08EQCABIQQgAyAGRw0BDAQLIAFBAWohAgsgBCACayEBIAQgAksNAAsLIABBQGsgAiADEJ8BCwuPAQEFfwJAAkAgASgCBCIDIAJPBEAgA0UNAiADQQJ0IQMgASgCACEEIAJBAnQiBUUEQEEEIQYgA0UNAiAEEBMMAgsgBCADQQQgBRD+ASIGDQEgACAFNgIEIABBCGpBBDYCAEEBIQcMAgtByIjAAEEkQeyIwAAQzQEACyABIAI2AgQgASAGNgIACyAAIAc2AgALowEBAn8CQAJAAkACQCAAKAIAQZiMwAAgACgCCBsvAQAOAwABAgMLIAAgACgCOCAAKAIYEFogACAAKAI8IgFBAWogACgCHCICECogACABIAIQrQEPCyAAQQAgACgCGCIBIAAoAjhBAWoiAiACIAFLGxBaIABBACAAKAI8IgEQKiAAQQAgAUEBahCtAQ8LIABBACAAKAIcIgEQKiAAQQAgARCtAQsLrAEBA38jAEHQAGsiACQAIABBMzYCDCAAQfCAwAA2AgggAEIANwIUIABB1IPAACgCADYCECAAQSBqIgEgAEEQakGogsAAEOkBIABBCGoiAigCACACKAIEIAEQqQIEQEHAgsAAQTcgAEHIAGpB3IPAAEHEg8AAEIoBAAsgACAAQRBqIgEoAgg2AgQgACABKAIANgIAIAAoAgAgACgCBBCqAiABEPsBIABB0ABqJAALlgEBA38jAEEgayIDJAAgASgCACEEIANBEGoiBSACKAIINgIEIAUgAigCADYCACADQQhqIAQgAygCECADKAIUEP8BIAMoAgwhAgJ/IAMoAghFBEAgAyACNgIcIAFBBGogA0EcahCQAiADKAIcIgFBJE8EQCABEAALQQAMAQtBAQshASAAIAI2AgQgACABNgIAIANBIGokAAuWAQECfyAALQAIIQEgACgCBCICBEAgAUH/AXEhASAAAn9BASABDQAaAkAgAkEBRw0AIAAtAAlFDQAgACgCACICLQAAQQRxDQBBASACKAIYQdyfwABBASACQRxqKAIAKAIMEQIADQEaCyAAKAIAIgEoAhhB3Z/AAEEBIAFBHGooAgAoAgwRAgALIgE6AAgLIAFB/wFxQQBHC30BBn8CQCAAQcgAaigCACIBRQ0AIABBQGsgACgCQCEFIAAoAjghA0EAIQAgASECA0ACQAJAIAFBAXYgAGoiAUECdCAFaigCACIGIANPBEAgAyAGRg0CIAEhAgwBCyABQQFqIQALIAIgAGshASACIABLDQEMAgsLIAEQswELC4sBAQF/IwBBMGsiASQAIAEgAjcDCCAAAn8gAkKAgICAgICAEFoEQCABQQs2AgQgASABQQhqNgIAIAEgASkDADcDECABQSxqQQE2AgAgAUICNwIcIAFBkIbAADYCGCABIAFBEGo2AihBASEDIAFBGGoQegwBCyACuhABCzYCBCAAIAM2AgAgAUEwaiQAC4IBAQJ/IAAoAgghAiABQTtGBEAgAiAAKAIERgRAIAAgAhC+ASAAKAIIIQILIAAoAgAgAkEBdGpBADsBACAAIAAoAghBAWo2AggPCyACQX9qIQMgAgRAIAAoAgAgA0EBdGoiACAALwEAQQpsIAFqQVBqOwEADwsgA0EAQciKwAAQmQEAC5MBAQJ/IwBB0ABrIgEkACABQgA3AhQgAUHkh8AAKAIANgIQIAFBIGoiAiABQRBqQbiGwAAQ6QEgACACEJ0BBEBB0IbAAEE3IAFByABqQeyHwABB1IfAABCKAQALIAFBCGoiAiABQRBqIgAoAgg2AgQgAiAAKAIANgIAIAEoAgggASgCDBCqAiAAEPsBIAFB0ABqJAALhAEBAX8jAEEgayIGJAAgAQRAIAYgASADIAQgBSACKAIQEQcAIAZBGGogBkEIaigCACIBNgIAIAYgBikDADcDECAGKAIUIAFLBEAgBkEQaiABEMQBIAYoAhghAQsgBigCECECIAAgATYCBCAAIAI2AgAgBkEgaiQADwtB/IjAAEEwEKQCAAuaAQECfwJAAkACQAJAAkACQCAAKAIAQZiMwAAgACgCCBsvAQAOAwABAgQLIAAgACgCOCAAKAIYEFoMAgsgAEEAIAAoAhgiASAAKAI4QQFqIgIgAiABSxsQWgwBCyAAQQAgACgCGBBaCyAAQYwBaigCACICIAAoAjwiAU0NASAAKAKEASABakEBOgAACw8LIAEgAkHcjMAAEJkBAAuGAQECfyAAQQA6AKYBIAAgACgCGEF/aiIBIAAoAjgiAiACIAFLGzYCOCAAIAAoAlAgACgCHEF/aiAALQCjASIBGyICIAAoAkxBACABGyIBIAAoAgBBmIzAACAAKAIIGy8BACIAQX9qQQAgABtB//8DcWoiACABIAAgAUsbIgAgACACSxs2AjwLfQEFfyABKAIAIQMgASgCBCEEA0ACQCADIARHBEAgASADQQFqIgI2AgAgAy0AACABIAEoAggiBkEBajYCCCACIQNFDQIgACgCCCICIAAoAgRHDQEgACACELwBDAELDwsgACACQQFqNgIIIAAoAgAgAkECdGogBjYCAAwACwALgwEBA38gASgCBCIDIAJPBEACQCADRQ0AIAEoAgAhBAJAAkAgAkUEQEEBIQMgBBATDAELIAQgA0EBIAIQ/gEiA0UNAQsgASACNgIEIAEgAzYCAAwBCyAAIAI2AgQgAEEIakEBNgIAQQEhBQsgACAFNgIADwtBupjAAEEkQeCYwAAQzQEAC30BAX8jAEEQayIEJAAgBEEIaiABKAIAIAIgAxD/ASAEKAIMIQICfyAEKAIIRQRAAkAgASgCDEUNACABQRBqKAIAIgNBJEkNACADEAALIAFBATYCDCABQRBqIAI2AgBBAAwBC0EBCyEBIAAgAjYCBCAAIAE2AgAgBEEQaiQAC5QBAQJ/IwBBEGsiAyQAIABBFGooAgAhBAJAAn8CQAJAIABBBGooAgAOAgABAwsgBA0CQQAhAEGImcAADAELIAQNASAAKAIAIgQoAgQhACAEKAIACyEEIAMgADYCBCADIAQ2AgAgA0GsmsAAIAEoAgggAhBTAAsgA0EANgIEIAMgADYCACADQZiawAAgASgCCCACEFMAC30BA38CQCAAKAJQQQFqIgIgACgCTCIDTwRAIABBKGooAgAiBCACSQ0BIAIgA2siBCABIAQgAUkbIQEgACgCICADQQxsaiAEIAEQ5AEgACACIAFrIAIQKiAAIAMgAhCtAQ8LIAMgAkGcjMAAEJwBAAsgAiAEQZyMwAAQmwEAC34BAX8CQCAAQShqKAIAIgQgAksEQCAAKAIgIAJBDGxqIgAoAggiAiABTQ0BIAAoAgAgAUEUbGoiACADKQIANwIAIABBEGogA0EQaigCADYCACAAQQhqIANBCGopAgA3AgAPCyACIARB2IvAABCZAQALIAEgAkHYi8AAEJkBAAt2AQN/IAAoAgQgACgCCCICayABSQRAIAAgAiABELsBIAAoAgghAgsgACgCACIEIAJqIQMCQAJAIAFBAk8EQCADQQEgAUF/aiIBEFwgBCABIAJqIgJqIQMMAQsgAUUNAQsgA0EBOgAAIAJBAWohAgsgACACNgIIC3ABAX8jAEEQayIDJAAgAyACKAIAIAEoAgAQNCADKAIEIQICfyADKAIARQRAIAMgAjYCDCABQQRqIANBDGoQkAIgAygCDCIBQSRPBEAgARAAC0EADAELQQELIQEgACACNgIEIAAgATYCACADQRBqJAALcAEBfyMAQRBrIgMkACADIAEoAgAgAjUCABB4IAMoAgQhAgJ/IAMoAgBFBEAgAyACNgIMIAFBBGogA0EMahCQAiADKAIMIgFBJE8EQCABEAALQQAMAQtBAQshASAAIAI2AgQgACABNgIAIANBEGokAAt2AQJ/IwBBsAFrIgEkACABQQhqIgIgABC2AQJAIAIoAgQiAEUNACAAQQF0RQ0AIAIoAgAQEwsgAUEUahDsASABQShqIgAQsQEgABDtASABQTRqIgAQsQEgABDtASABQcgAahDnASABQYwBahD7ASABQbABaiQAC3gBAn8jAEEgayICJAAgAkEQaiIDIAAgARDcASACQQA6ABwgAkEIaiACQRxqIAMQUCACKAIMIQAgAigCCARAIAIgADYCHEGghMAAQSsgAkEcakHMhMAAQYCEwAAQigEACyACQRBqIgEQ0QEgARDOASACQSBqJAAgAAt6AQN/AkAgACgCUEEBaiICIAAoAkwiBE8EQCAAQShqKAIAIgMgAkkNASACIARrIgMgASADIAFJGyEBIAAoAiAgBEEMbGogAyABEN0BIABBACABECogAEEAIAIQrQEPCyAEIAJBrIzAABCcAQALIAIgA0GsjMAAEJsBAAuAAQEBfyMAQUBqIgUkACAFIAE2AgwgBSAANgIIIAUgAzYCFCAFIAI2AhAgBUEsakECNgIAIAVBPGpB5wA2AgAgBUICNwIcIAVB4J7AADYCGCAFQegANgI0IAUgBUEwajYCKCAFIAVBEGo2AjggBSAFQQhqNgIwIAVBGGogBBDoAQALbQEBfyMAQRBrIgMkACADIAIgASgCABAbIAMoAgQhAgJ/IAMoAgBFBEAgAyACNgIMIAFBBGogA0EMahCQAiADKAIMIgFBJE8EQCABEAALQQAMAQtBAQshASAAIAI2AgQgACABNgIAIANBEGokAAtlAgJ/AX4CQAJAAkAgAa1CFH4iBEIgiKcNACAEpyICQQBIDQAgAkUNASACQQQQigIiAw0CIAJBBEGAucAAKAIAIgBB1AAgABsRAQAACxCRAgALQQQhAwsgACABNgIEIAAgAzYCAAtlAgJ/AX4CQAJAAkAgAa1CDH4iBEIgiKcNACAEpyICQQBIDQAgAkUNASACQQQQigIiAw0CIAJBBEGAucAAKAIAIgBB1AAgABsRAQAACxCRAgALQQQhAwsgACABNgIEIAAgAzYCAAt8AQF/IAAtAAQhASAALQAFBEAgAUH/AXEhASAAAn9BASABDQAaIAAoAgAiAS0AAEEEcUUEQCABKAIYQdefwABBAiABQRxqKAIAKAIMEQIADAELIAEoAhhB1p/AAEEBIAFBHGooAgAoAgwRAgALIgE6AAQLIAFB/wFxQQBHC28BAX8jAEEgayICJAAgAkEAOgAfIAACfyABKAIAQQFHBEAgAkEQakKAgICAgAQ3AwAgAigCFCEBIAIoAhAMAQsgAkEIaiACQR9qIAFBBGoQVCACKAIMIQEgAigCCAs2AgAgACABNgIEIAJBIGokAAtuAQJ/An8gACgCPCICIAAoAlAiA00EQCABIAJqIgEgAyADIAFLGwwBCyABIAJqIgEgACgCHEF/aiICIAIgAUsbCyEBIABBADoApgEgACABNgI8IAAgACgCGEF/aiIBIAAoAjgiACAAIAFLGzYCOAs3AQF/QQRBBBCKAiIBRQRAQQRBBEGAucAAKAIAIgBB1AAgABsRAQAACyAAQQE2AgQgACABNgIAC24BAn8gAEEAOgCmASAAIAAoAhhBf2oiASAAKAI4IgIgAiABSxs2AjggACAAKAI8IgIgACgCAEGYjMAAIAAoAggbLwEAIgFBASABG2siAUEAIAFBAEobIAEgACgCTCIAIAEgAEobIAIgAEkbNgI8C14BAn8CQAJAAkAgASABaiICIAFJDQAgAkEASA0AIAJFDQEgAkECEIoCIgMNAiACQQJBgLnAACgCACIAQdQAIAAbEQEAAAsQkQIAC0ECIQMLIAAgATYCBCAAIAM2AgALVQEBfyAAIABBCGoiAEEHakF4cSAAayICaiEAQai8wAAgASACayIBNgIAQbC8wAAgADYCACAAIAFBAXI2AgQgACABakEoNgIEQcy8wABBgICAATYCAAtvAQR/IwBBIGsiAiQAQQEhAwJAIAAgARAvDQAgAUEcaigCACEEIAEoAhggAkEcakEANgIAIAJB0J3AADYCGCACQgE3AgwgAkHUncAANgIIIAQgAkEIahAdDQAgAEEEaiABEC8hAwsgAkEgaiQAIAMLcQECfyAAKAIYIAAoAjgiAWshAiAAIAEgASACIAAoAgBBmIzAACAAKAIIGy8BACIBQQEgARsiASACIAFJG2oQWiAAQYwBaigCACICIAAoAjwiAU0EQCABIAJB3IzAABCZAQALIAAoAoQBIAFqQQE6AAALcgEBfyMAQTBrIgIkACACIAE2AgQgAiAANgIAIAJBHGpBAjYCACACQSxqQeEANgIAIAJCAzcCDCACQdScwAA2AgggAkHhADYCJCACIAJBIGo2AhggAiACQQRqNgIoIAIgAjYCICACQQhqQeycwAAQ6AEAC3IBAX8jAEEwayICJAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakHhADYCACACQgM3AgwgAkGQncAANgIIIAJB4QA2AiQgAiACQSBqNgIYIAIgAkEEajYCKCACIAI2AiAgAkEIakHoi8AAEOgBAAtvAQF/IwBBMGsiAyQAIAMgATYCBCADIAA2AgAgA0EcakECNgIAIANBLGpB4QA2AgAgA0ICNwIMIANBzJ7AADYCCCADQeEANgIkIAMgA0EgajYCGCADIAM2AiggAyADQQRqNgIgIANBCGogAhDoAQALbwEBfyMAQTBrIgMkACADIAE2AgQgAyAANgIAIANBHGpBAjYCACADQSxqQeEANgIAIANCAjcCDCADQeCiwAA2AgggA0HhADYCJCADIANBIGo2AhggAyADQQRqNgIoIAMgAzYCICADQQhqIAIQ6AEAC28BAX8jAEEwayIDJAAgAyABNgIEIAMgADYCACADQRxqQQI2AgAgA0EsakHhADYCACADQgI3AgwgA0GAo8AANgIIIANB4QA2AiQgAyADQSBqNgIYIAMgA0EEajYCKCADIAM2AiAgA0EIaiACEOgBAAtvAQF/IwBBMGsiAyQAIAMgATYCBCADIAA2AgAgA0EcakECNgIAIANBLGpB4QA2AgAgA0ICNwIMIANBtKPAADYCCCADQeEANgIkIAMgA0EgajYCGCADIANBBGo2AiggAyADNgIgIANBCGogAhDoAQALWAEDfyMAQSBrIgIkACABQRxqKAIAIQMgASgCGCACQQhqIgFBEGogAEEQaikCADcDACABQQhqIABBCGopAgA3AwAgAiAAKQIANwMIIAMgARAdIAJBIGokAAtYAQN/IwBBIGsiAiQAIABBHGooAgAhAyAAKAIYIAJBCGoiAEEQaiABQRBqKQIANwMAIABBCGogAUEIaikCADcDACACIAEpAgA3AwggAyAAEB0gAkEgaiQAC1kBAn8gACgCCCIDIAFPBEAgAyAAKAIERgRAIAAgAxC8AQsgACgCACABQQJ0aiIEQQRqIAQgAyABa0ECdBAZIAAgA0EBajYCCCAEIAI2AgAPCyABIAMQlwEAC1sBAX8jAEEgayICJAAgAiAAKAIANgIEIAJBCGoiAEEQaiABQRBqKQIANwMAIABBCGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakHwmMAAIAAQHSACQSBqJAALWwEBfyMAQSBrIgIkACACIAAoAgA2AgQgAkEIaiIAQRBqIAFBEGopAgA3AwAgAEEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQcCawAAgABAdIAJBIGokAAtbAQF/IwBBIGsiAiQAIAIgACgCADYCBCACQQhqIgBBEGogAUEQaikCADcDACAAQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpBuJ3AACAAEB0gAkEgaiQAC1sBAX8jAEEgayICJAAgAiAAKAIANgIEIAJBCGoiAEEQaiABQRBqKQIANwMAIABBCGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakHYocAAIAAQHSACQSBqJAALWAEBfyMAQSBrIgIkACACIAA2AgQgAkEIaiIAQRBqIAFBEGopAgA3AwAgAEEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQfCYwAAgABAdIAJBIGokAAtYAQF/IwBBIGsiAiQAIAIgADYCBCACQQhqIgBBEGogAUEQaikCADcDACAAQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpB2KHAACAAEB0gAkEgaiQAC58BAQF/IwBBEGsiAyQAIAMgAq1CgICAgBBCACACKAIYQd+fwABBASACQRxqKAIAKAIMEQIAG4Q3AwAgAQRAIAFBFGwhAQNAIAMgADYCDCADIANBDGpBiJbAABCVAiAAQRRqIQAgAUFsaiIBDQALCyADLQAEBH9BAQUgAygCACIAKAIYQeCfwABBASAAQRxqKAIAKAIMEQIACyADQRBqJAALUAEBfwJAAkAgAUEATgRAIAFFDQEgAUEBEIoCIgINAiABQQFBgLnAACgCACIAQdQAIAAbEQEAAAsQkQIAC0EBIQILIAAgATYCBCAAIAI2AgALnwEBAX8jAEEQayIDJAAgAyACrUKAgICAEEIAIAIoAhhB35/AAEEBIAJBHGooAgAoAgwRAgAbhDcDACABBEAgAUECdCEBA0AgAyAANgIMIAMgA0EMakG4lsAAEJUCIABBBGohACABQXxqIgENAAsLIAMtAAQEf0EBBSADKAIAIgAoAhhB4J/AAEEBIABBHGooAgAoAgwRAgALIANBEGokAAufAQEBfyMAQRBrIgMkACADIAKtQoCAgIAQQgAgAigCGEHfn8AAQQEgAkEcaigCACgCDBECABuENwMAIAEEQCABQQF0IQEDQCADIAA2AgwgAyADQQxqQeiVwAAQlQIgAEECaiEAIAFBfmoiAQ0ACwsgAy0ABAR/QQEFIAMoAgAiACgCGEHgn8AAQQEgAEEcaigCACgCDBECAAsgA0EQaiQAC58BAQF/IwBBEGsiAyQAIAMgAq1CgICAgBBCACACKAIYQd+fwABBASACQRxqKAIAKAIMEQIAG4Q3AwAgAQRAIAFBAnQhAQNAIAMgADYCDCADIANBDGpBqJbAABCVAiAAQQRqIQAgAUF8aiIBDQALCyADLQAEBH9BAQUgAygCACIAKAIYQeCfwABBASAAQRxqKAIAKAIMEQIACyADQRBqJAALWQEBfyMAQRBrIgQkACAEQQhqIAEgAkECEIABIAACfyAEKAIIRQRAIAQgASADEDMgBCgCACECIAQoAgQMAQtBASECIAQoAgwLNgIEIAAgAjYCACAEQRBqJAALVwEBfyMAQRBrIgQkACAEQQhqIAEgAiADEIABIAACfyAEKAIIRQRAIAQgARAyIAQoAgAhAiAEKAIEDAELQQEhAiAEKAIMCzYCBCAAIAI2AgAgBEEQaiQAC1sBAX8CQCACIAFPBEAgAEGMAWooAgAiAyACSQ0BIAEgAkcEQCAAKAKEASIAIAFqIgFBASAAIAJqIAFrEFwLDwsgASACQcyMwAAQnAEACyACIANBzIzAABCbAQALnQEBAX8jAEEQayIDJAAgAyACrUKAgICAEEIAIAIoAhhB35/AAEEBIAJBHGooAgAoAgwRAgAbhDcDACABQQxsIgEEQANAIAMgADYCDCADIANBDGpBmJbAABCVAiAAQQxqIQAgAUF0aiIBDQALCyADLQAEBH9BAQUgAygCACIAKAIYQeCfwABBASAAQRxqKAIAKAIMEQIACyADQRBqJAALeQEDfyMAQSBrIgEkACABQRBqIgIhAyACAn9BACAALQCSAUUNABogAyAAKQI4NwIEQQELNgIAIAFBCGogAhCPASABKAIMIQAgASgCCARAIAEgADYCHEGghMAAQSsgAUEcakHMhMAAQZCEwAAQigEACyABQSBqJAAgAAtaAQF/AkAgACgCPCIBIAAoAlBHBEAgASAAKAIcQX9qTw0BIABBADoApgEgACABQQFqNgI8IAAgACgCGEF/aiIBIAAoAjgiACAAIAFLGzYCOA8LIABBARCCAQsLTgECfyAAKAIIIgEEQCAAKAIAIQAgAUEMbCEBA0ACQCAAQQRqKAIAIgJFDQAgAkEUbEUNACAAKAIAEBMLIABBDGohACABQXRqIgENAAsLC1kBAn8gAEEAOgCmASAAQQA2AjggACAAKAI8IgIgACgCAEGYjMAAIAAoAggbLwEAIgFBASABG2siAUEAIAFBAEobIAEgACgCTCIAIAEgAEobIAIgAEkbNgI8C0kBAn8gACgCCCICIAFLBEAgACgCACABQQJ0aiIDKAIAGiADIANBBGogAUF/cyACakECdBAZIAAgAkF/ajYCCA8LIAEgAhCYAQALZQEBfyMAQRBrIgIkAAJ/IAAtAABBAkYEQCABKAIYQdyWwABBBCABQRxqKAIAKAIMEQIADAELIAIgAUHIlsAAQQQQ4gEgAiAANgIMIAIgAkEMakHMlsAAEDcgAhB2CyACQRBqJAALmAEBAX8jAEEQayIDJAAgAyACrUKAgICAEEIAIAIoAhhB35/AAEEBIAJBHGooAgAoAgwRAgAbhDcDACABBEADQCADIAA2AgwgAyADQQxqQfiVwAAQlQIgAEEBaiEAIAFBf2oiAQ0ACwsgAy0ABAR/QQEFIAMoAgAiACgCGEHgn8AAQQEgAEEcaigCACgCDBECAAsgA0EQaiQAC08BAX8jAEGwAWsiAiQAAkAgAQRAIAEoAgANASABQQA2AgAgACACIAFBrAEQLCIAQQRyQagBECwaIAEQEyAAQbABaiQADwsQpQIACxCmAgALUgEBfyMAQRBrIgIkACACIAFBgI/AAEEEEOIBIAIgADYCDCACIAJBDGoiAUGEj8AAEDcgAiAAQQRqNgIMIAIgAUGUj8AAEDcgAhB2IAJBEGokAAtIAQN/IwBBEGsiAiQAIAIgATYCDEEBIQMgAkEMaigCABAHQQFGIAIoAgwhAQRAQQAhAwsgACABNgIEIAAgAzYCACACQRBqJAALUAECfyAAKAIAIgNBCGoiBCgCACEAIANBBGooAgAgAGsgAkkEQCADIAAgAhBWIAQoAgAhAAsgAygCACAAaiABIAIQLBogBCAAIAJqNgIAQQALWQEBfyMAQRBrIgMkACADIAAgASACEEYCQCADKAIAQQFGBEAgA0EIaigCACIARQ0BIAMoAgQgAEGAucAAKAIAIgBB1AAgABsRAQAACyADQRBqJAAPCxCRAgALWQEBfyMAQRBrIgMkACADIAAgASACEFICQCADKAIAQQFGBEAgA0EIaigCACIARQ0BIAMoAgQgAEGAucAAKAIAIgBB1AAgABsRAQAACyADQRBqJAAPCxCRAgALVwEBfyMAQRBrIgIkACACIAAgARBKAkAgAigCAEEBRgRAIAJBCGooAgAiAEUNASACKAIEIABBgLnAACgCACIAQdQAIAAbEQEAAAsgAkEQaiQADwsQkQIAC1kBAX8jAEEQayIDJAAgAyAAIAEgAhBHAkAgAygCAEEBRgRAIANBCGooAgAiAEUNASADKAIEIABBgLnAACgCACIAQdQAIAAbEQEAAAsgA0EQaiQADwsQkQIAC1cBAX8jAEEQayICJAAgAiAAIAEQTQJAIAIoAgBBAUYEQCACQQhqKAIAIgBFDQEgAigCBCAAQYC5wAAoAgAiAEHUACAAGxEBAAALIAJBEGokAA8LEJECAAtXAQF/IwBBEGsiAiQAIAIgACABEEgCQCACKAIAQQFGBEAgAkEIaigCACIARQ0BIAIoAgQgAEGAucAAKAIAIgBB1AAgABsRAQAACyACQRBqJAAPCxCRAgALRQEBfyAAKAIEIAAoAggiA2sgAiABayICSQRAIAAgAyACELsBIAAoAgghAwsgACgCACADaiABIAIQLBogACACIANqNgIIC1kBAX8jAEEQayICJAAgAiAAIAFBARBSAkAgAigCAEEBRgRAIAJBCGooAgAiAEUNASACKAIEIABBgLnAACgCACIAQdQAIAAbEQEAAAsgAkEQaiQADwsQkQIAC1cBAX8jAEEQayICJAAgAiAAIAEQWwJAIAIoAgBBAUYEQCACQQhqKAIAIgBFDQEgAigCBCAAQYC5wAAoAgAiAEHUACAAGxEBAAALIAJBEGokAA8LEJECAAtIAQF/An8gAS0AAEUEQBAFDAELQQEhAxAGCyECIAAgATYCBCAAQQA2AgAgAEEQakEANgIAIABBDGogAjYCACAAQQhqIAM2AgALVwEBfyMAQRBrIgIkACACIAAgARByAkAgAigCAEEBRgRAIAJBCGooAgAiAEUNASACKAIEIABBgLnAACgCACIAQdQAIAAbEQEAAAsgAkEQaiQADwsQkQIAC1cBAX8jAEEQayICJAAgAiAAIAEQfwJAIAIoAgBBAUYEQCACQQhqKAIAIgBFDQEgAigCBCAAQYC5wAAoAgAiAEHUACAAGxEBAAALIAJBEGokAA8LEJECAAtLAAJAAn8gAUGAgMQARwRAQQEgACgCGCABIABBHGooAgAoAhARAAANARoLIAINAUEACw8LIAAoAhggAiADIABBHGooAgAoAgwRAgALQgEBfwJAIAAEQCAAKAIAIgJBf0YNASAAIAJBAWo2AgAgAEEEaiABEIgBIAAgACgCAEF/ajYCAA8LEKUCAAsQpgIAC0kBAX8gAEEANgIIIAAoAgRFBEAgAEEAEL4BIAAoAgghAQsgACgCACABQQF0akEAOwEAIABBFGpBADYCACAAIAAoAghBAWo2AggLSAEBfyAAQRRqKAIAIgIgAEEQaigCAEYEQCAAQQxqIAIQvAEgACgCFCECCyAAKAIMIAJBAnRqIAE2AgAgACAAKAIUQQFqNgIUC0sBAX8gAEEAOgCmASAAQQAgACgCOCAAKAIAQZiMwAAgACgCCBsvAQAiAUEBIAEbaiIBIAAoAhgiAEF/aiABIABJGyABQQBIGzYCOAtLAQF/IABBADoApgEgAEEAIAAoAjggACgCAEGYjMAAIAAoAggbLwEAIgFBASABG2siASAAKAIYIgBBf2ogASAASRsgAUEASBs2AjgLQAEBfwJAIAAEQCAAKAIAIgFBf0YNASAAIAFBAWo2AgAgAEEEahCvASAAIAAoAgBBf2o2AgAPCxClAgALEKYCAAtIAQF/IwBBIGsiAyQAIANBFGpBADYCACADQdCdwAA2AhAgA0IBNwIEIAMgATYCHCADIAA2AhggAyADQRhqNgIAIAMgAhDoAQALPQEBfyMAQRBrIgEkACABIAAQ1wECQCABKAIAIgBFDQAgASgCBEUNACABQQhqKAIAGiAAEBMLIAFBEGokAAs7AQF/IwBBEGsiAiQAIAIgAUGghsAAQQUQ4gEgAiAANgIMIAIgAkEMakGohsAAEDcgAhB2IAJBEGokAAtWAQJ/IAEoAgQhAiABKAIAIQNBCEEEEIoCIgFFBEBBCEEEQYC5wAAoAgAiAEHUACAAGxEBAAALIAEgAjYCBCABIAM2AgAgAEGImsAANgIEIAAgATYCAAs0AQF/IAAoAggiAQRAIAAoAgAhACABQRxsIQEDQCAAEOwBIABBHGohACABQWRqIgENAAsLCzoBAX8gAUEIaigCACECAkAgASgCDEUNACABQRBqKAIAIgFBJEkNACABEAALIAAgAjYCBCAAQQA2AgALOAEBfyABKAIEIgIEQCABKAIAIQEgAEEIakEENgIAIAAgAkECdDYCBCAAIAE2AgAPCyAAQQA2AgALOQEBfyABQRB2QAAhAiAAQQA2AgggAEEAIAFBgIB8cSACQX9GIgEbNgIEIABBACACQRB0IAEbNgIACzMBAX8jAEHQAmsiAiQAIAIgACABEB4gAkGoAWoiACACQagBECwaIAAQ2gEgAkHQAmokAAtrAQN/IwBBEGsiASQAIAAoAgwiAkUEQEGQmcAAQStB2JnAABDNAQALIAAoAggiA0UEQEGQmcAAQStB6JnAABDNAQALIAEgAjYCCCABIAA2AgQgASADNgIAIAEoAgAgASgCBCABKAIIEIEBAAs0AQF/IAEoAgQiAgRAIABBCGpBBDYCACAAIAJBHGw2AgQgACABKAIANgIADwsgAEEANgIACysAAkAgAEF8Sw0AIABFBEBBBA8LIAAgAEF9SUECdBCKAiIARQ0AIAAPCwALPwACQAJAAkACQCAAKAIAQZiMwAAgACgCCBsvAQAOBgADAQMDAgMLIAAQcQ8LIAAQdw8LIABByABqQQA2AgALC0EBAX9BrAFBBBCKAiIBRQRAQawBQQRBgLnAACgCACIAQdQAIAAbEQEAAAsgAUEANgIAIAFBBGogAEGoARAsGiABCy4AIAEgAk8EQCABIAJrIgEgAUEUbCAAaiACECEPC0H4k8AAQSFBnJTAABDNAQALMAEBfyABQShqKAIAIgMgAk0EQCACIANBvIzAABCZAQALIAAgASgCICACQQxsahAQCy4AIAEgAk8EQCABIAJrIgEgAUEMbCAAaiACEBEPC0G0lcAAQSFB2JXAABDNAQALPAAgACgCACEAIAEtAABBEHFBBHZFBEAgAS0AAEEgcUEFdkUEQCAAIAEQlAIPCyAAIAEQbQ8LIAAgARBsCzwBAn8jAEEQayICJAAgAkEIaiIDIAAoAgg2AgQgAyAAKAIANgIAIAIoAgggAigCDCABEKkCIAJBEGokAAs+ACAAKAIAIQAgAS0AAEEQcUEEdkUEQCABLQAAQSBxQQV2RQRAIAAzAQAgARAuDwsgACABEGsPCyAAIAEQaAs8ACAAKAIAIQAgAS0AAEEQcUEEdkUEQCABLQAAQSBxQQV2RQRAIAAgARCXAg8LIAAgARBqDwsgACABEGkLNAAgACABKAIYIAIgAyABQRxqKAIAKAIMEQIAOgAIIAAgATYCACAAIANFOgAJIABBADYCBAssACABIAJPBEAgAiACQRRsIABqIAEgAmsQIQ8LQfiSwABBI0Hok8AAEM0BAAssACABIAJPBEAgAiACQQxsIABqIAEgAmsQEQ8LQbSUwABBI0GklcAAEM0BAAsyAQF/IAAgASgCBCABKAIIIgJLBH8gASACEMUBIAEoAggFIAILNgIEIAAgASgCADYCAAsqACAAIAAoAgRBAXEgAXJBAnI2AgQgACABakEEaiIAIAAoAgBBAXI2AgALIAEBfwJAIAAoAgQiAUUNACAAKAIAIAFBAnRFDQAQEwsLNQEBfyMAQRBrIgIkACACIAE2AgwgAiAANgIIIAJBiJ7AADYCBCACQdCdwAA2AgAgAhDWAQALNAAgAEEDOgAgIABCgICAgIAENwIAIAAgATYCGCAAQQA2AhAgAEEANgIIIABBHGogAjYCAAs1AAJAAkACQCAAKAIAQZiMwAAgACgCCBsvAQAOBAACAgECCyAAEHcPCyAAQcgAakEANgIACwsyAQF/IAAgACgCAEGYjMAAIAAoAggbLwEAIgFBASABGxCQASAAQQA6AKYBIABBADYCOAsgAQF/AkAgACgCBCIBRQ0AIAFBAnRFDQAgACgCABATCwsgAQF/AkAgACgCBCIBRQ0AIAFBDGxFDQAgACgCABATCwseAAJAIABBBGooAgBFDQAgACgCACIARQ0AIAAQEwsLIAEBfwJAIAAoAgQiAUUNACAAQQhqKAIARQ0AIAEQEwsLHwACQCABQXxNBEAgACABQQQgAhD+ASIADQELAAsgAAslACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgBSABKAIQEQsAC0IAIAAtAABBAUcEQCABKAIYQcGPwABBByABQRxqKAIAKAIMEQIADwsgASgCGEG4j8AAQQkgAUEcaigCACgCDBECAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBEFAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBESAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBERAAsjACAARQRAQfyIwABBMBCkAgALIAAgAiADIAQgASgCEBEJAAsgAQF/EAQhAiAAIAE2AgQgAEEANgIAIABBCGogAjYCAAshACAARQRAQfyIwABBMBCkAgALIAAgAiADIAEoAhARAwALHwAgAEUEQEH8iMAAQTAQpAIACyAAIAIgASgCEBEAAAstACABKAIYQbSPwABBto/AACAAKAIALQAAQQFGG0ECIAFBHGooAgAoAgwRAgALEQAgACgCBARAIAAoAgAQEwsLHAAgASgCGEGQssAAQQUgAUEcaigCACgCDBECAAsTACAAKAIAIgBBJE8EQCAAEAALCwwAIAAgASACIAMQFwsUACAAIAIgAxACNgIEIABBADYCAAsWACAAKAIAIgAoAgAgACgCCCABEKoBCxYAIAAoAgAiACgCACAAKAIIIAEQqQELFgAgACgCACIAKAIAIAAoAgggARC1AQsWACAAKAIAIgAoAgAgACgCCCABEKgBCxYAIAAoAgAiACgCACAAKAIIIAEQrgELFgAgACgCACIAKAIAIAAoAgggARCmAQsLACABBEAgABATCwsTACAAKAIAIAEgASACahDAAUEACxMAIAAoAgAgASgCACACKAIAEAoLFAAgACgCACABIAAoAgQoAgwRAAALCAAgACABECQLDgAgACABIAEgAmoQwAELEwAgAEGImsAANgIEIAAgATYCAAsQACABIAAoAgAgACgCBBAVCw0AIAAgASACEIsCQQALDQAgACgCACABIAIQAwsPACAAKAIAIAEoAgAQCBoLEgBB9JrAAEERQYibwAAQzQEACw0AIAAoAgAgARArQQALDgAgACgCABoDQAwACwALCwAgADUCACABEC4LCgAgACABIAIQOgsNACAAKAIAIAEgAhAYCwsAIAAxAAAgARAuCwsAIAApAwAgARAuCwsAIwAgAGokACMACwcAIAAQ+wELDAAgACgCACABEJcCCwsAIAAoAgAgARBwCwsAIAAoAgAgARBeCwwAIAAoAgAgARC3AQsMACAAKAIAIAEQ8gELCwAgACgCACABEF8LCwAgACgCACABEEMLCwAgACgCACABEGYLDAAgACgCACABELQBCwkAIAAgARAMAAsNAEGEl8AAQRsQpAIACw4AQZ+XwABBzwAQpAIACwsAIAAoAgAgARAjCykAAn8gACgCAC0AAEUEQCABQfShwABBBRAVDAELIAFB8KHAAEEEEBULCwoAIAIgACABEBULCAAgACABEAkLDQBC9Pme5u6jqvn+AAsMAELRy/+wrqSi1goLDABCwPTl+cSQy/10CwMAAQsDAAELC+Q4AQBBgIDAAAvaOC9ob21lL3J1bm5lci8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9zZXJkZS13YXNtLWJpbmRnZW4tMC4zLjEvc3JjL3Nlci5ycwAAEABgAAAAmwAAACgAAABNYXAga2V5IGlzIG5vdCBhIHN0cmluZyBhbmQgY2Fubm90IGJlIGFuIG9iamVjdCBrZXljYWxsZWQgYE9wdGlvbjo6dW53cmFwKClgIG9uIGEgYE5vbmVgIHZhbHVlZmdiZ2JvbGQBaXRhbGljdW5kZXJsaW5lc3RyaWtldGhyb3VnaGJsaW5raW52ZXJzZXJnYigsKQAAAP8AEAAEAAAAAwEQAAEAAAADARAAAQAAAAQBEAABAAAAAgAAAAwAAAAEAAAAAwAAAAQAAAAFAAAAYSBEaXNwbGF5IGltcGxlbWVudGF0aW9uIHJldHVybmVkIGFuIGVycm9yIHVuZXhwZWN0ZWRseS9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3N0cmluZy5ycwAAdwEQAEsAAABfCQAADgAAAAEAAAAAAAAABgAAAAAAAAABAAAABwAAAOwBEAAAAAAAc3JjL2xpYi5ycwAA9AEQAAoAAAAjAAAALQAAAPQBEAAKAAAAKAAAAC8AAABjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlAAkAAAAEAAAABAAAAAoAAABUcmllZCB0byBzaHJpbmsgdG8gYSBsYXJnZXIgY2FwYWNpdHkvcnVzdGMvOWQxYjIxMDZlMjNiMWFiZDMyZmNlMWYxNzI2NzYwNGE1MTAyZjU3YS9saWJyYXJ5L2FsbG9jL3NyYy9yYXdfdmVjLnJzgAIQAEwAAACrAQAACQAAAAEAAAAAAAAAIGNhbid0IGJlIHJlcHJlc2VudGVkIGFzIGEgSmF2YVNjcmlwdCBudW1iZXLkAhAAAAAAAOQCEAAsAAAARXJyb3IAAAAMAAAABAAAAAQAAAANAAAADgAAAAwAAAAEAAAADwAAABAAAAAFAAAAYSBEaXNwbGF5IGltcGxlbWVudGF0aW9uIHJldHVybmVkIGFuIGVycm9yIHVuZXhwZWN0ZWRseS9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3N0cmluZy5ycwAAhwMQAEsAAABfCQAADgAAAAEAAAAAAAAAEQAAAAAAAAABAAAABwAAAC9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMucnNUcmllZCB0byBzaHJpbmsgdG8gYSBsYXJnZXIgY2FwYWNpdHn8AxAATAAAAKsBAAAJAAAAY2xvc3VyZSBpbnZva2VkIHJlY3Vyc2l2ZWx5IG9yIGRlc3Ryb3llZCBhbHJlYWR5ZiYAAJIlAAAJJAAADCQAAA0kAAAKJAAAsAAAALEAAAAkJAAACyQAABglAAAQJQAADCUAABQlAAA8JQAAuiMAALsjAAAAJQAAvCMAAL0jAAAcJQAAJCUAADQlAAAsJQAAAiUAAGQiAABlIgAAwAMAAGAiAACjAAAAxSIAAHZ0LXJzL3NyYy9saWIucnMoBRAAEAAAAEsCAAARAAAAKAUQABAAAABuAgAAGgAAACgFEAAQAAAA7AIAABoAAAAoBRAAEAAAAO8CAAAaAAAAKAUQABAAAABUAwAADQAAACgFEAAQAAAAWQMAAA0AAAAoBRAAEAAAAGUDAAANAAAAKAUQABAAAABqAwAADQAAACgFEAAQAAAAdwMAAAkAAAAoBRAAEAAAAJcDAAAYAAAAKAUQABAAAACwBAAACQAAACgFEAAQAAAAvgQAACQAAAAoBRAAEAAAAMoEAAAaAAAAKAUQABAAAADSBAAAGgAAAAAAAAAoBRAAEAAAAGkFAAAJAAAAKAUQABAAAABxBQAACQAAACgFEAAQAAAAtQUAABoAAAAoBRAAEAAAANgFAAAXAAAAKAUQABAAAADeBQAACQAAAFNvc1BtQXBjU3RyaW5nT3NjU3RyaW5nRGNzSWdub3JlRGNzUGFzc3Rocm91Z2hEY3NJbnRlcm1lZGlhdGVEY3NQYXJhbURjc0VudHJ5Q3NpSWdub3JlQ3NpSW50ZXJtZWRpYXRlQ3NpUGFyYW1Dc2lFbnRyeUVzY2FwZUludGVybWVkaWF0ZUVzY2FwZUdyb3VuZFJHQgAAJgAAAAQAAAAEAAAAJwAAAEluZGV4ZWRQZW5mb3JlZ3JvdW5kKAAAAAQAAAAEAAAAKQAAAGJhY2tncm91bmRib2xkAAAqAAAABAAAAAQAAAArAAAAaXRhbGljdW5kZXJsaW5lc3RyaWtldGhyb3VnaGJsaW5raW52ZXJzZUNlbGwsAAAABAAAAAQAAAAtAAAALgAAAAQAAAAEAAAALwAAADAAAAAEAAAABAAAADEAAABHMUcwQWx0ZXJuYXRlUHJpbWFyeVNhdmVkQ3R4Y3Vyc29yX3gyAAAABAAAAAQAAAAzAAAAY3Vyc29yX3lwZW5vcmlnaW5fbW9kZWF1dG9fd3JhcF9tb2RlVlRzdGF0ZQA0AAAABAAAAAQAAAA1AAAAcGFyYW1zAAA2AAAABAAAAAQAAAA3AAAAaW50ZXJtZWRpYXRlc2NvbHVtbnNyb3dzYnVmZmVyAAA4AAAABAAAAAQAAAA5AAAAYWx0ZXJuYXRlX2J1ZmZlcmFjdGl2ZV9idWZmZXJfdHlwZQAAOgAAAAQAAAAEAAAAOwAAAGN1cnNvcl92aXNpYmxlY2hhcnNldAAAADwAAAAEAAAABAAAAD0AAAB0YWJzPgAAAAQAAAAEAAAAPwAAAGluc2VydF9tb2RlbmV3X2xpbmVfbW9kZW5leHRfcHJpbnRfd3JhcHN0b3BfbWFyZ2luYm90dG9tX21hcmdpbnNhdmVkX2N0eEAAAAAEAAAABAAAAEEAAABhbHRlcm5hdGVfc2F2ZWRfY3R4YWZmZWN0ZWRfbGluZXMAAABCAAAABAAAAAQAAABDAAAABAAAAAAAAAACAAAAAAAAAGFzc2VydGlvbiBmYWlsZWQ6IG1pZCA8PSBzZWxmLmxlbigpL3J1c3RjLzlkMWIyMTA2ZTIzYjFhYmQzMmZjZTFmMTcyNjc2MDRhNTEwMmY1N2EvbGlicmFyeS9jb3JlL3NyYy9zbGljZS9tb2QucnObCRAATQAAAKILAAAJAAAAYXNzZXJ0aW9uIGZhaWxlZDogayA8PSBzZWxmLmxlbigpAAAAmwkQAE0AAADNCwAACQAAAAQAAAAAAAAAYXNzZXJ0aW9uIGZhaWxlZDogbWlkIDw9IHNlbGYubGVuKCkvcnVzdGMvOWQxYjIxMDZlMjNiMWFiZDMyZmNlMWYxNzI2NzYwNGE1MTAyZjU3YS9saWJyYXJ5L2NvcmUvc3JjL3NsaWNlL21vZC5yc1cKEABNAAAAogsAAAkAAABhc3NlcnRpb24gZmFpbGVkOiBrIDw9IHNlbGYubGVuKCkAAABXChAATQAAAM0LAAAJAAAARAAAAAQAAAAEAAAARQAAAEYAAAAEAAAABAAAACsAAABHAAAABAAAAAQAAABIAAAASQAAAAQAAAAEAAAASgAAAEsAAAAEAAAABAAAADMAAABMAAAABAAAAAQAAAAtAAAAU29tZU0AAAAEAAAABAAAAE4AAABOb25lSnNWYWx1ZSgpAAAAYAsQAAgAAABoCxAAAQAAAAQAAAAAAAAAbnVsbCBwb2ludGVyIHBhc3NlZCB0byBydXN0cmVjdXJzaXZlIHVzZSBvZiBhbiBvYmplY3QgZGV0ZWN0ZWQgd2hpY2ggd291bGQgbGVhZCB0byB1bnNhZmUgYWxpYXNpbmcgaW4gcnVzdC9ydXN0Yy85ZDFiMjEwNmUyM2IxYWJkMzJmY2UxZjE3MjY3NjA0YTUxMDJmNTdhL2xpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMucnNUcmllZCB0byBzaHJpbmsgdG8gYSBsYXJnZXIgY2FwYWNpdHkAAO4LEABMAAAAqwEAAAkAAABQAAAABAAAAAQAAABRAAAAUgAAAFMAAAABAAAAAAAAAGNhbGxlZCBgT3B0aW9uOjp1bndyYXAoKWAgb24gYSBgTm9uZWAgdmFsdWVsaWJyYXJ5L3N0ZC9zcmMvcGFuaWNraW5nLnJzALsMEAAcAAAA8AEAAB8AAAC7DBAAHAAAAPEBAAAeAAAAVQAAAAwAAAAEAAAAVgAAAFcAAAAIAAAABAAAAFgAAABZAAAAEAAAAAQAAABaAAAAWwAAAFcAAAAIAAAABAAAAFwAAABdAAAAVwAAAAQAAAAEAAAAXgAAAF8AAABgAAAAbGlicmFyeS9hbGxvYy9zcmMvcmF3X3ZlYy5yc2NhcGFjaXR5IG92ZXJmbG93AAAAWA0QABwAAAAGAgAABQAAAGEgZm9ybWF0dGluZyB0cmFpdCBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvcmxpYnJhcnkvYWxsb2Mvc3JjL2ZtdC5ycwDLDRAAGAAAAFUCAAAcAAAAKSBzaG91bGQgYmUgPCBsZW4gKGlzIClsaWJyYXJ5L2FsbG9jL3NyYy92ZWMvbW9kLnJzaW5zZXJ0aW9uIGluZGV4IChpcyApIHNob3VsZCBiZSA8PSBsZW4gKGlzIAAAJw4QABQAAAA7DhAAFwAAAAoOEAABAAAACw4QABwAAAA9BQAADQAAAHJlbW92YWwgaW5kZXggKGlzIAAAfA4QABIAAAD0DRAAFgAAAAoOEAABAAAAYgAAAAAAAAABAAAABwAAAGIAAAAEAAAABAAAAGMAAABkAAAAZQAAAC4uAADQDhAAAgAAAGNhbGxlZCBgT3B0aW9uOjp1bndyYXAoKWAgb24gYSBgTm9uZWAgdmFsdWUAawAAAAAAAAABAAAAbAAAAGluZGV4IG91dCBvZiBib3VuZHM6IHRoZSBsZW4gaXMgIGJ1dCB0aGUgaW5kZXggaXMgAAAYDxAAIAAAADgPEAASAAAAYDogANAOEAAAAAAAXQ8QAAIAAABrAAAADAAAAAQAAABtAAAAbgAAAG8AAAAgICAgbGlicmFyeS9jb3JlL3NyYy9mbXQvYnVpbGRlcnMucnOMDxAAIAAAAC8AAAAhAAAAjA8QACAAAAAwAAAAEgAAACB7CiwKLCAgeyB9IH0oCigsKQpbXWxpYnJhcnkvY29yZS9zcmMvZm10L251bS5yc+EPEAAbAAAAZQAAABQAAAAweDAwMDEwMjAzMDQwNTA2MDcwODA5MTAxMTEyMTMxNDE1MTYxNzE4MTkyMDIxMjIyMzI0MjUyNjI3MjgyOTMwMzEzMjMzMzQzNTM2MzczODM5NDA0MTQyNDM0NDQ1NDY0NzQ4NDk1MDUxNTI1MzU0NTU1NjU3NTg1OTYwNjE2MjYzNjQ2NTY2Njc2ODY5NzA3MTcyNzM3NDc1NzY3Nzc4Nzk4MDgxODI4Mzg0ODU4Njg3ODg4OTkwOTE5MjkzOTQ5NTk2OTc5ODk5AABrAAAABAAAAAQAAABwAAAAcQAAAHIAAAB0cnVlZmFsc2VsaWJyYXJ5L2NvcmUvc3JjL3NsaWNlL21lbWNoci5ycwAAAPkQEAAgAAAAWwAAAAUAAAByYW5nZSBzdGFydCBpbmRleCAgb3V0IG9mIHJhbmdlIGZvciBzbGljZSBvZiBsZW5ndGggLBEQABIAAAA+ERAAIgAAAHJhbmdlIGVuZCBpbmRleCBwERAAEAAAAD4REAAiAAAAc2xpY2UgaW5kZXggc3RhcnRzIGF0ICBidXQgZW5kcyBhdCAAkBEQABYAAACmERAADQAAAGF0dGVtcHRlZCB0byBpbmRleCBzbGljZSB1cCB0byBtYXhpbXVtIHVzaXplbGlicmFyeS9jb3JlL3NyYy9zdHIvdmFsaWRhdGlvbnMucnMA8BEQACMAAAAeAQAAEQAAAFsuLi5dYnl0ZSBpbmRleCAgaXMgb3V0IG9mIGJvdW5kcyBvZiBgAAApEhAACwAAADQSEAAWAAAAXA8QAAEAAABiZWdpbiA8PSBlbmQgKCA8PSApIHdoZW4gc2xpY2luZyBgAABkEhAADgAAAHISEAAEAAAAdhIQABAAAABcDxAAAQAAACBpcyBub3QgYSBjaGFyIGJvdW5kYXJ5OyBpdCBpcyBpbnNpZGUgIChieXRlcyApIG9mIGApEhAACwAAAKgSEAAmAAAAzhIQAAgAAADWEhAABgAAAFwPEAABAAAAbGlicmFyeS9jb3JlL3NyYy91bmljb2RlL3ByaW50YWJsZS5ycwAAAAQTEAAlAAAACgAAABwAAAAEExAAJQAAABoAAAA2AAAAAAEDBQUGBgIHBggHCREKHAsZDBoNEA4NDwQQAxISEwkWARcEGAEZAxoHGwEcAh8WIAMrAy0LLgEwAzECMgGnAqkCqgSrCPoC+wX9Av4D/wmteHmLjaIwV1iLjJAc3Q4PS0z7/C4vP1xdX+KEjY6RkqmxurvFxsnK3uTl/wAEERIpMTQ3Ojs9SUpdhI6SqbG0urvGys7P5OUABA0OERIpMTQ6O0VGSUpeZGWEkZudyc7PDREpOjtFSVdbXF5fZGWNkam0urvFyd/k5fANEUVJZGWAhLK8vr/V1/Dxg4WLpKa+v8XHzs/a20iYvc3Gzs9JTk9XWV5fiY6Psba3v8HGx9cRFhdbXPb3/v+AbXHe3w4fbm8cHV99fq6vf7u8FhceH0ZHTk9YWlxefn+1xdTV3PDx9XJzj3R1liYuL6evt7/Hz9ffmkCXmDCPH9LUzv9OT1pbBwgPECcv7u9ubzc9P0JFkJFTZ3XIydDR2Nnn/v8AIF8igt8EgkQIGwQGEYGsDoCrBR8JgRsDGQgBBC8ENAQHAwEHBgcRClAPEgdVBwMEHAoJAwgDBwMCAwMDDAQFAwsGAQ4VBU4HGwdXBwIGFg1QBEMDLQMBBBEGDww6BB0lXyBtBGolgMgFgrADGgaC/QNZBxYJGAkUDBQMagYKBhoGWQcrBUYKLAQMBAEDMQssBBoGCwOArAYKBi8xTQOApAg8Aw8DPAc4CCsFgv8RGAgvES0DIQ8hD4CMBIKXGQsViJQFLwU7BwIOGAmAviJ0DIDWGgwFgP8FgN8M8p0DNwmBXBSAuAiAywUKGDsDCgY4CEYIDAZ0Cx4DWgRZCYCDGBwKFglMBICKBqukDBcEMaEEgdomBwwFBYCmEIH1BwEgKgZMBICNBIC+AxsDDw0ABgEBAwEEAgUHBwIICAkCCgULAg4EEAERAhIFExEUARUCFwIZDRwFHQgkAWoEawKvA7wCzwLRAtQM1QnWAtcC2gHgBeEC5wToAu4g8AT4AvoC+wEMJzs+Tk+Pnp6fe4uTlqKyuoaxBgcJNj0+VvPQ0QQUGDY3Vld/qq6vvTXgEoeJjp4EDQ4REikxNDpFRklKTk9kZVy2txscBwgKCxQXNjk6qKnY2Qk3kJGoBwo7PmZpj5JvX7/u71pi9Pz/mpsuLycoVZ2goaOkp6iturzEBgsMFR06P0VRpqfMzaAHGRoiJT4/5+zv/8XGBCAjJSYoMzg6SEpMUFNVVlhaXF5gY2Vma3N4fX+KpKqvsMDQrq9ub5NeInsFAwQtA2YDAS8ugIIdAzEPHAQkCR4FKwVEBA4qgKoGJAQkBCgINAtOQ4E3CRYKCBg7RTkDYwgJMBYFIQMbBQFAOARLBS8ECgcJB0AgJwQMCTYDOgUaBwQMB1BJNzMNMwcuCAqBJlJOKAgqFhomHBQXCU4EJAlEDRkHCgZICCcJdQs/QSoGOwUKBlEGAQUQAwWAi2IeSAgKgKZeIkULCgYNEzoGCjYsBBeAuTxkUwxICQpGRRtICFMNSYEHRgodA0dJNwMOCAoGOQcKgTYZgLcBDzINg5tmdQuAxIpMYw2EL4/RgkehuYI5ByoEXAYmCkYKKAUTgrBbZUsEOQcRQAULAg6X+AiE1ioJoueBMy0DEQQIgYyJBGsFDQMJBxCSYEcJdDyA9gpzCHAVRoCaFAxXCRmAh4FHA4VCDxWEUB+A4SuA1S0DGgQCgUAfEToFAYTggPcpTAQKBAKDEURMPYDCPAYBBFUFGzQCgQ4sBGQMVgqArjgdDSwECQcCDgaAmoPYBRADDQN0DFkHDAQBDwwEOAgKBigIIk6BVAwVAwUDBwkdAwsFBgoKBggIBwmAyyUKhAZsaWJyYXJ5L2NvcmUvc3JjL3VuaWNvZGUvdW5pY29kZV9kYXRhLnJzAAAAtRgQACgAAABLAAAAKAAAALUYEAAoAAAAVwAAABYAAAC1GBAAKAAAAFIAAAA+AAAARXJyb3IAAAAAAwAAgwQgAJEFYABdE6AAEhcgHwwgYB/vLKArKjAgLG+m4CwCqGAtHvtgLgD+IDae/2A2/QHhNgEKITckDeE3qw5hOS8YoTkwHOFH8x4hTPBq4U9PbyFQnbyhUADPYVFl0aFRANohUgDg4VMw4WFVruKhVtDo4VYgAG5X8AH/VwBwAAcALQEBAQIBAgEBSAswFRABZQcCBgICAQQjAR4bWws6CQkBGAQBCQEDAQUrAzwIKhgBIDcBAQEECAQBAwcKAh0BOgEBAQIECAEJAQoCGgECAjkBBAIEAgIDAwEeAgMBCwI5AQQFAQIEARQCFgYBAToBAQIBBAgBBwMKAh4BOwEBAQwBCQEoAQMBNwEBAwUDAQQHAgsCHQE6AQIBAgEDAQUCBwILAhwCOQIBAQIECAEJAQoCHQFIAQQBAgMBAQgBUQECBwwIYgECCQsGSgIbAQEBAQE3DgEFAQIFCwEkCQFmBAEGAQICAhkCBAMQBA0BAgIGAQ8BAAMAAx0CHgIeAkACAQcIAQILCQEtAwEBdQIiAXYDBAIJAQYD2wICAToBAQcBAQEBAggGCgIBMB8xBDAHAQEFASgJDAIgBAICAQM4AQECAwEBAzoIAgKYAwENAQcEAQYBAwLGQAABwyEAA40BYCAABmkCAAQBCiACUAIAAQMBBAEZAgUBlwIaEg0BJggZCy4DMAECBAICJwFDBgICAgIMAQgBLwEzAQEDAgIFAgEBKgIIAe4BAgEEAQABABAQEAACAAHiAZUFAAMBAgUEKAMEAaUCAAQAApkLMQR7ATYPKQECAgoDMQQCAgcBPQMkBQEIPgEMAjQJCgQCAV8DAgEBAgYBoAEDCBUCOQIBAQEBFgEOBwMFwwgCAwEBFwFRAQIGAQECAQECAQLrAQIEBgIBAhsCVQgCAQECagEBAQIGAQFlAwIEAQUACQEC9QEKAgEBBAGQBAICBAEgCigGAgQIAQkGAgMuDQECAAcBBgEBUhYCBwECAQJ6BgMBAQIBBwEBSAIDAQEBAAIABTsHAAE/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&&B0){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;B0&&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&&B0?{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);Q1&&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;g2&&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='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'}}]); \ 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=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='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'}}]); \ 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=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(word):\n\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=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")),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;h126){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&&++a1)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;e0&&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;yt)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=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.index8&&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.start0&&(s+=";"),0!==o.length){for(var p=0,h=[],c=0,l=o;c1&&(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;s1){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;ee)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;i1&&(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];rthis.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;i0&&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.startthis.depth){this.shouldRewriteAsFunction=!0;for(var o=0,p=this.thisRefs;oe.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;n0&&(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;h0?(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||pL&&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(wthis.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];ar[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;on.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=/\{(?<range>[\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<o.length;){const t=o[d].match(a);if(!t){d+=1;continue}const e=t.slice(1).find((t=>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<s?1:-1;"-"!==n&&".."!==n&&"\u2025"!==n||(s+=t);for(let e=r;e!==s;e+=t)i.push(e)}}return i}e.default=i,t.exports=i},2817:(t,e,i)=>{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;e<arguments.length;e++){var i=arguments[e];for(var r in i)Object.prototype.hasOwnProperty.call(i,r)&&(t[r]=i[r])}return t},n=function(){function t(t,e){for(var i=0;i<e.length;i++){var r=e[i];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return function(e,i,r){return i&&t(e.prototype,i),r&&t(e,r),e}}(),s=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e.default=t,e}(i(7294));function a(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(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}var p=90,h=219,c=222,l=192,u=100,d=3e3,f="navigator"in i.g&&/Win/i.test(navigator.platform),m="navigator"in i.g&&/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform),g="npm__react-simple-code-editor__textarea",y="\n/**\n * Reset the text fill color so that placeholder is visible\n */\n."+g+":empty {\n -webkit-text-fill-color: inherit !important;\n}\n\n/**\n * Hack to apply on some CSS on IE10 and IE11\n */\n@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {\n /**\n * IE doesn't support '-webkit-text-fill-color'\n * So we use 'color: transparent' to make the text transparent on IE\n * Unlike other browsers, it doesn't affect caret color in IE\n */\n ."+g+" {\n color: transparent !important;\n }\n\n ."+g+"::selection {\n background-color: #accef7 !important;\n color: transparent !important;\n }\n}\n",v=function(t){function e(){var t,i,n;a(this,e);for(var s=arguments.length,g=Array(s),y=0;y<s;y++)g[y]=arguments[y];return i=n=o(this,(t=e.__proto__||Object.getPrototypeOf(e)).call.apply(t,[this].concat(g))),n.state={capture:!0},n._recordCurrentState=function(){var t=n._input;if(t){var e=t.value,i=t.selectionStart,r=t.selectionEnd;n._recordChange({value:e,selectionStart:i,selectionEnd:r})}},n._getLines=function(t,e){return t.substring(0,e).split("\n")},n._recordChange=function(t){var e=arguments.length>1&&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<d){var l=/[^a-z0-9]([a-z0-9]+)$/i,f=n._getLines(c.value,c.selectionStart).pop().match(l),m=n._getLines(t.value,t.selectionStart).pop().match(l);if(f&&m&&m[1].startsWith(f[1]))return void(n._history.stack[n._history.offset]=r({},t,{timestamp:h}))}}n._history.stack.push(r({},t,{timestamp:h})),n._history.offset++},n._updateInput=function(t){var e=n._input;e&&(e.value=t.value,e.selectionStart=t.selectionStart,e.selectionEnd=t.selectionEnd,n.props.onValueChange(t.value))},n._applyEdits=function(t){var e=n._input,i=n._history.stack[n._history.offset];i&&e&&(n._history.stack[n._history.offset]=r({},i,{selectionStart:e.selectionStart,selectionEnd:e.selectionEnd})),n._recordChange(t),n._updateInput(t)},n._undoEdit=function(){var t=n._history,e=t.stack,i=t.offset,r=e[i-1];r&&(n._updateInput(r),n._history.offset=Math.max(i-1,0))},n._redoEdit=function(){var t=n._history,e=t.stack,i=t.offset,r=e[i+1];r&&(n._updateInput(r),n._history.offset=Math.min(i+1,e.length-1))},n._handleKeyDown=function(t){var e=n.props,i=e.tabSize,r=e.insertSpaces,s=e.ignoreTabKey,a=e.onKeyDown;if(!a||(a(t),!t.defaultPrevented)){27===t.keyCode&&t.target.blur();var o=t.target,u=o.value,d=o.selectionStart,g=o.selectionEnd,y=(r?" ":"\t").repeat(i);if(9===t.keyCode&&!s&&n.state.capture)if(t.preventDefault(),t.shiftKey){var v=n._getLines(u,d),x=v.length-1,b=n._getLines(u,g).length-1,_=u.split("\n").map((function(t,e){return e>=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+"<br />"}}:{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<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},o=Object.keys(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=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<o;h++)l[h]=n[h];return a.createElement.apply(null,l)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},6290:(e,t,n)=>{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<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?l(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):l(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function r(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},l=Object.keys(e);for(a=0;a<l.length;a++)n=l[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a<l.length;a++)n=l[a],t.indexOf(n)>=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<l;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}h.displayName="MDXCreateElement"},7125:(e,t,n)=>{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<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=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<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},9323:(e,t,n)=>{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 <<EOF\nimport os\nfor r in range(3): print(os.open('/dev/random', os.O_RDONLY))\nEOF\n")),(0,i.kt)("p",null,"This code uses redirection (see how useful it is?) to pipe a small Python script into the Python program, which writes the results to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),". You will probably see the following output:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"3\n4\n5\n")),(0,i.kt)("p",null,"It doesn't really matter whether you know Python or not (and there weird looking ",(0,i.kt)("inlineCode",{parentName:"p"},"EOF")," is a ",(0,i.kt)("em",{parentName:"p"},"heredoc")," which we have a whole chapter on later). The script is just a way of showing the file descriptors that the operating system gives me when I try to open three files (each time I open the same file, the magic ",(0,i.kt)("inlineCode",{parentName:"p"},"/dev/random")," file which just contains random data)."),(0,i.kt)("p",null,"The interesting thing is that the descriptors in my program start from ",(0,i.kt)("inlineCode",{parentName:"p"},"3")," and go upwards - that's because ",(0,i.kt)("inlineCode",{parentName:"p"},"0"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"1")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"2")," are already in use, for ",(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)("p",null,"So to make our error message go through the ",(0,i.kt)("inlineCode",{parentName:"p"},"tr")," command, we can redirect ",(0,i.kt)("inlineCode",{parentName:"p"},"stderr")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout"),", which means the error message will go to ",(0,i.kt)("inlineCode",{parentName:"p"},"stdout")," and then be piped to ",(0,i.kt)("inlineCode",{parentName:"p"},"tr"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ mkdir ~/effective-shell/new-folder 2>&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<arguments.length;e++){var l=null!=arguments[e]?arguments[e]:{};e%2?s(Object(l),!0).forEach((function(e){i(t,e,l[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(l)):s(Object(l)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(l,e))}))}return t}function a(t,e){if(null==t)return{};var l,r,i=function(t,e){if(null==t)return{};var l,r,i={},s=Object.keys(t);for(r=0;r<s.length;r++)l=s[r],e.indexOf(l)>=0||(i[l]=t[l]);return i}(t,e);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);for(r=0;r<s.length;r++)l=s[r],e.indexOf(l)>=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<s;h++)n[h]=l[h];return r.createElement.apply(null,n)}return r.createElement.apply(null,l)}p.displayName="MDXCreateElement"},4216:(t,e,l)=>{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<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,r=function(e,t){if(null==e)return{};var n,a,r={},o=Object.keys(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=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<o;u++)i[u]=n[u];return a.createElement.apply(null,i)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},1773:(e,t,n)=>{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"},"<function-name> {\n <function-command 1>\n <function-command 2>\n <function-command 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<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?o(Object(a),!0).forEach((function(t){i(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):o(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function s(e,t){if(null==e)return{};var a,n,i=function(e,t){if(null==e)return{};var a,n,i={},o=Object.keys(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=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<o;h++)r[h]=a[h];return n.createElement.apply(null,r)}return n.createElement.apply(null,a)}g.displayName="MDXCreateElement"},4859:(e,t,a)=>{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 <file>..." 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 <file>..." 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 <file>..." to unstage)\n new file: install.sh\n new file: shell.sh\n\nUntracked files:\n (use "git add <file>..." 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 <file>..." 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 <file>..." 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 <new_branch_name>"),". 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 <dwmkerr@gmail.com>\nDate: Tue Jun 15 21:00:28 2021 +0800\n\n Merge branch 'more_aliases'\n\ncommit a95bd90e3656b2e55b8708193d387c80c282a6ad\nAuthor: Dave Kerr <dwmkerr@gmail.com>\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 <dwmkerr@gmail.com>\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 <file>..." 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 <file>..." 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 <file>..." to update what will be committed)\n (use "git restore <file>..." 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 <file_path>")," 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 <file>..." to update what will be committed)\n (use "git restore <file>..." to discard changes in working directory)\n deleted: install.sh\n\nUntracked files:\n (use "git add <file>..." 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 <file>..." 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 <file>..." 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 <commit_sha>"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout f61369d\nNote: switching to 'f61369d'.\n\nYou are in 'detached HEAD' state. You can look around, make experimental\nchanges and commit them, and you can discard any commits you make in this\nstate without impacting any branches by switching back to a branch.\n")),(0,i.kt)("p",null,"This snippet moved our HEAD to the third commit in our repository. If you look at the files in your working tree you'll see that they are in exact state that they were when we made our third commit. Visually, we have moved the HEAD as shown below:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"Diagram showing a detached HEAD",src:a(7107).Z,width:"2486",height:"742"})),(0,i.kt)("p",null,"The warning that says we are in a 'detached HEAD' state is telling us that our current position in the history is not at the 'tip' of a branch - this means we cannot create a new commit without first starting a new branch."),(0,i.kt)("p",null,"If we want to move back to the 'tip' of our main branch (or any other branch), we can use ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout <branch_name>"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout main\n")),(0,i.kt)("p",null,"This moves us back to the 'tip' of the main branch."),(0,i.kt)("p",null,"Git has a very convenient syntax we can use to move backwards. We can specify a branch name, a SHA or 'HEAD', use a ",(0,i.kt)("inlineCode",{parentName:"p"},"~")," tilde character, and then provide the number of commits we want to move 'backwards':"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"$ git checkout HEAD~1\n")),(0,i.kt)("p",null,"This command moves the 'head' backwards one commit. This is useful if you realise you have made a mistake with your commit or recent commits and want to go backwards."),(0,i.kt)("p",null,"Remember - whenever you run ",(0,i.kt)("inlineCode",{parentName:"p"},"git checkout <branch_name>")," Git moves the HEAD to the tip of the branch by default."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"In this chapter we looked at some of the core concepts behind Git - the repository, the working tree and the index. We saw how to stage and unstage changes, commit changes, create branches, merge branches, deal with conflicts and remove and rename files."),(0,i.kt)("p",null,"In the next chapter we will look at how to work with 'remote' repositories - this will allow us to push our changes up to a location on the internet and share these changes with other users, or to download these changes to other machines we might work on."),(0,i.kt)("p",null,"We've introduced a lot of commands in this chapter - you can use this table as a quick reference:"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Command"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git init")),(0,i.kt)("td",{parentName:"tr",align:null},"Initialise a new Git Repository.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git status")),(0,i.kt)("td",{parentName:"tr",align:null},"Show the status of the working tree and index.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git add <files>")),(0,i.kt)("td",{parentName:"tr",align:null},"Stage ",(0,i.kt)("inlineCode",{parentName:"td"},"files")," - you can use patterns and wildcards.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git reset <files>")),(0,i.kt)("td",{parentName:"tr",align:null},"Unstage ",(0,i.kt)("inlineCode",{parentName:"td"},"files")," - you can use patterns and wildcards.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git rm --cached <files>")),(0,i.kt)("td",{parentName:"tr",align:null},"Unstage ",(0,i.kt)("inlineCode",{parentName:"td"},"files")," - you can use patterns and wildcards.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git commit")),(0,i.kt)("td",{parentName:"tr",align:null},"Create a commit from the current index - the shell editor will open for the commit message.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git commit -m 'message'")),(0,i.kt)("td",{parentName:"tr",align:null},"Create a commit with message ",(0,i.kt)("inlineCode",{parentName:"td"},"message"),".")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git commit -a")),(0,i.kt)("td",{parentName:"tr",align:null},"Stage and commit all changes in the working tree.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git checkout <branch>")),(0,i.kt)("td",{parentName:"tr",align:null},"Checkout a branch called ",(0,i.kt)("inlineCode",{parentName:"td"},"branch"),".")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git checkout -b branch")),(0,i.kt)("td",{parentName:"tr",align:null},"Create and checkout a new branch called ",(0,i.kt)("inlineCode",{parentName:"td"},"branch"),".")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git branch <name>")),(0,i.kt)("td",{parentName:"tr",align:null},"Create a branch called ",(0,i.kt)("inlineCode",{parentName:"td"},"name")," but do not check it out.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git branch -m <new_name>")),(0,i.kt)("td",{parentName:"tr",align:null},"Change the current branch name to ",(0,i.kt)("inlineCode",{parentName:"td"},"new_name"),".")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git merge <branch>")),(0,i.kt)("td",{parentName:"tr",align:null},"Merge the branch named ",(0,i.kt)("inlineCode",{parentName:"td"},"branch")," into the current branch.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git log")),(0,i.kt)("td",{parentName:"tr",align:null},"Show the log of commits.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git log --oneline --branch")),(0,i.kt)("td",{parentName:"tr",align:null},"Show the log of commits, one line per commit, with the branch graph.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git rm <files>")),(0,i.kt)("td",{parentName:"tr",align:null},"Stage the removal of ",(0,i.kt)("inlineCode",{parentName:"td"},"files")," from the repostiry - you can use patterns and wildcards.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git mv <source> <destination>")),(0,i.kt)("td",{parentName:"tr",align:null},"Stage the movement of ",(0,i.kt)("inlineCode",{parentName:"td"},"source")," to ",(0,i.kt)("inlineCode",{parentName:"td"},"destination"),".")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git checkout 8342bec")),(0,i.kt)("td",{parentName:"tr",align:null},"Checkout a commit with SHA ",(0,i.kt)("inlineCode",{parentName:"td"},"834bec"),".")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git checkout HEAD~1")),(0,i.kt)("td",{parentName:"tr",align:null},"Move the current HEAD back one commit.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"git checkout <branch>~3")),(0,i.kt)("td",{parentName:"tr",align:null},"Checkout ",(0,i.kt)("inlineCode",{parentName:"td"},"branch"),", move back three commits from the tip.")))))}d.isMDXComponent=!0},9126:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-add-273352897de39c41175ab1570deba674.png"},7685:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-checkout-b-6475f62943d9c57369f6e44991d92efe.png"},7107:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-checkout-detached-head-1a94781840cf8e5c7a4fb3fd4b59ce1b.png"},8853:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-commit-number-2-9dba4c3b9c31e2268ea9ecbbfb1ed196.png"},7745:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-commit-on-aliases-84e8cf5ab03a662ab391896ba9438b7f.png"},9395:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-commit-910cd46c3ad343660e47a0b60cd70934.png"},3763:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-diverged-branches-d7921427ff846f43f168d004001862fb.png"},5524:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-log-showing-head-5a0e2000cb1a17a77297bbb98dd64a12.png"},4983:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-merge-fast-forwards-9cea41f3cdc7252424a0d46eb8aae96e.png"},8506:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-merge-prepare-fast-forwards-62b89b1712bd04787afe47b3c955bc96.png"},4876:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-merge-recursive-e40ed73171c316ca6754a895d84beb07.png"},7878:(e,t,a)=>{a.d(t,{Z:()=>n});const n=a.p+"assets/images/git-reset-ff25fe1fd1489ef9f82cf1b0cc5e7e73.png"}}]); \ No newline at end of file diff --git a/pr-preview/pr-346/assets/js/96104a02.67744804.js b/pr-preview/pr-346/assets/js/96104a02.67744804.js new file mode 100644 index 00000000..89dec6cd --- /dev/null +++ b/pr-preview/pr-346/assets/js/96104a02.67744804.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkeffective_shell=self.webpackChunkeffective_shell||[]).push([[7542],{3905:(e,t,n)=>{n.d(t,{Zo:()=>h,kt:()=>c});var o=n(7294);function a(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 o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,o,a=function(e,t){if(null==e)return{};var n,o,a={},r=Object.keys(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=o.createContext({}),p=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},h=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,h=l(e,["components","mdxType","originalType","parentName"]),u=p(n),d=a,c=u["".concat(s,".").concat(d)]||u[d]||m[d]||r;return n?o.createElement(c,i(i({ref:t},h),{},{components:n})):o.createElement(c,i({ref:t},h))}));function c(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var p=2;p<r;p++)i[p]=n[p];return o.createElement.apply(null,i)}return o.createElement.apply(null,n)}d.displayName="MDXCreateElement"},5736:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var o=n(7462),a=(n(7294),n(3905));const r={title:"Job Control",slug:"/part-2-core-skills/job-control"},i=void 0,l={unversionedId:"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.",source:"@site/docs/02-core-skills/09-job-control/index.md",sourceDirName:"02-core-skills/09-job-control",slug:"/part-2-core-skills/job-control",permalink:"/part-2-core-skills/job-control",draft:!1,editUrl:"https://github.com/dwmkerr/effective-shell/edit/main/docs/02-core-skills/09-job-control/index.md",tags:[],version:"current",frontMatter:{title:"Job Control",slug:"/part-2-core-skills/job-control"},sidebar:"sidebar",previous:{title:"Fly on the Command Line",permalink:"/part-2-core-skills/fly-on-the-command-line"},next:{title:"Understanding Commands",permalink:"/part-2-core-skills/understanding-commands"}},s={},p=[{value:"What Is Job Control?",id:"what-is-job-control",level:2},{value:"The Problem",id:"the-problem",level:2},{value:"Solution 1: Start the Server in the Background",id:"solution-1-start-the-server-in-the-background",level:3},{value:"Solution 2: Move the Server to the Background",id:"solution-2-move-the-server-to-the-background",level:3},{value:"Moving Background Jobs to the Foreground",id:"moving-background-jobs-to-the-foreground",level:2},{value:"Cleaning Up Jobs",id:"cleaning-up-jobs",level:2},{value:"Why You Shouldn't Use Jobs",id:"why-you-shouldnt-use-jobs",level:2},{value:"The Most Key Takeaways",id:"the-most-key-takeaways",level:2},{value:"Alternatives to Jobs",id:"alternatives-to-jobs",level:2},{value:"Quick Reference",id:"quick-reference",level:2}],h={toc:p};function u(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,o.Z)({},h,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,(0,a.kt)("em",{parentName:"p"},"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."),(0,a.kt)("h2",{id:"what-is-job-control"},"What Is Job Control?"),(0,a.kt)("p",null,"Let's start with an example. I am building a simple web page. It has one ",(0,a.kt)("inlineCode",{parentName:"p"},"index.html")," file, one ",(0,a.kt)("inlineCode",{parentName:"p"},"styles.css")," file, and one ",(0,a.kt)("inlineCode",{parentName:"p"},"code.js")," file. The ",(0,a.kt)("inlineCode",{parentName:"p"},"index.html")," file looks like this:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-html"},'<html>\n <head>\n <title>My New Project\n \n + + + + + + + + + + + + +
+

hack-on

Hack On! See those system calls!​

I mentioned earlier on that if you make a call like fopen, the Kernel is going to provide access to a file. It's quite easy to see this in action. Check the code below:

#include <stdio.h>

void main()
{
void* handle = fopen("/tmp/some-file");
fwrite(handle, "some text");
fclose(handle);
}

If you compile this program, then run XXX you will see the actual calls made to the Kernel. It can be very useful to use this technique to see what is going on with programs under the hood, particularly when diagnosing issues.

Hack On! Ahah! So that's TTY?​

Consider a command like:

docker -it

Consider a command like:

ssh user@remote.com my-script
no tty

Once you understand the concept of shells and terminals in a bit more detail, more obscure messages like this start to make sense.

In the first instance, we are telling Docker we are interactive - i.e. we are going to use an interface to send commands. The second parameter, -t says use a TTY - which is short for teletype terminal, old fashioned lingo for the screen.

TODO TTY picture

TODO​

Summary; technical​

For technical readers, there's quite a lot of terms which get thrown about almost interchangeably; shell, command-line, terminal, tty, command-prompt, CLI and so on. But each of these have a very specific meaning. It's important to understand exactly what each of these terms really means, where they came from, and how these different types of system or concept relate to each other.

Summary; non-tech​

A computer in a nutshell.


-- Could link to other layers of abstractions - such as sandboxes in webpages? +-- Could link to other layers of abstraction, such as containers?

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/donate/index.html b/pr-preview/pr-346/donate/index.html new file mode 100644 index 00000000..4182818c --- /dev/null +++ b/pr-preview/pr-346/donate/index.html @@ -0,0 +1,26 @@ + + + + + +Effective Shell | Effective Shell + + + + + + + + + + + + + + +
+

Effective Shell is free and open-source. If you have found this book useful please do consider a small donation!

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/donate/thanks/index.html b/pr-preview/pr-346/donate/thanks/index.html new file mode 100644 index 00000000..051efd1d --- /dev/null +++ b/pr-preview/pr-346/donate/thanks/index.html @@ -0,0 +1,26 @@ + + + + + +Effective Shell | Effective Shell + + + + + + + + + + + + + + +
+

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/downloads/effective-shell-samples.tar.gz b/pr-preview/pr-346/downloads/effective-shell-samples.tar.gz new file mode 100644 index 00000000..fb14bce3 Binary files /dev/null and b/pr-preview/pr-346/downloads/effective-shell-samples.tar.gz differ diff --git a/pr-preview/pr-346/downloads/effective-shell-samples.zip b/pr-preview/pr-346/downloads/effective-shell-samples.zip new file mode 100644 index 00000000..f70632c3 Binary files /dev/null and b/pr-preview/pr-346/downloads/effective-shell-samples.zip differ diff --git a/pr-preview/pr-346/favicon.png b/pr-preview/pr-346/favicon.png new file mode 100644 index 00000000..a6fdcadf Binary files /dev/null and b/pr-preview/pr-346/favicon.png differ diff --git a/pr-preview/pr-346/img/favicon.png b/pr-preview/pr-346/img/favicon.png new file mode 100644 index 00000000..a6fdcadf Binary files /dev/null and b/pr-preview/pr-346/img/favicon.png differ diff --git a/pr-preview/pr-346/img/logo.png b/pr-preview/pr-346/img/logo.png new file mode 100644 index 00000000..a6fdcadf Binary files /dev/null and b/pr-preview/pr-346/img/logo.png differ diff --git a/pr-preview/pr-346/index.html b/pr-preview/pr-346/index.html new file mode 100644 index 00000000..e1324292 --- /dev/null +++ b/pr-preview/pr-346/index.html @@ -0,0 +1,26 @@ + + + + + +Effective Shell | Effective Shell + + + + + + + + + + + + + + +
+

Effective Shell

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.

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.

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.

If you find this book useful, please consider donating to support my open source work.

Who Should Read This Book​

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.

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.

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.

What's In This Book?​

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.

  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.

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.

What You'll Need​

If you are using a computer, you have enough to get started! In Chapter 1 - Setting Up Your Shell Environment you'll learn how to setup your shell in Windows, Linux or MacOS.

Run the following command in your shell to download the samples:

curl effective.sh | sh

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.

How To Read This Book​

Commands that you can type into your shell, such as grep are shown as monospaced text. Paths to file and folders, such as the ~/effective-shell folder are shown in italics.

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 **bold**:

$ echo "my shell is $SHELL"
my shell is /bin/bash

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:

Z shell

Z shell specifics are highlighted like this.

Email Updates​

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.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/index.html b/pr-preview/pr-346/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/index.html new file mode 100644 index 00000000..d07d2f2a --- /dev/null +++ b/pr-preview/pr-346/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/index.html @@ -0,0 +1,26 @@ + + + + + +Becoming a Clipboard Gymnast | Effective Shell + + + + + + + + + + + + + + +
+

Becoming a Clipboard Gymnast

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.

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.

We'll also briefly introduce aliases and pipelines, which will be covered in a lot more detail in later chapters.

The Clipboard Essentials​

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:

CommandWindows ShortcutLinux ShortcutMacOS Shortcut
CutCtrl + XCtrl + X⌘ + X
CopyCtrl + CCtrl + C⌘ + C
PasteCtrl + VCtrl + V⌘ + V

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 Ctrl + V a few times to paste into terminal on Ubuntu:

Screenshot: Ctrl + V on Ubuntu

Instead of the contents of the clipboard being dropped into the shell, we see the text ^V. Why is this?

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 Ctrl key is called the Control Key is that it is used to send 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, Ctrl + P is almost universally used as a shortcut for the 'Print' command.

We tend to think of these commands as 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.

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!

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 Ctrl + C. This actually sends a signal to the program and typically the program responds by closing. We'll see signals again and again as we go through the book.

The Ctrl + C combination terminates the current program. What about Ctrl + V? This is the grand-sounding "Verbatim Insert" command. It tells the shell to write out the next keystroke you give it. This allows you to write out 'special' characters like the escape key, left or right keys, or even the Ctrl + V combination itself.

So if you type Ctrl + V twice, the shell writes out the text ^V. The hat symbol ^ represents 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 Alt key and other special keys.

So why do we need to care? Well the shell already has a command for Ctrl + C and Ctrl + V, so we're going to need to work around this to use our familiar 'copy' and 'paste' commands.

How this works varies across platforms. Follow the instructions below for the platform you are using.

Windows

If you are using a 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 not work. Instead, select the Use Ctrl+Shift+C/V as Copy/Paste option from the properties menu:

Screenshot: Use Ctrl+Shift+C/V as Copy/Paste on Bash on Windows

You can now use Ctrl+Shift+C for copy and Ctrl+Shift+V for paste. You can also copy text by just dragging the cursor over it with the right mouse button.

Linux

On most Linux systems you'll be using the Gnome terminal, which means that you can use Ctrl+Shift+C for copy and Ctrl+Shift+V for paste. You can also right click on text with the cursor to select it.

MacOS

Mac users can just use ⌘ + C for copy and ⌘ + V for paste. The shell doesn't use the special Mac Command character ⌘, which means the default keyboard mappings on MacOS work fine in a shell as they do not clash with anything.

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.

Preparing the Clipboard Commands​

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.

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.

To make things easier for the reader I'm going to assume you have created the pbcopy and 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!

Windows

Assuming you are using WSL, you will need to run the following two commands. By the time this book is published there may be a cleaner way, but for now this is a workaround for some limitations on the WSL system:

alias pbcopy='clip.exe'
alias pbpaste="powershell.exe -command 'Get-Clipboard' | tr -d '\r' | head -n -1"

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 - pbcopy and pbpaste, which will work in Bash on Windows.

Linux

Hopefully if you are Linux user the commands below will seem familiar. They install the xclip program and create shortcuts to copy and paste. You absolutely don't need to do this if you prefer to call xclip directly, these commands are just setup so that across all platforms the tutorial looks the same.

sudo apt install -y xclip
alias pbcopy="xclip -selection c"
alias pbpaste="xclip -selection c -o"

MacOS

Nothing is required on MacOS - pbcopy and pbpaste are built in.

Making these changes permanent

We've used the alias command to create pbcopy and pbpaste. In Bash (and most shells) an 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.

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.

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!

Copy and Paste Basics​

Now that we've created these commands, we can use them to access the clipboard. For example, if I copy the following text:

Kirk Van Houten
Timothy Lovejoy
Artie Ziff

Then I can paste it into the shell with the following command:

pbpaste

And we'll see something like this:

Screenshot: pbpaste in action

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 effective-shell/text/simpsons-characters.txt. Now we could use the cat command to show the contents of the file, and then manually select the text and copy it. Even easier though is to just pipe the contents of the file to the pbcopy command:

cat ~/effective-shell/text/simpsons-characters.txt | pbcopy

The output will look similar to the below (I've included the output of cat for reference as well):

Screenshot: pbcopy in action

The vertical bar | is the pipe operator. It tells the shell to take the output from the command on the left and send it straight to the input of the program on the right. We're going to see a lot more of the pipeline operator as we continue. For now it's enough to know you can use it to 'chain' commands together.

This might not seem super useful so far - but if the text file was a lot larger then it would be much harder to 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.

Now we can see some real world examples of how these commands can be useful in daily tasks!

Removing Formatting​

Don't you hate it when you have to copy formatted text and don't have an easy way to paste it as unformatted text? Here's an example, I want to copy this Wikipedia page on 'bash', and paste it into a Word document:

Copying and pasting with formatting

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 into a plain text editor just to copy out the plain text.

If you just run the command pbpaste | pbcopy, you can easily strip the formatting:

Stripping formatting from the clipboard

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 back into the clipboard, replacing the formatted text which was there before.

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.

Sorting Text​

Because we can 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.

Let's take another look at the list of characters we have in the ~/plaground/text/simpsons-characters.txt file:

$ cat ~/effective-shell/text/simpsons-characters.txt
Artie Ziff
Kirk Van Houten
Timothy Lovejoy
Artie Ziff
Nick Riviera
Seymore Skinner
Hank Scorpio
Timothy Lovejoy
John Frink
Cletus Spuckler
Ruth Powers
Artie Ziff
Agnes Skinner
Helen Lovejoy

We can easily take this text, sort it and then directly copy the results:

$ cat ~/effective-shell/text/simpsons-characters.txt | sort | pbcopy

The contents of the clipboard will now contain:

Agnes Skinner
Artie Ziff
Artie Ziff
Artie Ziff
Cletus Spuckler
Hank Scorpio
Helen Lovejoy
John Frink
Kirk Van Houten
Nick Riviera
Ruth Powers
Seymore Skinner
Timothy Lovejoy
Timothy Lovejoy

The 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.

Manipulating Text​

Let's say someone has emailed me a list of people I need to invite to an event:

Email List

The problem is:

  1. The list is in Excel and is formatted
  2. The list has duplicates
  3. I need to turn each name into an email address like 'Artie_Ziff@simpsons.com'

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.

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):

Artie Ziff
Kirk Van Houten
Timothy Lovejoy
Artie Ziff
Nick Riviera
Seymore Skinner
Hank Scorpio
Timothy Lovejoy
John Frink
Cletus Spuckler
Ruth Powers
Artie Ziff
Agnes Skinner
Helen Lovejoy

First, we copy the text to the clipboard.

Now we can paste and sort:

$ pbpaste | sort
Agnes Skinner
Artie Ziff
Artie Ziff
Artie Ziff
Cletus Spuckler
Hank Scorpio
Helen Lovejoy
John Frink
Kirk Van Houten
Nick Riviera
Ruth Powers
Seymore Skinner
Timothy Lovejoy
Timothy Lovejoy

Then remove the duplicates:

$ pbpaste | sort | uniq
Agnes Skinner
Artie Ziff
Cletus Spuckler
Hank Scorpio
Helen Lovejoy
John Frink
Kirk Van Houten
Nick Riviera
Ruth Powers
Seymore Skinner
Timothy Lovejoy

Replace the space with an underscore:

$ pbpaste | sort | uniq | tr " " "_"
Agnes_Skinner
Artie_Ziff
Cletus_Spuckler
Hank_Scorpio
Helen_Lovejoy
John_Frink
Kirk_Van_Houten
Nick_Riviera
Ruth_Powers
Seymore_Skinner
Timothy_Lovejoy

Then add the final part of the email address:

$ pbpaste | sort | uniq | tr " " "_" | sed 's/$/@simpsons.com/'
Agnes_Skinner@simpsons.com
Artie_Ziff@simpsons.com
Cletus_Spuckler@simpsons.com
Hank_Scorpio@simpsons.com
Helen_Lovejoy@simpsons.com
John_Frink@simpsons.com
Kirk_Van_Houten@simpsons.com
Nick_Riviera@simpsons.com
Ruth_Powers@simpsons.com
Seymore_Skinner@simpsons.com
Timothy_Lovejoy@simpsons.com

This looks perfect! We can now put the transformed text back onto the clipboard:

$ pbpaste | sort | uniq | tr ' ' '_' | sed 's/$/@simpsons.com/' | pbcopy

All in all we have the following pipeline:

  1. pbpaste - output the clipboard
  2. sort - order the output
  3. uniq - deduplicate the rows
  4. tr ' ' '_' - replace spaces with underscores
  5. sed /$/@simpsons.com - add the email domain to the end of the row

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.

In fact - if you are on a Linux system, try running:

cat /dev/clipboard

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.

We're also going to spend a lot of time later on looking at pipelines in detail, so don't worry too much if this seems overwhelming at this stage!

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.

Summary​

In this chapter we learnt:

  • 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.
  • Different operating systems access the clipboard in different ways, but we can work around this by creating an alias command (which we'll see in detail later)
  • We can use pbcopy to copy and pbpaste to paste.
  • We can 'chain' commands together with the | (pipe) operator.
  • We can turn formatted text on the clipboard into plain text by just running pbpaste | pbcopy.
  • We can sort lines of text with the sort command.
  • There is clearly a lot more we can do with text as we save hints of with the uniq, tr and sed commands - which we'll introduce in detail later.
  • You can treat the clipboard a bit like a file in the shell.
  • On Linux, lots of things can be represented as files - including the clipboard (which is accessed via the /dev/clipboard file).
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-1-transitioning-to-the-shell/getting-help/index.html b/pr-preview/pr-346/part-1-transitioning-to-the-shell/getting-help/index.html new file mode 100644 index 00000000..4e02eb6f --- /dev/null +++ b/pr-preview/pr-346/part-1-transitioning-to-the-shell/getting-help/index.html @@ -0,0 +1,26 @@ + + + + + +Getting Help | Effective Shell + + + + + + + + + + + + + + +
+

Getting Help

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.

In this chapter I'll show you how to quickly get help when working with tools in the shell, without disrupting your flow!

Getting Help is Important!​

If you are trying to be more effective when using the shell, it is crucial to know how to quickly look things up.

There'll be many circumstances where you'll need to open a browser to search for help. But there's also a wealth of information only a few keystrokes away. Looking up parameters, checking how to run commands, C library documentation, or even useful information like ASCII charts are available directly in the shell.

Being able to access this information quickly, without jumping into a browser or interrupting your flow is going to be one of the most crucial things you can do to become an effective shell user.

First we're going to look at the standard help system which is available on all Unix-like systems, which is man (short for 'manual'). Then we'll see a useful tool you can installed called tldr, which might be more helpful for day-to-day use. Finally we'll take a look at the cht.sh site as an alternative source for help.

Understanding 'man'​

Most tools you encounter in the shell have manual pages available. Many people will be familiar with the man command to get help on a tool, but there is a lot more help available than people often realise.

Getting help on a command​

The most basic way to get help on a command is with man. Here's an example:

$ man cp


CP(1) BSD General Commands Manual CP(1)

NAME
cp -- copy files

SYNOPSIS
cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file target_file
cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file ...
target_directory

DESCRIPTION
In the first synopsis form, the cp utility copies the contents of the
source_file to the target_file. In the second synopsis form, the con-
tents of each named source_file is copied to the destination
target_directory. The names of the files themselves are not changed. If
cp detects an attempt to copy a file to itself, the copy will fail.

...

The man command opens the manual for the given tool. These manuals should contain all command line options and details of how to use the tool.

You can scroll up and down through the content with the arrow keys. This scrolling capability actually is not part of man - it is available because the information is presented in the shell pager. A pager is a tool for looking through content which might not easily fit on a screen.

Using the pager​

The first thing you might notice is that you can move through the manual pages with the arrow keys.

The man command finds the appropriate manual page (often shortened to 'manpages') and then opens the page in a pager tool. The pager is what is providing the keyboard interface to look through the file.

On most systems, the pager will be the less program. There are lots of commands you can use to navigate through files with less, but the bare essentials are:

  • d - Scroll down half a page
  • u - Scroll up half a page
  • j / k - Scroll down or up a line. You can also use the arrow keys for this
  • q - Quit
  • /<search> - Search for text
  • n - When searching, find the next occurrence
  • N - When searching, find the previous occurrence

There are many other commands, but the set above is normally what I find myself using the most.

If you are interested, you can actually see what your pager is with the command below:

$ echo $PAGER
less

The $PAGER environment variable is used to tell the shell what program to use for paging. A few more details can be found with the man man command.

You can put any text content into your pager - try this:

ls -al /usr/bin | less

This lists the contents of the /usr/bin folder, piping the output to less so we can easily scroll through it.

There are alternative pagers available (on many Unix-y systems you'll have less, more and most) but in general you'll normally get what you need with less.

The Alternative - Help​

Sometimes you'll look something up in the manual and get the 'builtins' page. For example:

$ man cd
BUILTIN(1) BSD General Commands Manual BUILTIN(1)

NAME
builtin, !, %, ., :, @, {, }, alias, alloc, bg, bind, bindkey, break,
breaksw, builtins, case, cd, chdir, command, complete, continue,

## (I've skipped the bulk of the output to save space!)

This happens when the command you are looking up is not actually a program with a manual page, but a built-in shell command. Most shells have a way get help on such commands - bash for example has help:

$ help cd
cd: cd [-L|[-P [-e]] [-@]] [dir]
Change the shell working directory.

Change the current directory to DIR. The default DIR is the value of the
HOME shell variable.

# (I've skipped the bulk of the output to save space!)

This is all I'll say about help for now. We visit it again in Chapter 10 - Understanding Commands, where we talk more about built-in commands. For now we'll go back to the man command, which works across all shells as it is a Linux feature rather than a shell specific feature!

Manual Sections​

You'll often see tools referred to in manpages with numbers after them. Take a look at man less:

Screenshot of numbers

The number is the manual Section Number. The different sections of the manual are documented and can be found on most Unix-like systems in man's documentation, which you can check by running man man1. Here's what you'd get on Ubuntu 16:

  • Section 1 - Executable programs or shell commands
  • Section 2 - System calls (functions provided by the kernel)
  • Section 3 - Library calls (functions within program libraries)
  • Section 4 - Special files (usually found in /dev)
  • Section 5 - File formats and conventions (e.g. /etc/passwd)
  • Section 6 - Games
  • Section 7 - Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)
  • Section 8 - System administration commands (usually only for root)
  • Section 9 - Kernel routines (Non standard)

Not all of these explanations will be entirely clear to everyone, so we'll go through the sections in detail shortly.

If you want to, you can specifically choose which section of the manual you are looking in by using:

man <section> <search>

You can also get more information about the sections themselves by opening up the intro page. For example:

$ man 1 intro

INTRO(1) BSD General Commands Manual INTRO(1)

NAME
intro -- introduction to general commands (tools and utilities)

DESCRIPTION
Section one of the manual contains most of the commands which comprise...

Why would you do this, and why would you care? In general you won't need to worry about the sections unless you are looking for something which has an entry in multiple sections and you want to specify which one you use.

Another reason it is useful to know about the sections is that a lot of documentation (online and offline) includes a section number after the name of a command or file. Knowing what the section is can be useful in this case.

Here are a few examples of entries from each section, which illustrate what each section is for.

Section 1: Programs and Shell Commands​

These are programs - probably what you are going to be looking up most regularly! For example, man 1 time shows:

TIME(1)                   BSD General Commands Manual                  TIME(1)

NAME
time -- time command execution

SYNOPSIS
time [-lp] utility

DESCRIPTION
The time utility executes and times utility. After the utility finishes, time writes the total time
elapsed, the time consumed by system overhead, and the time used to execute utility to the standard
error stream. Times are reported in seconds.

...

Section 2: System Calls​

You'll probably not use this section unless you are doing systems programming2. This section contains info on the available Linux Kernel system calls. For example, running man 2 chown gives:

CHOWN(2)                    BSD System Calls Manual                   CHOWN(2)

NAME
chown, fchown, lchown, fchownat -- change owner and group of a file

SYNOPSIS
#include <unistd.h>

int
chown(const char *path, uid_t owner, gid_t group);
...

This entry shows you how you would call the function if you were programming for the Kernel.

Section 3: Library Calls​

These are the manpages for the C standard library functions. For example, man 3 time:

TIME(3)                  BSD Library Functions Manual                  TIME(3)

NAME
time -- get time of day

LIBRARY
Standard C Library (libc, -lc)

SYNOPSIS
#include <time.h>

time_t
time(time_t *tloc);
...

You would use this information if you were writing programs to run on the system.

Here we can see why the sections are important to know about. There are multiple entries for time. We need to use the sections to differentiate between them.

Running man time would not open the page above, because man searches the library in ascending section order, meaning that it actually finds time(1) and shows the pages for the time program, not the time C library call.

Because of the potential ambiguity of names if no section number is included, in lots of Linux documentation you'll see the man section number written next to library calls, system calls, programs and so on (things will refer to sed(1) or time(3) for example.

Section 4: Devices​

This section deals with the special devices which live in the /dev/* folder. For example, running man 4 random shows:

RANDOM(4)                BSD Kernel Interfaces Manual                RANDOM(4)

NAME
random , urandom -- random data source devices.

SYNOPSIS
pseudo-device random

DESCRIPTION
The random device produces uniformly distributed random byte values of
potentially high quality.
...

Again, we see that section numbers can be important. If you just run man random, you'll see:

RANDOM(3)                BSD Library Functions Manual                RANDOM(3)

NAME
initstate, random, setstate, srandom, srandomdev -- better random num-
ber generator; routines for changing generators

LIBRARY
Standard C Library (libc, -lc)

SYNOPSIS
#include <stdlib.h>

char *
initstate(unsigned seed, char *state, size_t size);

long
random(void);
...

Which is the manpage for random(3), which is C library function, not the /dev/random file!

We'll see more of these special files later in the book.

Section 5: File Formats​

This section details special files in the system. For example, man 5 crontab shows:

CRONTAB(5)                  BSD File Formats Manual                 CRONTAB(5)

NAME
crontab -- tables for driving cron

DESCRIPTION
A crontab file contains instructions to the cron(8) daemon of the gen-
eral form: ``run this command at this time on this date''. Each user
has their own crontab, and commands in any given crontab will be exe-
cuted as the user who owns the crontab. Uucp and News will usually
have their own crontabs, eliminating the need for explicitly running
su(1) as part of a cron command.
...

Which describes the crontab file used to define scheduled tasks. Again, this is different to man crontab which would document crontab(1). Similarly, man 5 passwd is going to show something quite different to man passwd.

You'll potentially use this section if you are performing system administration.

Section 6: Games​

Nothing says it better than man 6 intro itself (this'll not work on a Mac sadly, but try it on another Linux system):

...
DESCRIPTION
Section 6 of the manual describes all the games and funny little programs available on the system.
...

There are probably a few silly programs available on your system, here you'll find their manuals. For example, man 6 banner on a Mac shows:

BANNER(6)                      BSD Games Manual                      BANNER(6)

NAME
banner -- print large banner on printer

SYNOPSIS
banner [-d] [-t] [-w width] message ...

DESCRIPTION
Banner prints a large, high quality banner on the standard output. If
the message is omitted, it prompts for and reads one line of its stan-
dard input.
...

This section is going to be highly dependent on your operating system!

Section 7: Miscellaneous​

This is where you'll find additional assorted documentation. For example, man 7 ascii shows:

ASCII(7)             BSD Miscellaneous Information Manual             ASCII(7)

NAME
ascii -- octal, hexadecimal and decimal ASCII character sets

DESCRIPTION
The octal set:

000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel
...

Section 8: System Commands​

We've actually already seen one of these commands mentioned, in the manpage for crontab(5) it mentions cron(8). Let's see, with man 8 cron:

CRON(8)                   BSD System Manager's Manual                  CRON(8)

NAME
cron -- daemon to execute scheduled commands (Vixie Cron)

SYNOPSIS
cron [-s] [-o] [-x debugflag[,...]]

These are commands which system administrators would normally run. You might open section eight unexpectedly, for example man chmod will open chmod(1), but man chown will open chown(8), as it is a system command.

Some distributions might vary for section nine. On my Mac it contains information about the kernel interfaces, a C style guide and some more.

Getting the Index of Manual Section​

Manpages are just files on the filesystem, so you can get the index of a section just by looking in the appropriate folder.

For example, to index the available system calls, try ls /usr/share/man/man2:

EV_SET.2
FD_CLR.2
FD_COPY.2
FD_ISSET.2
FD_SET.2
FD_ZERO.2
_exit.2
accept.2
access.2
acct.2
...

This is quick and easy way to see what sort of entries you have on your system. If you want to work out where an entry lives, use the -w flag:

$ man -w printf
/usr/share/man/man1/printf.1

There are other ways to show the index of each section, but they vary a lot from system to system so showing the actual files is probably easier.

Searching the Manual​

You can search the manpage titles and summaries with man -k. For example, man -k cpu shows:

cpuwalk.d(1m)            - Measure which CPUs a process runs on. Uses DTrace
dispqlen.d(1m) - dispatcher queue length by CPU. Uses DTrace
gasm(n), grammar::me::cpu::gasm(n) - ME assembler

You can find more advanced options for searching by using your newfound man skills on man itself.

You can also use the apropos or whatis commands to search through the manuals. However, for simplicity I suggest just remember man -k!

Introducing tl;dr​

In general for this book I'm trying to avoid suggesting too many non-standard tools which don't come pre-installed on systems. However, this one is just too good to miss!

Let's say I need to find and replace some text in a file. I know I can do this with the sed command, but have forgotten the syntax. So I run man sed:

sed manpage

Wow, that's a lot of detail! And this is just page one of six!

Now let's compare this to the output from tldr (which is short for "Too Long, Didn't Read"). All I need to do is run tldr sed:

tldr sed screenshot

The first example is exactly what I'm looking for. Now for any more detail than a few basic examples, I'm going to have to go to the manual, but for the basics this is great.

You can install the tldr tool with npm install -g tldr. It's open source and community maintained. You will need Node.js installed to install the tool, the instructions are available online.

I'd recommend tldr as a first-call for checking to see how to use a command.

The Online Cheatsheet​

One final resource which I think is worth sharing is the website www.cheat.sh. This is a fantastic online collection of 'cheat sheets'.

These sheets cover almost all of the tools you will encounter, programming languages and more. But the real beauty of the tool is how it integrates into the shell. To see what I mean, just run the following command:

$ curl cht.sh

You will see something like this:

Screenshot: The Cheat.sh tool

The curl command we'll see again and again. It is a tool which lets you download content from the web. If we load the cheat.sh website (or its shortened version, cht.sh) from the shell, we get a text version of the website. We can now look at all sorts of content by following the guide shown.

The Cheat.sh site aggregates many data sources - including tldr! This means we can get information on tools without even having to install a tool like tldr locally.

This online cheatsheet is a wonderful resource. As well as guides for specific tools, there are entire courses on programming languages. You can even use it to search for the answers to questions, these features are powered by Stack Overflow. For example:

$ curl cht.sh/"How do I copy a folder in bash?"

You'll see something like this:

Screenshot: The Cheat.sh to ask a question

Now that can be a real time saver!

Summary​

In this chapter we looked at some of the ways we can get help. To quickly summarise:

  • The man tool can be used to look at the manual page for a topic
  • The man pages are grouped into sections, we can see them with man man
  • The tldr tool shows a very short description of a tool, which covers the most common use cases only
  • The cht.sh website can be used directly from the shell to get help on tools or even ask specific questions

  1. Weirdly satisfying to run.↩
  2. Which it is always fun to try if you get the chance, and a great way to learn more about the fundamentals of the operating system.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-1-transitioning-to-the-shell/getting-started/index.html b/pr-preview/pr-346/part-1-transitioning-to-the-shell/getting-started/index.html new file mode 100644 index 00000000..86632830 --- /dev/null +++ b/pr-preview/pr-346/part-1-transitioning-to-the-shell/getting-started/index.html @@ -0,0 +1,26 @@ + + + + + +Getting Started | Effective Shell + + + + + + + + + + + + + + +
+

Getting Started

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.

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.

If you are already comfortable with running a shell, know what bash is, and know how to run basic commands like ls and cd, are familiar with terms like command and parameter then you can skip this section. You could also just review the Summary to make sure that you are comfortable with the material which has been introduced and then move onto the next section.

What is the Shell?​

If you don't know what the shell is, then this is the place to start!

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.

Here's what the shell looks like on Windows:

And here's what it looks like on a Mac:

And here's what it looks like on Fedora, a popular Linux distribution:

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.

Why would you want to do this? There are a few reasons!

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.

Secondly, there are some scenarios where you have to use a shell. Not every program or system can be operated with a 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.

Finally, there are some scenarios where it can be 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 shell scripts to automate these kinds of operations.

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.

Opening the Shell​

Now let's actually learn how to open the shell on your computer.

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 other shells as possible - we'll get to that in the next chapter.

Microsoft Windows​

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".

To open the command prompt, start by clicking the start button on the bottom left hand side of the screen, and type command prompt. Open the Command Prompt program:

Screenshot: Search for Command Prompt

Once the program has opened, type whoami then hit the Return key. The whoami program will show the username of the logged in user:

Screenshot: whoami on Windows

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.

MacOS​

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 terminal. Open the terminal program which is shown:

Screenshot: Search for Terminal

Once the program has opened, type whoami then hit the Return key. The whoami program will show the username of the logged in user:

Screenshot: whoami on OSX

That's it! In the next section we'll make some minor configuration changes to keep things consistent with the samples in the book.

Linux / Unix​

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.

Configuring the Shell​

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.

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 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.

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.

Once this is done then we are ready to get into the book properly!

Microsoft Windows​

Windows is not anything like Linux under the hood. So to get a shell working, we have three options:

  1. Use a tool which provides common Linux tools which have been written to work with Windows
  2. Use a "virtual machine" running Linux
  3. Use the Windows Subsystem for Linux

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.

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.

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.

We'll go through all options here.

Option 1: Install Linux Tools​

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.

To get a Linux-like experience on a Windows machine, we'll install Cygwin. Cygwin provides a large set of programs which are generally available on Linux systems, which are designed to work on Windows.

Download the Cygwin installer and start the installation process. You should see something like this:

Screenshot: Cygwin Setup

Start the installation and tell it to install from the internet (the default option):

Screenshot: Cygwin Setup

Install for all users in the default location. It is also fine to change the options if you prefer:

Screenshot: Cygwin Setup

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":

Screenshot: Cygwin Setup

The installer will now start downloading and installing the programs:

Screenshot: Cygwin Setup

Once Cygwin has finished installing, you will have a link to open Cygwin available on the desktop and start menu.

You can use this link to start using the "Bash" shell, or if you prefer you can open the "Command Prompt" as described in Opening the Shell and run the bash program:

Screenshot: Cygwin Setup

Note that you shouldn't use the --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.

At this point you have a ready-to-go bash environment and can continue on to the Summary and Next Section.

Option 2: Use a Virtual Machine​

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.

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 Ubuntu distribution of Linux.

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):

Screenshot: Ubuntu Download

While the Ubuntu software downloads, we can install VirtualBox. Go to the VirtualBox Website and download the VirtualBox installer. You will need the installer for 'Windows Hosts'.

Once the installer has downloaded, run it to start the installation:

Screenshot: VirtualBox Setup

Next you will be asked to configure the installation options. The defaults will be fine for most users:

Screenshot: VirtualBox SetupScreenshot: VirtualBox Setup

Then the installation will start:

Screenshot: VirtualBox Setup

Once the installation is complete and the Ubuntu installer has downloaded we can move onto the next step.

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:

Screenshot: Ubuntu Installation

You will be asked to setup a virtual hard disk. I would recommend the default options for most users:

Screenshot: Ubuntu Installation

Once the machine has been created it will be shown in the main VirtualBox window. Select the machine and choose "Start":

Screenshot: Ubuntu Installation

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 .iso:

Screenshot: Ubuntu Installation

If this step fails, you may need to disable "Hyper-V" and "Windows Sandbox" by going to "Add or Remove Windows features":

Screenshot: Ubuntu Installation

After a short while you will see the Ubuntu installer start up. Choose the "Install Ubuntu" option:

Screenshot: Ubuntu Installation

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:

Screenshot: Ubuntu Installation

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!

Screenshot: Ubuntu Installation

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.

When you have logged in, press the applications icon on the bottom-left of the screen and search for the "Terminal" application:

Screenshot: Ubuntu Installation

You are now running the "Bash" shell in the terminal. You can run the whoami command to show the current user, or bash --version to see the version of Bash which is installed:

Screenshot: Ubuntu Installation

That's it! You now have a virtual machine running Ubuntu and Bash which you can use to learn about the shell.

Option 3: Setup the Windows Subsystem for Linux​

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.

First, open up the "Turn Windows Features on or off" option from the control panel:

Screenshot: Turn Windows Features on or off

Then enable the "Windows Subsystem for Linux" feature:

Screenshot: Enable Windows Subsystem for Linux

After your computer has restarted, open up the Windows App Store and search for "Ubuntu":

Screenshot: Ubuntu on App Store

Once Ubuntu has installed, open up the app. It will then initialise (which can take a little while):

Screenshot: Initialise Ubuntu

Choose a username and password to complete the setup:

Screenshot: Choose Username and Password

And that's it! You can now open the Ubuntu app at any time to use Ubuntu on Windows, interfacing using the Bash shell.

MacOS​

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 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 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).

To install the right software, we'll use a tool called 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!

First, follow the instructions online to install Homebrew:

Screenshot: OSX Installation

In most cases, this will require opening the Terminal program and running a snippet which looks like this:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

However, this might have changed since the time of writing so do 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 Ruby programming language (which comes pre-installed on MacOS).

Once this has installed, install Bash by running the following command in the shell:

brew install bash

This uses the brew command, which we have just installed, to install the bash program.

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 /usr/local/bin/bash:

Screenshot: OSX Installation

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 terminal window, it will run the latest version of Bash, which you can confirm by running echo $BASH_VERSION:

Screenshot: OSX Installation

There is actually a more sophisticated way to change what shell is used in a system, which is the special chsh command (short for "change shell"). We'll see this in a later section. We'll also see what echo is in more detail shortly.

Linux​

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.

That's It!​

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 Summary.

A Quick Demo of the Shell​

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.

Start by opening your shell. This is covered in Opening the Shell. Your shell should be Bash - if this doesn't sound familiar, then make sure you have followed the instructions in Configuring the Shell.

You should see your terminal program running your shell. You can see what the version is of your shell by running:

bash --version

Screenshot: Bash Version

Let's quickly dissect this. We have run the bash 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.

The --version text is a parameter. Parameters affect how commands work. This is actually easier to see with an example.

Let's move to the home folder. On most computers your home folder is your personal space where things like documents, photos, music, downloads and so on are kept.

Let's switch to the home folder by running the following command:

cd ~

Once you've done that, run the pwd command:

pwd

Screenshot: Moving to the home directory

So what has happened here? The first command:

cd ~

Is used to change directory - that's what cd stands for. The parameter we passed to cd was just the 'tilde' character (~). This character has a special meaning in the shell - it means "the current user's home directory".

Finally, we ran the pwd command. This command is short for print working directory. It writes out to the screen where you currently are. On my Mac, my home directory is located at /Users/dwmkerr, which is what the command has shown me.

Let's take another look at a command. Run the following in your shell:

ls

The ls command is short for 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.

Screenshot: List directory contents

We can pass different parameters to 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 Music folder, we'd just run:

ls Music

Not much to see here:

Screenshot: List the contents of the Music directory

Many commands actually allow us to pass multiple parameters. For example, we could list the contents of my Movies and my personal applications:

ls Movies Applications

Screenshot: List the contents of the Movies and Applications directory

There's not much in either. You might wonder why Applications is so empty - that's because we're looking at the applications only installed for the current user, because we are in the user's home directory. To see the applications for everyone we'd need to use the folder where applications are kept for all users.

We can do this by running ls /Applications:

Screenshot: List global applications

The trick here is that we start with a leading forward slash - this means the Applications folder in the root of the computer, not the one in my current folder.

On Windows, applications are kept in different places, but we can see some of the installed applications by running ls "c:\program files\":

Screenshot: List applications on Windows

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 two parameters. It would think we wanted to see the contents of the c:\program and files folders - and they don't exist!

Screenshot: List applications on Windows incorrectly

The error above shows what happens when we miss the quotation marks.

Now we can take a look at how a 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 size of the files in the folder. We do this by using the -lh pass the parameter, which is short for long list, human readable:

ls -lh Downloads/*.jpg

Screenshot: List downloaded photos

Now I can see all of the jpg files (jpg files are images) in my 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:

  • ls - List the contents of a folder
  • -lh - This is the long list in human-readable sizes parameter, which means we see how big the files are in a friendly format (like 911K for Kilobytes, rather than showing something like 911012 which would be the number of bytes - and harder to read!)
  • Downloads/*.jpg - Show the contents of the Downloads folder, including any files which end with .jpg - the * is a wildcard which means that we don't mind what the filename is

The -lh parameter is shorthand. Many commands offer longhand parameters (such as --version) as well as shorthand (such as -v as an alternative for --version). Longhand is easier to read, shorthand is faster to type.

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!

Now let's look at one more command.

The Echo Command​

The 'echo' command is used to write out a message in the shell. Here's an example of how it works:

echo "Hello Shell!"

This command writes out the text Hello Shell!:

Screenshot: Echo command

Why would we do this? One of the most common reasons would be to see what the shell thinks a certain value is. For example, try this command:

echo "My home directory is at: $HOME"

You'll see something like this:

Screenshot: Echo the home directory

The $HOME part of the text is called a variable. We can recognise variables because they start with a dollar symbol. $HOME is a built-in variable which holds the location of the current user's home directory.

We're going to see all sorts of cool things we can do with echo as we continue in the book!

Move Around​

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'.

A useful reference might be the picture below:

Screenshot: Shell quick reference

Here we map the shell commands to the visual interface's equivalents:

  • pwd shows the current working directory - where you currently are in the file system
  • ls lists the files in the current directory (or any directory you tell it)
  • cd .. changes the directory to another location - if you use the special .. directory, you are telling it to change to the parent directory, i.e. 'go up' in the file system

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:

cd ~/Downloads
open himalayas.jpg

We can see the result here:

Screenshot: Open a photo

Running open himalayas.jpg has opened the photo in the application which is used for photos by default in the operating system.

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 open command will open a file on MacOS. On Windows you can use start, and on Linux you can generally use xdg-open.

As a nifty trick, trying running open .1:

Screenshot: Open the current directory

. This will open the current folder. Every folder contains two 'special' folders. The first is .., which we've seen means 'my parent folder' and the second is ., which means 'myself'. Having this . folder is convenient, as it means we can do things like this - run a command to open the current folder.

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 concepts to remember, which are much more important than the individual commands we've see are:

  • In the shell we run commands
  • We can change how commands work by using parameters
  • Some parameters just go at the end of the command - like ls Downloads
  • Some parameters start with a hyphen, and change how the command behaves - these are often called 'flags'. An example is ls -lh, which lists the files in the current folder with a human-readable file size

We've also learned:

  • cd changes the current directory
  • pwd prints the current directory
  • ls lists the files in a directory
  • echo can be used to write out text to the screen
  • open, start and xdg-open can be used to open a file or folder on MacOS, Windows and Linux respectively

Now we can start to get into more detail!

Summary​

In this section we learnt:

  • That this book is for IT professionals, hobbyists or anyone who wants to learn more about how to work with computers
  • What the shell is, and why we might want to use it
  • How to open the shell programs for Windows, Mac and Linux which are installed by default
  • 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

We introduced the following commands:

  • cd - which changes directory
  • pwd - which prints the current working directory
  • ls - which lists the contents of a directory
  • echo - which writes text to the screen
  • open - which will open a file or folder

We also briefly introduced variables, which are special values which start with the dollar symbol, such as $HOME which stores the user's home directory. We saw that each directory contains two special directories - .. which represents the parent directory, and . which represents the current directory.

With these tasks complete we can now move onto the next section.


  1. On Windows you might need to run start . and on Linux, xdg-open ..↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-1-transitioning-to-the-shell/index.html b/pr-preview/pr-346/part-1-transitioning-to-the-shell/index.html new file mode 100644 index 00000000..d0bc6ec0 --- /dev/null +++ b/pr-preview/pr-346/part-1-transitioning-to-the-shell/index.html @@ -0,0 +1,26 @@ + + + + + +Part 1 - Transitioning to the Shell | Effective Shell + + + + + + + + + + + + + + +
+

Part 1 - Transitioning to the Shell

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.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-1-transitioning-to-the-shell/managing-your-files/index.html b/pr-preview/pr-346/part-1-transitioning-to-the-shell/managing-your-files/index.html new file mode 100644 index 00000000..d3c8a449 --- /dev/null +++ b/pr-preview/pr-346/part-1-transitioning-to-the-shell/managing-your-files/index.html @@ -0,0 +1,27 @@ + + + + + +Managing Your Files | Effective Shell + + + + + + + + + + + + + + +
+

Managing Your Files

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.

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.

This chapter will introduce the wget, unzip, cp, mv, rm, mkdir, rmdir, cat and zip commands. We'll also briefly look at wildcards and redirection.

Creating a Playground​

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!

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 effective-shell.com website, right here:

effective-shell.com/downloads/effective-shell-samples.zip

We 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!

Open your shell - if you've not yet got set up with your shell, that's OK, just check Chapter 1 - Getting Started.

Now that you have your shell open, we can run the wget command (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 Chapter 2- Navigating Your System.

First, we'll move to our home directory, then download the file.

cd
wget https://effective-shell.com/downloads/effective-shell-samples.zip

You'll see something like this:

Screenshot: wget

When you call the 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!).

As an aside, if we were not in our home folder when we called the 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 -O (Output File) flag to say explicitly where we want to download the file.

As an example, if were not in the home folder, but wanted to download there, we'd just call:

cd
wget -O ~/playground.zip https://effective-shell.com/downloads/effective-shell-samples.zip

Now that we've downloaded the file, let's look at our home directory now, with a quick call to ls ~:

Screenshot: ls home

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.

Finding out about files​

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:

file ~/effective-shell-samples.zip
Screenshot: file

The file command is showing us we have a zip file - now it's time to unzip it!

Extracting the Zip​

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!

Run the command:

unzip ~/effective-shell-samples.zip

Now let's look at what we've got with the ls command:

Screenshot: unzip

Excellent - we've now got a folder which contains all of the files in the zip archive.

Deleting Files​

Now that we've downloaded and unzipped the file, we don't need the zipped version any more. So let's delete this file.

The rm (Remove) command can be used to delete a file. If we run:

rm ~/effective-shell-samples.zip
ls | grep samples

Then we'll see the following:

Screenshot: rm

Notice that the zip file is gone - just the folder is left.

By the way - be really careful with the 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 rm is a little risky!

However one thing it will do to try and help you not make mistakes is let you know if you are trying to delete a folder, not a file.

Run the following command to try and delete the unzipped folder:

rm ~/effective-shell-samples
Screenshot: rm error directory

The rm command has not succeeded in this case - it's warning us that we're not deleting a file, but a whole directory.

Now we can get around this by adding the -r flag, which means 'recursive' - i.e. not just the folder but everything in it. But use this with caution!

Examining the Contents of a Folder​

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!

In a graphical user interface, we'd open the folders and look at the files. In the shell, we can use the tree command to show the contents of a folder.

Now the tree command is not installed by default on all systems. So if you are on a Mac, run:

brew install tree

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. apt-get install -y tree).

Using a non-universal command is generally not our goal in this book, but in these early stages while we are transitioning from the graphical user interface, the tree command can be really helpful. Later on we'll see how to use the more universal find command to give a similar output.

Try it out with:

tree ~/effective-shell-samples
Screenshot: tree

The 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 loas-gch.JPG file by running:

file ~/effective-shell-samples/pictures/loas-gch.JPG
Screenshot: file info for JPEG file

Note that the file command is already showing it is a bit more clever. It knows that the file is a JPEG file (a picture), but is giving other details as well.

Copying a File​

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 cp (_Copy) command:

cp ~/effective-shell-samples/pictures/laos-gch.JPG ~/effective-shell-playground/pictures/laos-gch-copy.JPG

This makes a copy of the file - if you are not sure if it has worked, just run:

tree ~/effective-shell-samples
Screenshot: cp command

We can see we've made a copy.

Saving Some Keystrokes​

Wow, it's painful putting ~/effective-shell-samples before everything! From Chapter 2- Navigating Your System we already know how to change directory, so let's do that now:

cd ~/effective-shell-samples

Remember - cd is change directory. Excellent - until we tell our shell otherwise, this our new working directory.

Renaming or Moving Files​

You might have noticed that the photos have different endings - one of them ends in .JPG. Let's rename it so that it has the ending .jpeg to be consistent with the others.

To do this, we use the mv (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.

Rename the copy we made of the photo by running:

mv pictures/loas-gch-copy.JPG pictures/loas-gch-copy.jpeg

Let's run tree to see what happened. Remember - now that our working folder is the playground, we don't even need to tell tree where to look, if we give it no arguments it'll assume we're looking at the working directory:

Screenshot: mv command

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 mv to also change what folder a file is in.

Creating a New Folder​

Perhaps we're not happy with the name pictures for our folder we've been playing with, maybe we'd prefer to have them all in a folder called photos?

Probably the first thing we'd do in a graphical environment is create a new folder - so let's do thee same here!

Run the commands:

mkdir photos
tree

And we should see:

Screenshot: mkdir command

We've use the mkdir command, which is short for Make Directory. This is how we create a new folder in the shell.

Now let's say we wanted to be really organised, and create a photos folder by year and topic, perhaps 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!

mkdir -p 2019/outdoors/pictures
tree

Let's see how it looks:

Screenshot: mkdirp command

All we had to do was add the -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 more effectively in a shell than in your graphical user interface!

Copying or Moving Multiple Files with Wildcards​

Let's copy the photos that we have in the pictures folder into the photos/2019/outdoor/climbing folder.

When we run the cp or mv command, we can use a 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:

cp pictures/* photos/2019/outdoor/climbing

Here's how it works for +Now we need to copy over our files from the pictures folder to the 2019/outdoor/photos folder. We'll use exactly the command we used before to copy a file - cp:

$ cp pictures/* photos/2019/outdoors/climbing/

$ tree photos
photos
β”œβ”€β”€ 2019
β”‚Β Β  └── outdoors
β”‚Β Β  └── climbing
β”‚Β Β  β”œβ”€β”€ laos-gch-copy.jpeg
β”‚Β Β  β”œβ”€β”€ laos-gch.JPG
β”‚Β Β  └── nepal-mardi-himal.jpeg
└── 2020
└── outdoors
└── climbing

6 directories, 3 files

Here we've used the wildcard symbol, which is *, to say "everything in the folder". Many commands can take wildcards as inputs. We'll see much more about them later!

Deleting a Folder​

Now that we have our more organise 2019/outdoors/photos folder, we don't need the photos folder we created. So let's delete it! Remember how rm removes a file, and mkdir creates a folder? Well rmdir will remove a folder!

rmdir photos
tree
Screenshot: rmdir command

As an important sidenote, just how rm doesn't move files to your recycle bin, so you cannot undo the operation, rmdir works the same way. So if we try to remove a directory which has things in it, such as the pictures directory, it will fail:

rmdir pictures
Screenshot: rmdir fail command

In this case, it is actually easier to just call rm -r pictures. Why is that? Well it's just like we saw in the earlier example - rm can delete files or directories. And if the directory is not empty, we just add the -r (Recursive) flag to tell it to delete the directory and everything it contains.

Looking at Text Files​

Run tree and you'll see we have a quotes folder:

tree
Screenshot: tree of new quotes folder

We're going to use the cat (Concatenate) command to look at the Ursula Le Guin quote. Run the following command:

cat quotes/ursula-le-guin.txt
Screenshot: cat

In the screenshot we snuck in a quick file call to see what the shell thinks the file is.

Why Concatenate? We're just showing the text in the terminal, not concatenating (i.e. joining) anything! Well the reason is that the cat command 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, 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.

We'll see a 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.

First, let's just cat the whole folder:

cat quotes/*
Screenshot: cat wildcard

There we see the * wildcard again. We could be more specific and use something like cat quotes/*.txt to only show files ending in .txt.

Notice how the output from all of the files has been concatenated together into a single output? That's where the cat name comes from - it concatenates, i.e. joins files.

As one last trick, let's use this output but instead of showing it on the screen, put it into a single all-quotes.txt file:

cat quotes/* > quotes/all-quotes.txt
tree
cat quotes/all-quotes.txt
Screenshot: cat redirect

The > part of this is called a redirect operator - in short it's telling the shell not to write to the screen, but to write to a file. We've concatenated all of the individual quotes and made a single file from them.

We'll look at wildcards and redirection in a lot more detail as we continue through the book!

Zipping up Files​

Let' say that we want to zip up the new 2019/outdoors/pictures folder. We've already seen the unzip command, let's see how to use the zip command to zip up a folder:

Run the command below:

zip -r 2019-outdoor-pictures.zip 2019

This is how it will look - there's a tree and ls command before and after so we can see what's happening!

Screenshot: zip

Great! We've created a zip. Let's dissect the command a bit:

  • zip just means call the zip executable
  • -r means recursive we don't just want to zip the 2019 folder, we want to zip everything inside it as well!
  • 2019-outdoor-pictures.zip is the name of the file we want to create, we put this first...
  • ...because everything which follows (e.g. 2019) is going to be zipped, and we can specify many files and folders if we want

Summary​

In this chapter we introduced the following:

  • The wget (web get) command can download a file from the web.
  • If we use the -O (output location) flag, we can specify where we want to download the file to.
  • The 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).
  • The unzip command can unzip a file for us.
  • The rm (remove) command can delete a file.
  • The rm command won't delete a folder which has files in it, unless you tell it to by adding the -r (recursive) flag.
  • The tree command can show the files and folders in a given directory, or the current directory by default.
  • The cp (copy) command can copy a file.
  • The cp can also be given wildcards like * to copy many files.
  • The mv (move) command can move or rename a file.
  • The mkdir command can create a folder - it can even create a whole tree of folders if you pass the -p (create parent directories) flag.
  • The rmdir command can delete a folder - but just like rm it will fail if the folder is not empty!
  • When we delete files in the shell with rm or rmdir they are gone forever, no recycle bin!
  • The cat command (concatenated) can be used to write the contents of a file to the screen.
  • We can pass multiple files to commands like cat if we use wildcards, such as quotes/*.
  • We can write the output to a file instead of the screen, if we use the > (redirect to file) operator.
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-1-transitioning-to-the-shell/navigating-your-system/index.html b/pr-preview/pr-346/part-1-transitioning-to-the-shell/navigating-your-system/index.html new file mode 100644 index 00000000..f46ff85b --- /dev/null +++ b/pr-preview/pr-346/part-1-transitioning-to-the-shell/navigating-your-system/index.html @@ -0,0 +1,26 @@ + + + + + +Navigating Your System | Effective Shell + + + + + + + + + + + + + + +
+

Navigating Your System

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.

This section will introduce the pwd, ls, cd, pushd and 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.

If these commands are familiar to you then feel free to jump to the next chapter! Otherwise, let's get started.

The Working Directory​

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:

pwd

You should see something like this:

Screenshot: pwd

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.

The pwd command is the Print Working Directory command. It shows the full path of the directory that you are in.

There's one more way to find the working directory. It is stored in an Environment Variable called PWD.

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.

Try the following command:

echo "My current working directory is: $PWD"

You should see something like this:

Screenshot: PWD Environment Variable

The dollar symbol is used to tell the shell we want to use the PWD variable, not write out the text PWD. We'll see a lot more about environment variables as we continue through the book.

Listing the Contents of the Working Directory​

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:

ls

You should see something like this:

Screenshot: ls

The ls command is the 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.

There are a lot of options for the ls command. For now, let's look at one of the most common options -l. This shows the contents as a list:

ls -l
Screenshot: ls -l

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.

Changing the Directory​

In a graphical user interface, you move to a different directory by clicking on it.

In the shell, you run the cd command. Try it out with:

# Move to the pictures directory...
cd Pictures

# ...then list the contents of the directory.
# Note that the '-al' flags mean show *all* files, as a *list*.
ls -al

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.

On my system, we'll see the following output:

Screenshot: cd

The cd command is the 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 (pwd for Print Working Directory, cd for Change Directory).

Now that you know how the 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 paths work in systems.

Understanding Paths​

In Linux, Windows and MacOS (and most other operating systems), paths are the 'addresses' of files or folders.

There are two types of paths - Absolute Paths and 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:

/Users/dwmkerr/repos/github/dwmkerr/effective-shell

Absolute paths always start with a slash. That's how the system knows it is an absolute path. The / is the root of the file system - basically it's the folder which everything else lives in.

If I have an absolute path, I know exactly where the file or folder is. Let's compare this to a relative path. Below is the relative path in my shell for the file I'm writing right now:

website/content/docs/part-1-transitioning-to-the-shell

This path is 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 Pictures folder, and I want to move to the 2020-photos folder, I could do it in two ways. The first is with an absolute path:

cd /Users/dwmkerr/Pictures/2020-photos

The second is with a relative path:

cd 2020-photos

In short - relative paths are often useful if you want to move to something within the current directory and absolute paths are useful if you need to move to somewhere completely different.

The Special Dot and Dot Dot Folders​

As you experiment with these commands, you might have noticed that every folder contains two other folders, one with the name . and one with the name ... Run ls -al on the pictures folder to see an example:

ls -al pictures

You should see something like this:

Screenshot: Special Dot Folders

This picture highlights two special folders - . and ... These are special folders which exist in every folder in the system.

The first folder, ., 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:

cp . /backup

The cp command is the Copy command, and we'll see it in the next chapter. But the key thing to note is that we can use . to tell the command to copy the folder we are in right now.

The .. folder means the parent folder. You can use this to "go up" to the parent folder, for example:

cd ..
ls .

Would give:

Screenshot: cd dot dot

Note that we've used cd .. to change directory to the parent folder then ls to list the contents of the current folder. We could also just have used ls on its own as it defaults to the current folder.

The .. 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 pictures folder and I want to move to the scripts folder, I can just use:

cd ../scripts
ls

And we'll see this:

Screenshot: cd dot dot scripts

The Home Directory​

There is one more special part of the file system we have to know about. That is the Home Directory. In Linux-like systems every user has their own personal directory where they can keep their files and folders.

This directory can always be accessed through the ~ 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:

cd ~
ls

This would show something like this:

Screenshot: cd home

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:

cd ~/Pictures

Your home directory on most computers will be where you keep your documents, pictures, videos and so on. Normally this directory is not accessible to other users of the system. Each user in a system gets their own home directory.

You can also see the home directory by using the special HOME environment variable:

echo "My home directory is: $HOME"

This would show something like this:

Screenshot: echo home

One useful trick - running cd without any parameters will always take you home! So to go home, just run:

cd

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!

Pushing and Popping the Working Directory​

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 pushd command:

pushd ~/Downloads
ls
popd

After I've checked my downloads, I can run popd to go back to where I was:

Screenshot: pushd and popd

The pushd command 'pushes' a new working directory onto a stack - moving you there. The 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.

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 pushd and popd would look if we were to visualise it:

Screenshot: pushd popd stack

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.

Going Back​

One last trick which can save time is the following command:

cd -

This is a special parameter for cd which tells it to go back to the last location you moved to. Here's how it might look if you use it:

Screenshot: cd dash

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 pushd and popd instead.

Summary​

In this chapter we introduced the following:

  • The pwd (print working directory) command shows the current working directory
  • The $PWD environment variable holds the current working directory
  • The ls (list) command shows the contents of the current directory or a given directory
  • The ls -l command shows the contents of the current directory as list
  • The cd (change directory) changes the current working directory
  • Absolute paths are paths which specify the exact location of a file or folder...
  • ...Relative paths are paths which are relative to the current directory
  • The . special folder means 'this folder'
  • The .. special folder means 'the parent folder'
  • The ~ special folder is the 'home directory'
  • The $HOME environment variable holds the user's home directory
  • You can run cd at any time to quickly go to your home directory
  • You can use pushd and popd to push and pop the working directory stack
  • You can use the cd - command to go back to the last location
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/index.html b/pr-preview/pr-346/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/index.html new file mode 100644 index 00000000..ecf75ed4 --- /dev/null +++ b/pr-preview/pr-346/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/index.html @@ -0,0 +1,26 @@ + + + + + +The Renaissance of the Shell | Effective Shell + + + + + + + + + + + + + + +
+

The Renaissance of the Shell

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.

In this first interlude we'll look at just why the shell is experiencing something of a renaissance in the modern age of IT.

Is there a Renaissance of the Shell?​

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 specific increase in the popularity of keyboard and script operated systems is a challenge.

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.

We'll look at three specific developments in technology:

  • Diversity of programming languages
  • Convergence of operating platforms
  • DevOps

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.

The Changing Technology Landscape​

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.

The Diversity of Programming Languages​

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.

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.

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).

"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.

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).

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.

Transition to the time of writing. In the Stack Overflow 2020 Technology Survey1, the top ten languages most wanted by employers are:

  • Python
  • JavaScript
  • Go
  • TypeScript
  • Rust
  • Kotlin
  • Java
  • C++
  • SQL
  • C#

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.

Why does this matter for the shell? The answer is that for 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.

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 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.

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 extremely efficient when working, and that overly featured IDEs can get in the way and hide complexity.

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.

Convergence of Operating Platforms​

Whilst the variety in programming languages and developer tooling may have increased, in many ways the operating platforms engineers use have become more homogeneous.

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.

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.

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.

Even Windows has increasing support for Linux-like operation, in the form of the Windows Subsystem for Linux.

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.

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.

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.

DevOps​

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.

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.

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.

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.

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 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 require automation.

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 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).

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.

And for these reasons and many more, learning how to use the shell effectively has never been more relevant or practical.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-2-core-skill/index.html b/pr-preview/pr-346/part-2-core-skill/index.html new file mode 100644 index 00000000..a87ebd94 --- /dev/null +++ b/pr-preview/pr-346/part-2-core-skill/index.html @@ -0,0 +1,26 @@ + + + + + +Part 2 - Core Skills | Effective Shell + + + + + + + + + + + + + + +
+

Part 2 - Core Skills

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.

In this section, we'll look at core shell skills. These skills are fundamental to how the shell works, and fundamental to using it effectively. Even if you are familiar with the concepts in each chapter, I would still recommend skimming these sections to make sure there is nothing you have missed.

A solid understanding of these core skills will be useful for later sections. So even though this book is designed to be something you can dip and dip out of, in any order you choose, it may be worth focusing on this section before moving to later sections.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-2-core-skills/finding-files/index.html b/pr-preview/pr-346/part-2-core-skills/finding-files/index.html new file mode 100644 index 00000000..2033bfd1 --- /dev/null +++ b/pr-preview/pr-346/part-2-core-skills/finding-files/index.html @@ -0,0 +1,28 @@ + + + + + +Finding Files | Effective Shell + + + + + + + + + + + + + + +
+

Finding Files

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.

Introducing the Find Command​

The 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 ~/effective-shell folder.

Downloading the Samples

Run the following command in your shell to download the samples:

curl effective.sh | sh

Let's set the current working directory to the effective-shell folder and run the find command:

$ cd ~/effective-shell
$ find
.
./text
./text/simpsons-characters.txt
./scripts
./scripts/show-info.sh
./websites
./websites/simple
./websites/simple/index.html
./websites/simple/styles.css
./websites/simple/code.js
...

By default, find will list all of the files and folders which are present in the current working directory. It will also show the children of any folders it finds, meaning that it shows the full hierarchy of files and folders.

Find not working on MacOS

If you are running these samples on MacOS, you will probably see the following output:

$ find
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]

This is the find command telling you what parameters can be used. On MacOS the default find command does not assume the current working directory.

This is because there is a difference between the MacOS and GNU versions of 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).

To run the equivalent command on MacOS, just provide the current directory as a parameter:

$ find .

A better solution is to install the findtools package, which will install the GNU versions of the tools we'll be using:

$ brew install findtools
$ gfind

If you do install findtools, remember that all of the GNU versions of the tools start with g - so when reading this chapter substitute find with gfind.

For more details on what BSD and GNU are, you can check Chapter - Unix, Linux, GNU and POSIX, which covers these concepts in detail.

So this is the 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.

You can also provide multiple directories:

$ find /usr/bin /usr/sbin
/usr/bin
/usr/bin/fwupdtool
/usr/bin/gnome-keyring
...
/usr/sbin
/usr/sbin/cupsd
/usr/sbin/pppdump
...

This is the most basic use of find - showing a file and folder hierarchy. Now let's look at how to search using this command.

Searching with Find

Perhaps the most common use for 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 ~/effective-shell folder as a playground to search in.

Searching for Files or Folders only​

The -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 -type f:

$ find . -type f
./text/simpsons-characters.txt
./scripts/show-info.sh
./websites/simple/index.html
./websites/simple/styles.css
./websites/simple/code.js
...

And for folders, using -type d (remember, d is for directory!):

$ find . -type d
.
./text
./scripts
./websites
./websites/simple
...

It's important to note that when searching for folders, the find command shows folders which are normally hidden, such as the special 'dot' folder1.

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.

Searching by Name​

We can use the -name parameter to search for files and folders by name. For example, this is how we would search for anything with the letters log in the name:

$ find . -name "*log*"
./logs
./logs/web-server-logs.txt
./logs/apm-logs
./logs/apm-logs/apm05.logs
./logs/apm-logs/apm02.logs
./logs/apm-logs/apm03.logs
./logs/apm-logs/apm00.logs
./logs/apm-logs/apm01.logs
./logs/apm-logs/apm04.logs

You can see I've used a * wildcard before and after the letters log - this means that I have actually supplied a pattern, which could be read as 'any characters (including nothing), followed by the characters log, followed by any other characters (including no characters)'.

If I don't use a wildcard, the logs folder will not be found - because it doesn't match the pattern log - without using a wildcard, the pattern is explicitly looking for an exact match:

$ find . -name "log"

Note the output - no files or folders were found, as none have the exact name log. The -name parameter is very specific - it will only match files or folders with the name provided. Here's an example:

$ find . -name "apm00.logs"
./logs/apm-logs/apm00.logs

Here I have used -name to search for an exact name. What about if I search for apm-logs?

$ find . -name "apm-logs"
./logs/apm-logs

The folder named apm-logs is found, but not the files in the folder - the names of those files don't match the pattern apm-logs. What if we make it a wildcard pattern?

$ find . -name "*apm-logs*"
./logs/apm-logs

The same results! This is because for the files in the apm-logs folder they don't have apm-logs in their name anywhere - that is in their path, i.e. the full address of the file including its folder. So let's look at how to search by path next!

An Important Note on Quotes

Make sure you use quotes when building your patterns. This command:

$ find . -name "*log*"

Will give different output to this command:

$ find . -name *log*

Why is this? In the first case, we explicitly pass the text *log* to the 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.

In the second case, the shell itself tries to deal with the wildcards, then passes the results to find. And the shell deals with them differently. You can see exactly what the shell expands them to with this snippet:

$ parameter=(*log*)
$ echo $parameter
logs

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 not to interfere with the text and instead pass it to find, so that find can deal with the wildcard.

The shell is using globbing in the second case, which is covered in a later chapter.

Searching by Path​

The -path parameter can be used to filter the results based on a pattern in the path of the file:

$ find . -path "*apm-logs*"
./logs/apm-logs
./logs/apm-logs/apm05.logs
./logs/apm-logs/apm02.logs
./logs/apm-logs/apm03.logs
./logs/apm-logs/apm00.logs
./logs/apm-logs/apm01.logs
./logs/apm-logs/apm04.logs

Again, note that this is very specific, we've added wildcards to the pattern, making it *apm-logs*. Without the wildcards we would find nothing, because none of the results have the exact path apm-logs.

Combining Searches - the AND and OR operators​

We can provide multiple search options. For example, if we want to search only for files which end in .logs, we can do this:

$ find . -type f -name "*.logs"
./logs/apm-logs/apm05.logs
./logs/apm-logs/apm02.logs
./logs/apm-logs/apm03.logs
./logs/apm-logs/apm00.logs
./logs/apm-logs/apm01.logs
./logs/apm-logs/apm04.logs

By using both the -type and -name parameters, we've created an 'AND' style search - i.e. we're looking for items which match both of the given criteria.

What if we want to perform a search which returns items which match either of the patterns (i.e an 'OR' search)? For that we can use the -or parameter:

$ find . -name "*.js" -or -name "*.html"
./websites/simple/index.html
./websites/simple/code.js
./programs/web-server/web-server.js

In this case we show results which match either of the expressions.

Case Insensitive Searches​

Any one of the search parameters you've seen so far can be made case-insensitive by putting the letter i before the parameter name.

This means that the following commands are identical:

$ find . -name "*.js" -or -name "*.Js" -or -name "*.jS" -or name "*.JS"

And:

$ find . -iname "*.js"

I know which one I'd rather type! You can use -iname for case-insensitive name searches, or -ipath for case-insensitive path searches.

Grouping and the NOT operator​

We can build more complex expressions by grouping together patterns using brackets, or by using the -not pattern. Here's an example:

$ find . \( -name "*.js" -or -name "*.html" \) -and -not -path "*programs*"
./websites/simple/index.html
./websites/simple/code.js

The first section groups together two expressions, meaning "files with names which match *.js or *.html, the second section then says "and also the text programs must not be in the path".

The only annoying thing about grouping is that you must escape the brackets, by putting a \ 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 find command.

Why the Weird Parameters?

The find command bothered me for years. The parameters looked odd - for example why is it -name instead of -n or --name, which would be more standard2?

Also, why is it that I cannot type this:

find -name "something.txt" /home/

But instead have to put the folder name before the parameters, which again is non-standard?

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 search expression.

The structure of the find command is as follows:

find <options> [starting point...] [expression]

The options (or parameters) for the find command are short, one-letter options which go before the file name. The -L (follow links) option is an example - it goes before the starting point of the search:

find -L /usr/bin -name "*sh"

All of the other things we've seen so far which we've described as parameters are actually used to build the expression - the actual search pattern.

The -name, -and, -or, -ipath and similar constructs we've looked at are actually part of a mini 'search language', they're not parameters to the command.

This might seem obvious, or it might seem silly, but either way, remembering that the structure of the find command is find <options> [starting points...] [expression] may help you remember what order to write each part of the command in.

It certainly took a few years for me to realise this was the reason, and until that point I used to get frustrated with 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.

You can find details on this in the manpage for find, open it with man find.

Performing Actions on Search Results

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.

The find command can perform operations on the files which are found.

Now before we continue, I'll warn that I'm not going to go into much detail here. The reason is that I actually recommend not using these operations. Instead, use the xargs command which is covered in Chapter 14 - Build Commands on the Fly with Xargs. The Xargs command lets you take the list of files from 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 find - and it is closer to the Unix Philosophy of having the find command 'do one thing and do it well'.

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!

Printing Paths​

The -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.

Here's how we'd find all files in the user's home directory with the ending .tmp and show their path:

$ find ~ -name "*.tmp" -print
/home/dwmkerr/commands1.tmp
/home/dwmkerr/commands2.tmp
/home/dwmkerr/commands3.tmp

You are unlikely to need to use -print - but it will come in handy to know it exists later on when we look at the -print0 option (we'll see this in Chapter 14). Let's look at the other actions we can perform.

Deleting Files​

We can delete the files and folders found by using the -delete action:

$ find ~ -name "*.tmp" -delete

This can be a convenient way to delete files, but I would recommend 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 children of folders are deleted where necessary before the folders themselves. This might not be what you expect because that's not what the -print output would show (although you can force this behaviour with the -depth option).

Check Chapter 14 for a better solution - in short we can use find ~ -name "*.tmp" -print0 | xargs -p -0 rm to instead pass the files to rm and ask the user to confirm before the deletion happens. This will be explained in a lot more detail in Chapter 14.

Execute a Command​

You can use the -exec action to execute an arbitrary command. Use the special characters {} as a placeholder for the filename.

Here's an example - we'll find all text files and count the number of words in each one:

$ find ~/effective-shell -name "*.txt" -exec wc -w {} \;
29 /home/parallels/effective-shell/text/simpsons-characters.txt
20 /home/parallels/effective-shell/quotes/iain-banks.txt
16 /home/parallels/effective-shell/quotes/ursula-le-guin.txt
10373 /home/parallels/effective-shell/logs/web-server-logs.txt

We use -exec to tell find we want to execute a command. Then we use wc -w {} to say "call the wc (word count) command with the -w (words) flag. The {} text is expanded to the list of files. Finally, we need a semi-colon to tell find where the end of the exec command is. And because the semi-colon has a special meaning in the shell, we have to escape this semi-colon by putting a \ backslash before it.

The -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.

Execute a Command with a Confirmation​

Now if there is one action to learn, it is the -ok action, which works just like -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 *.txt:

$ find ~/effective-shell -name "*.txt" -ok rm {} \;
< rm ... /home/parallels/effective-shell/text/simpsons-characters.txt > ? n
< rm ... /home/parallels/effective-shell/quotes/iain-banks.txt > ? n
< rm ... /home/parallels/effective-shell/quotes/ursula-le-guin.txt > ? n
< rm ... /home/parallels/effective-shell/logs/web-server-logs.txt > ? n

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 n (for 'no'). Type y (for 'yes') if you want to run the command.

Handling Symlinks

It is worth briefly mentioning symlinks - because if you don't understand how find handles symlinks then you might be surprised.

As an example of how this might surprise you, compare the output of the two commands below:

$ find /usr/bin
/usr/bin
/usr/bin/uux
/usr/bin/cpan
/usr/bin/BuildStrings
/usr/bin/loads.d
/usr/bin/write
...
$ find /bin
/bin

It seems that /bin doesn't contain any files - but is that the case? Running ls /bin will probably show that you have quite a few files. If you are on MacOS instead try running find /tmp to show the same oddity - the find program doesn't seem to show the contents of the files.

So why did find /bin not show the files in the /bin folder?

The answer is that /bin is a symlink (or if you are on MacOS and want to test the same behaviour and are using /tmp, /tmp is a symlink). You can see this by running the command below:

$ ls -l / /usr | grep bin
lrwxrwxrwx 1 root root 7 Aug 7 18:06 bin -> usr/bin
lrwxrwxrwx 1 root root 8 Aug 7 18:06 sbin -> usr/sbin
drwxr-xr-x 2 root root 40960 Jan 25 17:17 bin
drwxr-xr-x 2 root root 20480 Jan 25 16:42 sbin

Note how the root bin and sbin folders are actually just symlinks to usr/bin and usr/sbin respectively.

When using the find command just remember that it won't follow symlinks by default - provide the -L option to follow symlinks:

$ find -L /bin
/bin
/bin/fwupdtool
/bin/gnome-keyring
/bin/dpkg-gencontrol
/bin/prltoolsd
...

There are more options which control how find works with links, check man find for the details.

Scratching the Surface

The find command is 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!

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 find, which might make you want to learn more, here are a few commands which show just how versatile it can be!

Find large files

The -size test can be used to search by file size - note how with a + and - we can set the minimum and maximum sizes:

find / -size +1G -500G

Find recently edited files

The -mtime test can be used to find files which were recently modified:

find . -not -path "*/\.*" -mtime -2

Note how a -not -path test is used to skip anything which starts with a . dot (i.e files and folders which are normally considered hidden).

Find files which have had permissions changed

The -ctime test can be used to find files which have had their attributes (such as permissions) changed:

find ~/.ssh -ctime -30

Find any executable scripts and make them non-executable

We can search by permissions, as shown below:

find ~ -perm /a=x -exec chmod -x {} +

This example uses the -perm test, checking if 'all' (users, the owner and group) have the x (executable) bit set, then executes the chmod -x command to remove the executable bit. We also end the command with + rather than ;, which means we will execute chmod once with each file passed to the command (rather than running chmod for each file). Note that the + operator can cause an error if the list of files is too big for the command you pass it to to handle!

Find empty folders and remove them with a confirmation

We can use the -empty test to find empty folders:

find ~ -type d -maxdepth 3 -empty -ok rmdir {} \;

This example uses the -empty test, as well as the -maxdepth parameter to limit the search to only three folders deep.

These examples just scratch the surface of what find can do. The goal of this chapter is not to have an exhaustive description of the find command, but equip you with the essentials. When you feel comfortable with the essentials you can then build on your knowledge of find.

Summary

In this chapter we introduced the 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.

In the next chapter we'll take a look in a bit more detail into what a shell actually is!


Share - with "why the hell are the parameters so stupid" +Blog post on linux essentials, refer to alpine for an example of why find is important +Test work in progress page

Footnotes


  1. If you are not sure what the 'dot' folder is, check Chapter 2 - Navigating Your System↩
  2. This is not just a personal preference thing; this is based on the POSIX standard: 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.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-2-core-skills/fly-on-the-command-line/index.html b/pr-preview/pr-346/part-2-core-skills/fly-on-the-command-line/index.html new file mode 100644 index 00000000..89d53790 --- /dev/null +++ b/pr-preview/pr-346/part-2-core-skills/fly-on-the-command-line/index.html @@ -0,0 +1,27 @@ + + + + + +Fly on the Command Line | Effective Shell + + + + + + + + + + + + + + +
+

Fly on the Command Line

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.

In this chapter, we'll look at the ways you can rapidly move your cursor around on the command line, as well as how to easily open up the current command in an editor (which is incredibly useful if you realise you are building a more complex sequence of commands).

Below is a quick reference which you can use - we'll see each operation in more detail as we go through the chapter!

command line

Basic Navigation​

Let's assume we have a very simple command we are writing, which is going to write a quote to a text file:

echo "The trouble with writing fiction is that it has to make sense,
whereas real life doesn't. -- Iain M. Banks" >> quote.txt

Navigating around long lines of text is a slow process if you are only relying on the arrow keys.

Let's see how we can quickly move around and manipulate text!

Go to beginning / end​

Quickly jump to the beginning or end of the text:

  • Ctrl + a - Go to beginning
  • Ctrl + e - Go to end

begin / end

Move backwards / forwards one word​

For a little more fine-grained movement, you can jump backwards or forwards one word at a time:

  • Alt + b - Go back one word
  • Alt + f - Go forward one word

backward / forward

Delete a word or undo a mistake​

As this is the first operation we're seeing which changes the text, it is useful to remember how to undo any changes you make!

  • Ctrl + w - Delete a word
  • Ctrl + - - Undo most recent change

delete / undo

Delete the next word​

We've seen how to delete the word on or behind the cursor, now let's see how to delete the next word:

  • Alt + d - Delete next word

delete next word

Remember, just like any edit you can undo these changes with the Ctrl + - command.

Delete to beginning / clear line​

In the Bash shell, you can delete all the way to the beginning of the line with Ctrl + u. However - if you are using the Z-Shell this will delete the entire line!

  • Ctrl + u - Delete to beginning of line OR delete line

delete to beginning

Delete to end​

You can also delete all of the way to the end of the line.

  • Ctrl + k - Delete to end

delete to end

When you find yourself repeatedly using the arrow or delete keys, refer back to this section to remind yourself of the shortcut - it will save a lot of time in the long run!

Searching​

Once you have the basic navigation commands down, the next essential is searching. Let's assume we've run the following three commands:

$ command1 param1 param2 param3
$ command2 param4 param5 param6
$ command3 param7 param8 param9

You can search backwards or forwards with Ctrl + r and Ctrl + s. This will search in the current line and then iteratively through previous lines:

search backwards and forwards

People often remember this as searching through the history - but remember that it actually searches the current line as well. So this is often the fastest way to move to the desired location in the current line.

This is useful for searching in the current command, but can be also used to quickly search backwards and forwards through the command history:

search commands backwards and forwards

As you type, your command history is searched, the most recent commands coming first. Use the arrow keys to edit the command, press enter to execute it, or Ctrl + g to cancel the search.

I think it's a little easier to see these commands in action with a more realistic example, so here's how they look with the text we used earlier.

Search Backwards / Forwards​

Search backwards or forwards through the current line and also the history:

  • Ctrl + r - Search backwards (reverse search)
  • Ctrl + s - Search forwards

find next occurrence

This one is easy! Just hit Enter: +execute

Edit the command found​

When you have found the command or positioned the cursor where you want it, use the Left or Right arrow keys to stop searching and to go back into the normal editing mode:

edit command

Stop Searching​

Cancel the search and return back to the text as it was before you started with the Ctrl + g command:

cancel search

Editing In-Place​

These tips and tricks are helpful, but if you are working with a really long or complex command, you might find it useful just to jump into your favourite editor. This is one of those tricks that when you know it, you'll wonder how you lived without it!

Use Ctrl + x , Ctrl + e to edit-in place, opening the current command line in your default editor:

edit in place

Now it's important to explain that this is the shell's default editor. This might not be the same as the default editor for your operating system. You can see what the shell is using as its default editor by printing the contents of the EDITOR environment variable. For example, my shell will show this:

$ echo $EDITOR
vim

This means vim will be used to edit the command line. Your shell might use emacs or nano as a default editor. Unless you are familiar with vim or emacs, you might not find them particularly user friendly as an editor. You can change your default editor by setting the EDITOR variable. For example, below I set the editor to code (with the -w flag which tells the code program not to return control immediately back to the shell but instead wait until I've finished editing the file):

Screen Capture: Edit in Place with Visual Studio Code

Now this works (just about), but I wouldn't recommend using a Graphical Editor like Visual Studio Code for this. The reason is that because the editor runs in a separate window, it is actually easy to lose track of it (or the shell). You pause to take a short break, come back, close the editor and the contents are either lost or written to the shell (and if you see in the example above, the shell actually executed the command, rather than just putting it in the command line ready for me to execute).

The other reason to avoid a graphical editor is that if you are using a shell on another user's machine, the editor might not be present (or might be configured differently). In general however, the main reason to avoid a graphical editor is that it moves the context of the command away from where you are in the shell to another place, which can be confusing. If you see the screenshot below, I have two editors open:

Screenshot: Two shell editors

The top right pane has my git commit command running (which is asking me to write a description of my changes) and the bottom right pane has the command line editor running (where I am testing out the commands for this chapter).

In this example, each editor has taken the place of the contents of the shell, so there's no ambiguity about which command I am editing. If I was to open a graphical editor, it would open multiple tabs for this operation and I'd have to track which tab was which.

It can be daunting to learn an editor like vim or emacs. Chapter 27 goes into more detail on the terminal based text editors - for now if you are not familiar with these programs I recommend you use the nano editor. Nano is small, simple and shows the shortcuts in a convenient menu at the bottom of the screen:

Screenshot: The Nano Editor

In Chapter 18 we'll see how to make permanent customisations to the shell, configuring things like the default editor.

Clear the Screen​

Probably the shortcut I use the most is Ctrl + l, which clears the screen without trashing your current command. Here's how it looks:

clear screen

This is very helpful if you have a lot of noise and output on the screen and are ready to start with a fresh command.

See the History and Execute a Recent Command​

Just a few days ago a friend showed me a fantastic trick. If you run the history command, the shell will print the recent history of commands you have entered. But as an added bonus, you can execute any of these commands by entering an exclamation mark and the number next to the command:

Screenshot History and Execute Recent Command

The number is actually just the line number in the history file. Most shells maintain a history of commands which have been entered (to allow for things like searching through old commands). Where this history file is kept will depend on your shell, configuration and operating system, but in most cases you can find the file by running:

echo $HISTFILE

There are many configuration options for the shell history. But the main thing to remember is that you can see recent history with the history command and quickly execute the command at line n by running !n.

Pro Tip: All The Keys!​

You can use the bindkey command to see a list of all keyboard shortcuts:

$ bindkey
"^@" set-mark-command
"^A" beginning-of-line
"^B" backward-char
"^D" delete-char-or-list
"^E" end-of-line
"^F" forward-char
"^G" send-break
"^H" backward-delete-char
"^I" expand-or-complete
"^J" accept-line
"^K" kill-line
"^L" clear-screen
...

This is an extremely useful command to use if you forget the specific keyboard shortcuts, or just want to see the shortcuts which are available.

Pro Tip: Transposing!​

If you've mastered all of the commands here and feel like adding something else to your repertoire, try this:

transpose-word

The Alt + t shortcut will transpose the last two words. Use Ctrl + t to transpose the last two letters:

transpose-letters

These two commands were new to me when I was researching this chapter. I can't see myself ever being able to remember the commands more quickly than just deleting the last two words (Ctrl+w twice!) or characters and re-typing them, but perhaps you'll find them useful!

The Power of Readline​

All of the movement commands you've learned in this chapter apply to:

  1. Bash
  2. zsh
  3. The Python REPL
  4. The Node.js REPL

And many more! The reason is that all of these programs use the same library under the hood to control reading command line input. This library is called GNU Readline.

If you are ever looking to go deeper on this topic then search the web for GNU Readline. You can actually configure lower level details of how all programs which use readline work, with the .inputrc configuration file.

This configuration file can be used to configure things like the shortcuts used to move around. All of these shortcuts should be familiar to Emacs users. There is in fact also 'Vi Mode' option for readline, which allows you to use vi commands to work with text. You can enter this mode with set -o vi.

There's a great cheat sheet on emacs readline commands at readline.kablamo.org/emacs, which is a very useful reference if you want to dig deeper.

We'll also see GNU Readline later on when we talk about writing programs which work well in the shell.

I hope that was useful! Being able to rapidly move around the command line will hopefully save you time and make you a more confident user of not just the shell, but many command line programs.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-2-core-skills/job-control/index.html b/pr-preview/pr-346/part-2-core-skills/job-control/index.html new file mode 100644 index 00000000..ff539260 --- /dev/null +++ b/pr-preview/pr-346/part-2-core-skills/job-control/index.html @@ -0,0 +1,26 @@ + + + + + +Job Control | Effective Shell + + + + + + + + + + + + + + +
+

Job Control

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.

What Is Job Control?​

Let's start with an example. I am building a simple web page. It has one index.html file, one styles.css file, and one code.js file. The index.html file looks like this:

<html>
<head>
<title>My New Project</title>
<link rel="stylesheet" type="text/css" href="styles.css">
<script src="code.js"></script>
</head>
<body>
<!-- Snip... -->
</body>
</html>

Opening the file in a browser doesn't quite work, as it won't load the code or the styles. We need a web server to serve styles and code.

A super-useful one-liner to run a web server on any machine with Python installed is:

python -m SimpleHTTPServer 3000

In fact, this is so useful that I normally alias this command, so that I can just type serve. We'll see aliases in a later chapter.

Make sure you have the samples folder downloaded.

Downloading the Samples

Run the following command in your shell to download the samples:

curl effective.sh | sh

Now run the following commands to open the webpage:

$ cd ~/effective-shell/websites/simple
$ python -m SimpleHTTPServer 3000

For now, if we run this command, then we can open the webpage in a browser, with the styles and code loaded:

Screenshot: Website

We can also see that the server has served the HTML, JavaScript, and CSS files, this is clear from the output of the Python command we ran:

$ python -m SimpleHTTPServer 3000

Serving HTTP on 0.0.0.0 port 3000 ...
127.0.0.1 - - [08/Jan/2021 16:33:40] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [08/Jan/2021 16:33:40] "GET /styles.css HTTP/1.1" 200 -
127.0.0.1 - - [08/Jan/2021 16:33:40] "GET /code.js HTTP/1.1" 200 -
127.0.0.1 - - [08/Jan/2021 16:33:40] code 404, message File not found
127.0.0.1 - - [08/Jan/2021 16:33:40] "GET /favicon.ico HTTP/1.1" 404 -

All well and good so far. But if you try and use the shell to do something else, you will encounter a problem, let’s take a look.

The Problem​

Let's say we want to now continue using our shell, maybe to edit the website with a terminal editor like Vim or Emacs, or we want to zip up the site, or just run any shell command1.

We have a problem. The python process is still running - it's serving the website. Our shell is essentially useless, until we stop the server. See what happens when I try to edit a file:

Demo: Blocked Shell

In the example above, I try to run vi, but nothing is happening. Standard input is not being read by the server and not being interpreted by the shell.

I have to kill the server by hitting Ctrl+C. This sends a SIGINT signal (which tells the command to stop). We saw signals briefly in Chapter 4 - Becoming a Clipboard Gymnast and we'll see more of them in as we continue. Now I need to clear my screen to get rid of all of the error messages, then start again.

This is obviously not optimal. Let's look at some solutions.

Solution 1: Start the Server in the Background​

In most shells, you can run a command and instruct the shell to run it in the background. To do this, you end the line with an ampersand. Here's how the example would look in this case:

$ python -m SimpleHTTPServer 3000 &
[1] 7025

$ Serving HTTP on 0.0.0.0 port 3000 ...

By ending the command with an & ampersand symbol, we instruct the shell to run the command as a background job. This means that our shell is still functional. The shell has also notified us that this command is running as a background job with a specific job number:

$ python -m SimpleHTTPServer 3000 &
[1] 19372

In slightly obtuse language, the shell has informed us that it has started a job in the background, with job number 1 and that this job is currently handling the process with ID 19372.

The ampersand solution is a fairly common pattern used in day-to-day work. The process is in the background and our shell is available for us to use as normal, the web server will continue to run in the background.

Solution 2: Move the Server to the Background​

Let's say you forgot to start the command in the background. Most likely in this case you'd kill the server with Ctrl+C and then start it again with the & option. However, what if this was a large file download or a task you didn't want to abort?

In the example below, we'll move the job to the background:

$ python -m SimpleHTTPServer 3000
Serving HTTP on 0.0.0.0 port 3000 ...
^Z
[1] + 7657 suspended python -m SimpleHTTPServer 3000

The process is currently in the foreground, so my shell is inactive. Hitting Ctrl+Z sends a 'suspend' signal to the process2, pausing it and moving it to the background.

Let's dissect this:

$ python -m SimpleHTTPServer 3000
Serving HTTP on 0.0.0.0 port 3000 ...
127.0.0.1 - - [03/Jun/2019 13:38:45] "GET / HTTP/1.1" 200 -
^Z
[1] + 21268 suspended python -m SimpleHTTPServer 3000

The shell echos as I type, so we see ^Z (i.e., the Ctrl+Z chord I entered). The shell responds by moving the process into a background job and suspending it.

The key here is that it is suspended. The process is paused. So the web server is no longer serving. If you are following with the sample, reload your browser. The webpage fails to load, as the server process is not able to respond to requests.

To continue the job, in the background, we use the bg ('background') command, with a job identifier (which always starts with a % symbol - we'll see why soon) to tell the shell to continue the job:

$ bg %1
[1] + 21268 continued python -m SimpleHTTPServer 3000

The shell lets us know the job is being continued, and if we load the webpage again, the content is shown as expected.

As a final check, we run the jobs command to see what jobs the shell is running:

$ jobs
[1] + running python -m SimpleHTTPServer 3000

And there you have it - our server is running as a background job. This is exactly what we would see if we run jobs after starting the server with an & at the end. In fact, using an & is perhaps an easier way to remember how to continue a suspended job:

$ %1 &
[1] + 21268 continued python -m SimpleHTTPServer 3000

In the same way ending a command with & runs it in the background, ending a job identifier with & continues it in the background.

There is at least one more way to move a job to the background3, but I have not yet found it useful in any scenarios, and it is overly complex to explain. See the footnote for details if you are interested.

Moving Background Jobs to the Foreground​

If you have a job in the background, you can bring it back to the foreground with the fg ('foreground') command. Let's show the jobs, with the jobs command:

$ jobs
[1] + running python -m SimpleHTTPServer 3000

Here I have a background job running a server. Any one of the following commands will bring it back to the foreground:

fg %1   # Explicitly bring Job 1 into the foreground

%1 # ...or in shorthand, just enter the job id...

fg # ...if not given an id, fg and bg assume the most recent job.

Now the job is in the foreground, and you can interact with the process again however you like.

Cleaning Up Jobs​

You might realise you cannot continue what you are doing because an old job is still running. Here's an example:

Demo: Cleaning Up Jobs

I tried to run my web server, but there was still one running as a background job. The server failed to start because the port is in use.

To clean it up, I run the jobs command to list the jobs:

$ jobs
[1] + suspended python -m SimpleHTTPServer 3000

There's my old web server. Note that even though it is suspended, it'll still be blocking the port it is serving on4. The process is paused, but it is still holding onto all of the resources it is using.

Now that I know the job identifier (%1 in this case), I can kill the job:

$ kill %1
[1] + 22843 terminated python -m SimpleHTTPServer 3000

This is why job identifiers start with a percentage sign! The kill command I have used is not a special job control command (like bg or fg). It is the normal kill command, which terminates a process. But shells that support job control can normally use a job identifier in place of a process identifier. So rather than working out what the process identifier is that I need to kill, I can just use the job identifier5.

Why You Shouldn't Use Jobs​

Avoid jobs. They are not intuitive to interface with and they suffer from some challenges.

The most obvious one is that all jobs write to the same output, meaning you can quickly get garbled output like this:

Screenshot: Garbled Output

This is what happens when I run a job, which just outputs text every second. It's in the background, but it's printing all over my commands. Even running the jobs command to try and find the job to stop it is difficult.

Input is even more complex. If a job is running in the background, but requires input, it will be silently suspended. This can cause confusion.

Jobs can be used in scripts but must be done so with caution and could easily confuse a consumer of the script if they leave background jobs hanging around, which cannot be easily cleaned up6.

Handling errors and exit codes for jobs can be problematic, causing confusion, poor error handling, or overly complex code.

If jobs should be avoided, why discuss them at all? Well sometimes you move things into the background by mistake, sometimes it can be useful to quickly shift a download or slow command into the background, and also if you are going to avoid something it's good to know why! And the challenge of managing multiple units of work on a computer has been around for a long, long time, with jobs as one of the tools in the toolkit to deal with the challenge.

But given I'd suggest to avoid jobs, let's summarise with the most key takeaways and some alternatives.

The Most Key Takeaways​

If there are two things to take away, they would be:

If you have started running a command in the foreground, and you don't want to stop it and would rather move it to the background, hit Ctrl+Z. Then Google "job control".

And:

If you think there is a job running in the background, and it is messing with your screen, type fg to bring it to the front and kill it with Ctrl+C. Repeat as needed!

In either case, if you need to do something more subtle, you can return to this reference. But the first command should allow you to get your shell back while you work out how to continue the job, and the second should kill a background job that is messing with your screen.

Alternatives to Jobs​

If you are using any kind of modern terminal such as iTerm, Terminal or the GNOME Terminal, just open a new tab or split! Much easier.

The benefit to this is that each tab gets its own standard input and output, so there's no risk of overwriting. And of course you can hide/reveal/rearrange the tabs however you like.

The traditional alternative to a job for an operator who simply wants more than one thing going on at once would be a terminal multiplexer, such as screen or tmux:

terminal-multiplexer

Multiplexers work in a very similar way to a modern graphical terminal - they manage many shell instances. But there are some differences.

Modern terminals, such as iTerm, tend to have more intuitive GUIs and a lot of features. Multiplexers can be stateful - and manage work even when you close the shell (allowing you to 're-attach' later. We can also run them over SSH sessions to manage complex operations on remote machines. They run a client-server model, meaning many people can work with many multiplexed processes (and they can persist beyond sessions).

My personal preference is both - I use a modern terminal and run everything inside it in tmux, which is a very common multiplexer (and in some ways the spiritual successor to screen, an older multiplexer). We'll look at both of these options in later chapters.

Quick Reference​

You might find that jobs are useful, or you might find that they are not. Either way, here's a quick reference of some common commands:

CommandUsage
command &Run the command as a background job.
<Ctrl+Z>Move the current process into a background job, suspended.
jobsList all jobs.
fg %1Move background job number 1 into the foreground.
bg %1Continue background job number 1.
kill %1Terminate job number 1.
wait %1Block until job number 1 exits.

If you want to find out more about the gory details of jobs, the best place to start is the Bash Manual - Job Control Section, or the 'Job Control' section of your preferred shell's manual. On Bash you can find this by using man bash and searching for the text JOB CONTROL. You can find out more about how to get help in Chapter 5 - Getting Help


  1. If you are not a heavy shell user, this might seem unlikely. But if you do a lot of work in shells, such as sysadmin, devops, or do your coding from a terminal, this happens all the time!↩
  2. Technically, SIGTSTP signal - which is 'TTY stop'. If you have always wondered about the 'TTY' acronym, check the chapter, Interlude: Understanding the Shell.↩
  3. The alternative method is to use Ctrl+Y, which will send a delayed interrupt, which will continue to run the process until it tries to read from stdin. At this point, the job is suspended and the control given to the shell. The operator can then use bg or kill or fg to either move to the background, stop the process, or keep in the foreground as preferred. See: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Job-Control↩
  4. Another super-useful snippet: lsof -i -P -n | grep 8000 to find any process that has a given port open. Another one for the aliases chapter!↩
  5. There are times this is needed. If a job runs many processes - for example, by running a pipeline - the process identifier will change as the command moves from one stage of the pipeline to the next. The job identifier will remain constant. Remember, a job is a shell command, so could run many processes.↩
  6. To see how bad this can be, create a script that starts jobs, then run it. Then run the jobs command to see what is running. The output might surprise you!↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-2-core-skills/thinking-in-pipelines/index.html b/pr-preview/pr-346/part-2-core-skills/thinking-in-pipelines/index.html new file mode 100644 index 00000000..f46a42d9 --- /dev/null +++ b/pr-preview/pr-346/part-2-core-skills/thinking-in-pipelines/index.html @@ -0,0 +1,26 @@ + + + + + +Thinking in Pipelines | Effective Shell + + + + + + + + + + + + + + +
+

Thinking in Pipelines

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.

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!

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.

Input and Output​

Many of the programs we have seen so far follow a very similar pattern:

Diagram: Input -> Program -> Output

In fact, when you get down to the details, there are very few programs which don't do something like this! As a more concrete example, we can look at the sort program - which sorts the input in alphabetic order:

Diagram: Sort

We can easily see this in action by just running sort in a shell. Start the sort program, enter some text, then press Ctrl+D. Ctrl+D (which is normally written as ^D is a special control character which means 'end of transmission' - in this case we use it to tell sort we've finished writing text. If you were to use ^C (which is the interrupt command) it would closes the sort program instead).

$ sort
Dogs
chase
cats
and
cats
chase
mice

Once you've entered the text you want to sort, hit ^D and you'll see the sorted output:

Dogs
and
cats
cats
chase
chase
mice

So by default, the sort command is reading input from the keyboard (until we send it a special message saying we're done), then writing the output to the terminal.

In fact, sort is using two special files - stdin and stdout - but what does this mean?

Standard Input, Output and Error​

Every program has access to three 'special' files, stdin, stdout and stderr:

Diagram: stdin, stdout, stderr
  • stdin is short for 'standard input' - it's where many programs read their input from
  • stdout is short for 'standard output' - it's where many programs write their output to
  • stderr is short for 'standard error' - it's where some programs write error messages to

Why do I say 'many' and 'some'? Well the reason is that while this is 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.

Each of these files has a special number which is shown in grey in the diagram. This is known as the 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 ls -al /dev/std*:

$ ls -al /dev/std*
lr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stderr -> fd/2
lr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stdin -> fd/0
lr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stdout -> fd/1

If you are not familiar with ls (the list directory contents command) then check 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 /dev/ (short for device folder). We can also see the associated file descriptors.

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.

When you are running programs in a shell, the shell attaches your keyboard to the program's standard input1, and attaches the standard output and standard error to the terminal display:

Diagram: Shell, Keyboard, Terminal

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.

We can already see the beginnings of a pipeline here. There's a clear flow of data from the keyboard, through the stdin file, through the program, then through the output files, then to the display.

Looking at some real programs in action will hopefully make this clearer!

A Pipeline in Action​

Do you remember the cat command? It's the one which writes the contents of a file to the screen. For example:

$ cat ~/effective-shell/text/simpsons-characters.txt
Artie Ziff
Kirk Van Houten
Timothy Lovejoy
Artie Ziff
Nick Riviera
Seymore Skinner
Hank Scorpio
Timothy Lovejoy
John Frink
Cletus Spuckler
Ruth Powers
Artie Ziff
Agnes Skinner
Helen Lovejoy

We saw in Chapter 4 - Becoming a Clipboard Gymnast that we could pipe the output of this command into the sort command to order it and then into the uniq command to remove duplicates, like this:

$ cat ~/effective-shell/text/simpsons-characters.txt | sort | uniq
Agnes Skinner
Artie Ziff
Cletus Spuckler
Hank Scorpio
Helen Lovejoy
John Frink
Kirk Van Houten
Nick Riviera
Ruth Powers
Seymore Skinner
Timothy Lovejoy

The pipe operator (which is the vertical pipe symbol or |) has a very specific meaning in the shell - it attaches the stdout of the first program to the stdin of the second. This means we can now visualise the entire pipeline and see exactly what is going on:

Diagram: cat-sort-uniq pipeline

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!

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 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.

Now we'll look at stdin, stdout and 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.

Common Patterns - Standard Input​

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.

Diagram: Input Examples

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.

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!

The Shell

You might just use code in the shell as input, for example:

$ echo "I am in the $PWD folder" | sed 's/folder/directory/'
I am in the /Users/dwmkerr/repos/github/dwmkerr/effective-shell directory

Here we've just used echo to write out message including a variable and then used the sed (stream editor) program to replace the word folder with directory. We'll get a lot of practice with sed as we go through this book!

Files

We've already seen a few examples of using cat to write a file to stdout.

A lot of the time we don't need to use 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:

$ cat ~/effective-shell/text/simpsons-characters.txt | wc -l

14

Or more simply, like this:

$ wc -l ~/effective-shell/text/simpsons-characters.txt
14 /Users/dwmkerr/effective-shell/text/simpsons-characters.txt

In this case, we've passed the file path as an argument to the wc (word, line, character and byte count) program. But be aware - not all programs use the same convention or parameter names!

Now here's a cool trick. Type rev < /dev/stdin, then enter some text and hit ^D or ^C when done. You should see something like this:

$ rev < /dev/stdin
Red Rum
muR deR

What's going on here? Remember we mentioned that stdin is a special stream which represents input and that it lives at /dev/stdin? This little trick uses redirection to redirect the stdin file to the rev (reverse) command.

The < operator redirects the standard input of a program to come from the given file. We could also have written cat /dev/stdin | rev. Or just enter rev and type in the input we want to reverse!

The Clipboard

In 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:

$ pbpaste | rev | pbcopy

This pipeline pastes the contents of the clipboard to stdout, which is piped to rev (reversing the text) and then pipes the output to pbcopy, which copies the results to the clipboard2.

Filtered Input

This is a trick a friend shared with me. He works with data scientists and whenever he shows them this command they love it!

$ head -n 100 100GBFile.csv > 100linefile.csv

The head (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 > symbol (the redirection symbol) means in the section lower down on Standard Output.

You can also use tail in the same way to get the last lines from a file. And if you are a more advanced user, you might use something like this:

$ grep -C 5 error /var/log/mylogfile.txt | less

We'll see all of these commands as we go through the book, but this very cool trick uses the grep (file pattern searcher) command to search for the text error in the file /var/log/mylogfile.txt, shows five lines of context (-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 grep-ing as we go through the book so don't worry if this looks a little confusing for now.

Many More!

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!

Common Patterns - Standard Output​

Now let's look at some of the things we can do with the standard output:

Diagram: Output Examples

Some of these outputs are things we've seen before, but let's do a quick revision.

Display

This is what we've been doing a lot of so far. When you are working with the shell interactively this makes a lot of sense.

If you have jobs which run in the 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.

What about if you have a 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 pager. A pager is a program which makes it possible to interactively page through output in the shell, scrolling up and down, searching and so on.

Try this out as an example:

ls /usr/bin /usr/local/bin /usr/sbin | less

You'll see something like this:

Screenshot: Less Example

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 d and u keys to go down and up, or the / and ? keys to search forwards or backwards.

Piping into your pager is a really useful trick - you can read more about pagers in Chapter 5 - Getting Help.

File

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 > or redirection operator:

$ echo "Here's some data" > some_file.txt

It's as easy as that! Note that this will overwrite anything already in the file.

Append

What if you don't want to overwrite a file, but instead just add a new line? The >> or append redirection operator:

$ echo "Tuesday was good" >> diary.txt
$ echo "Wednesday was better!" >> diary.txt
$ echo "Thursday suuucks" >> diary.txt
$ cat diary.txt
Tuesday was good
Wednesday was better!
Thursday suuucks

This example writes each line in turn to the diary.txt file, appending the text to the end of the file (and creating it if it doesn't already exist).

Appending to a file is extremely useful for circumstances where you might want to build or update a log of events over time.

Pipe

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!

In this case, the output of our program becomes the input of the next one in the pipeline.

Common Patterns - Standard Error​

We haven't actually seen stderr in action yet. Let's see how it works.

$ mkdir ~/effective-shell/new-folder
$ mkdir ~/effective-shell/new-folder
mkdir: /home/dwmkerr/effective-shell/new-folder: File exists

In the first call to 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.

There are lots of ways to make text uppercase in the shell, let's use the tr (translate characters) program. Here's an example of how it works:

$ echo 'Be quiet, this is a library!' | tr '[:lower:]' '[:upper:]'
BE QUIET, THIS IS A LIBRARY!

Now let's use it to shout out our error message:

$ mkdir ~/effective-shell/new-folder | tr '[:lower:]' '[:upper:]'
mkdir: /home/dwmkerr/effective-shell/new-folder: File exists

In this case the output has not been made uppercase. What's going on?

To understand, let's quickly review the three streams:

Diagram: stdin/stdout/stderr

When we are in the shell, the shell automatically writes the stderr stream to the screen. But the shell's pipe operator pipes stdout only - it is not piping our error output. And the mkdir command is writing this error message to stderr.

The command we ran before:

mkdir ~/effective-shell/new-folder | tr '[:lower:]' '[:upper:]'

Actually looks like this:

Diagram: Standard Error

The pipe has piped the standard output to the tr program. But there is no standard output - the error message was written to standard error instead. The shell has still written it to the screen for us, but has not piped it to the tr program.

So how do we deal with stderr? Here are some common options:

Diagram: Standard Error Options

Now this might be the ah-ha! moment if you have done some shell scripting before - some of these obscure sequences like 2>&1 might look familiar (even if it is just the thing you know you always have to Google to get right!).

Let's take a quick look at some of these options.

To Standard Output

If we want to be able to pipe the error message to another command, we can use another redirection trick - we can redirect stderr to stdout.

The characters 2>&1 look really obscure - let's break it down:

  • Take the file with descriptor 2 - which is standard error
  • Redirect it with the redirect symbol > - we saw this in the earlier section
  • Redirect it into the file with descriptor (&) 1 - which is standard output

Remember, there are three 'magic' files each process has access to:

  • stdin, the standard input, which has the file descriptor 0
  • stdout, the standard output, which has the file descriptor 1
  • stderr, the standard error, which has the file descriptor 2

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:

python <<EOF
import os
for r in range(3): print(os.open('/dev/random', os.O_RDONLY))
EOF

This code uses redirection (see how useful it is?) to pipe a small Python script into the Python program, which writes the results to stdout. You will probably see the following output:

3
4
5

It doesn't really matter whether you know Python or not (and there weird looking EOF is a heredoc which we have a whole chapter on later). The script is just a way of showing the file descriptors that the operating system gives me when I try to open three files (each time I open the same file, the magic /dev/random file which just contains random data).

The interesting thing is that the descriptors in my program start from 3 and go upwards - that's because 0, 1 and 2 are already in use, for stdin, stdout and stderr!

So to make our error message go through the tr command, we can redirect stderr to stdout, which means the error message will go to stdout and then be piped to tr:

$ mkdir ~/effective-shell/new-folder 2>&1 | tr '[:lower:]' '[:upper:]'
MKDIR: /HOME/DWMKERR/PLAYGROUND/NEW-FOLDER: FILE EXISTS

Visually, what is happening is this:

Diagram: stderr redirect

If you can wrap your head around this, the other options we showed for stderr might start to make a little more sense.

A nice trick to remember the slightly obscure ampersand & which references a file descriptor - if you were to write this:

cat some-file-that-might-not-exist 2>1

What would happen is that the shell would write stderr to a new file with the name 1! Why don't we need an ampersand before the > 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.

To a File

Before, we redirected to &2, which is 'the file with descriptor 2. We can also use a similar trick to redirect to any arbitrary file:

mkdir ~/effective-shell/new-folder 2>./errors.txt

This command just redirects all of the errors (remember, 2 is stderr) to a file called ./errors.txt.

This is quite a common trick - run the program, but log the errors to a file for later review.

To Nowhere

What if we just don't want to see the errors at all? Well there's a special file called /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!

mkdir ~/effective-shell/new-folder 2>/dev/null

This just redirects all errors to the black hole of /dev/null - we won't see them on the screen or anywhere else. This is a common way to 'silence' errors3 in shell commands.

Notice how we're starting to see patterns? This is just redirection, the same tricks we saw for stdout, but we're explicitly redirecting stderr (file descriptor 2). If we don't tell the shell what to redirect, it assumes stdout by default.

So if we can redirect, can we append too?

Append

Yes! Just like we did with stdout, there's nothing stopping us appending to a file:

mkdir ~/effective-shell/new-folder 2>>./all-errors.log

Just like before, we use >> which means append (rather than overwrite or create).

All to a File

This is a really important subtlety. If you want to write both stdout and stderr to a file, you might try this:

ls /usr/bin /nothing 2>&1 > all-output.txt

If you run this command, you'll get stdout written to all-output.txt, but the error message cannot access '/nothing' is written to the screen, not the file. Why is this?

Bash (and most bash-like shells) process redirections from left to right, and when we redirect we duplicate the source. So breaking this down:

  • 2>&1 - duplicate file descriptor 2 (stderr) and write it to 1 - which is currently the terminal!
  • > all-output.txt - duplicate file descriptor 1 (stdout) and write it to a file called all-output.txt

To write everything to the file, try do this:

ls /usr/bin /nothing > all-output.txt 2>&1

This will work. Breaking it down:

  • Redirect stdout to the file all-output.txt
  • Now redirect stderr to stdout - which by this point has already been redirected to a file

This can be tough to remember so it's worth trying it out4. There are many variations you can play with and we'll see more as we go through the book.

One Last Trick - The T Pipe​

This is a long chapter, but I can't talk about pipelines without briefly mentioning the T pipe. Check out this command:

cat ~/effective-shell/text/simpsons-characters.txt | sort | tee sorted.txt | uniq | grep '^A'

This command sorts the list of Simpsons characters, removes duplicates and filters down to ones which start with the letter A. And it has the tee command in the middle. What does this do?

Well the tee command is like a T-pipe in plumbing - it lets the stream of data go in two directions! The sorted.txt file contains the sets of characters after the sort operation, but before the unique and filter operation. Visually, it does this:

Diagram: Tee

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.

Thinking in Pipelines​

Once you get comfortable with pipelines, a whole world of possibilities open up.

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:

cat data.dat | sort | uniq | grep -v '^#' | wc -l

I didn't have to find a special program which does exactly what I needed5 - 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:

  • cat data.dat - OK, first I need to write out the file
  • sort - now I can sort it, that'll put all the blank lines together
  • uniq - this'll remove all of those duplicate blank lines, although it still leaves one blank one at the top!
  • grep -v '^#' - this should get rid of all the lines which start with #
  • wc -l - this'll count the number of lines I'm left with

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.

Of course, as things get more complex you might want to build scripts, or use a programming language, or other methods, but this 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.

Summary​

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.

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!

In this chapter we looked at:

  • How each program has access to three 'standard' streams - one for input, one for output and one for reporting errors
  • The standard input stream is available as a file at /dev/stdin, is often called stdin in programming languages, and always has the special file descriptor 0
  • The standard output stream is available as a file at /dev/stdout, is often called stdout in programming languages, and always has the special file descriptor 1
  • The standard error stream is available as a file at /dev/stderr, is often called stderr in programming languages, and always has the special file descriptor 2
  • The Ctrl+D sequence means 'end of transmission' - we can use it to signal that we have completed putting our input into stdin...
  • ...but the Ctrl+C sequence means 'interrupt' and is normally used to force a program to close
  • We can pipe the output of one program to the input of another with the pipe | symbol
  • We can redirect a file to the standard input of a program with the < operator
  • We can redirect the standard output of a program to create or overwrite a file with the > operator
  • We can redirect the standard output of a program to create or append to a file with the >> operator
  • We can redirect the standard error of a program to its standard output with 2>&1
  • We can redirect the standard error of a program to another file (such as the 'null' file) with 2>/dev/null
  • We can redirect the standard error of a program to create or append to a file, just like with standard output, using the >> operator

We also briefly saw some commands:

  • sort sorts text
  • sed can replace content in text
  • tr can replace parts of text
  • wc can count words or lines of text
  • tee takes the input stream and sends it straight to the output, but also to a file (like a T-pipe in plumbing)
  • grep can filter lines

These programs can do a lot more and are workhorses we'll see in more detail through the book.

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:

  • Writing Good Programs - How to write programs which use stdin, stdout and stderr sensibly
  • The Unix Philosophy - Why we have so many small simple programs which we can pipe together
  • Streams in Detail - How streams like stdin actually work, especially with things like line endings, command sequences like ^D and so on
  • Signals - A little more on Signals (such as ^C and ^D)

When these chapters are published I'll update the links here. If you want to be updated when new chapters are published, you can Join the Mailing Lits on the Homepage.


  1. Technically there is another layer here, which is the tty. You can see this by running tty in the shell. We'll talk more about this in the Interlude - What is a Shell section.↩
  2. Check Chapter 4 - Becoming a Clipboard Gymnast for how to do this on a Linux or Windows machine.↩
  3. Although always use tricks like this with caution! If we had a different error, perhaps one we really do want to know about, we would lose the message in this case.↩
  4. There is a very detailed explanation of this behaviour at https://linuxnewbieguide.org/21-and-understanding-other-shell-scripts-idioms/.↩
  5. With the correct options, sed could likely do this in a single operation, but I'd probably spend a lot longer Googling the right options for it!↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-2-core-skills/understanding-commands/index.html b/pr-preview/pr-346/part-2-core-skills/understanding-commands/index.html new file mode 100644 index 00000000..9b79238f --- /dev/null +++ b/pr-preview/pr-346/part-2-core-skills/understanding-commands/index.html @@ -0,0 +1,26 @@ + + + + + +Understanding Commands | Effective Shell + + + + + + + + + + + + + + +
+

Understanding Commands

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.

By the end of this chapter, you might even be able to make sense of the horrifying and perfectly syntactically valid code below:

which $(where $(what $(whence $(whereis who))))

What Are Commands?​

This is really important to understand! A command in a shell is something you execute. It might take parameters. Generally it'll have a form like this:

command param1 param2

We've already seen many commands during this series:

ls              # Show the contents of the current directory
cd ~ # Move to the user's home
cat file.txt # Output the contents of 'file.txt' to stdout

But to be an effective shell user, you must understand that not all commands are created equal. The differences between the types of commands will affect how you use them.

The Different Types of Commands​

There are four types of commands in most shells:

  1. Executables
  2. "Built-Ins" (which we'll just call builtins from now on)
  3. Functions
  4. Aliases

Each is different and has its own quirks. Let's quickly dig in and see a bit more.

Executables - Programs​

Executables are just files with the 'executable' bit set1. If I execute the cat command, the shell will search for an executable named cat in my $PATH. If it finds it, it will run the program.

$ cat file.txt
This is a simple text file

What is $PATH? $PATH is the standard environment variable used to define where the shell should search for programs. If we temporarily empty this variable, the shell won't find the command:

$ PATH="" cat file.txt
bash: cat: No such file or directory

Normally your $PATH variable will include the standard locations for Linux programs - folders such as /bin, /sbin, /usr/bin and so on2.

If you were to print the variable, you'd see a bunch of paths (they are separated by colons; I've put them on separate lines for readability):

/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

The shell will start with the earlier locations and move to the later ones. This allows local flavours of tools to be installed for users, which will take precedence over general versions of tools.

There will likely be other locations too - you might see Java folders, package manager folders and so on.

Executables - Scripts​

Imagine we create a text file called dog in the local folder that looks like this:

#!/bin/sh
echo "🐢 woof 🐢"

This is a shell script (you've heard this before, but we'll see a lot more of these as we go through the book!). We mentioned that executables are any files which have the executable bit set. Let's actually do this, using the chmod (change file modes) command:

$ ls -l dog
-rw-r--r-- 1 dwmkerr staff 32 Oct 8 22:44 dog
$ chmod +x dog
$ ls -l dog
-rwxr-xr-x 1 dwmkerr staff 32 Oct 8 22:44 dog

I've used ls -l dog to show the file permissions of dog before and after the chmod +x dog3 command. We can see that there are some new x's in the first section. These are saying that the file is now executable by all users.

Now that we have made the file executable we can run this just like any other program - as long as we tell the shell to look for programs in the current directory:

$ PATH="." dog
🐢 woof 🐢

By the way - don't do this! Adding the special . directory to the path is generally not a safe or sensible thing to do, this is just a demonstration of how it works. More common would be to run the program by specifying the path to the file, like so:

$ ./dog
🐢 woof 🐢

Another option would just be to move it to a standard location that the shell already checks for programs:

$ mv dog /usr/local/bin
$ dog
🐢 woof 🐢

Executables don't have to be compiled program code, they can be scripts. If a file starts with #! (the 'shebang'), then the system will try to run the contents of the file with the program specified in the shebang.

We will look at shebangs in greater detail in a later chapter. But the key takeaway here is that we can also have executable scripts as commands.

Builtins​

OK, so we've seen executables. What about a command like this?

local V="hello" echo $V

You will not find the local executable anywhere on your system. It is a builtin - a special command built directly into the shell program.

Builtins are often highly specific to your shell. They might be used for programming (local for example is used to declare a locally scoped variable), or they might be for very shell-specific features.

This is where we need to take note. As soon as you are running a builtin, you are potentially using a feature that is specific to your shell, rather than a program that is shared across the system and can be run by any shell.

Trying to programmatically execute local as a process will fail - there is no executable with that name; it is purely a shell construct.

So how do we know if a command is a builtin? The preferred method is to use the type command:

$ type local
local is a shell builtin

The type command (which is itself a builtin!) can tell you the exact type of shell command.

Interestingly, you might be using more builtins than you think. echo is a program, but most of the time you are not executing it when you are in a shell:

$ type -a echo
echo is a shell builtin
echo is /bin/echo

By using the -a flag on type to show all commands that match the name, we see that echo is actually both a builtin and a program.

Many simple programs have builtin versions. The shell can execute them much faster.

Some commands are a builtin so that they can function in a sensible manner. For example, cd command changes the current directory - if we executed it as a process, it would change only the directory for the cd process itself, not the shell, making it much less useful.

Echo is builtin because the shell can run much more quickly by not actually running a program if it has its own built in implementation.

Builtins will vary from shell to shell, but many shells are 'Bash-like' - meaning they will have a set very similar to the Bash builtins, which you can see here:

https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html

As should be familiar from Chapter 5 - Getting Help, you can get help for builtins:

$ man source     # source is a builtin
BUILTIN(1) BSD General Commands Manual BUILTIN(1)

NAME
builtin, !, %, # ...snip...

SYNOPSIS
builtin [-options] [args ...]

However, the manual will not show information on specific builtins, which is a pain. Your shell might have an option to show more details - for example, in Bash you can use help:

$ help source
source: source filename [arguments]
Read and execute commands from FILENAME and return. The pathnames
in $PATH are used to find the directory containing FILENAME. If any
ARGUMENTS are supplied, they become the positional parameters when
FILENAME is executed.

But remember: help is a builtin; you might not find it in all shells (you won't find it in zsh, for example). This highlights again the challenges of builtins.

Functions​

You can define your own shell functions. We will see a lot more of this later, but let's show a quick example for now:

$ restart-shell () { exec -l $SHELL }

This snippet creates a function that restarts the shell (quite useful if you are messing with shell configuration files or think you might have irreversibly goofed up your current session).

We can execute this function just like any command:

$ restart-shell

And running type will show us that this is a function:

$ type restart-shell
restart-shell is a function
restart-shell ()
{
exec -l $SHELL
}

Functions are one of the most powerful shell constructs we will see; they are extremely useful for building sophisticated logic. We're going to see them in a lot more detail later, but for now it is enough to know that they exist, and can run logic, and are run as commands.

Aliases​

An alias is just a shortcut. Type in a certain set of characters, and the shell will replace them with the value defined in the alias.

Some common commands are actually already aliases - for example, in my zsh shell, the ls command is an alias:

% type -a ls
ls is an alias for ls -G
ls is /bin/ls

I make sure that when I use the ls command, the shell always expands it to ls -G, which colours the output.

We can quickly define aliases to save on keystrokes. For example:

$ alias k='kubectl'

From this point on, I can use the k alias as shorthand for the kubectl command.

Aliases are far less sophisticated than functions. Think of them as keystroke savers and nothing more, and you won't go far wrong.

The Key Takeaways​

So we now hopefully have a greater understanding of the variety of shell commands. Not all commands are executables, not all of the commands we think are executables necessarily are, and some commands might be more sophisticated.

As a shell user, the key things to remember are:

  1. Executables are programs your system can use; your shell just calls out to them.
  2. Builtins are very shell-specific and usually control the shell itself
  3. Functions are powerful ways to write logic but will normally be shell-specific.
  4. Aliases are conveniences for human operators, but only in the context of an interactive shell.

To find out how a command is implemented, just use the type -a command:

$ type -a cat
cat is /bin/cat

More than You Need to Know​

OK, for the masochistic few, you might be wondering about all of the other commands and utilities you may have seen that can tell you about programs and commands:

  • what
  • whatis
  • which
  • whence
  • where
  • whereis
  • command
  • type

A lot of these are legacy and should be avoided, but for completeness sake, we'll go through them.

what​

what reads out special metadata embedded in a program, generally used to identify the version of source code it was built from:

$ what /bin/ls
/bin/ls
Copyright (c) 1989, 1993, 1994
PROGRAM:ls PROJECT:file_cmds-272.220.1

There should be almost no circumstance in which you need to use it in your day-to-day work, but you might come across it if you meant to type whatis.

whatis​

whatis searches a local help database for text. This can be useful in tracking down manual pages:

$ whatis bash
bash(1) - GNU Bourne-Again SHell
bashbug(1) - report a bug in bash

But I can't imagine it will be a regularly used tool by most users.

which​

which will search your $PATH to see whether an executable can be found. With the -a flag, it will show all results.

$ which -a vi
/usr/local/bin/vi
/usr/bin/vi

which originated in csh. It remains on many systems for compatibility but in general should be avoided due to potentially odd behaviour4.

whence​

whence was added to the Korn shell. You are unlikely to use it unless you are on systems using ksh. zsh also has this command, but it should be avoided and considered non-standard.

% whence brew
/usr/local/bin/brew

where​

This is a shell builtin that can provide information on commands, similar to type:

% where ls
ls: aliased to ls -G
/bin/ls

However, type should be preferred, as it is more standard.

whereis​

whereis is available on some systems and generally operates the same as which, searching paths for an executable:

% whereis ls
/bin/ls

Again, type should be preferred for compatibility.

command​

command is defined in the POSIX standard, so should be expected to be present on most modern systems. Without arguments, it simply executes a command. With the -v argument, you get a fairly machine-readable or processable response; with the -V argument, you get a more human readable response:

% command -v ls
alias ls='ls -G'
% command -V ls
ls is an alias for ls -G

command can be useful in scripts, as we will see in later chapters.

type​

type is part of the Unix standard and will be present in most modern systems. As we've already seen, it will identify the type of command as well as the location for an executable:

% type -a ls
ls is an alias for ls -G
ls is /bin/ls

This command can also be used to only search for paths:

% type -p ls
ls is /bin/ls

Summary​

In summary, avoid anything that starts with 'w'! These are legacy commands, generally needed only when working on older Unix machines. type or command should be used instead.


  1. We will cover permissions and modes in later chapters.↩
  2. Why these names and locations? It's a long story. The best place to start if you are interested is the Filesystem Hierarchy Standard.↩
  3. chmod changes the mode of a file; +x means 'add the executable bit'. This tells the operating system the file can be executed.↩
  4. Stack Exchange: Why not use β€œwhich”? What to use then?↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-2-core-skills/what-is-a-shell/index.html b/pr-preview/pr-346/part-2-core-skills/what-is-a-shell/index.html new file mode 100644 index 00000000..9a66c6f5 --- /dev/null +++ b/pr-preview/pr-346/part-2-core-skills/what-is-a-shell/index.html @@ -0,0 +1,26 @@ + + + + + +What is a Shell? | Effective Shell + + + + + + + + + + + + + + +
+

What is a Shell?

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.

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.

Introduction for the Non-Technical Reader

It might come as a surprise that 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:

Diagram: The Shell

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 shell, and it has been a common way to interface with computers ever since the first screens and keyboards were created.

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:

Image: The Evolution of Windows

(By Source (WP:NFCC#4), Fair use, https://en.wikipedia.org/w/index.php?curid=58853841)

Why would people choose to use such an archaic interface as a shell?

  • Typing is fast: A skilled shell user can manipulate a system at dazzling speeds just using a keyboard. Typing commands is generally much faster than exploring through user interfaces with a mouse
  • Shells are programmable: Users will often being programming as they work in a shell, creating scripts to automate time-consuming or repetitive processes
  • Shells are 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.

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.

Introduction for the Technical Reader

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 tty? How do shells really work? Hopefully as you read this chapter you'll discover something that you didn't know about shells.

Let's Get Started!

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!

A Computer in a Nutshell​

The diagram below shows a simplified view of a typical computer:

Diagram: Operating System

Already there's a lot going on.

Your computer is going to have a CPU1 and memory2, and almost certainly a network adapter3 and display adapter4. 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.

The Operating System​

The operating system is the piece of software installed on a computer that can interface with the 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 software interface to it.

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.

The operating system is generally broken down into two parts - the kernel and user space:

Diagram: The Kernel and User Space

Let's look at these in more detail.

The Kernel​

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.

Software running in the kernel has direct access to resources, so is 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.

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.

Image: Photo of a walnut, showing the kernel and the shell

(By Kkchaudhary11 - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=49069244)

The operating system kernel really is the 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 possible5. And that is where user space comes in.

User Space​

The vast majority of programs run in 'user space' (also commonly called 'user land').

When a program starts, the kernel will allocate it a private segment of memory and provide 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.

For example, a program running in user space can use the standard fopen function, which is provided on almost every operating system as part of the 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 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.

Now that the key components have been introduced, we can look at the shell. The name should come as no surprise, as it is a wrapper or outer layer to the operating system (which itself contains the sensitive nugget of the kernel).

The Shell​

So what is the shell? The shell is just a general name for any user space program that allows access to resources in the system, via some kind of interface.

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.

For example, to see all of the files in a folder, the human operator 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.

Here's an example, where a shell is being used to show the 'png' images in the folder I am working in6:

Screenshot: Browsing Contents of the File System the the Bourne Again Shell

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, tree is a program). Let's pick these apart.

Here's a diagram that more accurately shows what is going on:

Diagram: The Terminal & The ShellWe'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).

Let's dissect this bit by bit.

The Terminal​

We're not directly interacting with the 'shell' in this diagram. We're actually using a 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 terminal.

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.

Why the word 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 Teletype Terminal7 became a common way for users to interface with computers.

Photo: ASR-33 TTY

(Photograph by Rama, Wikimedia Commons, Cc-by-sa-2.0-fr, CC BY-SA 2.0 fr, https://commons.wikimedia.org/w/index.php?curid=17821795)

At this time, computers were very large, complex, and expensive machines. It was common to have 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:

Photo: IBM 3486

(By ClickRick - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=6693700)

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.

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 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 secure shell - which is a secured-shell connection over a network.

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 shell program - a program that we can use to operate the computer.

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 gnome-terminal for Linux, and iTerm2 and Hyper. There's a bunch of screenshots of different setups at the end of the article.

Back to the Shell​

Now that we've described the terminal, we can go back and look at the shell in detail.

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.

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.

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.

So what else does a shell do? Most of the features are related to helping human operators work with the system more efficiently.

  • Quickly enter commands, see the history of commands and quickly restructure commands (see Chapter 8 - Fly on the Command Line)
  • Navigate through the file system, moving from folder to folder (see Chapter 1- Navigating Your System), which makes it easier for an operator to navigate the file system
  • Chain the output of commands together - for example, taking the output of one basic program, such as the tree program we saw, and writing it to a file (see Chapter 7 - Thinking in Pipelines)
  • Offer a programming language, allowing the operator to perform more complicated tasks

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.

The Command Prompt or Command Line​

The last part of the diagram, which we haven't covered yet, is the command prompt.

Diagram: Command Prompt

When a shell is running in 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 they have to enter commands, the shell will output some kind of prompt.

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.

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.

Shell command prompts can be customised, so they will often look different from machine to machine. Below is an example that shows a lot of technical information. This is from the highly popular oh-my-zsh framework for the 'Z Shell' shell, which is very popular among developers:

Image: Customised oh-my-zsh

*(Source: https://ohmyz.sh/)

Shell Commands and Different Shells​

A lot of the 'commands' in a shell, such as 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.

Some commands, such as 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 Chapter 10 - Understanding Commands). Commands will often differ between shells.

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.

On most Unix-like systems, the default shell is a program called 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.

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.

Let's review the earlier diagram again:

Diagram: The Terminal & The Shell

We can see the real internals of what is going on in this "Terminal -> Shell -> Program" chain in the diagram above quite easily.

Try the command pstree -psa $$ in a shell8:

Image: Process Tree

The first systemd process is the primary process for the OS - it is process number 1, which initialises everything else. The second 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.

What is interesting is that we can see a terminal (the gnome terminal), which has started my preferred shell (which is zsh), which is running a command (the program pstree). Here we can see the exact chain as shown in the diagram earlier.

That's a Wrap!​

These are the key technologies and concepts that surround a shell.

If you are interested in more technical details of working with shells, then my 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.

To close the article, below are some examples of different terminals, shells, command prompts and so on.

Example: iTerm 2 / tmux / zsh​

Example: iTerm 2, tmux, zsh

In this example, we have:

  • A MacOS operating system
  • iTerm2 as the terminal program
  • tmux running as a 'terminal multiplexer' (see Effective Shell: Terminal Multiplexers)
  • zsh (Z Shell) as the shell program, using 'oh my zsh', which is easily recognised by the % sign in the command prompt.
  • A customised command line, which shows the user and folder on one line, with only the % symbol below, to leave lots of space for the input commands9.

Example: Bash​

Example: BashExample: Bash Elevated

In this example, we have:

  • A Linux operating system (Ubuntu 14)
  • The gnome terminal
  • bash as the shell
  • In the second screenshot, the user has 'root privileges', and to indicate this, bash helpfully changes the default command prompt from a dollar sign to a hash sign

Example: Windows Explorer​

Example: Windows Explorer

In this example, we have:

  • The Windows 10 operating system
  • No terminal
  • The explorer.exe program showing us a graphical shell

This looks different from previous examples. The program, which shows the familiar Windows interface, 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 Shell Library. I also maintain a popular library for building extensions to the graphical Windows shell - sharpshell.

Example: Windows Command Prompt​

Example: Command Prompt

In this example, we have:

  • The Windows 10 operating system
  • The command prompt terminal and shell

In Windows, the terminal and shell are combined into a single cmd.exe program. There's an excellent article on the internals - Microsoft DevBlogs: Windows Command-Line: Inside the Windows Console

Example: Windows PowerShell​

Example: Windows Powershell

In this example, we have:

  • The Windows 10 operating system
  • The PowerShell terminal

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.

Example: Windows Subsystem for Linux (WSL)​

Example: WSL

In this example, we have:

  • The Windows 10 operating system
  • The Bash.exe program

This screenshot, from 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.

Share and Discuss​

If you enjoyed this article, please do share it! Feel free to include suggestions, improvements or corrections in the comments below.


Useful References


Footnotes


  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).↩
  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 ephemeral - everything is gone when you turn off the power to it.↩
  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.↩
  4. This is the part of your computer you plug the screen into.↩
  5. This is because a mistake in 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.↩
  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 - github.com/dwmkerr/dotfiles.↩
  7. And that's where the 'TTY' acronym you will see sometimes comes from. Enter the 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.↩
  8. $$ is a Bash internal variable. These will also be covered in a later article in the series.↩
  9. Feel free to see my dotfiles to configure a similar setup for yourself.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/advanced-text-manipulation/index.html b/pr-preview/pr-346/part-3-manipulating-text/advanced-text-manipulation/index.html new file mode 100644 index 00000000..40b54a97 --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/advanced-text-manipulation/index.html @@ -0,0 +1,27 @@ + + + + + +Advanced Text Manipulation | Effective Shell + + + + + + + + + + + + + + +
+

Advanced Text Manipulation

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.

sed can be used to perform a variety of tasks with text. In many cases a small command involving sed can quickly solve problems. We'll look at some of the common ways to use sed, some more sophisticated examples and discuss when you might want to consider using alternative tools like awk or a programming language.

Introducing the Stream Editor​

The stream editor command takes input from a 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 stdout. This command is extremely powerful - there are many sophisticated transformations which can be applied.

Seeing 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!

Transformations with Sed​

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 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 sed.

Downloading the Samples

Run the following command in your shell to download the samples:

curl effective.sh | sh

Replacing Text​

The samples folder has a script which copies some configuration files to a backup folder. Let's take a quick look at it:

$ cd scripts
$ cat backup-config.sh

#!/usr/bin/env bash

# Make sure we have a backup directory.
mkdir ~/backup

# Copy over alicloud, aws, azure, gcp and ssh config and credentials.
cp ~/.aliyun/config.json ~/backup/settings/aliyun/
cp ~/.aws/config ~/backup/settings/aws/
cp ~/.aws/credentials ~/backup/settings/aws/
cp ~/.azure/config ~/backup/settings/azure/
cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/
cp ~/.ssh/config ~/backup/settings/ssh/
cp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?
cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/

We could use the sed command to change the name of the folder we backup to. If we wanted to change the settings folder to configuration we could run the command below:

$ sed 's/settings/configuration/' backup-config.sh
#!/usr/bin/env bash

# Copy over alicloud, aws, azure, gcp and ssh config and credentials.
cp ~/.aliyun/config.json ~/backup/configuration/aliyun/
cp ~/.aws/config ~/backup/configuration/aws/
cp ~/.aws/credentials ~/backup/configuration/aws/
cp ~/.azure/config ~/backup/configuration/azure/
cp ~/.config/gcloud/credentials.db ~/backup/configuration/gcloud/
cp ~/.ssh/config ~/backup/configuration/ssh/
cp ~/.ssh/id_rsa ~/backup/configuration/ssh/ # is this safe?
cp ~/.ssh/id_rsa.pub ~/backup/configuration/ssh/

This shows the basics of how sed works. We give sed an 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 stdin.

Let's look at the expression in detail:

s/settings/dotfiles/
  • The s indicates that we are going to run the substitute function, which is used to replace text
  • The / indicates the start of the pattern we are searching for...
  • ...settings is the pattern - which is just the literal text 'settings'
  • The second / indicates the start of the replacement we will make when the pattern is found
  • The final / indicates the end of the replacement - we can also optionally put flags after this slash

Just as we saw in Chapter 13 - Get to Grips with Grep, we can provide a regular expression as the pattern to search for. By default, sed will use basic regular expressions. We can use extended regular expressions by providing the -E flag.

Basic and Extended Regular Expressions

If you are not sure about the difference between Basic and Extended Regular Expressions, you can use the:

man re_pattern

Command to get detailed documentation.

Applying Multiple Expressions​

We can use the -e <expression> flag to supply multiple expressions to sed.

If we wanted to delete our backup folders, we could run two substitutions - one which changes the cp command to rmdir command and one which deletes the first parameter, which was the source directory for cp but is not needed for rmdir1.

Replacing Text on Specific Lines​

Let's start with the first expression, which should change cp to rmdir:

$ sed -e 's/cp/rmdir/' backup-config.sh
#!/usr/bin/env bash

# Make sure we have a backup directory.
mkdir ~/backup

# Copy over alicloud, aws, azure, grmdir and ssh config and credentials.
rmdir ~/.aliyun/config.json ~/backup/settings/aliyun/
rmdir ~/.aws/config ~/backup/settings/aws/
rmdir ~/.aws/credentials ~/backup/settings/aws/
rmdir ~/.azure/config ~/backup/settings/azure/
rmdir ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/
rmdir ~/.ssh/config ~/backup/settings/ssh/
rmdir ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?
rmdir ~/.ssh/id_rsa.pub ~/backup/settings/ssh/

We have provided a single expression with the -e parameter. If you are providing a single expression only then this parameter is superfluous, but we will shortly add another expression.

This has changed all of the cp commands to rmdir. But did you notice the bug? It has also changed the letters cp in the comment which mentions gcp to rmdir, meaning our comment line has changed. I've made the changes bold to make this easier to see below:Before, it was:

# Copy over alicloud, aws, azure, gcp and ssh config and credentials.

Now it is:

# Copy over alicloud, aws, azure, grmdir and ssh config and credentials.

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 cp:

$ sed -e '/^cp/s/cp/rmdir/' backup-config.sh
#!/usr/bin/env bash

# Make sure we have a backup directory.
mkdir ~/backup

# Copy over alicloud, aws, azure, gcp and ssh config and credentials.
rmdir ~/.aliyun/config.json ~/backup/settings/aliyun/
rmdir ~/.aws/config ~/backup/settings/aws/
rmdir ~/.aws/credentials ~/backup/settings/aws/
rmdir ~/.azure/config ~/backup/settings/azure/
rmdir ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/
rmdir ~/.ssh/config ~/backup/settings/ssh/
rmdir ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?
rmdir ~/.ssh/id_rsa.pub ~/backup/settings/ssh/

Let's look at the expression in detail. Before the s symbol, which indicates that we are performing a substitution, we now include a line pattern. A line pattern tells sed which lines it should apply the expression to. Our line pattern is just ^cp, which is the regular expression meaning "any line which starts with cp".

Line patterns can be quite sophisticated and are covered in the box later on in this chapter!

So all in all:

  • ^cp- use the line pattern ^cp (lines which start with cp) for the expression
  • s - use the substitution function (which replaces text)
  • cp - find the pattern cp
  • rmdir - replace each occurrence with rmdir +on lines which start with the text cp, replace any occurrences of the text text cp with the text rmdir

We combine these parts together using a / symbol, this is needed so that sed knows where the line pattern starts and end, the function starts and end and so on2.

Removing Parts of a Line​

Now we need to remove the first parameter for the rmdir command, it was used when we had cp as the command but doesn't make sense any more.

Let's apply a new expression:

$ sed -E -e '/^cp/s/cp/rmdir/' -e '/^rmdir/s/~[^ ]+ //' backup-config.sh
#!/usr/bin/env bash

# Make sure we have a backup directory.
mkdir ~/backup

# Copy over alicloud, aws, azure, gcp and ssh config and credentials.
rmdir ~/backup/settings/aliyun/
rmdir ~/backup/settings/aws/
rmdir ~/backup/settings/aws/
rmdir ~/backup/settings/azure/
rmdir ~/backup/settings/gcloud/
rmdir ~/backup/settings/ssh/
rmdir ~/backup/settings/ssh/ # is this safe?
rmdir ~/backup/settings/ssh/

Let's take a look at the second expression, component by component:

  • ^rmdir - use a line pattern which matches lines which start with rmdir
  • s - use the substitution function to replace text
  • ~[^ ]+ - search for the tilde ~ symbol and any non-space characters, up until the first space character

We actually don't even include a replacement - which is why the expression ends with // - the inside of the replacement part of the expression is empty. This means we search for a tilde ~, match everything up until the next space and then replace it with nothing - meaning we remove it!

Note as well that we've used the -E flag to tell sed to use extended regular expressions. This allows us to use the + symbol without escaping it with a \ 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.

Let's look at a few other ways to use sed for some real-world tasks.

Stripping Comments​

In the script we've used above, there are some comments. Let's use sed to remove them:

$ sed -E 's/#.*$//' backup-config.sh




mkdir ~/backup


cp ~/.aliyun/config.json ~/backup/settings/aliyun/
cp ~/.aws/config ~/backup/settings/aws/
cp ~/.aws/credentials ~/backup/settings/aws/
cp ~/.azure/config ~/backup/settings/azure/
cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/
cp ~/.ssh/config ~/backup/settings/ssh/
cp ~/.ssh/id_rsa ~/backup/settings/ssh/
cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/

Notice how we have removed the comments which started at the beginning of the file, as well as the comment after the cp ~/.ssh/id_rsa line. This is because the regular expression matches any hash symbol # and strips everything which follows it.

What about if we want to get rid of those empty lines? We can use the d or delete function:

$ sed -E -e 's/#.*$//' -e '/^ *$/d' backup-config.sh
mkdir ~/backup
cp ~/.aliyun/config.json ~/backup/settings/aliyun/
cp ~/.aws/config ~/backup/settings/aws/
cp ~/.aws/credentials ~/backup/settings/aws/
cp ~/.azure/config ~/backup/settings/azure/
cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/
cp ~/.ssh/config ~/backup/settings/ssh/
cp ~/.ssh/id_rsa ~/backup/settings/ssh/
cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/

The delete command is applied to all lines which match the line pattern. In this case, the line pattern is a regular expression, ^ *$, which matches any line made up only of space characters (including zero space characters, i.e. empty lines).

Line Patterns

Line patterns, which can be used on many sed functions, are actually quite sophisticated. Here are a few examples:

  • /test/ - any line matching the pattern test
  • /test/! - any line not matching test
  • 6 - line six
  • $ - the last line
  • 1-10 - lines one to ten
  • 1-10! - lines except _one to ten

Check man sed to see more about line patterns.

Appending Text​

In a regular expression the dollar-sign $ symbol represents the end of a line.

We can use this symbol to add content to the end of lines - we just search for $ 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.

Here's how we can make sure that the cp command in the script doesn't fail:

$ sed -E -e '/^cp/s/$/ || true/' backup-config.sh

#!/usr/bin/env bash

# Make sure we have a backup directory.
mkdir ~/backup

# Copy over alicloud, aws, azure, gcp and ssh config and credentials.
cp ~/.aliyun/config.json ~/backup/settings/aliyun/ || true
cp ~/.aws/config ~/backup/settings/aws/ || true
cp ~/.aws/credentials ~/backup/settings/aws/ || true
cp ~/.azure/config ~/backup/settings/azure/ || true
cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/ || true
cp ~/.ssh/config ~/backup/settings/ssh/ || true
cp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe? || true
cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ || true

Now we have a command which strips comments, deletes empty lines and then adds the text || true to the end of the line. This little trick ensures that the script won't fail if one of the individual cp commands fails. We'll see more about shell scripts later.

What if we want to add a semicolon to the end of all lines?

sed 's/$/;/'

Easy!

Prepending Text​

In a regular expression the caret ^ symbol represents the start of a line.

We can apply the same trick as with the dollar-sign $ symbol to add text to the start of a line - we just replace ^ with whatever we want the line to start with.

Here's how we can use this trick!

$ sed -E -e '/^cp/s/$/"/' -e '/"$/s/^/echo "/' backup-config.sh
#!/usr/bin/env bash

# Make sure we have a backup directory.
mkdir ~/backup

# Copy over alicloud, aws, azure, gcp and ssh config and credentials.
echo "cp ~/.aliyun/config.json ~/backup/settings/aliyun/"
echo "cp ~/.aws/config ~/backup/settings/aws/"
echo "cp ~/.aws/credentials ~/backup/settings/aws/"
echo "cp ~/.azure/config ~/backup/settings/azure/"
echo "cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/"
echo "cp ~/.ssh/config ~/backup/settings/ssh/"
echo "cp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?"
echo "cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/"

We first put at quote at the end of each line which starts with cp, then put echo " at the beginning of each line which ends with a quote!

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!

Extracting Information​

What about if we want to extract some information from lines in a file?

Let's take a quick look at our movies file in our data folder for reference:

$ cd ~/effective-shell/data
$ head -n 3 top100.csv

"Rank","Rating","Title","Reviews"
"1","97","Black Panther (2018)","515"
"2","94","Avengers: Endgame (2019)","531"

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!

$ head -n 3 data/top100.csv | sed -E 's/.*\(([0-9]+)\).*/\1/' 

"Rank","Rating","Title","Reviews"
2018
2019

This works because we match any text, then capture any digits enclosed in brackets, then replace with the \1 text, which means 'the first match'.

Given what we know about line patterns, we can also easily exclude the first line:

$ sed -E -e '1d' -e 's/.*\(([0-9]+)\).*/\1/' data/top100.csv 

2018
2019
...

Here we have just added the 1d expression - delete the first line.

Advanced - Surrounding Parts of Text with Quotes​

Let's look at another example. Here we have some yaml content, a set of keys and values. Note that some of the values are quoted, and some are not:

title: "Advanced Text Manipulation"
slug: advanced-text-manipulation
weight: 14

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.

First, let's see if we can extract the keys:

$ grep -E '[^:]+:' ~/effective-shell/docs/chapter12.mdtitle: "Advanced Text Manipulation"slug: advanced-text-manipulationweight: 14Let'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...

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.

The pattern is [^:]+:, which means 'at least one character which is not the : character, followed by :.

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:

% grep -E '^[^: ]+:' ~/effective-shell/docs/chapter12.mdtitle: "Advanced Text Manipulation"slug: advanced-text-manipulationweight: 14"2","94","Avengers: Endgame (2019)","531"

Much better. The pattern now starts with ^ meaning 'start of line', and the type of characters we search for [^: ] 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:

$ grep -E '^[^: "]+:' ~/effective-shell/docs/chapter12.mdtitle: "Advanced Text Manipulation"slug: advanced-text-manipulationweight: 14

Awesome!

Building Regular Expressions

I was a hold out for years, but regular expressions are 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.

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.

Now let's start using this pattern in sed. Let's print all lines which match the pattern:

$ sed -E -n '/^[^: "]+:/p' ~/effective-shell/docs/chapter12.md
title: "Advanced Text Manipulation"
slug: advanced-text-manipulation
weight: 14

Now there are two critical things we've added to sed here while we're working. The first is the -n flag, which means no automatic printing - meaning it will show no output unless told. Then in the pattern, we finish the command with p, meaning 'print lines which match the pattern'.

These options make sed behave like grep, which is useful because we are still building the command.

Now let's actually quote the result. To do that, we need to find lines where the value is not already quoted - so let's add that to our pattern:

$ sed -E -n '/^[^: "]+: +[^"]+$/p' ~/effective-shell/docs/chapter12.md
slug: advanced-text-manipulation
weight: 14

Our pattern now reads like this:

  • ^[^ :"]+: - match the start of a line, any characters which are not space, colon or quote, which then finish with a colon and a space.
  • +[^"]+$ - 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.

The pattern is working - its found our two unquoted keys. Now let's get it to print the substitution.

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:

$ sed -E -n 's/(^[^: "]+:)( +[^"]+$)/Key is "\1"/p' ~/effective-shell/docs/chapter12.md
Key is "slug:"
Key is "weight:"

We are now not just searching for a pattern, we're using the s (substitute) function to replace all of the matched text with Key is "\1". The \1 just means 'what you found in the first capture group".

We could just as easily show the value:

$ sed -E -n 's/(^[^: "]+:)( +[^"]+$)/Value is "\2"/p' ~/effective-shell/docs/chapter12.md
Value is " advanced-text-manipulation"
Value is " 14"

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:

$ sed -E -n 's/(^[^: "]+)(: +)([^"]+$)/Key "\1", Value "\3", Separator "\2"/p' ~/effective-shell/docs/chapter12.md
Key "slug", Value "advanced-text-manipulation", Separator ": "
Key "weight", Value "14", Separator ": "

I think this is really starting to show just how powerful sed and regular expressions are.

Let's finally tie it together - add quotes around unquoted values:

$ sed -E -n 's/(^[^: "]+)(: +)([^"]+$)/\1\2"\3"/p' ~/effective-shell/docs/chapter12.md
slug: "advanced-text-manipulation"
weight: "14"

Awesome!

If we wanted to actually change the file, we could remove the -n flag, so that we write out everything. This makes the p option at the end of the substitution superfluous:

$ sed -E 's/(^[^: "]+)(: +)([^"]+$)/\1\2"\3"/' ~/effective-shell/docs/chapter12.md > updated.md

Let's peep at the top of the file we created to see if it looks right:

$ head -n 10 updated.md
---
title: "Advanced Text Manipulation"
slug: "advanced-text-manipulation"
weight: "15"
---

# Chapter 15 - Advanced Text Manipulation

In [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.

Impressive - we've found a very specific pattern in a large file, substituted to match what we need and then saved the results.

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:

$ sed -E -n '/(^[^: "]+:)( +[^"0-9]+$)/p' ~/effective-shell/docs/chapter12.md
slug: advanced-text-manipulation

All we've done is changed the value pattern from [^"] (anything except quotes) to [^0-9] (anything except quotes and digits).

Advanced - Template Files​

Here's another example which I have found useful again and again. We can use sed's text replacement capabilities to create a basic templating system.

For example, let's say we have the file below:

apiVersion: v1
kind: Secret
metadata:
name: database-credentials
namespace: dev
stringData:
username: admin
password: ThisIsVerySensitive!

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.

Let's say we want to be able to not have the username and password stored in the file itself, because they are sensitive. We also want to make the 'namespace' value configurable.

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 sed.

First, let's create the 'template' version of this file:

$ cat << EOF > secret.template.yaml
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
namespace: %NAMESPACE%
stringData:
username: %DB_USERNAME%
password: %DB_PASSWORD%
EOF

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 effective-shell/templates/secret.template.yaml.

Now let's apply our substitution:

$ sed -e 's/%NAMESPACE%/staging/' \
-e 's/%DB_USERNAME%/admin/' \
-e 's/%DB_PASSWORD%/secret/' \
~/effective-shell/templates/secret.template.yaml
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
namespace: staging
stringData:
username: admin
password: secret

I've used the \ 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.

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:

$ export NAMESPACE=production
$ export DB_USERNAME=prod-admin
$ export DB_PASSWORD=Dhhs22kfid9c
$ sed -e "s/%NAMESPACE%/${NAMESPACE}/" \
-e "s/%DB_USERNAME%/${DB_USERNAME}/" \
-e "s/%DB_PASSWORD%/${DB_PASSWORD}/" \
~/effective-shell/templates/secret.template.yaml
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
namespace: production
stringData:
username: prod-admin
password: Dhhs22kfid9c

In fact, we could even take this a step further and simply replace every environment variable!

file_path="~/effective-shell/templates/secret.template.yaml"
env_var_names=$(env | sed -E -n 's/^([^=]+)(=.*)/\1/p')

for env_var_name in ${env_var_names}; do
echo "Checking for '${env_var_name}'..."

if grep -q "%${env_var_name}%" "${file_path}"; then
echo "-> Found '${env_var_name}', expanding now..."

env_var_value="${!env_var_name}"
escaped_env_var_value=$(echo ${env_var_value} | sed -e 's/[\/&]/\\&/g')

sed -e "s/%${env_var_name}%/${escaped_env_var_value}/" \
"${file_path}" > "${file_path}.tmp"
mv "${file_path}.tmp" "${file_path}"
fi
done

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.

Here's what's going on blow-by-blow:

  1. 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
  2. env_var_names=... - we use sed to get the name of each environment variable. This comes from everything before the = sign in the output of the env command.
  3. for ... - this lets us 'loop' through each environment variable name found - we'll see more about this in the sections on scripting.
  4. if grep -q - check to see if the environment variable name is used in the file...
  5. ...and if it is, get the value of it with ${!env_var_name} - the ! exclamation mark is variable indirection and is Bash specific. It allows us to get the value of a variable which has a variable name
  6. 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 sed
  7. sed -e run the replacement, putting the results in a temporary file...
  8. ...replace the source file with the temporary one

Many of these concepts, like 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.

A Note on Security

It is very important to be careful when running commands like this or writing scripts which use this kind of pattern.

Allowing the contents of your environment variables to be put into files, or even the shell's history can be a serious security concern.

As an example - note what would happen if we replaced ${DB_USERNAME} with ${USERNAME} in the script above? In Z Shell rather than the value we provided being put in the file, the actual username of the user running the script would be written (which in my case would be dwmkerr).

Be very careful when working with environment variables to make sure you avoid exposing private information. Even more sensitive variables might be things like ${AWS_SECRET_ACCESS_KEY} - exposing variables like this could allow attackers to start accessing resources on your cloud environments.

What About 'In Place' Editing?​

You might be aware that there is an 'in-place' feature in sed which allows you to change the file you pass it directly. This allows you to do something like the below:

$ sed -i '.bak' 's/staging/production/' test.txt

This would perform the substitutions and then put them in a file with .bak appended. To just overwrite the existing file, you could use:

$ sed -i '' 's/staging/production/' test.txt

In this case we don't append anything to the name of the overwritten file, so we end up replacing the original file itself.

How the -i flag works can vary on some systems so I generally prefer to simply output the result of 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.

What about Awk?​

There is another very powerful text manipulation tool - awk. Often if you trying to find out how to perform more complex text based operations, you'll see awk in the mix as a potential solution.

Awk is very sophisticated and has its own language to support complex transformations and operations. My advice is to first master sed and then consider learning awk if you regularly find yourself limited by sed.

When to Program​

Personally, if I have tasks which are too complex for me to solve with the fairly basic knowledge of 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.

This will often be easier to maintain and understand than an extremely complex 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.

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.

Summary​

In this chapter we:

  • Introduced sed, the stream editor command
  • Saw that when we need to transform or manipulate text, sed is often a great tool for the job
  • Learnt how to perform simple substitutions with sed, using commands such as sed 's/old-text/new-text/'
  • Saw the components which make up a sed expression - the function, the expression and when needed, the line pattern
  • Saw how to use -e to apply multiple patterns
  • Went deeper into extended regular expressions
  • Saw an example of how to strip comments from a file with sed
  • Saw how to remove empty lines with sed
  • Looked into line patterns in more detail, showing how we can choose what lines sed operates on
  • Saw how to append text to lines with sed
  • Saw how to use patterns and capture groups to extract information from lines with sed
  • Saw more advanced examples of capture groups, allowing us to capture different parts of a match and manipulate them individually, or recompose them
  • Saw how sed can be used to implement a simple 'template' mechanism for files
  • Saw how the -i (in place) parameter works for sed
  • Briefly described awk as a potential alternative tool to learn about for more sophisticated text manipulation
  • Suggested that if something is too complex to write easily in sed, it may be faster to implement it with a programming language

  1. Note that if we used rm -r instead of rmdir, which is very common, we run the risk of making a big mistake - passing the source directory for the cp command as the first parameter to remove. This would mean we run rm -r on the source files for the backup and the backup folder itself, deleting both! This is one place where using rmdir makes a bit more sense - it won't delete the source files if we make a mistake!↩
  2. You can use any character to delimit expressions - sometimes people will use # or | rather than a forward slash, typically because they want to use the forward slash as part of a pattern.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/build-commands-on-the-fly/index.html b/pr-preview/pr-346/part-3-manipulating-text/build-commands-on-the-fly/index.html new file mode 100644 index 00000000..b7c48eb2 --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/build-commands-on-the-fly/index.html @@ -0,0 +1,26 @@ + + + + + +Build Commands on the Fly | Effective Shell + + + + + + + + + + + + + + +
+

Build Commands on the Fly

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.

Introducing Xargs​

The xargs [build and execute commands] command takes input, uses the input to create commands, then executes the commands. I tend to remember it as "Execute with Arguments" as the name xargs sounds a little odd!

How xargs is used is probably easiest to see with an example. Let's use it to build a set of commands which will remove any empty files from a folder.

Before we show xargs let's create some empty files which we'll later clean up:

$ mkdir -p ~/effective-shell/tmp
$ cd ~/effective-shell/tmp
$ touch file{1..100}.txt

We're using a nice shell trick here called Brace Expansion - the shell will expand file{1..100}.txt into file1.txt, file2.txt and so on, all the way to file100.txt.

We could just look for empty files in our /tmp folder for this example, but a file in that folder might be in use, so a safer way to demonstrate xargs is to use some temporary files which we create ourselves.

We could search for empty files with the command below:

$ find . -empty
file1.txt
file2.txt
file3.txt
file4.txt
file5.txt
...
A refresher on Finding Files

In this chapter we'll be using the find (find files and folders) command a lot - if you need a refresher, check Chapter 11 - Finding Files.

The find command has outputted a list of files, now we want to use the rm (remove file) command to delete each one. Let's just pipe the list of files to the rm command, check Chapter 7 - Thinking in Pipelines if you need a reminder of how piping works:

$ find . -empty | rm
rm: missing operand
Try 'rm --help' for more information.

What's going on here? Well basically the issue is that the rm command doesn't actually read the list of files from stdin, the list of files has to be passed as a parameter to the command. How can we take this list of files and pass it to rm as a set of parameters?

This is what xargs is for! Before we delete the files, let's just see what happens when we pass the list to xargs:

$ find . -empty | xargs
./file40.txt ./file8.txt ./file35.txt ./file81.txt ...

By default xargs take the input, joins each line together with a space and then passes it to the echo command. The echo command writes it out to the screen.

We can change the command xargs passes the arguments to:

$ find . -empty | xargs echo rm
rm ./file40.txt ./file8.txt ./file35.txt ./file81.txt ...

Very interesting! Now we've told xargs to pass the output to the echo rm command - this just writes out rm followed by the list of files. Putting echo before whatever command you want to run is a useful way to check the command before we commit to running it.

Let's finish the job and delete each file:

$ find . -empty | xargs rm

Done! You can run ls to confirm that the file has been deleted.

This is xargs - it constructs and executes a command using arguments from standard input. Now let's see how we can take this further.

Handling Whitespace, Special Characters and Tracing​

One common challenge with xargs is how to deal with spaces. To see what I mean, let's create three files with spaces in the names:

$ touch "chapter "{1,2,3}.md
$ find . -type f
./chapter 1.md
./chapter 2.md
./chapter 3.md

What if we wanted to delete these files? Let's try that with rm:

$ find . -type f | xargs rm
rm: cannot remove './chapter': No such file or directory
rm: cannot remove '1.md': No such file or directory
...

The file name has a space in it, which is confusing rm as it thinks we're providing six paths rather than three.

We can use the -t (trace) option to see what xargs actually tried to do:

$ find . -type f | xargs -t rm
rm ./chapter 1.md ./chapter 2.md ./chapter 3.md
...

Hopefully you can spot the error - the rm command thinks it needs to remove six files, because there are spaces in the filenames and there are not quotes around the filenames to let rm know this!

Fortunately, find loves xargs - they are part of the same package of tools (which is called 'findutils'). And there's a special pair of options that can deal with this.

For find, we are going to use the -print0 action and for xargs we'll use the -0 option. Let's see how it looks now, then describe what's going on under the hood:

$ find . -type f -print0 | xargs -0 -t rm
rm './chapter 1.md' './chapter 2.md' './chapter 3.md'

In Chapter 11 - Finding Files we saw that the default action of the find command is -print, which writes out the path of each item found. The -print0 action is very similar - but it instead it writes out each item followed by a special 'null' character1.

Now that we've told find to end each result with a special 'null' character, we just tell xargs that the 'null' character is what separates each line of input. We do this with the -0 (use NUL as separators) option.

You don't need to really understand the internals - if you are a computer programmer it might make sense, this is how strings in things like the C Programming language work. All you need to know is that it means the xargs program won't get confused when it sees spaces, tabs, quotes, newlines, or anything else which might be goofy in a file name.

My recommendation would be to always pair up the -print0 action with the -0 option - it means you won't get caught out by odd file names. And definitely make use of the -t (trace) option to see what xargs is actually doing!

One Command or Many Commands?​

By default xargs takes all of the input and passes it as a set of arguments to the provided command. We can see this below:

$ touch file{1..5}
$ find . -type f | xargs echo
./file1 ./file2 ./file3 ./file4 ./file5

We don't need to provide echo to xargs, it is the default, but I have added it for clarity. But what is really important is that we have called echo once and once only.

xargs has passed all of the arguments it has been given to the command.

We can tell xargs how many lines of input it should use for the command with the -L (max lines) parameter:

$ find . -type f | xargs -L 1 echo
./file1
./file2
./file3
./file4
./file5

We've now called the echo command once for each line of input, meaning that echo has been called five times.

In general if you can provide all of the arguments to a single command the system may be able to process the command slightly faster. However, if there are lots of arguments, the command itself might not be able to handle all of the arguments you give it.

You can set -L to other values too - xargs will use up to the number of lines provided:

$ find . -type f | xargs -L 3 echo
./file1 ./file2 ./file3
./file4 ./file5

Here we've allowed up to three input lines per command.

You will probably not use the -L parameter very often, but it is really important that you understand what it does. And that is because many of the other options we'll use imply -L 1 - we'll see why in the next example.

Constructing more complex commands with the 'I' Parameter​

You have probably noticed by now that the xargs command puts the arguments it is given at the end of the command you write.

What if you need the arguments to go somewhere else? For example, what if I wanted to copy every text file in a folder to another location?

Here's how we might start - and what'll go wrong!

$ find . -name "*.txt" -print0 | xargs -0 -t cp ~/backups
cp /home/dwmkerr/backups ./file2.txt ./file3.txt ./file1.txt
cp: target './file1.txt' is not a directory

The problem is that the destination location for where we copy the files has to be the last parameter - but xargs puts the list of files at the end of the command.

And by the way - we've used the -0 parameter to make sure that funny filenames are handled properly (a good habit to get into) and the -t parameter to trace - which means we see the command which will be run.

So we need to tell xargs where to put the list of arguments. We can do that with the -I (replace string) parameter. This parameter lets us tell xargs exactly where we want to put the arguments:

$ find . -name "*.txt" -print0 | xargs -0 -t -I {} cp {} ~/backups
cp ./file2.txt /home/dwmkerr/backups
cp ./file3.txt /home/dwmkerr/backups
cp ./file1.txt /home/dwmkerr/backups

Here we have set the 'replacement string' to be {}. This means when xargs sees {} in the command it will replace it with the arguments we provide as its input.

The first observation you might make is that as soon as we use the -I parameter it automatically implies that we use the -L 1 parameter, i.e. we run the command once for each individual input line.

For the example we have shown above, this isn't really necessary, xargs could just write all of the arguments. The reason xargs does this is that we are not actually limited to using the replacement once only - we can use it multiple times.

Here's a similar example, but in this one we put .bak at the end of each filename as we copy it:

$ find . -name "*.txt" -print0 | xargs -0 -t -I {} cp {} ~/backups/{}.bak
cp ./file2.txt /home/dwmkerr/backups/./file2.txt.bak
cp ./file3.txt /home/dwmkerr/backups/./file3.txt.bak
cp ./file1.txt /home/dwmkerr/backups/./file1.txt.bak

Because we can use the replacement string multiple times, xargs splits up the commands so it is one command per input argument. If it didn't do this and we tried the command above it would not work properly.

The -I parameter is incredibly powerful, it lets us construct complex commands.

You don't need to use the {} letters as the replacement string, any sequence of characters will work. For example:

$ env | xargs -I % echo "You have env var: % set!"
You have env var: SHELL=/bin/bash set!
You have env var: COLORTERM=truecolor set!
You have env var: EDITOR=vi set!

In this example we used % as the replacement string.

You might wonder why is {} so commonly used in examples or the manpages. The reason is that this is the default replacement string used by find if we perform an action like -exec:

$ find . -type f -empty -exec stat {} \;

The {} characters are used as the placeholder for files found with the find command, so people often use the same placeholder for xargs, but you are not required to use these characters.

Requesting Confirmation with the Prompt Option​

The -p (prompt) option tells xargs to ask the user to confirm each command before it is run.

Let's test this out by deleting a set of 'pods' from a Kubernetes cluster. You don't have to worry about what a Kubernetes cluster is, I'm just using this as an example to highlight that you don't have to be limited to using find as the input for xargs!

On my machine I can show the pods available to me in my cluster with this command:

$ kubectl get pods -o name
pod/my-app
pod/nginx
pod/postgres

Three pods are shown. I could use this command to build input to xargs to let me to chose which pods to delete, interactively:

$ kubectl get pods -o name | xargs -L 1 -p kubectl delete
kubectl --context minikube delete pod/my-app?...n
kubectl --context minikube delete pod/nginx?...y
pod "nginx" deleted
kubectl --context minikube delete pod/postgres?...n

This is fantastic! We've used -L 1 to make sure that we only deal with one pod at a time (rather than trying to delete all three at once) and the -p flag to ask the user to press 'y' or 'n' in each case. The xargs command helpfully shows us what it is going to do and asks for confirmation first.

I think this really hints at the true power of xargs - yes it can be combined with find to perform operations on files, but it can also be used with other tools to build more complex operations.

Splitting up Input with a Delimiter​

We can ask the user whether they want to see files in all of their 'path' locations with the command below:

$ echo $PATH | xargs -d ':' -p -L 1 ls
ls /home/dwmkerr/.pyenv/shims ?...n
ls /home/dwmkerr/.nvm/versions/node/v14.15.1/bin ?...n

The $PATH environment variable holds all of the folders the shell will search in for binaries - and each folder is separated by a : colon character (you can read more about $PATH in Chapter 10 - Understanding Commands.

We use the -d (delimiter) parameter to tell xargs that each argument in the input is separated with a colon. We also use the -L 1 and -p parameters to process this input one folder at a time and ask the user if they want to see the contents of the folder.

Summary​

In this chapter we introduced xargs, a powerful command which allows us to build other commands on the fly. We can trace, showing how the resulting command will look, ask the user for confirmation, control how many commands we run and more.

There are more options for the xargs command, you can read all about them with man xargs. But I think if you learn the key parameters we've shown in this chapter you'll be well equipped to use xargs in your day to day wok.

In the next chapter we'll look at some of the advanced features which are built into most shells which allow us to manipulate text.


  1. The character is ASCII NUL, which is the number zero. This is often used in programming to represent 'null' or 'nothing at all', not the digit zero as is used when printing to the screen, which is actually represented by number 30. You can see the actual ASCII table with man ascii.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/get-to-grips-with-grep/index.html b/pr-preview/pr-346/part-3-manipulating-text/get-to-grips-with-grep/index.html new file mode 100644 index 00000000..18e22b4d --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/get-to-grips-with-grep/index.html @@ -0,0 +1,102 @@ + + + + + +Get to Grips with Grep | Effective Shell + + + + + + + + + + + + + + +
+

Get to Grips with Grep

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.

As with the other tools we'll introduce in this chapter, we'll also look at when grep is the right tool for the job and when we should consider other options.

What is Grep​

A quick check of the manual page for grep gives an overview:

$ man grep

GREP(1) BSD General Commands Manual GREP(1)

NAME
grep, egrep, fgrep, zgrep, zegrep, zfgrep -- file pattern searcher

SYNOPSIS
grep [-abcdDEFGHhIiJLlmnOopqRSsUVvwxZ] [-A num] [-B num] [-C[num]] [-e pattern] [-f file]
[--binary-files=value] [--color[=when]] [--colour[=when]] [--context[=num]] [--label]
[--line-buffered] [--null] [pattern] [file ...]

DESCRIPTION
The grep utility searches any given input files, selecting lines that match one or more
patterns. By default, a pattern matches an input line if the regular expression (RE) in
the pattern matches the input line without its trailing newline. An empty expression
matches every line. Each input line that matches at least one of the patterns is written
to the standard output.

Wow. Lots of options for this command. And confusingly, lots of alternative forms as well (such as egrep, zgrep and so on).

Maybe the tldr tool will provide a more concise overview?

$ tldr grep

grep

Matches patterns in input text.
Supports simple patterns and regular expressions.

That is indeed a little more concise. By the way, if you are not familiar with how you can get help on commands, check out Chapter 5 - Getting Help. As the manual pages indicate, grep is used to match patterns in files. More advanced users will most likely know exactly what this means, but a more simple description is just:

Grep lets you search for text or filter text.

That's it. You can search in files, but you are not limited to searching in files. And you can search for literal text, such as the word 'error', or you can search for patterns. Patterns in this case means regular expressions - expressions which allow you to be more specific in how you search (such as looking for a set of 16 numbers in a row, like a credit card number, or any text which looks like an email address). You can also do the opposite - filtering out parts of text.

We'll use grep to search through text. Let's get straight into it!

Why Grep?​

Why the odd name? Grep is such a commonly used tool that the name has become a verb amongst technologists (people will often suggest you grep for something in files).

The name comes from a command which was used in the original ed text editor - the command:

g/re/p

This command ran on all lines (g, for global), applied a regular expression (re, for regular expression) and then printed (p for print) the results. A colleague of Ken Thompson, one of the early innovators and inventors in the Unix world, needed to edit a large file - a file which was too large to fit in ed. Ken wrote the grep program overnight to allow the file's text to be filtered - and the results passed to the ed editor!

You can read more about this story and some of the fascinating history of the early days of Unix in a great interview with Brian Kerninghan from Computerphile1.

Searching Through Text​

If you've been working through this book, you've probably entered quite a few commands in the shell. Most shells keep a history of the commands you type. Under the hood, when you use the up and down keys to look through commands you entered earlier, or use the Ctrl-R shortcut to search through earlier commands, your shell is looking through this file. If these tricks are not familiar, check Chapter 9 - Fly on the Command Line.

The file which keeps the history can vary from shell to shell. For example, on my system, my history for Bash is in the file ~/.bash_history. But most 'Bash-like' shells provide a built-in environment variable which let's you find the path of the shell history. Let's at this file:

cat $HISTFILE

...
cat ~/.ssh/config
ssh bastion.cloudops
help echo
help cd
exit

This file will generally contain a list of the commands that the logged in user has entered in their shell. This file will likely be huge. Let's search through it using grep! Here's how we can use the grep command to search for lines which contain the text man:

grep man $HISTFILE
+
+...
+man socket
+k describe services eventstoredb-http-management
+man cal
+gcb refactor/performance/standardise-eventstore
+vi src/tests/handlers/test_command_handlers.py
+gco src/handlers/command_handlers.py
+gcb feat/performance/use-eventstore-writer
+nvim performance.md
+man grep
+

Here I can see all of the commands I have recently entered which have the text man in them. Note that the text which matches is highlighted and shown in bold.

Now what if you use a different shell, or forget where the history file lives? A nice trick here is to use the history command. This command prints out the history, as well as the line number. The history command writes to stdout. If we don't give grep a source file, it will simply search through stdin. Just as we learnt in Chapter 7 - Thinking in Pipelines this means we can just grep the output of the history command!

Here's how that would work:

history | grep man
+
+...
+9125  man socket
+9188  k describe services eventstoredb-http-management
+9211  man cal
+9341  gcb refactor/performance/standardise-eventstore
+9344  vi src/tests/handlers/test_command_handlers.py
+9347  gco src/handlers/command_handlers.py
+9352  gcb feat/performance/use-eventstore-writer
+9355  nvim performance.md
+10002  man grep
+

This is easier to remember! There's one more cool trick - if we just type in the exclamation point symbol followed by any line number shown above, we repeat the command! For example, typing in:

!9355

Would repeat line 9355 of the history (which is nvim performance.md).

Using Patterns​

Now as you can see from the output above, when we searched through my history, we didn't just find times I executed the man command - we found any line which has the characters man in it. What about if we only wanted to find the lines which start with man?

To perform a search like this, we can use a regular expression. Here's how it would work:

history | grep "[0-9]\+  man"
+
+...
+9125  man socket
+9211  man cal
+10002  man grep
+

Let's break this down. In this search, we are using a pattern to search for text. The pattern in this case is a basic regular expression. Regular expressions allow us to use some clever constructs to search for text. The expression we've used is made up of the following components:

  • [0-9]\+ At least one number - any character in the range zero to nine.
  • man The literal text written, i.e. two spaces and the letters man.

Now for anyone who is familiar with regular expressions, you might wonder why we have a slash before the + symbol, when the + symbol has a specific meaning in regular expressions (it means 'at least one of the previous characters). The reason we have a leading slash is that by default grep is using basic regular expressions. In general, this will be less familiar for users and will be different to what they are used to from different tools.

To make regular expressions more 'standard', we can use the -E flag to tell grep to use Extended Regular Expressions. We can also use the egrep tool, which assumes the pattern will be an extended regular expression. Using either approach will work, and allow you to re-write the pattern as below:

history | grep -E "[0-9]+  man"

# ...or...

history | egrep "[0-9]+ man"

This is just a little hint of the power of regular expressions. They can be daunting at first, and many people never become comfortable with them, but I would strongly encourage you to start exploring them.

Getting Help on Regular Expressions

If you want to find out more about the difference between the slightly old-fashioned basic regular expressions and modern regular expressions, you can use:

man re_format

This manpage gives lots of information on regular expressions, including the differences between basic and extended patterns.

If you ever want to see how a regular expression works, try using the website regex101.com. It let's you test out regular expressions and also describes exactly how they work. For example, if I enter the regular expression we just used I'll see this:

Screenshot of the 'regex101' website

We're going to see more about regular expressions as we go through the book.

Finding Problems​

If there's one command I use a lot, it's this:

grep -i err

The -i flag makes the search case-insensitive. This makes this a very quick way to scan through a file for any text which matches the letters err - making it a very quick way to find errors in log files.

You can try this out by using some of the log files in the logs folder of the samples. Here's how you can try it out:

grep -i err ~/effective-shell/logs/web-server-logs.txt
+
+...
+2020-11-29T12:50:30.594Z: info - Serving file '../../../website/public/docs/part-2-core-skills/7-thinking-in-pipelines/images/diagram-stderr-redirect.png'...
+2020-11-29T12:50:31.827Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500
+2020-11-29T12:50:31.827Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500
+2020-11-29T12:50:31.827Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500
+2020-11-29T12:50:31.848Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/edit.svg', returning a 500
+2020-11-29T12:50:31.849Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/edit.svg', returning a 500
+

This is a very useful trick. You could use this technique to search for warnings, problems, specific messages and so on.

The ABC of Grep​

There are three really useful parameters for grep, which I used to struggle to remember, until I realised that they are simple - ABC!

Here's how they work:

$ grep host -A 3 ./programs/web-server/web-server.js
+
+host: process.env.HOST || 'localhost',
+port: process.env.PORT || getOptonalEnvInt('PORT', 8080),
+root: process.env.ROOT || process.cwd(),
+defaultPage: 'index.html',
+--
+httpServer.listen({host: config.host, port: config.port &brace;);
+log.info(`Server running on: ${config.host}:${config.port}`);
+}
+main();
+

A stands for after. In this example we show the three lines after each occurrence of the work host in the web-server.js script. This is a quick way to see how something you search for might be used!

B stands for before - we can use this to see what comes before a match when we're searching. What can lead to us sending an error in our web server? Let's see:

$ grep throw -B 5 ./programs/web-server/web-server.js
+
+//  Helper to return an optional numeric environment variable or the default.
+function getOptonalEnvInt(name, defaultValue) {
+const val = process.env[name];
+if (!val) return defaultValue;
+const intVal = parseInt(val, 10);
+if (isNaN(intVal)) throw new Error(`Unable to parse environment variable named '${name}' with value '${val}' into an integer`);
+

And finally C, the most useful of them all. C stands for context, and lets you see a number of lines before and after each match. What was I up to the last time I ran the git init command? Let's see!

$ history | grep -C 5 'git init'
+
+5802  git push --follow-tags && git push origin
+5803  cd ../java-maven-standard-version-sample
+5804  rm -rg .git
+5805  rm -rf git
+5806  rm -rf .idea
+5807  git init -h
+5808  git remote add origin git@github.com:dwmkerr/java-maven-standard-version-sample.git
+5809  git push origin -u
+5810  git push -u origin
+5811  git push --set-upstream origin master
+5812  git rm --cached tpm
+

Don't forget that these flags need to be capitalised! These three flags are very useful - knowing how to find context of a match can be a lifesaver when quickly searching through text.

Working with Multiple Files​

What about if you have a bunch of files you want to search? One problem we have at the moment is that everything we search through has been a single file. But if we are searching through multiple files, how can we identify where the matches come from?

There's a useful pair of flags for this. -H stands for 'header', which shows the file name before each match. -n stands for 'number', which makes sure the line number is shown. Here's how we might use this command:

$ grep -Hn ERROR ./logs/apm-logs/*.logs
+
+...
+./logs/apm-logs/apm02.logs:34893:2020-11-27T12:24:37.429Z       ERROR   [request]       middleware/log_middleware.go:95unauthorized     {"request_id": "53a41a98-ba12-454e-aadf-72c97dc40e96", "method": "POST", "URL": "/config/v1/agents", "content_length": 27, "remote_address": "127.0.0.1", "user-agent": "elasticapm-python/5.9.0", "response_code": 401, "error": "unauthorized"}
+./logs/apm-logs/apm02.logs:34906:2020-11-27T12:25:11.415Z       ERROR   [request]       middleware/log_middleware.go:95unauthorized     {"request_id": "a49d5546-b8d2-4e50-9dd0-6cbf419a365e", "method": "POST", "URL": "/config/v1/agents", "content_length": 27, "remote_address": "127.0.0.1", "user-agent": "elasticapm-python/5.9.0", "response_code": 401, "error": "unauthorized"}
+

Note that in this case we searched through many files - anything which matches the *.logs wildcard. To help us identify in which file the match was found, we used the -Hn flags. The beginning of the lines now start with the path of the file and the line number, for example:

./logs/apm-logs/apm02.logs:34906

You can take this even further:

$ grep -R -Hn -i error ./logs

Adding the -R or recursive flag tells grep to search recursively in folders if they are included in the search.

V for Invert​

As long as your remember that -i is the flag for case insensitive, it makes it a little easier to remember v for invert. This tells grep to exclude lines which match the pattern. This works kind of like a filter.

Here's how I could look through my log files, excluding any messages with 'debug' in them:

$ grep -v debug ./logs/web-server.logs

Don't forget, you can always pipe a series of grep commands together. Rather than trying to work out a perfect pattern which searches for exactly what you want, you could just pipe a a set of commands together:

$ grep -i error -R ./logs | grep -i -v memory | grep -i -v 'not found'

This set of small, simple, commands is chained together to make a more sophisticated operation:

  • First we recursively search for any error text in the ./logs folder
  • Then we exclude anything which matches memory
  • Then we exclude anything which matches not found

This is the essence of the Unix Philosophy - with a small number of simple tools, we can compose a more complex workflow!

Don't Forget Your Pipelines!​

We've introduced a very powerful command in this chapter. For familiar users, grep becomes a verb they use regularly - you grep the output of something, or might be grepping to find something. Remember that grep, just like most of the tools in this section, works on stdin by default. So you can easily grep the output of almost anything!

Here are a few simple examples just to show you how easy it is to perform more complex tasks with grep.

ps -a | grep vim

Show all processes, then filter the list down to only vim processes.

grep -Hn -C 3 -R password ./k8s/**/*.yaml | less

Search through all of the yaml files in my k8s folder, for the text 'password', show three lines of context, as well as the file name and number, and put the output in my pager so that it is easy to search through.

ls -al /usr/bin /bin /usr/local/bin | grep zip

Search through all of my installed programs for programs which have zip in the name.

history | grep grep | tail -n 10

Show me the last ten grep commands I typed in my shell!

We'll see a lot more examples as we go through the book - just remember that grep is always available to search or filter text!

Alternatives to Grep​

Grep is a very commonly used tool and has been around for a long time. It can vary a bit from system to system. Over the years a number of alternatives have been developed. Most of these alternatives are either designed to be faster, so that you can search through files much more quickly, or easier so that you don't have to remember too many flags.

In general, I would advise against using alternatives - until you genuinely find you are limited by grep. Every alternative is another tool to learn, which might not be present on other systems you use. It is also less likely to be available if you are writing scripts or instructions for others.

If you find yourself really struggling with performance - perhaps you often search huge folders of text or if you find yourself regularly struggling to find ways to craft your search patterns, then perhaps you can investigate some of the popular alternatives. But I would suggest that you master the core grep functionality first, before installing other tools.

If you do decide you want to add some more text searching tools to your toolkit, I would suggest ripgrep, ag and ack as three potential options. Each of them offer performance improvements and additional functionality.

Summary​

Grep is a simple text-based search tool! If you need to find text, or want to filter text, then grep should be your go-to tool.

Here's a summary of what we've covered:

  • grep pattern file searches file for the text pattern
  • the -E flag lets you use regular expressions for more sophisticated searches
  • You can make the search case insensitive with the -i flag
  • Remember the ABC flags - after, before and context, which show lines after, before and around the matches
  • Include the filename and line number with the -Hn flags
  • V for invert! Use the -v flag to invert the search, or filter out matches
  • grep works great in pipelines! Use it to search or filter when working with other commands
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/index.html b/pr-preview/pr-346/part-3-manipulating-text/index.html new file mode 100644 index 00000000..d2dbbd4f --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/index.html @@ -0,0 +1,26 @@ + + + + + +Part 3 - Manipulating Text and Streams | Effective Shell + + + + + + + + + + + + + + +
+

Part 3 - Manipulating Text and Streams

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.

This means that you may find yourself regularly manipulating text, searching through text and working with text files. There are a lot of options for how to do this! In fact, there are so many options that it can be a bit overwhelming to know what is the right tool for the job.

In this section we'll look at some of the key techniques which can be used to work with text, and demonstrate this with practical examples. In each chapter I'll try to show lots of real world use cases to keep things as applicable to usual tasks as possible.

Hopefully, by the time you have completed this section, you will have a great understanding of the tools available to you and how to apply them to the task at hand.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/regex-essentials/index.html b/pr-preview/pr-346/part-3-manipulating-text/regex-essentials/index.html new file mode 100644 index 00000000..de5d6929 --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/regex-essentials/index.html @@ -0,0 +1,138 @@ + + + + + +Regex Essentials | Effective Shell + + + + + + + + + + + + + + +
+

Regex Essentials

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.

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.

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!

An Introduction to Regular Expressions​

Regular Expressions (or 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.

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:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08
\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?
:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[
0-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
-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\
x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

Note that I have split the expression into multiple lines so that it fits on the page.

This is 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.

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.

So should we learn regular expressions? Are they even valuable if they are as complex as the example above?

Managing Complexity with Regular Expressions​

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 start simple and add complexity only if you need it.

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 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.

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:

  1. Create a small list of valid email address
  2. Add some items to the list which look 'kind of' valid but are not quite right
  3. Build a regular expression which matches the correct email address
  4. Refine the expression to eliminate the invalid addresses

In most cases this will be sufficient.

Let's start with the following set of addresses:

dave@effective-shell.com
dave@effective-shell
to: dave@effective-shell.com
dave@effective-shell.com <Dave Kerr>
test123.effective-shell.com
@yahoo.com
whatever123@πŸ˜‚.com
dave@kerr@effective.shell.com

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).

Building Regexes - Start with the Basics​

Here's how I would start building a regex for an email address:

  1. Any set of characters
  2. Followed by an @ at symbol
  3. Followed by any set of characters

That regex would look like this:

.*@.*

The first bit, .* means 'any character' (this is what the . dot symbol means), 'any number of times (this is what the * asterisk symbol means).

The second bit is just the literal @ at symbol character.

The third bit is the same as the first - any characters any number of times.

Let's see how that would look in regex101:

Regular Expression Example 1

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.

Now that we have a basic pattern which matches the valid address, we can refine it to eliminate invalid addresses.

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 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!

Building Regexes - Quantifiers​

The regular expression we have is very simple - .*@.*. 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.

Let's see how we can refine this expression further to eliminate some of the invalid addresses. Let's start with @yahoo.com. It doesn't have anything before the at symbol.

This is being matched by our pattern because our pattern allows any characters before and after the at symbol any number of times - including zero times.

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 quantifier (a 'quantifier' is the part of a pattern which says 'how many occurrences of the characters do we expect).

Previously we used the * asterisk quantifier (which means 'any number of times'). Now we'll use the + plus quantifier (which means 'at least one time'). Let's see how it looks:

.+@.+
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

This is better - we've eliminated some invalid addresses, test123.effective-shell.com, @yahoo.com and dave@.

We have introduced a key concept - the quantifier. The quantifier we have used is the + plus symbol. This is the part of a regular expression which says 'how many times can a character be matched?'.

There are a few different quantifiers, here is a quick reference:

QuantifierMeaning
*Any number of characters.
+At least one character.
?Between zero and one character.
{10}Exactly ten occurrences of the character.
{10,}Ten or more occurrences of the character.
{10,20}Between ten and twenty occurrences of the character.

Now let's look at the character itself.

Building Regexes - Character Sets and Metacharacters​

When we are matching text, we match a set of characters a number of times. The set of characters we match can be a character set (which is when we explicitly say what is allowed), or a metacharacter (which is a predefined character set). This concept is far easier to explain with an example.

Let's look at the address dave@kerr@effective.shell.com. This is clearly invalid, it has two at symbols. We can use character sets or metacharacters to fix this.

The reason this address matches our expressions is that we are using the . dot metacharacter before and after the at symbol. The dot metacharacter means 'any character' (except a newline). This includes the at symbol character.

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.

Character Sets - Ranges

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:

[A-Za-z0-9]

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:

[A-Za-z0-9]+@[A-Za-z0-9]+
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

This fails to match the valid email address 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.

Let's see how we can do better.

Character Sets - Special Characters

We can add more characters to our character set. To include the dot and the hyphen, we just add them directly to the set:

[A-Za-z0-9-.]

That's all there is to it! We can now see our pattern is more correct:

[A-Za-z0-9-.]+@[A-Za-z0-9-.]+
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

However, the expression is getting larger and larger. We can use a 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:

\w+@\w+
\w+@\w+
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

The uses the 'word' metacharacter, \w. This is just a shorthand for [a-zA-Z0-9_]. But now our pattern fails again as the \w metacharacter doesn't include the hyphen or the dot. We can fix this easily - a character set can include metacharacters! So we can just combine \w and the hyphen and dot:

[\w+-.]+@[\w+-.]+
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

Character Sets - Negating Characters

We can use the ^ circumflex symbol to negate a character. This allows us to build a character set which doesn't match a pattern. For example, we could rewrite our pattern like this:

[\S^@]+@[\S^@]+
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

We've used the character set [\S^@] which means 'any none-whitespace character' (this is the \S part) and 'not the at symbol character' (this is the ^@ part).

Notice that in this case we have more matches - because the set of characters we are using is larger than a set such as \w. This expression now covers the email address with the emoji, because the emoji is not a whitespace character or an at symbol.

Character Sets - Escaping Characters

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.

Here's an example:

[\[\]]+

This matches square brackets between one and many times.

[\^]\+

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 literal character, escape it by putting a slash in front of it.

Character Sets - Quick Reference

We've seen quite a few character sets and metacharacters, here's a quick reference for some commonly used ones:

QuantifierMeaning
.Any character except for a line break.
\wAny 'word' character, [a-zA-Z0-9_].
\WAny non 'word' character.
\sAny 'whitespace' character (space, tab, etc).
\SAny non 'whitespace' character.
\dAny 'digit' character [0-9].
\DAny non 'digit' character.

There are many other metacharacters, you can find more in the manpage man re_pattern

Building Regexes - Anchors​

At the moment, if we use the expression below:

[\S^@]+@[\S^@]+
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

Then we match any line which 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?

For this, we can use anchors. Anchors represent special parts of a string, such as the start or beginning of a line.

If we want to only match lines which contain a complete email address, we can use the ^ circumflex (start of line) anchor and $ dollar (end of line) anchor:

^[\S^@]+@[\S^@]+$
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

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 starts with the letters to: we could just use this:

^to: .* 
+
+dave@effective-shell.com
+dave@effective-shell
+to: dave@effective-shell.com
+dave@effective-shell.com <Dave Kerr>
+test123.effective-shell.com
+@yahoo.com
+dave@
+whatever123@πŸ˜‚.com
+dave@kerr@effective.shell.com
+

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.

Building Regexes - Capture Groups​

You can extract only part of what you match in a regular expression using 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.

Here's an example:

(.+)@(.+)
+
+dave@effective-shell.com
+

Now the entire line matches, but everything surrounded by () parentheses is a capture group. This means that the regular expression has actually made three matches:

  1. dave@effective-shell.com - The first match in an expression is always the complete match
  2. dave - This is the first capture group, everything before the at symbol
  3. effective-shell.com - This is the second capture group, everything after the at symbol.

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.

Building Regexes - Lazy and Greedy Expressions​

Regular expressions can be lazy or greedy. Whether an expression is lazy or greedy affects how it matches patterns - basically whether it stops at the earliest point the pattern is matched, or continues until the pattern no longer matches.

Regular expressions are 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:

<.+>
+
+This text is <strong>bold</strong>.
+

The regular expression <.+> 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 last angled bracket on the line - i.e. everything up until the closing </strong> tag.

We can create a lazy expression by using the ? 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:

<.+?>
+
+This text is <strong>bold</strong>.
+

In this example we have actually captured two results - the contents of the opening and closing tag. Because the expression <.+?> is lazy it matches only until the first closing brace it finds, meaning that the results are quite different.

To get the same results without using the lazy quantifier, we'd have to have an expression like this:

<[^>]+>
+
+This text is <strong>bold</strong>.
+

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.

Avoiding Advanced Topics - Backtracking, Lookarounds and Atomic Grouping​

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.

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!

I am not going to show examples of each of these concepts. I'll explain why after a brief summary of the concepts.

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.

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 exploits1.

In short - very broad and greedy expressions such as .+ (match 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.

Lookarounds are special constructs which allow you to essentially say +"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 not preceded or followed by another pattern".

As an example, the expression \d+(?=€) matches digits (this is the \d metacharacter), at least one or more (this is the + plus symbol), but only if the digits are followed by a Euro symbol. In this case the (?=€) part of the pattern is a 'positive lookahead'.

I have not yet found a situation where I've really needed lookaround expressions. For example, I would simply write the above expression as:

(\d+)€

Which simply matches digits which precede a Euro symbol and puts them in a capture group.

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.

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 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.

A Word of Warning​

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.

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).

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!

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 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.

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!

Summary​

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 learn regular expressions.

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.


  1. There is a fascinating write up of how this led to a severe Cloudflare outage in 2019 available online at: https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/shell-script-essentials/index.html b/pr-preview/pr-346/part-3-manipulating-text/shell-script-essentials/index.html new file mode 100644 index 00000000..2ac9906c --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/shell-script-essentials/index.html @@ -0,0 +1,26 @@ + + + + + +Shell Script Essentials | Effective Shell + + + + + + + + + + + + + + +
+

Shell Script Essentials

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.

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 env command in shebangs.

What is a Shell Script?​

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.

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.

Creating a Basic Shell Script​

Let's create a simple shell script that shows us our most commonly used shell commands.

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.

As we go through this section of the book, we're going to extend this script and make it more useful!

The 'common' Command​

We're going to create a new command, called 'common', that shows the most commonly used shell commands.

We should be able to do this using techniques we've seen so far. We'll do it like this:

  1. Read a large number of commands from the history
  2. Sort the commands, then count the number of duplicates
  3. Sort this list showing the most commonly run commands first
  4. Print the results to the screen.

Let's get started!

Creating a Simple Script​

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.

In your favourite editor, create a file called common.v1.sh and put it somewhere on your system. As an example, I'm going to create a folder called scripts in my home directory and create the common.v1.sh file there:

# Create a directory called 'scripts'.
# Using the '-p' flag means we won't get an error if the folder exists.
mkdir -p ~/scripts

# Create the script file.
touch ~/scripts/common.v1.sh

# Open the script file in my favourite editor.
vi ~/scripts.sh

I have called the script common.v1.sh rather than 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 common.v2.sh, common.v3.sh and so on.

These commands should be familiar. The mkdir command creates a directory. The -p (create parent directories if needed) flag stops the command from returning an error if the directory already exists.

The 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 Chapter 32 - A Vim Crash Course.

Before we run the script, let's quickly talk about comments.

Comments​

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 – they are purely for the benefit of the reader.

Any text that follows a # 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.

Using comments effectively can be a huge time-saver – 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.

Let’s look at some examples of comments.

# This is a comment - we can use this to describe what we're trying to do.

echo "Hello Shell" # Comments can go at the end of a line...

# You can also use a comment symbol to 'comment out' a line:
# echo "Goodbye Shell"

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 – we've just put a hash in front of some commands so that they will not be executed.

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 intent - why you are doing something. This is far more useful for the reader than what you are doing. The 'what' should be clear from the commands - the 'why' is the thing readers will likely want to understand.

Here's an example of a bad comment:

# Write the CSV file, reverse it, cut it, reverse it.
cat ~/effective-shell/data/top100.csv | rev | cut -d',' -f1 | rev

The comment just describes what the script is doing. But it doesn't explain why. A better comment would be:

# Get the last field of each line in the csv file.
cat ~/effective-shell/data/top100.csv | rev | cut -d',' -f1 | rev

If you 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!

Building and Testing the Script​

Add the following commands to the common.v1.sh file:

# Write the title of our command.
echo "common commands:"

# Show the most commonly used commands.
tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10

This is a short script, but there is quite a lot going on. Let's look at it blow-by-blow:

  1. First we take the last 1000 lines of the ~/.bash_history file using the tail command1
  2. Then we sort the commands. This will put all of the duplicates next to each other
  3. Then we remove all duplicates and use the -c (show count) flag to count the duplicates
  4. Then we remove the leading spaces from the output (which we need to do so that we can sort properly)
  5. Then we sort numerically and in reverse - the highest count first
  6. Finally, we limit the results to the first ten items

If you need a refresher on the shell history, sort or uniq the check the Slice and Dice Text chapter. If the sed command doesn't look familiar then check the Advanced Text Manipulation with Sed chapter.

If you want to see a more detailed breakdown of how the script works, check Appendix - How the Script Works. But this is not necessary for you to follow the content in this chapter.

Now save the file. In your shell, run the following command to execute the file:

sh ~/scripts/common.v1.sh

The 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:

common commands:
463 vi
267 gc
238 ga .
212 ls
169 gpo
122 make dev
112 gl
97 gcm
96 gpr

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.

We now have a basic shell script. Let's look at a few different ways we can run the script.

Multi-line Commands​

You can use the \ 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.

As an example, we could re-write our pipeline command to look like this:

# Show the most commonly used commands.
tail ~/.bash_history -n 1000 \
| sort \
| uniq -c \
| sed 's/^ *//' \
| sort -n -r \
| head -n 10

This will probably look very familiar to anyone with a background in functional programming!

Be careful when you split lines up - the continuation character must be the last character on the line. If you add something after it (such as a comment) then the command will fail.

Running a Shell Script​

There are a few different ways we can run shell scripts.

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:

bash ~/scripts/common.v1.sh

This is a perfectly valid technique. Now let's see the other ways we can run a script.

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 chmod (change file mode) command to do this:

chmod +x ~/scripts/common.v1.sh

If the chmod command looks unfamiliar then check the 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:

~/scripts/common.v1.sh

There is a problem with this approach though. How this file is executed is going to vary depending on how your system is set up2. 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 sh program (and depending on your system, this program might just be a link to another type of shell).

We want to avoid any ambiguity and be explicit about what program should run our script. We can do this using a special construct called a shebang.

Using Shebangs​

A 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.

If we were to add a shebang to our common.v1.sh file, it would look like this:

#!/usr/bin/sh

# Write the title of our command.
echo "common commands:"

# Show the most commonly used commands.
tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10

The shebang is the two characters - #!. 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'.

Immediately after the shebang you write the full path to the program which should be used to open the file.

For example, if you wanted to write a script that is run in Python, you could do this:

#!/usr/bin/python3

print('Hello from Python')

If we wanted to explicitly use the Bash shell to run a script, we might use a shebang like this:

#!/usr/bin/bash

echo "Hello from Bash"

What about Node.js? Easy!

#!/usr/bin/node

console.log("Hello from Node.js");

Shebangs - Dealing with Paths​

When we use a shebang we need to provide the full path to the executable that runs the script.

For example, if we want to use Ruby to run a script we could write a script like this:

#!/usr/bin/ruby

puts 'Hello from Ruby'

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. /usr/bin/ruby). If you do not have the Ruby program in this location the script will fail to run.

How can we know where a specific program is installed?

There is a common trick for dealing with this issue. We can use the env (set environment and execute command) command to run a command and it will work out the path for us.

The 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 env command is that it looks through the $PATH variable to find the path of the command to execute.

You can see this by running a command like the below:

$ env python3
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

We've used the env command to run the python3 command - and it worked out the correct path for us.

To use env in a shebang, specify the full path to env (which should be the same on all Unix-like systems) and then provide the name of the command to run:

#!/usr/bin/env bash

echo "Hello from Bash"

Or another example:

#!/usr/bin/env ruby

puts 'Hello from Ruby'

Using a shebang to specify the exact command to run, and then using the env command to allow the $PATH to be searched is generally the safest and most portable way to specify how a shell script should run.

Sourcing Shell Scripts​

We have discussed how to run shell scripts. You can also use the source (execute commands from a file) command to load the contents of a file into the current shell.

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.

Let's see an example. We'll create a script called set_editor.sh that sets the EDITOR environment variable to nano. The script's contents are below (can also find it in the samples at ~/effective-shell/scripts/set_editor.sh):

EDITOR=nano
echo "Editor changed to: $EDITOR"

Let's run this script and see what editor looks like before and after:

$ echo $EDITOR
vim
$ ~/effective-shell/scripts/set_editor.sh
Editor changed to: nano
$ echo $EDITOR
vim

Notice that although we changed the 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 copy of the environment.

If we want to run the commands in the file in the context of the current shell, we can use the source command to load the file:

$ echo $EDITOR
vim
$ source ~/effective-shell/scripts/set_editor.sh
Editor changed to: nano
$ echo $EDITOR
nano

Our existing environment has been changed. When we use source, the commands in the file are executed in the current shell, rather than in a new shell.

We can see this even more clearly if we use the showpstree.sh file:

$ ~/effective-shell/scripts/show-info.sh
bash
└─sh /home/ubuntu/effective-shell/scripts/showpstree.sh
└─pstree -l -a -s 2240

This script shows the current 'process tree', using the pstree (show process tree) command. We can see that the pstree command was run as a child of the sh program. This program was run with the script path, by the shell I was using, bash. This is a nice visualisation of what is going on - our bash shell has run the showpstree.sh script in a child shell.

If we source the same file, we'll see that we do not create a new shell:

$ source ~/effective-shell/scripts/show-info.sh
bash
└─pstree -l -a -s 2169

Dot Sourcing​

There is a slightly more concise syntax to source a script - the dot sourcing notation. When the shell sees a . dot character, it will source the file that follows:

$ . ~/effective-shell/scripts/show-info.sh
bash
└─pstree -l -a -s 2169

You may encounter this syntax as you look at things like shell configuration files, which we will discuss in Chapter 24 - Configuring the Shell.

Installing Your Script​

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.

Our common.v1.sh script (with the added shebang) looks like this:

#!/usr/bin/env sh

# Write the title of our command.
echo "common commands:"

# Show the most commonly used commands.
tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10

If we have made the script executable with the chmod command, then we can run the script by simply typing the location of the script in the shell:

$ ~/scripts/common.v1.sh
common commands:
97 gcm
96 gpr
...

If we want to 'install' this script as a local command which we can run easily, we can create a symbolic link to the shell script in our /usr/local/bin folder:

ln -s ~/scripts/common.v1.sh /usr/local/bin/common

The ln (create link) command creates a link (which is like a shortcut in Windows and other desktop systems) in our /usr/local/bin folder, with the name common, which points to the script we have written. We can now run the common command without specifying its path:

$ common
common commands:
97 gcm
96 gpr
...

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 /usr/local/bin folder is in this list of paths.

Why do we use the /usr/local/bin folder rather than the /usr/bin folder? This is just a convention. In general, the /usr/bin folder is for commands which are installed with package manager tools like apt or Homebrew (on MacOS). The /usr/local/bin folder is used for commands which you create for yourself on your local machine and manage yourself3.

Summary​

In this chapter we've covered quite a few of the fundamentals of shell scripts:

  • How to create a shell script
  • How comments work in shell scripts
  • How to handle long lines with continuations
  • How to run a shell script
  • How to make a shell script executable
  • How shebangs work
  • How to use the env command to make our shebangs more portable
  • How to 'install' scripts for the current user

In the next chapter we'll look at how to add logic to our shell scripts.


Appendix - How the Script Works​

This section briefly covers how the common.v1.sh script works. Assuming we have a history that looks like this:

vi README.md
git status
git checkout main
git status
restart-shell
git status
open .
vi README.md
open .

First we sort, putting duplicate lines next to each other:

git checkout main
git status
git status
git status
open .
open .
restart-shell
vi README.md
vi README.md

Then we use uniq to remove duplicate adjacent lines, passing the -c flag to include a count:

     1 git checkout main
3 git status
2 open .
1 restart-shell
2 vi README.md

Now we remove the leading whitespace:

1 git checkout main
3 git status
2 open .
1 restart-shell
2 vi README.md

Finally we sort numerically (by using the -n flag) and in descending order (by using the -r flag):

3 git status
2 vi README.md
2 open .
1 restart-shell
1 git checkout main

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:

1 git checkout main
1 restart-shell
13 git status
2 open .
2 vi README.md

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.


  1. The path to the shell history file is normally available in the $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 $HISTFILE variable or history command!↩
  2. Try putting the command pstree -p $$ in a shell script and running the script - you'll see exactly what process is run.↩
  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.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/slice-and-dice-text/index.html b/pr-preview/pr-346/part-3-manipulating-text/slice-and-dice-text/index.html new file mode 100644 index 00000000..15f88b3a --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/slice-and-dice-text/index.html @@ -0,0 +1,26 @@ + + + + + +Slice and Dice Text | Effective Shell + + + + + + + + + + + + + + +
+

Slice and Dice Text

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.

We'll start with the basics and move onto some of the more sophisticated commands in the next chapter.

Heads and Tails​

The commands head and tail are very simple but incredibly useful.

head is used to extract part of the top of a file and tail is used to extract part of the end of a file. Once you starting using these commands you'll find yourself using them regularly.

Let's start with 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?

$ head ~/effective-shell/data/top100.csv

"Rank","Rating","Title","Reviews"
"1","97","Black Panther (2018)","515"
"2","94","Avengers: Endgame (2019)","531"
"3","93","Us (2019)","536"
"4","97","Toy Story 4 (2019)","445"
"5","99","Lady Bird (2017)","393"
"6","100","Citizen Kane (1941)","94"
"7","97","Mission: Impossible - Fallout (2018)","430"
"8","98","The Wizard of Oz (1939)","120"
"9","96","The Irishman (2019)","441"

The head command just shows the first ten lines of a file. Here we can see that this is a 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.

You can use the -n flag to specify the number of lines you want to see, for example:

$ head -n 3 ~/effective-shell/data/top100.csv

"Rank","Rating","Title","Reviews"
"1","97","Black Panther (2018)","515"
"2","94","Avengers: Endgame (2019)","531"

The tail command works in the same way - but looks at the 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 recent entries.

Here's how we can see the ten most recent commands we entered in our shell:

$ tail $HISTFILE

: 1606818280:0;ls
: 1606818300:0;ln -s $(pwd) ~/effective-shell
: 1606818308:0;cat ~/effective-shell/data/top100.csv
: 1606818342:0;head -n 3 ~/effective-shell/data/top100.csv
: 1606819062:0;head ~/effective-shell/data/top100.csv
: 1606819647:0;gcd
: 1606819649:0;git stash
: 1606819650:0;gcd
: 1606819662:0;git stash pop
: 1606819803:0;tail $HISTFILE
What is $HISTFILE?

Most Bash-like shells keep a file called the history file. This is essentially a record of all of the commands which have been written in the shell. The 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 $HISTFILE.

Enter help history for more information on the shell history.

We can be more specific, just like with head, by specifying the number of lines to show:

$ tail -n 3 $HISTFILE

: 1606819650:0;gcd
: 1606819662:0;git stash pop
: 1606819803:0;tail $HISTFILE

tail can also be used to show the changes to a file in real time. Add the -f flag to follow the contents of the file - this means the tail command show each new line as it gets added to the file.

To try it out, run the following command in one shell:

$ tail -f $HISTFILE

In another terminal window, start entering commands. You'll see that the tail command in the first window is writing the updates to the terminal as they are entered in the file. Press Ctrl+C to close the tail program.

Another trick I use a lot with tail is to use -n +2. This shows everything from the second line - the + 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:

$ head ~/effective-shell/data/top100.csv | tail -n +2

"1","97","Black Panther (2018)","515"
"2","94","Avengers: Endgame (2019)","531"
"3","93","Us (2019)","536"
"4","97","Toy Story 4 (2019)","445"
"5","99","Lady Bird (2017)","393"
"6","100","Citizen Kane (1941)","94"
"7","97","Mission: Impossible - Fallout (2018)","430"
"8","98","The Wizard of Oz (1939)","120"
"9","96","The Irishman (2019)","441"

Here I've taken the head of the file (otherwise the output gets quite difficult to follow), then piped the results into 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.

We're going to use head and tail quite a lot when working with text. These are two crucial tools which can really speed up your work.

Replacing Text​

The next tool we'll look at is tr (translate characters). This program is very simple. My most common use for tr is to perform a simple substitution of characters.

Let's create a list of each of the columns in the data file we saw before to show how the command works:

$ head -n 1 ~/effective-shell/data/top100.csv | tr ',' '\n'

"Rank"
"Rating"
"Title"
"Reviews"

What about if we wanted to remove the quotes?

$ head -n 1 ~/effective-shell/data/top100.csv | tr ',' '\n' | tr -d '"'

Rank
Rating
Title
Reviews

Here we've seen two variations on how we can run the command. The first form is used to replace characters. Running:

tr ',' '\n'

Replaces the first specified character with the second. The \n character is the special newline character, which is used to create a line break at the end of a file.

The second form uses the -d flag to specify a set of characters to delete:

tr -d '"'

In the form above we delete quote (") characters.

When using tr remember that it works on characters. For example, the following might not work as you expect:

$ echo "Welcome to the shell" | tr 'shell' 'machine'

Wcicomc to tac macii

The reason the output is like this is that we're specifying character replacements - so we're changing characters as shown below:

s -> m
h -> a
e -> c
l -> h
l -> i

There are plenty of ways to replace entire words or perform more complex operations, but we'll use sed or awk for these operations - which we'll see in the following chapter.

There is one final thing it is worth mentioning about tr. It can be provided with character classes. This is easiest to explain with an example:

$ echo "Use your inside voice..." | tr '[[:lower:]]' '[[:upper:]]'

USE YOUR INSIDE VOICE...

In this case we are transforming characters in the lower class (lowercase characters) to the upper class (uppercase characters).

On Linux systems you can find more about character classes with 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 extended regexes) which I think are likely to be more useful to learn about instead.

How to Cut​

The next command is one which I've used far more than I expected. The cut command splits a line of text, using a given delimiter. Let's see some examples:

$ cut -d',' -f 3 ~/effective-shell/data/top100.csv | head

"Title"
"Black Panther (2018)"
"Avengers: Endgame (2019)"
"Us (2019)"
"Toy Story 4 (2019)"
"Lady Bird (2017)"
"Citizen Kane (1941)"
"Mission: Impossible - Fallout (2018)"
"The Wizard of Oz (1939)"
"The Irishman (2019)"

This is the first way to use cut. We specify the -d flag to choose a delimiter which we will cut the text with, then -f to choose which field we want to see. In this case we show split on the command character and show the third field - the title of the film in the data file.

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:

$ kubectl get pods

NAME READY STATUS RESTARTS AGE
elastic-operator-0 1/1 Running 0 35d
elk-apm-server-65b698fb8c-rzncz 1/1 Running 0 13d
elk-es-default-0 1/1 Running 0 35d
elk-kb-6f8bb6457b-bbbnn 1/1 Running 0 35d
filebeat-beat-filebeat-ccgl7 1/1 Running 1 13d
filebeat-beat-filebeat-dvf2l 1/1 Running 2 13d
filebeat-beat-filebeat-mnpms 1/1 Running 329 13d
kube-state-metrics-5cb57bdc45-mqv9d 1/1 Running 0 35d
metricbeat-beat-metricbeat-2xm7t 1/1 Running 6103 35d
metricbeat-beat-metricbeat-96dkt 1/1 Running 6097 35d
metricbeat-beat-metricbeat-n7kxm 1/1 Running 6109 35d

Now to get the name I can just cut the lines on the 'space' character and grab the first field:

$ kubectl get pods | cut -d' ' -f 1

NAME
elastic-operator-0
elk-apm-server-65b698fb8c-rzncz
elk-es-default-0
elk-kb-6f8bb6457b-bbbnn
filebeat-beat-filebeat-ccgl7
filebeat-beat-filebeat-dvf2l
filebeat-beat-filebeat-mnpms
kube-state-metrics-5cb57bdc45-mqv9d
metricbeat-beat-metricbeat-2xm7t
metricbeat-beat-metricbeat-96dkt
metricbeat-beat-metricbeat-n7kxm

And if we want to strip the first line? We can use the tail -n +2 command to tail everything from the second line onwards:

$ kubectl get pods | cut -d' ' -f 1 | tail -n +2

elastic-operator-0
elk-apm-server-65b698fb8c-rzncz
elk-es-default-0
elk-kb-6f8bb6457b-bbbnn
filebeat-beat-filebeat-ccgl7
filebeat-beat-filebeat-dvf2l
filebeat-beat-filebeat-mnpms
kube-state-metrics-5cb57bdc45-mqv9d
metricbeat-beat-metricbeat-2xm7t
metricbeat-beat-metricbeat-96dkt
metricbeat-beat-metricbeat-n7kxm

Bingo - we've removed the heading line. If you remember grep from the previous chapter, you might have spotted that we could also just filter the content:

$ kubectl get pods | cut -d' ' -f 1 | grep -v NAME

elastic-operator-0
elk-apm-server-65b698fb8c-rzncz
elk-es-default-0
elk-kb-6f8bb6457b-bbbnn
filebeat-beat-filebeat-ccgl7
filebeat-beat-filebeat-dvf2l
filebeat-beat-filebeat-mnpms
kube-state-metrics-5cb57bdc45-mqv9d
metricbeat-beat-metricbeat-2xm7t
metricbeat-beat-metricbeat-96dkt
metricbeat-beat-metricbeat-n7kxm

With even just a few simple shell commands there are often many ways to accomplish the same goal!

There is another way we can cut text. We can cut by slicing a number of characters from each line.

Let's take a look at our web logs file:

$ tail ~/effective-shell/logs/web-server-logs.txt

2020-11-29T12:50:52.721Z: info - Request: GET /en.search.min.1f83b222e24a227c0f5763727cb9e4f3b435f08b936f6ce529c9c9359f6b61a8.js
2020-11-29T12:50:52.722Z: info - Serving file '../../../website/public/en.search.min.1f83b222e24a227c0f5763727cb9e4f3b435f08b936f6ce529c9c9359f6b61a8.js'...
2020-11-29T12:50:52.762Z: info - Request: GET /svg/menu.svg
2020-11-29T12:50:52.763Z: info - Serving file '../../../website/public/svg/menu.svg'...
2020-11-29T12:50:52.763Z: info - Request: GET /svg/calendar.svg
2020-11-29T12:50:52.764Z: info - Serving file '../../../website/public/svg/calendar.svg'...
2020-11-29T12:50:52.765Z: info - Request: GET /svg/edit.svg
2020-11-29T12:50:52.766Z: info - Serving file '../../../website/public/svg/edit.svg'...
2020-11-29T12:50:52.784Z: info - Request: GET /fonts/roboto-v19-latin-300italic.woff2
2020-11-29T12:50:52.785Z: info - Serving file '../../../website/public/fonts/roboto-v19-latin-300italic.woff2'...

We can use the -c (characters) flag to specify the characters in the line we want to see. Let's extract the timestamp only:

$ tail -n 3 ~/effective-shell/logs/web-server-logs.txt | cut -c 12-19

12:50:52
12:50:52
12:50:52

We can also use the character option to extract everything from a specific point onwards:

$ tail -n 3 ~/effective-shell/logs/web-server-logs.txt | cut -c 27-

info - Serving file '../../../website/public/svg/edit.svg'...
info - Request: GET /fonts/roboto-v19-latin-300italic.woff2
info - Serving file '../../../website/public/fonts/roboto-v19-latin-300italic.woff2'...

By cutting from the 27th character onwards (-c 27-) we remove the timestamp and just get the log message.

As a nice trick you can use the same syntax when splitting by fields:

$ tail -n 3 ~/effective-shell/data/top100.csv | cut -d',' -f 3-

"Pinocchio (1940)","55"
"Chinatown (1974)","75"
"The Dark Knight (2008)","342"

This is field three onwards. If we just want fields two and three, we use:

$ tail -n 3 ~/effective-shell/data/top100.csv | cut -d',' -f 2,3

"100","Pinocchio (1940)"
"99","Chinatown (1974)"
"94","The Dark Knight (2008)"

There's a surprising amount you can do with the cut tool. As we introduce more complex tools later on, like sed and awk, we'll see other ways to accomplish the same goals, but I often find that by filtering down the content with grep first I can cut my way to what I need without having to use more complex tools.

A Trick with Rev​

There is a very simple command called rev which reverses the given input. For example:

$ echo "A nut for a jar of tuna" | rev

anut fo raj a rof tun A

At first glance this doesn't seem very useful - but there's a nice trick we can do with this:

$ pwd | rev | cut -d'/' -f 1 | rev

effective-shell

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:

pwd              /Users/dwmkerr/effective-shell
rev llehs-evitceffe/rrekmwd/sresU/
cut -d'/' -f 1 llehs-evitceffe
rev effective-shell

This is a neat trick to rip all of the text from the 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!

Sort and Unique​

Two other commands which can be really helpful are sort and uniq. Let's see sort first:

$ cut -d',' -f 3 ~/effective-shell/data/top100.csv | sort | head

"12 Years a Slave (2013)"
"A Hard Day's Night (1964)"
"A Night at the Opera (1935)"
"A Quiet Place (2018)"
"A Star Is Born (2018)"
"Alien (1979)"
"All About Eve (1950)"
"Argo (2012)"
"Arrival (2016)"
"Avengers: Endgame (2019)"

Here we've grabbed the third field in our data file (the name of the film), sorted, then shown the first ten values.

You can reverse the direction of sort with the -r flag:

$ cut -d',' -f 3 ~/effective-shell/data/top100.csv | sort -r | head

"Zootopia (2016)"
"Wonder Woman (2017)"
"Won't You Be My Neighbor? (2018)"
"Widows (2018)"
"War for the Planet of the Apes (2017)"
"Us (2019)"
"Up (2009)"
"Toy Story 4 (2019)"
"Toy Story 3 (2010)"
"Toy Story 2 (1999)"

There are actually quite a few other options for sort, you can see them with 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 uniq). You might find some of them useful so don't be shy to explore some of the other options.

The uniq command removes duplicate lines from a stream of text. Note that this only removes duplicate lines when they are next to each other. This means that you will often have to sort first.

Here's an example of where I might use uniq - getting all unique error messages in a log file:

$ cut -c 27- ~/effective-shell/logs/web-server-logs.txt | grep error | sort | uniq

error - Unhandled error EACCES trying to read '../../../website/public/docs/part-1-transitioning-to-the-shell/5-getting-help/index.html', returning a 500
error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500
error - Unhandled error EACCES trying to read '../../../website/public/svg/edit.svg', returning a 500
info - Request: GET /docs/1-getting-started/images/ls-applications-windows-error.png
info - Request: GET /docs/part-1-transitioning-to-the-shell/3-managing-your-files/images/rm-error-directory.png
info - Serving file '../../../website/public/docs/1-getting-started/images/ls-applications-windows-error.png'...
info - Serving file '../../../website/public/docs/part-1-transitioning-to-the-shell/3-managing-your-files/images/rm-error-directory.png'...

Let's break this down:

  • cut -c 27- ~/effective-shell/logs/web-server-logs.txt - extract log messages from a log file, skipping the timestamp
  • grep error - filter down to lines which contain the text error
  • sort - sort the output
  • uniq - show only unique values

This is a powerful technique - if we had thousands of errors in the file, this would make sure we only see distinct errors, rather than showing every error.

Don't Forget Your Pager!​

In Chapter 5 - Getting Help we talked about the 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 before you use head or tail) then you can use the pager to avoid filling your screen and terminal with too much text.

For example, when looking at the sorted list of films, I might run this:

$ cut -d',' -f 3 ~/effective-shell/data/top100.csv | sort | less "Jaws (1975)" "King Kong (1933)" "La Grande illusion (Grand Illusion) (1938)" "La La Land (2016)" "Lady Bird (2017)" "Laura (1944)" /Jaws

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 less so that I can page through the data and ensure it is correct - I've also searched for the text Jaws to see where it is in the file.

Summary​

In this chapter we introduced a number of basic tools which let us work with text.

  • head will show the first ten lines of a file.
  • head -n 30 will show the first thirty lines of a file, using the -n flag to specify the number of lines.
  • tail will show the final ten lines of a file.
  • tail -n 3 uses the -n flag to specify three lines only.
  • The $HISTFILE environment variable holds the path to the shell command history file.
  • tail -f $HISTFILE uses the -f flag to follow the file, printing output as it is written to the file.
  • tr 'a' 'b' is the translate text command, which turns one set of characters into another
  • tr -d '!' shows how the -d or delete flag can specify characters to delete.
  • The cut command can be used to extract parts of a line of text.
  • cut -d',' -f 3 shows how the -d or delimiter flag is used to specify the delimiter to cut on and how the -f or field flag specifies which of the fields the text has been cut into is printed.
  • cut -c 2-4 uses the -c or characters flag to specify that we are extracting a subset of characters in the line, in this case characters two to four.
  • cut -c 10- cuts from character ten to the end of the line
  • The cut command also allows for multiple fields to be specified when cutting by field, such as -f 2,3 for the second and third field, or -f 4- for fields four onwards.
  • rev reverses text - by reversing, cutting and then re-reversing you can quickly extract text from the end of a line.
  • sort sorts the incoming text alphabetically.
  • The -r flag for sort reverses the sort order.
  • The uniq command removes duplicate lines - but only when they are next to each other, so you'll often use it in combination with sort.
  • Your pager, for example the less program can be useful when inspecting the output of your text transformation commands.
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-3-manipulating-text/variables-reading-input-and-mathematics/index.html b/pr-preview/pr-346/part-3-manipulating-text/variables-reading-input-and-mathematics/index.html new file mode 100644 index 00000000..e65b6897 --- /dev/null +++ b/pr-preview/pr-346/part-3-manipulating-text/variables-reading-input-and-mathematics/index.html @@ -0,0 +1,26 @@ + + + + + +Variables, Reading Input, and Mathematics | Effective Shell + + + + + + + + + + + + + + +
+

Variables, Reading Input, and Mathematics

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.

Variables​

Variables are places where the system, the shell, or shell users like ourselves can store data.

We've already seen variables a few times in this book. For example in Chapter 5 - Getting Help we saw the $PAGER variable that is used to specify what pager program should be used in the shell.

When we want to use a variable in the shell, we use the $ dollar symbol to specify the variable name:

echo "Your pager is: $PAGER"

If you run this command you will see something like this:

Your pager is: less

By convention, if a variable is in uppercase then it is an environment variable or a built in variable that comes from the shell. An environment variable is a variable that is set by the system. They often contain useful values to help configure your system.

Here's a few common environment variables we might use:

echo "Your shell is: $SHELL"
echo "Your user is: $USER"
echo "Your user's home directory is: $HOME"

Your output will look similar to the below:

Your shell is: /bin/bash
Your user is: dwmkerr
Your user's home directory is: /home/dwmkerr

Setting Variables​

You can create or set your own variables by simply entering the name you would like to use and putting an = equals symbol after the variable, followed by the value you would like to use.

This is the one of the few times that you will use a variable name without putting a dollar symbol before it!

name="Dave"
location="Singapore"
echo "Hello $name in $location"

This will produce the output:

Hello Dave in Singapore

By convention, variables that you define yourself should be lowercase. This helps to distinguish between environment variables and your own variables.

It is a good habit to use lowercase for variable names. Using uppercase will work, but when you use uppercase you run the risk of 'overwriting' the value of an environment variable and causing unexpected results later.

For example, in this snippet I accidentally overwrite the USER variable. If a later part of the script expects the USER variable to contain the Linux username of the user then there will likely be an error because I have set it to something else!

# Don't do this!
USER="Dave Kerr"

# If I wanted to go to my home directory, this command would fail. That's
# because USER should be 'dwmkerr' but I've set it to something else!
cd "/home/$USER"

If you set a system variable to something incorrect, the impact will be limited to only the script you are running or the shell session you are in, or any commands you run from the script or session - other running programs will not have their copy of the variable changed. You can read more about this in the Processes chapter of the Linux Fundamentals section.

Shell Variables and Environment Variables​

The variables we create in the Shell are called Shell Variables. They are accessible in the current shell session that we are running.

Shell variables are isolated to the current process. If we run another process from our shell, such as another shell script or program, our shell variables are not inherited by this process. This is by design - these shell variables are expected to be used for our local session only.

If you want to ensure that a variable is available to all child processes, you can use the export (set export attribute) builtin to tell the shell to export the variable as an Environment Variable.

Environment Variables are always inherited by child processes - so if you need to provide some kind of configuration or context to a child process, you will likely want to export your variable.

As an example, let's set a variable to indicate whether we want to show some kind of extra diagnostic information to the user when running scripts:

export DEBUG_MODE=1
sh -c 'echo "Debug Mode is: ${DEBUG_MODE}"'

Note that we are not using the DEBUG_MODE variable in the current script, we have provided a literal command to the sh program which will run in its own process. This process will inherit the shells environment and therefore can use the value of the DEBUG_MODE variable. If we did not use the export keyword then the value would be undefined in the child process.

We also see another convention here - environment variables are generally capitalized. This can make them a bit more noticeable. These variables should be used with care, you could potentially overwrite a variable in your environment (and therefore the child environments) that another program has set.

You can see a list of the current environment variables that are set with the env (set or print environment) command:

env
SHELL=/bin/zsh
LSCOLORS=ExFxBxDxCxegedabagacad
COLORTERM=truecolor
PYENV_SHELL=bash
# etc

Storing the Output of a Command into a Variable​

We can use a subshell to run a command and store the result in a variable.

For example, if we had a variable which held a user's password and wanted to show it on the screen in a 'masked' form, where all of the characters are replaced with an asterisks symbol, we could write the password variable into the sed command and replace every character with an asterisks symbol like so:

password="somethingsecret"
masked_password=$(echo "$password" | sed 's/./*/g')
echo "Setting password '${masked_password}'..."

The output of this script will look like this:

Setting password '***************'...

To execute a set of commands in a 'sub shell', we can use the $() sequence. Everything inside the brackets will be executed in a new shell. We can then store the output of the commands in a variable by using the = equals symbol.

Being Explicit with Variable Names​

You can use curly braces around the name of a variable to be more explicit about what the variable name is. Let's take a look at why you might need to do this:

echo "Creating backup folder at: '$USER_backup'"
mkdir $USER_backup

This script shows the output:

Creating backup folder at: ''
usage: mkdir [-pv] [-m mode] directory ...

Rather than creating a folder called dwmkerr_backup (which is my $USER variable followed by the text _backup), the script has actually failed. That is because it is looking for a variable called USER_backup - which does not exist!

To get around this we would surround the variable name with curly braces like so:

echo "Creating backup folder at: '${USER}_backup'"
mkdir "${USER}_backup"

This script will show the correct output:

Creating backup folder at: 'dwmkerr_backup'

If there is ever any potential ambiguity with a variable name you should enclose it with curly braces to be on the safe side. Some people will use curly braces in all circumstances to be as explicit as possible about what the variable name is and reduce the risk of mistakes if someone later comes along to change the code.

This script would be improved with the use of a variable of our own to avoid us having to repeat the ${USER}_backup text:

backupdir="${USER}_backup"
echo "Creating backup folder at: '${backupdir}'"
mkdir "${backupdir}"

In this case creating a variable to save us from creating the backup directory folder name each time we want to use it.

We've looked at environment variables and our own local variables. Now let's look at how we can read input from the user and store it in a variable for later usage.

Arrays​

Arrays are variables that can store multiple values. An array is created by using the equals symbol and putting the array values in parenthesis, like so:

days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")

Once you have defined your array you can retrieve an element at a given index by using the square bracket notation shown below:

echo "The first day is: ${days[0]}"
echo "The last day is: ${days[6]}"

Arrays in Bash start at index zero. Arrays in the Z-Shell start at index one - this can cause confusion and mistakes in scripts so it is something you might have to consider if you are writing scripts that can be used by either shell.

There are a number of useful operations you can perform on arrays. An example of each is shown below:

OperationSyntaxExample
Create Arrayarray=()days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
Get Array Element${array[index]}echo ${days[2]} # prints 'Wednesday'
Get All Elements${array[@]}echo ${days[@]} # prints 'Monday Tuesday Wednesday Thursday Friday Saturday Sunday'
Set Array Elementarray[index]=valuedays[0]="Mon"
Get Array Indexes${!array[@]}arr=(); arr[3]="apple"; arr[5]="pear"; echo ${!arr[@]} # prints 3 5
Get Array Length${#array[@]}echo ${#days[@]} # Prints 7
Append to Arrayarray+=(val1 val2 valN)fruits=(); fruits+=("Apples"); fruits+=("Pears" "Grapes"); echo ${fruits[@]} # prints 'Apples Pears Grapes'
Get a subset of elements${array[@]:start:number}echo ${days[@]:5:2} # prints 'Saturday Sunday'

It's important to use curly braces around your array expressions. Note that in the examples above when we set an array value we don't use braces or the dollar symbol - this is consistent with what we've seen so far - variable names do not have a dollar symbol when we are setting a value.

You might have noticed from the examples that arrays in Bash can be sparse - that means that you can have 'gaps' in your array. Arrays can also have a mixture of strings or numbers - not every element in an array has to be of the same type.

We'll see arrays in more detail in the chapter on Loops.

Associative Arrays​

More recent versions of Bash support the concept of Associative Arrays. These are arrays where rather than having a numeric index associated with each value, you can have a string. This allows you to create a 'map' or 'hash table' structure.

An associative array is created using the declare (set variable) command:

# Create an associative array called 'book'.
declare -A book

# Set some values on the array.
book[title]="Effective Shell"
book[author]="Dave Kerr"

# Show one of the values.
echo "Book details: ${book[title]} - ${book[author]}"

Running this command will show the output:

Book details: Effective Shell - Dave Kerr

If you find yourself using associative arrays, I expect that there is a good chance you are trying to do something that is more complex than is suitable for a shell script. In this circumstance I'd suggest you read Chapter 30 - How to Avoid Scripting to see how I'd look at alternative options!

Quoting Variables and Values​

There is often a lot of confusion about a specific topic in the shell - when should you surround a variable in quotes? This might sound like a purely stylistic question, but surrounding a variable in quotes can dramatically change how your script works.

We're going to look at each type of quoting and when it should be used in the examples below. But if you ever need a reminder, run man bash and search for the text QUOTING.

Single Quotes - Literal Values​

Use single quotes when declaring a variable or using a value if you want to use literal text. The shell will not expand special characters or variables:

message='   ~~ Save $$$ on with ** "this deal" ** ! ~~   '
echo "$message"

This script will show:

   ~~ Save $$$ on with ** "this deal" ** ! ~~

Note that the shell has not tried to expand the ~ tilde into /home/dwmkerr. It has not expanded the * asterisks into a wildcard pattern and it has not tried to use the $ dollar symbol to reference an array.

Single quotes should be used when you want to put special characters into a variable, or call a command that includes whitespace or special characters.

Single Quotes - ANSI C Quoting​

There is a special form of single quotes called 'ANSI C Quoting' that allows you to use escape sequences from the C language. ANSI C quoting is single quoting that starts with a dollar symbol. You can use it to use special characters like newlines in a variable:

message1='Hello\nWorld'
message2=$'Hello\nWorld'
echo "Message 1: $message1"
echo "Message 2: $message2"

This snippet will show the following results:

Message 1: Hello\nWorld
Message 2: Hello
World

Double Quotes - Parameter Expansion​

Double quotes work in a very similar way to single quotes except that they allow you to use parameter expansion with the $ dollar symbol and escaping with the \ symbol. The ```` backtick symbol is also treated differently. Let's see some examples:

deal="Buy one get one free"
message="Deal is '$deal' - save \$"
echo "$message"

The output of this snippet is:

Deal is 'Buy one get one free' - save $

Notice that the $deal value has been expanded into the contents of the $deal variable. The last dollar symbol has been escaped with a \ backslash - the shell knows that this means we want to use the literal value of the dollar symbol at the end of the message. The backslash has been removed.

The backtick character is also treated differently, as the backtick can be used to run a sub-shell:

$ echo "The date is `date`"
The date is Sun 23 May 2021 11:36:54 AM +08

However, you should not use the backtick character to run a sub-shell, it is harder to read than using the dollar and parenthesis syntax we've already seen:

$ echo "The date is $(date)"
The date is Sun 23 May 2021 11:36:54 AM +08

No Quotes - Shell Expansion​

If you don't include quotes around a variable or value, then the shell will perform a series of operations called Shell Expansion. This includes many options we've seen so far, let's take a look at some examples:

home=~
tilde="~"
echo "My home is: $home"
echo "A tilde is: $tilde"

This snippet shows the results:

My home is: /home/dwmkerr
A tilde is: ~

In the first case the shell has expanded the ~ tilde to the home directory.

We do not use quotes around a variable or a value if we want the shell to shell expansion. The following expansions will be performed:

  • Brace expansion: touch file{1,2,3} is expanded to touch file1 file2 file3
  • Tilde expansion: cd ~ is expanded to cd /home/dwmkerr
  • Parameter and variable expansion echo $SHELL is expanded to echo /usr/bin/sh (note that this expansion also occurs with double quotes)
  • Command substitution: echo $(date) is expanded to echo the results of the date command (this also occurs with double quotes)
  • Arithmetic expansion: square=$((4 * 4)) has the value 4 * 4 evaluated mathematically (we see this at the end of this chapter)
  • Word splitting: this is a more complex topic discussed in Chapter 21 - Loops and working with Files and Folders
  • Pathname expansion: ls *.txt is expanded to all filename that match the wildcard pattern *.txt

We are going to see more detail on Shell Expansion as we continue through this part of the book. There is also a detailed explanation in Chapter 29 - Understanding Shell Expansion final section of the book and an appendix with a quick reference.

Quoting Tips​

Quoting can seem confusing - but remember these tips and you will generally be on the right path:

  • Use double quotes most of the time - they will handle variables and sub-shells for you and not do weird things like word splitting
  • Use single quotes for literal values
  • Use no quotes if you want to expand wildcards

Shell Parameter Expansion​

Shell Parameter Expansion is the process by which the shell evaluates a variable that follows the $ dollar symbol. In most of our examples we simply expand the variable into its value, like so:

$ echo "My shell is $SHELL"
My shell is: /usr/bin/sh

But there are a number of special features we can use when expanding parameters. There are many options available and you can find them all by running man bash and searching for the text EXPANSION. Let's take a look at some of the most common ones.

Length

The ${#var} operator returns the length of the variable var:

var="The quick brown fox jumps over the lazy dog"
length=${#var}
echo "Length: $length"
# Prints: 43

Set Default Value

The ${var:-default} operator returns the value of the variable var or the text default if it is not found:

read -p "Enter your username: " user
username=${user:-$USER}
echo "Username: $username"
# Prints what you typed or the value of $USER otherwise

Substring

The ${var:start:count} operator returns a subset of the var variable, starting at position start and extracting up to count characters. If count is omitted everything from start to the end of the string is returned.

path="~/effective-shell"
echo "${path:0:2}"
# Prints ~/
echo "${path:2}"
# Prints effective-shell

Make Uppercase

The ${var^^} operator returns the value of var with the text transformed to uppercase:

message="don't shout"
echo ${message^^}
# Prints: DON'T SHOUT

Make Lowercase

The ${var,,} operator returns the value of var with the text transformed to lowercase:

message="DON'T SHOUT"
echo ${message,,}
# Prints: don't shout

Variable Indirection

The ${!var_name} operator returns the value of the variable with the name in specified in the var_name variable. This is useful if you want to get the value of a variable but don't know the name of the variable:

read -p "Enter a variable name: " var_name
echo "The value of '${var_name}' is: ${!var_name}"

The output of this snippet would look like this:

$ Enter a variable name: SHELL
The value of 'SHELL' is: /bin/bash

Notice the similarity to the Array operators such as ${#array[@]} to get the length of an array.

There are a number of other operators that exist. They allow you to extract parts of a string, apply regular expressions, manipulate the case and perform a number of complex operations. I would avoid these techniques if possible as they are fairly specific to Bash and likely will be confusing to readers. Some of these substitutions are not available in older versions of Bash.

If you need to manipulate text I would recommend that you use the techniques described in Part 3 - Manipulating Text.

It is generally enough to know that if you see special symbols inside a ${variable} expression then the writer is performing some kind of string manipulation. Hopefully they have included a comment that describes what they are doing to make it easier to follow!

You can find out more about these features in the manual under the EXPANSION section1.

The Read Command​

The read (read from standard input) command can be used to read a line of text from standard input. When the text is read it is put into a variable, allowing it to be used in our scripts.

Let's see how this look in action!

echo "What is your name?"
read
echo "Hello, $REPLY"

Run the script - when you have finished writing your name, press 'enter'. This is needed because read will keep on reading until it reaches the end of a line, so we need to press 'enter' to complete the input.

What is your name?
Dave
Hello, Dave

The read command reads a line of text from standard input and stores the result in a variable called REPLY. We can then use this variable to use the text that was read.

Why is the variable in uppercase? That's because even though we are setting the variable itself, it is still a 'special' variable defined by the shell. It is the variable that read puts its input into if we don't explicitly tell read what the variable name should be.

Reading into a Variable​

We can tell the read command to put the input it reads into a variable with a name of our choice by specifying the variable name after the command, like so:

echo "What is your name?"
read name
echo "Hello, ${name}"

In general you should provide a variable name for read - it will make your script a little easier to understand. Not every user will know that the $REPLY variable is the default location, so they might find it confusing if you don't provide a variable name. By specifying a variable name explicitly we make our script easier to follow.

This also shows good coding practices - your variable names should be descriptive, and inform the reader of what they are likely to be used for. This makes the script easier to follow and maintain over time.

This is another time that we use a variable name without putting a dollar before it. It might be helpful to remember that the dollar is used when we want to use the variable and the dollar is omitted when we want to set the variable.

Prompting for Input​

Before you run the read command you are probably going to write a message to the user letting them know they need to enter some input. We can either write out a message first to prompt the user, using the echo command as shown above, or we can use the special -p (prompt) parameter:

read -p "Please enter your name: " name
echo "Hello, $name"

Now the output will look like this:

Please enter your name: Dave
Hello, Dave

Z-Shell Note

If you are using the Z-Shell, then this command will fail as zsh does not use the -p parameter for prompt. To prompt a user for input with the read command in zsh, just put a line of text after the command that starts with a question mark:

read "?Please enter your name: "
echo "Hello, $REPLY"

Reading Secrets​

The -s (silent) flag can be used to hide the input as it is being written. This is useful if you want to read a secret such as a password:

read -s -p "Enter a new password: " password
masked_password=$(echo "$password" | sed 's/./*/g')
echo ""
echo "Your password is: $masked_password"

The output of this script will be something like the below:

Enter a new password:
Your password is: ********

This uses the same trick as before to mask the characters. Note that when we use the -s flag, the read command does not print what we've typed - meaning we don't print the 'enter' symbol that the user presses to finish entering text. This means we don't see a new line after the read command. So we use echo "" to write a newline before we show the output.

Limiting the Input​

There may be times where you don't want to have the user press 'enter' to indicate that they have finished writing input.

There are a couple of ways we can limit the input. The first is to use the -n (number of characters) parameter to limit the number of characters that are read:

read -n 1 -p "Continue? (y/n): " yesorno
echo ""
echo "You typed: ${yesorno}"

This script will only wait for the user to type a single character as we used the -n flag with the value 1 to specify that we want to read a single character only.

Because the user doesn't press 'enter' at the end of their input, we need to add a blank newline before we show the output - otherwise it would look like this:

Continue? (y/n): nYou typed: n

It's only when we read a full line of text that we don't need to write an empty line. That's because when we read a full line of text we finish by pressing 'enter', which moves the cursor down to the next line for us.

The other way to limit the input is to specify a character that is to use a delimiter to indicate when read should stop reading input:

read -d ' ' -p "Enter your favourite word (then a space): " word
echo ""
echo "Your favourite word is: ${word}"

Because we used the -d ' ' parameter, the read command will read up until it finds a 'space' symbol. This can be confusing for users however - if they press enter then read will read it as a newline and continue waiting for a space. So you should let the user know to finish input with the delimiter you have chosen!

In general using anything other than a newline as the delimiter may be confusing to the user, and also causes some problems when the user wants to type special characters such as backspace, so I would suggest that you avoid this trick. Instead, let the user type their input and then use something like sed to extract everything up to the point that you want.

There are a number of other options for the read command that you can read about by typing help read. But these are the ones that I think you will see most commonly used.

Mathematics​

The shell has some built in features that let you perform mathematical operations. You will commonly perform these operations on variables.

You might assume that you can use symbols like + directly in the your scripts to perform mathematical operations - but they may not perform as expected. For example, here's what happens if you try to add two numbers together with the + plus symbol:

read -p "Enter a number: " number1
read -p "Enter another number: " number2
sum=$number1+$number2
echo "The sum of $number1 and $number2 is $sum"

If you run this script you'll see something like this:

Enter a number: 23
Enter another number: 34
The sum of 23 and 34 is 23+34

The result we see is not the sum of the two numbers - it is the two numbers with the literal + plus symbol between them.

To tell the shell that we want to perform an arithmetic operation, rather than just write out a mathematical operator, we use the 'double parenthesis' syntax shown below:

read -p "Enter a number: " number1
read -p "Enter another number: " number2
sum=$(($number1 + $number2))
echo "The sum of $number1 and $number2 is $sum"

The output of this script will be something like the below:

Enter a number: 23
Enter another number: 34
The sum of 23 and 34 is 57

There is an alternative syntax - we can use the let keyword to indicate to the shell that we want to perform an arithmetic operation. This would look like this:

let sum=$number1 + $number2

I've included the let keyword here for completeness, but I would recommend that you use the double-parenthesis where possible as I think that it is probably the more commonly used construct.

There are many arithmetic operators available. Here's a table showing a few common ones and how they are used:

OperatorMeaningExample
+Additionecho $((3+4)) # prints 7
-Subtractionecho $((4-2)) # prints 2
*Multiplicationecho $((4*2)) # prints 8
/Divisionecho $((4/2)) # prints 2
**Exponentecho $((4**3)) # prints 64
%Modulusecho $((7%3)) # prints 1
++iPrefix Incrementi=1; echo $((++i)) # prints 1, i is set to 2
i++Postfix Incrementi=1; echo $((i++)) # prints 2, i is set to 2
--iPrefix Decrementi=3; echo $((--i)) # prints 3, i is set to 2
i--Postfix Decrementi=3; echo $((i--)) # prints 2, i is set to 2
i+=nIncrementi=3; echo $((i+=3)) # prints 6, i is set to 6
i-=nDecrementi=3; echo $((i-=2)) # prints 1, i is set to 1

If you want to find the complete set of arithmetic operators available or find more details on how arithmetic works in the shell, use man bash and search for the text ARITHMETIC\ EVALUATION (the backslash is needed to escape the space between the words when searching in the manual).

The script below shows how you can use a combination of operators to convert a value in degrees Celsius to Fahrenheit:

read -p "Enter a value in Celsius: " celcius
fahrenheit=$(( (celcius * 9/5) + 32 ))
echo "${celcius} degrees Celsius is ${fahrenheit} degrees Fahrenheit"

Note that you can use brackets in your arithmetic expressions to be explicit about the order in which the calculations should be performed. The order that is used if you don't use brackets is detailed in the manual page, but in general using brackets will make things clearer to the reader.

Updating the 'Common' Command​

With our new understanding of variables, we can improve the 'common' command we created in the previous chapter by extracting certain values into variables so that they can be more easily changed.

Let's look at our original 'common' command:

# Write the title of our command.
echo "common commands:"

# Show the most commonly used commands.
tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n | tail -n 10

We could improve on this by making the number of lines of text in the history we search through and the number of commands to show variables, so that they can be more easily changed.

Create a copy of the common.v1.sh script and call it common.v2.sh and update it like so:

# Write the title of our command.
echo "common commands:"

# The following variables control how the command runs.
history_lines=1000 # The number of lines of history to search through
command_count=10 # The number of common commands to show.

# Show the most commonly used commands.
tail ~/.bash_history -n ${history_lines} \
| sort \
| uniq -c \
| sed 's/^ *//' \
| sort -n -r \
| head -n ${command_count}

We have replaced two 'hard-coded' values (the number of lines of history to search and the number of common commands to show) with variables, which are now easier to find and change. We have also split the command into multiple lines so that it is easier to read (as the line is quite long otherwise).

If you want to replace the installed common command with this new one, update the symlink in your /usr/local/bin folder:

ln -sf $HOME/effective-shell/scripts/common.v2.sh /usr/local/bin/common

Note that in this command we use the -f flag to force the creation of the symlink even if one already exists in the given location.

Summary

In this chapter we looked at how environment variables work and how we can use our own variables. We saw how to read input from the user and how to perform arithmetic operations.

We've seen a few new constructs in this chapter that will appear again and again, these are summarised below so that you can recognise them!

  • ${variable} gets the value of variable - the braces surround the variable name
  • $(echo "$PAGER") runs the echo command in a subshell - the single parenthesis indicates we are running a subshell
  • $(($left + $right)) adds the values in the variables left and right - the double parenthesis indicate that we are performing arithmetic

In the next chapter we are going to see how to perform logic in scripts - running commands only when certain conditions are met. This is an incredibly powerful technique and will let you create much more sophisticated scripts!


  1. There is also a very good discussion on the differences in quoting options in the following Stack Overflow thread: https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-4-shell-scripting/functions-parameters-and-error-handling/index.html b/pr-preview/pr-346/part-4-shell-scripting/functions-parameters-and-error-handling/index.html new file mode 100644 index 00000000..ccfea267 --- /dev/null +++ b/pr-preview/pr-346/part-4-shell-scripting/functions-parameters-and-error-handling/index.html @@ -0,0 +1,26 @@ + + + + + +Functions, Parameters and Error Handling | Effective Shell + + + + + + + + + + + + + + +
+

Functions, Parameters and Error Handling

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.

Creating a Function ​

A function has the following structure:

<function-name> {
<function-command 1>
<function-command 2>
<function-command n>
}

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.

Let's take a look at a very simple function in action:

title() {
echo "My Script version 1.0"
}

This script defines a very simple function called 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:

$ title
My Script version 1.0"

Easy! Functions let you structure commands into logical blocks and can help make your scripts easier to read and manage.

Variables in Functions​

A function can read and write to any variables in the current shell session. Here's an example:

# Set some variables.
title="My Cool Script"
version="1.2"
succeeded=0

# Create a function that writes a message and changes a variable.
title() {
# Note that we can read variables...
title_message="${title} - version ${version}"
echo "${title_message}"

# ...and set them as well.
succeeded=1
}

# Show the value of 'succeeded' before and after the function call.
echo "Succeeded: ${succeeded}"
title
echo "Succeeded: ${succeeded}"
echo "Title Message: ${title_message}"

The output of this script will be:

Succeeded: 0
My Cool Script - version 1.2
Succeeded: 1
Title Message: My Cool Script - version 1.2

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.

Variable Scoping ​

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 dynamic scoping. Many common programming languages like Python, JavaScript, C, Java and others use an alternative mechanism called lexical scoping.

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.

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:

run_loop() {
local count=0
for i in {1..10}; do
# Update our counter.
count=$((count + 1))
done
echo "Count is: ${count}"
}

Let's see what happens if we run this function:

$ run_loop
Count is: 10
$ echo "Count: ${count}"
Count:

Notice that because we declared the count variable using the 'local' keyword, it is only available inside the run_loop function. If we try and access it outside of the function it is undefined.

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:

# Set a count variable somewhere in our script...
count=3

# Call our 'run_loop' function.
run_loop

# Write out the value of 'count'.
echo "The 'count' variable is: ${count}"

The output of this script is:

Count is: 10
The 'count' variable is: 3

Notice that even though we used a variable named count in the 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:

Count is: 10
The 'count' variable is: 10

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.

Passing Parameters to Functions​

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:

sum() {
local value1=$1
local value2=$2
local result=$((value1 + value2))
echo "The sum of ${value1} and ${value2} is ${result}"
}

Let's see how we can pass parameters to this function:

$ sum 3 6
The sum of 3 and 6 is 9
$ sum 10 33
The sum of 10 and 33 is 43

In this script we have used the special $1 and $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:

# Create a function that calculates the sum of two numbers.
sum() {
echo "The sum of $1 and $2 is $(($1 + $2))"
}

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.

Parameter Variables​

There are a number of special parameter variables that the shell provides. Let's see a few in action:

# Create a function that sums a set of numbers.
sum() {
local total=0
for value in $@; do
total=$((total + value))
done

# Write out the result.
echo "Summed $# values for a total of: ${total}"
}

We can call this function with any number of parameters:

$ sum 1 2 3 4 5
Summed 5 values for a total of: 15

In this script we've used two special variables. The $@ variable is expanded into a list of all of the function parameters. The $# variable contains the number of parameters provided to the function.

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 Chapter 19 - Variables, Reading Input, and Mathematics. You can actually use some of the array-style operators with the special parameters variables:

# Show the top 'n' values of a set.
show_top() {
local n=$1
local values=${@:2:n}
echo "Top ${n} values: ${values}"
}

We can call this function with any number of parameters. The first parameter specifies how many of the subsequent parameters we will show:

$ show_top 3 10 20 30 40 50
Top 3 values: 10 20 30

We have used the 'range' operator on the $@ 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.

The table below shows some of the common variables you can use when working with function parameters:

VariableDescription
$1The first parameter
$2The second parameter
${11}The 11th parameter - if the parameter is more than one digit you must surround it with braces
$#The number of parameters
$@The full set of parameters as an array
$*The full set of parameters as a string separated by the first value in the $IFS variable
${@:start:count}A subset of 'count' parameters starting at parameter number 'start'

The $@ and @* parameters look quite similar. The first one is an array, just like we saw in 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 $IFS variable).

Parameter Shifting​

We can use the shift (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.

This is a little hard to describe so let's see how we can use it to simplify our show_top function:

# Show the top 'n' values of a set.
show_top() {
# Grab the number of values to show, then shift.
local n=$1
shift

# Get the set of values to show. Notice that we start in position 1 now.
local values=${@:1:n}
echo "Top ${n} values: ${values}"
}

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!

Return Values​

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:

is_even() {
local number=$1

# A number is even if when we divide it by 2 there is no remainder.
# Set 'result' to 1 if the parameter is even and 0 otherwise.
if [ $((number % 2)) -eq 0 ]; then
result=1
else
result=0
fi
}

A function could set any number of variables to provide output. Here's how we could use the is_even function:

$ number=33
$ is_even $number
$ echo "Result is: $result"
Result is: 0

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.

A more common way to return a value from a function is to write its result to stdout - let's look at this in detail.

Writing Results to Stdout​

If we write our result to stdout, then we can capture the result of a function in a far more readable way:

lowercase() {
local params="$@"
# Translate all uppercase characters to lowercase characters.
echo "$params" | tr '[:upper:]' '[:lower:]'
}

In this example we write the result of the function to stdout. This means that we can capture the result and put it in another variable by simply executing the command in a subshell:

$ result=$(lowercase "Don't SHOUT!")
$ echo "$result"
don't shout!

If you have a programming background it might seem very strange that you write results in a function by writing to 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 ls does, what find does, what cat does and so on. When we echo a result from a function, we are really just following the Unix standard of writing the results of a program to the screen.

This is important - if we run our function directly in a shell, we can see the result written to the screen:

$ lowercase "PLEASE don't SHOUT!"
please don't shout!

Remember - shell functions are designed to behave in a similar way to shell commands. They write their output to stdout.

Dealing with Output in Commands​

Although it might feel a bit clunky, writing the results of a command to 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!

# This function creates a temporary folder for today and returns its path.
temp_today() {
# Get today's date in the format YYYY-MM-DD.
local today=$(date +"%Y-%m-%d")

# Create a temporary directory for today and return it.
tmpdir_today="/tmp/${today}"
echo "Creating folder '${tmpdir_today}'..."
mkdir -p "${tmpdir_today}"
echo "${tmpdir_today}"
}

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:

# Go to today's temporary folder.
folder=$(temp_today)
cd "${folder}"

This script fails, with the output:

'Creating folder \'/tmp/2021-05-28\'...\n/tmp/2021-05-28': No such file or directory

What's going on here?

Well in the 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 all of the text we have written.

It is important to remember that any command you call in a function that might write to stdout could cause problems as it could write text to your output:

command_exists() {
if type "$1"; then
echo "1"
else
echo "0"
fi
}

What happens when we try and store the result of the function in a variable?

$ result=$(command_exists "touch")
$ echo "Result is: ${result}"
Result is: touch is hashed (/usr/bin/touch)
1

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 type is used to find out whether a command exists it returns success if the command exists but also writes to stdout.

In 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:

command_exists() {
if type "$1" >> /dev/null; then
echo "1"
else
echo "0"
fi
}

Now if we run this command we will not get the output from the type command in our result - the output was redirected to the null device.

Returning Status Codes​

The return (return from shell function) command causes a function to exit with a given status code.

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 status code of the function.

What is a status code? We actually touched on this in Chapter 20 - Mastering Conditional Logic. When a command runs, we expect it to return a status code of 'zero' to indicate success. Any non-zero status code is used to specify an error code.

Let's see how we could re-write the command_exists function to set a status code:

command_exists() {
if type "$1" >> /dev/null; then
return 0
else
return 1
fi
}

Now that our command sets a status code properly, we can use it in an 'if statement' like so:

if command_exists "common"; then
echo "The 'common' command is installed on your system"
else
echo "The 'common' command is not installed on your system"
fi

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 stdout or if you must, set the value of a global variable.

The result of the last executed command is always available in the special variable $?. Here's how you could use it:

$ type "test"
test is a shell builtin
$ echo "Result: $?"
Result: 0

Error Handling​

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.

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:

#!/usr/bin/env sh

# Get today's date in the format YYYY-MM-DD.
today=$(date +"%Y-%m-%d")

# Create the path to today's temp folder and then make sure the folder exists.
temp_path="/tmp/${today}"
mkdir -p "${temp_path}"

# Now that we've created the folder, make a symlink to it in our homedir.
ln -sf "${temp_path}" "${HOME}/today"

# Write out the path we created.
echo "${temp_path}"

Now we can run the script to create temporary folder for the current day and a link to it in our home directory:

$ chmod +x ./today.sh
$ ./today.sh
/tmp/2021-05-28
$ cd ~/today

In this example we created a new directory in the tmp folder and created a link to it in our home directory. But what happens if we cause one of the commands to fail?

First, let's clean up the folder we created:

$ rm -rf $(./today.sh)
$ rm ~/today

Now we'll create a file where we want to put our 'today' folder:

$ touch "/tmp/$(date +"%Y-%m-%d")"

If we run our script now, we can see a problem:

$ ./today.sh
mkdir: /tmp/2021-05-28: Not a directory
/tmp/2021-05-28
$ cd ~/today
bash: cd: /home/dwmkerr/today: Not a directory

The mkdir command failed - because there was a 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 today folder we get another error - it is a link to a file not a folder.

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.

You can use the set (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:

#!/usr/bin/env sh

# Exit if any command fails.
set -e

# ...

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'.

Now let's clean up again:

$ rm -rf $(./today.sh)
$ rm ~/today

And finally, we'll run the same script after creating the file that will cause a failure:

$ touch "/tmp/$(date +"%Y-%m-%d")"
$ ./today.sh
mkdir: /tmp/2021-05-28: Not a directory

In this case the script stopped running as soon as there was a failure - after the mkdir command failed.

One thing to be aware of is that the set -e option only affects the final command of a pipeline. This means that if you have a pipeline such as the below:

grep '[:space:]*#' ~/effective-shell/scripts/common.sh | tr 'a-z' 'A-Z'

Then the script will still run if the grep command fails. To ensure that the shell terminates if a command in a pipeline fails we must set the pipefail option:

set -o pipefail

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 might fail are properly handled.

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 if block to 'catch' the error and show a warning:

if ! [ rm ~/my-file.text ]; then
echo "warning: unable to delete file"
fi

Another option would be to use a conditional expression:

rm ~/my-file.txt || true

This expression always evaluates to 'true' so will not stop the script if an error occurs when running the rm command.

The Function Keyword​

In some scripts you might see functions defined using the function keyword, as below:

function title() {
echo "My Script version 1.0"
}

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 alias with the same name as the function you are declaring. However, the drawback is that it is less standard and therefore less portable.

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!

Parameters and Status Codes for Scripts​

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 $1, $2 and so on.

The only difference is that instead of using the return command when we want to exit a script with a status code, we use the exit (exit the shell) command. The exit command exits the current shell with the provided status code.

Be careful when using the exit command - if you are running a script then it is fine to use exit, it will simply close the subshell that the script is running in. But if you type exit in your shell that you are using interactively, it will close it.

Updating the 'common' Command​

In the previous chapter we created the common.v4.sh command, that shows common commands from the users shell history.

If you need a refresher on what is in the script, you can view it in your pager with:

less ~/effective-shell/scripts/common.v4.sh

The output of the command will look something like:

1: 280 gst
2: 144 vi
3: 84 gc
4: 72 ga .
5: 62 gl
6: 54 ls
7: 50 gpo
8: 48 gcm
9: 45 make dev
10: 44 gpr

Let's make a couple of changes.

First, let's make sure we exit the script if one of the commands fails:

# Exit if any command fails.
set -e

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:

# ...
command_count=${1:-10} # The number of common commands to show
# ...

Here we are using the $1 variable. But we are also using Shell Parameter Expansion as described in Chapter 19 - Variables, Reading Input, and Mathematics to provide a default value to use if the parameter is not set.

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 after the text and in brackets:

write_command_then_count() {
# Get the command and count, this will be text that looks like:
# '43 git commit'
# Then write the command and the count afterwards.
local line="$1"
local count=$(echo "${line}" | cut -d' ' -f1)
local command=$(echo "${line}" | cut -d' ' -f2-)
echo "${command} (${count})"
}

We can now re-write our loop to make it a little cleaner:

for command in $commands
do
echo "$counter: $(write_command_then_count "$command")"
counter=$((counter + 1))
done

The updated script is in the samples folder at ~/effective-shell/scripts/common.v5.sh, you can update your link to point to this version by running the ln command:

ln -s ~/effective-shell/scripts/common.v5.sh /usr/local/bin/common

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 after the command text itself:

$ common 5
common commands:
1: gst (139)
2: vi (74)
3: gc (42)
4: ga . (36)
5: gl (31)

Summary​

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.

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.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-4-shell-scripting/index.html b/pr-preview/pr-346/part-4-shell-scripting/index.html new file mode 100644 index 00000000..dffbfc18 --- /dev/null +++ b/pr-preview/pr-346/part-4-shell-scripting/index.html @@ -0,0 +1,26 @@ + + + + + +Part 4 - Shell Scripting | Effective Shell + + + + + + + + + + + + + + +
+

Part 4 - Shell Scripting

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.

Shell scripting is the process of writing re-usable scripts, which you can use to automate repetitive work, create new programs and manage your environment.

In this section we'll look at the fundamentals of how shell scripts work and how to write and structure shell scripts. We'll also look at some more advanced techniques to deal with more complex scenarios, tricks for debugging shell scripts and more.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-4-shell-scripting/loops-and-working-with-files-and-folders/index.html b/pr-preview/pr-346/part-4-shell-scripting/loops-and-working-with-files-and-folders/index.html new file mode 100644 index 00000000..5cac9acf --- /dev/null +++ b/pr-preview/pr-346/part-4-shell-scripting/loops-and-working-with-files-and-folders/index.html @@ -0,0 +1,26 @@ + + + + + +Loops and working with Files and Folders | Effective Shell + + + + + + + + + + + + + + +
+

Loops and working with Files and Folders

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.

The For Loop​

We can use the for loop to run a set of commands for each item in a list.

The for loop has the following structure:

for <name> in <words>
do
<conditional-command 1>
<conditional-command 2>
<conditional-command n>
done

The for loop executes a sequence of commands for every item in a list. In the documentation you will see that this list is called 'words'. There's a technical (and complex) reason for this that we'll discuss in the end of the chapter.

Let's see how the for loop works by showing a simple example. We will loop through every item in a folder and print its name to the screen:

for item in ~/effective-shell/*
do
echo "Found: $item"
done

As long as you have the effective-shell folder in your home directory, you will see output that looks like this:

Found: /home/dwmkerr/effective-shell/data
Found: /home/dwmkerr/effective-shell/docs
Found: /home/dwmkerr/effective-shell/logs
Found: /home/dwmkerr/effective-shell/pictures
Found: /home/dwmkerr/effective-shell/programs
Found: /home/dwmkerr/effective-shell/quotes
Found: /home/dwmkerr/effective-shell/scripts
Found: /home/dwmkerr/effective-shell/templates
Found: /home/dwmkerr/effective-shell/text
Found: /home/dwmkerr/effective-shell/websites

Notice how the shell is smart enough to expand the wildcard expression that we have included in the for loop. In just the same way we can use wildcards in commands such as ls or cp or mv, we can also use them in for loops - or in fact any statement1!

You will also see that when we specify the name of the variable to use in the loop (which in this example was item) we don't need to use a dollar symbol. Remember - when we are setting a variable, we don't use a dollar symbol, we only use the dollar symbol when we want to get the value of the variable.

The for loop is closed with the done keyword. Here we can also see an inconsistency with the shell syntax - for the if statement, the statement is closed with if backwards (fi). But the for loop is closed with done. The shell is an old platform and there are some oddities like that that you might not see in more modern programming languages.

For Loops - Arrays​

In Chapter 19 - Variables, Reading Input, and Mathematics we saw how to create arrays. We can easily loop through the items in an array with a for loop. Here's an example:

days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
for day in ${days[@]}
do
echo -n "$day, "
done
echo "happy days!"

If we run this script we'll see the following output:

Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, happy days!

It's important to remember that we want to go through every item in the array, so we have to use the ${days[@]} syntax. This is the syntax for 'all of the members of the array'.

The -n (don't output a trailing newline) flag of the echo command is used inside the for loop so that we don't write each day on its own line.

For Loops - Words​

The for loop documentation names the input to the loop as 'words'. We can see this by running help for:

$ help for
for: for NAME [in WORDS ... ] ; do COMMANDS; done
Execute commands for each member in a list.
...

The reason that the items are called 'words' is that the shell splits up the input into a set of words and loops though each - this can be a real surprise if you come from a programming background.

Let's see what this means with an example:

sentence="What can the harvest hope for, if not for the care of the Reaper Man?"
for word in $sentence
do
echo "$word"
done

The output of this will be:

What
can
the
harvest
hope
for,
if
not
for
the
care
of
the
reaper
man?

Z-Shell - if you are using Z-Shell then the sentence will not be split up into words. There is an appendix at the end of the chapter that describes the differences between Z-Shell and Bash-like shells (which tend to be closer to the Posix standard).

The for loop has split up the sentence variable into a set of words. This might seem illogical, as the shell is making quite a big assumption (that the operator wants their input split up), but we'll see with a few examples how this is often what is needed.

This is not how most programming languages would work, so why does the shell do this?

The reason is that the shell is a text based environment and the designers have taken this into account. Most of the time when we are running shell commands in a terminal we are running commands that simply output text. If we want to be able to use the output of these commands in constructs like loops, the shell has to decide how to split the output up.

For example, let's see how the ls command would write its output:

$ ls ~/effective-shell
data docs logs pictures programs quotes scripts templates text websites

The output of the ls program is plain text. It is not an array, it is just a set of files separated by spaces. What would we expect the shell to do if we ran the following command?

files=$(ls ~/effective-shell)
for file in $files
do
echo "Found: $file"
done

The output is:

Found: data
Found: docs
Found: logs
Found: pictures
Found: programs
Found: quotes
Found: scripts
Found: templates
Found: text
Found: websites

Here we see why the shell splits up words in a sentence. It is making a best effort with plain text - trying to split plain text up into sensible 'chunks'.

When we operate in a shell for day to day work we don't have to use the more specific syntax that would be used in a programming language - the shell has more of an emphasis on terseness of statements and the ability to quickly work with files. It is not designed as a general purpose programming tool, so it makes assumptions like this.

We go into detail in word splitting nearer the end of this chapter.

For Loops - Files with Wildcards​

One of the most common scenarios for using a for loop is to loop through a set of files or folders.

The most simple way to do this is to use a simple wildcard pattern in the for loop statement, like so:

for script in ~/effective-shell/scripts/*.sh
do
echo "Found script: $script"
done

We will see output that looks like this:

Found script: /home/dwmkerr/effective-shell/scripts/common.mac.sh
Found script: /home/dwmkerr/effective-shell/scripts/common.sh
Found script: /home/dwmkerr/effective-shell/scripts/common.v1.sh
Found script: /home/dwmkerr/effective-shell/scripts/common.v2.sh
Found script: /home/dwmkerr/effective-shell/scripts/common.v3.sh
Found script: /home/dwmkerr/effective-shell/scripts/show-info.sh

We have to be careful with scripts like this - there is a bug.

By default, if the shell doesn't find anything with a wildcard pattern it does not expand it. This is very confusing - so let's see an example.

Take a look at the sample below - what would you expect it to show?

for script in ~/bad-shell/scripts/*.sh
do
echo "Found: $script"
done

You might think the logical result is that nothing is printed - there is not a bad shell folder, so the pattern should not find any files. But instead, we see the following output:

Found: ~/bad-shell/scripts/*.sh

By default, if a shell 'glob' (a pattern that includes a wildcard) does not match any files, the shell simply leaves the pattern as-is.

There are two ways we can deal with this problem. The first way is to enable the 'nullglob' (return null for unmatched globs) option:

shopt -s nullglob
for script in ~/bad-shell/scripts/*.sh
do
echo "Found: $script"
done

The shopt (set and unset shell option) command is used to configure shell options. We will be looking at shell options in detail in Part 5. The 'nullglob' option changes the shell behaviour so that if a wildcard pattern does not match any results, it is set to null string2.

The second way we can deal with this problem is to just use a test command. I think that this is actually far more readable than the shopt solution. Here's how it would look:

for script in ~/bad-shell/scripts/*.sh
do
# If the file / folder doesn't exist, skip it.
if ! [ -e "$script" ]; then continue; fi
echo "Found: $script"
done

Here we use the -e (exists) operator in a test command to check whether the file exists. If it does not exist, we run the continue statement.

The continue statement 'skips' the current item in the loop and moves to the next one. We will see it a little more later on.

For Loops - Files with Find​

If the files that you are trying to loop through are too complex to match with a shell pattern, you can use the find command to search for files, then loop through the results.

If you are not familiar with the find command, check Chapter 11 - Finding Files.

Let's use the find command to run a loop that prints every symlink in the user's home directory. But before we run the loop we'll create a symlink with a space - this will cause some interesting output in our script:

# Create a symlink to 'effective-shell' that has a space in it...
ln -s ~/effective-shell ~/effective\ shell

# Find all symlinks and print each one.
links=$(find ~ -type l)
for link in $links
do
echo "Found Link: $link"
done

You will see a few different links shown when you run this script, depending on how your system is set up. But you will also certainly see the results below:

...
Found Link: /home/dwmkerr/effective-shell/effective
Found Link: shell
...

This is clearly a problem - the shell has taken the path that has a space - /home/dwmkerr/effective-shell/effective shell and performed word splitting and turned it into two separate items.

This is a persistent headache for anyone who needs to build shell scripts. There are a large number of ways to solve this problem, and none of them are particularly intuitive. I am going to demonstrate one common solution, which is not perfect but should cover most cases. I'll then suggest a better work-around.

The solution that we will use is to temporarily change the values that the shell uses to split text into words. We will set it to only split on newlines. The find command puts each file it finds on its own line. This means we will not split up files with spaces or other whitespace in the name:

# Save the current value of IFS - so we can restore it later. Split on newlines.
old_ifs=$IFS
IFS=$'\n'

# Find all symlinks and print each one.
links=$(find ~ -type l)
for link in $links
do
echo "Found Link: $link"
done

# Restore the original value of IFS.
IFS=$old_ifs

If you run this command now you will see the correct output:

...
Found Link: /home/dwmkerr/effective-shell/effective shell
...

This will cover you in most cases. However, this method is not ideal for a number of reasons:

  1. It is quite verbose - we have to store the current value of $IFS and then reset it later
  2. It is not quite foolproof - filenames on some systems can have a newline character and this script would fail for those files
  3. We have to use the complex looking 'ANSI C Quoting' syntax to set $IFS to a newline3
  4. If the reader doesn't know what $IFS is then the entire script will be difficult to follow

The $IFS variable can be complex to work with and discussed at the end of the chapter.

I believe that in this case it is probably best to not use a shell script. There is no solution that is particularly clean or simple. In this case I think you might be better off using a programming language. Check Chapter 30 - How to Avoid Scripting for more details on this.

For Loops - C Style Loops​

If you have used programming languages like C, C++, Python, Java and others, you may well be familiar with the 'C style loop' structure that is shown below:

for (( expression1 ; expression2 ; expression3 ))
do
<command 1>
<command 2>
<command n>
done

This loop structure uses three arithmetic expressions to run the loop. The first is in 'initialise' expression, this is typically used to setup the initial state of the loop. The second is the 'conditional' expression, this is used to check whether the loop is complete. The third is the 'iterate' expression, this is evaluated after the loop commands are completed.

Here's how we can use a C style for loop to iterate through five numbers:

for (( i = 1; i <= 5; i++ ))
do
echo "Loop ${i}"
done

The output of this script is:

Loop 1
Loop 2
Loop 3
Loop 4
Loop 5

For Loops - Looping over Sequences​

Another common way to use a for loop is with brace expansion. Brace expansion we have already seen a number of times so far - we can use it to generate a sequence of values. Here is how we might create three files using brace expansion:

touch {coffee,tea,milkshake}-menu.txt

This will create three files:

$ ls -1 *-menu.txt
coffee-menu.txt
milkshake-menu.txt
tea-menu.txt

Brace expansion can be used in for loops, and brace expansion can be used to create sequences. For example, the loop below could be used as a way to loop through the numbers from one to ten:

for i in {1..10}
do
echo "Loop ${i}"
done

Brace expansion can be used to loop through a sequence of values or a range of numbers. You can even specify the 'increment' used in a sequence. For example, this loop iterates through a sequence of numbers adding five each time:

for i in {0..25..5}
do
echo "Loop ${i}"
done

The output of this loop would be:

Loop 0
Loop 5
Loop 10
Loop 15
Loop 20
Loop 25

The While Loop​

The while loop is a loop that executes commands until a certain condition is met.

The while loop has the following structure:

while <test-commands>
do
<conditional-command 1>
<conditional-command 2>
<conditional-command n>
done

As long as the test commands return success, the loop will run the conditional commands. After the conditional commands have been run, the loop goes 'back to the start' and evaluates the test commands again.

Here's an example of how a while loop can be used to generate a list of random numbers:

# Create an empty array of random numbers.
random_numbers=()

# As long as the length of the array is less than five, continue to loop.
while [ ${#random_numbers[@]} -lt 5 ]
do
# Get a random number, ask the user if they want to add it to the array.
random_number=$RANDOM
read -p "Add $random_number to the list? (y/n): " choice

# If the user chose 'y' add the random number to the array.
if [ "$choice" = "y" ]; then random_numbers+=($random_number); fi
done

# Show the contents of the array.
echo "Random Numbers: ${random_numbers[@]}"

When you run this script, you can choose to add a number to the list by typing 'y' - once there are five items in the list the while loop condition fails and the loop ends:

Add 14718 to the list? (y/n): y
Add 2646 to the list? (y/n): n
Add 11898 to the list? (y/n): y
Add 31506 to the list? (y/n): y
Add 32436 to the list? (y/n): y
Add 6803 to the list? (y/n): n
Add 25811 to the list? (y/n): y
Random Numbers: 14718 11898 31506 32436 25811

The $RANDOM variable is a built-in variable in the shell that returns a random number.

You would typically use a while loop when you don't know how many iterations you will perform and you need to re-evaluate at each iteration whether you should continue to loop.

While Loops - Looping through the lines in a file​

You can use a while loop to iterate through each line in a file, without having to load the entire file into memory.

Here's an example of how to iterate through the lines of a file:

while read line; do
echo "Read: $line"
done < ~/effective-shell/data/top100.csv

The output will look like this:

Read: "Rank","Rating","Title","Reviews"
Read: "1","97","Black Panther (2018)","515"
Read: "2","94","Avengers: Endgame (2019)","531"
...

This uses shell redirection to redirect the contents of the ~/effective-shell/data/top100.csv file into the read command in the while loop. The read command will read the file, line by line, until it finds the final line.

This script has some issues:

  • If the last line is does not end with a newline, then it is not read
  • Backlashes will be treated as escape sequences and lead to broken output
  • Leading whitespace will be removed

It is possible to avoid these issues, but the resulting script is a lot harder to read:

while IFS="" read -r line || [ -n "$line" ]; do
echo "Read: $line"
done < ~/effective-shell/data/top100.csv

In this case we've had to use some complex tricks to avoid each issue:

  • The || [ -n "$line"] test ensures that the loop iterates as long as the line read is not zero-length, ensuring we read the last line even if it doesn't have a newline
  • The -r (do not escape) option for read ensures that backlashes are not interpreted as escape sequences
  • The IFS="" temporarily disables any word splitting in the loop, meaning that we do not lose leading whitespace

However this still has issues - if commands in the loop read from standard input then the loop will still have errors. For this reason, I would again suggest you follow the advice in the How to avoid scripting! Chapter to see better ways to read files!

Even though I would recommend using a programming language to read the lines of a file, I have kept this example here because it is something you are likely to come across if you see scripts written by others. And for simple scenarios, where you are fairly sure of structure of a file, it might be useful. But this is definitely a case where you should consider using a programming language if you want to create more maintainable solutions to problems!

While Loops - The Infinite Loop​

There are times that you may want to loop forever. For example you might be writing a script that reads an option from the user, processes it, and then starts again.

Here's an example of an infinite loop - we use the true command, which always returns success:

while true
do
echo "1) Move forwards"
echo "2) Move backwards"
echo "3) Turn Left"
echo "4) Turn Right"
echo "5) Explore"
echo "0) Quit"

read -p "What will you do: " choice
if [ $choice -eq 0 ]; then
exit
fi
# The rest of the game logic would go here!
# ...
done

This example shows a common pattern for an infinite loop - offering a menu of options which the user can call repeatedly until they decide to quit.

The Until Loop​

The until loop operates just like the while loop, except that it runs until the test commands return success.

The structure of the until loop is just like the while loop:

until <test-commands>
do
<conditional-command 1>
<conditional-command 2>
<conditional-command n>
done

As long as the test commands do not return success, the loop will run the conditional commands. After the conditional commands have been run, the loop goes 'back to the start' and evaluates the test commands again.

Here's an example of an until loop that builds a random number that is at least 15 characters long:

# Create an empty random number string - we're going to build it up in the loop.
random_number=""

# Keep on looping until the random number is at least 15 characters long.
until [ "${#random_number}" -ge 15 ]
do
random_number+=$RANDOM
done
echo "Random Number: ${random_number}"

When you run this script you will see something like this:

Random Number: 364272371462227929

Note that we've used the string-length parameter expansion function to get the length of the random_number_ variable here. If this is not familiar, check Chapter 19 - Variables, Reading Input, and Mathematics.

In general I would recommend using while loops rather than until loops. While loops are going to be more familiar to readers as they exist in many programming languages - until loops are a little more rare. And you can easily turn any until loop into a while loop by simply inverting the test commands you are running.

For example, we could re-write the loop created before like so:

random_number=""
while [ "${#random_number}" -lt 15 ]
do
random_number+=$RANDOM
done
echo "Random Number: ${random_number}"

In this case we've changed the condition from -ge 15 (greater than or equal to fifteen) to -lt 15 (less than fifteen). The while loop version of the script will probably be a little easier for most readers to parse.

Continue and Break​

We briefly saw that the continue (resume loop) statement can be used to 'skip' an iteration in a loop. break (exit loop) statement that can be used to stop running the loop.

When we use the continue statement, we are telling the shell that we want to stop processing the current 'iteration' of the loop and move onto the next item. You can use as many continue statements as you like in a loop.

Here's an example of a script that let's the users show the contents of a directory. If the directory is empty it uses the continue statement to skip to the next directory. If the users chooses to cancel the operation, it uses the break statement to stop iterating:

echo "For each folder, choose y/n to show contents, or c to cancel."
for file in ~/*
do
# If the file is not a directory, or it cannot be searched, skip it.
if ! [ -d "$file" ] || ! [ -x "$file" ]; then continue; fi

# Ask the user if they want to see the contents.
read -p "Show: $file? [y/n/c]: " choice

# If the user chose 'c' for cancel, break.
if [ "$choice" = "c" ]; then break; fi

# If the user choice 'y' to show contents, list them.
if [ "$choice" = "y" ]; then ls "$file"; fi
done

Using break and continue can simplify our loops - otherwise it would be much harder to write the loop above.

Creating Compact Loops​

In each example in this chapter we have split the loop constructs so that there is one statement per line. But just as with the if statement, we can combine any of these lines, as long as we use a semi-colon to let the shell know where each statement ends.

A common pattern you will see is the do keyword on the same line as the for or while statement:

numbers=(0 1 1 2 3 5)
for num in ${numbers[@]}; do
echo "$num"
done

If you are simply typing in the shell in a terminal, rather than writing a script, you might write the loop on a single line:

for script in *.sh; do touch "$script"; done

This one-liner updates the last access and modified of all files that end with *.sh in the current folder.

Just like with the if statement I would recommend that you keep each statement on its own line until you are 100% familiar with the syntax. Then when it is second-nature to be able to write a loop, you can use the more compact syntax if it is appropriate.

When you are running the shell interactively, i.e. actually typing in the shell rather than writing a shell script, you can still use multiple lines. If you type for script in *.sh and press enter, the shell will let you type the next line. You can keep on adding lines until you type done and press enter.

If you want to make a really compact for loop, you can actually skip the in <words> part. If in <words> is omitted then the special 'all parameters' variable $@ is used. We will look at this special parameter in the next chapter. But this will be confusing to readers so I would recommend that you are always explicit with the in <words part of a for loop.

Word Splitting and IFS​

At a number of points in this chapter we have touched on the concept of 'word-splitting' and the $IFS variable. Before we close out the chapter with an update to the common script, let's talk about these concepts in more detail.

If you are not expecting to use shell scripts as a regular part of your work you can safely skip this section. If you think that you are likely to come across shell scripts, loops and similar constructs, it might be worth reading this section.

Word Splitting​

Word splitting is the process by which the shell splits text up into a set of words.

We saw that the shell will split the words in a loop, which we can see with the example below:

$ sentence="Here are some words"
for word in $sentence; do echo "$word"; done
Here
are
some
words

But why is it that wrapping the $sentence variable in quotes stops the word splitting from happening?

$ sentence="Here are some words"
for word in "$sentence"; do echo "$word"; done
Here are some words

The reason for this has been touched on in Chapter 19 - Variables, Reading Input, and Mathematics and also partly in this chapter.

In the first example the loop iterates over the $sentence variable. Note that this variable is not quoted. This means that it follows the standard rules for 'expansion' in the shell. This means that as well as all of the usual features such as wildcard expansion, word expansion will occur.

In the second example, the loop iterates over the "$sentence" variable. Note that this variable is quoted. As we saw in Chapter 19 quoting a variable means that it is treated literally, expect for parameter expansion.

This means that in most circumstances you probably want to quote your variables - otherwise the shell is going to perform word splitting on them. But if you do want expansion and splitting to occur, then you should not quote text. For example, if we run the following we see invalid output:

$ for file in "*"; do echo "Found: $file"; done
Found: *

Because we have quoted the asterisks, the shell does not treat it as a special character which expands into a list of files.

What about this example, when we use wildcard expansion to list files, but the results do not have word splitting applied?

$ touch file\ with\ spaces.test
$ for file in *.test; do echo "Found: $file"; done
Found: file with spaces.test

The *.test is not surrounded in quotes, so it is expanded. But why does word splitting not happen?

The reason is that the shell applies these 'expansions' in a certain order, which is as follows:

  • Brace expansion
  • Tilde expansion
  • Parameter and variable expansion
  • Command substitution
  • Arithmetic expansion
  • Word splitting
  • Pathname expansion

Word splitting happens before pathname expansion, and it is pathname expansion that turns the asterisks wildcard into the list of files. At the point that this happens, word splitting has already been applied and won't be applied again.

Each of these types of expansion we have actually already seen in the book, but we're going to review them in detail in the final section on advanced techniques. You can find the appropriate section of the Bash manual for this topic by searching for EXPANSION.

The IFS Variable​

The $IFS variable is the 'internal field separator' variable. It is what the shell uses to decide what characters should be used to split up text into words. By default, this variable includes the space character, the tab character and the newline character.

Whenever you see a script or a command that changes the value of the $IFS variable, the operator is modifying the behaviour of subsequent commands so that they do not split words in the same way.

Here's an example of how we could change the IFS variable to split text using commas:

text="mother,danzig,1988"
IFS=","
for word in $text
do
echo "Word: $word"
done

This script will split text using the comma symbol and output:

Word: mother
Word: danzig
Word: 1988

Be careful when changing the IFS variable - it could cause subsequent commands to behave in unexpected ways. You should normally first copy current value into a variable, then change it, then set it back, like so:

old_ifs="$IFS"
IFS=":"
# Do some stuff
IFS="$old_ifs"

In general if you are changing IFS you might be doing something that would be better done with a programming language.

Updating the 'common' Command​

In the previous chapter we created the common.v3.sh command, that shows common commands from the users shell history.

If you need a refresher on what is in the script, you can view it in your pager with:

less ~/effective-shell/scripts/common.v3.sh

Let's add a loop to our common to show a number next to each command so that we can see the order of the commands.

As the file is a little larger now, I am not going to show the entire file, only the key changes we will make.

First, in each of the sections that performs the command to get the common commands we will use Command Substitution to run a sub-shell and store the results in a variable:

# Store the most recently used commands in the 'commands' variable.
commands=$(tail ~/.bash_history -n ${history_lines} \
| sort \
| uniq -c \
| sed 's/^ *//' \
| sort -n -r \
| head -n ${command_count})

There are two places we have to make this change - the first is for the Bash Shell and the second is for the Z-Shell. Now that we have stored our commands in a variable, we can loop through it at the end of the script and show a number that gives the order of each command:

# Print each command, showing what its order is in the list.
# Commands are separated by newlines, so temporarily change IFS to loop over
# each line of the commands.
counter=1
old_ifs=$IFS
IFS=$'\n'
for command in $commands
do
echo "$counter: $comand"
counter=$((counter + 1))
done
IFS=$old_ifs

The updated script is in the samples folder at ~/effective-shell/scripts/common.v4.sh, you can update your link to point to this version by running the ln command:

ln -s ~/effective-shell/scripts/common.v4.sh /usr/local/bin/common

Now when we run this command, each of our common commands is printed with its order shown:

$ common
1: 135 gst
2: 73 vi
3: 47 gc
4: 40 ls
5: 37 ga .
6: 27 gpo
7: 25 gl
8: 24 gpr
9: 21 gcm
10: 17 make dev

Summary​

In this chapter we looked at how to use different types of loops in the shell, to iterate over values in an array, words in a sentence, files and folders or even the results of commands.

We also looked in detail at how 'word-splitting' works, as well as the $IFS variables. In the next chapter we'll look at functions and parameters.

Appendix - Loops and the Z-Shell​

The Z-Shell does not perform word-splitting on unquoted variables. This is a deliberate choice by the designers, to avoid what can often be confusing behaviour.

We can see this behaviour below:

% sentence="one two three"
% for word in $sentence; do echo "Word: $word"; done
Word: one two three

If you want to use more Posix-like functionality then you can set the SH_WORD_SPLIT parameter. You can find out more about this parameter by running man zsh and searching for SH_WORD_SPLIT.


  1. If we had put quotes around the wildcard text it would not be expanded - check the section on 'Quoting' in Chapter 19 - Variables, Reading Input, and Mathematics if you need a refresher on this.↩
  2. There is a good reason for this. Would you prefer ls *.nothing-here to show a warning that *.nothing-here doesn't exist or show the result of ls - which lists the current directory! This is discussed in more detail on this Stack Overflow thread: https://unix.stackexchange.com/questions/204803/why-is-nullglob-not-default↩
  3. ANSI C Quoting is described in the 'Quoting' section in Chapter 19 - Variables, Reading Input, and Mathematics↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-4-shell-scripting/mastering-conditional-logic/index.html b/pr-preview/pr-346/part-4-shell-scripting/mastering-conditional-logic/index.html new file mode 100644 index 00000000..4d6db7ac --- /dev/null +++ b/pr-preview/pr-346/part-4-shell-scripting/mastering-conditional-logic/index.html @@ -0,0 +1,26 @@ + + + + + +Mastering Conditional Logic | Effective Shell + + + + + + + + + + + + + + +
+

Mastering Conditional Logic

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.

Let's get right into it!

The If Statement​

We can use the if statement to perform operations in shell scripts only when certain conditions are met.

The if statement has the following structure:

if <test-commands>
then
<conditional-command 1>
<conditional-command 2>
<conditional-command n>
fi

The if statement will run the 'test commands'. If the result of the commands are all zero (which means 'success'), then each of the 'conditional' commands will be run. We 'close' the if statement with the fi keyword, which is if written backwards.

Let's see how the if statement is used with a simple example. We will try and create a folder using mkdir. The mkdir command will return zero if the folder is created successfully:

if mkdir ~/backups
then
echo "Successfully created the 'backups' folder"
fi

If you don't have a folder called backups in your home directory then the command will run successfully. The mkdir command will return zero and the conditional statements will be run and you will see the output below:

Successfully created the 'backups' folder

If you then run the script again, the mkdir command will fail. In this case it does not return zero and the conditional commands are not executed. We will see an error message from the mkdir command:

mkdir: /home/dwmkerr/backups: File exists

This is the basics of how the if statement works. We provide test commands, if the test commands succeed, a set of conditional commands are then executed.

You might be surprised to hear that the result of the test commands has to be zero for the conditional commands to run. This is the opposite of how most programming languages work - normally zero would be considered 'false'.

The reason for this - is that for computer programs that run, 'zero' generally means success. Any non-zero value is typically used to indicate an error code. So whilst inside a programming language, an if statement will check for a value to be 'true', just remember that in the shell an if statement will check for a command to be successful.

The Test Command​

The test (evaluate expression) command is used to check whether a certain condition is true or not. If the condition is true then the test command returns zero to indicate success.

We could improve our earlier if statement example by only creating the 'backups' folder if it doesn't already exist, using the test command:

if ! test -d ~/backups
then
echo "Creating backups folder"
mkdir ~/backups
fi

The test command evaluates an expression. In this case the expression is:

-d ~/backups

This expression uses the -d (file exists and is a directory) operator to check if the provided path is a directory. We want to create the directory only if it doesn't exist, so we use the 'not' operator to 'invert' the result of test. The 'not' operator is written with the ! exclamation point symbol.

You can surround an expression with square bracket and the shell will evaluate the expression with the test command. This can make your scripts far more compact:

if ! [ -d ~/backups ]
then
echo "Creating backups folder"
mkdir ~/backups
fi

This square bracket syntax is very commonly used - but just remember it is shorthand for the test command.

One of the most useful manual pages is the page for the test command as it shows all of the available operators. Open the page with man test.

Using Multiple Statements in a Single Line​

You will often see 'if' and 'then' statements on the same line as below:

if ! [ -d ~/backups ]; then
mkdir ~/backups
fi

The shell assumes that each individual line is a single statement. If you want to put more than one statement on a line then you need to let the shell know when one statement ends and another starts. We can use a semi-colon for this. The shell uses the semi-colon as a 'command separator' symbol.

If you don't include a semi-colon at the end of a command then the shell assumes that the entire line is a single statement. If you try and run the script without the semi-colon you will get an error:

bash: syntax error near unexpected token `fi'

I would suggest you start by writing your if statements with the if and the then on separate lines. Once you are more familiar with the syntax, you can start to combine the lines if you prefer.

You can put as many statements on a single line as you like - you could even write the script like so:

if ! test -d ~/backups; then mkdir ~/backups; fi

The then doesn't require a semi-colon as it is a keyword rather than a command. I think that in general keeping things on separate lines will be a bit more readable for other users, but sometimes you may prefer a more compact form.

The Else Statement​

You can use the else statement to define a series of statements that should be executed if the condition in the if statement is not true.

Here's how we can write a script that informs the user of whether they have installed the 'common' command or not:

if [ -e /usr/local/bin/common ]
then
echo "The 'common' command has been installed in the local bin folder."
else
echo "The 'common' command has not been installed in the local bin folder."
fi

In this case we used the -e (file or folder exists) operator to check whether a file or folder exists in the location /usr/local/bin/common. The 'common' command is the command we created in Chapter 18 - Shell Script Essentials.

Now if you run the script and you don't have the 'common' command installed you will see the following output:

The 'common' command has not been installed in the local bin folder.

Note that we still need to use the 'fi' keyword to close the 'if' statement.

The Elif Statement​

The elif statement (which is short for 'else if') can be used to create additional checks and define statements that should run if other conditions are true.

Let's see this in action by updating our script to check whether the 'common' command is executable, using the -x (is executable) operator:

if [ -x /usr/local/bin/common ]; then
echo "The 'common' command has been installed and is executable."
elif [ -e /usr/local/bin/common ]; then
echo "The 'common' command has been installed and is not executable."
else
echo "The 'common' command has not been installed."
fi

The message you see will depend on whether you have installed the 'common' command in your local binaries folder and whether the script is executable. If you want to see each of the different messages, you might find the following snippets useful to add or remove the command or change its executable permissions:

  • ln -s $HOME/effective-shell/scripts/common.v1.sh /usr/local/bin/common - Create a link to the 'common' command in the local binaries folder
  • chmod -x $HOME/effective-shell/scripts/common.v1.sh remove the 'executable' flag from the 'common' command, making it not executable
  • chmod +x $HOME/effective-shell/scripts/common.v1.sh add the 'executable' flag from the 'common' command, making it executable
  • rm /usr/local/bin/common remove the link to the 'common' command from the local binaries folder

The elif statement looks very similar to the if statement. The statement takes a set of commands. These commands could be normal shell commands, test commands, or test commands written with the square brackets short-hand notation.

It is very important to think about the order in which the if and elif statement are executed. If we had written the script like this, it would not work:

if [ -e /usr/local/bin/common ]; then
echo "The 'common' command has been installed and is executable."
elif [ -x /usr/local/bin/common ]; then
echo "The 'common' command has been installed and is not executable."
else
echo "The 'common' command has not been installed."
fi

In this script we check to see if the file exists first. If the file exists then the condition -e operator will return true, and we will not run the check in the elif statement. This means we'll never successfully evaluate the statements in the elif block (because for the file to be executable it must exist, so the first condition in the if statement will always take precedence. So it is important to think about the order of the statements!

Common Test Operators​

There are many operators that can be used in a test expression. You can find the full list by running man test.

Here are the most common operators you should know about!

OperatorUsage
-nTrue if the length of a string is non-zero.
-zTrue if the length of a string is zero.
varTrue if the variable var is set and is not empty.
s1 = s2True if the strings s1 and s2 are identical.
s1 != s2True if the strings s1 and s2 are not identical.
n1 -eq n2True if the numbers n1 and n2 are equal.
n1 -ne n2True if the numbers n1 and n2 are not equal.
n1 -lt n2True if the number n1 is less than n2.
n1 -le n2True if the number n1 is less than or equal to n2.
n1 -gt n2True if the number n1 is greater than n2.
n1 -ge n2True if the number n1 is greater than or equal to n2.

Common Test Operators for Files​

One of the great things about the test command is the presence of a number of operators that are specifically used to work with the filesystem. These operators are very handy when you are building shell scripts!

Here are some of the most useful ones:

OperatorUsage
-dTrue if the file exists and is a folder.
-eTrue if the file exists, regardless of the file type.
-fTrue if the file exists and is a regular file.
-LTrue if the file exists and is a symbolic link.
-rTrue if the file exists and is readable.
-sTrue if the file exists and has a size greater than zero.
-wTrue if the file exists and is writable.
-xTrue if the file exists and is executable - if it is a directory this checks if it can be searched.
file1 -nt file2True if file1 exists and is newer than file2.
file1 -ot file2True if file1 exists and is older than file2.
file1 -ef file2True if file1 and file2 exist and are the same file.

There are plenty of other operators that you can use when working with files, you can see them all by running man test.

Combining Tests​

Often you will want to check multiple conditions. You can use the && 'and' operator and the || 'or' operator to check for multiple conditions:

if [ $year -ge 1980 ] && [ $year -lt 1990 ]; then
echo "$year is in the 1980s"
fi

This script checks to see whether the variable 'year' is greater than or equal to 1980 and less than 1990.

You can use 'and' or 'or' in a single test statement by using the special -a (and) and -o (or) operators. This is how the script would look using the -a operator:

if [ $year -ge 1980 -a $year -lt 1990 ]; then
echo "$year is in the 1980s"
fi

These operators can lead to some subtle problems so I would not recommend that you use them. A better option is 'Conditional Expressions' which are described in the next section. However, it is important to be able to recognise these operators so that they don't surprise you if you see them in someone else's script.

Conditional Expressions​

'Conditional Expressions' are a feature of Bash, and bash-like shells, that offer a more sophisticated option to perform conditional checks. Conditional expressions use two square brackets rather than one:

if [[ $year -ge 1980 && $year -lt 1990 ]]; then
echo "$year is in the 1980s"
fi

Conditional expressions have a number of benefits over plain test commands. Some of the most important ones are:

  • You can use the && and || operators directly in the expression
  • If you use an || expression and the left hand side of the expression is true, the right hand side will not be evaluated - this is not always the case with older versions of Bash when using the -o operator (this is a subtle difference but can help avoid potentially incorrect behaviour)
  • Numbers are correctly compared even if they are in different formats (for example, you can compare hexadecimal and octal numbers, this does not work in the standard test expression)
  • You can use the incredibly useful =~ operator to use a regular expression in your condition (we'll look at this next)

You can find more details on conditional expressions by using man bash and searching for \[\[ (this is the double square brackets with each one escaped with a backslash).

Some people prefer to use single brackets so that their script is more portable, as the double brackets are specific to Bash and Bash-like shells. Others prefer to use the double brackets so that they can use the additional features offered.

Whether you use single or double brackets will partly be down to preference and whether it is more important in your use case to have portability or whether it is more important to have the more 'correct' behaviour.

Using Regexes in a Conditional Expression​

When you use the double square brackets conditional expression syntax you can use the =~ operator to test for a regular expression. This can be extremely useful. If you need a reminder on how regular expressions work check Chapter 13 - Regex Essentials.

In the example below we check to see if the user's shell is 'zsh' by seeing whether the path of the shell ends with the text zsh:

zsh_regex="zsh$"
if [[ $SHELL =~ $zsh_regex ]]; then
echo "It looks like your shell '$SHELL' is Z-Shell"
fi

If you are running Z-Shell you will see the output below:

It looks like your shell '/bin/zsh' is Z-Shell

It is best to declare the regular expression in a variable rather than including it directly in the expression, this makes it easier to handle special characters such as the dollar symbol.

You can use capture groups in your regular expression to help you extract text. For example, we could get the name of the current shell binary with the code below:

shell_regex="([^/]+)$"
if [[ $SHELL =~ $shell_regex ]]; then
echo "Your shell binary is: ${BASH_REMATCH[1]}"
else
echo "Unable to extract your shell binary"
fi

On my machine this script shows the following output:

Your shell binary is: bash

The $BASH_REMATCH variable is an array - the first result value in the array is the entire match, each subsequent value in the array is the result of each capture group in the expression. Double check Chapter 19 - Variables, Reading Input, and Mathematics if you need a reminder on how arrays work in Bash.

Chaining Commands​

You can 'chain' commands together in the shell, this allows you to run a command based on the result of a previous command.

Let's take a look at how this would work:

mkdir -p ~/backups && cd ~/backups

In this case we have chained two commands together using the && operator. The shell will only run the second command if the first command succeeds. It evaluates the result of the first command - if it is successful, then it evaluates the second command. It does this because we are trying to evaluate the combination of both commands. Or, if we were to write this in pseudo-code:

does (command1 and command2) succeed?

If command fails, the shell doesn't need to evaluate the second command - the overall result must be false, as the first command has already failed.

Contrast this to the || operator:

[ -d ~/backups ] || mkdir ~/backups

In this case we evaluate the second command only if the first command fails. Let's look at the pseudo code:

does (command1 or command2) succeed?

If the first command succeeds, the shell doesn't need to evaluate the second command. However, if the first command fails, the shell does have to evaluate the second command, to see if either of them succeed.

In summary, here's how command chaining works:

# Run command1, if it succeeds run command2.
command1 && command2

# Run command1, if it does not succeed run command2.
command1 || command2

You will see this syntax a lot in shell scrips as it is very succinct. It can also be very useful when using the shell interactively. For example, it is almost second nature for me to write the following commands:

make build && make deploy

Here I am using the make (build programs) command. If the 'build' step for a project succeeds, I want to run the 'deploy' step. But I don't want to run the 'deploy' step if the 'build' step fails!

Case Statements​

If you find yourself writing overly complex 'if statements', you might use a case statement to simplify your code.

A case statement is a bit like an 'if statement'. The structure is as follows:

case <expression> in
pattern1)
<pattern1-commands>
;;
pattern2 | pattern3)
<pattern2and3-commands>
;;
*)
<default-commands>
;;
esac

Typically you will provide the 'case' statement a variable and use it to check against a number of values. Here's a common example you'll see - checking to see whether a response is 'yes' or 'no':

read -p "Yes or no: " response
case "${response}" in
y | Y | yes | ok)
echo "You have confirmed"
;;
n | N | no)
echo "You have denied"
;;
*)
echo "'${response}' is not a valid response"
;;
esac

The example above shows very simple text patterns, but any text pattern can be used:

read -p "Yes or no: " response
case "${response}" in
[yY]*)
echo "You have (probably) confirmed"
;;
[nN]*)
echo "You have (probably) denied"
;;
*)
echo "'${response}' is not a valid response"
;;
esac

In this example the first pattern is [yY]* which means either the 'y' or 'Y' character followed by zero or more characters, this will match things like 'yes' 'YES' or 'yay'. We have a similar pattern for the negative response.

The case statement can look quite complex, I often think that even if it takes more lines to write the logic using 'if statements' it will be more readable, but this is common pattern nonetheless and good to know about!

Updating the 'Common' Command​

Now that we know how to use if statements, we can update the 'common' command that we have been improving as part of each chapter.

We will update it to check whether the user is using Bash or Z-Shell and search through the history for common commands appropriately.

As a reference, let's look at the common.v2.sh command we created in the previous chapter:

# Write the title of our command.
echo "common commands:"

# The following variables control how the command runs.
history_lines=1000 # The number of lines of history to search through
command_count=10 # The number of common commands to show

# Show the most commonly used commands.
tail ~/.bash_history -n ${history_lines} \
| sort \
| uniq -c \
| sed 's/^ *//' \
| sort -n \
| tail -n ${command_count}

We'll create a new version of this script called common.v3.sh that checks the user's shell to work out what file to use to find the history of commands:

# The following variables control how the command runs.
shell_binary="" # We will work out what shell we are in later.
history_file="" # We will work out the history file later.
history_lines=1000 # The number of lines of history to search through
command_count=10 # The number of common commands to show

# Check if we can work out the name of the shell binary.
shell_regex="([^/]+$)"
if [[ $SHELL =~ $shell_regex ]]; then
# Depending on the name of the shell binary, set the history file path.
shell_binary=${BASH_REMATCH[1]}
if [[ $shell_binary == "bash" ]]; then
history_file=~/.bash_history
elif [[ $shell_binary == "zsh" ]]; then
history_file=~/.zsh_history
fi
fi

# If we are searching through the bash history, we can look at the history file
# to get the most common commands.
if [[ $shell_binary == "bash" ]]; then
# Show the most commonly used commands.
tail "${history_file}" -n ${history_lines} \
| sort \
| uniq -c \
| sed 's/^ *//' \
| sort -n -r \
| head -n ${command_count}
elif [[ $shell_binary == "zsh" ]]; then
# Z-Shell history lines look like this:
# : 1621135004:0;uname -a
# So we run the same command as above, but use the 'rev | cut | rev' trick
# to extract everything _after_ the semi-colon, which is the command text.
tail "${history_file}" -n ${history_lines} \
| rev \
| cut -d';' -f1 \
| rev \
| sort \
| uniq -c \
| sed 's/^ *//' \
| sort -n -r \
| head -n ${command_count}
else
# Show a warning to the user that we don't know where the history file is
# for their shell.
echo "Sorry, I don't know where to find the history for '${SHELL}'"
fi

In this script we now first check if we can extract the name of the shell binary from the shell path. If we can, we store the name of the shell binary and its associated history in a pair of variables.

Then when we come to actually search through the history, we check the shell binary. If it is bash, we run the same command as before. If it is zsh we run a similar command, but account for the fact that the Z-Shell history file has some extra content which needs to be removed.

Note that as well as showing how to use more variables and if statements, as well as nested if statements (when one if statement is inside another) we can also see that we have very descriptive comments. Each comment is giving clear information on what we are trying to accomplish, which should make the script easier to maintain.

If you want to replace the installed common command with this new one, update the symlink in your /usr/local/bin folder:

ln -sf $HOME/effective-shell/scripts/common.v3.sh /usr/local/bin/common

Note that in this command we use the -f flag to force the creation of the symlink even if one already exists in the given location.

Summary​

In this chapter we looked at the If statement - an extremely important statement that allows us to perform conditional logic. In the next chapter we will look at another crucial logical feature of the shell - loops.

You can find most of the documentation for conditional logic in the manual, just run man bash and search for GRAMMAR.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-4-shell-scripting/useful-patterns-for-shell-scripts/index.html b/pr-preview/pr-346/part-4-shell-scripting/useful-patterns-for-shell-scripts/index.html new file mode 100644 index 00000000..46246f95 --- /dev/null +++ b/pr-preview/pr-346/part-4-shell-scripting/useful-patterns-for-shell-scripts/index.html @@ -0,0 +1,26 @@ + + + + + +Useful Patterns for Shell Scripts | Effective Shell + + + + + + + + + + + + + + +
+

Useful Patterns for Shell Scripts

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.

Remember that although this chapter focuses on patterns that are useful in scripts, you can apply these patterns in any shell session. This means you might find this chapter useful even if you are not expecting to write scripts, just as a way to understand some more advanced shell techniques.

Ensuring Exit on Failure​

By default, shell scripts will continue to execute if a command fails. This behaviour makes sense when running an interactive shell - we don't want the shell to close if a command fails. For a shell script however, continuing after an error has occurred is most likely going to lead to unexpected behaviour.

There are two options that you will often see set at the top of a script:

set -e
set -o pipefail

The first option ensures that the shell script will abort if a command fails.

The second option ensures that if a command that is part of a pipeline fails, then the shell script fails. Even when the set -e option is set, only the final command in a pipeline will cause the script to exit if it fails.

Here's an example showing why these options are useful:

# Create the effective shell folder.
mkdir -p ~/effective-shell

# Download and untar the effective shell samples.
samples_uri='https://https://effective-shell.com/downloads/effective-shell-samples.tar.gz'
$ sudo wget -c "${samples_uri}" -O - | tar -xz -C ~/effective-shell

If we don't include the set -e option, then if the mkdir -p command fails, the script will continue to run. We will then attempt to download and untar a file into a folder that does not exist. Why would mkdir -p fail? Although mkdir -p succeeds even if the folder exists, it will still fail if there is a file in the location specified, or there are not permissions to create the folder and so on. So even commands that you assume should run successfully you have to be careful with.

In the second part of this snippet, we use the wget (web get) command to download the samples and pipe the results to tar to extract them. If we have only set set -e, then if wget fails (for example if the address is wrong or we are offline) then the shell will not abort the script and continue trying to run the subsequent commands, which are not going to work as expected.

If you have a command that you expect may fail, but want to continue execution even if it does fail, then use the || operator:

# Remove the shell configuration.
rm "$HOME/.shell.sh" || true

In this case I have used a conditional operator, as described in Chapter 20 - Mastering Conditional Logic, to ensure that even if the rm command fails for some reason, the overall result of the statement will be true and the script will not exit.

Check Chapter Chapter 22 - Functions, Parameters and Error Handling if you need a refresher on the set -e or set -o pipefail commands.

Debugging Shell Scripts​

You can use the set (set option) command to set the trace option. This option is incredibly useful for debugging shell scripts. When the trace option is set, the shell will write out each statement before it is evaluated.

Let's see just how useful this is with an example!

# today.sh - creates a 'today' symlink in the home directory folder to a fresh
# temporary folder each day.

# Enable tracing in the script.
set -x

# Get today's date in the format YYYY-MM-DD.
today=$(date +"%Y-%m-%d")

# Create the path to today's temp folder and then make sure the folder exists.
temp_path="/tmp/${today}"
mkdir -p "${temp_path}"

# Now that we've created the folder, make a symlink to it in our homedir.
ln -sf "${temp_path}" "${HOME}/today"

# Disable tracing now that we are done with the work.
set +x

# Write out the path we created.
echo "${temp_path}"

Notice that we use set -x to enable tracing early on in the script, and set +x to disable tracing towards the end. If we run this script, we'll see the following output:

$ ~/effective-shell/scripts/today.sh
++ date +%Y-%m-%d
+ today=2021-05-29
+ temp_path=/tmp/2021-05-29
+ mkdir -p /tmp/2021-05-29
+ ln -sf /tmp/2021-05-29 /home/dwmkerr/today
+ set +x
/tmp/2021-05-29

Each command that the shell executes is written to stdout before it is executed. The parameters are expanded, which can make it far easier to see what is going on and troubleshoot issues.

The + symbol is written at the start of each trace line, so that you can differentiate it from normal output that you write in your script1. The final line of output in the example above does not have a + in front of it - because it is actual output from an echo command, rather than a trace line.

The number of + symbols indicates the 'level of indirection' - this is how many sub-shells you are in. Each subshell is traced on its own line. This makes tracing complex commands far easier:

set -x
echo "Name of home folder is $(basename $(echo ~) )"

The output of this command is:

+++ echo /home/dwmkerr
++ basename /home/dwmkerr
+ echo 'Name of home folder is dwmkerr'
Name of home folder is dwmkerr

Notice that each subshell command is shown with an additional plus as it gets more nested. The nested commands are shown in the order that they are evaluated.

I often start my shell scripts with a snippet like this:

# Fail on errors in commands or in pipelines.
set -e
set -o pipefail

# Uncomment the below if you want to enable tracing to debug the script.
# set -x

This combines the first two patterns we've seen - failing on errors and having the option to trace a script.

Checking for Existing Variables or Functions​

The declare (set variable values and attributes) command can be used to explicitly declare that we are creating a variable. We saw in Chapter 19 - Variables, Reading Input, and Mathematics that sometimes this command is required - if we want to create an associative array for example.

There are a number of options for the 'declare' command, but one that is particularly useful is the -p (display attributes and value) option. This can be used to show all of the variables of a certain type.

Here's an example to show all associative arrays that have been created:

$ declare -p -A
declare -A BASH_ALIASES=()
declare -A BASH_CMDS=()

You can also use this command to validate whether a variable has been set or not:

if declare -p -A my_options > /dev/null 2>&1; then
echo "'my_options' exists"
else
echo "'my_options' does not exist"
fi

We have to silence the error output of the declare command unless we want it to print an message if the variable doesn't exist. This technique can be useful to use before setting variables to ensure that they are not already in use, or check that the variable exists.

Functions are also variables - so we can use this trick to show all functions that are declared, the value of a function, or check if a function exists.

Unsetting Values​

If you are writing a script that should clean up after itself, you might want to use the unset (unset values and attributes) command. This can be useful if you want to create a script that leaves behind no variables or functions that could cause issues for later users:

# Remove the 'is_even' function from the shell session.
unset -f is_even

Traps​

You can use the trap (trap signals and events) command to specify a set of commands to run when the shell receives signals, or at certain points such as when the script exits or a function returns.

A very common use for traps is to create a 'cleanup' function that is executed when the script exits or if the user aborts execution by pressing Ctrl+C (which sends the SIGINT signal).

Here's an example of how a trap can be set to cleanup a temporary folder when a script exits or is interrupted:

# Create a temporary folder for the effective shell download.
source="https://effective-shell.com/downloads/effective-shell-samples.tar.gz"
tmp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'effective-shell')
tmp_tar="${tmp_dir}/effective-shell.tar.gz"

# Define a cleanup function that we will call when the script exits or if
# it is aborted.
cleanup () {
if [ -e "${tmp_tar}" ]; then rm "${tmp_tar}"; fi
if [ -d "${tmp_dir}" ]; then rm -rf "${tmp_dir}"; fi
}

# Cleanup on interrupt or terminate signals and on exit.
trap "cleanup" INT TERM EXIT

# Download the samples.
curl --fail --compressed -q -s "${source}" -o "${tmp_tar}"

# Extract the samples.
tar -xzf "${tmp_tar}" -C "${tmp_dir}"

In this script we have defined a function called 'cleanup'. We then use the trap command to ensure that we call the function if INT is sent, TERM is sent or when the script exits. This is very useful in scripts that can take a while. This script downloads the effective shell samples from the internet. If the user is having connectivity issues then this might take a while and they may end up aborting the script. If they do so in this case we will still clean up the temporary folder we created.

Traps provide a very convenient way to handle things like cleanup, provide more diagnostic information or even disable a user from interrupting your script. In the example below we force the user to press Ctrl+C twice if they want to interrupt the script:

interrupt_count=0
on_interrupt() {
if [ $interrupt_count -lt 1 ]; then
echo "Aborting this operation can cause errors."
echo "Press Ctrl+C again if you are sure you want to cancel."
interrupt_count=$((interrupt_count + 1))
else
# Convention is to use the status code 130 for interrupted scripts.
echo "Aborting long operation"
exit 130
fi
}

trap on_interrupt INT

total_time=0
while true; do
echo "Long operation: ${total_time} seconds elapsed"
sleep 3
total_time=$((total_time + 3))
done

If we run this script we can see that the user must press Ctrl+C twice to abort the operation:

$ ~/effective-shell/scripts/long-operation.sh
Long operation: 0 seconds elapsed
Long operation: 3 seconds elapsed
Long operation: 6 seconds elapsed
^CAborting this operation can cause errors.
Press Ctrl+C again if you are sure you want to cancel.
Long operation: 9 seconds elapsed
Long operation: 12 seconds elapsed
^CAborting long operation

Some other things that you might want to be aware of for the trap command are:

  • The SIG at the beginning of the name of a signal is optional but not supported in all shells, and a signal number can also be used - this means that SIGINT, INT and 2 are all equivalent options for trap, but INT is the most portable and easiest to read!
  • You can list the signals available with trap -l or kill -l - but remember that special conditions such as EXIT and RETURN are not listed, you can find these with help trap
  • You can stop a signal from being processed with trap "" INT - this means that no command will be executed when we receive a INT
  • You can reset a trap by running trap - INT, this will remove any trap handler
  • You can test your traps by sending a signal explicitly to your script with kill -s INT, providing the name of the signal

Handling Options​

You can use the getopts (parse option arguments) command to process the arguments for a script or function

Let's imagine we wanted to update our 'common' command to support the following options:

  • -h for 'help', which shows command help
  • -e for 'execute', which takes the number of a command from the list which will be executed

The 'getopts' command takes two parameters. The first is an 'option string', which is a list of the parameter letters that are allowed. The option string for 'getopts' expects that each letter followed by a colon represents an option that requires a value. The second parameter specifies the name of the variable that will be set to the currently processed option. If the option string starts with a colon, it affects how 'getopts' assigns values to the shell variables specified by 'name' and 'OPTARG'. For more detailed information, please refer to the 'man getopts' manual.

Typically this command is used in a while loop, as it will return 'success' until the final option has been processed. A case statement is typically used to process the option:

# Helper function to show how the command should be invoked.
show_help() {
echo "usage:"
echo " common [-h] [-e <command_number>] count"
}

# Process the options.
while getopts ":he:" option; do
case ${option} in

# Handle the 'help' option.
h )
show_help
exit 0
;;

# Handle the 'execute command' option by storing the value provided
# for the option.
e )
execute_command=${OPTARG}
;;

# If we have an invalid argument, warn and fail.
\? )
echo "The value '${OPTARG}' is not a valid option"
exit 1
;;

# If we are missing a required argument, warn and exit.
: )
echo "The option '${OPTARG}' requires an argument"
;;
esac
done

There are a few things to point out from this script:

  • The option string starts with a colon - any option letter that is followed by a colon expects an argument
  • If an invalid option letter is set, the value of the option variable is set to \? - we can then handle this in our case statement
  • If a letter is provided without an argument that is required, the value of the option variable is set to : - we can then handle this in our case statement

For complex option processing you might see scripts where multiple loops are used to process sets of options. It is common to end option processing with the following line:

shift $((OPTIND - 1))

The ${OPTIND} variable stores the index of the last option processed. By shifting by this value minus one, we remove the processed options from the $@ (all parameters) array. This means we don't try to process the same options again.

The ~/effective-shell/scripts/common.sh script processes parameters using the getopts command. You can use this as an example to help you with your own scripts.

Colouring Output​

There are special escape sequences that can be used in the shell to colour the output of the text shown. For example, in many terminals the following text will be shown in green:

green='\e[0;32m'
reset='\e[0m'
echo -e "Do you like ${green}apples${reset}?"

On most terminals you will see the text below, with the word 'apples' rendered in green:

Do you like apples?

Note that it is important to provide the -e flag to the 'echo' command so that it correctly processes the colour codes. In fact, a better option is to use the printf (format and print arguments) command, as it is more portable and behaves more consistently across different versions of Unix and Linux.

The colour codes are ANSI escape sequences that have been defined to control the formatting of content in a terminal. There are number of formatting options - such as foreground and background colours, bold, underline and so on. These codes can be quickly found online if you search for "ANSI color codes".

It is important to be careful when using colour codes - you don't want them in all circumstances. Let' see an example. The 'rainbow' function below writes out a message in a number of colours:

rainbow () {
local message="$1"
local reset='\e[0m'
for ((colour=31; colour<=37; colour++))
do
colour_code="\\e[0;${colour}m"
printf "${colour} - ${colour_code}${message}${reset}\n"
done
}

If we run this function in most terminals, we'll see the provided message with the colour number in seven different colours:

$ rainbow test
31 - test
32 - test
33 - test
34 - test
35 - test
36 - test
37 - test

We have to be careful when formatting output. It can be helpful for a user in an interactive shell (on many systems for example even the ls command is actually an alias for ls --color=auto meaning that the ls command uses colours in its output). But there are circumstance when we don't want to use colour codes. Let's see what we get when we write the rainbow output to a file:

$ rainbow hello >> text.txt
$ cat -v text.txt
31 - ^[[0;31mhello^[[0m
32 - ^[[0;32mhello^[[0m
33 - ^[[0;33mhello^[[0m
34 - ^[[0;34mhello^[[0m
35 - ^[[0;35mhello^[[0m
36 - ^[[0;36mhello^[[0m
37 - ^[[0;37mhello^[[0m

The '-v' parameter tells cat to make escape characters visible. If you open the in a text editor you will see the same escape characters written in the file.

This shows the problem with the rainbow function - it adds the colour escape sequences even when we are writing the results to a file. In most cases this is not going to be what we want. Commands like ls do not include colour codes when writing to a file.

There is not an entirely fool-proof way to avoid this issue, but the most common pattern I have seen is to check whether the standard output file descriptor is associated with a terminal. We can do this using the -t expression of the test command:

if [ -t 1 ]; then
echo "We are writing to a terminal"
else
echo "We are not writing to a terminal"
fi

You will see -t 1 in a number of scripts as a way to check whether the output is going to a terminal device. The -t test returns success if the provided file descriptor is associated with a terminal device. The file descriptor '1' is the descriptor for the stdout stream (if this is unfamiliar, check Chapter 7 - Thinking in Pipelines).

Here's how we could use the test in our rainbow function:

rainbow () {
local message="$1"
local reset='\e[0m'
for ((colour=31; colour<=37; colour++))
do
colour_code="\\e[0;${colour}m"
if [ -t 1 ]; then
printf "${colour} - ${colour_code}${message}${reset}\n"
else
printf "${colour} - ${message}\n"
fi
done
}

This version of the function will not write the ANSI escape sequences if the output device is not a terminal, meaning that if we run:

$ rainbow test > text.txt

Then the output file will not contain escape sequences. You can find out more about the -t test by running man test.

As a final tip - if you are formatting output you should consider using the tput (query terminfo database) command to make your code more readable and portable:

green=$(tput setaf 2) # set ansi foreground to '2' (green)
reset=$(tput sgr0) # reset the colours
echo -e "Do you like ${green}apples${reset}?"

The 'tput' command is quite advanced, but you can search online for more details (the manual pages for the command are hard to decipher as it can be used for many operations and is complex).

The ~/effective-shell/scripts/common.sh script includes colourised output and also checks to see whether colour codes should be printed based - you can use this as a reference for your own scripts.

Checking the Operating System​

Different flavours of Unix and Linux can behave quite differently. A common requirement is to write scripts that are portable and can be used across systems. However, this is not always possible. There are times when we need to check to see whether we are on a specific operating system and take a specific action.

You will often see the uname (show operating system name) command used to check the operating system:

case "$(uname)" in
Darwin)
os="OSX"
;;

Linux)
os="Linux"
;;

CYGWIN*|MINGW32*|MSYS*|MINGW*)
os="Windows"
;;

SunOS)
os="Solaris"
;;

*)
echo "Unsupported operating system"
exit 1
;;
esac
echo "Your OS is: ${os}"

The ~/effective-shell/scripts/common.sh script checks to see whether the operating system is OSX and if so, temporarily aliases the text commands such as sed to their GNU equivalent, as the OSX versions of the commands are based on BSD so have slightly different parameters. You can use this script as an example of how to deal with OSX in shell scripts that are designed to be used on Linux as well as OSX.

Checking for Installed Programs​

As we saw in Chapter 10 - Understanding Commands there are many different ways to determine whether a command is available. The most correct and portable way to test to see whether a command is available is to use the command -v command as shown below:

if ! command -v "curl" >/dev/null 2>&1; then
echo "'curl' is not installed, please install and try again"
fi

Note that when we're using the command command, we silence error output and standard output. This is required because otherwise we would see an error message written to the screen if the command doesn't exit or would see the details of the command if it does exist.

The ~/effective-shell/scripts/common.sh script checks to see whether certain GNU versions of tools are installed when running on OSX. You can refer to this script for an example of checking for the presence of commands.

Using 'Select' to Show a Menu​

The select compound command prints a menu and allows the user to make a selection. It is not part of the Posix standard, but is available in Bash and most Bash-like shells.

Here's how we could ask a user to select their favourite fruit from a list:

select fruit in Apple Banana Cherry Durian
do
echo "You chose: $fruit"
echo "This is item number: $REPLY"
done

If we run these commands we will see output like the below:

1) Apple
2) Banana
3) Cherry
4) Durian
#? 1
You chose: Apple
This is item number: 1
#? 3
You chose: Cherry
This is item number: 3
#? 4
You chose: Durian
This is item number: 4
#? ^D

Notice that select will run just like an infinite loop - after the statements in the select body are run, the selection is offered again. The user can either end transmission with the ^D character or press ^C to quit.

You will normally see the select used with a case statement to process the selection. This is something you may come across in scripts so is useful to be aware of.

Running Commands in Subshells​

You will often see a nice little trick that allows you to change the current directory for a specific command, without affecting the current directory for the shell.

Here's how this trick will look:

(mkdir -p ~/new-project; cd ~/new-project; touch README.md)

The brackets around the statements mean that these commands are run in a sub-shell. Because they run in a sub-shell, they change the directory in the sub-shell only, not the current shell. This means we don't need to change back to the previous directory after the commands have completed.

This sequence of commands would create a new folder (we use mkdir -p so that if the folder exists the command does not fail), then change to the folder, then create a new file called README.md.

Anti-Patterns​

Anti-patterns are techniques that you may see but should be avoided. I have noted a few here as you will likely see them in your travels and should know why they are problematic.

Configuring Options in Shebangs​

You will sometimes see shebangs in shell scripts that contain options, like so:

#!/usr/bin/bash -ex

# Script contents below...

It is possible to specify the arguments to the program that is used to execute the script in the shebang. In the case above, the -ex flags are passed to the bash program, enabling the 'exit on error' and 'trace' options.

I include this pattern because it is possible you will see it in other scripts, but please do not do this. There are two particular reasons that it is risky.

The first is that pattern requires that you know the path to the shell. As we saw in Chapter 18 - Shell Script Essentials, we should use the #!/usr/bin/env program so that we search the $PATH for the shell rather than assuming that we know the location of the shell program.

The second reason is that multiple parameters are not handled consistently across operating systems. For example, on some Unix systems the following shebang will run bash with the -e parameter:

#!/usr/bin/env bash -e

However, on many Unix distributions only one parameter is passed. This would mean that the -e parameter would be silently ignored, which would be very confusing for the reader.

Complex Logic in Shell Scripts​

The shell is amazing. Considering how long it has been around, it has in many ways changed remarkably little in the last few decades. This is a testament to the genius of the design of Unix systems and the shell in general.

However, the shell is not generally going to be the best choice for any kind of complex logic or work. Shell scripts are great for automating simple tasks, creating utilities to help you out, but come with many challenges. The syntax can be confusing, making scripts work across multiple systems can be challenging, and there are not many features to help you write robust code.

Perhaps the biggest anti-pattern in shell scripts is to simply let them get too large and do too much with them. There comes a certain point where you will almost certainly create a more portable, performant and maintainable solution to your problem using a dedicated programming language like Python (which is available on almost all systems) or one of the many other languages available.

This is a topic we discuss in detail in the Chapter 30 - How to Avoid Scripting, but for now I would just say that as soon as your script starts to get longer than a page, or takes more than a few minutes to reason about, then you might be reaching the point that a programming language could be a better option.

Scripts without Shebangs​

You might find shell scripts that do not have a shebang at the top. This is something that you should avoid. The shebang gives you a way to be very explicit about what shell is required to run your script.

For example, if I see a shebang like this:

#!/usr/bin/env sh

Then my assumption would be that this script can run on any Posix compliant shell, i.e. it is as compatible as possible. However, if I see this:

#!/usr/bin/env bash

Then the assumption would be that this script is Bash-specific and uses "Bash-isms", such as the if [[ conditional ]] construct. Finally, if I was to see a shebang like this:

#!/usr/bin/env zsh

Then I would expect that this script has been explicitly written to be used with Z-Shell.

If you do not include a shebang in a script, then the behaviour can be ambiguous. For example, at the time of writing, if you run a shell script without a shebang from a Bash shell, then the script will be run using a new instance of Bash. However, if you run a shell script without a shebang from Z-Shell then Z-Shell will use sh from your path. But depending on your system, sh may be a symlink to dash, or bash, or another shell.

Scripts that do not have shebangs are inherently ambiguous and will run using different shells depending on the shell used to execute the script as well as the operating system.

If you want to experiment and see what shell runs a script, create a script such as the ~/effective-shell/scripts/nobang.sh script that looks like this:

# nobang: This script shows an anti-pattern - not using a shebang in a shell
# script. It shows the process tree to for the shell that runs the script:
pstree $$

If I run this script from MacOS, the output below is shown:

-+= 00001 root /sbin/launchd
\-+= 07995 dwmkerr tmux
\-+= 31195 dwmkerr /bin/zsh
\-+= 49833 dwmkerr sh ./samples/script/nobang.sh
\-+- 49834 dwmkerr pstree -p 49833
\--- 49835 root ps -axwwo user,pid,ppid,pgid,command

Although I ran the script from a Z-Shell session, it was executed with sh, which is the Bourne Shell (version 3 on my system).

The only time you should omit the shebang is if you expect the script to be sourced - we will see sourcing in the next part of the book.

Summary​

In this chapter we saw an assortment of common patterns that can be useful when building shell scripts. In the next part of the book we're going to look at how you can customise your shell and environment to build your own toolkit!


  1. The value shown before each trace line can be configured by setting the $PS4 variable.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-5-building-your-toolkit/configuring-the-shell/index.html b/pr-preview/pr-346/part-5-building-your-toolkit/configuring-the-shell/index.html new file mode 100644 index 00000000..8183e99e --- /dev/null +++ b/pr-preview/pr-346/part-5-building-your-toolkit/configuring-the-shell/index.html @@ -0,0 +1,26 @@ + + + + + +Configuring the Shell | Effective Shell + + + + + + + + + + + + + + +
+

Configuring the Shell

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.

The Shell Configuration File​

There are a number of different files that the shell uses for configuration, and we're going to see all of them in this chapter. However, the file we will use most often is the ~/.bashrc file.

When you log into a machine using the shell, or start a shell program in a terminal emulator like the Gnome Terminal or Konsole, you are running an interactive shell. An interactive shell is one that is connected to your keyboard and screen.

When an interactive shell starts, one of the operations it performs is to run all of the commands in the file ~/.bashrc. This is one of the 'shell startup' files.

The 'RC' in the file name stands for 'run commands' (sometimes people will also refer to this as 'run configuration'). This is a convention from the early days of Unix. Many tools on Unix and Linux have files that end in 'rc' that are loaded when a program starts up. For example, the ~/.vimrc run commands file is loaded by the vim program when it starts.

The ~/.bashrc file is in your home directory - this means that it is your personal Bash configuration file. There is also a file that is normally at /etc/bash.bashrc that is used to configure Bash for all users.

Again - this is a common convention for Unix and Linux systems - there is a 'global' configuration file that is used for all users, as well as a 'user' configuration file in the user's home directory that the user can edit to personalise things for themselves.

Z-Shell

The zsh shell uses a ~/.zshrc file for per-user configuration and /etc/zsh/zshrc for global configuration. The paths are different but the concepts are the same. Other shells may use different paths as well - you should be able to find the paths in their manual pages.

The Default Configuration File​

Let's take a look at some of the commands that are in the ~/.bashrc on a clean Ubuntu 20 installation (if you want to know how to set up a free Ubuntu 20 machine check Appendix - Setting Up a Linux Virtual Machine.

I've omitted parts of the file in the snippet below, we'll focus on some of the most interesting areas.

# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# ...

# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# ...

It's very important to understand that this file is sourced by the shell - so we have to use return if we want to stop processing it. If we used exit instead then the shell would close, which is definitely not what we want! If you need a reminder on sourcing, check Chapter 18 - Shell Script Essentials.

The next section of the file sets up some of the configuration for the history features of the shell. Some variables are set, such as HISTSIZE (the number of commands to store in the history), we also set some options using the shopt (set shell option flag).

Later on, we can see that some aliases are defined, for user convenience. For example, the la alias is a shorthand for ls -A, which can save a few keystrokes.

This exactly the sort of configuration that makes sense to keep in the ~/.bashrc file. Users can modify this to suit their preferences.

Now let's look at some of the common features you might configure in the ~/.bashrc file.

Common Shell Configurations​

You can add any commands you like to the ~/.bashrc file, these commands will be run when the shell starts up.

Let's see a few examples of what you might add to your ~/.bashrc.

Aliases​

If you find yourself typing the same series of keystrokes again and again, you might want to add some aliases to your configuration file:

# Start a web server
alias serve="python3 -m SimpleHTTPServer 3000"

# Open vim without loading the vimrc.
alias vimnilla='vi -u NONE'

# Shortcut for 'kubectl', saves a lot of time!
alias k='kubectl'

# Quickly go to my GitHub repositories.
alias gocode='cd ~/repos/github/dwmkerr'

If you are not familiar with aliases, check Chapter 10 - Understanding Commands.

You might also use aliases to change the behaviour of existing commands. For example, we can change the rm command to automatically ask for confirmation before a file is deleted:

# Always run 'rm' in interactive mode.
alias rm='rm -i'

Be aware that the more you customise default commands the more that you run the risk that tutorials or samples you use may not work as expected, as those samples will expect the default behaviour of the command.

Functions​

If you have more complex operations that you regularly perform, you could add them to your ~/.bashrc as a function:

# Restart the shell.
restart-shell() {
exec -l $SHELL
}

# Make a directory (don't fail if it exists) and move into it in one line.
function mkd {
mkdir -p -- "$1" && cd -P -- "$1";
}

# Cut, but in reverse, e.g:
# $ echo "One;Two;Three;Four;Five" | revcut -d';' -f2
# -> Four
function revcut {
rev | cut "$@" | rev
}

You can find out more about functions in Chapter 22 - Functions, Parameters and Error Handling.

Shell Options​

The ~/.bashrc file is the ideal place to configure shell options to suit your preferences:

# If we enter a directory name on its own, assume we want to 'cd' into it.
shopt -s autocd

In this example we use the shopt (set shell option) command to set the autocd option. This option allows you to enter the name of a directory as if it was a command, when you press 'enter' the shell will cd into the directory.

You can set an option using the -s (set option) flag and unset an option with the -u (unset option) flag.

You can list the options available to set by running shopt -p, or searching the man bash page for shopt. Some of the most useful options are:

OptionDescription
autocdEnter a directory name as a command and the shell will cd to it.
cdable_varsAllows you to cd into a variable, such as repos=~/repos; cd repos
cdspellThe shell will try to fix typos to the cd command.
checkjobsShow the status of stopped and running jobs before exiting the shell.
cmdhistSave multi-line commands in the shell history as single entries, rather than an entry per line.
dirspellTry to correct typos when auto-completing directory names.
globstarSupport recursive globbing such as **/*.py to find files in subdirectories.
histappendAppend to the history file when the shell exists, rather than overwriting it.

As well as the options that can be set using the shopt command, there are also many variables that are used to configure the shell. We've seen some of these variables already, such as the EDITOR variable that defines what text editor to use and the PAGER variable that defines what pager program to use.

Changing the Command Prompt​

The command prompt is the information that is shown to the left of the caret in the shell where you enter commands. It will often look something like this:

dwmkerr@ip-172-31-28-144:~/effective-shell$

This command prompt in this example is made up of the following parts:

  • ubuntu - the name of the current user
  • ip-172-31-28-144 - the hostname of the machine
  • ~/effective-shell - the current directory
  • $ - an indicator showing that we are using Bash (this will be # if we are a super user)

The structure and format of the command prompt can be configured using the PS1 variable. This is a large enough topic that the whole of the next chapter is dedicated to customising the command prompt.

Source Files​

Another common pattern for the ~/.bashrc file is to simply source another file.

For example, you might want to create a set of common functions that you keep in a file called shell-functions.sh. You could source this file as part of your shell configuration:

# Load my common shell functions.
source ~/shell-functions.sh

In fact, a lot of the shell startup files do exactly this. For example, in the default ~/.bashrc file on Ubuntu 20, you will see these lines:

if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi

This line uses the -f test to see whether a file named ~/.bash_aliases exists. If it does, it is loaded (using dot sourcing as the notation).

There are lots of different ways to manage your shell configuration. This can range from the simple, such as adding an alias to the ~/.bashrc file, to the complex, such as sourcing the contents of an entire directory, or configuring a shell dynamically based on what tools are installed on a system.

Configure Your System​

You might have particular commands you want to ensure are run when you start a shell. For example, let's say that you want to always have a folder named ~/today that links to a temporary folder which is updated daily.

To do this, you could add the following commands to the ~/.bashrc file:

# Get today's date in the format YYYY-MM-DD.
today=$(date +"%Y-%m-%d")

# Create the path to today's temp folder and then make sure the folder exists.
temp_path="/tmp/${today}"
mkdir -p "${temp_path}"

# Now that we've created the folder, make a symlink to it in our homedir.
ln -sf "${temp_path}" "${HOME}/today"

If I add this code to my ~/.bashrc file then whenever I start a new shell, a folder will be created with today's date in the /tmp/ directory, and a link will be created to this folder at ~/today. This provides a convenient way to have a temporary working folder for the day. You can then go back and refer to old temporary folders if you need to.

Configuration Tips​

There are a few things that you should pay attention to when working with startup files.

Do not print output

It is considered bad practice to print output during startup of the shell. Avoid running commands like echo or printf. If you call commands that write to stdout then silence the output by piping it to /dev/null.

Do not run long operations

You might have written a cool scripts that pulls down information on stocks or weather from a website, ready to show in your shell. But avoid running anything in a startup file that can take a lot of time. Every time you start your shell you'll have a delay while the command runs and this can really slow you down!

Be careful not to break things

Don't run so many commands that you might cause errors or failures on startup. This can make your shell difficult to use or slow to start up. If your startup logic is failing it can be hard to debug, so try not to make it too complex!

Clean up after yourself!

Remember, any variables you set will be set for all shells that read the startup file. If there are variables that you only need during the processing of the file, consider using the unset command to unset the variable at the end of startup.

Expect commands to be run multiple times

Write your startup files with the assumption that they will be run multiple times. If you start a new shell from your current shell, your configuration file will be loaded again. Your configuration should not cause errors if it is run multiple times!

Shell Startup​

In most cases you will only need to work with the ~/.bashrc file to configure your shell. However, the shell actually uses a number of different configuration files (which are called 'startup files') depending on how the shell is being used.

You may have seen references to files such as /etc/profile, ~/.bash_profile, ~/.bash_logout and more. The different files that are used can be quite confusing. For the rest of this chapter we're going to go into the details of exactly how the shell uses these different files.

Different Types of Shells​

For us to be able to understand how shells are configured, we need to understand the different types of shells that can run. This does not mean different shell programs, such as bash, zsh or dash, but instead the differences between interactive and non-interactive shells, as well as login shells.

A lot of people get confused by how the shell is configured because they don't fully understand what these different types of shells are. So let's introduce each one, what it is and how it is used.

Interactive Shells​

An interactive shell is any shell that has its input, output and error standard streams connected to a terminal. This sounds complicated, but it really just means that an interactive shell is one that you interact with via the keyboard and display!

When we type commands into our shell, we're using an interactive shell.

Non-Interactive Shells​

Any shell that does not have its standard input, output and error streams attached to a terminal is generally called a non-interactive shell.

The most common example we've seen so far for non interactive shells are the shells that run shell scripts! Let's run the showpstree.sh script from the samples to show the process tree for the current process. This script shows the process tree for the shell process it is running in and looks like this:

# GNU pstree; use the long form (-l) show the command line (-a) and the
# details for a specific process (-s).
pstree -a -s $$

Here's the output when we run this script:

~$ ./effective-shell/scripts/showpstree.sh
systemd
└─sshd
└─sshd
└─sshd
└─bash
└─sh ./effective-shell/scripts/showpstree.sh
└─pstree -a -s 1675

The output will look different depending on what system you are using, but the key section to focus on are the final three processes:

  • pstree -a -s 1675 - this is just the pstree (show process tree) command that is run in the showpstree.sh script
  • sh ./effective-shell/scripts/showpstree.sh - this is a non-interactive shell that is running our shell script
  • bash - this is the interactive shell that we used to invoke our shell script

When you run a shell script, it runs in a non-interactive shell. This is really important to remember! Shell scripts are run in non-interactive shells. This means that anything you define in ~/.bashrc will not be loaded, so don't try and use aliases or other customisations that you have made.

In fact, on many distributions you will see the following lines in the default ~/.bashrc:

# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac

The first section of the script checks the current shell parameters (which are stored in the special $- variable) to see whether the i (interactive) parameter is present. If it is not present, the return command runs. This check for the shell parameters ensures that even if a non-interactive shell does load the run commands file for some reason, it stops reading it right away.

If you need a refresher on how the case statement works, check Chapter 20 - Mastering Conditional Logic.

Another way to show a non-interactive shell in action is to simply invoke the shell program with a specified command from the command like:

$ sh -c "echo $((5 + 5))"
10

In this example we started the sh (shell) program and provided a command via the -c (command) flag. This starts a non-interactive shell.

Why do non-interactive shells not load the configuration file? There are two reasons. The first is that it doesn't make sense for scripts to rely on user-level customisations. If one user has an alias and refers to it in a script, then the script will not run for another user unless they have the same alias. The second reason is for performance - when using a shell to run a script the shell can start much more quickly if it doesn't need to load configuration or customisations.

Login Shells​

When you login to a computer with a shell, entering credentials such as a username and password, then you are using a login shell. A login shell will normally run some initial setup of your environment and provide the bare minimum configuration required to work with the system. For example, most shells set up the $PATH variable as part of the initialisation of the login shell.

For systems that don't have a graphical interface, any shell you create will be a child of the login shell, so will inherit the login shell's configuration. For graphical interfaces to systems, such as KDE or Gnome, when you log in with the graphical interface, the desktop manager normally configures the environment using the same configuration as is used for a login shell. The desktop manager process will therefore have variables like the $PATH set up just as if you had logged in at the command line.

When you ssh onto a remote machine, you will be running a login shell. In most cases, when you switch users with commands like su (set user), you will start a login shell as well1.

The key thing to remember about login shells is that they are normally run once, when you start working with a computer, and all of the other shells you then run will be children of the login shell.

You can see whether your shell is a login shell by examining the $0 variable. This variable holds the parameters that were provided to start the shell. By convention, if the parameter starts with a - dash symbol, you can assume that you are in a login shell.

Let's see an example of this in action by logging into a virtual machine (if you would like to set up your own Linux virtual machine you can follow the guide in Appendix - Setting Up a Linux Virtual Machine:

$ ssh effective-shell-ubuntu-20
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-1045-aws x86_64)
...
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

~$ echo "$0"
-bash

Here we can see that the parameter that the shell was started with was -bash. This starts with a - dash symbol, indicating that it is a login shell.

Login shells are normally interactive shells, but it is possible to run a non-interactive login shell (it's just quite an unusual thing to do).

In the early days of Unix, executing any commands could be time consuming. The login shell would perform the most essential configuration only once when a user logs in and all subsequent shell processes could start more quickly as they would inherit the login configuration and then load the user specific configuration.

Shell Startup Files​

When the shell starts, it reads a set of startup files. These files are shell scripts that are sourced by the shell. A script that is sourced is loaded into the current shell process, rather than running in a new shell process.

For many people, the various different files that are loaded can cause confusion. But as long as you understand the different types of shells that exist, it is actually quite straightforward to understand the process.

When a login shell starts, the following steps are taken:

  • The shell attempts to load the profile file
  • The profile file will normally load the run commands file

When an interactive shell starts, the following steps are taken:

  • The shell attempts to load the run commands file

When a non-interactive shell starts, it doesn't load any configuration files, unless one has been specified in the BASH_ENV variable.

Let's take a look at these files in detail.

The Shell Profile File​

When a login shell is started, the shell loads and executes commands from the /etc/profile file.

The profile file contains the most essential configuration that is required. It is often used to set things like the $PATH environment variable, which will sometimes have different values depending on the operating system you are using.

The shell will then attempt to read each of the following files. If the shell finds one that is readable, it reads it and executes its commands, and then does not attempt to read the others:

  • ~/.bash_profile
  • ~/.bash_login
  • ~/.profile

There are very few circumstances in which you should change any of these files. It is based to think of the profile files as essential operating system specific configuration that is needed to have a functional login shell.

When a login shell closes, it will run any commands in the ~/.bash_logout file. However, users might terminate the shell process forcibly, which means that you cannot be sure this file will always be sourced as the shell exits.

The configuration that the startup files perform varies from distribution to distribution, but in general they will do at least the following:

  • Set the $PATH variable to include the appropriate folders for tools for your distribution
  • Set the shell prompt, the characters that are shown to the user to show they need to enter input (such as ~$ when we are in the home folder, or ~# if we are in the home folder as a super user)
  • Set up auto-completion (the feature that allows you to press 'tab' to see suggestions when entering commands)
  • Load the run commands file - we'll look at this file next

The key thing to remember about the profile files is that you normally don't need to change them and they normally load the run command file for you.

The Shell Run Commands File​

When an interactive non-login shell is started, the shell loads and executes the commands from the /etc/bash.bashrc file and then the ~/.bashrc file (if they exist).

It is also convention that you can have two rc files - one that is for all users (in this case, /etc/bash.bashrc) and one that is user specific (in this case, ~/.bashrc).

The ~/.bashrc file is where you can put your own commands to configure the shell to suit how you want to use it. This is the file we spent the first half of the chapter looking at in detail.

Startup Files for Non-Interactive Shells​

If you need to load a startup file for a non-interactive shell, you can set the BASH_ENV variable to the path of the file that you want to load. In general you should be very careful when doing this, as shell commands or shell scripts should be written so that they can operate without a startup file being loaded.

Login Shells and Desktop Managers​

Before the advent of the graphical user interface, almost all shell processes would be children of a login shell, as you had to use a login shell to access the system.

For modern systems that use a desktop environment such as Gnome or KDE, the desktop manager process normally loads the /etc/profile file. This means that when you open a terminal program use as the Gnome Terminal or Konsole, the shell is a child of a process which has loaded the profile. Even if you don't use a login shell to access a system, you can normally be sure that the profile will have been loaded by the desktop manager.

Different distributions and operating systems may handle this in slightly different ways. For example, on MacOS when you run the Terminal program it actually starts a login shell2. Again, this means that you can be sure that the profile has been loaded (which in turn will load the RC files).

Changing Your Shell​

You can see the shell that is currently set as the default shell for a user by checking the /etc/passwd file. Here's how I could see what shell is used when the dwmkerr user logs in:

$ grep 'dwmkerr' /etc/passwd
dwmkerr:x:1001:1001:Dave Kerr,,,:/home/dwmkerr:/bin/bash

The /etc/passwd file keeps track of the local user accounts on the system. The final item on a line is the shell that is used for the user. When a user logs in, their shell is set in the SHELL environment variable, we can write this value out with the echo command:

$ echo "My shell is: $SHELL"
My shell is: /bin/bash

There are a few ways that you can change your shell. However, before you change your shell, you need to make sure that the shell you want to use is listed in the 'available shells' file. This file is kept at /etc/shells:

$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/usr/bin/tmux

If the shell you want to use is not listed in /etc/shells you will need to add it to the list.

Once you have installed the shell you want to use and added it to the /etc/shells list you can run the chsh (change shell) command to change the shell for a given user:

$ chsh -s /bin/sh dwmkerr

The -s (shell) parameter is used to specify the shell path. After this we provide the name of the user we are changing the shell for. On many systems users are allowed to change their own shell as long as it is in the /etc/shells list. To change the shell for another user, or to use a shell that is not in the /etc/shells list the chsh command will need to be run as a super-user.

You can also change the shell for a user by editing the /etc/passwd file.

Changing your shell is an advanced topic - if you prefer to use another shell you could also simply start the shell from your login shell, for example by running sh from your Bash shell session.

As an end-to-end example, here's how you would install zsh and set it for the current user on a Debian based system:

# Elevate privileges to super-user.
sudo su

# Update the apt databases and install 'zsh'.
apt update -y
apt install zsh

# Add 'zsh' to the list of shell.
echo "/bin/zsh" >> /etc/shells

# Return to normal user mode.
exit

# Change the current user's shell to 'zsh'.
chsh -s "/bin/zsh" $USER

Be careful when changing your shell - if you get this wrong then you may inadvertently lock yourself out of your account, if logging in tries to start a shell that is not properly configured. Always test that the new shell works before you set it!

Summary​

In this chapter we saw how to customise shell configuration with the ~/.bashrc file. We also looked in detail at the differences between login and non-login shells, interactive and non-interactive shells, and how these different shells load startup files.

You can find all of the detail on how the shell starts up in the man bash page, just search for ^INVOCATION.

In the next chapter we will look at how you can set up your command prompt to suit your preferences.


  1. There are three excellent discussions on login and non-login shells and interactive shells here, here and here.↩
  2. There is a very good discussion on this topic at https://unix.stackexchange.com/questions/119627/why-are-interactive-shells-on-osx-login-shells-by-default.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-5-building-your-toolkit/controlling-changes-with-git/index.html b/pr-preview/pr-346/part-5-building-your-toolkit/controlling-changes-with-git/index.html new file mode 100644 index 00000000..fae93941 --- /dev/null +++ b/pr-preview/pr-346/part-5-building-your-toolkit/controlling-changes-with-git/index.html @@ -0,0 +1,26 @@ + + + + + +Controlling Changes with Git | Effective Shell + + + + + + + + + + + + + + +
+

Controlling Changes with Git

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.

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.

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.

What is Git​

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.

Git is a 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.

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 CVS (Concurrent Version System) and SVN (Subversion, an system similar to CVS but with some improvements). There were also a number of proprietary and commercial solutions.

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.

Creating a Git Repository​

All of the information about a set of files or folders that you are tracking the changes for is stored in a Git repository. We can create a Git repository by running the git init (initialise Git repository) command.

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 Chapter 27 - Managing Your Dotfiles or you can download the Effective Shell samples to get a copy of the 'dotfiles' folder.

Downloading the Samples

Run the following command in your shell to download the samples:

curl effective.sh | sh

If you have installed the samples, you can copy the ~/effective-shell/dotfiles folder to your home directory - this is where we will create our Git repository and start using the Git commands:

$ cp -r ~/effective-shell/dotfiles ~/dotfiles
$ cd ~/dotfiles

We have created the ~/dotfiles folder in our home directory from the samples and moved into it.

Now we will initialise a Git repository with the git init command, and choose a 'branch' name with the git checkout command:

$ git init
Initialized empty Git repository in /home/dwmkerr/dotfiles/.git/
$ git checkout -b main

We'll see the 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.

Initialising a Git Repository with a Branch Name

If you are using Git 2.2 or later, you can initialise a repository and set the branch name with a single command:

$ git init -b main

If you see the error message error: unknown switch 'b' then this means that you are using a version of Git that does not have the -b (initial branch name) flag (any version of Git lower than 2.2).

Adding and Resetting Changes to the Index​

We now have an empty Git repository. We can use the git status (show the working tree status) command to show some information on the files in the working tree. The working tree is the folder that we are using Git to track changes for, in our case ~/dotfiles:

$ git status
On branch main

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
install.sh
shell.d/
shell.sh

nothing added to commit but untracked files present (use "git add" to track)

The first thing that git status tells us is the name of the branch we are on. We'll look at branches in detail shortly. The next thing we see is that there are no 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 install.sh and shell.sh files as well as the shell.d folder.

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 git add (add file contents to index) command:

$ git add .

The git add command takes a list of file paths. We have used the special dot folder to represent the entire current directory. Let's take a look at the status again:

$ git status
On branch main

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: install.sh
new file: shell.d/set_ps1.sh
new file: shell.sh

Git is now telling us that we have three new files which are ready to be committed. At the moment these files are in the 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.

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.

If we were to visualise what we've done so far, it would look like this:

Diagram: A diagram showing how the &#39;git add&#39; command tells Git to track changes to items in the working tree and adds them to the index

Our working tree is the folder associated with our Git repository, this is the ~/dotfiles folder. Our index is initially empty. When we run the 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.

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 git reset (reset changes) command. Let's reset the ~/dotfiles/shell.d/set_ps1.sh file and check the status:

$ git reset shell.d/set_ps1.sh
$ git status
On branch main

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: install.sh
new file: shell.sh

Untracked files:
(use "git add <file>..." to include in what will be committed)
shell.d/

The 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.

Here's how we can visualise the changes we've made:

Diagram: The &#39;git reset&#39; command removes items from the index

You can also reset changes by using the git rm --cached (remove changes from index) command. However, I think this is a little harder to work with as you have to remember to use the --cached flag to tell Git that you are removing from the index and not the repository. We'll see the git rm command a little later in the chapter.

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'.

Now let's look at how we can commit our changes with the git commit command.

Committing Changes​

Once we are happy with the set of changes in the index, we can use the git commit (record changes to the repository) command.

Now run the git commit command:

$ git commit 

At this stage your shell editor will open up, with the text shown below:


# Please enter the commit message for your changes. Lines staring
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
#
# Initial commit
#
# Changes to be committed:
# new file: install.sh
# new file: shell.sh
#
# Untracked files:
# shell.d/
#

The reason you shell editor opens is that the git commit command would like you to provide a message describing your changes. Type a short description, such as:

add the 'install' and 'shell' scripts
# Please enter the commit message for your changes. Lines staring
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
#
# Initial commit
#
# Changes to be committed:
# new file: install.sh
# new file: shell.sh
#
# Untracked files:
# shell.d/
#

Note that below the cursor there is some information that starts with the # 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.

Save the file by pressing Ctrl+W and close the editor with Ctrl+X (these are the commands for the nano editor, if you are using a different editor use whatever commands are needed to save and close).

When the editor closes you'll see a confirmation below the git commit command:

$ git commit
[main (root-commit) 01e7a10] add the 'install' and 'shell' scripts
2 files changed, 90 insertions(+)
create mode 100755 install.sh
create mode 100644 shell.sh

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:

Diagram showing how &#39;git commit&#39; commits changes to the repository

We 'staged' a set of changes and then 'committed' these changes. We now have a single commit in our repository. Our files are still unchanged, but our Git repository now has a single commit in it that tracks the two files we added.

Let's run git status again:

$ git status
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
shell.d/

nothing added to commit but untracked files present (use "git add" to track)

The 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 git commit command below, enter a message to describe the commit:

$ git add .
$ git commit
[main d7e1bb9] add the 'shell.d' folder
1 file changed, 228 insertions(+)
create mode 100644 shell.d/set_ps1.sh

We have now created a second commit - our timeline will look like this:

Diagram showing our second git commit

If we run the git status command one last time we will see that everything in the working tree is tracked in Git:

$ git status
On branch main
nothing to commit, working tree clean

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.

Commit Messages​

You can use any text you like for a commit message. However, there are a couple of things that you should bear in mind:

  1. 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.
  2. 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.

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!

Now let's take a look at how we can work on changes to our files with branches.

Creating Branches​

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.

We can create branches using the git branch (list, create or delete branches) or git checkout (switch branches or restore working tree) command. To show these features in action, we'll create a new branch called aliases and add some files to it:

$ git checkout -b aliases
Switched to a new branch 'aliases'
$ git status
On branch aliases
nothing to commit, working tree clean

We have used the git checkout command to 'switch' to another branch. The -b (new branch) option tells Git that we want to create a new branch. The git status command now shows the new branch name when we run it.

Let's create a new file which includes an alias for the git status command, then let's see what git status tells us about the status of the working tree:

$ echo 'alias gs="git status"' >> ./shell.d/git_aliases.sh
$ git status
On branch aliases
Untracked files:
(use "git add <file>..." to include in what will be committed)
shell.d/git_aliases.sh

nothing added to commit but untracked files present (use "git add" to track)

Excellent - we have a new file and Git knows that it is not currently tracked. Let's stage this file and then commit it:

$ git add .
$ git commit -m "add alias 'gs' for 'git status'"

[aliases f61369d] add alias 'gs' for 'git status'
1 file changed, 1 insertion(+)
create mode 100644 shell.d/git_aliases.sh

In the example above I used the -m (commit message) parameter for the git commit command, this means my editor will not open up as we've already provided a commit message.

We now have a series of commits that looks like this:

Diagram showing how &#39;git checkout -b&#39; creates a new branch

Our new ~/dotfiles/shell.d/git_aliases.sh file has been committed to the aliases branch.

We can switch back to the main branch at any time with git checkout:

$ git checkout main
Switched to branch 'main'
$ tree
.
β”œβ”€β”€ install.sh
β”œβ”€β”€ shell.d
β”‚Β Β  └── set_ps1.sh
└── shell.sh

When we switch back to the main branch and look at our working tree we can see that the 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 git checkout command we can switch branches. If we are on the main branch we don't see the git_aliases.sh file, because the commit that added is was not on the main branch. To go back to the aliases branch we can just checkout again:

$ git checkout aliases
Switched to branch 'aliases'
$ tree
.
β”œβ”€β”€ install.sh
β”œβ”€β”€ shell.d
β”‚Β Β  β”œβ”€β”€ git_aliases.sh
β”‚Β Β  └── set_ps1.sh
└── shell.sh

As a nice little tip, you can always go back to the last branch you were on by running git checkout - just like you can use cd - to change to the last directory you visited! The dash character is a shortcut for the last branch you were on.

Let's add another alias to the file and create another commit:

$ echo 'alias gcm="git checkout main"' >> ./shell.d/git_aliases.sh
$ git add .
$ git commit -m "add alias 'gcm' for 'git checkout main'"
[aliases b9ae0ad] add alias 'gcm' for 'git checkout main'
1 file changed, 1 insertion(+)

Our branches will now look like this:

Diagram showing two commits on the &#39;aliases&#39; branch

You can create as many branches as you like - just remember that when you run git checkout -b, you branch from the current branch (and in fact, the current HEAD, which we will see a little later).

If you want to create a branch, but don't want to switch to it, you can run git branch <new_branch_name>. This command will create a branch from your current position, but will not move to it.

Merging​

You can use the git merge (join two or more branches) command to take the changes from one branch and bring them into another.

We can merge the changes from our aliases branch into the main branch of the repository by first checking out the branch we want to merge into, and then running git merge:

$ git checkout main
$ git merge aliases
Updating d7e1bb9..b9ae0ad
Fast-forward
shell.d/git_aliases.sh | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 shell.d/git_aliases.sh

When we run the git merge command Git tells us what type of merge it has performed. In this case we have a 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 aliases branch can be applied sequentially to the main branch:

Diagram showing how Git prepares for a &#39;fast forwards&#39; merge

One the merge is complete, our branches look like this:

Diagram showing a fast forward merge result

The main branch and aliases branch contain the exact same set of commits.

Let's look at a more common merge scenario - merging branches that have diverged.

Merging Diverged Branches​

Let's create set of commits that show a case where we our branches have diverged - the branches both have their own new commits:

# Create a branch called 'more_aliases', add a file to it, then commit.
git checkout -b more_aliases
touch ./shell.d/bash_aliases.sh
git add .
git commit -m "add a file to store 'bash' aliases"

# Create another file, add it, then commit.
touch ./shell.d/zsh_aliases.sh
git add .
git commit -m "add a file to store 'zsh' aliases"

This snippet checks out a new branch called more_aliases and adds two new empty files, as two separate commits. Now we'll go back to our main branch and change a file:

# Go back to the 'main' branch, add and commit another file.
git checkout main
echo 'alias gm="git merge"' >> ./shell.d/git_aliases.sh
git commit -a -m "add the 'gm' alias for 'git merge'"

I have added a new alias to the shell.d/git_aliases.sh file. By adding the -a (all changes) flag to the git commit command I was able to add the changes to the index and commit them with a single command.

Our branches now look like this:

A diagram showing two diverged branches

Let's merge the more_aliases branch into the main branch:

$ git merge more_aliases

At this point your shell editor will open up with a request for a commit message:

Merge branch 'more_aliases'
# Please enter a commit message to explain why this merge is needed
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message will abort
# the commit.

Git is going to create a new commit on the main branch that brings in the changes from the 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 more_aliases.

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:

$ git merge more_aliases
Merge made by the 'recursive' strategy.
shell.d/bash_aliases.sh | 0
shell.d/zsh_aliases.sh | 0
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 shell.d/bash_aliases.sh
create mode 100644 shell.d/zsh_aliases.sh

Git now tells us that we have made a recursive merge and our branches will look like this:

Diagram showing the results of a recursive merge of two diverged branches

At this stage both the main branch and the more_aliases branch have the full set of changes that we made to both of the branches. Git merged the two branches together and created a new commit that joins them.

The Git Log​

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 SHA, which is a hash. A hash is a sequence of letters and numbers that uniquely identify each commit.

You can use the git log (show commit logs) command to see the log of commits and their SHAs:

$ git log
commit 138b40418d5658bc64421e7bcf2680c8339f8350 (HEAD)
Merge: a95bd90 a51ae1a
Author: Dave Kerr <dwmkerr@gmail.com>
Date: Tue Jun 15 21:00:28 2021 +0800

Merge branch 'more_aliases'

commit a95bd90e3656b2e55b8708193d387c80c282a6ad
Author: Dave Kerr <dwmkerr@gmail.com>
Date: Tue Jun 15 21:00:22 2021 +0800

add the 'gm' alias for 'git merge'

commit a51ae1aa42432c2f391ca782c1c20b3793c232ab (more_aliases)
Author: Dave Kerr <dwmkerr@gmail.com>
Date: Tue Jun 15 20:53:01 2021 +0800

add a file to store 'zsh' aliases

You can see that the log shows each of the commits, the branch the commit was on, the message, the date and so on.

If you want to see a more compact log you can use the --oneline (show one line per commit) flag:

$ git log --oneline
138b404 (HEAD) Merge branch 'more_aliases'
a95bd90 add the 'gm' alias for 'git merge'
a51ae1a (more_aliases) add a file to store 'zsh' aliases
63ea74f add a file to store 'bash' aliases
b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'
f61369d add alias 'gs' for 'git status'
d7e1bb9 add the 'shell.d' folder
01e7a10 add the 'install' and 'shell' scripts

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.

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 --graph (show commit graph) flag:

$ git log --oneline --graph
* 138b404 (HEAD) Merge branch 'more_aliases'
| \
| * a51ae1a (more_aliases) add a file to store 'zsh' aliases
| * 63ea74f add a file to store 'bash' aliases
* | a95bd90 add the 'gm' alias for 'git merge'
|/
* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'
* f61369d add alias 'gs' for 'git status'
* d7e1bb9 add the 'shell.d' folder
* 01e7a10 add the 'install' and 'shell' scripts

Each commit is shown with an * asterisk symbol - we can also see when we created the more_aliases branch and when we merged it back in.

The Git log is very useful to help you understand the changes that have happened in the repository.

Merge Conflicts​

One of the most important features of any version control system is the ability to manage conflicts. Conflicts occur when a set of changes are made that cannot be merged without some kind of manual intervention to decide which of the changes are correct.

Here are a few common scenarios that might lead to merge conflicts:

  • 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?
  • In one branch a file is edited and in another branch the same part of the file is edited in a different way - which edit should we keep? Should we keep both?
  • 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?

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 main branch while you are working on another branch, you are drifting from the main branch. You should either regularly update your branch with the changes in main or merge your changes into main.

There are many different ways for version control systems to manage conflicts. Let's see how Git does it by creating a conflict.

First, we will create a branch that adds a new alias to the git_aliases.sh file:

# Create a 'glog_alias' branch and commit a file.
git checkout -b glog_alias
echo 'alias glog="git log --graph --oneline"' >> ./shell.d/git_aliases.sh
git commit -a -m "add the 'glog' alias"

Now we'll go back to the main branch and add another alias:

# Go back to 'main' and commit a change to the same file as the last one.
git checkout main
echo 'alias glog="git log"' >> ./shell.d/git_aliases.sh
git commit -a -m "add the 'glog' alias"

We've changed the same file in two branches - if we try to merge we will get a conflict:

$ git merge glog_alias

Auto-merging shell.d/git_aliases.sh
CONFLICT (content): Merge conflict in shell.d/git_aliases.sh
Automatic merge failed; fix conflicts and then commit the result.

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 index. We have to now manually fix these files.

Let's see what the status shows:

$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)

Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: shell.d/git_aliases.sh

no changes added to commit (use "git add" and/or "git commit -a")

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 shell.d/git_aliases.sh file, then use git add to stage the changes and commit the result.

To resolve the conflict, we need to edit the file. If you open the file in an editor it will look like this:

alias gs="git status"
alias gcm="git checkout main"
alias gm="git merge"
<<<<<<< HEAD
alias glog="git log"
=======
alias glog="git log --graph --oneline"
>>>>>>> glog_alias

Many modern editors will immediately recognise the sequence of symbols that Git uses to indicate conflicts and highlight them. The <<<<<< HEAD line indicates the changes in the current branch, the ======= line is a separator, and the >>>>>> glog_alias line indicates that everything since the ======= line is the changes in the glog_alias branch.

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.

In your editor, update the file to look like this:

alias gs="git status"
alias gcm="git checkout main"
alias gm="git merge"
alias glog="git log --graph --oneline"

We have chosen the version of the glog alias that was in the 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 <<<, === and >>>. 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.

In the example above we deleted one of the glog aliases, but we could have also just changed the name of one of them:

alias gs="git status"
alias gcm="git checkout main"
alias gm="git merge"
alias glog="git log"
alias ggraph="git log --graph --oneline"

We can now use git add to mark the file as resolved, and run git commit:

$ git add shell.d/git_aliases.sh

The editor will open showing a sensible commit message:

Merge branch 'glog_alias'

# Conflicts:
# shell.d/git_aliases.sh
#
# It looks like you may be committing a merge.
# If this is not correct, please run
# git update-ref -d MERGE_HEAD
# and try again.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main

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.

Our Git log will now show this new merge commit:

$ git log --graph --oneline
* 2532277 (HEAD -> main) Merge branch 'glog_alias'
|\
| * a8cbb15 (glog_alias) add the 'glog' alias
* | 31548e4 add the 'glog' alias
|/
* 138b404 Merge branch 'more_aliases'
|\
| * a51ae1a (more_aliases) add a file to store 'zsh' aliases
| * 63ea74f add a file to store 'bash' aliases
* | a95bd90 add the 'gm' alias for 'git merge'
|/
* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'
* f61369d add alias 'gs' for 'git status'
* d7e1bb9 add the 'shell.d' folder
* 01e7a10 add the 'install' and 'shell' scripts

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.

Other Merge Strategies​

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.

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.

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.

Deleting and Renaming Files​

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.

Let's see both ways in action. First we'll use the git rm (remove files from the working tree and index) command:

$ git rm install.sh
rm 'install.sh'

The 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 git status:

$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: install.sh

The deletion is only in the index at this stage - we need to run git commit to commit the deletion.

If we want to restore the file, we can use git checkout to get the file back from Git:

$ git checkout HEAD install.sh

We use 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 main).

Another common way to delete a file is to simply remove it from the file-system:

$ rm install.sh
$ git status
On branch main
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: install.sh

At this point Git knows the file is missing, but the deletion is not yet 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:

$ git rm install.sh

We could also run git add install.sh - think of this like saying 'stage my change to index.sh' - we are adding a change to the index. I often just run 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.

Let's restore the file:

$ git reset .
Unstaged changes after reset:
D install.sh
$ git checkout .
Updated 1 path from the index

This is another way to restore the file - first we reset all of the changes to the index with git reset ., then we checkout all of the files in the current commit with git checkout .. But be careful with this method - it will also reset any other changes you have made. I prefer the git checkout HEAD <file_path> method as it is more explicit and only restores one file.

What about if we rename a file? Let's rename the install file and see what the status is:

$ mv install.sh install_dotfiles.sh
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: install.sh

Untracked files:
(use "git add <file>..." to include in what will be committed)
install_dotfiles.sh

no changes added to commit (use "git add" and/or "git commit -a")

We've renamed the file, but when we run 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 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:

$ git add .
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: install.sh -> install_dotfiles.sh

Let's restore the file by renaming it back to what it was and adding these changes:

$ mv install_dotfiles.sh install.sh
$ git add .
$ git status
On branch main
nothing to commit, working tree clean

We can also use the git mv (move or rename a file) command to move or rename a file and stage the changes in one go:

$ git mv install.sh install_dotfiles.sh
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: install.sh -> install_dotfiles.sh

We can also restore the changes by using git mv:

$ git mv install_dotfiles.sh install.sh
$ git status
On branch main
nothing to commit, working tree clean

These commands work equally well with folders, or lists of files and folders.

Restoring Your Working Tree​

We've already seen how the git checkout command can be used to switch branches. We can also use the 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.

Let's take a quick look at our Git log so far.

$ git log --graph --oneline
* 2532277 (HEAD -> main) Merge branch 'glog_alias'
|\
| * a8cbb15 (glog_alias) add the 'glog' alias
* | 31548e4 add the 'glog' alias
|/
* 138b404 Merge branch 'more_aliases'
|\
| * a51ae1a (more_aliases) add a file to store 'zsh' aliases
| * 63ea74f add a file to store 'bash' aliases
* | a95bd90 add the 'gm' alias for 'git merge'
|/
* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'
* f61369d add alias 'gs' for 'git status'
* d7e1bb9 add the 'shell.d' folder
* 01e7a10 add the 'install' and 'shell' scripts

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.

Notice that the most recent commit is marked with the text HEAD. The HEAD is where Git is currently 'pointing' to - we will see how to move the head shortly (the term HEAD also refers to the most recent commit of a branch).

At the moment we can visualise our Git log, with the commits, branches and HEAD like so:

Diagram showing our Git log with HEAD indicated

We can restore the working tree to the state of any of the commits by running git checkout <commit_sha>:

$ git checkout f61369d
Note: switching to 'f61369d'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

This snippet moved our HEAD to the third commit in our repository. If you look at the files in your working tree you'll see that they are in exact state that they were when we made our third commit. Visually, we have moved the HEAD as shown below:

Diagram showing a detached HEAD

The warning that says we are in a 'detached HEAD' state is telling us that our current position in the history is not at the 'tip' of a branch - this means we cannot create a new commit without first starting a new branch.

If we want to move back to the 'tip' of our main branch (or any other branch), we can use git checkout <branch_name>:

$ git checkout main

This moves us back to the 'tip' of the main branch.

Git has a very convenient syntax we can use to move backwards. We can specify a branch name, a SHA or 'HEAD', use a ~ tilde character, and then provide the number of commits we want to move 'backwards':

$ git checkout HEAD~1

This command moves the 'head' backwards one commit. This is useful if you realise you have made a mistake with your commit or recent commits and want to go backwards.

Remember - whenever you run git checkout <branch_name> Git moves the HEAD to the tip of the branch by default.

Summary​

In this chapter we looked at some of the core concepts behind Git - the repository, the working tree and the index. We saw how to stage and unstage changes, commit changes, create branches, merge branches, deal with conflicts and remove and rename files.

In the next chapter we will look at how to work with 'remote' repositories - this will allow us to push our changes up to a location on the internet and share these changes with other users, or to download these changes to other machines we might work on.

We've introduced a lot of commands in this chapter - you can use this table as a quick reference:

CommandDescription
git initInitialise a new Git Repository.
git statusShow the status of the working tree and index.
git add <files>Stage files - you can use patterns and wildcards.
git reset <files>Unstage files - you can use patterns and wildcards.
git rm --cached <files>Unstage files - you can use patterns and wildcards.
git commitCreate a commit from the current index - the shell editor will open for the commit message.
git commit -m 'message'Create a commit with message message.
git commit -aStage and commit all changes in the working tree.
git checkout <branch>Checkout a branch called branch.
git checkout -b branchCreate and checkout a new branch called branch.
git branch <name>Create a branch called name but do not check it out.
git branch -m <new_name>Change the current branch name to new_name.
git merge <branch>Merge the branch named branch into the current branch.
git logShow the log of commits.
git log --oneline --branchShow the log of commits, one line per commit, with the branch graph.
git rm <files>Stage the removal of files from the repostiry - you can use patterns and wildcards.
git mv <source> <destination>Stage the movement of source to destination.
git checkout 8342becCheckout a commit with SHA 834bec.
git checkout HEAD~1Move the current HEAD back one commit.
git checkout <branch>~3Checkout branch, move back three commits from the tip.
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-5-building-your-toolkit/customising-your-command-prompt/index.html b/pr-preview/pr-346/part-5-building-your-toolkit/customising-your-command-prompt/index.html new file mode 100644 index 00000000..27abf0cb --- /dev/null +++ b/pr-preview/pr-346/part-5-building-your-toolkit/customising-your-command-prompt/index.html @@ -0,0 +1,26 @@ + + + + + +Customising Your Command Prompt | Effective Shell + + + + + + + + + + + + + + +
+

Customising Your Command Prompt

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.

We will also create a script that allows us to set our own command prompt 'theme' from a list that we can extend over time. This script will also handle the differences between Bash-like shells and Z-Shell for us, allowing us to have a consistent command prompt across different types of shells!

The Command Prompt​

The command prompt is the text that is shown to the left of your cursor to show that the shell is waiting for you to type a command. Each distribution comes with its own configuration for the command prompt, but the default is often similar to the one shown below:

dwmkerr@effective-shell-ubuntu-20:~$

This is the prompt on an Ubuntu virtual machine I have set up. If you want to set up a free virtual machine yourself, you can follow the guide at Appendix - Setting Up a Linux Virtual Machine.

Let's take a look at each of the components that make up the prompt:

  • dwmkerr - The first thing that is shown is the name of the current user
  • @ - Next we have an 'at symbol' character that is used as a separator between the username field and the following field
  • effective-shell-ubuntu: This is the hostname of the machine
  • : - A colon separates the hostname from the next field
  • ~ - Next we have the current working directory
  • $ - Finally we have the prompt itself, the $ symbol shows we are a normal user, rather than a 'root' user

If we change directory, our prompt will be updated:

dwmkerr@effective-shell-ubuntu-20:~$ cd effective-shell
dwmkerr@effective-shell-ubuntu-20:~/effective-shell$

If we change to the 'super' user we can see that the username changes to root and the $ dollar symbol changes to a # hash symbol:

dwmkerr@effective-shell-ununtu-20:~/effective-shell$ sudo su
root@effective-shell-ununtu-20:/home/dwmkerr/effective-shell#

The # symbol is a useful reminder that we are the root user. It is important to be careful when running commands as the root user as we could easily break things by changing system files.

So out-of-the box on most systems our command prompt shows a number of useful fields. But we can actually customise this prompt to include almost any kind of information we would like to see. Let's take a look!

Customising the Command Prompt​

The structure of the command prompt is specified in the PS1 shell variable. This stands for 'Prompt String 1'. The shell uses this variable to write out the command prompt.

We can see the contents of this variable by using echo or printf to write it to the screen:

dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ echo $PS1
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$

This looks extremely complicated - but don't worry, by the time we've finished this chapter you'll be able to understand what this mess of special characters means!

The easiest way to see how these special prompt strings work is to start using them, so let's get started and customise our prompt.

The Prompt String​

You can set your own prompt string by setting the PS1 variable:

dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ PS1="---> "
--->

The shell will use the contents of the PS1 variable to display the prompt. We can use plan text as shown above, but there's also a lot more that we can do to customise this prompt!

Special Characters​

When the shell reads the PS1 variable, it allows certain special characters to be specified. These characters can be used to customise how the prompt string looks.

The special characters that the shell uses are listed below:

CharactersUsage
\aThe special 'beep' character, that tells the shell to play a beep sound through the speakers.
\dThe date in "Weekday Month Date" format, for example: 'Tue May 26')
\D{format}The date in a format specified by the format value.
\eAn ASCII escape character (033). This is used to print special characters.
\hThe hostname up to the first . dot.
\HThe hostname.
\jThe number of jobs currently managed by the shell.
\lThe basename of the shell's terminal device.
\nA newline character.
\rA carriage return character.
\sThe name of the shell, the basename of $0, for example: -bash.
\tThe current time in 24-hour HH:MM:SS format.
\TThe current time in 12-hour HH:MM:SS format.
\@The current time in 12-hour am/pm format.
\AThe current time in 24-hour HH:MM format.
\uThe username of the current user.
\vThe version of bash, for example: '5.0'.
\VThe release of bash, with the patch level, for example: '5.0.17'.
\wThe current working directory, with $HOME abbreviated with a ~ tilde symbol.
\WThe current working directory name (rather than the entire path as is used for \w).
\!The history number of this command.
\#The command number of this command
\$The $ dollar symbol, unless we are a super-user, in which case the # hash symbol is used.
\nnnThe character corresponding to the octal number nnn, used to show special characters.
\\A \ backslash character.
\[The 'start of non-printing characters' sequence.
\]The 'end of non-printing characters' sequence.

Some of these sequences are reasonably self-explanatory, some are a little more complex. Let's use some of them now to see how we can customise the prompt.

Z-Shell

The zsh shell uses different sequences. However, I suggest that you follow this chapter through to understand how Bash-like shells work and then you can apply the same techniques using Z-Shell. The Z-Shell documentation links are at the end of the chapter.

Later on in this chapter we will introduce a function to help set the prompt, this function automatically converts to the prompt into Z-Shell format if needed. So the techniques you learn here should still be able to be used in Z-Shell.

To change the prompt, all we need to do is set the PS1 variable. Let's start by changing the prompt so that it shows the date, time and the $ or # prompt symbol:

dwmkerr@effective-shell-ubuntu-20:~$ PS1='\d \@ \$ '
Sun Jun 06 12:43 PM $

In this example we've used the \d (current date), \@ (current time in am/pm format) and \$ (prompt) and a space for our prompt. Notice that once we set PS1 in the shell, the prompt immediately changed.

How about if we want to show the number of jobs, then the command number, then the prompt? Easy!

Sun Jun 06 04:43 AM $ PS1='[\j] (\#) \$ '
[0] (4) $ sleep 10 &
[1] 27598
[1] (5) $ sleep 10 &
[2] 27600
[2] (6) $ sleep 10 &
[3] 27601
[3] (7) $

In this example we've used the \j (current job) sequence, and surrounded it with square brackets. Then we used # (command number), surrounded by parentheses, then the \$ shell prompt. I also started some background jobs, that just run the sleep (wait for a number of seconds) command, so that we can see that the number of jobs is changing. If you need a refresher on jobs, check Chapter 9 - Job Control.

Note that we are using single quotes when specifying the value of the PS1. If we didn't use single quotes, then the shell would see the dollar symbol and think that we were trying to use a variable. For a reminder on how quoting works, check Chapter 19 - Variables, Reading Input, and Mathematics.

If you are following along or trying this out in your own shell, you might have noticed that we don't have any colours for the new prompts we have set, everything is shown in white. To set the colour of the prompt we need to use some special characters.

Changing the Colour and Text Formatting​

In the earlier part of this chapter we saw that the default prompt on systems like Ubuntu contains lots of special characters. For reference, here is the value of the PS1 variable on a clean Ubuntu 20 installations:

dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ echo $PS1
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$

Some of these characters we might now be able to recognise, such as \u for the username and \h for the host. The characters that start with the sequence \033 are ANSI color codes. ANSI stands for American National Standards Institute, an organisation that was set up to attempt to set common standards for computing platforms.

In the early days of Unix, each vendor developed their own special characters that could be used to control the visual formatting of output. These characters would vary from platform to platform, which made trying to create scripts or functionality that worked across multiple platforms complex. To deal with this, the ANSI organisation defined a common set of codes that could be printed to a terminal to control the visual style of the output.

To tell a terminal that we want to use a special sequence to control the formatting or output of text, we can use these ANSI Escape Sequences. First we write out the characters \033 or \e. This is the sequence that represents the 'escape' key. The first version is the 'escape' key code written in 'octal' format (octal is a format where numbers are written in base eight, rather than base ten). The second sequence is an alternative way of writing the 'escape' key.

When a terminal sees the escape sequence, it knows that the following sequence is used to define the formatting. The table below shows some of the different formats that can be used:

SequenceMeaning
Foreground Color
\033[30mSet foreground to 'black'.
\033[31mSet foreground to 'red'.
\033[32mSet foreground to 'green'.
\033[33mSet foreground to 'yellow'.
\033[34mSet foreground to 'blue'.
\033[35mSet foreground to 'magenta'.
\033[36mSet foreground to 'cyan'.
\033[37mSet foreground to 'white' (normally light grey)
Foreground Color (Bold)
\033[1;30mSet foreground to 'bright black' (grey, or bold black)
\033[1;31mSet foreground to 'bright red' (or bold red).
\033[1;32mSet foreground to 'bright green' (or bold green).
\033[1;33mSet foreground to 'bright yellow' (or bold yellow).
\033[1;34mSet foreground to 'bright blue' (or bold blue).
\033[1;35mSet foreground to 'bright purple' (or bold purple).
\033[1;36mSet foreground to 'bright cyan' (or bold cyan).
\033[1;37mSet foreground to 'bright white' (or bold white).
Background Color
\033[0;40mSet background to 'black'.
\033[0;41mSet background to 'red'.
\033[0;42mSet background to 'green'.
\033[0;43mSet background to 'brown'.
\033[0;44mSet background to 'blue'.
\033[0;45mSet background to 'purple'.
\033[0;46mSet background to 'cyan'.
\033[0;47mSet background to 'white' (normally light grey).
Reset Colors
\033[0mReset the text colors.

Notice that each sequence starts with the 'escape' character, followed by [ or [1;. [ will use the 'normal' colour, [1; will use the 'bright' colour (how this is shown depends on your terminal emulator, in many modern emulators the text is shown in the same colour but is bold). You can also use [0; to clear any changes to the foreground or background before you set the new one. After this there are one of eight colours that can be used, specified by the characters in the range from 30m to 37m. The sequences in the range 40m to 47m set the background color. The sequence 0m resets the colors.

With these codes we can print coloured text. When we write text that uses escape sequences, we need to tell the shell that our text needs to have these escape sequences processed properly. We can use the printf command or echo -e to do this. printf should be preferred as not all systems support the -e parameter for echo:

printf "\033[31mRED\033[0m\n"
printf "\033[1;31mLIGHT RED\033[0m\n"
printf "\033[0;30m\033[42mBLACK ON GREEN\033[0m\n"

The output of these commands will be the text below:

RED
LIGHT RED
BLACK ON GREEN

However, the colour of the foreground and background should change on each line. The exact formatting will change depending on the terminal emulator you use. Some terminal emulators use bold text for the 'bright' colours.

With our new knowledge of how to use ANSI Escape Sequences to set the format of text, we can update our PS1 variable to show a prompt in colour. As an example, the code below sets the prompt to show the username in blue and the name of the current working directory in green, followed by a white $ prompt symbol, followed by the 'reset' sequence so that the text we type afterwards does not have its colour changed:

dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ PS1='\033[34m\u \033[32m\W \033[37m\$ '
dwmkerr effective-shell $

The prompt above will be shown in color on modern terminals.

There is one snag to this. If you set your prompt in this way and press the 'up' and 'down' keys to cycle through previously entered commands, you might see that your shell prompt gets overwritten. The reason for this is that we need to tell the shell that colour and formatting sequences are 'non-printing' characters - the sequences don't actually produce written text in the terminal.

To deal with this we need to surround each colour sequence with the special characters \[ and \]. This tells the shell when a 'non-printing' sequence starts and when it ends. To fix our PS1 variable, we can use the value below:

PS1='\[\033[34m\]\u \[\033[32m\]\W \[\033[37m\]\$ \[\033[0m\]'

Phew! This is a lot of work to go to just to format the colour of the prompt. Later in this chapter we'll build a script that will make it far easier to work with colours and text formatting!

Adding Data to the Command Prompt​

When we set the PS1 variable, we are simply setting it to a string. This string could be anything, for example:

dwmkerr@effective-shell-ubuntu-20:~$ PS1='-Ready?---> '
-Ready?--->

We don't need to limit ourselves to the special sequences we've seen so far in this chapter - we can run any commands we like to build a command prompt. For example, we could use the use the ls (list directory contents) and wc (count lines and words) commands to count the number of files and folders in the current directory and show that in the prompt:

PS1="$(ls -al | wc -l | tr -d '[:space:]') \\$ "

When I run this command, my prompt will look something like this:

32 $

We have used the $() notation to run a sub-shell that lists the contents of the current directory and then pipes them to wc -l, which counts the number of lines. Finally we pipe the result into tr -d '[:space:] to remove the whitespace around the line count.

To use the $() notation, or any shell variable, we have to use double quotes in the string, otherwise the shell will write out those characters literally. And because we are using double quotes, we need an extra backslash before last \$ character to escape it, so that the shell doesn't try to treat it as a variable.

However - there's a subtle bug in this PS1 configuration! Let's see what happens when we change directories:

32 $ cd effective-shell/
32 $ touch newfile-{1..10}
32 $

In the session above I changed to the effective-shell directory. But the count is still showing as 32. This is suspicious. After creating ten new files with touch newfile-{1..10} the count still shows 32.

The reason for this is that 32 was the number of files and folders in the current directory at the time the PS1 variable was set. We changed the PS1 variable once - what we really need to do is have the prompt count the files each time the prompt is shown.

Fortunately, there is a special syntax for this! We just put a \ backslash character in front of the $ dollar symbol for the sub-shell:

PS1="\$(ls -al | wc -l | tr -d '[:space:]') \\$ "

The backslash before the sub-shell tells the shell that it should evaluate the sub-shell each time the prompt is shown:

32 $ touch newfile-{1..10}
42 $

This is where the real power of the PS1 variable comes into play. Because we set it using the shell itself, we can run any commands that we find useful and integrate their output into our command prompt.

Let's see this in action by creating a script to make customising our prompt far easier and more intuitive!

A Shell Script to Customise the Prompt​

We can write a script to make it much easier to customise our shell prompt. Rather than having to remember each of the colour sequences, we can store them in variables to make them easier to refer to. We can also run any commands that we'd like to run to allow us to show extra information.

There is a script in the Effective Shell samples at ~/effective-shell/scripts/set_ps1.sh that we can use to set our PS1 variable in a much more user-friendly way.

Downloading the Samples

Run the following commands in your shell to download the samples:

curl effective.sh | sh

The set_ps1.sh script is quite long, so let's go through it bit-by-bit.

# Keep track of the original PS1 value.
_original_ps1="${PS1}"

set_ps1() {
# Foreground colours.
local fg_black=$(tput setaf 0) # \033[30m
local fg_red=$(tput setaf 1) # \033[31m
local fg_green=$(tput setaf 2) # \033[32m
local fg_yellow=$(tput setaf 3) # \033[33m
local fg_blue=$(tput setaf 4) # \033[34m
local fg_magenta=$(tput setaf 5) # \033[35m
local fg_cyan=$(tput setaf 6) # \033[36m
local fg_white=$(tput setaf 7) # \033[37m

# Background colours.
local bg_black=$(tput setab 0) # \033[40m
local bg_red=$(tput setab 1) # \033[41m
local bg_green=$(tput setab 2) # \033[42m
local bg_yellow=$(tput setab 3) # \033[43m
local bg_blue=$(tput setab 4) # \033[44m
local bg_magenta=$(tput setab 5) # \033[45m
local bg_cyan=$(tput setab 6) # \033[46m
local bg_white=$(tput setab 7) # \033[47m

First, we store the current value of PS1 in a variable named _original_ps1. This is so that later on if we have changed the PS1 variable, we can change it back to what it was set to originally. The _ underscore in the variable name is a convention that indicates that this variable is used internally in the script.

Next, we define a function called set_ps1. Then we use the tput command (query terminfo database) to get the exact escape sequences for the foreground and background colours. For easy reference the escape sequences are shown to the right of each command as a comment.

Next, we get the escape sequences for some of the other formatting options, such as 'bold' (which will be 'bright' on some terminals):

    # Text styles and reset. Note that on some terminals 'bold' will produce
# light colours for bright colours, on others it will actually show the text
# in bold.
local bold=$(tput bold) # \033[1m
local dim=$(tput dim) # \033[2m
local start_underline=$(tput smul) # \033[4m
local stop_underline=$(tput mmul) # \033[24m
local reset=$(tput sgr0) # \033[0m

You might recall the tput command from the section 'colourising output' in Chapter 23 - Useful Patterns for Shell Scripts.

After this we use a case statement to set the PS1 variable based on the value of the first parameter that was provided to the function:

    # Depending on the name of the theme provided, set the prompt.
case $1 in
debian)
# Debian/Ubuntu style:
# \u@\h - username@host (bold/green)
# \w - working directory (bold/blue)
# \$ - prompt (# if root, otherwise $) (bold/white)
PS1="\[${bold}${fg_green}\]\u@\h:\[${fg_blue}\]\w\[${fg_white}\]\\$\[${reset}\] "
;;

datetime)
# A style that shows the date and time:
# \D{%Y-%m-%d} - the year/month/date (in white)
# \@ - the time (in green)
# \$ - prompt (# if root, otherwise $) (bold/white)
PS1="\[${fg_white}\]\D{%Y-%m-%d} \[${bold}${fg_green}\]\@\[${fg_white}\] \\$\[${reset}\] "
;;

# Add your own themes here!

*)
# Restore PS1 to its original value.
PS1="${_original_ps1}"
;;
esac

# If we are in Z-Shell convert the PS1 to use Z-Shell format.
[ -n "$ZSH_VERSION" ] && PS1=$(_to_zsh "$PS1")
}

In this code we check the first parameter of the function $1. If it matches the string debian we set the PS1 variable to a format that is similar to what is used by Debian Linux distributions. If it matches the string datetime we set PS1 to a prompt that shows the current date and time. If any other value is used, we reset the PS1 variable back to its original value.

Before we complete the function, we check to see if ZSH_VERSION is set - this is to check whether we are in a zsh shell. If we are, then we use the _to_zsh function to convert the PS1 string into the format used by Z-Shell.

Finally, we use the } to complete the definition of the function.

Z-Shell

The zsh shell differs considerably from Bash and Bash-like shells in how it handles the PS1 variable. There is no need for the \[ or \] sequences, there are built in color variables such as $fg[red] for 'red' and the special sequences are different (for example, rather than \u for username, Z-Shell uses %n).

The set_ps1 function in the samples converts the PS1 string to Z-Shell format if it is running in Z-Shell. However, this conversion is not perfect as some of the sequences shown in this chapter do not have an equivalent in Z-Shell. If you want to customise a Z-Shell prompt you can check the manual page man zshmisc and search for PROMPT\ SEQUENCES.

Notice how much easier it is to specify the values for the PS1 string when we have the colours and formatting defined in variables! We still need to wrap the formatting characters with \[ and \] to make sure that the shell knows how long the command prompt is, but this is far easier to read than the samples we saw before where we provide the ANSI Escape Sequences.

To use this script, we can simply source it into our current session and then change the prompt by calling the set_ps1 function:

$ 
dwmkerr@effective-shell-ubuntu-20:~$ source ~/effective-shell/scripts/set_ps1.sh
dwmkerr@effective-shell-ubuntu-20:~$ set_ps1 datetime
2021-06-06 04:10 PM $ set_ps1 debian
dwmkerr@effective-shell-ubuntu-20:~$

This script has a placeholder in the case statement for you to add your own 'themes' that you want to be able to use in your shell.

For example, one 'theme' I often use is below:

git)
# A style that shows some git information.

# Build a string that shows:
# - The branch (underlined if 'main') in green
# - A red exclamation if there are any local changes not committed
# - An indicator of the number of stashed items, if any.
_git_info() {
# Git details.
local git_branch_name="$(git branch --show-current)"
local git_any_local_changes="$(git status --porcelain=v1 2>/dev/null)"
local git_stash_count="$(git rev-list --walk-reflogs --count \
refs/stash -- 2>/dev/null)" # Ignore error when no stashes
local git_info=""
if [ "${git_branch_name}" = "main" ]; then
git_info="${bold}${fg_green}${start_underline}${git_branch_name}${reset}"
else
git_info="${bold}${fg_green}${git_branch_name}${reset}"
fi
if ! [ -z "${git_any_local_changes}" ]; then
# Note that we have to be careful to put the exclamation mark
# in single quotes so that it is not expanded to the last command!
git_info="${git_info} ${bold}${fg_red}"'!'"${reset}"
fi
if [ "${git_stash_count:-0}" -gt 0 ]; then
git_info="${git_info} ${bold}${fg_yellow}${git_stash_count} in stash${reset}"
fi
printf "${git_info}"
}

# Now show a Debian style prompt with the git info above it.
PS1="\$(_git_info)\n\\[${bold}${fg_green}\]\u@\h:\[${fg_blue}\]\w\[${fg_white}\]\\$\[${reset}\] "
;;

Don't worry if you are not familiar with 'git', we will see it in a couple of chapters. The important thing is that this snippet shows that you can add almost any kind of information that you might find useful to your command prompt. When I run set_ps1 git, my prompt looks like this:

feat/chapter-26-customise-your-command-prompt ! 3 in stash
dwmkerr@effective-shell-ubuntu-20:~/repos/github/dwmkerr/effective-shell$

My prompt is now spread across two lines - the first shows me the branch I am on, a red exclamation point if I have made changes but not saved them, and the number of items I have in my 'stash'. The second line shows the standard Debian prompt.

The code shown above has been slightly simplified to make it more readable, you can see the exact version in the samples.

You can use the ~/effective-shell/scripts/set_ps1.sh file to build your own 'themes' and easily change between them in the shell.

If you want to always source this file into your shell on startup, just add the following like to ~/.bashrc:

source "~/effective-shell/scripts/set_ps1.sh"

You could also set the default PS1 variable immediately after sourcing the script if you like:

# Source the set_ps1 function and set our 'theme' to Debian.
source "~/effective-shell/scripts/set_ps1.sh"
set_ps1 "debian"

In the next chapter we will look at some sensible ways we can organise files like the set_ps1.sh script and the ~/.bashrc file so that we can easily manage our customisations and share them across different machines.

Additional Prompt Configuration​

There are some other variables that you might want to use to configure your prompt:

VariableDescription
PS2This is shown when performing 'continuation' and is normally set to >.
PS3This is shown when the select command is used and is normally not set, so the default #? is used.
PS4This is shown when tracing with set -x and is normally set to +.
PROMPT_DIRTRIMThis can be set to limit the number of directories shown with using \w or \W in your prompt.
PROMPT_COMMANDThis can be set to limit the number of directories shown with using \w or \W in your prompt.

Let's take a look at how each one can be used.

PS2

If we have a long line of text in the shell, we can start a 'continuation' by entering the backlash symbol:

$ echo "This is a really really \
> long \
> long line of text"
This is a really really long long line of text

The > symbol is shown when we press 'enter' after entering a \ backslash symbol. This symbol is used to remind us that we are not entering a new command, we are just continuing the current command on a new line. You can change the text shown by setting PS2.

PS3

PS3 allows you to specify the prompt used by the select command:

$ PS3="Your choice? : "
$ select fruit in Apples Pears; do echo "$fruit"; done
1) Apples
2) Pears
Your choice? :

The PS3 variable is not set by default. If it is not set, then the select statement uses #? for the prompt.

PS4

When you enable 'tracing' by setting the -x option, each traced line starts with a + symbol:

$ set -x
$ echo "The date is $(date)"
++ date
+ echo 'The date is Sun 06 Jun 2021 08:49:07 AM UTC'
The date is Sun 06 Jun 2021 08:49:07 AM UTC

You can change this symbol by setting the PS4 option.

PROMPT_DIRTRIM

If you set a value in the PROMPT_DIRTRIM variable, the shell will not show the entire contents of the working directory when you use the special \w sequence in a prompt variable. Instead, it will limit the number of directories shown to the value in PROPMT_DIRTRIM and use an 'ellipses' for the rest (and ellipses is written as three dots).

For example, if I was in the folder ~/effective-shell/logs/apm-logs and had PROMPT_DIRTRIM set to 2, then on Debian my command prompt would look like this:

dwmkerr@effective-shell-ubuntu-20:~/.../logs/apm-logs$

Note that only the last two parts of the path to the folder are shown.

PROMPT_COMMAND

The PROMPT_COMMAND variable can be used to specify a command or set of commands to run before the prompt is shown.

A common use for the PROMPT_COMMAND is to save and reload the shell command history before each command is run:

PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"

In this example, we used the history (display or manipulate history list) command three times. First with -a to append the lines from the current session to the history file, then -c to clear the shell history in the session, then -r to reload it.

For many shells the history of commands is only updated when the shell is closed, this change means that even if the shell is terminated unexpectedly, each command we have executed will still have been written to the history.

Z-Shell and Oh-My-Zsh​

Z-Shell does not use the same sequences to format the prompt-string variables. However, the set_ps1.sh script included in the Effective Shell samples will convert the Bash-style PS1 variable into Z-Shell formatted prompt strings automatically.

For Z-Shell users, you might also consider the very popular "Oh-My-Zsh" project. This is a collection of themes and plugins that add many more aliases, functions, autocompletions and more to the shell. One of the most popular features of "Oh-My-Zsh" is its large collection of themes that customise how the prompt looks.

However, just like with most things in computing, I would strongly recommend that you learn how the fundamentals work as they are described in this chapter before using "Oh-My-Zsh" themes. This will help you understand how things like "Oh-My-Zsh" actually work under the hood.

You might also realise that you don't need to install an additional package to get the styling you want. For example, my own shell prompt includes information on Git, the working directory (trimmed to only show up to three entries), but only requires a few lines of setup and works consistently in Bash-like shells and Z-Shell.

Enjoy playing around with the prompt customisation! It can be a lot of fun and the options are almost limitless!

Summary​

In this chapter we looked at how you can customise the command prompt with the PS1 variable, the shell's special sequences for useful information like \u for the current user, and how to configure the visual formatting of the prompt. We also looked at a script that makes configuring the command prompt a little easier to manage.

We've now seen quite a few ways to configure the shell, in the next chapter we'll look at some sensible practices that you can use to organise your shell configuration files.

To find all of the information on how to control the command prompt in the manual, run man bash and search for ^PROMPTING. For Z-Shell, run man zshmisc and search for PROMPT\ SEQUENCES.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-5-building-your-toolkit/index.html b/pr-preview/pr-346/part-5-building-your-toolkit/index.html new file mode 100644 index 00000000..36c5223f --- /dev/null +++ b/pr-preview/pr-346/part-5-building-your-toolkit/index.html @@ -0,0 +1,26 @@ + + + + + +Part 5 - Building Your Toolkit | Effective Shell + + + + + + + + + + + + + + +
+

Part 5 - Building Your Toolkit

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.

The goal of this part of the book is to equip you with the knowledge and techniques you need to build your own toolkit that you can grow over time, enabling you to manage your shell configuration across many machines, keep track of scripts and functions, and know how to customise the shell to suit the work you are doing.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-5-building-your-toolkit/managing-rempte-git-repositories/index.html b/pr-preview/pr-346/part-5-building-your-toolkit/managing-rempte-git-repositories/index.html new file mode 100644 index 00000000..00e8c153 --- /dev/null +++ b/pr-preview/pr-346/part-5-building-your-toolkit/managing-rempte-git-repositories/index.html @@ -0,0 +1,27 @@ + + + + + +Managing Remote Git Repositories and Sharing Your Dotfiles | Effective Shell + + + + + + + + + + + + + + +
+

Managing Remote Git Repositories and Sharing Your Dotfiles

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.

We'll use the popular "GitHub" site to host our repository. We'll see how we can manage remote changes and use GitHub to share our dotfiles, so that we can quickly setup any machine with our personal configuration.

If you want to follow along with the code, copy the version of the 'dotfiles' folder from the previous chapter to your home directory:

cp -r ~/effective-shell/repositories/chapter-27-dotfiles ~/dotfiles

This folder contains the Git repository with the exact set of changes made in Chapter 27.

Downloading the Samples

Run the following command in your shell to download the samples:

curl effective.sh | sh

Git Remotes​

So far all of the changes we have made are stored in a local Git repository. The repository's files are stored on your local machine in a folder named .git in the location where you initialised the repository. A remote is a repository that is on another machine.

You can push and pull your changes to and from a remote repository as a way to back-up your repository. You can share this remote with other people so that they can collaborate on it, or download it to other machines you work on.

There are a number of services you can use that allow you to host public repositories (which can be seen by anyone) and private repositories (which have more restricted access).

To see how remotes work I will show how to create a repository using the popular GitHub service. GitHub is free for individuals and is an extremely popular Git provider and online collaboration platform.

To create a remote, you will first need to sign-up for a GitHub account. When you have done that you will be offered the option to create a repository:

Screenshot of the GitHub &quot;What do you want to do first&quot; page showing the &quot;Create a repository&quot; option

Choose the 'Create a repository' option. You will then be asked to provide a name for your repository. I'm going to use this repository to host my dotfiles, so I have chosen "dotfiles":

Screenshot of the GitHub Create Repository page

If you don't want members of the public to be able to see your repository, choose the 'private' option.

If you already have a local repository then don't check any of the boxes under "Initialise this repository with" - we want to create an empty repository which we will then 'push' our changes to.

Once you have chosen the "Create Repository" option you will be shown some commands that you can use to configure your local repository to point to this newly created remote repository:

Screenshot of the repository setup page

We already have a repository, so we will follow the instructions in the section "...or push an existing repository from the command line". Copy the commands (there is a button that copies the command text to the clipboard) and run them in your shell, from the ~/dotfiles folder:

$ git remote add origin https://github.com/dwmkerr-effective-shell/dotfiles.git
$ git branch -M main
$ git push -u origin main

Username for 'https://github.com': dwmkerr+effective-shell@gmail.com
Password for 'https://dwmkerr+effective-shell@gmail.com@github.com':
Enumerating objects: 39, done.
Counting objects: 100% (39/39), done.
Delta compression using up to 16 threads
Compressing objects: 100% (36/36), done.
Writing objects: 100% (39/39), 12.83 KiB | 1.83 MiB/s, done.
Total 39 (delta 7), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (7/7), done.
To https://github.com/dwmkerr-effective-shell/dotfiles.git
* [new branch] main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

When you run the third command (the one that starts with git push) you will be asked for your username and password. Once you enter them your local changes will be 'pushed' to the remote repository.

{{< hint warning >}} +Avoid Using Passwords

To keep this example simple I have authenticated with a username and password. However, I would strongly recommend that as soon as possible you set up an SSH key to authenticate with GitHub. SSH keys are far more secure than usernames and passwords.

GitHub have an excellent guide on how to setup SSH keys at:

https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh

I also describe how to setup SSH keys in the chapter The Secure Shell.

:::

Let's look at each command in detail.

  • git remote add origin https://github.com/dwmkerr-effective-shell/dotfiles.git first we tell Git that we would like to add a new 'remote' called origin and we provide its address. The remote in this case is the repository we created in GitHub.
  • git branch -M main now we rename our current branch to main - because our current branch is already called main this shows no output as nothing has changed.
  • git push -u origin main finally, we 'push' our main branch to the remote named origin. The -u flag is used to track changes - we'll see what this means shortly.

At this stage if you refresh your browser you'll see your dotfiles repository, with all of our changes we've made so far!

Screenshot showing the initial view of the Dotfiles repository

Each of the files and folders we have created is shown, we can view any of the files, look at the commit history, see the log messages and more.

Before you make too many changes, we'll cover three important commands you need to be aware of when working with remotes - git push, git fetch and git pull.

The Git Push Command and Remotes​

We've already used the git push command once. This command pushes the changes we have made locally to a remote. It is a common convention to call the primary remote that you work with origin - but you can use any name you want. You can also have multiple remotes. For example, you could sign up with GitLab, another Git services provider, create a repository with their service and add that as a remote and call it gitlab.

You can show your remotes with the git remote (managed remote repositories) command. If we run the command with the -v (verbose) parameter each remote will be shown, along with the address used when we push changes, as well as the address used when we 'fetch' changes (which we'll look at next):

$ git remote -v
origin https://github.com/dwmkerr-effective-shell/dotfiles.git (fetch)
origin https://github.com/dwmkerr-effective-shell/dotfiles.git (push)

The command we used to push our changes was:

$ git push -u origin main

The -u (set upstream) option tells Git that we want to associate our local main branch with the remote main branch. This means that we don't need to specify the remote name for each subsequent git push command - Git knows that our 'upstream' branch that we push to is called main and is in the origin remote.

The Git Fetch Command​

The git fetch (get remote changes) command downloads all of the information about the changes that have been made to the remote.

This command does not download the actual changes to your working tree! All it does is download the information about the changes. To see what I mean, let's run the fetch command:

$ git fetch

There will be no output - the remote has not changed. Let's make a change to the remote so we can see how fetch works. Open the repository in the GitHub website. There's a message saying that we should add a 'README' file:

Screenshot showing the &#39;create readme&#39; file instructions

Press the "Add a README" button and add a description of your project. By convention, the file named README.md in a repository is shown on the home page of the project online and typically should include instructions on how to use the repository. This is a plain text file, you can use a plain text styling language called "Markdown" to show headings, bullets, code samples and so on (search for "GitHub Flavored Markdown" to find out about the syntax):

Screenshot of the README.md file content

Once you are happy with the content (you can choose 'Preview' to see how it will look) scroll down to the "Commit" button. Provide a commit message:

Screenshot of the GitHub README commit page

Once you have pressed "Commit New File" you will be taken back to the repository page. The contents of the README.md file will be shown:

Screenshot of the dotfiles repository homepage

We have now created a commit on the origin remote. Now when we run git fetch we will see that the remote has changed:

$ git fetch
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 962 bytes | 240.00 KiB/s, done.
From https://github.com/dwmkerr-effective-shell/dotfiles
2532277..4a28994 main -> origin/main

When we run git fetch, Git looks at the upstream associated with the current branch and checks to see if there are any changes. The information about any changes is downloaded - but the changes themselves are not yet downloaded. If you run git log you will not see the new commit that includes the README.md file - we have not checked out this commit yet.

At the moment, our repository looks like this:

Diagram of our local main and upstream main branch

  • The local branch HEAD is exactly where it was before we ran git fetch
  • Because we have run git fetch, Git knows that our upstream has changed - in fact it even told us what the changes are, the message includes the text 2532277..4a28994 main -> origin/main which means 'new commits from 2532277 to 4a28994 have been fetched for main which tracks origin/main

To actually download this (and any other) new commit we will need to run the git pull command.

The Git Pull Command​

The git pull (download from remote) command integrates the changes from a remote into the current branch. Because we've already told Git what the 'upstream' for the main branch is, we can just run the git pull command to move to the latest commit:

$ git pull
Updating 2532277..4a28994
Fast-forward
README.md | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 README.md

The git pull command tells us what commit we have moved from and to, and gives a summary of the files that have changed. We can see that a file named README has been created - if you look at your local files you'll see README.md is now present:

$ ls
README.md install.sh shell.d shell.sh

Finally, if we run git log --graph --oneline we can see that our HEAD is at the tip of the main branch, and that this is also the tip of the origin remote's main branch:

$ git log --graph --oneline
* 4a28994 (HEAD -> main, origin/main) add a simple 'README' file
* 2532277 Merge branch 'glog_alias'
|\
| * a8cbb15 (glog_alias) add the 'glog' alias
* | 31548e4 add the 'glog' alias
|/
* 138b404 Merge branch 'more_aliases'
|\
| * a51ae1a (more_aliases) add a file to store 'zsh' aliases
| * 63ea74f add a file to store 'bash' aliases
* | a95bd90 add the 'gm' alias for 'git merge'
|/
* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'
* f61369d (more_changes) add alias 'gs' for 'git status'
* d7e1bb9 add the 'shell.d' folder
* 01e7a10 add the 'install' and 'shell' scripts

Congratulations - you have now created a local repository, staged and unstaged changes to and from the index, created commits, created branches, handled merging and merge conflicts and even learnt how to setup a remote and push and pull changes from it!

You can pull any branch into your current branch - just provide the name of a remote and the name of a branch:

git pull <remote_name> <branch_name>

If you don't provide any parameters to git pull it will pull from the 'upstream' branch. But you can also use Git pull to merge other branches into your current branch.

Sharing Your Dotfiles​

If you have been following through with these examples, you will now have a 'dotfiles' repository available on your GitHub account. You can make this repository public and allow people to copy your code or propose changes.

If you want to clone your 'dotfiles' repository onto another machine, you can do so with this one-line command:

git clone git://github.com/<your_user_name>/dotfiles.git

The git clone (download a repository) command downloads the repository into the current folder. If your repository is private you will have to authenticate to be able to do this, but if your repository is public then you (or anyone else) can download your dotfiles.

Hosting your dotfiles on GitHub is a very convenient way of making your dotfiles accessible anywhere. With a single command you can download them to any machine, and then run the install.sh script from the dotfiles folder to setup the shell startup files.

If you search online for 'dotfiles', you will find many articles and users who have shared their dotfiles online, you can look over these for inspiration!

Forking and Pull Requests​

Forking and Pull Requests are features offered by popular Git hosting services like GitHub, BitBucket and GitLab. They are not actually Git features, but have become so widely used that you are likely to come across the terminology when working with online Git repositories.

A fork is a copy of a Git repository. Typically you will fork a repository if you want to take a copy of someone else's code and work on it yourself.

I have created a simple dotfiles repository on GitHub that you can use as a starting point for your own dotfile configuration. Let's use this to see how forking works.

First, we open up the GitHub project that we'd like to fork, which in this case is at www.github.com/effective-shell/dotfiles. Notice that there is a 'Fork' option on the top-right of the screen:

Screenshot of the effective-shell/dotfiles repository with the fork option shown

Choose the 'fork' option and GitHub will create a copy of the repository in your own account. You can now clone this repository, make changes and work on it as if it was your own. The original repository is tracked by GitHub, meaning that you can update from it at any time.

If you have made changes to a fork, you can then open a Pull Request. A pull request is a request to merge a set of changes from one branch into another, or from one fork into the original repository. So if you were to improve upon the dotfiles that you forked, and wanted to share your changes back, you could open a pull request to do so.

Typically when a pull request is opened, the project maintainer will then review the changes, make suggestions or discuss the proposal, and then either merge the pull request or reject it. In the screenshot below I am opening a pull request from my clone of the dotfiles repository to the original dotfiles repository, this pull requests adds an uninstall script to the repository. A maintainer of the target repository will then review the changes:

Screenshot of a pull request being opened

This model of forking and pull requests is really just a nice user interface on top of the underlying capabilities Git has to track remotes and manage branches. Services like GitHub offer functionality to discuss changes, run arbitrary pipelines project maintainers might create to test the code and so on.

GitHub has become a remarkably popular site for people to collaborate on projects together. At the time of writing the microsoft/vscode repository, which is the popular Visual Studio Code open-source editor, has had contributions from more than 19,000 individual contributors!

A Script to Open a Pull Request​

When you push a branch to a remote on GitHub, GitLab, BitBucket and a number of other Git services providers, a message is shown in the command prompt with a link to open a Pull Request:

$ git push -u origin fix/fix-shell-configuration
...
remote:
remote: Create a pull request for 'fix/fix-shell-configuration' on GitHub by visiting:
remote: https://github.com/dwmkerr/dotfiles/pull/new/fix/fix-shell-configuration
...

We can write a shell function that runs the git push command, reads its output, and then if it finds a web address, open it in a browser. In fact, the dotfiles repository at github.com/effective-shell/dotfiles has exactly this function - just run the command gpr to open a pull request!

This function is actually quite straightforward - the code for it is below (with some of the comments and code to colour the output removed for legibility):

gpr() {
# Get the current branch name, or use 'HEAD' if we cannot get it.
branch=$(git symbolic-ref -q HEAD)
branch=${branch##refs/heads/}
branch=${branch:-HEAD}

# Pushing take a little while, so let the user know we're working.
printf "Opening pull request for ${branch}...\n"

# Push to origin, grabbing the output but then echoing it back.
push_output=`git push origin -u ${branch} 2>&1`
printf "\n${push_output}"

# If there's anything which starts with http, it's a good guess it'll be a
# link to GitHub/GitLab/Whatever. So open the first link found.
link=$(echo ${push_output} | grep -o 'http.*' | head -n1 | sed -e 's/[[:space:]]*$//')
if [ ${link} ]; then
printf "\nOpening: ${GREEN}${link}${RESET}..."
python -mwebbrowser ${link}
fi
}

This snippet first gets the name of the current branch, or HEAD if we cannot work the name out. When then run the git push origin command, and record the output of the command into a variable.

Once the push command has completed, we write its output to the screen, and search for the first hyperlink, using grep and sed, then use python to open the link in a browser.

I use the gpr function many times per day and it has been a real time-saver!

Showing Git Information in the Command Prompt​

In Chapter 26 - Customising Your Command Prompt we saw how to customise the command prompt by setting the PS1 variable. As you start using Git more you might find it convenient to show some information about the repository in your prompt.

As an example, while I am writing this chapter, my command prompt looks like this:

github/dwmkerr/effective-shell feat/managing-git-remotes ! 1 in stash
$

This is easier to see in the screenshot below as it shows the colour:

Screenshot showing my PS1 line in with Git information in colour

I spread my prompt over two lines and leave the prompt indicator as the only thing on the line where I type. This means my cursor does not become too indented. My command prompt shows the following information:

ContentDescription
github/dwmkerr/effective-shellMy current folder, and up to two parent folders.
feat/managing-git-remotesMy current Git branch, if I am in a Git repository.
!A red exclamation mark is shown if I have uncommitted changes.
1 in stashIf I have anything in my Git stash, a message is shown in amber.

You can try out this style of command prompt by calling the set_ps1 function:

source ~/dotfiles/shell.d/set_ps1.sh
set_ps1 dwmkerr

If you want to change back to your previous prompt, just run set_ps1 again without any parameters.

If you are interested in showing Git information in your command prompt you can take a look at the set_ps1.sh file to see the full details. We'll take a look at a snippet as it is interesting to see how to get this Git information.

_git_info() {
# Don't write anything if we're not in a folder tracked by git.
if ! [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == "true" ]
then
return
fi

# Get the branch name, changes, and number of stashes.
local git_branch_name="$(git branch --show-current)"
local git_any_local_changes="$(git status --porcelain=v1 2>/dev/null)"
local git_stash_count="$(git rev-list --walk-reflogs --count \
refs/stash -- 2>/dev/null)" # Ignore error when no stashes
local git_info=""
if [ "${git_branch_name}" = "main" ]; then
git_info="${fg_green}${start_underline}${git_branch_name}${reset}"
else
git_info="${fg_green}${git_branch_name}${reset}"
fi
if ! [ -z "${git_any_local_changes}" ]; then
# Note that we have to be careful to put the exclamation mark
# in single quotes so that it is not expanded to the last command!
git_info="${git_info} ${fg_red}"'!'"${reset}"
fi
if [ "${git_stash_count:-0}" -gt 0 ]; then
git_info="${git_info} ${fg_yellow}${git_stash_count} in stash${reset}"
fi
printf "${git_info}"
}

This script does the following:

  • The git rev-parse is used to check whether we are in a folder that is part of a Git working tree. If we are not in a Git working tree, we don't show anything.
  • Get the current branch name with git branch --show-current).
  • Check to see if there are any changes by looking at git status. The --porcelain=v1 line ensures that the status is machine-readable (this is easier to work with when scripting).
  • Check the number of 'stash' revisions. Don't worry about stashes for now or the git rev-list command - these are more advanced features.

The rest of the script is formatting only - underlining the branch name if it is the main branch, showing the exclamation mark if we have changes and so on.

You can take the script as a starting point for your own customisations for the command prompt if you will be using Git a lot!

Scratching the Surface​

We have only seen the absolute basics of Git in this chapter. Git is an amazingly powerful tool, I cannot recommend enough that you take the time to really learn how the commands work.

Many users will use a graphical tool to work with Git - this is perfectly fine if it works for you. But to be an effective shell user you should really spend the time getting familiar with the core Git commands using the command-line.

Git can sometimes seem overwhelming to people and has a reputation for being complex. This is somewhat unfair - version control of files is itself an inherently complex topic. No matter what tool you use, there will always be the challenges of managing changes across environments, dealing with conflicts, integrating work and so on. The basic Git functionality is incredibly good at making 99% of this work simple and straightforward, and Git gives you the tools to make the other 1% at least manageable.

Spend some time getting familiar with the core Git commands that we have introduced in this chapter. As you find yourself becoming more familiar, here are the next set of topics I would recommend learning about:

  • The gitignore file: this special file can be used to tell Git not to track changes for certain files.
  • The .gitconfig file: this is Git's own dotfile and can be used to fine-tune Git configuration.
  • Tags: these are labels you can add to commits, which are ideal for tracking releases for projects or other metadata.
  • Diffs: knowing how to use git diff to see changes between branches, commits, the index and working tree and more will certainly help you as you use Git more.
  • Stashes: If you want to save your changes, but they are currently not ready to be committed (perhaps because they are only partially complete), you can use the git stash command to store working tree changes. This lets you store the changes away, then checkout other branches, then restore the changes later when you are ready.
  • Git Clean: The git clean command is very useful to help you remove unneeded files from your working tree.
  • Interactive Staging: You can interactively stage files, parts of files (called 'hunks') or even individual lines directly from the shell, this can be invaluable when making sure that exactly the right changes are going into the index.
  • Patch staging or checkout: I probably use the git add -p command to 'patch' changes dozens of times a day, this is my preferred mechanism of reviewing my changes as I stage them.
  • Merge Strategies: Understanding how 'squashing' works (and its drawbacks) can be very useful when working with branches. Merge strategies are a useful topic to go deeper on.
  • Rebasing: Rebasing can be used as a merge strategy but can also help in other scenarios, I would definitely recommend learning about rebasing if you have multiple people working on a repository, it can save a lot of trouble when integrating complex changes.
  • Commit and Tag signing: Great for security sensitive users, you can use special keys to 'sign' your commits and improve the security of your repositories.
  • GitHub Flow: A common workflow used with GitHub projects.

There are numerous articles and online books on Git - I also recommend the excellent book "Pro Git" by Scott Chacon and Ben Straub.

An Overview of the Git Commands​

Before we finish the chapter, let's do a quick review of the key commands for working with Git. You can refer to the illustration below:

Diagram showing a summary of the key commands

As a reminder, the core concepts are:

ConceptDescription
The Working TreeThe folder you are working in and tracking change to.
The IndexThe 'staging' area for building commits.
The RepositoryThe full set of all commits, branches and metadata.
A ForkA copy of an entire repository, including its history and all branches.
A Pull RequestA proposal to merge one branch into another, or a branch in a fork to the upstream.
git initCreates a local repository.
git cloneDownloads a remote repository.
git addStage a change from the working tree to the index.
git resetUnstage a change from the index.
git commitCreate a new commit.
git checkout -bCreate a new branch.
git mergeMerge a branch into the current branch.
git checkoutMove the current HEAD to a new branch or commit.
git pushPush changes to an upstream branch.
git fetchRetrieve information about changes to a remote.
git pullDownload and merge changes from a remote.

Summary​

In this chapter we learned how to use GitHub to host a remote repository, how to push, fetch and pull changes, and how remotes work. We also looked at pull requests, forks, how to show Git information on the command line, and some of the more advanced topics that you might want to explore as you use Git more.

Although we've only scratched the surface of what Git can do, you should now have the tools to work with repositories, share content like your dotfiles, collaborate with others and manage your own changes.

In the next part of the book we will look at some advanced techniques that can help you as a shell user.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-5-building-your-toolkit/managing-your-dotfiles/index.html b/pr-preview/pr-346/part-5-building-your-toolkit/managing-your-dotfiles/index.html new file mode 100644 index 00000000..845b9723 --- /dev/null +++ b/pr-preview/pr-346/part-5-building-your-toolkit/managing-your-dotfiles/index.html @@ -0,0 +1,26 @@ + + + + + +Managing your Dotfiles | Effective Shell + + + + + + + + + + + + + + +
+

Managing your Dotfiles

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.

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.

Z-Shell

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.

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 ~/effective-shell/dotfiles folder.

Downloading the Samples

Run the following command in your shell to download the samples:

curl effective.sh | sh

Dotfiles​

Any file or folder on your system that starts with a . 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 ls, unless you provide flags such as -a (show all files and folders) flag. In desktop environments such as Gnome, KDE and MacOS X dotfiles are also hidden by default.

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.

You will mostly see dotfiles in your 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 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.

As an example, a user's personal Bash configuration is stored in ~/.bashrc, but the global Bash configuration applied to all users is stored in /etc/bash.bashrc. The second configuration file does not need a dot in front of it - the /etc folder is where configuration is kept so there is no need to differentiate it from other files like a user's personal files.

Nowadays, when a user says "my dotfiles", they typically mean their 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 ~/.bashrc for your shell configuration, ~/.ssh/config for your SSH configuration and so on.

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.

The Default Shell Dotfile​

On many platforms the default ~/.bashrc file will contain a number of customisations out-of-the-box.

Let's take a look at the ~/.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 ~/.bashrc file the contents may be different as it will vary from distribution to distribution:

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

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.

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

The shopt -s globstar command has been commented out, so that users can quickly remove the comment symbol to enable pathname expansion across subdirectories.

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color|*-256color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi

if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

This rather complex looking code determines whether the shell supports colour, and if so, updates the command prompt appropriately1.

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'

alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi

If the shell supports colour, then aliases are used so that common commands like ls will show their output in colour.

# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# Add an "alert" alias for long running commands. Use like so:
# sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi

More aliases are added as shortcuts for useful commands. We also are sourcing the ~/.bash_aliases file if it exists.

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.

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.

Creating a Dotfiles Folder​

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 source it from the ~/.bashrc file.

The other great thing about keeping your shell configuration in its own file is that you can then use it for 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.

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.

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!

First, let's start by creating a sensible location for all of our per-user personal configuration files, a folder called ~/dotfiles:

mkdir ~/dotfiles

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.

Creating a Shell Dotfile​

Rather than changing the system-provided ~/.bashrc file to contain all of our customisations, let's create our own shell configuration file in the dotfiles folder:

touch ~/dotfiles/shell.sh

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:

  • I have not put a dot in front of the name! This is because within the ~/dotfiles folder I don't actually want this file to be hidden - if I am looking in the ~/dotfiles folder I want to see this file
  • I have not used the name of a shell program in this file - this is because I will make this file work with any shell that I regularly use - so whether I am using zsh, bash or sh, this file should still be able to be loaded
  • I have put .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

Now let's edit the ~/dotfiles/shell.sh file to add some configuration that might be useful for our shell:

# Note: there is no shebang in this script. This script sets my preferred shell
# configuration and should be able to be sourced from any Bash-like shell or
# from Z shell.

# If we are not running interactively do not continue loading this file.
case $- in
*i*) ;;
*) return;;
esac

We'll start the file with a comment that clearly explains why this file does not have a 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 $- parameter) to see if the i (interactive) parameter is set. If the interactive parameter is not set then we call return to stop loading the script.

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).

Next, let's set our preferred editor:

# Set our editor. Some tools use 'VISUAL', some use 'EDITOR'.
VISUAL=nano
EDITOR=nano

There are two variables are used by the shell and command line programs to run an editor. The first, and original, variable was EDITOR. This was originally often a 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 VISUAL variable was used to specify the editor that could be used for 'full screen' terminal editing. Some programs use EDITOR and some use VISUAL so it is best to set both.

I have used the nano editor in this example as it available on many distributions and is a little easier than vi or emacs, but you can use whatever you like. For my personal dotfiles I use vi.

At this stage you can start to go a bit over the top - for example here's an alternative way to set the editor:

# Set our preferred editor to the first available editor in the array below.
preferred_editors=(nano vi)
for editor in ${preferred_editors[@]}; do
if command -v "${editor}" >/dev/null 2>&1; then
# Note that 'VISUAL' can be a full screen terminal editor. On legacy
# systems 'EDITOR' was normally a line mode editor but there is
# generally no need to differentiate any more.
VISUAL="$(command -v ${editor})"
EDITOR="${VISUAL}"
break
fi
done
unset editor preferred_editors

In this method we specify an array of editors, go through each one, check to see if it exists2, 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!

You'll notice that in the ~/effective-shell/dotfiles/shell.sh folder I 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.

Another useful option to set is stty -ixon:

# Allow us to use Ctrl+S to perform forward search, by disabling the start and
# stop output control signals, which are not needed on modern systems.
stty -ixon

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.

Now let's set some sensible settings for working with folders:

# Set a shell option but don't fail if it doesn't exist!
safe_set() { shopt -s "$1" >/dev/null 2>&1 || true; }

# Set some options to make working with folders a little easier. Note that we
# send all output to '/dev/null' as startup files should not write to the
# terminal and older shells might not have these options.
safe_set autocd # Enter a folder name to 'cd' to it.
safe_set cdspell # Fix minor spelling issues with 'cd'.
safe_set dirspell # Fix minor spelling issues for commands.
safe_set cdable_vars # Allow 'cd varname' to switch directory.

# Uncomment the below if you want to be able to 'cd' into directories that are
# not just relative to the current location. For example, if the below was
# uncommented we could 'cd my_project' from anywhere if 'my_project' is in
# the 'repos' folder.
# CDPATH="~:~/repos"

If we run this script on an older shell, some of these options might not be present. This is why we have created a safe_set function that won't fail if the shopt function fails and will pipe any output to /dev/null. You can use these settings or remove them, it's really up to you. Each one is described below:

SettingDescription
autocdAllows you to simply type a directory name or path and press enter to cd to it.
cdspellWhen running commands like cd dirname, have the shell fix minor typos.
dirspellWhen running commands like cat dirname/test, have the shell fix minor typos.
cdable_varsIf you create a var like repos="$HOME/repos, then cd repos to move into it.

I have also left a comment on how you can use the CDPATH shell variable to allow you to 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.

Finally, let's set some common shell history options:

# Configure the history to make it large and support multi-line commands.
safe_set histappend # Don't overwrite the history file, append.
safe_set cmdhist # Multi-line commands are one entry only.
PROMPT_COMMAND='history -a' # Before we prompt, save the history.
HISTSIZE=10000 # A large number of commands per session.
HISTFILESIZE=100000 # A huge number of commands in the file.
# HISTCONTROL="ignorespace:ignoredup" # Ignore starting with space or duplicates?
# export HISTIGNORE="ls:history" # Any commands we want to not record?
# HISTTIMEFORMAT='%F %T ' # Do we want a timestamp for commands?

These shell options and variables can be used to fine-tune how the history works. Here's a description of each one:

SettingDescription
shopt -s histappendWhen we write to the history file, append entries, don't overwrite the old file.
shopt -s cmdhistIf we have a multi-line command, record it as one entry, not one per line.
PROMPT_COMMANDBefore we show the PS1 prompt, update the history file.
HISTSIZEStore up to 10000 items in the history for the current session.
HISTFILESIZEStore up to 100000 items in the history file for all sessions.
HISTCONTROLUncomment to ignore commands that start with a space, or ignore duplicated commands.
HISTIGNOREUncomment to not record certain commands in the history.
HISTTIMEFORMATUncomment to keep a date and time next to each command in the history file.

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.

Now let's look at how we could test this file, before we actually source it.

Testing the Shell Dotfile​

Before we 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!

From your shell, just run the command below:

$ sh -iex ~/dotfiles/shell.sh
+ case $- in
+ EDITOR=vi
+ VISUAL=vi
+ safe_set autocd
+ shopt -s autocd
...

What we have done is run a shell program, in this case the sh program, and passed the following flags:

  • i - this makes the shell interactive - our script only runs in interactive shells so we need this to test it!
  • e - this causes the shell to exit if a command fails
  • x - this sets the tracing output

By running this script in a shell this way we can see 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 bash or zsh.

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.

Sourcing the Shell Dotfile​

Now that we have a working shell dotfile, we can source it as part of our shell startup.

Rather than having our shell startup file know about our ~/dotfiles folder, we will create a symlink to the shell script from our home directory:

Finally, we can create a symlink in our home directory that points to our ~/dotfiles/shell.sh file and we are good to go!

$ ln -sf "$HOME/dotfiles/shell.sh" "$HOME/.shell.sh"

Note that in this example we used the ln -sf command to create a symlink, the -s flag ensures we create a normal symlink (rather than a 'hard' link) and the -f flag forces the creation of the link by overwriting any link that already exists.

Now all we need to do is add the following lines to our ~/.bashrc (or for Z-Shell, ~/.zshrc file):

# Source our shell configuration if it exists.
[ -r ~/.shell.sh ] && source ~/.shell.sh

This command uses the -r (does file exist and is it readable) test to check whether we have a ~/.shell.sh file and if it exists, sources it.

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.

Sourcing Files from a Folder​

A common pattern with Linux and Unix systems is to allow multiple configuration files to be stored in a folder. A convention is to have a folder with the letters .d at the end, to differentiate between a single configuration file and a configuration folder.

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.

Here are some common examples:

Configuration FileConfiguration DirectoryNotes
/etc/crontab/etc/cron.dConfiguration for scheduled tasks.
/etc/bash_completion/etc/bash_completion.dConfiguration for Bash auto-complete.
/etc/sudoers/etc/sudoers.dConfiguration for super-users.

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 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 PS1 variable in our shell configuration.

First, let's make a directory to hold our shell configuration files:

$ mkdir ~/dotfiles/shell.d

Now let's copy over our ~/effective-shell/scripts/set_ps1.sh file:

$ cp ~/effective-shell/scripts/set_ps1.sh ~/dotfiles/shell.d

Now let's update our shell.sh file to source all of the files in the ~/.shell.d folder:

# If we are not running interactively do not continue loading this file.
case $- in
*i*) ;;
*) return;;
esac

# Source any files in our ~/.shell.d folder.
if [ -x ~/.shell.d ]; then
for shellfile in ~/.shell.d/*; do
[ -r "$shellfile" ] && source "$shellfile"
done
unset shellfile
fi

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 -x test), and then we loop through each file in the directory. If the file can be read (using the -r test) then we source it.

At the end of the shell.sh file we can now call the set_ps1 function to set our theme:

# Set the theme. Do not fail if the function doesn't exist.
set_ps1 "debian" || true

Finally, let's create a symlink in our home directory for the shell configuration files:

$ ln -sf "$HOME/dotfiles/shell.d" "$HOME/.shell.d"

At this stage we've now successfully created a dotfiles folder to store our configuration, symlinks in our $HOME directory that point to our dotfiles and we have also updated our ~/.bashrc or ~/.zshrc to load our shell configuration.

If you want to see the new links you've created you can run the ls command just like so (I've abbreviated the output to make it more readable):

$ ls -al ~ | grep shell
lrwxr-xr-x dwmkerr .shell.d -> /home/dwmkerr/dotfiles/shell.d
lrwxr-xr-x dwmkerr .shell.sh -> /home/dwmkerr/dotfiles/shell.sh

A Dotfile Install Script​

The manual steps we performed to setup the links for our dotfiles can be easily run using a shell script.

The script below shows how we can easily setup the links to the dotfiles, and source the appropriate files from our shell configuration:

#!/usr/bin/env sh

# This script installs the dotfiles locally. Note that it should be run from the
# dotfiles folder so that the links are set properly!

# Create links for the shell configuration.
ln -sf "$PWD/shell.sh" "$HOME/.shell.sh"
ln -sf "$PWD/shell.d" "$HOME/.shell.d"

# Source our shell configuration in any local shell config files.
config_files=(~/.bashrc ~/.zshrc)
for config_file in ${config_files[@]}; do
# Skip config files that don't exist.
[ -r "${config_file}" ] || continue

# If we don't have the 'source ~/.shell.d' line in our config, add it.
source_command="[ -r ~/.shell.sh ] && source ~/.shell.sh"
if ! grep -q "${source_command}" "${config_file}"; then
echo ".shell.sh is not sourced in '${config_file}' adding this now..."
echo "${source_command}" >> "${config_file}"
fi
done

This script creates the symlinks to our dotfiles and loops through a set of shell configuration files, adding a line to source the ~/.shell.sh in the configuration file if it doesn't exist.

Note how we use the grep -q command to search through the shell configuration file to see if the line that sources our dotfile exists. The grep command returns 0 if it finds a result and 1 otherwise, meaning we can easily use it in an 'if' statement

This script can be run from the dotfiles folder like so:

$ cd ~/dotfiles
$ ./install.sh
.shell.sh is not sourced in '/home/dwmkerr/.bashrc' adding this now...

And that is it - we now have a ~/dotfiles folder with our configuration, a sensible set of options for the shell, and the ability to quickly configure our dotfiles for different shells.

The dotfiles that we have a created are available in the ~/effective-shell/dotfiles folder from the samples. The install script shown above is also in that folder.

Summary​

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.

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.


  1. If you are curious, the debian_chroot variable is set when you are running as a user that has run the chroot (change root) command. The 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. chroot is an advanced topic and out of the scope of this book, but the debian_chroot command in the PS1 variable is used to help make it clear when running a shell if you are in a 'changed root' environment.↩
  2. For a reminder on how to check whether a command is available, see Checking for Installed Programs in Chapter 23 - Useful Patterns for Shell Scripts.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-6-advanced-techniques/a-vim-crash-course/index.html b/pr-preview/pr-346/part-6-advanced-techniques/a-vim-crash-course/index.html new file mode 100644 index 00000000..9bc51032 --- /dev/null +++ b/pr-preview/pr-346/part-6-advanced-techniques/a-vim-crash-course/index.html @@ -0,0 +1,26 @@ + + + + + +A Vim Crash Course | Effective Shell + + + + + + + + + + + + + + +
+

A Vim Crash Course

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.

In this chapter we'll see why we might want to use or at least become familiar with Vim, and show just a few features that might excite you enough to want to dive in and learn more!

What is a Terminal Editor​

A Terminal Editor is any text editor that is designed to run inside a terminal, often from a shell. This means it is designed to be used without a mouse, on a smaller screen, and on a system that might have no graphical user interface or windowing or desktop environment.

Some of the most popular terminal editors you might have heard of are GNU nano, Vim and Emacs. Let's take a look at why we might want to know how to use a terminal editor.

Why use a Terminal Editor?​

Many technologists will be familiar with Graphical Text Editors, such as Visual Studio Code, Atom and Sublime. Software engineers will likely be familiar with Integrated Development Environments such as Visual Studio or the JetBrains family of IDEs which include dedicated environments for many languages, such as IntelliJ IDEA for Java.

So, with such a selection of powerful and user-friendly text editing options available, why would you choose to use a Terminal Editor?

There are many reasons, but perhaps the most compelling one is that you will not always have a graphical environment available! If you are working on a remote server using the ssh tool as described in Chapter 31 - The Secure Shell, you will not have a desktop or windowed environment to run a graphical tool. The only options available for you to edit text, code or scripts in such an environment are terminal editors - because the terminal is your only interface to the server.

There are other times where you might not have easy access to a graphical environment - for example if you are editing a file in a Docker container.

There are some other reasons that being able to use a terminal editor is powerful:

Maintaining flow - if you are working in a shell already and need to edit text, being able to quickly do so without leaving the shell, and ideally without even having to touch the mouse, will allow you to maintain your 'flow' while you work.

Speed of editing - editors such as Vim and Emacs allow you to be incredibly efficient at editing text, once you get over the initial learning curve. Vim in particular is designed to try and keep your fingers on the home row of the keyboard as much as possible to make it possible to manipulate text very quickly.

Customisation - terminal editors can be customised to suit your working style - you can then manage your customisations through dotfiles that you share across environments as described in Chapter 26 - Managing Your Dotfiles.

Improving Efficiency in Graphical Editors and other tools - if you learn how to use Vim or Emacs and find yourself able to edit text very efficiently using their idioms, then you can install plugins in your graphical editors or IDEs, allowing you to edit text using the same commands. There are even browser plugins that let you navigate webpages using Vim style movement commands.

All in all I believe it is extremely useful to get to grips with at least the basics of Vim - you will be more efficient when working with remote machines, when quickly editing text from the shell, and might even find it replaces your other tools for many tasks.

Introducing Vim​

I have chosen Vim as it has the reputation for being the most complex of the terminal text editors to work with, but actually with an hour or two of practice you'll find that it is quite straightforward to use for simple tasks. With just a little knowledge you'll be effective - and as you invest more time in learning you'll become increasingly efficient.

Vim is installed on most Linux distributions out of the box and is an extremely popular editor. But it has a style of editing, called Modal Editing that can be a bit confusing at first.

What is Modal Editing?​

Vim is a modal text editor. This means that it runs in different 'modes' - and in each mode the keyboard has different functions. For example, when you are in Command Mode the keyboard is used to enter sequences that manipulate text or move the cursor. In Insert Mode your keyboard is used just like with a normal text editor - typing edits the text on the screen.

First, we'll open Vim by running the vi command:

$ vi

β–ˆ
~
~
~
~
~ VIM - Vi IMproved
~
~ version 8.2.4314
~ by Bram Moolenaar et al.
~ Modified by <bugzilla@redhat.com>
~ Vim is open source and freely distributable
~
~ Help poor children in Uganda!
~ type :help iccf<Enter> for information
~
~ type :q<Enter> to exit
~ type :help<Enter> or <F1> for on-line help
~ type :help version8<Enter> for version info
~
~
~
~
~
0,0-1 All

When Vim is started it either displays the file that it has been asked to open or if no file has been provided it shows the welcome message above.

Vim starts in Command Mode. This means that the keys on the keyboard are not used to enter text - they are used to enter commands. One command that is going to be useful is the 'insert' command, which tells vim to enter Insert Mode, before the cursor. The position of the cursor in the code block above is shown with a full-block β–ˆ symbol.

Let's insert some text. Enter the characters in the first column of the table below - the output should match what is shown on the right:

KeystrokesOutput
i

Enter insert mode
 
Hello Vim!

Enter text
Hello Vim! 

The i command tells Vim to enter Insert Mode. Insert mode is the mode that will be most familiar to you - when Vim is in insert mode your keystrokes enter text in the current file. When Vim is in insert mode it behaves more like a non-modal editor. We can enter some text as shown above.

You might have noticed that when you entered insert mode Vim updated its status bar to show you what mode it is in:

Hello Vim!β–ˆ
~
~
~
-- INSERT -- 1,11 All

Vim will show you what mode you are in on the status line at the bottom of the screen. Vim's default mode is 'Command Mode' - when Vim is in command mode the mode is not shown - it's what you assume Vim is in unless it tells you otherwise.

To return to Command Mode, press Control and 'C', or 'Escape'. In Vim's documentation a 'chord', which is more than one key pressed at the same time, is shown in angled brackets, to differentiate it from a 'sequence' which is a set of keystrokes pressed one after the other. Control and C is written as <C-c> in the documentation for Vim. We will use the same style to describe chords and sequences.

Let's exit insert mode:

KeystrokesOutput
<C-c>

Command mode
Hello Vim!

You will notice that the -- INSERT -- text in the status bar is no longer shown - meaning we are back in Command Mode.

Let's exit Vim. When we are in Command Mode, the keystrokes when enter are used to manipulate text or move around. If we want to perform an administrative task, such as saving a file or calling another program, we use what is called an Ex Command. Ex is short for 'execute'. When we press the colon : keystroke, we enter 'Ex Mode'. This mode is a bit like Insert Mode, but for writing commands:

KeystrokesOutput
:

Start ex command
: 
q!

Quit without saving
:q! 

Keystrokes we enter after entering Ex mode will show in the status line, showing the text for the command we are building. At this point the status bar for Vim will look like this:

Hello Vim!
~
~
~
:q!β–ˆ

Vim is showing us that our current command is q!. This command translates to 'quit without saving'. Press the Enter key to execute the command - Vim will now close.

We have seen the basics of modal editing - showing Vim's Command Mode, Insert Mode and Ex Mode. We can also close Vim - which is famously a task that people who haven't used Vim before struggle with! Now let's start building our own Vim Cheatsheet - using Vim - to show more of what it can do!

Building a Vim Cheat Sheet​

We're going to build a Vim Cheatsheet - this will be a great way to show what Vim can do at the same time as documenting our learnings as we go along. You'll then be able to extend your personal Cheatsheet over time with the commands that you find useful.

Let's start by creating a folder to keep the cheat sheet in and initialing an empty Git Repository in the folder so that we can track changes to the cheat sheet. If you need a reminder on how Git works, check Chapter 27 - Controlling Changes with Git.

First we'll create a new folder, move into it and then initialise an empty Git repository with the main branched named main:

$ mkdir ~/vim-cheatsheet`
$ cd ~/vim-cheatsheet
$ git init -b main
Initialized empty Git repository in /home/dwmkerr/vim-cheatsheet/.git/

Creating a File in Vim​

We now have a new repository ready to hold our cheatsheet. Let's open Vim and tell it that we want to work on a file named cheatsheet.md:

$ vi cheatsheet.md

β–ˆ
~
~
"cheatsheet.md" [New] 0,0-1 All

The text we are editing in Vim is called a buffer. A buffer can be thought of as a view on a file - when we open a file we load the context into a buffer - it is this buffer that Vim shows. When we want to create a new file, we enter text in a new buffer and then save it when we are ready.

Vim is showing us in the status line that we have a buffer named cheatsheet.md and that this is a 'New' buffer - it has not been saved.

The extension md at the end of the file is short for 'Markdown'. Markdown is a plain text format that is great for documentation. You write text as normal - you can also use some special characters such as Hash # to format things like headings, or dash - for bullets. Markdown will be rendered in a very reader-friendly way when we look at on things like GitHub.

Let's enter insert mode, and create a title for our cheatsheet, then exit insert mode.

KeystrokesOutput
i

Enter insert mode
 
# Vim Cheatsheet

Enter text
# Vim Cheatsheet 
<C-c>

Enter command mode
# Vim Cheatsheet

Now let's use the w (write) ex-mode command to save the file. Enter :w<Enter> - the Vim status line will to tell us it has written the buffer to disk:

# Vim Cheatsheet
~
~
~
"cheatsheet.md" [New] 1L, 17B written 1,16 All

Exit Vim by entering :q<Enter>. Let's add this new cheatsheet file to our repository:

$ git add cheatsheet.md

Let's tell our shell to use Vim as our text editor. We're going to use Vim to work with Git for the duration of this chapter. When you are more familiar with Vim, being able to work with Git repositories and commands without leaving the shell and using Vim as the editor will start to feel extremely efficient:

$ export EDITOR=vi
$ git commit

β–ˆ
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Committer: dwmkerr
#
# On branch main
#
# Initial commit
#
# Changes to be committed:
# new file: cheatsheet.md
#
~
~
"~/vim-cheatsheet/.git/COMMIT_EDITMSG" 13L, 325B 1,0-1 All

Git has opened Vim to ask us to provide a commit message. Git has also provided some helpful information on the changes we are committing - it's told us that we have a new file called cheatsheet.md in the commit. Let's use Vim to enter a commit message:

KeystrokesOutput
i

Enter insert mode
 
add the cheatsheet

Enter text
add the cheatsheet 
<C-c>

Enter command mode
add the cheatsheet

Now type :wq to write and quit - Git will use the message we have provided for the commit:

 1 file changed, 1 insertion(+)
create mode 100644 cheatsheet.md

We've used Vim to create the initial cheatsheet file as well as to quickly set the Git commit message - all without leaving our shell!

Vim Motions and Movement Commands​

Vim commands that move the cursor are called 'Motions'. Understanding how motions work is essential to becoming effective at moving around Vim buffers.

Let's open our cheatsheet in Vim:

$ vi cheatsheet.md

Now update the file so that it looks like the version below, this will give us some text to play with:

# Vim Cheatsheet

## Vim Motions

Vim motions are commands used to move the cursor.

Now that we have this text, let's see some of the motion commands in action.

The table below shows what I think are the most essential Vim motions:

MotionUsageMotionUsage
ggGo to beginning of buffer.hGo left.
GGo to end of buffer.jGo down.
0Go to beginning of line.kGo up.
$Go to end of line.lGo right.
wGo forwards one word.)Go forwards one sentence.
bGo backwards one word.(Go backwards one sentence.

Let's see a few of these motions in action. Make sure to use <C-c> to exit Insert Mode and enter Command Mode before entering the keystrokes below:

KeystrokesOutput
gg

Go to beginning of buffer
## Vim Motions

## Vim Motions
l

Go right
## Vim Motions

## Vim Motions
h

Go left
## Vim Motions

## Vim Motions
j

Go down
## Vim Motions

## Vim Motions
k

Go up
## Vim Motions

## Vim Motions

The first motion is gg - go to the beginning of the buffer.

The next four motions are hjkl - move left, down, up and right. Vim uses these keys as they are all next to each other and on the home row for the right hand. Although it takes a little getting used to, once you use hjkl instead of arrow keys to move around you'll wonder how you lived without them - being able to navigate without moving your right hand from the home row lets you navigate text incredibly quickly.

We can use the 0 and $ motions to go to the beginning and end of a line:

KeystrokesOutput
0

Go to beginning of line
## Vim Motions
$

Go to end of line
## Vim Motions

The w and b motions move backwards and forwards by one word:

KeystrokesOutput
0

Go to beginning of line
## Vim Motions
w

Go forward one word
## Vim Motions
b

Go to back one word
## Vim Motions

Vim Command Counts​

Many Vim commands can be provided with a 'count', which indicates how many times the command should be run. This makes motions far more flexible - instead of pressing jjjjj to move down five lines, we can just press 5j.

Let's see how the cursor moves when we add counts to motion commands:

KeystrokesOutput
gg

Go to beginning of buffer
## Vim Motions

## Vim Motions
4j

Go down four lines
## Vim Motions

## Vim Motions
3w

Go forwards three words
## Vim Motions

## Vim Motions

Vim Text Insert Commands​

Now that we know how to move the cursor with the movement commands, we can move to where we want to insert text and use the i command to enter Insert Mode. However, Vim has a set of commands that enter Insert Mode in specific positions - and these can save us moving around unnecessarily!

The most essential 'enter insert mode' commands are:

CommandDescription
iInsert at cursor.
IInsert beginning of current line.
aAppend text after the cursor.
AAppend text at the end of the current line.
oOpen a new line below the position.
OOpen a new line above the current line.

These commands make it much faster to quickly enter Insert Mode just where you like. Let's see some of them in action:

KeystrokesOutput
gg

Go to beginning of buffer
## Vim Motions
o

Open line below current line
# Vim Cheatsheet
Hello<C-c>

Enter text, enter command mode
# Vim Cheatsheet
Hello
I

Insert at beginning of line
# Vim Cheatsheet
Hello
Welcome and <C-c>

Enter text, enter command mode
# Vim Cheatsheet
Welcome and Hello
A

Append at end of line
# Vim Cheatsheet
Welcome and Hello
Vim!<C-c>

Enter text, enter command mode
# Vim Cheatsheet
Welcome and Hello Vim!
O<C-c>

Open line above current line, enter command mode
# Vim Cheatsheet

Welcome and Hello Vim

We use the <C-c> chord to quickly go back into Command Mode when we have inserted text. This should start to become a habit as you use Vim - it is faster to enter small amounts of text, then go back to command mode and reposition the cursor, than to enter lots of text and use the arrow keys to move around in Insert Mode1.

Now that we have seen motions and text insert commands, let's look at some essential Vim operators.

Vim Operators​

A Vim operator is any command that can be applied to a range of text. We can combine counts, motions and operators to rapidly manipulate a specific part of the buffer.

Some of the operators that are particularly useful are listed in the table below - we'll see them in action afterwards:

OperatorDescription
cChange the range, i.e. delete the characters and move into insert mode at the beginning of the range.
dDelete the range.
yYank the range - copy it to a register ready to paste later.
g~Swap case.
guMake lowercase ('go lower').
gUMake uppercase ('go upper').
!Send through external program.

We can use operators with a motion to alter a range of text. Let's see some of the basic operators:

KeystrokesOutput
gg

Go to the beginning of the buffer
Welcome to Vim!
w

Move forwards one word
Welcome to Vim!
c2w

Change two words
Welcome to  
to Terminal Editing!<C-c>

Enter new text, go to Command Mode
Welcome to Terminal Editing!
gU0

Go uppercase to the beginning of the line
WELCOME TO TERMINAL EDITING!

Hopefully from these examples you are starting to get an idea of just how powerful modal editing is - you are able to express complex changes to text, ranges of text, and movements in the buffer with just a few keystrokes.

These operators can also quickly be applied to the current line - just type them twice!

OperatorDescription
ccChange current line, i.e. delete the line and move into insert mode at the beginning of the line.
ddDelete the current line.
yyYank the current line - copy it to a register ready to paste later.
g~~Swap case for the current line.
guuMake lowercase ('go lower') for the current line.
gUUMake uppercase ('go upper') for the current line.

Search Motions and the 'In' and 'A' Operators​

Some of the most powerful motions are the ones that can be used to search for a specific character. These 'search' motions can be used to quickly select a range of characters for an operator to perform on. Let's see a few of the most essential search motions:

MotionDescription
f{character}Find the next specified character.
F{character}Find the previous specified character.
t{character}'To' the next specified character.
T{character}'To' the previous specified character.

Now let's see how these motions look in action, by quickly moving around a little bit of Python code:

keystrokesoutput
f(

go to next '(' character
def search_for_word(word):
2fd

go to second previous 'd' character
def search_for_word(word):
t_

go to first '_' character
def search_for_word(word):
tΒ 

go to previous space character
def search_for_word(word):

Just like any motion, we can add a {count} to indicate how many times we want the motion to run.

Two other powerful motions are 'in' and 'a' - these motions are special because they come after the operator. Let's change some text using these motions:

Keystrokesoutput
f(

Find first '(' character
def search_for_word(word):
gUi)

Go uppercase in brackets
def search_for_word(WORD):
guu

Make entire line lowercase
def search_for_word(word):
f(da(

Find first bracket, delete a 'brackets'
def search_for_word:

When you have searched for a character, you can use the following keystrokes to find the next or previous result:

MotionDescription
;Move to the next result for the search motion.
,Move to the previous result for the search motion.

The 'In' and 'A' Modifiers​

The 'in' and 'a' modifiers for an operator are particularly useful when you want to change the contents of something like brackets, parenthesis, braces and so on. If you use ca) for example, you are saying 'Change a Brackets' - the brackets are removed and you are put into insert mode at the position the brackets were in. In comparison, ci{ changes 'in' braces, removing the text inside the braces, but keeping the braces themselves.

If you use an open bracket or brace, a space is added before and after the text you insert, if you use a close bracket or brace, no spaces are used:

KeystrokesOutput
f(

Go to first open bracket
def search_for_word(word):
ci)

Change in brackets
def search_for_word():
word = "default"<C-c>

Enter text, return to Command Mode
def search_for_word(word = "default"):
ca"

Change a quotes
def search_for_word(word = ):
'sample'<C-c>

Enter text, return to Command Mode
def search_for_word(word = 'sample'):

Editing Commands in Vim​

We're going to update our cheatsheet with a few notes - at this stage you can add any notes that you think are the most relevant, here are the first few lines of what I have added:

# Vim Cheatsheet

## Vim Motions

Vim motions are commands used to move the cursor. Essential motions are:

* `hjkl` - move the cursor left/down/up/right
* `w` - forward one word
* `b` - back one word
* `(` - back one sentence
* `)` - forwards one sentence
* `0` - beginning of line
* `$` - end of line
* `gg` - beginning of buffer
* `G` - end of buffer

Motions can take a `{count}` - this indicates how many motions we want to make, such as `3w` to move forwards three words.

Before we continue we should note that there are three new Markdown syntax features we have used:

  1. The ## characters, which indicate a sub-heading
  2. The * character, which indicates an item in a bullet list (a dash - can also be used for this)
  3. The backtick ` character, which can surround text to indicate that it is code and should be rendered as such

These simple formatting features will make the cheatsheet look excellent when it is viewed on GitHub or in a suitable editor - here's how it looks on GitHub at the moment:

Screenshot: Basic Markdown Rendered by GitHub

Let's look at another way we can use Vim in our day to day work. We'll enter a shell command that has a mistake, and quickly edit the command and fix the mistake in Vim. Let's add these changes to Git - and show one more way to use Vim. Enter the line of text below in the shell but don't press 'Enter' to execute it yet!

$ git commit -m 'added more detail on motions'

This command will commit the changes - but we haven't added any yet! Rather than fixing this by moving around the command line, let's just open the current shell command line in Vim. Press ^x^e (Control X, Control E) - this is the shell command to open the current line in the editor:

git commit -m 'added more detail on motions'
~
~
~
"/tmp/bash-fc-5094983766" 1L, 45B 1,1 All

Let's fix up the command with our new Vim skills:

KeystrokesOutput
i

Enter insert mode
git commit -m 'added more detail on motions'
git add . && <C-c>

Add text, enter command mode
git add . && git commit -m 'added more detail on motions'
:wq

Write and quit
(Vim closes and git commit completes)

Being able to rapidly edit the current shell command in Vim is a huge time saver.

Next Steps with Vim​

Vim is a huge topic - we've barely scratched the surface but I hope have focused on the features that you can use most immediately. As you use Vim more, I would recommend learning about the following features in order:

  1. The dot command
  2. Visual mode
  3. Yank and paste and registers
  4. Search and replace
  5. Customising Vim with the ~/.vimrc file
  6. Macros

There are a few resources that I would recommend. The first is vimtutor and the next are some excellent online resources and books.

Vimtutor​

Vim comes installed with a very useful vimtutor program. You can run this program to open Vim with a special file that describes the key functionality of Vim.

Use this program regularly while you are learning Vim to see how familiar you are getting and refresh your skills!

Vimcasts​

The excellent website Vimcasts by Drew Neil has some superb videos on how to use Vim - from the basics to some really advanced functionality.

Drew Neil is an extremely skilled Vim user and does a superb job of making a complex subject easy to follow.

Practical Vim & Modern Vim​

If you are enjoying using Vim, Drew Neil's books "Practical Vim" and "Modern" Vim should be on your bookshelf! They are truly excellent and again, just like his Vimcasts, make a complex subject much more user friendly.

The Vim Shortcuts Wallpaper​

Something I found useful when learning Vim was to have a wallpaper with the most common shortcuts. There are a number available on the web if you search for "Vim Wallpaper" - the one I currently use is below:

Vim Cheatsheet Wallpaper

Unfortunately I have not been able to find the name of the original author. I found this file at:

https://wallha.com/wallpaper/vim-cheat-sheet-minimalism-linux-1250034

Summary​

In this chapter we introduced the concept of the terminal editor. We looked at the essentials of Vim, Insert Mode, Command Mode, motions, operators and more. We also introduced some resources that will help with your learning journey.

In the next chapter we'll look at another essential tool - the Terminal Multiplexer.


  1. There is also another benefit to short commands - it makes them easier to repeat with the Dot Command which is not covered in this chapter but something that you will learn about as you use Vim more.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-6-advanced-techniques/how-to-avoid-scripting/index.html b/pr-preview/pr-346/part-6-advanced-techniques/how-to-avoid-scripting/index.html new file mode 100644 index 00000000..616f13cd --- /dev/null +++ b/pr-preview/pr-346/part-6-advanced-techniques/how-to-avoid-scripting/index.html @@ -0,0 +1,26 @@ + + + + + +How to Avoid Scripting - A Dictionary Lookup in Python | Effective Shell + + + + + + + + + + + + + + +
+

How to Avoid Scripting - A Dictionary Lookup in Python

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.

In this chapter, we'll look at when you might want to avoid shell scripting, what the alternatives might be, the pros and cons of each, and then create a real-world useful tool using Python, that would otherwise be very difficult to create with a shell script. We'll also look at what characterises a 'good' shell tool and how to follow the patterns and conventions set by common tools installed on your system.

When should you avoid shell scripting?​

Shell scripts can be very powerful. As a quick and dirty way to solve a simple problem, they often cannot be beat. When you combine bring in powerful tools that are built in on most systems such as awk (which can perform advanced text manipulation) they can be even more powerful. But there are reasons you might want to avoid using a shell script:

  1. If the problem you are solving is quite complex, the script will be large and hard to manage
  2. Shell scripts become hard for others to reason about when they become complex
  3. Shell scripts are limited to the features of the shell and the tools on your system - but not every system has the same tools, so complex scripts may not be portable

Shell scripts are sometimes the only sensible tool to use, for example if we wanted to write a script that showed the shell's options and let the user toggle them on and off, a script would be ideal. This is because another technology, such as a Python script, would not know what shell you are using. This would make the Python script needlessly complex.

But in general, as soon as a script gets to more than about a page of code, I tend to think that this a good point at which to consider using an alternative tool.

What are the alternatives?​

There are hundreds of programming languages that exist to help you solve technical problems. But not all of them are ideal as an alternative to a shell script. Some of the questions you might ask are:

  1. Is the programming language going to be available on almost any machine? Simple shell scripts run almost anywhere without having to install other tools - will the language give me this functionality?
  2. Is the language designed for handling the kind of problem I want to solve? Does it support console based input and output? Is it easy to write shell-style tools in this language?
  3. Is the language simple and popular? Can others understand or adapt the script without too much intervention?

Some languages jump to mind as good options for shell scripts:

  1. Python - it is installed by default on almost every system, highly popular, simple to use and read and works well to write input-processing-output programs.
  2. Ruby - again, installed on many systems by default. This is a simple language and also highly popular, but perhaps less well-known than Python.
  3. C - most platforms have a C compiler installed, and C is great for working with low-level system libraries. But it requires compilation, may behave quite differently on different systems, and is fairly complex for others to use.
  4. NodeJS - Node.js uses Javascript as its language, which is highly popular. It is event-driven, meaning it can be very fast. But the version installed across systems varies considerably, and this can cause headaches when sharing scripts.
  5. Perl - installed almost universally on any system, very powerful, but possibly less well known nowadays and therefore perhaps less likely to be understood by others.

Now when you are writing complex tools or programs, the criteria will change, you want to use a language and platform that really suits the problem you are solving, or is used already by the team you are working with. But in this chapter we're looking at alternatives to shell scripts to write tool that work well when used in the shell.

Given it's almost universal presence on systems, its huge (and increasing) popularity, and robust standard library (which allows you to use many features without having to have users download additional packages), Python is an excellent choice for writing shell friendly tools.

What makes a shell-friendly tool?​

When we are writing a tool that is aimed at shell users, it makes sense to follow the conventions set by other shell tools. This means that users will be able to use the tool in a familiar way, and complement it by combining it with existing tools on their system. So what makes a shell-friendly tool?

  • Being able to read from standard input - this allows us to pipe inputs from other tools into our programs (see Thinking in Pipelines for more on this), we also want to read and process line-by-line, in case the input is very large
  • Being able to write to standard output - this sounds obvious, but it means making sure that our output can be read by a human operator, but also ideally be processed by other tools such as cut, sed, rev and so on, it also means thinking about how colour will or will not be used in output, and avoiding superfluous output that might make it harder to process the output (such as titles, version numbers and so on)
  • Being able to specify options using sensibly defined flags - there are many common conventions for how flags or parameters work in tools, using these patterns (rather than inventing our own) will make our tool easier to use. For example, having an -h flag to show help is a very common convention1
  • Being able to run on different systems - shell users are used to being able to use tools like grep, sed and so on in a similar way across platforms, a well-written tool will do the same
  • Handling errors using shell idioms - shell-friendly tools use 0 as a status code to indicate success, and define error codes in their documentation, so that people using the tools know how to handle exceptional circumstances

There are many other conventions and ideas that could be added, but these are some of the fundamentals.

Writing a Dictionary Lookup Tool in Python​

As an example of how to write a shell-friendly tool, we're going to create a simple program that takes a list of words and provides a definition loaded from the free and fantastic Free Dictionary API.

This is a good example of a tool that would be overly complex to write with a shell script. We need to handle input, parse and process it, make HTTP requests to download pages from the internet, parse and process those requests, format the output, and provide some options for the user on how the output looks. A highly experienced shell programmer would likely be able to create this tool with a shell script without breaking a sweat, but it would likely be quite hard for a less experienced user to follow. Python is easy to write and read, has a wealth of online learning resources, and is available on almost any platform.

Our requirements for the tool will be quite simple:

  1. Allow the user to provide a set of words to be looked up as a text file or from standard input
  2. Download the definition of the words
  3. Write the words to standard output, with the option to format how this output looks
  4. Offer help to the user on how to use the tool

We'll also limit ourselves to 'raw' Python using only the standard library - meaning users will not have to install any packages to make this tool work.

Help! I don't know Python!

That's OK! All of the chapters in the section 'Advanced Techniques' will likely stretch you and require some additional learning and experimentation. Don't worry if you don't know Python - I'll explain as much as I can as I go through the chapter. You should be able to take away the key lessons to be learnt without a particular familiarity with the language.

Each of the files you'll see in this section are in the ~/effective-shell/programs/lookup folder.

Downloading the Samples

Run the following command in your shell to download the samples:

curl effective.sh | sh

OK - let's get started.

Version 1 - Basic Structure​

First, let's check if Python is installed. There are two versions of Python that are commonly used. Python 3 is the current latest version and should be preferred. This is what we will use. Python 2 is still used by many people, and a lot of existing code is written in Python 2, but where possible Python 2 code should be upgraded to Python 3. Python 2 official went out of support in January 2020.

Check Python 3 is installed by running:

% python3 --version
Python 3.9.10

In general I would recommend that you explicitly make it clear you are using Python 3 by using the python3 tool. On many systems the python tool points to python3, but it is safer to by explicit and use python3.

If Python is not installed

If you see a message such as command not found: python3, then you will need to install Python.

Shell tools take input, process it and produce output. So let's take our structure and create a first cut. This first cut will not perform any processing - it'll just take the input and produce simple output. But it will give us a working starting point to incrementally add more features to.

At this stage I'll share the code in the form of snippets - you can see the code as it evolves by looking in the ~/effective-shell/programs/lookup/ folder.

lookup-v1.py
import sys

# Read standard input until there is nothing left to read.
while True:
# Read a line of input.
word = sys.stdin.readline()

# If the user hits 'Ctrl+D' to end transmission, readline returns an
# empty string and we can stop reading.
if not word:
break

# If the input is an empty line or whitespace, skip it.
if word.isspace():
continue

# Add the word to our list of lookups, and strip any whitespace from the
# beginning and end of it. For now, we don't have a definition.
word = word.strip()
definition = ''

# Write the result.
print("{} - {}".format(word, definition))

Let's test this out and then we'll dissect the code. First, we'll just run the program, type some words, then press Ctrl D to signal end-of-transmission (check Thinking In Pipelines if you need a reminder on what 'end-of-transmission' means). You can also press Ctrl C to close the program.

$ python3 ~/effective-shell/programs/lookup/lookup-v1.py
one
one -
two
two -
three
three -

The program successfully reads our input, and writes out a result for each word.

We can also test that the program can receive input piped from a file:

$ cat ~/effective-shell/data/words.txt | python3 ~/effective-shell/programs/lookup/lookup-v1.py
louche -
liana -
lieder -
Manchu -
Nankeen -
naevi -
Ness -

So we have a program that can read from standard input, either interactively or from a file. Let's break down the code section by section.

First we create a loop that will run continuously, reading lines from standard input:

lookups = []
while True:
# Read a line of input.
word = sys.stdin.readline()

If the input is completely empty, then that means we've reached the end of the file or the user has signaled 'end-of-transmission' by pressing Ctrl+D:

    if not word:
break
if word.isspace():
continue

If the input is just whitespace, such as a newline or tab, we skip it.

Now we record the value of the word with the whitespace that might surround it stripped, note that has not been found and set the definition of the word to an empty string:

    word = word.strip()
definition = ''

Now we write out the word and its definition:

    print("{} - {}".format(word, definition))

Now let's look at actually downloading the definition.

Version 2 - Downloading the Definition​

Now that we've got the list of words, we can try and download a definition of each one by using the excellent https://dictionaryapi.dev/ website. This site searches a number of online dictionaries, including Wiktionary.

We will add a new function to the script. You can see the complete script in the file ~/effective-shell/programs/lookup/lookup-v2.py.

The new function downloads the definition of a word from the dictionaryapi.dev site:

lookup-v2.py
def search_for_word(word):
# Encode the word for HTML.
encoded_word = urllib.parse.quote(word.encode('utf8'))

# Try and download the definition using the amazing dictionaryapi.dev site.
try:
url = "https://api.dictionaryapi.dev/api/v2/entries/en/{}".format(encoded_word)
response = urllib.request.urlopen(url)
if response.status == 404:
print("NOT FOUND")
sys.exit(1)
with urllib.request.urlopen(url) as response:
raw_json_data = response.read().decode('utf-8')
# If the word is not found, return an empty definition.
except urllib.error.HTTPError as http_error:
if http_error.code == 404:
return ''
raise

# Now try and parse the data.
data = json.loads(raw_json_data)
first_definition = data[0]['meanings'][0]['definitions'][0]['definition']

# Return the result.
return first_definition

I'm not going to go through this blow-by-blow, it's a fairly rough and ready way to try and get the definition of a word from an online resource. In a nutshell it does the following:

  1. Make sure that have the right address to search for the word
  2. Search for the word and download the result
  3. If the word is not found, return an empty result
  4. If the word is found, try and decode the definition and return it

With this new function, we can update the main loop of our program to look like this:

lookup-v2.py
    # Strip whitespace from the word and find the definition.
word = word.strip()
stripped_word = word.strip()
definition = search_for_word(stripped_word)

# Write the result.
print("{} - {}".format(word, definition))

If we pass some test words into the program our output looks like this:

$ cat ~/effective-shell/data/words.txt | python3 ~/effective-shell/programs/lookup/lookup-v2.py
louche - To make (an alcoholic beverage, e.g. absinthe or ouzo) cloudy by mixing it with water, due to the presence of anethole. This is known as the ouzo effect.
liana - A climbing woody vine, usually tropical.
lieder - An art song, sung in German and accompanied on the piano.
Manchu -
Nankeen - A type of cotton cloth originally from Nanking in China.
naevi - A pigmented, raised or otherwise abnormal area on the skin. Naevi may be congenital or acquired, and are always benign.
Ness - A promontory; a cape or headland. (Frequently used as a suffix in placenames.)

Pretty cool - our program can find a reasonable definition for most of the words in the test data set we have. Now let's look at cleaning up the output.

Version 3 - Formatting the Output​

Our program is working quite well, but we can improve on it by making the output a little friendlier to read. We can show the word in a different colour to the definition, separate the definition with a colon which will make it easier for us to process it with other tools, or even limit the length of the definition so that it fits on the screen.

We're also going to let the user provide a 'crop' value if they want to. This is a number that limits the length of the output each line, which could be useful if the user wants to fit the definitions on the screen without them spilling over to the next line.

There is a special module in Python called argparse that helps you parse the arguments for a program, we'll use this to specify and parse the 'crop' argument.

You can see the complete script in the file ~/effective-shell/programs/lookup/lookup-v3.py.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument(
'-c', '--crop',
help='crop the output line length',
type=int,
nargs='?',
const=80, # Default value if -c is supplied
default=None) # Default value if -c is not supplied
args = parser.parse_args()

The argparse module is very sophisticated, you can read more about it online if you'd like to discover more. But for now, it's enough to know that this code defines an optional argument named crop, that can be provided with a number, or without a number. We'll see it in action shortly.

Next we'll add a function that writes a word and its definition in a nicer way:

def write_definition(word, definition):
# Check if stdout is a terminal - if it is we'll colour the output.
is_terminal = sys.stdout.isatty()

# We will separate the word and the definition with a colon and space.
separator = ": "

# If the 'crop' argument is set, use it.
if args.crop:
output_length = len(word) + len(separator) + len(definition)
if output_length > args.crop:
# We need to chop some letters off the end of the definition, but
# leave space for '...' to indicate the output is cropped.
new_length = len(definition) - 3 - (output_length - args.crop)
definition = definition[:new_length] + '...'

# If we are in a terminal make the word green and the separator white.
if is_terminal:
word = "\033[92m" + word + "\033[0m"
separator = "\033[37m" + separator + "\033[0m"

# Write out the word, separator and definition.
print(word + separator + definition)

This code first checks to see whether stdout is a terminal. This is useful because if we are in a terminal, we can show colour codes, but if the output is something like a file, we can skip the colour codes (which would look messy in the resulting file). Then we do some arithmetic if the crop argument is provided, shortening the definition if needed.

The weird looking characters such as /033[92m are ANSI control codes to set the colour of the output - you can read all about them in Useful Patterns for Shell Scripts in the section 'Colouring Output'.

With this function added, and called in the right place in our program, we can now lookup definitions, have the output printed in colour, and specify a 'crop' value:

$ cat ./effective-shell/data/words.txt | python3 ./effective-shell/programs/lookup/lookup-v3.py -c 60
louche: To make (an alcoholic beverage, e.g. absinthe or ...
liana: A climbing woody vine, usually tropical.
lieder: An art song, sung in German and accompanied on th...
Manchu:
Nankeen: A type of cotton cloth originally from Nanking i...
naevi: A pigmented, raised or otherwise abnormal area on ...
Ness: A promontory; a cape or headland. (Frequently used ...

The nice thing about using the argparse module is that our program automatically gets a --help or -h option that can be used to provide instructions:

$ python3 ./samples/programs/lookup/lookup-v3.py -h
usage: lookup-v3.py [-h] [-c [CROP]]

optional arguments:
-h, --help show this help message and exit
-c [CROP], --crop [CROP]
crop the output line length

We've really just scratched the surface of what can be done here. You can find this version of the program in ~/effective-shell/programs/lookup/lookup-v3.py

Installing the Lookup Tool​

The great thing about a Python script like the one we have built is that it is standalone, and anyone can install it as tool on their system with very little effort.

All we need to do is first tell the shell that if it encounters this script and is asked to execute it, it needs to use the python3 program. We can do this by putting a shebang at the top of the file:

lookup.py
#!/usr/bin/env python3

# ...the rest of the code goes here, it's been omitted for brevity!

This shebang uses the env program to locate the python3 program. This is important as python3 might be installed in different locations on different systems. You can read more about how to use env in shebangs in the chapter Shell Script Essentials under 'Using Shebangs'.

Now that we have a shebang, we can make the file executable using the chmod program and link to it from our personal bin folder:

chmod +x ~/effective-shell/programs/lookup/lookup.py
ln -s ~/effective-shell/programs/lookup/lookup.py /usr/local/bin/lookup

If you need a reminder how to use the chmod tool and ln tool to install scripts, check the chapter Shell Script Essentials under the section 'Installing Your Script'.

Now that we have the tool in our local binaries folder, we can call it like so:

$ lookup -c -- effective shell
effective: A soldier fit for duty
shell: A hard external covering of an animal.

Note that the lookup.py script, which is the final version of the script, has some additional features which are described at the end of the chapter. One of these features is that we can just provide a word or list of words as positional arguments to the command.

Note that the -- in the command shown above is the 'end of options marker' - this is the standard Linux pattern to indicate that the list of flags is complete, and that what follows is the list of positional parameters. If we didn't have this, the tool would think that we are providing effective as the value of the -c flag. The -- removes this ambiguity. Many Linux tools support this separator, you can check man bash to find out more.

Improving the Lookup Program​

One of the fun things about coding is thinking about all of the exciting additional features you can add!

The final version of the script, which is in the ~/effective-shell/programs/lookup/lookup.py folder has a set of additional features that you might find useful to explore when building your own programs. These features are:

FeatureDescription
More robust error handlingThere are exception handlers in the key places the program may fail.
Graceful handling of Ctrl+CEnsure we close cleanly on Ctrl+C without a noisy error message. See KeyboardInterrupt in the code for this.
More detailed helpThe help text has examples, see argparse in the code.

There are all sorts of other features you could add as a coding and learning exercise! Here are a few that I considered:

  • A 'browse' flag - this could open the user's browser to the full definition online
  • Manpages - an option to install a manpage for the tool, meaning that we can run man lookup
  • Clearer interactive mode - when stdin is a terminal, meaning the user is interactive, show a prompt and instructions
  • A verbose flag - a --verbose flag to show detailed error messages if they are encountered

If you find yourself writing more complex command-line tools in Python, you might also explore the excellent Click Python package. This is a very popular package among Python developers and is used by a number of large and well-established projects. The Typer package is also worth exploring. The urllib package I have used works, but it can be quite unwieldy when dealing with more complex options - many developers will prefer alternative packages.

Summary​

In this chapter we looked at alternatives to shell scripts and when we might consider them. We looked at what makes a tool 'shell-friendly'. We also looked at how we can use the highly popular Python language to write a simple but useful shell-friendly tool.


  1. There is a detailed description of how options should be specified for GNU tools at http://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-6-advanced-techniques/index.html b/pr-preview/pr-346/part-6-advanced-techniques/index.html new file mode 100644 index 00000000..b0adf4bf --- /dev/null +++ b/pr-preview/pr-346/part-6-advanced-techniques/index.html @@ -0,0 +1,26 @@ + + + + + +Part 6 - Advanced Techniques | Effective Shell + + + + + + + + + + + + + + +
+

Part 6 - Advanced Techniques

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.

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!

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-6-advanced-techniques/master-the-multiplexer/index.html b/pr-preview/pr-346/part-6-advanced-techniques/master-the-multiplexer/index.html new file mode 100644 index 00000000..5351fd3f --- /dev/null +++ b/pr-preview/pr-346/part-6-advanced-techniques/master-the-multiplexer/index.html @@ -0,0 +1,26 @@ + + + + + +Master the Multiplexer | Effective Shell + + + + + + + + + + + + + + +
+

Master the Multiplexer

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.

What is a Terminal Multiplexer?​

A terminal multiplexer is a program that that allows a user, or many users, to run many programs at once. The multiplexer manages 'sessions' - these sessions run the programs, independently of your shell or terminal. Users can switch between sessions, save them, resume them, or even invite others to join the same session to allow collaboration. Multiplexers also normally offer 'window management' capabilities, allowing you to break your terminal into separate panes, tabs or windows.

The visual below shows a multiplexer in action - the one I am using while I write this chapter:

Screenshot: A Terminal Multiplexer

In this image I am running the Tmux multiplexer in my terminal. I have split the window into three panes - a large one on the left that contains the text for the chapter, and two smaller ones on the right. The upper right pane builds the effective shell website locally while I write, to check for errors, and the lower right contains a script I was working on.

When I want to switch between the tasks I'm working on, I don't need to start or stop any of the program, I can just switch between the 'panes' in my multiplexer.

At the bottom of the screen there are also two tabs - one that contains the website and the windows I'm currently working in, and another that has some windows for the manuscript for the book.

If I close my shell, the Tmux server is still running the sessions and I can re-open my terminal and resume. If I was to close my computer, when I restart my multiplexer will restart these programs for me, allowing me to pick up where I left off.

Why use a Multiplexer?​

It is not essential to know how to use a multiplexer. But knowing how to use one can certainly help you become a more effective shell user. Some of the key benefits to using a multiplexer are:

Window Management

Much like modern 'tabbed interfaces', multiplexers can support multiple windows, tabs and panes within a window. This means you can arrange your workspace exactly how you want it. You can save the configuration of your windows and use it later. You can organise different windows into different sessions, allowing you to have many projects running at once, that you can quickly switch between, each one with a layout configured to suit your needs.

Persistence of Sessions

Your programs are run independently from your terminal. If your terminal crashes, or freezes, your programs still run. If you are working with a remote machine, then you can run a multiplexer on it to manage your programs. This means that if your connection to the machine is reset, the programs will not stop running. You can re-attach to the session at later point and pick up where you left off. Session management is incredibly useful - you'll wonder how you lived without it if you use the shell a lot.

Configuration

Multiplexers offer many configuration options to allow you to customise how programs are run and interfaced with, allowing you to set up the ideal environment for you to be effective.

Collaboration

A multiplexer is a 'client-server' program. This means you as a user are a client and connect to a server that runs the multiplexer. But other users can connect as well - this means you can easily collaborate with other users, sharing your own work, connecting to theirs, or both working on a shared remote machine.

We'll look at a few of the most immediately useful features of multiplexers in this chapter - this will only really be scratching the surface but should be enough to help you decide whether it is a topic you'll investigate further on your own and add to your toolkit.

Introducing Screen and Tmux​

The two most popular multiplexers are probably GNU screen and Tmux. GNU screen was created in the late 80s and has been used widely ever since. It is pre-installed on many Linux systems.

Tmux is a more modern multiplexer, created in 2007. It has most of the features of GNU screen, is also open source, and adds some very useful features to make it a little more user friendly.

For the rest of this chapter we are going to look at Tmux in detail. I've chosen Tmux rather than screen because I think its user-friendliness makes it a little better for people who are new to multiplexers. Once you are familiar with Tmux you might decide to switch to GNU screen - if so it will seem very familiar.

Installing Tmux​

Tmux may already be installed on your system. You can check by trying the tmux -V command, which shows the current version:

$ tmux -V
tmux 3.2a

If it is not installed, or you do not have version 3 or higher, you should install the latest version of Tmux. Install Tmux with the package manager for your system. The package name on most distributions is just tmux. As an example, on a Debian-based distribution we would install Tmux with:

apt install -y tmux

Tmux Techniques​

Now that Tmux is installed let's see what we can do with it.

The Leader​

One thing you'll notice when reading about Tmux and GNU screen is the Leader. The leader is a key combination that prefixes any commands for the multiplexer. We just this leader combination so that the multiplexer key bindings don't clash with any existing ones. Typically you will use the leader keys to tell the multiplexer you are about to enter a command, then strike the keys that correspond to the command.

For Tmux, the default leader is Ctrl + B. For GNU screen it is Ctrl + A. You can easily remember these by remembering that Tmux is a successor to GNU screen, so the leader key letter is one letter further in the alphabet!

Window Management​

Run the tmux program by entering the command below:

tmux

We are now going to create some 'splits' - these will split our current window into separate panes. Each pane can run its own program. Let's take a look at a few commands for managing splits and panes.

Creating Splits and Moving Between Splits​

We can create a vertical split by pressing Ctrl+B followed by the % percent symbol. We can create a horizontal split by pressing Ctrl+B followed by the " quotes symbol. Finally, to move between splits, press Ctrl+B followed by an arrow key.

The short video demonstrates both types of split and moving between panes:

Zooming Panes​

You can 'zoom' a pane so that it takes up the entire window by moving to the pane you want to zoom and pressing Ctrl+b z. To 'unzoom' a pane, you can just move to another pane (even if it is hidden) or simply press Ctrl+B z again.

This what it looks like when we zoom a pane:

Creating Windows and Moving Between Windows​

If you want to run a program in an entirely new window, use the Ctrl+b c command to create a window. To switch windows, press Ctrl+b w to choose from the list of available windows:

Quickly Navigating Between Windows​

The ^B w command shows all of the windows (and sessions!) for Tmux and let's you rapidly switch between them, this is extremely useful if you have a lot of windows!

Here's how it looks in action - in this demo I have a few windows - notice that I've given them more clear names with ^B ,, this also makes it easier to sort and manage your windows.

Quick Reference​

There are some other useful commands when working with windows:

CommandDescription
^b nMove to the next window
^b pMove to the previous window
^b 0Select the window numbered '0' - use the number of any window from the status pane
^b &Close the current window
^b ,Rename the current window
^b wShow the window navigator

The 'rename window' is very useful if you have a lot of windows and want to give each one a descriptive name. Remember, Tmux is stateful so will remember these settings even if you detached and later re-attach (we'll see this in the next section).

Session Management​

Sessions in Tmux are a collection of independently managed windows. They are great for creating lots of 'projects' - each session can be a project with the appropriate windows for the work you are doing.

Let's start Tmux, but name the session effective-shell by using the tmux new -s effective-shell command. Once we've done this, we'll create a couple of windows, then create a new session by using Ctrl+b :. This command opens the Tmux command pane which is shown at the bottom of the screen. This command pane allows us to run a Tmux command. When we do this, we don't need to put tmux at the beginning. Let's create a second session named my-project. We can now switch between sessions with Ctrl+b s. This opens the list of sessions and lets us select one using the arrow keys. Tmux gives us a preview of each session as we move the selection over them.

The commands we will run are below. Remember, ^B means 'Control B', i.e. hold the Ctrl key and press 'b':

# Create a new tmux session with the name 'effective shell'.
tmux new -s effective-shell

# Create a new Window.
^B c

# Enter tmux command mode.
^B :

# Create a second session.
new -s my-project

# Select from the list of sessions.
^B s

We can see visually what this looks like below:

Attaching and Detaching from Sessions​

The great thing about sessions is that we can set them up, then detach from them to do other work. The sessions will keep running and we can re-attach to them later. This means you can close your terminal and re-open it and programs will still run.

If we are in the shell and want to open Tmux and attach to whatever was the last session, we can just run:

tmux attach

When we are in a Tmux session, we can detach from it by entering command mode with Ctrl+B : and using the detach command:

^B :
detach

We could close our terminal at this point - the sessions are persisted by the Tmux server. Now if we re-open a terminal, we can re-attach with the same command as before:

tmux attach

A video of how this looks is below:

If you use Tmux a lot you might find you end up with lots of sessions - you can delete sessions by pressing ^B s to show the session list, scroll to the session you want to delete and just press x. Tmux will ask for confirmation before it closes the session.

Some useful shortcuts for sessions are:

Sessions are extremely powerful for organising your work. Some useful commands for working with sessions are:

CommandDescription
tmux attachAttach to the last used session
tmux new -s nameStart a new tmux session named name
^b : new -s another-nameEnter command mode, start session named another-name
^b $Rename the current session
^b sShow the session list. Close the selected session with x
^b )Move to next session
^b (Move to the previous session
^b wShow all windows - this command also shows all sessions!

Configuration​

Tmux's out-of-the-box configuration is normally going to be fine for everyday use. However, if you find yourself using Tmux a lot you might want to look at some of the configuration options available to help you fine-tune the program to suit your preferences.

Tmux follows a very standard Unix-style configuration pattern - a dotfile is used to configure the program. If you are not familiar with dotifles, check Chapter 26 - Managing Your Dotfiles - this chapter also goes into detail on how you can version control and save your dotfiles as well as share them across many machines easily.

To configure Tmux, create a file named .tmux.conf in your home directory:

touch ~/.tmux.conf

Now open this file in your preferred editor. As we have just created it there will be nothing in the file.

There are a raft of configuration options available, you can check the manpages with man tmux to see details, or search for any of the excellent online guides on how to configure Tmux. I'll share what I think are some of the most useful configuration options for Tmux1.

Let's add some configuration options, I'll explain each as we go along.

The Default Shell

The first thing I do is to tell Tmux to use my current shell program. This means if I am using Z-Shell, Tmux will open windows with Z-Shell. If I am using Bash, it will use Bash. It will also source my shell dotfiles, so that each window that is opened has the same PS1 and configuration as my standard shell.

# Set the default shell, and set the default command to use our shell (this
# means we source things properly, show the correct PS1 etc).
set -g default-shell ${SHELL}
set -g default-command ${SHELL}

Open windows in the Working Directory

By default when you create a new window with ^B c, Tmux will set the working directory of the window to the home directory. In general I prefer to have the window open in my current working directory:

# Open new panes and splits in the same working directory.
bind c new-window -c "#{pane_current_path}"

Stable window names and sequential windows

Tmux will try to be smart and change the name of each window to the program it is currently running. This means window names change as you use them. I find this distracting, so disable the automatic renaming of windows - in general I rename a window as soon as I have opened it with ^B , and give it a descriptive name.

I also set Tmux to start windows from number 1 rather than 0, and when windows are created or deleted ensure that Tmux re-numbers them so that they are sequential, without gaps. If you don't do this you'll rapidly find yourself running into windows with double-digit numbers which are harder to select (you can only use ^B <window-number to select windows 0 through 9):

# Set the name of the window initially, but then don't let tmux change it.
# The name can still be set at any time with Ctrl+B + ,
set-option -g allow-rename off

# Start windows and panes at 1.
set -g base-index 1
set -g pane-base-index 1

# When we add/remove windows, renumber them in sequential order.
set -g renumber-windows on

Sensible Splitting Commands

I've always found ^B % and ^B " odd commands to split, and still to this day regularly mix them up. So I use ^B - to make a vertical split and ^B | t make a horizontal split. The direction of the bar, either the hyphen or vertical bar I find much easier as a way to remember what kind of split I'll be making!

# Split panes using | and -
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"

Nested Session Leaders

I am almost never not in a Tmux session. This means that if I open a nested session, for example by ssh-ing into a virtual machine that and running Tmux there, I have trouble sending commands to the nested Tmux - if I use ^b c to open a new window for example, it'll open the window on my machine, not in the nested session. By using bind-key v send-prefix I can use ^b b to send a command to the nested session. This might sound fiddly but we'll see how useful it is in the next section!

# Use ^b b to send the leader to a nested session. This means if you are
# using tmux and then ssh into a tmux session (i.e. a nested session) you
# can run commands in the nested session with ^B b <command>.
bind-key b send-prefix

Mouse Support

If you have a mouse, which will normally be the case if you are working with your local machine, then you can enable the mouse for Tmux, allowing you to drag panes to resize them, select panes and windows with the mouse and so on.

If you are feeling like you want to really get your 'mouseless' flow working - disable this option! It'll force you to learn the commands.

# Enable mouse mode (tmux 2.1 and above)
set -g mouse on

Vim Mode!

I set a number of configuration options to help Tmux interface more seamlessly with Vim, and also use Vim directions rather than arrow keys to move around. This means I use ^B j to go to the pane below, ^B l to go to the pane to the right.

I have also configured a number of keybindings to make resizing panes a little more intuitive to a Vim user, as well as keybindings for Vim style selection of text.

These are more advanced options and only going to be of interest to Vim users however, so I'll let you explore them if you are interested in my dotfiles project at github.com/dwmkerr/dotfiles

Advanced Configuration​

We have really only touched the most basic of configuration options. Tmux can be customised in almost any way imaginable.

The visual style of the status bar, the colours, the information shown, all of these settings can be changed. There are also plugin-managers for Tmux to make it easier to install plugins that provide more advanced configurations.

This is a more advanced topic and one I would only suggest exploring once you are familiar with 'vanilla' Tmux!

Collaboration​

So far we have run all of our sessions on our local machine. This is great for organising your local work. But you can run Tmux on another machine or server, then connect to it from your machine.

This is another quite advanced topic, but to show just how powerful this can be, the video below demonstrates some of Tmux's features and how they can make working across many machines so much easier.

In this video, the following actions are performed:

  1. We start a local Tmux session with tmux
  2. We split the window, giving us space to open a secure shell, using ^b %
  3. In our new split, we ssh onto the box we created in Chapter 31 - The Secure Shell
  4. On this box, we start a new Tmux session
  5. In this Tmux session we start creating a new Python script
  6. Now we connect to this session from our local machine over ssh using ssh -t effective-shell-aws-linux tmux attach
  7. This attaches us to our session on the server - and we're in Vim ready to work on the script!
  8. To complete the demo, I connect from another machine, my Macbook - and then edit the script live

In this demo we have two separate machines connecting to our server, able to collaborate real-time on a Tmux session that will persist even if we shut down the machines we are connecting from. This just touches the surface of what is possible!

In this example we used the command ssh -t effective-shell-aws-linux tmux attach. The ssh program allows you to run a command on the server. The command we are running is tmux attach. Now by default if we ask ssh to run a command it will not connect the input of our terminal to the server - the idea is that sometimes we are just using ssh to run one-off commands and don't need to stay connected. But for this command we actually want to stay attached to the server, so we use the -t request tty` flag to attach our terminal input to the SSH session.

You can setup your SSH config file to automatically attach to the Tmux session. This is how my ~/.ssh/config file entry is setup for the virtual machine we created in Chapter 31 - The Secure Shell:

Host effective-shell-aws-linux
HostName ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com
User ec2-user
IdentityFile ~/.ssh/effective-shell
RequestTTY yes # ensure that we attach our terminal input
RemoteCommand tmux attach # attach to the tmux session

This configuration means that when we SSH onto the effective-shell-aws-linux box we will run the tmux attach command and attach our terminal. To connect to Tmux on the server now we only need to run:

ssh effective-shell-aws-linux

Going Further with Tmux​

We've really only touched the surface of what Tmux can do. There are some truly incredible things you can do with a multiplexer like Tmux as you start to use it more. Selecting text from the shell without touching the mouse, seamless integration of Tmux splits and Vim splits, sending commands to multiple machines at once, using plugin managers to add advanced features, the list goes on.

Tmux also lets you rapidly resize panes, break a pane into its own window or session, re-order panes, swap panes and so on. These commands are probably the next ones to start becoming familiar with! If you have enabled mouse mode you can also resize panes with the mouse if you have one available.

I'd highly recommend using Tmux as part of your standard workflow - get familiar with the basic features shown in this chapter and then as you start to find limitations and want to do more explore some of the great books and blog posts out there that go into more advanced features.

Getting Help​

You can quickly see all of the Tmux commands by running ^b ?. The output of this command will look something like this:

C-b C-b     Send the prefix key
C-b C-o Rotate through the panes
C-b C-z Suspend the current client
C-b Space Select next layout
C-b ! Break pane to a new window
C-b " Split window vertically
C-b # List all paste buffers
C-b $ Rename current session
C-b % Split window horizontally
C-b & Kill current window
C-b ' Prompt for window index to select
C-b ( Switch to previous client
C-b ) Switch to next client
C-b , Rename current window
C-b . Move the current window
C-b / Describe key binding
C-b 0 Select window 0
C-b 1 Select window 1
...

A quick reference of command commands below:

CommandDescription
Essential Tmux Commands
tmux lsList sessions
tmux new [-s name]Start a new tmux (optionally with a session name)
tmux attach [-t name]Attach to the last used session, or the target session with -t
tmux kill-session [-t name]Kill a session named name
tmux kill-session -aKill all sessions except the current session
^b dDetach from the current session
^b : new -s another-nameEnter command mode, start session named another-name
^b ?Show command help
*Sessions
^b $Rename the current session
^b sShow the session list. Close the selected session with x
^b )Move to next session
^b (Move to the previous session
^b wShow all windows - this command also shows all sessions!
Windows
^b nMove to the next window
^b pMove to the previous window
^b 0Select the window numbered '0' - use the number of any window from the status pane
^b &Close the current window
^b ,Rename the current window
^b wShow the window navigator
^b $Kill the current window
Splits and Panes
^b %Create a horizontal split
^b "Create a vertical split
^b <arrow>Move to the pane in the direction of an arrow key
^b zZoom in or out of a pane
^b !Convert pane to window

Going Further with Tmux​

To learn more about Tmux I recommend the excellent book tmux 2: Productive Mouse-Free Development by Brian Hogen. This is suitable for anyone, from beginner to expert, you can regularly go back to it as you become more familiar with the basics of Tmux and learn how to take your skills to the next level!

Summary​

In this chapter we introduced the concept of Terminal Multiplexers, in particular GNU screen and Tmux. We saw how to manage windows, panes and sessions. We learned how to configure Tmux to suit your personal working style. We also looked at how we can use Tmux to manage sessions on remote machines and even collaborate real time with other users.


  1. You can find my complete set of dotfiles at github.com/dwmkerr/dofiles if you would like to see how I configure other programs.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-6-advanced-techniques/the-secure-shell/index.html b/pr-preview/pr-346/part-6-advanced-techniques/the-secure-shell/index.html new file mode 100644 index 00000000..e983ec6b --- /dev/null +++ b/pr-preview/pr-346/part-6-advanced-techniques/the-secure-shell/index.html @@ -0,0 +1,26 @@ + + + + + +The Secure Shell | Effective Shell + + + + + + + + + + + + + + +
+

The Secure Shell

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.

In this chapter we'll look at how to setup the required credentials and keys to allow us to securely connect to a remote machine, how to create a virtual machine in the cloud and then how to connect to that machine from our local machine using the ssh program. We'll also look at how to configure SSH to make it easier to work with!

What is SSH?​

SSH is the Secure Shell Protocol. It is a protocol that has been used for decades as a way to establish a secure connection to a remote machine, on your network or across the internet. The program named ssh is the 'secure shell' - it is a program that helps you establish SSH connections, manage credentials, and open a shell on a remote machine.

When you connect to a remote machine, you will typically have to authenticate. It is possible to authenticate with a username and password. In most scenarios however you will have to use Asymmetric Encryption to authenticate with the server.

Asymmetric encryption uses special 'keys', called 'public keys' and 'private keys' to manage authentication and secure data. This might be new to you if you are used to using simple authentication protocols such as username and password. But public/private key based security is extremely secure and a great thing to know about! So let's start by looking at how public and private keys work and then create our own key pair which we will later use to secure our connections.

An Authentication Crash Course​

The details of symmetric and asymmetric encryption complex and beyond the scope of this book1. We'll cover the only essential concepts and techniques in this chapter.

Before we look at asymmetric encryption, let's take a quick look at the challenges of symmetric encryption - which will help explain why asymmetric encryption is preferred.

Symmetric Encryption for Authentication​

When we use symmetric encryption, we have a shared 'secret', normally something like a username and password. If a user 'Alice' wants to send a message to another user, 'Bob', we have to share the secret so that Bob can decrypt the message that Alice sends:

loading...

The diagram above is a hugely simplified view, but essentially shows that when the users want to exchange a message they both need to know the secret - one user uses the secret to encrypt the message, the other uses the same secret to decrypt it.

When you connect to a remote machine using symmetric authentication, you are the client (Alice) and the machine is the server (Bob).

Now in reality there are ways to obscure and protect this secret somewhat, but no matter how many clever tricks are used, we are still in the situation where the recipient has sensitive data - the user's secret. This is actually quite problematic because it means if we don't trust this user, or their machine or environment, we might not want to share a secret.

Asymmetric Encryption for Authentication​

When we use asymmetric encryption, Alice first creates a 'key pair'. This is two files - a public key and a private key. Alice keeps the private key and sends the public key to Bob:

loading...

Now that Bob has the public key, he creates a secret that only he knows and then encrypts it with Alice's public key. He then sends this secret to Alice. The secret was encrypted by Alice's Public Key - meaning only the associated Private Key can decrypt it. This means even if someone intercepts the secret they cannot decrypt it! Only Alice can, as only Alice has the private key:

loading...

Now Alice has Bob's secret and only she can decrypt it. Once she decrypts it both she and Bob have a shared secret - that no one has been able to intercept. They can now use Symmetric Encryption to exchange messages, safe in the knowledge that the secret is kept just between them.

The fantastic thing about this mechanism is that only Alice can decrypt messages encrypted with her public key. Alice can keep her key private. Her public key is not sensitive - it can only be used to encrypt messages for Alice.

Alice can also encrypt messages with her Private Key - anyone who has the Public Key can decrypt them. This means that this is not a secure way to encrypt a message - but it is a very good way to sign a message. Given that only Alice has the private key, only she can encrypt messages with it. This means if she sends a message encrypted with her private key anyone who has the public key can decrypt it to assert it was sent by Alice - she's the only person with the private key.

This method of signing messages with the Private Key is not used to keep the message private but instead to verify the identity of the sender.

Most modern day cryptography protocols are based on this technique. In many of them Bob will not actually send back a secret - instead he'll generate his own key-pair and return the Public Key. This is called a 'key exchange'. Almost all modern day encryption is built on this mechanism - when you open a secure connection to a website, an exchange of keys is made between you and the server2.

When we use the secure shell to communicate with a remote machine, we will give the remote machine our public key, and we will encrypt our communications with the private key. This is essential because other users might have access to the remote machine - we don't want them seeing our sensitive data such as passwords or private keys.

Let's look at how to create a key-pair now.

Creating a Key Pair​

We can use the ssh-keygen (OpenSSH authentication key utility) to create a key pair. We are going to use this key pair to communicate with a server that we will create on Amazon Web Services or AWS. AWS requires that we use a particular format known as PEM, so let's create a key pair in that format now. We will move to the ~/.ssh folder first, which is where users often store their SSH keys:

cd ~/.ssh
ssh-keygen -m PEM

When you run this command, it will ask you where you would like to save the file:

Generating public/private rsa key pair.
Enter file in which to save the key (/home/dwmkerr/.ssh/id_rsa):

I suggest that you use the default location (the ~/.ssh folder), but name the key pair 'effective-shell':

Enter file in which to save the key (/home/dwmkerr/.ssh/id_rsa): effective-shell
Enter passphrase (empty for no passphrase):

At this point you will be asked to provide an optional passphrase. We will leave this blank as we are not going to use the server or key for any particularly sensitive data. However, if you are creating keys that will encrypt sensitive data you should definitely add a passphrase. If you add a passphrase, you'll be required to enter each time you load the key. This adds a layer of security - if someone steals your private key they would have to know the passphrase to open it.

Once you have skipped the passphrase and passphrase confirmation by pressing the enter key twice, you'll see the final output:

Your identification has been saved in effective-shell
Your public key has been saved in effective-shell.pub
The key fingerprint is:
SHA256:HcqIl3ZhRz9jvhYO3g64FEYT3mAoDc6P4mnh4aKuY08 dwmkerr@macbook
The key's randomart image is:
+---[RSA 3072]----+
| .o .+ . |
| o. oo = . |
| o. * + = |
| + * * + o |
| + o * S o o |
| + = o o + + o |
|. *E o o = |
|o+. . . + |
|*o.. . . |
+----[SHA256]-----+

The randomart shown is designed to provide a more user-friendly way to identify a key - if you have many keys you can see the randomart for each to pick the one you want. I have not yet met anyone who remembers their randomart so I think it's safe for you to ignore it for now!

The essential thing is that we now have two new files - our public and private keys:

$ ls | grep effective
effective-shell
effective-shell.pub

The public key is the key with .pub at the end.

Now let's create a new virtual machine in the cloud and provide it with our public key so that we can access it. To do this, we're going to set up a virtual machine using Amazon Web Services - a popular cloud computing platform. We'll use the free tier, meaning this should not cost you any money!

Setting up an AWS Account​

We're going to create a virtual machine in the cloud to connect to and test out ssh. We'll do this using Amazon Web Services, which is probably the most popular cloud services provider. If you already have an account you can skip this part of the chapter. If not, we'll look at how to setup an account now.

To sign up for an AWS account, open the site at:

https://aws.amazon.com/account/sign-up

Choose the 'Create Free Account' option:

Screenshot: Create Free Account

When you sign up you will be asked to provide quite a few personal details, as well as credit card details. Make sure you select the "Basic Support Plan", which is free.

Avoiding Credit Card Bills

Amazon requires your credit card details in case you use paid services. In this chapter we will be using 'free tier' services that have no costs, but be very careful if experimenting or playing with other services on AWS - most of them are will have some cost associated with their usage.

The costs are generally low and there are safeguards in place to ensure you don't accidentally create expensive resources, but keep your AWS credentials very safe. If someone has access to them they could run expensive services on your account.

Once you have signed up successfully, you will see the 'congratulations' message. You can now press the 'Go to the AWS Management Console' button to go to the management console.

Screenshot: Congratulations

You will be asked for your password again. Congratulations - you now have an AWS account that you can use to run services in the cloud! Now let's create a virtual machine that we can connect to.

Creating a Virtual Machine on AWS​

Use the search bar in the AWS Management Console to search for 'EC2'. EC2 is the name AWS uses for its virtual machine services.

The first thing we are going to do is upload our public key to AWS so that it can be used when we create our virtual machine. Select the 'Key Pairs' option from the menu on the left or the dashboard:

Screenshot: Dashboard - Key Pairs

When the key pairs view is open, choose 'Actions > Import Key Pair':

Screenshot: Import Key Pair

Give the key pair a sensible name and upload the public key file, which will be in ~/.ssh/effective-shell.pub if you have been following with the tutorial. Alternatively you can open that file on your local machine and copy its contents:

Screenshot: Import Key Pair Details

Now that the key pair has been imported we can go back to the EC2 dashboard and choose 'Launch Instance':

Screenshot: Launch Instance

We are now going to go through a set of options to specify the details of the virtual machine AWS will create for us.

Step 1: Choose an Amazon Machine Image (AMI)

The first option should be Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type, with a 'Free Tier Eligible' label on the left. Press the blue 'Select' button on the right of this machine to choose this instance type.

Step 2: Choose an Instance Type

Select the default t2.micro instance type. This is free-tier eligible and more than powerful enough for our needs.

Now press 'Review and Launch' - we do not need to configure any of the advanced options.

Step 3: Review Instance Launch

There will be a warning saying that 'your security group is open to the world' on this page. We can safely ignore that as we are not putting anything sensitive on this instance. This message is telling us that anyone who knows the address of our instance can try and connect to it.

This is not a problem for what we are doing, as we are creating this machine to experiment with and not putting sensitive data on it.

Press the 'Launch' button on the bottom right - another screen will pop up, don't dismiss this screen, it is where we will choose our key pair!

We will be asked to provide a key pair. Make sure the key pair you just imported is selected!

Screenshot: Select Key Pair

You will be required to select the check box that says "I acknowledge that I have access to the private key" - this is AWS warning us that if we don't have the private key associated with the public key we uploaded, we will not be able to connect to the instance.

Once you have checked the checkbox, you can choose "Launch Instances".

Launching the instance will take a few seconds. Press the "View Instances" button when this is done.

Select the checkbox next to this new instance:

Screenshot: Select Instance

Once you have selected the instance, press the 'Connect' button. In the screen that pops up, choose 'SSH Client':

Screenshot: Connect to Instance

Keep this browser window open, it contains the details we need to know for the next steps.

Shut down your Virtual Machine when you are done with it!

When you have finished experimenting with your virtual machine, you should power it down. You can select it in the AWS Console and choose the 'Instance State > Stop Instance' option. When you are completely finished with the instance, choose 'Instance State > Terminate'. You should not be billed for this instance as it is in the free tier, but better safe than sorry!

Using SSH to connect to a virtual machine​

Now that we have created a virtual machine, we can use the ssh program to open a connection to it and run a secure shell.

When we want to SSH onto a machine, we need to provide a few details:

  1. The address of the machine, which is its hostname
  2. The username for the user we are going to connect with
  3. Credentials for the user we are going to connect with

The address of the machine we have created will be shown in the 'Connect to instance' page we should still have open from the AWS dashboard:

Screenshot: Instance SSH Details

AWS is giving us some hints here about how to connect, it is even showing the specific command we will run with ssh.

From our own shell we will now run the ssh command to connect to our instance. To connect, using the details for my new virtual machine, I would run this command:

ssh -i ~/.ssh/effective-shell ec2-user@ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com

You can see that we have provided three pieces of information:

  • The credentials, by using the -i (identity file) flag, providing our private key file
  • The username, which is the ec2-user part of the command, coming before the at symbol @
  • The hostname, which is the address that follows the @

When I run this command a warning is shown:

The authenticity of host 'ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com (13.213.71.135)' can't be established.
ED25519 key fingerprint is SHA256:8wq6Xu4xEk/BO3diae+BWUFTTKunzvCz4XidFYpl6F8.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?

This is my SSH client telling me that I haven't connected to this machine before so it cannot be sure that this machine is one I want to connect to. Once I continue, by entering yes and pressing enter, my SSH client will record the IP address of this machine, as well as it's hostname, meaning that in the future when I connect it will recognise it. If the IP address changes my SSH client will warn me - this is a useful security feature to protect against someone 'swapping' the machine you are connecting to for another one!

After typing yes and pressing enter to continue connecting, I will see Bash running in my AWS Linux virtual machine!

Warning: Permanently added 'ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com' (ED25519) to the list of known hosts.

__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-172-31-23-196 ~]$

I have a welcome message from AWS and are running a shell on my virtual machine, via the ssh program from my local machine!

We can see the current shell being used by checking the SHELL variable:

[ec2-user@ip-172-31-23-196 ~]$ echo $SHELL
/bin/bash

We are now ready to run any commands that we like on this machine - at this point you might install programs, manipulate files and so on.

When I want to disconnect, I can just run the exit command to close the connection to the virtual machine.

Hopefully if you have followed this far, you also have access to a virtual machine to play with. Now let's look at some of the things we can do with the SSH to make accessing these machines even easier!

Dealing with Key Permission Errors​

It is possible that when you try to use a private key to connect to a virtual machine, you will see an error message like the below:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'my-key.pem' are too open.
It is recommended that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: my-key.pem
Permission denied (publickey).

In this case the server is warning you that your private key could be opened by other users on the system. You can verify this by running ls -a to check the file permissions:

$ ls -al
-rw-r--r-- 1 dwmkerr dwmkerr 1103 Apr 19 12:02 my-key.pem

If you recall that the permissions are the first thing that is shown, you'll see that they are -rw-r--r--. This translates to:

  • -rw - read and write for the owner, which is the dwmkerr user
  • -r- - read to the group the owner belongs to, which is the dwmkerr group
  • -r- - read to all users

We can see that this is not ideal - other users on the system would be allowed to open the file, and members of the same group would also be allowed to read the file. There is a quick fix for this - set the permissions to read and write for the owner only:

$ chmod 400 my-key.pem
$ ls -al
-rw------- 1 dwmkerr dwmkerr 1103 Apr 19 12:02 my-key.pem

The chmod (change file permissions) command is used here to set the permissions to 400 - which means read and write for the current user. Once this change is made you will be able to SSH into the server without it complaining about the permissions on your key.

Configuring SSH Hosts​

It can be difficult to remember the details such as the host name, location of the key and username of your virtual machines. One thing you can do to make it far easier to connect is to create an entry in your SSH Config file that stores this information. This will let you connect much more quickly.

To create an entry for my virtual machine, I would add the following text to the ~/.ssh/config file:

Host effective-shell-aws-linux
HostName ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com
User ec2-user
IdentityFile "~/.ssh/effective-shell.pem"

The first part of this configuration is the host 'alias' - how you will refer to the host when you want to connect to it. It can be convenient to give this a short but descriptive name. After this, we have a set of settings, each indented by a tab or two spaces, the settings we have are:

  • HostName the full address of the host
  • User the name of the user to connect as
  • IdentityFile the path to the private key file used to connect

Now if I want to connect to the virtual machine, all I need to do is run the following command:

ssh effective-shell-aws-linux

In fact the ssh program supports shell completion, meaning I can just type ssh and a few letters of the host then the tab key - the shell will suggest the hosts from my config, so I don't even need to remember the name I set for it:

ssh e<tab> # when I press tab, the shell expands this to:
ssh effective-shell-aws-linux

There are many other options available for the SSH config file, you can see them all with man ssh_config. We'll see some other options in Chapter 33 - Master the Multiplexer.

Running SSH Commands​

You don't need to actually run a shell on a remote machine over SSH to execute commands. You can simply provide the commands that you want to run to the ssh program and it will execute them on the server.

Here's an example:

$ ssh effective-shell-aws-linux 'curl effective.sh | ES_EXISTING_FOLDER_ACTION=o sh'
...
effective-shell: installed samples version 0.25.1 to '/home/ec2-user/effective-shell'
effective-shell: read 'effective shell' online at: www.effective-shell.com

In this example we downloaded and ran the Effective Shell samples installer on the server. Now normally when we install the samples, the installer will ask the user whether to overwrite, delete or keep the existing samples. This means that it will be requesting input from the terminal. The ssh program is not actually attaching stdin to the remote machine, so we use the ES_EXISTING_FOLDER_ACTION=o option to tell the installer to overwrite the samples.

If we wanted to be able to interact with the server, using our terminal to provide input, we can use the -t (request TTY) parameter:

$ ssh -t effective-shell-aws-linux 'curl effective.sh | sh'
...
effective-shell: downloaded samples, version 0.25.1
effective-shell: preparing to install the 'effective-shell.com' samples...
effective-shell: the '/home/ec2-user/effective-shell' folder already exists, would you like to:
effective-shell: [d]elete - remove the existing folder
effective-shell: [o]verwrite - extract over the existing folder
effective-shell: [q]uit
Your choice (d/o/q): d

In this example my terminal is attached to the remote server via SSH, meaning I can use the keyboard to provide input to the installer script.

Handling Disconnections​

One thing that will soon become a pain if you are regularly SSH-ing into virtual machines is disconnections. This can occur when you lose network connectivity. You might not even notice that a disconnection has occurred - I find it is more common that the ssh session is simply frozen and not responding to any input at all.

It is annoying when this happens - often the shell is so unresponsive that you cannot even free it by pressing ^D or ^C. There is actually an escape sequence that you can use to end a broken session, which is the Enter then ~ then ..

In Chapter 33 - Master the Multiplexer we'll look at some great ways to improve upon this, for now if you forget the Enter/Tidle/Dot escape sequence the easiest thing to do is to close your terminal program.

When you disconnect from your SSH session, the commands you are running will be terminated. This is because your shell will send the 'hang up' signal. Again, this can be frustrating if you actually want to keep something running on the server. We'll also look at how to work around this behaviour in Chapter 33.

Transferring Files with SCP​

We can use the scp (OpenSSH secure file copy) program to copy files to and from any remote machine that we have access to.

The format of the scp command is:

scp [parameters] <source> <destination>

We can provide scp with parameters, many of which are the same as the ssh command, such as -i for the Identity File.

The source and destination are normally a pair of designations, one local, one remote. However, you can also copy between two remote machines.

Some examples of how a command might look like are:

scp -i ~/.ssh/my-project.pem ~/project/output.zip dwmkerr@myserver.com:~
  • This uses the -i flag to specify a key
  • The source is a file called output.zip on the local machine
  • The destination is a server with the hostname myserver.com, connecting with a user named dwmkerr, and putting the file in the user's home folder
scp -P 8022 effective-shell.com:~/downloads/backup.zip ~/backups
  • This copies the downloads/backups folder to the local ~/backups folder
  • No username or credentials are specified, so the current user's name is used and any keys that are loaded in the SSH agent will be used to try and authenticate
  • We use the -P (port number) flag to specify port 8022, rather than the default port which is 22

Let's see this in action by copying the lookup tool we created in Chapter 30 - How to Avoid Scripting to our server. Then we'll run it, save some definitions, and copy them back to our local machine.

First we'll use scp to copy our lookup program to our remote machine:

$ cd ~/effective-shell/samples
$ scp ./programs/lookup/lookup.py effective-shell-aws-linux:~
lookup.py 100% 4485 10.5KB/s 00:00

We have provided the path to the local file, and specified the server we want to copy the file to, as well as the location for the file (which is the home directory).

Note that because we have created the alias effective-shell-aws-linux in our SSH config, we can use this alias for scp as well as ssh. Let's connect to the server now and run the script:

$ ssh effective-shell-aws-linux
Last login: Tue Apr 5 19:03:00 2022 from bb116-15-249-218.singnet.com.sg

__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-172-31-23-196 ~]$ ./lookup.py cryptography
cryptography: The discipline concerned with communication security (eg, confiden
tiality of messages, integrity of messages, sender authentication, non-repudiati
on of messages, and many other related issues), regardless of the used medium su
ch as pencil and paper or computers.

We can see that our lookup program has been copied to our server and we can run it to find a definition for the word 'cryptography'.

Let's save this definition then close our connection to the server, then copy the definition back to our local machine:

[ec2-user@ip-172-31-23-196 ~]$ chmod +x ./lookup.py
[ec2-user@ip-172-31-23-196 ~]$ ./lookup.py cryptography > definition.txt
[ec2-user@ip-172-31-23-196 ~]$ exit
logout
Connection to ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com closed.

$ scp effective-shell-aws-linux:~/defintion.txt .
definition.txt 100% 277 1.0KB/s 00:00

$ cat definition.txt
cryptography: The discipline concerned with communication security (eg, confiden
tiality of messages, integrity of messages, sender authentication, non-repudiati
on of messages, and many other related issues), regardless of the used medium su
ch as pencil and paper or computers.

Before we ran the script on the server, we used the chmod (change file permissions) command to ensure the script can be executed. That's all there is to it! Copying files and folders to and from remote machines is remarkably easy to do with scp once you know the basics of how ssh works.

There are many other operations that you can perform with scp, you can read more about the tool with man scp.

Next Steps​

Once you are familiar with the core techniques introduced in this chapter, I would suggest that you look into configuration for bastion hosts, port forwarding and VPN tunneling - these are two more advanced scenarios for SSH. As a teaser for when you might want to use some of these techniques, consider the diagram below:

loading...

In this scenario we have two hosts, host1.myproject and host2.myproject in a private part of a network, that is not open to the internet. To access these hosts, we use a 'jump box' or 'bastion' - a host that is running in a part of the network that is accessible to external users. We carefully control access to this box and use it as way to connect to the private host.

Now you could SSH onto the bastion, and then SSH onto either of the hosts. But you can speed this process up by setting your ~/.ssh/config like so:

Host bastion
HostName bastion.myproject.com
IdentityFile ~/.ssh/myproject.pem
ForwardAgent yes

Host *.myproject
ProxyJump bastion

With this configuration we can connect to either of the private hosts by simply using the ssh command with the host name:

ssh host1.internal

The ssh program recognises that host1.internal matches the patter *.myproject from our configuration file. It then sees that we have set the ProxyJump option, which instructs ssh to connect through another host. The other host is the bastion, which is accessible to the user, and has the key specified. We also tell the bastion that it should have its authentication forwarded, so that our private key is used when connecting to the host in the private network (without this option when we connect to the bastion we will not have our keys accessible).

This network configuration is very common and you can see the SSH configuration file supports quickly and conveniently configuring the SSH agent to use the bastion.

There are many advanced options for SSH that will make more complex tasks like this easier to manage.

Summary​

In this chapter we discussed the SSH protocol, and how keys are used to protect connections to remote servers. We saw how to setup an AWS account, create a virtual machine with a given public key, connect to it with the ssh program, and configure SSH with an alias to make future connections faster. We also saw some of the challenges we can face with network connectivity - which we'll see techniques to handle in Chapter 33. Finally, we looked at how to copy files to and from remote machines.


  1. If you want to go deeper, my favourite book on this topic is "Applied Cryptography: Protocols, Algorithms, and Source Code in C - Bruce Schneier". There are more details at the end of the chapter and in the Recommended Reading section.↩
  2. This process is very useful to know about, it is called Diffie–Hellman key exchange. There are many great articles online that explain it in detail.↩
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/part-6-advanced-techniques/understanding-shell-expansion/index.html b/pr-preview/pr-346/part-6-advanced-techniques/understanding-shell-expansion/index.html new file mode 100644 index 00000000..706c1382 --- /dev/null +++ b/pr-preview/pr-346/part-6-advanced-techniques/understanding-shell-expansion/index.html @@ -0,0 +1,26 @@ + + + + + +Understanding Shell Expansion | Effective Shell + + + + + + + + + + + + + + +
+

Understanding Shell Expansion

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.

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.

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.

What is Shell Expansion?​

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.

The seven operations that the shell performs are:

  1. Brace Expansion - expanding values between braces, such as file{1..3} into file1 file2 file3
  2. Tilde Expansion - expanding the ~ tilde symbol for the home directory into the path to the home directory, such as ~/effective-shell into /home/dwmkerr/effective-shell
  3. Parameter Expansion - expanding terms that start with a $ symbol into parameter values, such as $HOME into the value of the variable named HOME
  4. Command Substitution - evaluation of the contents of $(command) sequences, which are used to run commands and return the results to the shell command line
  5. Arithmetic Expansion - evaluation of the contents of $((expression)) sequences, which are used to perform basic mathematical operations
  6. 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
  7. Pathname Expansion - the shell expands wildcards and special characters in pathnames, such as file*.txt into the set of files that are matched by the sequence

If you want to see each of these operations in the manual, you can run man bash and search for the text ^EXPANSION. Now let's see how each operation works in more detail.

Shell Expansion​

Let's take a look through each of the forms of shell expansion that are available to use.

Brace Expansion​

Brace expansion is the first shell expansion operation that occurs, it expands a simple expression that represents a sequence or range of characters.

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:

mkdir /tmp/{one,two,three}

# The line above is expanded to:
mdkir /tmp/one /tmp/two /tmp/three

Expansions of sets like this are a great way to perform operations that work on multiple files or folders at once.

We can also create sequences of numbers or characters:

touch file{1..5}.txt

# The line above is expanded to:
touch file1.txt file2.txt file3.txt file4.txt file5.txt

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:

for x in {0..10..2}; do print $x; done

# The line above is expanded to:
for x in 0 2 4 6 8 10; do print $x; done

Tilde Expansion​

If a word starts with a ~ tilde character, then the shell will expand the tilde into the value of the $HOME variable:

cd ~/effective-shell

# The line above is expanded to:
cd $HOME/effective-shell

If we were to unset the $HOME variable, then the expansion would use the current user's home directory:

unset HOME
cd ~/effective-shell

# The line above is expanded to:
cd /home/dwmkerr/effective-shell

Tilde expansion is very simple!

Parameter Expansion​

When the dollar symbol $ is used, this indicates that the shell is going to perform parameter expansion, which expands variables or the parameters of a script. It can also be used to indicate command substitution or arithmetic expansion - which we will see once we've looked at parameter expansion.

A lot of these expansions are covered in detail in Chapter 19 - Variables, Reading Input, and Mathematics but I have included each of the available expansions here for reference.

In its most simple form, parameter expansion simple replaces the name of a variable or parameter with its value:

fruit=apples
echo "I like $fruit"

# The line above is expanded to:
echo "I like apples"

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:

echo "My backup folder is: ${HOME}backup"

# The line above is expanded to:
echo "My backup folder is: /home/dwmkerrbackup"

If we had not used braces, then the shell would expand the expression like so:

echo "My backup folder is: $HOMEbackup"

# The line above is expanded to:
echo "My backup folder is: "

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 HOMEbackup - the braces used in the first example make it clear to the shell that the parameter name is HOME and that the text backup should be added at the end of the expanded value.

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.

Default Values

The expression ${parameter:-default} will expand to the value of the parameter named parameter - but if that value is not set, then the value 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.

Assign Default Values

The expression ${parameter:=default} will expand to the value of the parameter named parameter - but if that value is not set, then the value default is used. In this case, parameter is also set to default. This means that this expression works just like the 'default values' expression above, but also sets the parameter at the same time.

Display Error if Null or Unset

The expression ${parameter:?message} tells the shell to expand to the value of parameter, and if that value is null or unset, to instead write the message message to standard error and exit (unless the shell is interactive, in which case the shell is not closed).

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:

backup_location=${BACKUP_DIR:?Please set BACKUP_DIR to use this script}
cp -r ~/effective-shell ${BACKUP_DIR}

In this script we copy the ~/effective-shell folder to the folder set in the 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 BACKUP_DIR parameter must be set.

Use Alternate Value

The expression ${parameter:+alternate} expands to an empty string if parameter is null or unset. However, if parameter has a value, then the value of alternate is used instead.

Offset and Length

You can tell the shell to expand only a subset of the value of a parameter by using the ${parameter:offset} expression. In this case, the shell will expand the value of parameter, but skip offset number of characters from the beginning:

echo "My home folder name is: ${HOME:6}"

# The line above is expanded to:
echo "My home folder name is: dwmkerr"

You can also specify how many characters should be used by providing a length value after the offset with the expression ${parameter:offset:length}:

echo "The error message is: ${error_message:0:64}"

In the expression above, only up to the first 64 characters of the parameter error_message will be shown.

The offset and length values can also be used with arrays:

days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
echo "${days[@]:2:3}"

# The line above is expanded to:
echo "Tuesday Wednesday Thursday"

It is important to note that when using this technique with arrays, you must specify the array name and then [@] after the array name, to indicate that you want to work with all of the members of the array. If you don't do this, the entire array is converted into a single string and the resulting string has the offset and length applied.

Expand Variable Names

The ${!name*} expression evaluates to the name of every parameter that starts with the text name. You can use this expression to find the full set of parameters that match a certain pattern.

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:

_es_download_folder=~/downloads
_es_backup_folder=~/backups
_es_download_address=https://effective-shell.com/downloads/effective-shell-samples.tar.gz

# At this point we might have a script that uses the variables above...

# Now clean up any variables we created.
for var_name in ${!_es_*}
do
echo "Cleaning up: ${var_name}..."
unset ${var_name}
done

This is rather an advanced technique but it does show how the 'expand variable names' expansion can be useful.

Array Expansion

This topic is covered in detail in Chapter 19. The expression ${!array[@} expands to the indices (or 'keys') for each item in an array:

days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
echo "${!days[@]}"

# The line above is expanded to:
echo "0 1 2 3 4 5 6"

This expansion is convenient if you do not know the keys that make up an array and want to loop through them.

Parameter Length

The ${#parameter} expression expands to the length of the value in the parameter named parameter.

You can also use this expression to find the length of an array - just add the [@] subscript like so ${#array[@]}:

days=("Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")
echo "There are ${#days[@]} days in the array"

# The line above is expanded to:
echo "There are 7 days in the array"

You may have noticed at pattern by this point - many of the expansions that can be performed on a parameter can also be performed on an array, just by adding the [@] 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.

Remove Pattern from Front

You can use the ${parameter#pattern} expression to expand the value of parameter, removing pattern from the front of the value:

address=https://effective-shell.com
echo "Address: ${address#https://}"

# The line above is expanded to:
echo "Address: effective-shell.com"

You can also tell the shell to remove as many sequential matches of pattern as possible, by using the ${parameter##pattern} expression. This can be useful to strip out all of the characters up to a certain point in a parameter:

folder=/home/dwmkerr/backups/2021-10-19
echo "Today's backup folder is: ${folder##*/}"

# The line above is expanded to:
echo "Today's backup folder is: 2021-10-19"

Notice that in this example we are using an asterisk * 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 / is found.

Remove Pattern from Back

The ${parameter%pattern} expression works exactly like the expression above, but removes text from the end of a parameter:

echo "My working directory is: ${PWD}"
echo "My parent folder is: ${PWD%/*}"

# The lines above are expanded to:
echo "My working directory is: /home/dwmkerr/repos/github/dwmkerr/effective-shell"
echo "My parent folder is: /home/dwmkerr/repos/github/dwmkerr"

In this example we used an asterisk * wildcard in the pattern to remove all of the text from the back of the parameter, up to and including the first forward-slash / symbol found.

We can also remove as many matches as possible, by using the expression ${parameter%%pattern}:

archive=effective-shell.tar.gz
echo "Name of archive is: ${archive%%.*}"

# The line above is expanded to:
echo "Name of archive is: effective-shell"

Notice that in this case the removal of the characters did not stop at the first period . symbol, it removed as many characters as possible until the last period . symbol was found.

Pattern Replacement

You can also replace a pattern in a parameter by using the expression ${parameter/pattern/string}. This can be used to perform substitutions:

message="Hello Dave"
echo "${message/Hello/Goodbye}"

# The line above is expanded to:
echo "Goodbye Dave"

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 tr or 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.

For suggestions on alternative ways to manipulate text check Chapter 15 - Slice and Dice Text or Chapter 16 - Advanced Text Manipulation with Sed.

Lowercase or Uppercase

You can use the ${parameter^^} expression to return the value of parameter converted to uppercase. You can also use the ${parameter,,} expression to return the value of parameter converted to lowercase. An example is below:

message="Hello Reader"
echo ${message^^}
echo ${message,,}

The output of this script is:

HELLO READER
hello reader

Parameter Indirection

If you want to get the value of a parameter that has an arbitrary name you can use the ${!parameter_name} expression. This will return the value of the parameter that has the name of the value of parameter_name - you can see this in action like so:

parameter_name="HOME"
echo "${!parameter_name}"

The output of this script is:

/home/dwmkerr

This can be very useful if you are writing scripts that will work with arbitrary or variable parameter names.

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 Chapter 19 - Variables, Reading Input, and Mathematics.

Command Substitution​

The second form of expansion that starts with a dollar $ symbol is command substitution. This form of expansion instructs the shell to run a specific command. The syntax is simply $(comand).

We have seen command substitution throughout the book - in the example below we expand the date command to print the current date:

echo "The date is: $(date)"

# The line above is expanded to:
echo "The date is: Tue Oct 19 16:49:07 +08 2021"

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:

archives=$(find ~/downloads -type f -name "*.tar.gz")

In this command we store the results of the find operation in the parameter named archives.

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:

archives=`find ~/downloads -type f -name "*.tar.gz"`

You may see this syntax from time to time, however I would suggest that you avoid it. The reason is that you cannot 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 result=$(command1 $(command2)).

Arithmetic Expansion​

The final form of shell expansion that starts with a dollar symbol $ is arithmetic expansion. This expansion can be used to perform simple arithmetic expressions:

echo "The result of 23*4 is: $((23*4))"
echo "The result of 23*4 is: 92"

Arithmetic expansion is covered in detail in Chapter 19 - Variables, Reading Input, and Mathematics.

Word Splitting​

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 any expansion that starts with a dollar symbol $ and that does not occur within double quotes.

The fact that word splitting only occurs if a substitution does 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.

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:

days="Monday Tuesday Wednesday      Thursday Friday   Saturday Sunday"
for day in "$days"
do
echo "${day}"
done

The output of this script is:

Monday Tuesday Wednesday      Thursday Friday   Saturday Sunday

In the expression for day in "$day" we are using shell parameter expansion to expand the days parameter. We have surrounded $day in quotes - this means that we are telling the shell 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.

Now let's run the same script but this time we will not surround $days in quotes, meaning that the shell will perform word splitting:

days="Monday Tuesday Wednesday      Thursday Friday   Saturday Sunday"
for day in $days
do
echo "${day}"
done

The output of this script is:

Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday

In this case we can see that word splitting has occurred. The shell has performed the following operations:

  1. First, it searches through each character in the input
  2. Every time it finds a character in the IFS (Input Field Separator) special variable, it splits the word
  3. If there are multiple instances of a separator character, they are removed, and replaced with a single instance only

By default, the IFS variable is set to <space><tab><newline>. 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 Wednesday value), they are replaced with a single instance of the first character (a space in this case) and then the splitting occurs.

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:

programs="/usr/bin/bash /usr/bin/zshell /usr/bin/new shell"
for program in $programs
do
echo "${program}"
done

The output of this script is:

/usr/bin/bash
/usr/bin/zshell
/usr/bin/new
shell

The final command, which has a space in the name, has been split into two words. You could avoid this issue by temporarily changing the value of IFS to use a different separator for words:

programs="/usr/bin/bash;/usr/bin/zshell;/usr/bin/new shell"
OLDIFS=$IFS
IFS=';'
for program in $programs
do
echo "${program}"
done
IFS=$OLDIFS

The output of this script is:

/usr/bin/bash
/usr/bin/zshell
/usr/bin/new shell

In this script we saved the original value of IFS into a parameter called OLDIFS, changed 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 IFS back to its original value.

You should be careful when changing IFS to make sure that you change it back to its original value straight afterwards - other programs or commands might expect IFS to be set to the default value so it should only be changed with caution.

If you were to look at the contents of the PATH variable, which specifies the locations the shell should search for commands, you will see that they are actually separated by colons:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

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 IFS to a colon character to get each of the paths - even if they contain spaces:

OLDIFS=$IFS
IFS=":"
for path in $PATH
do
echo "${path}"
done
IFS=$OLDIFS

The output of this script will look something like this:

/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games

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.

Pathname Expansion​

When the shell encounters the asterisk *, question mark ? or open square brackets [ characters, it marks the beginning of an expression that will have 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:

$ ls ~/downloads/*.tar.gz
/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64 (1).tar.gz
/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64.tar.gz
/home/dwmkerr/downloads/dotnet-sdk-3.1.412-osx-x64.tar.gz
/home/dwmkerr/downloads/effective-shell-playground.tar.gz
/home/dwmkerr/downloads/effective-shell-samples (1).tar.gz
/home/dwmkerr/downloads/effective-shell-samples (2).tar.gz
/home/dwmkerr/downloads/effective-shell-samples.tar.gz

This script shows all of the files in the ~/downloads folder that match the pattern *.tar.gz. The results you see will depend on what you have in your own ~/downloads folder!

It is important to remember that the shell performs all of the types of expansion that we have described in order. This means that word expansion is performed 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:

for $path in ~/downloads/*.tar.gz
do
echo "${path}"
done

The result of this script is:

/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64 (1).tar.gz
/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64.tar.gz
/home/dwmkerr/downloads/dotnet-sdk-3.1.412-osx-x64.tar.gz
/home/dwmkerr/downloads/effective-shell-playground.tar.gz
/home/dwmkerr/downloads/effective-shell-samples (1).tar.gz
/home/dwmkerr/downloads/effective-shell-samples (2).tar.gz
/home/dwmkerr/downloads/effective-shell-samples.tar.gz

Note that the spaces in the path names have been preserved - pathname expansion happens after word splitting - so the paths themselves are left as-is.

As well as the asterisk * character, which can be used as a wildcard character in pathname expansion, there is also the question mark ? character which means 'any single character'. You can also use expressions such as [abc] to match on a range of characters. The exact details of how these special characters are used can be found in man bash.

One feature of pathname expansion that people can sometimes be surprised by is what happens if the shell finds no files that match the pattern. You can see this in action below:

$ echo ~/effective-shell/*.txt
/home/dwmkerr/effective-shell/*.txt

There are no files in the ~/effective-shell folder that match the pattern *.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!

For example, if I wanted to run the touch command on a set of files, I would do the following:

for file in ~/effective-shell/*.txt
# If the file / folder doesn't exist, skip it.
if ! [ -e "$file" ]; then continue; fi
touch "$file"
do

In this script we first check to see whether the file or folder exists by using the -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 Chapter 21 - Loops and working with Files and Folders.

Pathname expansion has limitations - if you need a more sophisticated way to search for a set of files, check Chapter 11 - Finding Files.

Summary​

In this chapter we went into the lower level details of how 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 what the shell does with the input you provide it in your commands.

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.

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.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/search-index.json b/pr-preview/pr-346/search-index.json new file mode 100644 index 00000000..bc1adaed --- /dev/null +++ b/pr-preview/pr-346/search-index.json @@ -0,0 +1 @@ +[{"documents":[{"i":1,"t":"","u":"/donate","b":["Donate"]},{"i":2,"t":"","u":"/donate/thanks","b":["Donate"]},{"i":3,"t":"Thanks","u":"/appendices/thanks/","b":["Home","Appendices"]},{"i":5,"t":"hack-on","u":"/core-skills/what-is-a-shell/hack-on","b":[]},{"i":16,"t":"Part 1 - Transitioning to the Shell","u":"/part-1-transitioning-to-the-shell/","b":["Home","Transitioning to the Shell"]},{"i":18,"t":"Becoming a Clipboard Gymnast","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","b":["Home","Transitioning to the Shell"]},{"i":34,"t":"Getting Help","u":"/part-1-transitioning-to-the-shell/getting-help/","b":["Home","Transitioning to the Shell"]},{"i":54,"t":"Getting Started","u":"/part-1-transitioning-to-the-shell/getting-started/","b":["Home","Transitioning to the Shell"]},{"i":84,"t":"The Renaissance of the Shell","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","b":["Home","Transitioning to the Shell"]},{"i":96,"t":"Managing Your Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","b":["Home","Transitioning to the Shell"]},{"i":126,"t":"Navigating Your System","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","b":["Home","Transitioning to the Shell"]},{"i":146,"t":"Part 2 - Core Skills","u":"/part-2-core-skill/","b":["Home","Core Skills"]},{"i":148,"t":"Fly on the Command Line","u":"/part-2-core-skills/fly-on-the-command-line","b":["Home","Core Skills"]},{"i":186,"t":"Finding Files","u":"/part-2-core-skills/finding-files","b":["Home","Core Skills"]},{"i":222,"t":"Job Control","u":"/part-2-core-skills/job-control","b":["Home","Core Skills"]},{"i":244,"t":"Thinking in Pipelines","u":"/part-2-core-skills/thinking-in-pipelines/","b":["Home","Core Skills"]},{"i":263,"t":"What is a Shell?","u":"/part-2-core-skills/what-is-a-shell","b":["Home","Core Skills"]},{"i":293,"t":"Understanding Commands","u":"/part-2-core-skills/understanding-commands","b":["Home","Core Skills"]},{"i":331,"t":"Part 3 - Manipulating Text and Streams","u":"/part-3-manipulating-text/","b":["Home","Manipulating Text and Streams"]},{"i":333,"t":"Regex Essentials","u":"/part-3-manipulating-text/regex-essentials/","b":["Home","Manipulating Text and Streams"]},{"i":357,"t":"Advanced Text Manipulation","u":"/part-3-manipulating-text/advanced-text-manipulation/","b":["Home","Manipulating Text and Streams"]},{"i":387,"t":"Get to Grips with Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","b":["Home","Manipulating Text and Streams"]},{"i":411,"t":"Shell Script Essentials","u":"/part-3-manipulating-text/shell-script-essentials/","b":["Home","Shell Scripting"]},{"i":443,"t":"Slice and Dice Text","u":"/part-3-manipulating-text/slice-and-dice-text/","b":["Home","Manipulating Text and Streams"]},{"i":459,"t":"Build Commands on the Fly","u":"/part-3-manipulating-text/build-commands-on-the-fly/","b":["Home","Manipulating Text and Streams"]},{"i":475,"t":"Part 4 - Shell Scripting","u":"/part-4-shell-scripting/","b":["Home","Shell Scripting"]},{"i":477,"t":"Variables, Reading Input, and Mathematics","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","b":["Home","Shell Scripting"]},{"i":515,"t":"Functions, Parameters and Error Handling","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","b":["Home","Shell Scripting"]},{"i":547,"t":"Mastering Conditional Logic","u":"/part-4-shell-scripting/mastering-conditional-logic","b":["Home","Shell Scripting"]},{"i":577,"t":"Loops and working with Files and Folders","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","b":["Home","Shell Scripting"]},{"i":617,"t":"Part 5 - Building Your Toolkit","u":"/part-5-building-your-toolkit/","b":["Home","Building Your Toolkit"]},{"i":619,"t":"Configuring the Shell","u":"/part-5-building-your-toolkit/configuring-the-shell","b":["Home","Building Your Toolkit"]},{"i":651,"t":"Managing Remote Git Repositories and Sharing Your Dotfiles","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","b":["Home","Building Your Toolkit"]},{"i":675,"t":"Controlling Changes with Git","u":"/part-5-building-your-toolkit/controlling-changes-with-git","b":["Home","Building Your Toolkit"]},{"i":705,"t":"Customising Your Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","b":["Home","Building Your Toolkit"]},{"i":727,"t":"Part 6 - Advanced Techniques","u":"/part-6-advanced-techniques/","b":["Home","Advanced Techniques"]},{"i":729,"t":"Managing your Dotfiles","u":"/part-5-building-your-toolkit/managing-your-dotfiles","b":["Home","Building Your Toolkit"]},{"i":749,"t":"A Vim Crash Course","u":"/part-6-advanced-techniques/a-vim-crash-course/","b":["Home","Advanced Techniques"]},{"i":791,"t":"How to Avoid Scripting - A Dictionary Lookup in Python","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","b":["Home","Advanced Techniques"]},{"i":813,"t":"Master the Multiplexer","u":"/part-6-advanced-techniques/master-the-multiplexer/","b":["Home","Advanced Techniques"]},{"i":847,"t":"Useful Patterns for Shell Scripts","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","b":["Home","Shell Scripting"]},{"i":881,"t":"The Secure Shell","u":"/part-6-advanced-techniques/the-secure-shell/","b":["Home","Advanced Techniques"]},{"i":913,"t":"Work in Progress!","u":"/work-in-progress","b":[]},{"i":915,"t":"essential-manpages","u":"/xx-appendices/essential-manpages","b":[]},{"i":919,"t":"Good Scripts to write as exercises","u":"/xx-appendices/exercises","b":[]},{"i":920,"t":"Installing the Samples","u":"/xx-appendices/installing-samples/","b":["Home","Appendices"]},{"i":926,"t":"index-of-commands","u":"/xx-appendices/index-of-commands","b":[]},{"i":928,"t":"Posix","u":"/xx-appendices/posix","b":[]},{"i":932,"t":"Recommended Reading","u":"/xx-appendices/recommended-reading/","b":["Home","Appendices"]},{"i":970,"t":"shell-parameter-expansion","u":"/xx-appendices/shell-parameter-expansion","b":[]},{"i":974,"t":"the-future","u":"/xx-appendices/the-future","b":[]},{"i":976,"t":"Images and Diagrams","u":"/zz-developer-guide/images-and-diagrams","b":["Home","Developer Guide"]},{"i":982,"t":"Components","u":"/zz-developer-guide/components","b":["Home","Developer Guide"]},{"i":992,"t":"Recording Terminal Sessions","u":"/zz-developer-guide/recording-terminal-sessions","b":["Home","Developer Guide"]},{"i":998,"t":"Markdown","u":"/zz-developer-guide/markdown-guide","b":["Home","Developer Guide"]},{"i":1016,"t":"Understanding Shell Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","b":["Home","Advanced Techniques"]}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/1",[]],["t/2",[]],["t/3",[0,4.85]],["t/5",[1,4.85]],["t/16",[2,1.769,3,2.964,4,2.964,5,1.378]],["t/18",[6,3.405,7,3.405,8,3.405]],["t/34",[9,3.439,10,4.001]],["t/54",[9,3.439,11,4.001]],["t/84",[5,1.861,12,4.001]],["t/96",[13,3.069,14,3.069]],["t/126",[15,4.001,16,4.001]],["t/146",[2,1.769,17,2.964,18,2.964,19,2.964]],["t/148",[20,2.927,21,2.189,22,3.405]],["t/186",[14,3.069,23,4.001]],["t/222",[24,4.001,25,3.439]],["t/244",[26,4.001,27,4.001]],["t/263",[5,2.256]],["t/293",[21,2.572,28,3.439]],["t/331",[2,1.566,29,2.624,30,2.255,31,2.013,32,2.624]],["t/333",[33,4.001,34,3.069]],["t/357",[30,2.927,31,2.612,35,2.927]],["t/387",[36,4.001,37,4.001]],["t/411",[5,1.584,34,2.612,38,2.189]],["t/443",[31,2.612,39,3.405,40,3.405]],["t/459",[20,2.927,21,2.189,41,2.927]],["t/475",[2,1.769,5,1.378,38,1.905,42,2.964]],["t/477",[43,2.964,44,2.548,45,2.964,46,2.964]],["t/515",[47,2.964,48,2.548,49,2.964,50,2.964]],["t/547",[51,2.927,52,3.405,53,3.405]],["t/577",[14,2.274,54,2.964,55,2.548,56,2.964]],["t/617",[2,1.769,41,2.548,57,2.964,58,2.964]],["t/619",[5,1.861,59,4.001]],["t/651",[13,1.805,60,2.354,61,2.023,62,2.354,63,2.354,64,2.023]],["t/675",[25,2.927,61,2.927,65,3.405]],["t/705",[21,2.189,66,3.405,67,3.405]],["t/727",[2,1.769,35,2.548,68,2.964,69,2.964]],["t/729",[13,3.069,64,3.439]],["t/749",[70,3.405,71,3.405,72,3.405]],["t/791",[38,1.687,73,2.624,74,2.624,75,2.624,76,2.624]],["t/813",[51,3.439,77,4.001]],["t/847",[5,1.378,38,1.905,78,2.964,79,2.964]],["t/881",[5,1.861,80,4.001]],["t/913",[55,3.439,81,4.001]],["t/915",[34,3.069,82,4.001]],["t/919",[38,1.905,83,2.964,84,2.964,85,2.964]],["t/920",[86,4.001,87,4.001]],["t/926",[21,2.572,88,4.001]],["t/928",[89,4.85]],["t/932",[44,3.439,90,4.001]],["t/970",[5,1.584,48,2.927,91,2.927]],["t/974",[92,4.85]],["t/976",[93,4.001,94,4.001]],["t/982",[95,4.85]],["t/992",[96,3.405,97,3.405,98,3.405]],["t/998",[99,4.85]],["t/1016",[5,1.584,28,2.927,91,2.927]]],"invertedIndex":[["1",{"_index":3,"t":{"16":{"position":[[5,1]]}}}],["2",{"_index":17,"t":{"146":{"position":[[5,1]]}}}],["3",{"_index":29,"t":{"331":{"position":[[5,1]]}}}],["4",{"_index":42,"t":{"475":{"position":[[5,1]]}}}],["5",{"_index":57,"t":{"617":{"position":[[5,1]]}}}],["6",{"_index":68,"t":{"727":{"position":[[5,1]]}}}],["advanc",{"_index":35,"t":{"357":{"position":[[0,8]]},"727":{"position":[[9,8]]}}}],["avoid",{"_index":73,"t":{"791":{"position":[[7,5]]}}}],["becom",{"_index":6,"t":{"18":{"position":[[0,8]]}}}],["build",{"_index":41,"t":{"459":{"position":[[0,5]]},"617":{"position":[[9,8]]}}}],["chang",{"_index":65,"t":{"675":{"position":[[12,7]]}}}],["clipboard",{"_index":7,"t":{"18":{"position":[[11,9]]}}}],["command",{"_index":21,"t":{"148":{"position":[[11,7]]},"293":{"position":[[14,8]]},"459":{"position":[[6,8]]},"705":{"position":[[17,7]]},"926":{"position":[[9,8]]}}}],["compon",{"_index":95,"t":{"982":{"position":[[0,10]]}}}],["condit",{"_index":52,"t":{"547":{"position":[[10,11]]}}}],["configur",{"_index":59,"t":{"619":{"position":[[0,11]]}}}],["control",{"_index":25,"t":{"222":{"position":[[4,7]]},"675":{"position":[[0,11]]}}}],["core",{"_index":18,"t":{"146":{"position":[[9,4]]}}}],["cours",{"_index":72,"t":{"749":{"position":[[12,6]]}}}],["crash",{"_index":71,"t":{"749":{"position":[[6,5]]}}}],["customis",{"_index":66,"t":{"705":{"position":[[0,11]]}}}],["diagram",{"_index":94,"t":{"976":{"position":[[11,8]]}}}],["dice",{"_index":40,"t":{"443":{"position":[[10,4]]}}}],["dictionari",{"_index":74,"t":{"791":{"position":[[27,10]]}}}],["dotfil",{"_index":64,"t":{"651":{"position":[[50,8]]},"729":{"position":[[14,8]]}}}],["error",{"_index":49,"t":{"515":{"position":[[26,5]]}}}],["essenti",{"_index":34,"t":{"333":{"position":[[6,10]]},"411":{"position":[[13,10]]},"915":{"position":[[0,9]]}}}],["exercis",{"_index":85,"t":{"919":{"position":[[25,9]]}}}],["expans",{"_index":91,"t":{"970":{"position":[[16,9]]},"1016":{"position":[[20,9]]}}}],["file",{"_index":14,"t":{"96":{"position":[[14,5]]},"186":{"position":[[8,5]]},"577":{"position":[[23,5]]}}}],["find",{"_index":23,"t":{"186":{"position":[[0,7]]}}}],["fli",{"_index":20,"t":{"148":{"position":[[0,3]]},"459":{"position":[[22,3]]}}}],["folder",{"_index":56,"t":{"577":{"position":[[33,7]]}}}],["function",{"_index":47,"t":{"515":{"position":[[0,10]]}}}],["futur",{"_index":92,"t":{"974":{"position":[[4,6]]}}}],["get",{"_index":9,"t":{"34":{"position":[[0,7]]},"54":{"position":[[0,7]]}}}],["git",{"_index":61,"t":{"651":{"position":[[16,3]]},"675":{"position":[[25,3]]}}}],["good",{"_index":83,"t":{"919":{"position":[[0,4]]}}}],["grep",{"_index":37,"t":{"387":{"position":[[18,4]]}}}],["grip",{"_index":36,"t":{"387":{"position":[[7,5]]}}}],["gymnast",{"_index":8,"t":{"18":{"position":[[21,7]]}}}],["hack",{"_index":1,"t":{"5":{"position":[[0,4]]}}}],["handl",{"_index":50,"t":{"515":{"position":[[32,8]]}}}],["help",{"_index":10,"t":{"34":{"position":[[8,4]]}}}],["imag",{"_index":93,"t":{"976":{"position":[[0,6]]}}}],["index",{"_index":88,"t":{"926":{"position":[[0,5]]}}}],["input",{"_index":45,"t":{"477":{"position":[[19,6]]}}}],["instal",{"_index":86,"t":{"920":{"position":[[0,10]]}}}],["job",{"_index":24,"t":{"222":{"position":[[0,3]]}}}],["line",{"_index":22,"t":{"148":{"position":[[19,4]]}}}],["logic",{"_index":53,"t":{"547":{"position":[[22,5]]}}}],["lookup",{"_index":75,"t":{"791":{"position":[[38,6]]}}}],["loop",{"_index":54,"t":{"577":{"position":[[0,5]]}}}],["manag",{"_index":13,"t":{"96":{"position":[[0,8]]},"651":{"position":[[0,8]]},"729":{"position":[[0,8]]}}}],["manipul",{"_index":30,"t":{"331":{"position":[[9,12]]},"357":{"position":[[14,12]]}}}],["manpag",{"_index":82,"t":{"915":{"position":[[10,8]]}}}],["markdown",{"_index":99,"t":{"998":{"position":[[0,8]]}}}],["master",{"_index":51,"t":{"547":{"position":[[0,9]]},"813":{"position":[[0,6]]}}}],["mathemat",{"_index":46,"t":{"477":{"position":[[30,11]]}}}],["multiplex",{"_index":77,"t":{"813":{"position":[[11,11]]}}}],["navig",{"_index":15,"t":{"126":{"position":[[0,10]]}}}],["paramet",{"_index":48,"t":{"515":{"position":[[11,10]]},"970":{"position":[[6,9]]}}}],["part",{"_index":2,"t":{"16":{"position":[[0,4]]},"146":{"position":[[0,4]]},"331":{"position":[[0,4]]},"475":{"position":[[0,4]]},"617":{"position":[[0,4]]},"727":{"position":[[0,4]]}}}],["pattern",{"_index":79,"t":{"847":{"position":[[7,8]]}}}],["pipelin",{"_index":27,"t":{"244":{"position":[[12,9]]}}}],["posix",{"_index":89,"t":{"928":{"position":[[0,5]]}}}],["progress",{"_index":81,"t":{"913":{"position":[[8,9]]}}}],["prompt",{"_index":67,"t":{"705":{"position":[[25,6]]}}}],["python",{"_index":76,"t":{"791":{"position":[[48,6]]}}}],["read",{"_index":44,"t":{"477":{"position":[[11,7]]},"932":{"position":[[12,7]]}}}],["recommend",{"_index":90,"t":{"932":{"position":[[0,11]]}}}],["record",{"_index":96,"t":{"992":{"position":[[0,9]]}}}],["regex",{"_index":33,"t":{"333":{"position":[[0,5]]}}}],["remot",{"_index":60,"t":{"651":{"position":[[9,6]]}}}],["renaiss",{"_index":12,"t":{"84":{"position":[[4,11]]}}}],["repositori",{"_index":62,"t":{"651":{"position":[[20,12]]}}}],["sampl",{"_index":87,"t":{"920":{"position":[[15,7]]}}}],["script",{"_index":38,"t":{"411":{"position":[[6,6]]},"475":{"position":[[15,9]]},"791":{"position":[[13,9]]},"847":{"position":[[26,7]]},"919":{"position":[[5,7]]}}}],["secur",{"_index":80,"t":{"881":{"position":[[4,6]]}}}],["session",{"_index":98,"t":{"992":{"position":[[19,8]]}}}],["share",{"_index":63,"t":{"651":{"position":[[37,7]]}}}],["shell",{"_index":5,"t":{"16":{"position":[[30,5]]},"84":{"position":[[23,5]]},"263":{"position":[[10,6]]},"411":{"position":[[0,5]]},"475":{"position":[[9,5]]},"619":{"position":[[16,5]]},"847":{"position":[[20,5]]},"881":{"position":[[11,5]]},"970":{"position":[[0,5]]},"1016":{"position":[[14,5]]}}}],["skill",{"_index":19,"t":{"146":{"position":[[14,6]]}}}],["slice",{"_index":39,"t":{"443":{"position":[[0,5]]}}}],["start",{"_index":11,"t":{"54":{"position":[[8,7]]}}}],["stream",{"_index":32,"t":{"331":{"position":[[31,7]]}}}],["system",{"_index":16,"t":{"126":{"position":[[16,6]]}}}],["techniqu",{"_index":69,"t":{"727":{"position":[[18,10]]}}}],["termin",{"_index":97,"t":{"992":{"position":[[10,8]]}}}],["text",{"_index":31,"t":{"331":{"position":[[22,4]]},"357":{"position":[[9,4]]},"443":{"position":[[15,4]]}}}],["thank",{"_index":0,"t":{"3":{"position":[[0,6]]}}}],["think",{"_index":26,"t":{"244":{"position":[[0,8]]}}}],["toolkit",{"_index":58,"t":{"617":{"position":[[23,7]]}}}],["transit",{"_index":4,"t":{"16":{"position":[[9,13]]}}}],["understand",{"_index":28,"t":{"293":{"position":[[0,13]]},"1016":{"position":[[0,13]]}}}],["us",{"_index":78,"t":{"847":{"position":[[0,6]]}}}],["variabl",{"_index":43,"t":{"477":{"position":[[0,10]]}}}],["vim",{"_index":70,"t":{"749":{"position":[[2,3]]}}}],["work",{"_index":55,"t":{"577":{"position":[[10,7]]},"913":{"position":[[0,4]]}}}],["write",{"_index":84,"t":{"919":{"position":[[16,5]]}}}]],"pipeline":["stemmer"]}},{"documents":[{"i":7,"t":"Hack On! See those system calls!","u":"/core-skills/what-is-a-shell/hack-on","h":"#hack-on-see-those-system-calls","p":5},{"i":9,"t":"Hack On! Ahah! So that's TTY?","u":"/core-skills/what-is-a-shell/hack-on","h":"#hack-on-ahah-so-thats-tty","p":5},{"i":11,"t":"TODO","u":"/core-skills/what-is-a-shell/hack-on","h":"#todo","p":5},{"i":12,"t":"Summary; technical","u":"/core-skills/what-is-a-shell/hack-on","h":"#summary-technical","p":5},{"i":14,"t":"Summary; non-tech","u":"/core-skills/what-is-a-shell/hack-on","h":"#summary-non-tech","p":5},{"i":20,"t":"The Clipboard Essentials","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#the-clipboard-essentials","p":18},{"i":22,"t":"Preparing the Clipboard Commands","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#preparing-the-clipboard-commands","p":18},{"i":24,"t":"Copy and Paste Basics","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#copy-and-paste-basics","p":18},{"i":26,"t":"Removing Formatting","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#removing-formatting","p":18},{"i":28,"t":"Sorting Text","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#sorting-text","p":18},{"i":30,"t":"Manipulating Text","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#manipulating-text","p":18},{"i":32,"t":"Summary","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#summary","p":18},{"i":36,"t":"Getting Help is Important!","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#getting-help-is-important","p":34},{"i":38,"t":"Understanding 'man'","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#understanding-man","p":34},{"i":40,"t":"Getting help on a command","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#getting-help-on-a-command","p":34},{"i":42,"t":"Using the pager","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#using-the-pager","p":34},{"i":44,"t":"The Alternative - Help","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#the-alternative---help","p":34},{"i":46,"t":"Manual Sections","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#manual-sections","p":34},{"i":48,"t":"Introducing tl;dr","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#introducing-tldr","p":34},{"i":50,"t":"The Online Cheatsheet","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#the-online-cheatsheet","p":34},{"i":52,"t":"Summary","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#summary","p":34},{"i":56,"t":"What is the Shell?","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#what-is-the-shell","p":54},{"i":58,"t":"Opening the Shell","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#opening-the-shell","p":54},{"i":60,"t":"Microsoft Windows","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#microsoft-windows","p":54},{"i":62,"t":"MacOS","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#macos","p":54},{"i":64,"t":"Linux / Unix","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#linux--unix","p":54},{"i":66,"t":"Configuring the Shell","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#configuring-the-shell","p":54},{"i":68,"t":"Microsoft Windows","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#microsoft-windows-1","p":54},{"i":70,"t":"MacOS","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#macos-1","p":54},{"i":72,"t":"Linux","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#linux","p":54},{"i":74,"t":"That's It!","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#thats-it","p":54},{"i":76,"t":"A Quick Demo of the Shell","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#a-quick-demo-of-the-shell","p":54},{"i":78,"t":"The Echo Command","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#the-echo-command","p":54},{"i":80,"t":"Move Around","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#move-around","p":54},{"i":82,"t":"Summary","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#summary","p":54},{"i":86,"t":"Is there a Renaissance of the Shell?","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#is-there-a-renaissance-of-the-shell","p":84},{"i":88,"t":"The Changing Technology Landscape","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#the-changing-technology-landscape","p":84},{"i":90,"t":"The Diversity of Programming Languages","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#the-diversity-of-programming-languages","p":84},{"i":92,"t":"Convergence of Operating Platforms","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#convergence-of-operating-platforms","p":84},{"i":94,"t":"DevOps","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#devops","p":84},{"i":98,"t":"Creating a Playground","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#creating-a-playground","p":96},{"i":100,"t":"Finding out about files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#finding-out-about-files","p":96},{"i":102,"t":"Extracting the Zip","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#extracting-the-zip","p":96},{"i":104,"t":"Deleting Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#deleting-files","p":96},{"i":106,"t":"Examining the Contents of a Folder","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#examining-the-contents-of-a-folder","p":96},{"i":108,"t":"Copying a File","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#copying-a-file","p":96},{"i":110,"t":"Saving Some Keystrokes","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#saving-some-keystrokes","p":96},{"i":112,"t":"Renaming or Moving Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#renaming-or-moving-files","p":96},{"i":114,"t":"Creating a New Folder","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#creating-a-new-folder","p":96},{"i":116,"t":"Copying or Moving Multiple Files with Wildcards","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#copying-or-moving-multiple-files-with-wildcards","p":96},{"i":118,"t":"Deleting a Folder","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#deleting-a-folder","p":96},{"i":120,"t":"Looking at Text Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#looking-at-text-files","p":96},{"i":122,"t":"Zipping up Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#zipping-up-files","p":96},{"i":124,"t":"Summary","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#summary","p":96},{"i":128,"t":"The Working Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#the-working-directory","p":126},{"i":130,"t":"Listing the Contents of the Working Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#listing-the-contents-of-the-working-directory","p":126},{"i":132,"t":"Changing the Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#changing-the-directory","p":126},{"i":134,"t":"Understanding Paths","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#understanding-paths","p":126},{"i":136,"t":"The Special Dot and Dot Dot Folders","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#the-special-dot-and-dot-dot-folders","p":126},{"i":138,"t":"The Home Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#the-home-directory","p":126},{"i":140,"t":"Pushing and Popping the Working Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#pushing-and-popping-the-working-directory","p":126},{"i":142,"t":"Going Back","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#going-back","p":126},{"i":144,"t":"Summary","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#summary","p":126},{"i":150,"t":"Basic Navigation","u":"/part-2-core-skills/fly-on-the-command-line","h":"#basic-navigation","p":148},{"i":152,"t":"Go to beginning / end","u":"/part-2-core-skills/fly-on-the-command-line","h":"#go-to-beginning--end","p":148},{"i":154,"t":"Move backwards / forwards one word","u":"/part-2-core-skills/fly-on-the-command-line","h":"#move-backwards--forwards-one-word","p":148},{"i":156,"t":"Delete a word or undo a mistake","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-a-word-or-undo-a-mistake","p":148},{"i":158,"t":"Delete the next word","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-the-next-word","p":148},{"i":160,"t":"Delete to beginning / clear line","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-to-beginning--clear-line","p":148},{"i":162,"t":"Delete to end","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-to-end","p":148},{"i":164,"t":"Searching","u":"/part-2-core-skills/fly-on-the-command-line","h":"#searching","p":148},{"i":166,"t":"Search Backwards / Forwards","u":"/part-2-core-skills/fly-on-the-command-line","h":"#search-backwards--forwards","p":148},{"i":168,"t":"Run the command found in a search","u":"/part-2-core-skills/fly-on-the-command-line","h":"#run-the-command-found-in-a-search","p":148},{"i":170,"t":"Edit the command found","u":"/part-2-core-skills/fly-on-the-command-line","h":"#edit-the-command-found","p":148},{"i":172,"t":"Stop Searching","u":"/part-2-core-skills/fly-on-the-command-line","h":"#stop-searching","p":148},{"i":174,"t":"Editing In-Place","u":"/part-2-core-skills/fly-on-the-command-line","h":"#editing-in-place","p":148},{"i":176,"t":"Clear the Screen","u":"/part-2-core-skills/fly-on-the-command-line","h":"#clear-the-screen","p":148},{"i":178,"t":"See the History and Execute a Recent Command","u":"/part-2-core-skills/fly-on-the-command-line","h":"#see-the-history-and-execute-a-recent-command","p":148},{"i":180,"t":"Pro Tip: All The Keys!","u":"/part-2-core-skills/fly-on-the-command-line","h":"#pro-tip-all-the-keys","p":148},{"i":182,"t":"Pro Tip: Transposing!","u":"/part-2-core-skills/fly-on-the-command-line","h":"#pro-tip-transposing","p":148},{"i":184,"t":"The Power of Readline","u":"/part-2-core-skills/fly-on-the-command-line","h":"#the-power-of-readline","p":148},{"i":188,"t":"Introducing the Find Command","u":"/part-2-core-skills/finding-files","h":"#introducing-the-find-command","p":186},{"i":190,"t":"Searching with Find","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":192,"t":"Searching for Files or Folders only","u":"/part-2-core-skills/finding-files","h":"#searching-for-files-or-folders-only","p":186},{"i":194,"t":"Searching by Name","u":"/part-2-core-skills/finding-files","h":"#searching-by-name","p":186},{"i":196,"t":"Searching by Path","u":"/part-2-core-skills/finding-files","h":"#searching-by-path","p":186},{"i":198,"t":"Combining Searches - the AND and OR operators","u":"/part-2-core-skills/finding-files","h":"#combining-searches---the-and-and-or-operators","p":186},{"i":200,"t":"Case Insensitive Searches","u":"/part-2-core-skills/finding-files","h":"#case-insensitive-searches","p":186},{"i":202,"t":"Grouping and the NOT operator","u":"/part-2-core-skills/finding-files","h":"#grouping-and-the-not-operator","p":186},{"i":204,"t":"Why the Weird Parameters?","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":206,"t":"Performing Actions on Search Results","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":208,"t":"Printing Paths","u":"/part-2-core-skills/finding-files","h":"#printing-paths","p":186},{"i":210,"t":"Deleting Files","u":"/part-2-core-skills/finding-files","h":"#deleting-files","p":186},{"i":212,"t":"Execute a Command","u":"/part-2-core-skills/finding-files","h":"#execute-a-command","p":186},{"i":214,"t":"Execute a Command with a Confirmation","u":"/part-2-core-skills/finding-files","h":"#execute-a-command-with-a-confirmation","p":186},{"i":216,"t":"Handling Symlinks","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":218,"t":"Scratching the Surface","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":220,"t":"Summary","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":224,"t":"What Is Job Control?","u":"/part-2-core-skills/job-control","h":"#what-is-job-control","p":222},{"i":226,"t":"The Problem","u":"/part-2-core-skills/job-control","h":"#the-problem","p":222},{"i":228,"t":"Solution 1: Start the Server in the Background","u":"/part-2-core-skills/job-control","h":"#solution-1-start-the-server-in-the-background","p":222},{"i":230,"t":"Solution 2: Move the Server to the Background","u":"/part-2-core-skills/job-control","h":"#solution-2-move-the-server-to-the-background","p":222},{"i":232,"t":"Moving Background Jobs to the Foreground","u":"/part-2-core-skills/job-control","h":"#moving-background-jobs-to-the-foreground","p":222},{"i":234,"t":"Cleaning Up Jobs","u":"/part-2-core-skills/job-control","h":"#cleaning-up-jobs","p":222},{"i":236,"t":"Why You Shouldn't Use Jobs","u":"/part-2-core-skills/job-control","h":"#why-you-shouldnt-use-jobs","p":222},{"i":238,"t":"The Most Key Takeaways","u":"/part-2-core-skills/job-control","h":"#the-most-key-takeaways","p":222},{"i":240,"t":"Alternatives to Jobs","u":"/part-2-core-skills/job-control","h":"#alternatives-to-jobs","p":222},{"i":242,"t":"Quick Reference","u":"/part-2-core-skills/job-control","h":"#quick-reference","p":222},{"i":246,"t":"Input and Output","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#input-and-output","p":244},{"i":248,"t":"Standard Input, Output and Error","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#standard-input-output-and-error","p":244},{"i":250,"t":"A Pipeline in Action","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#a-pipeline-in-action","p":244},{"i":252,"t":"Common Patterns - Standard Input","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#common-patterns---standard-input","p":244},{"i":254,"t":"Common Patterns - Standard Output","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#common-patterns---standard-output","p":244},{"i":256,"t":"Common Patterns - Standard Error","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#common-patterns---standard-error","p":244},{"i":258,"t":"One Last Trick - The T Pipe","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#one-last-trick---the-t-pipe","p":244},{"i":261,"t":"Summary","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#summary","p":244},{"i":265,"t":"Introduction for the Non-Technical Reader","u":"/part-2-core-skills/what-is-a-shell","h":"","p":263},{"i":267,"t":"Introduction for the Technical Reader","u":"/part-2-core-skills/what-is-a-shell","h":"","p":263},{"i":269,"t":"Let's Get Started!","u":"/part-2-core-skills/what-is-a-shell","h":"","p":263},{"i":271,"t":"A Computer in a Nutshell","u":"/part-2-core-skills/what-is-a-shell","h":"#a-computer-in-a-nutshell","p":263},{"i":273,"t":"The Operating System","u":"/part-2-core-skills/what-is-a-shell","h":"#the-operating-system","p":263},{"i":275,"t":"The Kernel","u":"/part-2-core-skills/what-is-a-shell","h":"#the-kernel","p":263},{"i":277,"t":"User Space","u":"/part-2-core-skills/what-is-a-shell","h":"#user-space","p":263},{"i":279,"t":"The Shell","u":"/part-2-core-skills/what-is-a-shell","h":"#the-shell","p":263},{"i":281,"t":"The Terminal","u":"/part-2-core-skills/what-is-a-shell","h":"#the-terminal","p":263},{"i":283,"t":"Back to the Shell","u":"/part-2-core-skills/what-is-a-shell","h":"#back-to-the-shell","p":263},{"i":285,"t":"The Command Prompt or Command Line","u":"/part-2-core-skills/what-is-a-shell","h":"#the-command-prompt-or-command-line","p":263},{"i":287,"t":"Shell Commands and Different Shells","u":"/part-2-core-skills/what-is-a-shell","h":"#shell-commands-and-different-shells","p":263},{"i":289,"t":"That's a Wrap!","u":"/part-2-core-skills/what-is-a-shell","h":"#thats-a-wrap","p":263},{"i":291,"t":"Share and Discuss","u":"/part-2-core-skills/what-is-a-shell","h":"#share-and-discuss","p":263},{"i":295,"t":"What Are Commands?","u":"/part-2-core-skills/understanding-commands","h":"#what-are-commands","p":293},{"i":297,"t":"The Different Types of Commands","u":"/part-2-core-skills/understanding-commands","h":"#the-different-types-of-commands","p":293},{"i":299,"t":"Executables - Programs","u":"/part-2-core-skills/understanding-commands","h":"#executables---programs","p":293},{"i":301,"t":"Executables - Scripts","u":"/part-2-core-skills/understanding-commands","h":"#executables---scripts","p":293},{"i":303,"t":"Builtins","u":"/part-2-core-skills/understanding-commands","h":"#builtins","p":293},{"i":305,"t":"Functions","u":"/part-2-core-skills/understanding-commands","h":"#functions","p":293},{"i":307,"t":"Aliases","u":"/part-2-core-skills/understanding-commands","h":"#aliases","p":293},{"i":309,"t":"The Key Takeaways","u":"/part-2-core-skills/understanding-commands","h":"#the-key-takeaways","p":293},{"i":311,"t":"More than You Need to Know","u":"/part-2-core-skills/understanding-commands","h":"#more-than-you-need-to-know","p":293},{"i":313,"t":"what","u":"/part-2-core-skills/understanding-commands","h":"#what","p":293},{"i":315,"t":"whatis","u":"/part-2-core-skills/understanding-commands","h":"#whatis","p":293},{"i":317,"t":"which","u":"/part-2-core-skills/understanding-commands","h":"#which","p":293},{"i":319,"t":"whence","u":"/part-2-core-skills/understanding-commands","h":"#whence","p":293},{"i":321,"t":"where","u":"/part-2-core-skills/understanding-commands","h":"#where","p":293},{"i":323,"t":"whereis","u":"/part-2-core-skills/understanding-commands","h":"#whereis","p":293},{"i":325,"t":"command","u":"/part-2-core-skills/understanding-commands","h":"#command","p":293},{"i":327,"t":"type","u":"/part-2-core-skills/understanding-commands","h":"#type","p":293},{"i":329,"t":"Summary","u":"/part-2-core-skills/understanding-commands","h":"#summary","p":293},{"i":335,"t":"An Introduction to Regular Expressions","u":"/part-3-manipulating-text/regex-essentials/","h":"#an-introduction-to-regular-expressions","p":333},{"i":337,"t":"Managing Complexity with Regular Expressions","u":"/part-3-manipulating-text/regex-essentials/","h":"#managing-complexity-with-regular-expressions","p":333},{"i":339,"t":"Building Regexes - Start with the Basics","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---start-with-the-basics","p":333},{"i":341,"t":"Building Regexes - Quantifiers","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---quantifiers","p":333},{"i":343,"t":"Building Regexes - Character Sets and Metacharacters","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---character-sets-and-metacharacters","p":333},{"i":345,"t":"Building Regexes - Anchors","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---anchors","p":333},{"i":347,"t":"Building Regexes - Capture Groups","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---capture-groups","p":333},{"i":349,"t":"Building Regexes - Lazy and Greedy Expressions","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---lazy-and-greedy-expressions","p":333},{"i":351,"t":"Avoiding Advanced Topics - Backtracking, Lookarounds and Atomic Grouping","u":"/part-3-manipulating-text/regex-essentials/","h":"#avoiding-advanced-topics---backtracking-lookarounds-and-atomic-grouping","p":333},{"i":353,"t":"A Word of Warning","u":"/part-3-manipulating-text/regex-essentials/","h":"#a-word-of-warning","p":333},{"i":355,"t":"Summary","u":"/part-3-manipulating-text/regex-essentials/","h":"#summary","p":333},{"i":359,"t":"Introducing the Stream Editor","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#introducing-the-stream-editor","p":357},{"i":361,"t":"Transformations with Sed","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#transformations-with-sed","p":357},{"i":363,"t":"Replacing Text","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#replacing-text","p":357},{"i":365,"t":"Applying Multiple Expressions","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#applying-multiple-expressions","p":357},{"i":367,"t":"Stripping Comments","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#stripping-comments","p":357},{"i":369,"t":"Appending Text","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#appending-text","p":357},{"i":371,"t":"Prepending Text","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#prepending-text","p":357},{"i":373,"t":"Extracting Information","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#extracting-information","p":357},{"i":375,"t":"Advanced - Surrounding Parts of Text with Quotes","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#advanced---surrounding-parts-of-text-with-quotes","p":357},{"i":377,"t":"Advanced - Template Files","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#advanced---template-files","p":357},{"i":379,"t":"What About 'In Place' Editing?","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#what-about-in-place-editing","p":357},{"i":381,"t":"What about Awk?","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#what-about-awk","p":357},{"i":383,"t":"When to Program","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#when-to-program","p":357},{"i":385,"t":"Summary","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#summary","p":357},{"i":389,"t":"What is Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#what-is-grep","p":387},{"i":391,"t":"Why Grep?","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#why-grep","p":387},{"i":393,"t":"Searching Through Text","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#searching-through-text","p":387},{"i":395,"t":"Using Patterns","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#using-patterns","p":387},{"i":397,"t":"Finding Problems","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#finding-problems","p":387},{"i":399,"t":"The ABC of Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#the-abc-of-grep","p":387},{"i":401,"t":"Working with Multiple Files","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#working-with-multiple-files","p":387},{"i":403,"t":"V for Invert","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#v-for-invert","p":387},{"i":405,"t":"Don't Forget Your Pipelines!","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#dont-forget-your-pipelines","p":387},{"i":407,"t":"Alternatives to Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#alternatives-to-grep","p":387},{"i":409,"t":"Summary","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#summary","p":387},{"i":413,"t":"What is a Shell Script?","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#what-is-a-shell-script","p":411},{"i":415,"t":"Creating a Basic Shell Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#creating-a-basic-shell-script","p":411},{"i":417,"t":"The 'common' Command","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#the-common-command","p":411},{"i":419,"t":"Creating a Simple Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#creating-a-simple-script","p":411},{"i":421,"t":"Comments","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#commentsindex","p":411},{"i":423,"t":"Building and Testing the Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#building-and-testing-the-script","p":411},{"i":425,"t":"Multi-line Commands","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#multi-line-commands","p":411},{"i":427,"t":"Running a Shell Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#running-a-shell-script","p":411},{"i":429,"t":"Using Shebangs","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#using-shebangs","p":411},{"i":431,"t":"Shebangs - Dealing with Paths","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#shebangs---dealing-with-paths","p":411},{"i":433,"t":"Sourcing Shell Scripts","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#sourcing-shell-scripts","p":411},{"i":435,"t":"Dot Sourcing","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#dot-sourcingindex","p":411},{"i":437,"t":"Installing Your Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#installing-your-script","p":411},{"i":439,"t":"Summary","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#summary","p":411},{"i":441,"t":"Appendix - How the Script Works","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#appendix---how-the-script-works","p":411},{"i":445,"t":"Heads and Tails","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#heads-and-tails","p":443},{"i":447,"t":"Replacing Text","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#replacing-text","p":443},{"i":449,"t":"How to Cut","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#how-to-cut","p":443},{"i":451,"t":"A Trick with Rev","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#a-trick-with-rev","p":443},{"i":453,"t":"Sort and Unique","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#sort-and-unique","p":443},{"i":455,"t":"Don't Forget Your Pager!","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#dont-forget-your-pager","p":443},{"i":457,"t":"Summary","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#summary","p":443},{"i":461,"t":"Introducing Xargs","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#introducing-xargs","p":459},{"i":463,"t":"Handling Whitespace, Special Characters and Tracing","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#handling-whitespace-special-characters-and-tracing","p":459},{"i":465,"t":"One Command or Many Commands?","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#one-command-or-many-commands","p":459},{"i":467,"t":"Constructing more complex commands with the 'I' Parameter","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#constructing-more-complex-commands-with-the-i-parameter","p":459},{"i":469,"t":"Requesting Confirmation with the Prompt Option","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#requesting-confirmation-with-the-prompt-option","p":459},{"i":471,"t":"Splitting up Input with a Delimiter","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#splitting-up-input-with-a-delimiter","p":459},{"i":473,"t":"Summary","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#summary","p":459},{"i":479,"t":"Variables","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#variables","p":477},{"i":481,"t":"Setting Variables","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#setting-variables","p":477},{"i":483,"t":"Shell Variables and Environment Variables","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#shell-variables-and-environment-variables","p":477},{"i":485,"t":"Storing the Output of a Command into a Variable","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#storing-the-output-of-a-command-into-a-variable","p":477},{"i":487,"t":"Being Explicit with Variable Names","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#being-explicit-with-variable-names","p":477},{"i":489,"t":"Arrays","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#arrays","p":477},{"i":491,"t":"Associative Arrays","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#associative-arrays","p":477},{"i":493,"t":"Quoting Variables and Values","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#quoting-variables-and-values","p":477},{"i":495,"t":"Single Quotes - Literal Values","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#single-quotes---literal-values","p":477},{"i":497,"t":"Single Quotes - ANSI C Quoting","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#single-quotes---ansi-c-quoting","p":477},{"i":499,"t":"Double Quotes - Parameter Expansion","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#double-quotes---parameter-expansion","p":477},{"i":501,"t":"No Quotes - Shell Expansion","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#no-quotes---shell-expansion","p":477},{"i":503,"t":"Quoting Tips","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#quoting-tips","p":477},{"i":505,"t":"Shell Parameter Expansion","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#shell-parameter-expansion","p":477},{"i":507,"t":"The Read Command","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#the-read-command","p":477},{"i":509,"t":"Mathematics","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#mathematics","p":477},{"i":511,"t":"Updating the 'Common' Command","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#updating-the-common-command","p":477},{"i":513,"t":"Summary","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"","p":477},{"i":517,"t":"Creating a Function","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#creating-a-function--index-","p":515},{"i":519,"t":"Variables in Functions","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#variables-in-functions","p":515},{"i":521,"t":"Variable Scoping","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#variable-scoping-index","p":515},{"i":523,"t":"Passing Parameters to Functions","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#passing-parameters-to-functions","p":515},{"i":525,"t":"Parameter Variables","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#parameter-variables","p":515},{"i":527,"t":"Parameter Shifting","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#parameter-shifting","p":515},{"i":529,"t":"Return Values","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#return-values","p":515},{"i":531,"t":"Writing Results to Stdout","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#writing-results-to-stdout","p":515},{"i":533,"t":"Dealing with Output in Commands","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#dealing-with-output-in-commands","p":515},{"i":535,"t":"Returning Status Codes","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#returning-status-codes","p":515},{"i":537,"t":"Error Handling","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#error-handling","p":515},{"i":539,"t":"The Function Keyword","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#the-function-keyword","p":515},{"i":541,"t":"Parameters and Status Codes for Scripts","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#parameters-and-status-codes-for-scripts","p":515},{"i":543,"t":"Updating the 'common' Command","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#updating-the-common-command","p":515},{"i":545,"t":"Summary","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#summary","p":515},{"i":549,"t":"The If Statement","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-if-statement-index-","p":547},{"i":551,"t":"The Test Command","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-test-command","p":547},{"i":553,"t":"Using Multiple Statements in a Single Line","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#using-multiple-statements-in-a-single-line","p":547},{"i":555,"t":"The Else Statement","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-else-statement-index-","p":547},{"i":557,"t":"The Elif Statement","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-elif-statement-index-","p":547},{"i":559,"t":"Common Test Operators","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#common-test-operators","p":547},{"i":561,"t":"Common Test Operators for Files","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#common-test-operators-for-files","p":547},{"i":563,"t":"Combining Tests","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#combining-tests","p":547},{"i":565,"t":"Conditional Expressions","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#conditional-expressions","p":547},{"i":567,"t":"Using Regexes in a Conditional Expression","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#using-regexes-in-a-conditional-expression","p":547},{"i":569,"t":"Chaining Commands","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#chaining-commands","p":547},{"i":571,"t":"Case Statements","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#case-statements","p":547},{"i":573,"t":"Updating the 'Common' Command","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#updating-the-common-command","p":547},{"i":575,"t":"Summary","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#summary","p":547},{"i":579,"t":"The For Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#the-for-loop-index-","p":577},{"i":581,"t":"For Loops - Arrays","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---arrays","p":577},{"i":583,"t":"For Loops - Words","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---words","p":577},{"i":585,"t":"For Loops - Files with Wildcards","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---files-with-wildcards","p":577},{"i":587,"t":"For Loops - Files with Find","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---files-with-find","p":577},{"i":589,"t":"For Loops - C Style Loops","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---c-style-loops","p":577},{"i":591,"t":"For Loops - Looping over Sequences","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---looping-over-sequences","p":577},{"i":593,"t":"The While Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#the-while-loop","p":577},{"i":595,"t":"While Loops - Looping through the lines in a file","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#while-loops---looping-through-the-lines-in-a-file","p":577},{"i":597,"t":"While Loops - The Infinite Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#while-loops---the-infinite-loop","p":577},{"i":599,"t":"The Until Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#the-until-loop","p":577},{"i":601,"t":"Continue and Break","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#continue-and-break","p":577},{"i":603,"t":"Creating Compact Loops","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#creating-compact-loops","p":577},{"i":605,"t":"Word Splitting and IFS","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#word-splitting-and-ifsindex","p":577},{"i":607,"t":"Word Splitting","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#word-splitting","p":577},{"i":609,"t":"The IFS Variable","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#the-ifs-variable","p":577},{"i":611,"t":"Updating the 'common' Command","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#updating-the-common-command","p":577},{"i":613,"t":"Summary","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#summary","p":577},{"i":615,"t":"Appendix - Loops and the Z-Shell","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#appendix---loops-and-the-z-shell","p":577},{"i":621,"t":"The Shell Configuration File","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#the-shell-configuration-file","p":619},{"i":623,"t":"The Default Configuration File","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#the-default-configuration-file","p":619},{"i":625,"t":"Common Shell Configurations","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#common-shell-configurations","p":619},{"i":627,"t":"Shell Startup","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#shell-startup","p":619},{"i":629,"t":"Different Types of Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#different-types-of-shells","p":619},{"i":631,"t":"Interactive Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#interactive-shells","p":619},{"i":633,"t":"Non-Interactive Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#non-interactive-shells","p":619},{"i":635,"t":"Login Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#login-shells","p":619},{"i":637,"t":"Shell Startup Files","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#shell-startup-files","p":619},{"i":639,"t":"The Shell Profile File","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#the-shell-profile-fileindex","p":619},{"i":641,"t":"The Shell Run Commands File","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#the-shell-run-commands-fileindex","p":619},{"i":643,"t":"Startup Files for Non-Interactive Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#startup-files-for-non-interactive-shells","p":619},{"i":645,"t":"Login Shells and Desktop Managers","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#login-shells-and-desktop-managers","p":619},{"i":647,"t":"Changing Your Shell","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#changing-your-shell","p":619},{"i":649,"t":"Summary","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#summary","p":619},{"i":653,"t":"Git Remotes","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#git-remotes","p":651},{"i":655,"t":"The Git Push Command and Remotes","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#the-git-push-command-and-remotes","p":651},{"i":657,"t":"The Git Fetch Command","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#the-git-fetch-command","p":651},{"i":659,"t":"The Git Pull Command","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#the-git-pull-command","p":651},{"i":661,"t":"Sharing Your Dotfiles","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#sharing-your-dotfiles","p":651},{"i":663,"t":"Forking and Pull Requests","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#forking-and-pull-requests","p":651},{"i":665,"t":"A Script to Open a Pull Request","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#a-script-to-open-a-pull-request","p":651},{"i":667,"t":"Showing Git Information in the Command Prompt","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#showing-git-information-in-the-command-prompt","p":651},{"i":669,"t":"Scratching the Surface","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#scratching-the-surface","p":651},{"i":671,"t":"An Overview of the Git Commands","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#an-overview-of-the-git-commands","p":651},{"i":673,"t":"Summary","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#summary","p":651},{"i":677,"t":"What is Git","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#what-is-git","p":675},{"i":679,"t":"Creating a Git Repository","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#creating-a-git-repository","p":675},{"i":681,"t":"Adding and Resetting Changes to the Index","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#adding-and-resetting-changes-to-the-index","p":675},{"i":683,"t":"Committing Changes","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#committing-changes","p":675},{"i":685,"t":"Commit Messages","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#commit-messages","p":675},{"i":687,"t":"Creating Branches","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#creating-branches","p":675},{"i":689,"t":"Merging","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#merging","p":675},{"i":691,"t":"Merging Diverged Branches","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#merging-diverged-branches","p":675},{"i":693,"t":"The Git Log","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#the-git-log","p":675},{"i":695,"t":"Merge Conflicts","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#merge-conflicts","p":675},{"i":697,"t":"Other Merge Strategies","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#other-merge-strategies","p":675},{"i":699,"t":"Deleting and Renaming Files","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#deleting-and-renaming-files","p":675},{"i":701,"t":"Restoring Your Working Tree","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#restoring-your-working-tree","p":675},{"i":703,"t":"Summary","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#summary","p":675},{"i":707,"t":"The Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#the-command-promptindex","p":705},{"i":709,"t":"Customising the Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#customising-the-command-prompt","p":705},{"i":711,"t":"The Prompt String","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#the-prompt-string","p":705},{"i":713,"t":"Special Characters","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#special-characters","p":705},{"i":715,"t":"Changing the Colour and Text Formatting","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#changing-the-colour-and-text-formatting","p":705},{"i":717,"t":"Adding Data to the Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#adding-data-to-the-command-prompt","p":705},{"i":719,"t":"A Shell Script to Customise the Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#a-shell-script-to-customise-the-prompt","p":705},{"i":721,"t":"Additional Prompt Configuration","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#additional-prompt-configuration","p":705},{"i":723,"t":"Z-Shell and Oh-My-Zsh","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#z-shell-and-oh-my-zsh","p":705},{"i":725,"t":"Summary","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#summary","p":705},{"i":731,"t":"Dotfiles","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#dotfiles","p":729},{"i":733,"t":"The Default Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#the-default-shell-dotfile","p":729},{"i":735,"t":"Creating a Dotfiles Folder","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#creating-a-dotfiles-folder","p":729},{"i":737,"t":"Creating a Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#creating-a-shell-dotfile","p":729},{"i":739,"t":"Testing the Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#testing-the-shell-dotfile","p":729},{"i":741,"t":"Sourcing the Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#sourcing-the-shell-dotfile","p":729},{"i":743,"t":"Sourcing Files from a Folder","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#sourcing-files-from-a-folder","p":729},{"i":745,"t":"A Dotfile Install Script","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#a-dotfile-install-script","p":729},{"i":747,"t":"Summary","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#summary","p":729},{"i":751,"t":"What is a Terminal Editor","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#what-is-a-terminal-editor","p":749},{"i":753,"t":"Why use a Terminal Editor?","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#why-use-a-terminal-editor","p":749},{"i":755,"t":"Introducing Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#introducing-vim","p":749},{"i":757,"t":"What is Modal Editing?","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#what-is-modal-editing","p":749},{"i":759,"t":"Modal Editing in Action","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#modal-editing-in-action","p":749},{"i":761,"t":"Building a Vim Cheat Sheet","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#building-a-vim-cheat-sheet","p":749},{"i":763,"t":"Creating a File in Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#creating-a-file-in-vim","p":749},{"i":765,"t":"Vim Motions and Movement Commands","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-motions-and-movement-commands","p":749},{"i":767,"t":"Vim Command Counts","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-command-counts","p":749},{"i":769,"t":"Vim Text Insert Commands","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-text-insert-commands","p":749},{"i":771,"t":"Vim Operators","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-operators","p":749},{"i":773,"t":"Search Motions and the 'In' and 'A' Operators","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#search-motions-and-the-in-and-a-operators","p":749},{"i":775,"t":"The 'In' and 'A' Modifiers","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#the-in-and-a-modifiers","p":749},{"i":777,"t":"Editing Commands in Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#editing-commands-in-vim","p":749},{"i":779,"t":"Next Steps with Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#next-steps-with-vim","p":749},{"i":781,"t":"Vimtutor","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vimtutor","p":749},{"i":783,"t":"Vimcasts","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vimcasts","p":749},{"i":785,"t":"Practical Vim & Modern Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#practical-vim--modern-vim","p":749},{"i":787,"t":"The Vim Shortcuts Wallpaper","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#the-vim-shortcuts-wallpaper","p":749},{"i":789,"t":"Summary","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#summary","p":749},{"i":793,"t":"When should you avoid shell scripting?","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#when-should-you-avoid-shell-scripting","p":791},{"i":795,"t":"What are the alternatives?","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#what-are-the-alternatives","p":791},{"i":797,"t":"What makes a shell-friendly tool?","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#what-makes-a-shell-friendly-tool","p":791},{"i":799,"t":"Writing a Dictionary Lookup Tool in Python","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#writing-a-dictionary-lookup-tool-in-python","p":791},{"i":801,"t":"Version 1 - Basic Structure","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#version-1---basic-structure","p":791},{"i":803,"t":"Version 2 - Downloading the Definition","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#version-2---downloading-the-definition","p":791},{"i":805,"t":"Version 3 - Formatting the Output","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#version-3---formatting-the-output","p":791},{"i":807,"t":"Installing the Lookup Tool","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#installing-the-lookup-tool","p":791},{"i":809,"t":"Improving the Lookup Program","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#improving-the-lookup-program","p":791},{"i":811,"t":"Summary","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#summary","p":791},{"i":815,"t":"What is a Terminal Multiplexer?","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#what-is-a-terminal-multiplexer","p":813},{"i":817,"t":"Why use a Multiplexer?","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#why-use-a-multiplexer","p":813},{"i":819,"t":"Introducing Screen and Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#introducing-screen-and-tmux","p":813},{"i":821,"t":"Installing Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#installing-tmux","p":813},{"i":823,"t":"Tmux Techniques","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#tmux-techniques","p":813},{"i":825,"t":"The Leader","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#the-leader","p":813},{"i":827,"t":"Window Management","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#window-management","p":813},{"i":829,"t":"Session Management","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#session-management","p":813},{"i":831,"t":"Attaching and Detaching from Sessions","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#attaching-and-detaching-from-sessions","p":813},{"i":833,"t":"Configuration","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#configuration","p":813},{"i":835,"t":"Advanced Configuration","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#advanced-configuration","p":813},{"i":837,"t":"Collaboration","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#collaboration","p":813},{"i":839,"t":"Going Further with Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#going-further-with-tmux","p":813},{"i":841,"t":"Getting Help","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#getting-help","p":813},{"i":843,"t":"Going Further with Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#going-further-with-tmux-1","p":813},{"i":845,"t":"Summary","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#summary","p":813},{"i":849,"t":"Ensuring Exit on Failure","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#ensuring-exit-on-failure","p":847},{"i":851,"t":"Debugging Shell Scripts","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#debugging-shell-scripts","p":847},{"i":853,"t":"Checking for Existing Variables or Functions","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#checking-for-existing-variables-or-functions","p":847},{"i":855,"t":"Unsetting Values","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#unsetting-values","p":847},{"i":857,"t":"Traps","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#traps","p":847},{"i":859,"t":"Handling Options","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#handling-options","p":847},{"i":861,"t":"Colouring Output","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#colouring-output","p":847},{"i":863,"t":"Checking the Operating System","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#checking-the-operating-system","p":847},{"i":865,"t":"Checking for Installed Programs","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#checking-for-installed-programs","p":847},{"i":867,"t":"Using 'Select' to Show a Menu","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#using-select-to-show-a-menu","p":847},{"i":869,"t":"Running Commands in Subshells","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#running-commands-in-subshells","p":847},{"i":871,"t":"Anti-Patterns","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#anti-patterns","p":847},{"i":873,"t":"Configuring Options in Shebangs","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#configuring-options-in-shebangs","p":847},{"i":875,"t":"Complex Logic in Shell Scripts","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#complex-logic-in-shell-scripts","p":847},{"i":877,"t":"Scripts without Shebangs","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#scripts-without-shebangs","p":847},{"i":879,"t":"Summary","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#summary","p":847},{"i":883,"t":"What is SSH?","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#what-is-ssh","p":881},{"i":885,"t":"An Authentication Crash Course","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#an-authentication-crash-course","p":881},{"i":887,"t":"Symmetric Encryption for Authentication","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#symmetric-encryption-for-authentication","p":881},{"i":889,"t":"Asymmetric Encryption for Authentication","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#asymmetric-encryption-for-authentication","p":881},{"i":891,"t":"Creating a Key Pair","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#creating-a-key-pair","p":881},{"i":893,"t":"Setting up an AWS Account","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#setting-up-an-aws-account","p":881},{"i":895,"t":"Creating a Virtual Machine on AWS","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#creating-a-virtual-machine-on-aws","p":881},{"i":897,"t":"Using SSH to connect to a virtual machine","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#using-ssh-to-connect-to-a-virtual-machine","p":881},{"i":899,"t":"Dealing with Key Permission Errors","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#dealing-with-key-permission-errors","p":881},{"i":901,"t":"Configuring SSH Hosts","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#configuring-ssh-hosts","p":881},{"i":903,"t":"Running SSH Commands","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#running-ssh-commands","p":881},{"i":905,"t":"Handling Disconnections","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#handling-disconnections","p":881},{"i":907,"t":"Transferring Files with SCP","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#transferring-files-with-scp","p":881},{"i":909,"t":"Next Steps","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#next-steps","p":881},{"i":911,"t":"Summary","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#summary","p":881},{"i":917,"t":"The Most Important Manpages","u":"/xx-appendices/essential-manpages","h":"#the-most-important-manpages","p":915},{"i":922,"t":"Manually Downloading the Samples","u":"/xx-appendices/installing-samples/","h":"#manually-downloading-the-samples","p":920},{"i":924,"t":"Deleting the Samples","u":"/xx-appendices/installing-samples/","h":"#deleting-the-samples","p":920},{"i":930,"t":"Posix Shells","u":"/xx-appendices/posix","h":"#posix-shells","p":928},{"i":934,"t":"The Future of the Shell","u":"/xx-appendices/recommended-reading/","h":"#the-future-of-the-shell","p":932},{"i":936,"t":"General Shell Skills","u":"/xx-appendices/recommended-reading/","h":"#general-shell-skills","p":932},{"i":938,"t":"Fantastic Books","u":"/xx-appendices/recommended-reading/","h":"#fantastic-books","p":932},{"i":940,"t":"Shell Scripting by Jason Cannon","u":"/xx-appendices/recommended-reading/","h":"#shell-scripting-by-jason-cannon","p":932},{"i":942,"t":"Wicked Cool Shell Scripts - Dave Taylor & Brandon Perry","u":"/xx-appendices/recommended-reading/","h":"#wicked-cool-shell-scripts---dave-taylor--brandon-perry","p":932},{"i":944,"t":"Practical Vim: Edit Text at the Speed of Thought, Drew Niel","u":"/xx-appendices/recommended-reading/","h":"#practical-vim-edit-text-at-the-speed-of-thought-drew-niel","p":932},{"i":946,"t":"Joshua Levy - The Art of the Command Line","u":"/xx-appendices/recommended-reading/","h":"#joshua-levy---the-art-of-the-command-line","p":932},{"i":948,"t":"Essential Online Resources","u":"/xx-appendices/recommended-reading/","h":"#essential-online-resources","p":932},{"i":950,"t":"Command Line Interface Guidelines","u":"/xx-appendices/recommended-reading/","h":"#command-line-interface-guidelines","p":932},{"i":952,"t":"The Missing Semester of Your CS Education","u":"/xx-appendices/recommended-reading/","h":"#the-missing-semester-of-your-cs-education","p":932},{"i":954,"t":"Maarten Billemont - Bash Guide","u":"/xx-appendices/recommended-reading/","h":"#maarten-billemont---bash-guide","p":932},{"i":956,"t":"Great Books","u":"/xx-appendices/recommended-reading/","h":"#great-books","p":932},{"i":957,"t":"Great Videos","u":"/xx-appendices/recommended-reading/","h":"#great-videos","p":932},{"i":958,"t":"The UNIX Operating System","u":"/xx-appendices/recommended-reading/","h":"#the-unix-operating-system","p":932},{"i":960,"t":"The Mother of All Demos","u":"/xx-appendices/recommended-reading/","h":"#the-mother-of-all-demos","p":932},{"i":962,"t":"Where Grep Came From - Computerphile","u":"/xx-appendices/recommended-reading/","h":"#where-grep-came-from---computerphile","p":932},{"i":964,"t":"Advanced","u":"/xx-appendices/recommended-reading/","h":"#advanced","p":932},{"i":966,"t":"Research","u":"/xx-appendices/recommended-reading/","h":"#research","p":932},{"i":968,"t":"TODO","u":"/xx-appendices/recommended-reading/","h":"#todo","p":932},{"i":972,"t":"TODO","u":"/xx-appendices/shell-parameter-expansion","h":"#todo","p":970},{"i":978,"t":"Images","u":"/zz-developer-guide/images-and-diagrams","h":"#images","p":976},{"i":980,"t":"Diagrams","u":"/zz-developer-guide/images-and-diagrams","h":"#diagrams","p":976},{"i":984,"t":"AsciinemaPlayer","u":"/zz-developer-guide/components","h":"#asciinemaplayer","p":982},{"i":986,"t":"AnnotatedCommand","u":"/zz-developer-guide/components","h":"#annotatedcommand","p":982},{"i":988,"t":"Caret","u":"/zz-developer-guide/components","h":"#caret","p":982},{"i":990,"t":"Tips for Developing Components","u":"/zz-developer-guide/components","h":"#tips-for-developing-components","p":982},{"i":994,"t":"Asciinema","u":"/zz-developer-guide/recording-terminal-sessions","h":"#asciinema","p":992},{"i":996,"t":"Script Recording","u":"/zz-developer-guide/recording-terminal-sessions","h":"#script-recording","p":992},{"i":1000,"t":"Links","u":"/zz-developer-guide/markdown-guide","h":"#links","p":998},{"i":1002,"t":"Emphasis","u":"/zz-developer-guide/markdown-guide","h":"#emphasis","p":998},{"i":1004,"t":"Asides / Admonitions / Blurbs","u":"/zz-developer-guide/markdown-guide","h":"#asides--admonitions--blurbs","p":998},{"i":1006,"t":"Markua Tags","u":"/zz-developer-guide/markdown-guide","h":"#markua-tags","p":998},{"i":1008,"t":"Titles","u":"/zz-developer-guide/markdown-guide","h":"#titles","p":998},{"i":1010,"t":"Images","u":"/zz-developer-guide/markdown-guide","h":"#images","p":998},{"i":1012,"t":"Images","u":"/zz-developer-guide/markdown-guide","h":"#images-1","p":998},{"i":1014,"t":"Diagrams","u":"/zz-developer-guide/markdown-guide","h":"#diagrams","p":998},{"i":1018,"t":"What is Shell Expansion?","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#what-is-shell-expansion","p":1016},{"i":1020,"t":"Shell Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#shell-expansion","p":1016},{"i":1022,"t":"Brace Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#brace-expansion","p":1016},{"i":1024,"t":"Tilde Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#tilde-expansion","p":1016},{"i":1026,"t":"Parameter Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#parameter-expansion","p":1016},{"i":1028,"t":"Command Substitution","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#command-substitution","p":1016},{"i":1030,"t":"Arithmetic Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#arithmetic-expansion","p":1016},{"i":1032,"t":"Word Splitting","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#word-splitting","p":1016},{"i":1034,"t":"Pathname Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#pathname-expansion","p":1016},{"i":1036,"t":"Summary","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#summary","p":1016}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/7",[0,3.718,1,3.718,2,4.082,3,3.3,4,4.082]],["t/9",[0,4.206,5,4.617,6,3.935,7,4.617]],["t/11",[8,6.488]],["t/12",[9,2.938,10,5.335]],["t/14",[9,2.495,11,4.297,12,5.314]],["t/20",[13,5.702,14,5.702]],["t/22",[13,4.841,15,5.314,16,2.154]],["t/24",[17,4.529,18,5.314,19,4.111]],["t/26",[20,6.259,21,5.335]],["t/28",[22,5.702,23,3.946]],["t/30",[23,3.946,24,6.259]],["t/32",[9,3.574]],["t/36",[25,4.529,26,4.297,27,4.841]],["t/38",[28,5.702,29,6.259]],["t/40",[16,2.154,25,4.529,26,4.297]],["t/42",[30,4.136,31,5.702]],["t/44",[26,5.061,32,5.061]],["t/46",[33,5.702,34,6.259]],["t/48",[35,4.659,36,6.259]],["t/50",[37,5.702,38,6.259]],["t/52",[9,3.574]],["t/56",[39,3.057]],["t/58",[39,2.514,40,5.702]],["t/60",[41,5.702,42,5.335]],["t/62",[43,6.934]],["t/64",[44,4.841,45,3.708,46,4.841]],["t/66",[39,2.514,47,4.246]],["t/68",[41,5.702,42,5.335]],["t/70",[43,6.934]],["t/72",[44,6.934]],["t/74",[6,6.488]],["t/76",[39,2.134,48,4.841,49,4.841]],["t/78",[16,2.537,50,6.259]],["t/80",[51,4.659,52,6.259]],["t/82",[9,3.574]],["t/86",[39,2.514,53,6.259]],["t/88",[54,3.956,55,5.314,56,5.314]],["t/90",[57,5.314,58,4.111,59,5.314]],["t/92",[60,5.314,61,3.512,62,5.314]],["t/94",[63,7.612]],["t/98",[64,3.862,65,6.259]],["t/100",[66,4.111,67,5.314,68,2.69]],["t/102",[69,5.702,70,5.702]],["t/104",[68,3.169,71,4.246]],["t/106",[72,5.314,73,4.841,74,3.824]],["t/108",[17,5.335,68,3.169]],["t/110",[75,6.259,76,6.259]],["t/112",[51,3.956,68,2.69,77,4.841]],["t/114",[64,3.279,74,3.824,78,5.314]],["t/116",[17,3.479,51,3.039,68,2.067,79,3.3,80,3.718]],["t/118",[71,4.246,74,4.503]],["t/120",[23,3.351,68,2.69,81,5.314]],["t/122",[68,2.69,70,4.841,82,4.297]],["t/124",[9,3.574]],["t/128",[83,4.659,84,4.842]],["t/130",[73,4.206,83,3.437,84,3.572,85,4.617]],["t/132",[54,4.659,84,4.842]],["t/134",[28,5.702,86,5.061]],["t/136",[74,2.937,87,3.479,88,6.775]],["t/138",[84,4.842,89,6.259]],["t/140",[83,3.437,84,3.572,90,4.206,91,4.617]],["t/142",[92,5.061,93,5.702]],["t/144",[9,3.574]],["t/150",[19,4.842,94,6.259]],["t/152",[45,3.222,92,3.733,95,4.206,96,4.206]],["t/154",[45,2.552,51,2.723,97,3.332,98,3.332,99,3.118,100,2.552]],["t/156",[71,3.132,100,3.222,101,4.617,102,4.617]],["t/158",[71,3.605,100,3.708,103,4.529]],["t/160",[45,2.848,71,2.769,95,3.718,104,3.718,105,2.937]],["t/162",[71,4.246,96,5.702]],["t/164",[106,4.697]],["t/166",[45,3.222,97,4.206,98,4.206,106,2.849]],["t/168",[16,1.872,106,2.849,107,3.572,108,4.206]],["t/170",[16,2.154,108,4.841,109,3.824]],["t/172",[106,3.862,110,6.259]],["t/174",[109,4.503,111,5.702]],["t/176",[104,5.702,112,5.702]],["t/178",[1,3.718,16,1.655,113,4.082,114,3.158,115,4.082]],["t/180",[116,4.841,117,4.297,118,4.111]],["t/182",[116,4.841,117,4.297,119,5.314]],["t/184",[120,6.259,121,6.259]],["t/188",[16,2.154,35,3.956,66,4.111]],["t/190",[66,4.842,106,3.862]],["t/192",[68,2.69,74,3.824,106,3.279]],["t/194",[106,3.862,122,5.702]],["t/196",[86,5.061,106,3.862]],["t/198",[61,3.512,106,3.279,123,4.841]],["t/200",[106,3.279,124,4.841,125,5.314]],["t/202",[61,4.136,126,5.335]],["t/204",[127,6.259,128,4.246]],["t/206",[106,2.849,129,4.617,130,3.935,131,4.206]],["t/208",[86,5.061,132,6.259]],["t/210",[68,3.169,71,4.246]],["t/212",[16,2.537,114,4.842]],["t/214",[16,2.154,114,4.111,133,4.841]],["t/216",[134,4.842,135,6.259]],["t/218",[136,5.702,137,5.702]],["t/220",[9,3.574]],["t/224",[138,4.842,139,6.259]],["t/226",[140,6.934]],["t/228",[141,3.718,142,3.718,143,3.479,144,3.718,145,3.479]],["t/230",[51,3.039,141,3.718,144,3.718,145,3.479,146,3.718]],["t/232",[51,3.437,138,3.572,145,3.935,147,4.617]],["t/234",[82,4.297,138,4.111,148,5.314]],["t/236",[30,3.512,138,4.111,149,5.314]],["t/238",[118,4.842,150,5.702]],["t/240",[32,5.061,138,4.842]],["t/242",[48,5.702,151,6.259]],["t/246",[152,5.061,153,4.503]],["t/248",[152,3.733,153,3.322,154,3.733,155,3.733]],["t/250",[130,5.335,156,5.702]],["t/252",[152,3.733,154,3.733,157,2.978,158,3.572]],["t/254",[153,3.322,154,3.733,157,2.978,158,3.572]],["t/256",[154,3.733,155,3.733,157,2.978,158,3.572]],["t/258",[99,3.479,159,4.082,160,3.718,161,4.082,162,4.082]],["t/261",[9,3.574]],["t/265",[10,3.935,11,3.733,163,3.935,164,4.206]],["t/267",[10,4.529,163,4.529,164,4.841]],["t/269",[143,5.335,165,6.259]],["t/271",[166,6.259,167,6.259]],["t/273",[3,5.061,61,4.136]],["t/275",[168,7.612]],["t/277",[169,6.259,170,6.259]],["t/279",[39,3.057]],["t/281",[171,6.155]],["t/283",[39,2.514,93,5.702]],["t/285",[16,2.741,105,3.322,172,3.132]],["t/287",[16,1.872,39,2.715,173,3.935]],["t/289",[6,5.335,174,6.259]],["t/291",[175,5.702,176,6.259]],["t/295",[16,3.086]],["t/297",[16,2.154,173,4.529,177,4.529]],["t/299",[58,4.842,114,4.842]],["t/301",[114,4.842,178,3.407]],["t/303",[179,7.612]],["t/305",[180,5.667]],["t/307",[181,7.612]],["t/309",[118,4.842,150,5.702]],["t/311",[182,4.841,183,5.314,184,5.314]],["t/313",[]],["t/315",[185,7.612]],["t/317",[]],["t/319",[186,7.612]],["t/321",[]],["t/323",[187,7.612]],["t/325",[16,3.086]],["t/327",[177,6.488]],["t/329",[9,3.574]],["t/335",[163,4.529,188,4.841,189,3.956]],["t/337",[188,4.206,189,3.437,190,3.733,191,3.935]],["t/339",[19,3.572,143,3.935,192,3.222,193,3.322]],["t/341",[192,3.708,193,3.824,194,5.314]],["t/343",[192,2.848,193,2.937,195,3.479,196,3.479,197,4.082]],["t/345",[192,3.708,193,3.824,198,5.314]],["t/347",[126,3.935,192,3.222,193,3.322,199,4.617]],["t/349",[189,3.039,192,2.848,193,2.937,200,4.082,201,4.082]],["t/351",[126,2.824,202,3.019,203,2.563,204,3.313,205,3.313,206,3.313,207,3.313]],["t/353",[100,4.367,208,6.259]],["t/355",[9,3.574]],["t/359",[35,3.956,209,5.314,210,4.529]],["t/361",[211,6.259,212,6.259]],["t/363",[23,3.946,213,5.702]],["t/365",[79,4.297,189,3.956,214,5.314]],["t/367",[215,6.259,216,5.702]],["t/369",[23,3.946,217,6.259]],["t/371",[23,3.946,218,6.259]],["t/373",[69,5.702,219,5.702]],["t/375",[23,2.574,203,3.158,220,4.082,221,4.082,222,2.937]],["t/377",[68,2.69,203,4.111,223,5.314]],["t/379",[109,4.503,111,5.702]],["t/381",[224,7.612]],["t/383",[58,5.889]],["t/385",[9,3.574]],["t/389",[225,5.889]],["t/391",[225,5.889]],["t/393",[23,3.351,106,3.279,226,4.841]],["t/395",[30,4.136,158,4.842]],["t/397",[66,4.842,140,5.702]],["t/399",[225,4.842,227,6.259]],["t/401",[68,2.69,79,4.297,83,3.956]],["t/403",[228,6.259,229,6.259]],["t/405",[156,4.841,230,4.841,231,4.841]],["t/407",[32,5.061,225,4.842]],["t/409",[9,3.574]],["t/413",[39,2.514,178,3.407]],["t/415",[19,3.572,39,1.854,64,2.849,178,2.513]],["t/417",[16,2.537,157,4.037]],["t/419",[64,3.279,178,2.893,232,5.314]],["t/421",[216,6.934]],["t/423",[178,2.893,192,3.708,233,3.956]],["t/425",[16,2.154,105,3.824,234,5.314]],["t/427",[39,2.134,107,4.111,178,2.893]],["t/429",[30,4.136,235,5.061]],["t/431",[86,4.297,235,4.297,236,4.529]],["t/433",[39,2.134,178,2.893,237,4.297]],["t/435",[88,5.702,237,5.061]],["t/437",[178,3.407,238,4.842]],["t/439",[9,3.574]],["t/441",[83,3.956,178,2.893,239,4.841]],["t/445",[240,6.259,241,6.259]],["t/447",[23,3.946,213,5.702]],["t/449",[242,7.612]],["t/451",[160,5.702,243,6.259]],["t/453",[22,5.702,244,6.259]],["t/455",[31,4.841,230,4.841,231,4.841]],["t/457",[9,3.574]],["t/461",[35,4.659,245,6.259]],["t/463",[87,3.479,134,3.158,195,3.479,246,4.082,247,4.082]],["t/465",[16,2.741,99,3.935,248,4.617]],["t/467",[16,1.655,128,2.769,182,3.718,191,3.479,249,4.082]],["t/469",[133,4.206,172,3.132,250,3.935,251,3.935]],["t/471",[82,3.733,152,3.733,252,3.733,253,4.617]],["t/473",[9,3.574]],["t/479",[254,4.91]],["t/481",[196,5.335,254,4.037]],["t/483",[39,1.854,254,4.362,255,4.617]],["t/485",[16,1.872,153,3.322,254,2.978,256,4.617]],["t/487",[122,4.206,254,2.978,257,4.617,258,4.617]],["t/489",[259,6.488]],["t/491",[259,5.335,260,6.259]],["t/493",[222,3.824,254,3.428,261,4.297]],["t/495",[222,3.322,261,3.733,262,3.935,263,4.617]],["t/497",[222,4.439,262,3.479,264,4.082,265,3.718]],["t/499",[128,3.132,222,3.322,266,4.617,267,3.052]],["t/501",[39,2.134,222,3.824,267,3.512]],["t/503",[117,5.061,222,4.503]],["t/505",[39,2.134,128,3.605,267,3.512]],["t/507",[16,2.537,268,6.259]],["t/509",[269,7.612]],["t/511",[16,2.154,157,3.428,270,4.297]],["t/513",[9,3.574]],["t/517",[64,3.862,180,4.659]],["t/519",[180,4.659,254,4.037]],["t/521",[254,4.037,271,6.259]],["t/523",[128,3.605,180,3.956,272,5.314]],["t/525",[128,4.246,254,4.037]],["t/527",[128,4.246,273,6.259]],["t/529",[261,5.061,274,5.702]],["t/531",[131,4.841,275,4.841,276,5.314]],["t/533",[16,2.154,153,3.824,236,4.529]],["t/535",[274,4.841,277,4.841,278,4.841]],["t/537",[134,4.842,155,5.061]],["t/539",[180,4.659,279,6.259]],["t/541",[128,3.132,178,2.513,277,4.206,278,4.206]],["t/543",[16,2.154,157,3.428,270,4.297]],["t/545",[9,3.574]],["t/549",[280,5.889]],["t/551",[16,2.537,233,4.659]],["t/553",[30,2.698,79,3.3,105,2.937,262,3.479,280,3.158]],["t/555",[280,5.889]],["t/557",[280,4.842,281,6.259]],["t/559",[61,3.512,157,3.428,233,3.956]],["t/561",[61,3.052,68,2.338,157,2.978,233,3.437]],["t/563",[123,5.702,233,4.659]],["t/565",[189,4.659,282,5.702]],["t/567",[30,3.052,189,3.437,193,3.322,282,4.206]],["t/569",[16,2.537,283,6.259]],["t/571",[124,5.702,280,4.842]],["t/573",[16,2.154,157,3.428,270,4.297]],["t/575",[9,3.574]],["t/579",[284,4.697]],["t/581",[259,5.335,284,3.862]],["t/583",[100,4.367,284,3.862]],["t/585",[68,2.69,80,4.841,284,3.279]],["t/587",[66,4.111,68,2.69,284,3.279]],["t/589",[265,4.206,284,4.173,285,4.617]],["t/591",[284,4.173,286,4.617,287,4.617]],["t/593",[284,4.697]],["t/595",[68,2.067,105,2.937,226,3.718,284,3.807]],["t/597",[284,4.616,288,5.314]],["t/599",[284,3.862,289,6.259]],["t/601",[290,6.259,291,6.259]],["t/603",[64,3.279,284,3.279,292,5.314]],["t/605",[100,3.708,252,4.297,293,4.841]],["t/607",[100,4.367,252,5.061]],["t/609",[254,4.037,293,5.702]],["t/611",[16,2.154,157,3.428,270,4.297]],["t/613",[9,3.574]],["t/615",[39,1.854,239,4.206,284,2.849,294,4.206]],["t/621",[39,2.134,47,3.605,68,2.69]],["t/623",[47,3.605,68,2.69,295,4.841]],["t/625",[39,2.134,47,3.605,157,3.428]],["t/627",[39,2.514,296,5.335]],["t/629",[39,2.134,173,4.529,177,4.529]],["t/631",[39,2.514,297,5.335]],["t/633",[11,4.297,39,2.134,297,4.529]],["t/635",[39,2.514,298,5.702]],["t/637",[39,2.134,68,2.69,296,4.529]],["t/639",[39,2.134,68,2.69,299,5.314]],["t/641",[16,1.872,39,1.854,68,2.338,107,3.572]],["t/643",[11,3.3,39,1.639,68,2.067,296,3.479,297,3.479]],["t/645",[39,1.854,190,3.733,298,4.206,300,4.617]],["t/647",[39,2.514,54,4.659]],["t/649",[9,3.574]],["t/653",[301,4.246,302,5.702]],["t/655",[16,1.872,90,4.206,301,3.132,302,4.206]],["t/657",[16,2.154,301,3.605,303,5.314]],["t/659",[16,2.154,301,3.605,304,4.529]],["t/661",[175,5.702,305,4.367]],["t/663",[250,4.529,304,4.529,306,5.314]],["t/665",[40,4.206,178,2.513,250,3.935,304,3.935]],["t/667",[16,1.655,172,2.769,219,3.718,301,2.769,307,3.718]],["t/669",[136,5.702,137,5.702]],["t/671",[16,2.154,301,3.605,308,5.314]],["t/673",[9,3.574]],["t/677",[301,5.164]],["t/679",[64,3.279,301,3.605,309,5.314]],["t/681",[54,3.437,310,4.206,311,4.617,312,4.617]],["t/683",[54,4.659,313,5.702]],["t/685",[313,5.702,314,6.259]],["t/687",[64,3.862,315,5.702]],["t/689",[316,6.155]],["t/691",[315,4.841,316,4.297,317,5.314]],["t/693",[301,4.246,318,6.259]],["t/695",[316,5.061,319,6.259]],["t/697",[316,5.061,320,6.259]],["t/699",[68,2.69,71,3.605,77,4.841]],["t/701",[83,3.956,321,5.314,322,5.314]],["t/703",[9,3.574]],["t/707",[16,2.537,172,4.246]],["t/709",[16,2.154,172,3.605,323,4.841]],["t/711",[172,4.246,324,6.259]],["t/713",[87,5.335,195,5.335]],["t/715",[21,3.935,23,2.911,54,3.437,325,4.206]],["t/717",[16,1.872,172,3.132,310,4.206,326,4.617]],["t/719",[39,1.854,172,3.132,178,2.513,323,4.206]],["t/721",[47,3.605,172,3.605,327,5.314]],["t/723",[39,1.854,294,4.206,328,4.617,329,4.617]],["t/725",[9,3.574]],["t/731",[305,5.311]],["t/733",[39,2.134,295,4.841,305,3.708]],["t/735",[64,3.279,74,3.824,305,3.708]],["t/737",[39,2.134,64,3.279,305,3.708]],["t/739",[39,2.134,233,3.956,305,3.708]],["t/741",[39,2.134,237,4.297,305,3.708]],["t/743",[68,2.69,74,3.824,237,4.297]],["t/745",[178,2.893,238,4.111,305,3.708]],["t/747",[9,3.574]],["t/751",[171,5.061,210,5.335]],["t/753",[30,3.512,171,4.297,210,4.529]],["t/755",[35,4.659,330,3.946]],["t/757",[109,4.503,331,5.702]],["t/759",[109,3.824,130,4.529,331,4.841]],["t/761",[192,3.222,330,2.911,332,4.617,333,4.617]],["t/763",[64,3.279,68,2.69,330,3.351]],["t/765",[16,1.872,330,2.911,334,4.206,335,4.617]],["t/767",[16,2.154,330,3.351,336,5.314]],["t/769",[16,1.872,23,2.911,330,2.911,337,4.617]],["t/771",[61,4.136,330,3.946]],["t/773",[61,3.512,106,3.279,334,4.841]],["t/775",[338,7.612]],["t/777",[16,2.154,109,3.824,330,3.351]],["t/779",[103,4.529,330,3.351,339,4.841]],["t/781",[340,7.612]],["t/783",[341,7.612]],["t/785",[45,2.848,330,3.89,342,3.718,343,4.082]],["t/787",[330,3.351,344,5.314,345,5.314]],["t/789",[9,3.574]],["t/793",[39,2.134,178,2.893,202,4.841]],["t/795",[32,6.155]],["t/797",[39,1.854,346,4.617,347,4.617,348,3.935]],["t/799",[275,3.718,348,3.479,349,4.082,350,3.479,351,4.082]],["t/801",[19,3.572,142,4.206,352,3.935,353,4.617]],["t/803",[146,4.206,352,3.935,354,4.206,355,4.617]],["t/805",[21,3.935,153,3.322,352,3.935,356,4.617]],["t/807",[238,4.111,348,4.529,350,4.529]],["t/809",[58,4.111,350,4.529,357,5.314]],["t/811",[9,3.574]],["t/815",[171,5.061,358,5.702]],["t/817",[30,4.136,358,5.702]],["t/819",[35,3.956,112,4.841,359,4.111]],["t/821",[238,4.842,359,4.842]],["t/823",[359,4.842,360,6.259]],["t/825",[361,7.612]],["t/827",[42,5.335,190,5.061]],["t/829",[190,5.061,362,5.702]],["t/831",[362,4.841,363,5.314,364,5.314]],["t/833",[47,5.164]],["t/835",[47,4.246,203,4.842]],["t/837",[365,7.612]],["t/839",[92,4.297,359,4.111,366,4.841]],["t/841",[25,5.335,26,5.061]],["t/843",[92,4.297,359,4.111,366,4.841]],["t/845",[9,3.574]],["t/849",[367,5.314,368,5.314,369,5.314]],["t/851",[39,2.134,178,2.893,370,5.314]],["t/853",[180,3.437,254,2.978,371,3.935,372,4.617]],["t/855",[261,5.061,373,6.259]],["t/857",[374,7.612]],["t/859",[134,4.842,251,5.335]],["t/861",[153,4.503,325,5.702]],["t/863",[3,4.297,61,3.512,371,4.529]],["t/865",[58,4.111,238,4.111,371,4.529]],["t/867",[30,3.052,307,4.206,375,4.617,376,4.617]],["t/869",[16,2.154,107,4.111,377,5.314]],["t/871",[158,4.842,378,6.259]],["t/873",[47,3.605,235,4.297,251,4.529]],["t/875",[39,1.854,178,2.513,191,3.935,379,4.617]],["t/877",[178,2.893,235,4.297,380,5.314]],["t/879",[9,3.574]],["t/883",[381,6.155]],["t/885",[382,4.529,383,5.314,384,5.314]],["t/887",[382,4.529,385,5.314,386,4.841]],["t/889",[382,4.529,386,4.841,387,5.314]],["t/891",[64,3.279,118,4.111,388,5.314]],["t/893",[82,3.733,196,3.935,389,4.206,390,4.617]],["t/895",[64,2.849,389,4.206,391,4.206,392,4.206]],["t/897",[30,2.698,381,3.3,391,3.718,392,3.718,393,4.082]],["t/899",[118,3.572,155,3.733,236,3.935,394,4.617]],["t/901",[47,3.605,381,4.297,395,5.314]],["t/903",[16,2.154,107,4.111,381,4.297]],["t/905",[134,4.842,396,6.259]],["t/907",[68,2.69,397,5.314,398,5.314]],["t/909",[103,5.335,339,5.702]],["t/911",[9,3.574]],["t/917",[27,5.702,399,6.259]],["t/922",[33,4.841,354,4.841,400,4.841]],["t/924",[71,4.246,400,5.702]],["t/930",[39,2.514,401,6.259]],["t/934",[39,2.514,402,6.259]],["t/936",[39,2.134,403,5.314,404,5.314]],["t/938",[405,6.259,406,5.702]],["t/940",[39,1.854,178,2.513,407,4.617,408,4.617]],["t/942",[39,1.12,45,1.946,178,1.518,409,2.789,410,2.789,411,2.789,412,2.789,413,2.789,414,2.789]],["t/944",[23,1.91,109,2.179,330,1.91,342,2.759,415,3.028,416,3.028,417,3.028,418,3.028]],["t/946",[16,1.655,105,2.937,419,4.082,420,4.082,421,4.082]],["t/948",[14,4.841,37,4.841,422,5.314]],["t/950",[16,1.872,105,3.322,423,4.617,424,4.617]],["t/952",[425,4.617,426,4.617,427,4.617,428,4.617]],["t/954",[429,4.617,430,4.617,431,4.617,432,4.617]],["t/956",[406,5.702,433,5.702]],["t/957",[433,5.702,434,6.259]],["t/958",[3,4.297,46,4.841,61,3.512]],["t/960",[49,5.702,435,6.259]],["t/962",[225,4.111,436,5.314,437,5.314]],["t/964",[203,5.889]],["t/966",[438,7.612]],["t/968",[8,6.488]],["t/972",[8,6.488]],["t/978",[439,6.488]],["t/980",[440,6.934]],["t/984",[441,7.612]],["t/986",[442,7.612]],["t/988",[443,7.612]],["t/990",[117,4.297,444,5.314,445,5.314]],["t/994",[446,7.612]],["t/996",[178,3.407,447,6.259]],["t/1000",[448,7.612]],["t/1002",[449,7.612]],["t/1004",[45,4.304,450,4.082,451,4.082,452,4.082]],["t/1006",[453,6.259,454,6.259]],["t/1008",[455,7.612]],["t/1010",[439,6.488]],["t/1012",[439,6.488]],["t/1014",[440,6.934]],["t/1018",[39,2.514,267,4.136]],["t/1020",[39,2.514,267,4.136]],["t/1022",[267,4.136,456,6.259]],["t/1024",[267,4.136,457,6.259]],["t/1026",[128,4.246,267,4.136]],["t/1028",[16,2.537,458,6.259]],["t/1030",[267,4.136,459,6.259]],["t/1032",[100,4.367,252,5.061]],["t/1034",[267,4.136,460,6.259]],["t/1036",[9,3.574]]],"invertedIndex":[["",{"_index":45,"t":{"64":{"position":[[6,1]]},"152":{"position":[[16,1]]},"154":{"position":[[15,1]]},"160":{"position":[[20,1]]},"166":{"position":[[17,1]]},"785":{"position":[[14,1]]},"942":{"position":[[40,1]]},"1004":{"position":[[7,1],[21,1]]}}}],["1",{"_index":142,"t":{"228":{"position":[[9,2]]},"801":{"position":[[8,1]]}}}],["2",{"_index":146,"t":{"230":{"position":[[9,2]]},"803":{"position":[[8,1]]}}}],["3",{"_index":356,"t":{"805":{"position":[[8,1]]}}}],["abc",{"_index":227,"t":{"399":{"position":[[4,3]]}}}],["account",{"_index":390,"t":{"893":{"position":[[18,7]]}}}],["action",{"_index":130,"t":{"206":{"position":[[11,7]]},"250":{"position":[[14,6]]},"759":{"position":[[17,6]]}}}],["ad",{"_index":310,"t":{"681":{"position":[[0,6]]},"717":{"position":[[0,6]]}}}],["addit",{"_index":327,"t":{"721":{"position":[[0,10]]}}}],["admonit",{"_index":451,"t":{"1004":{"position":[[9,11]]}}}],["advanc",{"_index":203,"t":{"351":{"position":[[9,8]]},"375":{"position":[[0,8]]},"377":{"position":[[0,8]]},"835":{"position":[[0,8]]},"964":{"position":[[0,8]]}}}],["ahah",{"_index":5,"t":{"9":{"position":[[9,5]]}}}],["alias",{"_index":181,"t":{"307":{"position":[[0,7]]}}}],["altern",{"_index":32,"t":{"44":{"position":[[4,11]]},"240":{"position":[[0,12]]},"407":{"position":[[0,12]]},"795":{"position":[[13,13]]}}}],["anchor",{"_index":198,"t":{"345":{"position":[[19,7]]}}}],["annotatedcommand",{"_index":442,"t":{"986":{"position":[[0,16]]}}}],["ansi",{"_index":264,"t":{"497":{"position":[[16,4]]}}}],["anti",{"_index":378,"t":{"871":{"position":[[0,4]]}}}],["append",{"_index":217,"t":{"369":{"position":[[0,9]]}}}],["appendix",{"_index":239,"t":{"441":{"position":[[0,8]]},"615":{"position":[[0,8]]}}}],["appli",{"_index":214,"t":{"365":{"position":[[0,8]]}}}],["arithmet",{"_index":459,"t":{"1030":{"position":[[0,10]]}}}],["around",{"_index":52,"t":{"80":{"position":[[5,6]]}}}],["array",{"_index":259,"t":{"489":{"position":[[0,6]]},"491":{"position":[[12,6]]},"581":{"position":[[12,6]]}}}],["art",{"_index":421,"t":{"946":{"position":[[18,3]]}}}],["asciinema",{"_index":446,"t":{"994":{"position":[[0,9]]}}}],["asciinemaplay",{"_index":441,"t":{"984":{"position":[[0,15]]}}}],["asid",{"_index":450,"t":{"1004":{"position":[[0,6]]}}}],["associ",{"_index":260,"t":{"491":{"position":[[0,11]]}}}],["asymmetr",{"_index":387,"t":{"889":{"position":[[0,10]]}}}],["atom",{"_index":207,"t":{"351":{"position":[[57,6]]}}}],["attach",{"_index":363,"t":{"831":{"position":[[0,9]]}}}],["authent",{"_index":382,"t":{"885":{"position":[[3,14]]},"887":{"position":[[25,14]]},"889":{"position":[[26,14]]}}}],["avoid",{"_index":202,"t":{"351":{"position":[[0,8]]},"793":{"position":[[16,5]]}}}],["aw",{"_index":389,"t":{"893":{"position":[[14,3]]},"895":{"position":[[30,3]]}}}],["awk",{"_index":224,"t":{"381":{"position":[[11,4]]}}}],["back",{"_index":93,"t":{"142":{"position":[[6,4]]},"283":{"position":[[0,4]]}}}],["background",{"_index":145,"t":{"228":{"position":[[36,10]]},"230":{"position":[[35,10]]},"232":{"position":[[7,10]]}}}],["backtrack",{"_index":205,"t":{"351":{"position":[[27,13]]}}}],["backward",{"_index":97,"t":{"154":{"position":[[5,9]]},"166":{"position":[[7,9]]}}}],["bash",{"_index":431,"t":{"954":{"position":[[20,4]]}}}],["basic",{"_index":19,"t":{"24":{"position":[[15,6]]},"150":{"position":[[0,5]]},"339":{"position":[[34,6]]},"415":{"position":[[11,5]]},"801":{"position":[[12,5]]}}}],["be",{"_index":257,"t":{"487":{"position":[[0,5]]}}}],["begin",{"_index":95,"t":{"152":{"position":[[6,9]]},"160":{"position":[[10,9]]}}}],["billemont",{"_index":430,"t":{"954":{"position":[[8,9]]}}}],["blurb",{"_index":452,"t":{"1004":{"position":[[23,6]]}}}],["book",{"_index":406,"t":{"938":{"position":[[10,5]]},"956":{"position":[[6,5]]}}}],["brace",{"_index":456,"t":{"1022":{"position":[[0,5]]}}}],["branch",{"_index":315,"t":{"687":{"position":[[9,8]]},"691":{"position":[[17,8]]}}}],["brandon",{"_index":413,"t":{"942":{"position":[[42,7]]}}}],["break",{"_index":291,"t":{"601":{"position":[[13,5]]}}}],["build",{"_index":192,"t":{"339":{"position":[[0,8]]},"341":{"position":[[0,8]]},"343":{"position":[[0,8]]},"345":{"position":[[0,8]]},"347":{"position":[[0,8]]},"349":{"position":[[0,8]]},"423":{"position":[[0,8]]},"761":{"position":[[0,8]]}}}],["builtin",{"_index":179,"t":{"303":{"position":[[0,8]]}}}],["c",{"_index":265,"t":{"497":{"position":[[21,1]]},"589":{"position":[[12,1]]}}}],["call",{"_index":4,"t":{"7":{"position":[[26,6]]}}}],["came",{"_index":436,"t":{"962":{"position":[[11,4]]}}}],["cannon",{"_index":408,"t":{"940":{"position":[[25,6]]}}}],["captur",{"_index":199,"t":{"347":{"position":[[19,7]]}}}],["caret",{"_index":443,"t":{"988":{"position":[[0,5]]}}}],["case",{"_index":124,"t":{"200":{"position":[[0,4]]},"571":{"position":[[0,4]]}}}],["chain",{"_index":283,"t":{"569":{"position":[[0,8]]}}}],["chang",{"_index":54,"t":{"88":{"position":[[4,8]]},"132":{"position":[[0,8]]},"647":{"position":[[0,8]]},"681":{"position":[[21,7]]},"683":{"position":[[11,7]]},"715":{"position":[[0,8]]}}}],["charact",{"_index":195,"t":{"343":{"position":[[19,9]]},"463":{"position":[[29,10]]},"713":{"position":[[8,10]]}}}],["cheat",{"_index":332,"t":{"761":{"position":[[15,5]]}}}],["cheatsheet",{"_index":38,"t":{"50":{"position":[[11,10]]}}}],["check",{"_index":371,"t":{"853":{"position":[[0,8]]},"863":{"position":[[0,8]]},"865":{"position":[[0,8]]}}}],["clean",{"_index":148,"t":{"234":{"position":[[0,8]]}}}],["clear",{"_index":104,"t":{"160":{"position":[[22,5]]},"176":{"position":[[0,5]]}}}],["clipboard",{"_index":13,"t":{"20":{"position":[[4,9]]},"22":{"position":[[14,9]]}}}],["code",{"_index":278,"t":{"535":{"position":[[17,5]]},"541":{"position":[[22,5]]}}}],["collabor",{"_index":365,"t":{"837":{"position":[[0,13]]}}}],["colour",{"_index":325,"t":{"715":{"position":[[13,6]]},"861":{"position":[[0,9]]}}}],["combin",{"_index":123,"t":{"198":{"position":[[0,9]]},"563":{"position":[[0,9]]}}}],["command",{"_index":16,"t":{"22":{"position":[[24,8]]},"40":{"position":[[18,7]]},"78":{"position":[[9,7]]},"168":{"position":[[8,7]]},"170":{"position":[[9,7]]},"178":{"position":[[37,7]]},"188":{"position":[[21,7]]},"212":{"position":[[10,7]]},"214":{"position":[[10,7]]},"285":{"position":[[4,7],[22,7]]},"287":{"position":[[6,8]]},"295":{"position":[[9,9]]},"297":{"position":[[23,8]]},"325":{"position":[[0,7]]},"417":{"position":[[13,7]]},"425":{"position":[[11,8]]},"465":{"position":[[4,7],[20,9]]},"467":{"position":[[26,8]]},"485":{"position":[[24,7]]},"507":{"position":[[9,7]]},"511":{"position":[[22,7]]},"533":{"position":[[23,8]]},"543":{"position":[[22,7]]},"551":{"position":[[9,7]]},"569":{"position":[[9,8]]},"573":{"position":[[22,7]]},"611":{"position":[[22,7]]},"641":{"position":[[14,8]]},"655":{"position":[[13,7]]},"657":{"position":[[14,7]]},"659":{"position":[[13,7]]},"667":{"position":[[31,7]]},"671":{"position":[[23,8]]},"707":{"position":[[4,7]]},"709":{"position":[[16,7]]},"717":{"position":[[19,7]]},"765":{"position":[[25,8]]},"767":{"position":[[4,7]]},"769":{"position":[[16,8]]},"777":{"position":[[8,8]]},"869":{"position":[[8,8]]},"903":{"position":[[12,8]]},"946":{"position":[[29,7]]},"950":{"position":[[0,7]]},"1028":{"position":[[0,7]]}}}],["comment",{"_index":216,"t":{"367":{"position":[[10,8]]},"421":{"position":[[0,8]]}}}],["commit",{"_index":313,"t":{"683":{"position":[[0,10]]},"685":{"position":[[0,6]]}}}],["common",{"_index":157,"t":{"252":{"position":[[0,6]]},"254":{"position":[[0,6]]},"256":{"position":[[0,6]]},"417":{"position":[[4,8]]},"511":{"position":[[13,8]]},"543":{"position":[[13,8]]},"559":{"position":[[0,6]]},"561":{"position":[[0,6]]},"573":{"position":[[13,8]]},"611":{"position":[[13,8]]},"625":{"position":[[0,6]]}}}],["compact",{"_index":292,"t":{"603":{"position":[[9,7]]}}}],["complex",{"_index":191,"t":{"337":{"position":[[9,10]]},"467":{"position":[[18,7]]},"875":{"position":[[0,7]]}}}],["compon",{"_index":445,"t":{"990":{"position":[[20,10]]}}}],["comput",{"_index":166,"t":{"271":{"position":[[2,8]]}}}],["computerphil",{"_index":437,"t":{"962":{"position":[[23,13]]}}}],["condit",{"_index":282,"t":{"565":{"position":[[0,11]]},"567":{"position":[[19,11]]}}}],["configur",{"_index":47,"t":{"66":{"position":[[0,11]]},"621":{"position":[[10,13]]},"623":{"position":[[12,13]]},"625":{"position":[[13,14]]},"721":{"position":[[18,13]]},"833":{"position":[[0,13]]},"835":{"position":[[9,13]]},"873":{"position":[[0,11]]},"901":{"position":[[0,11]]}}}],["confirm",{"_index":133,"t":{"214":{"position":[[25,12]]},"469":{"position":[[11,12]]}}}],["conflict",{"_index":319,"t":{"695":{"position":[[6,9]]}}}],["connect",{"_index":393,"t":{"897":{"position":[[13,7]]}}}],["construct",{"_index":249,"t":{"467":{"position":[[0,12]]}}}],["content",{"_index":73,"t":{"106":{"position":[[14,8]]},"130":{"position":[[12,8]]}}}],["continu",{"_index":290,"t":{"601":{"position":[[0,8]]}}}],["control",{"_index":139,"t":{"224":{"position":[[12,8]]}}}],["converg",{"_index":60,"t":{"92":{"position":[[0,11]]}}}],["cool",{"_index":410,"t":{"942":{"position":[[7,4]]}}}],["copi",{"_index":17,"t":{"24":{"position":[[0,4]]},"108":{"position":[[0,7]]},"116":{"position":[[0,7]]}}}],["count",{"_index":336,"t":{"767":{"position":[[12,6]]}}}],["cours",{"_index":384,"t":{"885":{"position":[[24,6]]}}}],["crash",{"_index":383,"t":{"885":{"position":[[18,5]]}}}],["creat",{"_index":64,"t":{"98":{"position":[[0,8]]},"114":{"position":[[0,8]]},"415":{"position":[[0,8]]},"419":{"position":[[0,8]]},"517":{"position":[[0,8]]},"603":{"position":[[0,8]]},"679":{"position":[[0,8]]},"687":{"position":[[0,8]]},"735":{"position":[[0,8]]},"737":{"position":[[0,8]]},"763":{"position":[[0,8]]},"891":{"position":[[0,8]]},"895":{"position":[[0,8]]}}}],["cs",{"_index":427,"t":{"952":{"position":[[29,2]]}}}],["customis",{"_index":323,"t":{"709":{"position":[[0,11]]},"719":{"position":[[18,9]]}}}],["cut",{"_index":242,"t":{"449":{"position":[[7,3]]}}}],["data",{"_index":326,"t":{"717":{"position":[[7,4]]}}}],["dave",{"_index":411,"t":{"942":{"position":[[28,4]]}}}],["deal",{"_index":236,"t":{"431":{"position":[[11,7]]},"533":{"position":[[0,7]]},"899":{"position":[[0,7]]}}}],["debug",{"_index":370,"t":{"851":{"position":[[0,9]]}}}],["default",{"_index":295,"t":{"623":{"position":[[4,7]]},"733":{"position":[[4,7]]}}}],["definit",{"_index":355,"t":{"803":{"position":[[28,10]]}}}],["delet",{"_index":71,"t":{"104":{"position":[[0,8]]},"118":{"position":[[0,8]]},"156":{"position":[[0,6]]},"158":{"position":[[0,6]]},"160":{"position":[[0,6]]},"162":{"position":[[0,6]]},"210":{"position":[[0,8]]},"699":{"position":[[0,8]]},"924":{"position":[[0,8]]}}}],["delimit",{"_index":253,"t":{"471":{"position":[[26,9]]}}}],["demo",{"_index":49,"t":{"76":{"position":[[8,4]]},"960":{"position":[[18,5]]}}}],["desktop",{"_index":300,"t":{"645":{"position":[[17,7]]}}}],["detach",{"_index":364,"t":{"831":{"position":[[14,9]]}}}],["develop",{"_index":444,"t":{"990":{"position":[[9,10]]}}}],["devop",{"_index":63,"t":{"94":{"position":[[0,6]]}}}],["diagram",{"_index":440,"t":{"980":{"position":[[0,8]]},"1014":{"position":[[0,8]]}}}],["dictionari",{"_index":349,"t":{"799":{"position":[[10,10]]}}}],["differ",{"_index":173,"t":{"287":{"position":[[19,9]]},"297":{"position":[[4,9]]},"629":{"position":[[0,9]]}}}],["directori",{"_index":84,"t":{"128":{"position":[[12,9]]},"130":{"position":[[36,9]]},"132":{"position":[[13,9]]},"138":{"position":[[9,9]]},"140":{"position":[[32,9]]}}}],["disconnect",{"_index":396,"t":{"905":{"position":[[9,14]]}}}],["discuss",{"_index":176,"t":{"291":{"position":[[10,7]]}}}],["diverg",{"_index":317,"t":{"691":{"position":[[8,8]]}}}],["divers",{"_index":57,"t":{"90":{"position":[[4,9]]}}}],["don't",{"_index":230,"t":{"405":{"position":[[0,5]]},"455":{"position":[[0,5]]}}}],["dot",{"_index":88,"t":{"136":{"position":[[12,3],[20,3],[24,3]]},"435":{"position":[[0,3]]}}}],["dotfil",{"_index":305,"t":{"661":{"position":[[13,8]]},"731":{"position":[[0,8]]},"733":{"position":[[18,7]]},"735":{"position":[[11,8]]},"737":{"position":[[17,7]]},"739":{"position":[[18,7]]},"741":{"position":[[19,7]]},"745":{"position":[[2,7]]}}}],["doubl",{"_index":266,"t":{"499":{"position":[[0,6]]}}}],["download",{"_index":354,"t":{"803":{"position":[[12,11]]},"922":{"position":[[9,11]]}}}],["drew",{"_index":417,"t":{"944":{"position":[[50,4]]}}}],["echo",{"_index":50,"t":{"78":{"position":[[4,4]]}}}],["edit",{"_index":109,"t":{"170":{"position":[[0,4]]},"174":{"position":[[0,7]]},"379":{"position":[[22,8]]},"757":{"position":[[14,8]]},"759":{"position":[[6,7]]},"777":{"position":[[0,7]]},"944":{"position":[[15,4]]}}}],["editor",{"_index":210,"t":{"359":{"position":[[23,6]]},"751":{"position":[[19,6]]},"753":{"position":[[19,7]]}}}],["educ",{"_index":428,"t":{"952":{"position":[[32,9]]}}}],["elif",{"_index":281,"t":{"557":{"position":[[4,4]]}}}],["emphasi",{"_index":449,"t":{"1002":{"position":[[0,8]]}}}],["encrypt",{"_index":386,"t":{"887":{"position":[[10,10]]},"889":{"position":[[11,10]]}}}],["end",{"_index":96,"t":{"152":{"position":[[18,3]]},"162":{"position":[[10,3]]}}}],["ensur",{"_index":367,"t":{"849":{"position":[[0,8]]}}}],["environ",{"_index":255,"t":{"483":{"position":[[20,11]]}}}],["error",{"_index":155,"t":{"248":{"position":[[27,5]]},"256":{"position":[[27,5]]},"537":{"position":[[0,5]]},"899":{"position":[[28,6]]}}}],["essenti",{"_index":14,"t":{"20":{"position":[[14,10]]},"948":{"position":[[0,9]]}}}],["examin",{"_index":72,"t":{"106":{"position":[[0,9]]}}}],["execut",{"_index":114,"t":{"178":{"position":[[20,7]]},"212":{"position":[[0,7]]},"214":{"position":[[0,7]]},"299":{"position":[[0,11]]},"301":{"position":[[0,11]]}}}],["exist",{"_index":372,"t":{"853":{"position":[[13,8]]}}}],["exit",{"_index":368,"t":{"849":{"position":[[9,4]]}}}],["expans",{"_index":267,"t":{"499":{"position":[[26,9]]},"501":{"position":[[18,9]]},"505":{"position":[[16,9]]},"1018":{"position":[[14,10]]},"1020":{"position":[[6,9]]},"1022":{"position":[[6,9]]},"1024":{"position":[[6,9]]},"1026":{"position":[[10,9]]},"1030":{"position":[[11,9]]},"1034":{"position":[[9,9]]}}}],["explicit",{"_index":258,"t":{"487":{"position":[[6,8]]}}}],["express",{"_index":189,"t":{"335":{"position":[[27,11]]},"337":{"position":[[33,11]]},"349":{"position":[[35,11]]},"365":{"position":[[18,11]]},"565":{"position":[[12,11]]},"567":{"position":[[31,10]]}}}],["extract",{"_index":69,"t":{"102":{"position":[[0,10]]},"373":{"position":[[0,10]]}}}],["failur",{"_index":369,"t":{"849":{"position":[[17,7]]}}}],["fantast",{"_index":405,"t":{"938":{"position":[[0,9]]}}}],["fetch",{"_index":303,"t":{"657":{"position":[[8,5]]}}}],["file",{"_index":68,"t":{"100":{"position":[[18,5]]},"104":{"position":[[9,5]]},"108":{"position":[[10,4]]},"112":{"position":[[19,5]]},"116":{"position":[[27,5]]},"120":{"position":[[16,5]]},"122":{"position":[[11,5]]},"192":{"position":[[14,5]]},"210":{"position":[[9,5]]},"377":{"position":[[20,5]]},"401":{"position":[[22,5]]},"561":{"position":[[26,5]]},"585":{"position":[[12,5]]},"587":{"position":[[12,5]]},"595":{"position":[[45,4]]},"621":{"position":[[24,4]]},"623":{"position":[[26,4]]},"637":{"position":[[14,5]]},"639":{"position":[[18,4]]},"641":{"position":[[23,4]]},"643":{"position":[[8,5]]},"699":{"position":[[22,5]]},"743":{"position":[[9,5]]},"763":{"position":[[11,4]]},"907":{"position":[[13,5]]}}}],["find",{"_index":66,"t":{"100":{"position":[[0,7]]},"188":{"position":[[16,4]]},"190":{"position":[[15,4]]},"397":{"position":[[0,7]]},"587":{"position":[[23,4]]}}}],["folder",{"_index":74,"t":{"106":{"position":[[28,6]]},"114":{"position":[[15,6]]},"118":{"position":[[11,6]]},"136":{"position":[[28,7]]},"192":{"position":[[23,7]]},"735":{"position":[[20,6]]},"743":{"position":[[22,6]]}}}],["foreground",{"_index":147,"t":{"232":{"position":[[30,10]]}}}],["forget",{"_index":231,"t":{"405":{"position":[[6,6]]},"455":{"position":[[6,6]]}}}],["fork",{"_index":306,"t":{"663":{"position":[[0,7]]}}}],["format",{"_index":21,"t":{"26":{"position":[[9,10]]},"715":{"position":[[29,10]]},"805":{"position":[[12,10]]}}}],["forward",{"_index":98,"t":{"154":{"position":[[17,8]]},"166":{"position":[[19,8]]}}}],["found",{"_index":108,"t":{"168":{"position":[[16,5]]},"170":{"position":[[17,5]]}}}],["friendli",{"_index":347,"t":{"797":{"position":[[19,8]]}}}],["function",{"_index":180,"t":{"305":{"position":[[0,9]]},"517":{"position":[[11,8]]},"519":{"position":[[13,9]]},"523":{"position":[[22,9]]},"539":{"position":[[4,8]]},"853":{"position":[[35,9]]}}}],["further",{"_index":366,"t":{"839":{"position":[[6,7]]},"843":{"position":[[6,7]]}}}],["futur",{"_index":402,"t":{"934":{"position":[[4,6]]}}}],["gener",{"_index":403,"t":{"936":{"position":[[0,7]]}}}],["get",{"_index":25,"t":{"36":{"position":[[0,7]]},"40":{"position":[[0,7]]},"841":{"position":[[0,7]]}}}],["git",{"_index":301,"t":{"653":{"position":[[0,3]]},"655":{"position":[[4,3]]},"657":{"position":[[4,3]]},"659":{"position":[[4,3]]},"667":{"position":[[8,3]]},"671":{"position":[[19,3]]},"677":{"position":[[8,3]]},"679":{"position":[[11,3]]},"693":{"position":[[4,3]]}}}],["go",{"_index":92,"t":{"142":{"position":[[0,5]]},"152":{"position":[[0,2]]},"839":{"position":[[0,5]]},"843":{"position":[[0,5]]}}}],["great",{"_index":433,"t":{"956":{"position":[[0,5]]},"957":{"position":[[0,5]]}}}],["greedi",{"_index":201,"t":{"349":{"position":[[28,6]]}}}],["grep",{"_index":225,"t":{"389":{"position":[[8,4]]},"391":{"position":[[4,5]]},"399":{"position":[[11,4]]},"407":{"position":[[16,4]]},"962":{"position":[[6,4]]}}}],["group",{"_index":126,"t":{"202":{"position":[[0,8]]},"347":{"position":[[27,6]]},"351":{"position":[[64,8]]}}}],["guid",{"_index":432,"t":{"954":{"position":[[25,5]]}}}],["guidelin",{"_index":424,"t":{"950":{"position":[[23,10]]}}}],["hack",{"_index":0,"t":{"7":{"position":[[0,4]]},"9":{"position":[[0,4]]}}}],["handl",{"_index":134,"t":{"216":{"position":[[0,8]]},"463":{"position":[[0,8]]},"537":{"position":[[6,8]]},"859":{"position":[[0,8]]},"905":{"position":[[0,8]]}}}],["head",{"_index":240,"t":{"445":{"position":[[0,5]]}}}],["help",{"_index":26,"t":{"36":{"position":[[8,4]]},"40":{"position":[[8,4]]},"44":{"position":[[18,4]]},"841":{"position":[[8,4]]}}}],["histori",{"_index":113,"t":{"178":{"position":[[8,7]]}}}],["home",{"_index":89,"t":{"138":{"position":[[4,4]]}}}],["host",{"_index":395,"t":{"901":{"position":[[16,5]]}}}],["if",{"_index":293,"t":{"605":{"position":[[19,3]]},"609":{"position":[[4,3]]}}}],["imag",{"_index":439,"t":{"978":{"position":[[0,6]]},"1010":{"position":[[0,6]]},"1012":{"position":[[0,6]]}}}],["import",{"_index":27,"t":{"36":{"position":[[16,10]]},"917":{"position":[[9,9]]}}}],["improv",{"_index":357,"t":{"809":{"position":[[0,9]]}}}],["index",{"_index":312,"t":{"681":{"position":[[36,5]]}}}],["infinit",{"_index":288,"t":{"597":{"position":[[18,8]]}}}],["inform",{"_index":219,"t":{"373":{"position":[[11,11]]},"667":{"position":[[12,11]]}}}],["input",{"_index":152,"t":{"246":{"position":[[0,5]]},"248":{"position":[[9,6]]},"252":{"position":[[27,5]]},"471":{"position":[[13,5]]}}}],["insensit",{"_index":125,"t":{"200":{"position":[[5,11]]}}}],["insert",{"_index":337,"t":{"769":{"position":[[9,6]]}}}],["instal",{"_index":238,"t":{"437":{"position":[[0,10]]},"745":{"position":[[10,7]]},"807":{"position":[[0,10]]},"821":{"position":[[0,10]]},"865":{"position":[[13,9]]}}}],["interact",{"_index":297,"t":{"631":{"position":[[0,11]]},"633":{"position":[[4,11]]},"643":{"position":[[22,11]]}}}],["interfac",{"_index":423,"t":{"950":{"position":[[13,9]]}}}],["introduc",{"_index":35,"t":{"48":{"position":[[0,11]]},"188":{"position":[[0,11]]},"359":{"position":[[0,11]]},"461":{"position":[[0,11]]},"755":{"position":[[0,11]]},"819":{"position":[[0,11]]}}}],["introduct",{"_index":163,"t":{"265":{"position":[[0,12]]},"267":{"position":[[0,12]]},"335":{"position":[[3,12]]}}}],["invert",{"_index":229,"t":{"403":{"position":[[6,6]]}}}],["jason",{"_index":407,"t":{"940":{"position":[[19,5]]}}}],["job",{"_index":138,"t":{"224":{"position":[[8,3]]},"232":{"position":[[18,4]]},"234":{"position":[[12,4]]},"236":{"position":[[22,4]]},"240":{"position":[[16,4]]}}}],["joshua",{"_index":419,"t":{"946":{"position":[[0,6]]}}}],["kernel",{"_index":168,"t":{"275":{"position":[[4,6]]}}}],["key",{"_index":118,"t":{"180":{"position":[[17,5]]},"238":{"position":[[9,3]]},"309":{"position":[[4,3]]},"891":{"position":[[11,3]]},"899":{"position":[[13,3]]}}}],["keystrok",{"_index":76,"t":{"110":{"position":[[12,10]]}}}],["keyword",{"_index":279,"t":{"539":{"position":[[13,7]]}}}],["know",{"_index":184,"t":{"311":{"position":[[22,4]]}}}],["landscap",{"_index":56,"t":{"88":{"position":[[24,9]]}}}],["languag",{"_index":59,"t":{"90":{"position":[[29,9]]}}}],["last",{"_index":159,"t":{"258":{"position":[[4,4]]}}}],["lazi",{"_index":200,"t":{"349":{"position":[[19,4]]}}}],["leader",{"_index":361,"t":{"825":{"position":[[4,6]]}}}],["let'",{"_index":165,"t":{"269":{"position":[[0,5]]}}}],["levi",{"_index":420,"t":{"946":{"position":[[7,4]]}}}],["line",{"_index":105,"t":{"160":{"position":[[28,4]]},"285":{"position":[[30,4]]},"425":{"position":[[6,4]]},"553":{"position":[[38,4]]},"595":{"position":[[34,5]]},"946":{"position":[[37,4]]},"950":{"position":[[8,4]]}}}],["link",{"_index":448,"t":{"1000":{"position":[[0,5]]}}}],["linux",{"_index":44,"t":{"64":{"position":[[0,5]]},"72":{"position":[[0,5]]}}}],["list",{"_index":85,"t":{"130":{"position":[[0,7]]}}}],["liter",{"_index":263,"t":{"495":{"position":[[16,7]]}}}],["log",{"_index":318,"t":{"693":{"position":[[8,3]]}}}],["logic",{"_index":379,"t":{"875":{"position":[[8,5]]}}}],["login",{"_index":298,"t":{"635":{"position":[[0,5]]},"645":{"position":[[0,5]]}}}],["look",{"_index":81,"t":{"120":{"position":[[0,7]]}}}],["lookaround",{"_index":206,"t":{"351":{"position":[[41,11]]}}}],["lookup",{"_index":350,"t":{"799":{"position":[[21,6]]},"807":{"position":[[15,6]]},"809":{"position":[[14,6]]}}}],["loop",{"_index":284,"t":{"579":{"position":[[8,4]]},"581":{"position":[[4,5]]},"583":{"position":[[4,5]]},"585":{"position":[[4,5]]},"587":{"position":[[4,5]]},"589":{"position":[[4,5],[20,5]]},"591":{"position":[[4,5],[12,7]]},"593":{"position":[[10,4]]},"595":{"position":[[6,5],[14,7]]},"597":{"position":[[6,5],[27,4]]},"599":{"position":[[10,4]]},"603":{"position":[[17,5]]},"615":{"position":[[11,5]]}}}],["maarten",{"_index":429,"t":{"954":{"position":[[0,7]]}}}],["machin",{"_index":392,"t":{"895":{"position":[[19,7]]},"897":{"position":[[34,7]]}}}],["maco",{"_index":43,"t":{"62":{"position":[[0,5]]},"70":{"position":[[0,5]]}}}],["make",{"_index":346,"t":{"797":{"position":[[5,5]]}}}],["man",{"_index":29,"t":{"38":{"position":[[14,5]]}}}],["manag",{"_index":190,"t":{"337":{"position":[[0,8]]},"645":{"position":[[25,8]]},"827":{"position":[[7,10]]},"829":{"position":[[8,10]]}}}],["mani",{"_index":248,"t":{"465":{"position":[[15,4]]}}}],["manipul",{"_index":24,"t":{"30":{"position":[[0,12]]}}}],["manpag",{"_index":399,"t":{"917":{"position":[[19,8]]}}}],["manual",{"_index":33,"t":{"46":{"position":[[0,6]]},"922":{"position":[[0,8]]}}}],["markua",{"_index":453,"t":{"1006":{"position":[[0,6]]}}}],["mathemat",{"_index":269,"t":{"509":{"position":[[0,11]]}}}],["menu",{"_index":376,"t":{"867":{"position":[[25,4]]}}}],["merg",{"_index":316,"t":{"689":{"position":[[0,7]]},"691":{"position":[[0,7]]},"695":{"position":[[0,5]]},"697":{"position":[[6,5]]}}}],["messag",{"_index":314,"t":{"685":{"position":[[7,8]]}}}],["metacharact",{"_index":197,"t":{"343":{"position":[[38,14]]}}}],["microsoft",{"_index":41,"t":{"60":{"position":[[0,9]]},"68":{"position":[[0,9]]}}}],["miss",{"_index":425,"t":{"952":{"position":[[4,7]]}}}],["mistak",{"_index":102,"t":{"156":{"position":[[24,7]]}}}],["modal",{"_index":331,"t":{"757":{"position":[[8,5]]},"759":{"position":[[0,5]]}}}],["modern",{"_index":343,"t":{"785":{"position":[[16,6]]}}}],["modifi",{"_index":338,"t":{"775":{"position":[[17,9]]}}}],["more",{"_index":182,"t":{"311":{"position":[[0,4]]},"467":{"position":[[13,4]]}}}],["mother",{"_index":435,"t":{"960":{"position":[[4,6]]}}}],["motion",{"_index":334,"t":{"765":{"position":[[4,7]]},"773":{"position":[[7,7]]}}}],["move",{"_index":51,"t":{"80":{"position":[[0,4]]},"112":{"position":[[12,6]]},"116":{"position":[[11,6]]},"154":{"position":[[0,4]]},"230":{"position":[[12,4]]},"232":{"position":[[0,6]]}}}],["movement",{"_index":335,"t":{"765":{"position":[[16,8]]}}}],["multi",{"_index":234,"t":{"425":{"position":[[0,5]]}}}],["multipl",{"_index":79,"t":{"116":{"position":[[18,8]]},"365":{"position":[[9,8]]},"401":{"position":[[13,8]]},"553":{"position":[[6,8]]}}}],["multiplex",{"_index":358,"t":{"815":{"position":[[19,12]]},"817":{"position":[[10,12]]}}}],["name",{"_index":122,"t":{"194":{"position":[[13,4]]},"487":{"position":[[29,5]]}}}],["navig",{"_index":94,"t":{"150":{"position":[[6,10]]}}}],["need",{"_index":183,"t":{"311":{"position":[[14,4]]}}}],["new",{"_index":78,"t":{"114":{"position":[[11,3]]}}}],["next",{"_index":103,"t":{"158":{"position":[[11,4]]},"779":{"position":[[0,4]]},"909":{"position":[[0,4]]}}}],["niel",{"_index":418,"t":{"944":{"position":[[55,4]]}}}],["non",{"_index":11,"t":{"14":{"position":[[9,3]]},"265":{"position":[[21,3]]},"633":{"position":[[0,3]]},"643":{"position":[[18,3]]}}}],["nutshel",{"_index":167,"t":{"271":{"position":[[16,8]]}}}],["oh",{"_index":328,"t":{"723":{"position":[[12,2]]}}}],["on",{"_index":99,"t":{"154":{"position":[[26,3]]},"258":{"position":[[0,3]]},"465":{"position":[[0,3]]}}}],["onlin",{"_index":37,"t":{"50":{"position":[[4,6]]},"948":{"position":[[10,6]]}}}],["open",{"_index":40,"t":{"58":{"position":[[0,7]]},"665":{"position":[[12,4]]}}}],["oper",{"_index":61,"t":{"92":{"position":[[15,9]]},"198":{"position":[[36,9]]},"202":{"position":[[21,8]]},"273":{"position":[[4,9]]},"559":{"position":[[12,9]]},"561":{"position":[[12,9]]},"771":{"position":[[4,9]]},"773":{"position":[[36,9]]},"863":{"position":[[13,9]]},"958":{"position":[[9,9]]}}}],["option",{"_index":251,"t":{"469":{"position":[[40,6]]},"859":{"position":[[9,7]]},"873":{"position":[[12,7]]}}}],["out",{"_index":67,"t":{"100":{"position":[[8,3]]}}}],["output",{"_index":153,"t":{"246":{"position":[[10,6]]},"248":{"position":[[16,6]]},"254":{"position":[[27,6]]},"485":{"position":[[12,6]]},"533":{"position":[[13,6]]},"805":{"position":[[27,6]]},"861":{"position":[[10,6]]}}}],["over",{"_index":286,"t":{"591":{"position":[[20,4]]}}}],["overview",{"_index":308,"t":{"671":{"position":[[3,8]]}}}],["pager",{"_index":31,"t":{"42":{"position":[[10,5]]},"455":{"position":[[18,6]]}}}],["pair",{"_index":388,"t":{"891":{"position":[[15,4]]}}}],["paramet",{"_index":128,"t":{"204":{"position":[[14,11]]},"467":{"position":[[48,9]]},"499":{"position":[[16,9]]},"505":{"position":[[6,9]]},"523":{"position":[[8,10]]},"525":{"position":[[0,9]]},"527":{"position":[[0,9]]},"541":{"position":[[0,10]]},"1026":{"position":[[0,9]]}}}],["part",{"_index":221,"t":{"375":{"position":[[23,5]]}}}],["pass",{"_index":272,"t":{"523":{"position":[[0,7]]}}}],["past",{"_index":18,"t":{"24":{"position":[[9,5]]}}}],["path",{"_index":86,"t":{"134":{"position":[[14,5]]},"196":{"position":[[13,4]]},"208":{"position":[[9,5]]},"431":{"position":[[24,5]]}}}],["pathnam",{"_index":460,"t":{"1034":{"position":[[0,8]]}}}],["pattern",{"_index":158,"t":{"252":{"position":[[7,8]]},"254":{"position":[[7,8]]},"256":{"position":[[7,8]]},"395":{"position":[[6,8]]},"871":{"position":[[5,8]]}}}],["perform",{"_index":129,"t":{"206":{"position":[[0,10]]}}}],["permiss",{"_index":394,"t":{"899":{"position":[[17,10]]}}}],["perri",{"_index":414,"t":{"942":{"position":[[50,5]]}}}],["pipe",{"_index":162,"t":{"258":{"position":[[23,4]]}}}],["pipelin",{"_index":156,"t":{"250":{"position":[[2,8]]},"405":{"position":[[18,10]]}}}],["place",{"_index":111,"t":{"174":{"position":[[11,5]]},"379":{"position":[[15,6]]}}}],["platform",{"_index":62,"t":{"92":{"position":[[25,9]]}}}],["playground",{"_index":65,"t":{"98":{"position":[[11,10]]}}}],["pop",{"_index":91,"t":{"140":{"position":[[12,7]]}}}],["posix",{"_index":401,"t":{"930":{"position":[[0,5]]}}}],["power",{"_index":120,"t":{"184":{"position":[[4,5]]}}}],["practic",{"_index":342,"t":{"785":{"position":[[0,9]]},"944":{"position":[[0,9]]}}}],["prepar",{"_index":15,"t":{"22":{"position":[[0,9]]}}}],["prepend",{"_index":218,"t":{"371":{"position":[[0,10]]}}}],["print",{"_index":132,"t":{"208":{"position":[[0,8]]}}}],["pro",{"_index":116,"t":{"180":{"position":[[0,3]]},"182":{"position":[[0,3]]}}}],["problem",{"_index":140,"t":{"226":{"position":[[4,7]]},"397":{"position":[[8,8]]}}}],["profil",{"_index":299,"t":{"639":{"position":[[10,7]]}}}],["program",{"_index":58,"t":{"90":{"position":[[17,11]]},"299":{"position":[[14,8]]},"383":{"position":[[8,7]]},"809":{"position":[[21,7]]},"865":{"position":[[23,8]]}}}],["prompt",{"_index":172,"t":{"285":{"position":[[12,6]]},"469":{"position":[[33,6]]},"667":{"position":[[39,6]]},"707":{"position":[[12,6]]},"709":{"position":[[24,6]]},"711":{"position":[[4,6]]},"717":{"position":[[27,6]]},"719":{"position":[[32,6]]},"721":{"position":[[11,6]]}}}],["pull",{"_index":304,"t":{"659":{"position":[[8,4]]},"663":{"position":[[12,4]]},"665":{"position":[[19,4]]}}}],["push",{"_index":90,"t":{"140":{"position":[[0,7]]},"655":{"position":[[8,4]]}}}],["python",{"_index":351,"t":{"799":{"position":[[36,6]]}}}],["quantifi",{"_index":194,"t":{"341":{"position":[[19,11]]}}}],["quick",{"_index":48,"t":{"76":{"position":[[2,5]]},"242":{"position":[[0,5]]}}}],["quot",{"_index":222,"t":{"375":{"position":[[42,6]]},"493":{"position":[[0,7]]},"495":{"position":[[7,6]]},"497":{"position":[[7,6],[23,7]]},"499":{"position":[[7,6]]},"501":{"position":[[3,6]]},"503":{"position":[[0,7]]}}}],["read",{"_index":268,"t":{"507":{"position":[[4,4]]}}}],["reader",{"_index":164,"t":{"265":{"position":[[35,6]]},"267":{"position":[[31,6]]}}}],["readlin",{"_index":121,"t":{"184":{"position":[[13,8]]}}}],["recent",{"_index":115,"t":{"178":{"position":[[30,6]]}}}],["record",{"_index":447,"t":{"996":{"position":[[7,9]]}}}],["refer",{"_index":151,"t":{"242":{"position":[[6,9]]}}}],["regex",{"_index":193,"t":{"339":{"position":[[9,7]]},"341":{"position":[[9,7]]},"343":{"position":[[9,7]]},"345":{"position":[[9,7]]},"347":{"position":[[9,7]]},"349":{"position":[[9,7]]},"567":{"position":[[6,7]]}}}],["regular",{"_index":188,"t":{"335":{"position":[[19,7]]},"337":{"position":[[25,7]]}}}],["remot",{"_index":302,"t":{"653":{"position":[[4,7]]},"655":{"position":[[25,7]]}}}],["remov",{"_index":20,"t":{"26":{"position":[[0,8]]}}}],["renaiss",{"_index":53,"t":{"86":{"position":[[11,11]]}}}],["renam",{"_index":77,"t":{"112":{"position":[[0,8]]},"699":{"position":[[13,8]]}}}],["replac",{"_index":213,"t":{"363":{"position":[[0,9]]},"447":{"position":[[0,9]]}}}],["repositori",{"_index":309,"t":{"679":{"position":[[15,10]]}}}],["request",{"_index":250,"t":{"469":{"position":[[0,10]]},"663":{"position":[[17,8]]},"665":{"position":[[24,7]]}}}],["research",{"_index":438,"t":{"966":{"position":[[0,8]]}}}],["reset",{"_index":311,"t":{"681":{"position":[[11,9]]}}}],["resourc",{"_index":422,"t":{"948":{"position":[[17,9]]}}}],["restor",{"_index":321,"t":{"701":{"position":[[0,9]]}}}],["result",{"_index":131,"t":{"206":{"position":[[29,7]]},"531":{"position":[[8,7]]}}}],["return",{"_index":274,"t":{"529":{"position":[[0,6]]},"535":{"position":[[0,9]]}}}],["rev",{"_index":243,"t":{"451":{"position":[[13,3]]}}}],["run",{"_index":107,"t":{"168":{"position":[[0,3]]},"427":{"position":[[0,7]]},"641":{"position":[[10,3]]},"869":{"position":[[0,7]]},"903":{"position":[[0,7]]}}}],["sampl",{"_index":400,"t":{"922":{"position":[[25,7]]},"924":{"position":[[13,7]]}}}],["save",{"_index":75,"t":{"110":{"position":[[0,6]]}}}],["scope",{"_index":271,"t":{"521":{"position":[[9,7]]}}}],["scp",{"_index":398,"t":{"907":{"position":[[24,3]]}}}],["scratch",{"_index":136,"t":{"218":{"position":[[0,10]]},"669":{"position":[[0,10]]}}}],["screen",{"_index":112,"t":{"176":{"position":[[10,6]]},"819":{"position":[[12,6]]}}}],["script",{"_index":178,"t":{"301":{"position":[[14,7]]},"413":{"position":[[16,7]]},"415":{"position":[[23,6]]},"419":{"position":[[18,6]]},"423":{"position":[[25,6]]},"427":{"position":[[16,6]]},"433":{"position":[[15,7]]},"437":{"position":[[16,6]]},"441":{"position":[[19,6]]},"541":{"position":[[32,7]]},"665":{"position":[[2,6]]},"719":{"position":[[8,6]]},"745":{"position":[[18,6]]},"793":{"position":[[28,10]]},"851":{"position":[[16,7]]},"875":{"position":[[23,7]]},"877":{"position":[[0,7]]},"940":{"position":[[6,9]]},"942":{"position":[[18,7]]},"996":{"position":[[0,6]]}}}],["search",{"_index":106,"t":{"164":{"position":[[0,9]]},"166":{"position":[[0,6]]},"168":{"position":[[27,6]]},"172":{"position":[[5,9]]},"190":{"position":[[0,9]]},"192":{"position":[[0,9]]},"194":{"position":[[0,9]]},"196":{"position":[[0,9]]},"198":{"position":[[10,8]]},"200":{"position":[[17,8]]},"206":{"position":[[22,6]]},"393":{"position":[[0,9]]},"773":{"position":[[0,6]]}}}],["section",{"_index":34,"t":{"46":{"position":[[7,8]]}}}],["sed",{"_index":212,"t":{"361":{"position":[[21,3]]}}}],["see",{"_index":1,"t":{"7":{"position":[[9,3]]},"178":{"position":[[0,3]]}}}],["select",{"_index":375,"t":{"867":{"position":[[6,8]]}}}],["semest",{"_index":426,"t":{"952":{"position":[[12,8]]}}}],["sequenc",{"_index":287,"t":{"591":{"position":[[25,9]]}}}],["server",{"_index":144,"t":{"228":{"position":[[22,6]]},"230":{"position":[[21,6]]}}}],["session",{"_index":362,"t":{"829":{"position":[[0,7]]},"831":{"position":[[29,8]]}}}],["set",{"_index":196,"t":{"343":{"position":[[29,4]]},"481":{"position":[[0,7]]},"893":{"position":[[0,7]]}}}],["share",{"_index":175,"t":{"291":{"position":[[0,5]]},"661":{"position":[[0,7]]}}}],["shebang",{"_index":235,"t":{"429":{"position":[[6,8]]},"431":{"position":[[0,8]]},"873":{"position":[[23,8]]},"877":{"position":[[16,8]]}}}],["sheet",{"_index":333,"t":{"761":{"position":[[21,5]]}}}],["shell",{"_index":39,"t":{"56":{"position":[[12,6]]},"58":{"position":[[12,5]]},"66":{"position":[[16,5]]},"76":{"position":[[20,5]]},"86":{"position":[[30,6]]},"279":{"position":[[4,5]]},"283":{"position":[[12,5]]},"287":{"position":[[0,5],[29,6]]},"413":{"position":[[10,5]]},"415":{"position":[[17,5]]},"427":{"position":[[10,5]]},"433":{"position":[[9,5]]},"483":{"position":[[0,5]]},"501":{"position":[[12,5]]},"505":{"position":[[0,5]]},"615":{"position":[[27,5]]},"621":{"position":[[4,5]]},"625":{"position":[[7,5]]},"627":{"position":[[0,5]]},"629":{"position":[[19,6]]},"631":{"position":[[12,6]]},"633":{"position":[[16,6]]},"635":{"position":[[6,6]]},"637":{"position":[[0,5]]},"639":{"position":[[4,5]]},"641":{"position":[[4,5]]},"643":{"position":[[34,6]]},"645":{"position":[[6,6]]},"647":{"position":[[14,5]]},"719":{"position":[[2,5]]},"723":{"position":[[2,5]]},"733":{"position":[[12,5]]},"737":{"position":[[11,5]]},"739":{"position":[[12,5]]},"741":{"position":[[13,5]]},"793":{"position":[[22,5]]},"797":{"position":[[13,5]]},"851":{"position":[[10,5]]},"875":{"position":[[17,5]]},"930":{"position":[[6,6]]},"934":{"position":[[18,5]]},"936":{"position":[[8,5]]},"940":{"position":[[0,5]]},"942":{"position":[[12,5]]},"1018":{"position":[[8,5]]},"1020":{"position":[[0,5]]}}}],["shift",{"_index":273,"t":{"527":{"position":[[10,8]]}}}],["shortcut",{"_index":344,"t":{"787":{"position":[[8,9]]}}}],["shouldn't",{"_index":149,"t":{"236":{"position":[[8,9]]}}}],["show",{"_index":307,"t":{"667":{"position":[[0,7]]},"867":{"position":[[18,4]]}}}],["simpl",{"_index":232,"t":{"419":{"position":[[11,6]]}}}],["singl",{"_index":262,"t":{"495":{"position":[[0,6]]},"497":{"position":[[0,6]]},"553":{"position":[[31,6]]}}}],["skill",{"_index":404,"t":{"936":{"position":[[14,6]]}}}],["solut",{"_index":141,"t":{"228":{"position":[[0,8]]},"230":{"position":[[0,8]]}}}],["sort",{"_index":22,"t":{"28":{"position":[[0,7]]},"453":{"position":[[0,4]]}}}],["sourc",{"_index":237,"t":{"433":{"position":[[0,8]]},"435":{"position":[[4,8]]},"741":{"position":[[0,8]]},"743":{"position":[[0,8]]}}}],["space",{"_index":170,"t":{"277":{"position":[[5,5]]}}}],["special",{"_index":87,"t":{"136":{"position":[[4,7]]},"463":{"position":[[21,7]]},"713":{"position":[[0,7]]}}}],["speed",{"_index":415,"t":{"944":{"position":[[32,5]]}}}],["split",{"_index":252,"t":{"471":{"position":[[0,9]]},"605":{"position":[[5,9]]},"607":{"position":[[5,9]]},"1032":{"position":[[5,9]]}}}],["ssh",{"_index":381,"t":{"883":{"position":[[8,4]]},"897":{"position":[[6,3]]},"901":{"position":[[12,3]]},"903":{"position":[[8,3]]}}}],["standard",{"_index":154,"t":{"248":{"position":[[0,8]]},"252":{"position":[[18,8]]},"254":{"position":[[18,8]]},"256":{"position":[[18,8]]}}}],["start",{"_index":143,"t":{"228":{"position":[[12,5]]},"269":{"position":[[10,8]]},"339":{"position":[[19,5]]}}}],["startup",{"_index":296,"t":{"627":{"position":[[6,7]]},"637":{"position":[[6,7]]},"643":{"position":[[0,7]]}}}],["statement",{"_index":280,"t":{"549":{"position":[[7,9]]},"553":{"position":[[15,10]]},"555":{"position":[[9,9]]},"557":{"position":[[9,9]]},"571":{"position":[[5,10]]}}}],["statu",{"_index":277,"t":{"535":{"position":[[10,6]]},"541":{"position":[[15,6]]}}}],["stdout",{"_index":276,"t":{"531":{"position":[[19,6]]}}}],["step",{"_index":339,"t":{"779":{"position":[[5,5]]},"909":{"position":[[5,5]]}}}],["stop",{"_index":110,"t":{"172":{"position":[[0,4]]}}}],["store",{"_index":256,"t":{"485":{"position":[[0,7]]}}}],["strategi",{"_index":320,"t":{"697":{"position":[[12,10]]}}}],["stream",{"_index":209,"t":{"359":{"position":[[16,6]]}}}],["string",{"_index":324,"t":{"711":{"position":[[11,6]]}}}],["strip",{"_index":215,"t":{"367":{"position":[[0,9]]}}}],["structur",{"_index":353,"t":{"801":{"position":[[18,9]]}}}],["style",{"_index":285,"t":{"589":{"position":[[14,5]]}}}],["subshel",{"_index":377,"t":{"869":{"position":[[20,9]]}}}],["substitut",{"_index":458,"t":{"1028":{"position":[[8,12]]}}}],["summari",{"_index":9,"t":{"12":{"position":[[0,8]]},"14":{"position":[[0,8]]},"32":{"position":[[0,7]]},"52":{"position":[[0,7]]},"82":{"position":[[0,7]]},"124":{"position":[[0,7]]},"144":{"position":[[0,7]]},"220":{"position":[[0,7]]},"261":{"position":[[0,7]]},"329":{"position":[[0,7]]},"355":{"position":[[0,7]]},"385":{"position":[[0,7]]},"409":{"position":[[0,7]]},"439":{"position":[[0,7]]},"457":{"position":[[0,7]]},"473":{"position":[[0,7]]},"513":{"position":[[0,7]]},"545":{"position":[[0,7]]},"575":{"position":[[0,7]]},"613":{"position":[[0,7]]},"649":{"position":[[0,7]]},"673":{"position":[[0,7]]},"703":{"position":[[0,7]]},"725":{"position":[[0,7]]},"747":{"position":[[0,7]]},"789":{"position":[[0,7]]},"811":{"position":[[0,7]]},"845":{"position":[[0,7]]},"879":{"position":[[0,7]]},"911":{"position":[[0,7]]},"1036":{"position":[[0,7]]}}}],["surfac",{"_index":137,"t":{"218":{"position":[[15,7]]},"669":{"position":[[15,7]]}}}],["surround",{"_index":220,"t":{"375":{"position":[[11,11]]}}}],["symlink",{"_index":135,"t":{"216":{"position":[[9,8]]}}}],["symmetr",{"_index":385,"t":{"887":{"position":[[0,9]]}}}],["system",{"_index":3,"t":{"7":{"position":[[19,6]]},"273":{"position":[[14,6]]},"863":{"position":[[23,6]]},"958":{"position":[[19,6]]}}}],["t",{"_index":161,"t":{"258":{"position":[[21,1]]}}}],["tag",{"_index":454,"t":{"1006":{"position":[[7,4]]}}}],["tail",{"_index":241,"t":{"445":{"position":[[10,5]]}}}],["takeaway",{"_index":150,"t":{"238":{"position":[[13,9]]},"309":{"position":[[8,9]]}}}],["taylor",{"_index":412,"t":{"942":{"position":[[33,6]]}}}],["tech",{"_index":12,"t":{"14":{"position":[[13,4]]}}}],["technic",{"_index":10,"t":{"12":{"position":[[9,9]]},"265":{"position":[[25,9]]},"267":{"position":[[21,9]]}}}],["techniqu",{"_index":360,"t":{"823":{"position":[[5,10]]}}}],["technolog",{"_index":55,"t":{"88":{"position":[[13,10]]}}}],["templat",{"_index":223,"t":{"377":{"position":[[11,8]]}}}],["termin",{"_index":171,"t":{"281":{"position":[[4,8]]},"751":{"position":[[10,8]]},"753":{"position":[[10,8]]},"815":{"position":[[10,8]]}}}],["test",{"_index":233,"t":{"423":{"position":[[13,7]]},"551":{"position":[[4,4]]},"559":{"position":[[7,4]]},"561":{"position":[[7,4]]},"563":{"position":[[10,5]]},"739":{"position":[[0,7]]}}}],["text",{"_index":23,"t":{"28":{"position":[[8,4]]},"30":{"position":[[13,4]]},"120":{"position":[[11,4]]},"363":{"position":[[10,4]]},"369":{"position":[[10,4]]},"371":{"position":[[11,4]]},"375":{"position":[[32,4]]},"393":{"position":[[18,4]]},"447":{"position":[[10,4]]},"715":{"position":[[24,4]]},"769":{"position":[[4,4]]},"944":{"position":[[20,4]]}}}],["that'",{"_index":6,"t":{"9":{"position":[[18,6]]},"74":{"position":[[0,6]]},"289":{"position":[[0,6]]}}}],["those",{"_index":2,"t":{"7":{"position":[[13,5]]}}}],["thought",{"_index":416,"t":{"944":{"position":[[41,8]]}}}],["through",{"_index":226,"t":{"393":{"position":[[10,7]]},"595":{"position":[[22,7]]}}}],["tild",{"_index":457,"t":{"1024":{"position":[[0,5]]}}}],["tip",{"_index":117,"t":{"180":{"position":[[4,4]]},"182":{"position":[[4,4]]},"503":{"position":[[8,4]]},"990":{"position":[[0,4]]}}}],["titl",{"_index":455,"t":{"1008":{"position":[[0,6]]}}}],["tl;dr",{"_index":36,"t":{"48":{"position":[[12,5]]}}}],["tmux",{"_index":359,"t":{"819":{"position":[[23,4]]},"821":{"position":[[11,4]]},"823":{"position":[[0,4]]},"839":{"position":[[19,4]]},"843":{"position":[[19,4]]}}}],["todo",{"_index":8,"t":{"11":{"position":[[0,4]]},"968":{"position":[[0,4]]},"972":{"position":[[0,4]]}}}],["tool",{"_index":348,"t":{"797":{"position":[[28,5]]},"799":{"position":[[28,4]]},"807":{"position":[[22,4]]}}}],["topic",{"_index":204,"t":{"351":{"position":[[18,6]]}}}],["trace",{"_index":247,"t":{"463":{"position":[[44,7]]}}}],["transfer",{"_index":397,"t":{"907":{"position":[[0,12]]}}}],["transform",{"_index":211,"t":{"361":{"position":[[0,15]]}}}],["transpos",{"_index":119,"t":{"182":{"position":[[9,12]]}}}],["trap",{"_index":374,"t":{"857":{"position":[[0,5]]}}}],["tree",{"_index":322,"t":{"701":{"position":[[23,4]]}}}],["trick",{"_index":160,"t":{"258":{"position":[[9,5]]},"451":{"position":[[2,5]]}}}],["tti",{"_index":7,"t":{"9":{"position":[[25,4]]}}}],["type",{"_index":177,"t":{"297":{"position":[[14,5]]},"327":{"position":[[0,4]]},"629":{"position":[[10,5]]}}}],["understand",{"_index":28,"t":{"38":{"position":[[0,13]]},"134":{"position":[[0,13]]}}}],["undo",{"_index":101,"t":{"156":{"position":[[17,4]]}}}],["uniqu",{"_index":244,"t":{"453":{"position":[[9,6]]}}}],["unix",{"_index":46,"t":{"64":{"position":[[8,4]]},"958":{"position":[[4,4]]}}}],["unset",{"_index":373,"t":{"855":{"position":[[0,9]]}}}],["until",{"_index":289,"t":{"599":{"position":[[4,5]]}}}],["up",{"_index":82,"t":{"122":{"position":[[8,2]]},"234":{"position":[[9,2]]},"471":{"position":[[10,2]]},"893":{"position":[[8,2]]}}}],["updat",{"_index":270,"t":{"511":{"position":[[0,8]]},"543":{"position":[[0,8]]},"573":{"position":[[0,8]]},"611":{"position":[[0,8]]}}}],["us",{"_index":30,"t":{"42":{"position":[[0,5]]},"236":{"position":[[18,3]]},"395":{"position":[[0,5]]},"429":{"position":[[0,5]]},"553":{"position":[[0,5]]},"567":{"position":[[0,5]]},"753":{"position":[[4,3]]},"817":{"position":[[4,3]]},"867":{"position":[[0,5]]},"897":{"position":[[0,5]]}}}],["user",{"_index":169,"t":{"277":{"position":[[0,4]]}}}],["v",{"_index":228,"t":{"403":{"position":[[0,1]]}}}],["valu",{"_index":261,"t":{"493":{"position":[[22,6]]},"495":{"position":[[24,6]]},"529":{"position":[[7,6]]},"855":{"position":[[10,6]]}}}],["variabl",{"_index":254,"t":{"479":{"position":[[0,9]]},"481":{"position":[[8,9]]},"483":{"position":[[6,9],[32,9]]},"485":{"position":[[39,8]]},"487":{"position":[[20,8]]},"493":{"position":[[8,9]]},"519":{"position":[[0,9]]},"521":{"position":[[0,8]]},"525":{"position":[[10,9]]},"609":{"position":[[8,8]]},"853":{"position":[[22,9]]}}}],["version",{"_index":352,"t":{"801":{"position":[[0,7]]},"803":{"position":[[0,7]]},"805":{"position":[[0,7]]}}}],["video",{"_index":434,"t":{"957":{"position":[[6,6]]}}}],["vim",{"_index":330,"t":{"755":{"position":[[12,3]]},"761":{"position":[[11,3]]},"763":{"position":[[19,3]]},"765":{"position":[[0,3]]},"767":{"position":[[0,3]]},"769":{"position":[[0,3]]},"771":{"position":[[0,3]]},"777":{"position":[[20,3]]},"779":{"position":[[16,3]]},"785":{"position":[[10,3],[23,3]]},"787":{"position":[[4,3]]},"944":{"position":[[10,4]]}}}],["vimcast",{"_index":341,"t":{"783":{"position":[[0,8]]}}}],["vimtutor",{"_index":340,"t":{"781":{"position":[[0,8]]}}}],["virtual",{"_index":391,"t":{"895":{"position":[[11,7]]},"897":{"position":[[26,7]]}}}],["wallpap",{"_index":345,"t":{"787":{"position":[[18,9]]}}}],["warn",{"_index":208,"t":{"353":{"position":[[10,7]]}}}],["weird",{"_index":127,"t":{"204":{"position":[[8,5]]}}}],["whati",{"_index":185,"t":{"315":{"position":[[0,6]]}}}],["whenc",{"_index":186,"t":{"319":{"position":[[0,6]]}}}],["wherei",{"_index":187,"t":{"323":{"position":[[0,7]]}}}],["whitespac",{"_index":246,"t":{"463":{"position":[[9,11]]}}}],["wick",{"_index":409,"t":{"942":{"position":[[0,6]]}}}],["wildcard",{"_index":80,"t":{"116":{"position":[[38,9]]},"585":{"position":[[23,9]]}}}],["window",{"_index":42,"t":{"60":{"position":[[10,7]]},"68":{"position":[[10,7]]},"827":{"position":[[0,6]]}}}],["without",{"_index":380,"t":{"877":{"position":[[8,7]]}}}],["word",{"_index":100,"t":{"154":{"position":[[30,4]]},"156":{"position":[[9,4]]},"158":{"position":[[16,4]]},"353":{"position":[[2,4]]},"583":{"position":[[12,5]]},"605":{"position":[[0,4]]},"607":{"position":[[0,4]]},"1032":{"position":[[0,4]]}}}],["work",{"_index":83,"t":{"128":{"position":[[4,7]]},"130":{"position":[[28,7]]},"140":{"position":[[24,7]]},"401":{"position":[[0,7]]},"441":{"position":[[26,5]]},"701":{"position":[[15,7]]}}}],["wrap",{"_index":174,"t":{"289":{"position":[[9,5]]}}}],["write",{"_index":275,"t":{"531":{"position":[[0,7]]},"799":{"position":[[0,7]]}}}],["xarg",{"_index":245,"t":{"461":{"position":[[12,5]]}}}],["z",{"_index":294,"t":{"615":{"position":[[25,1]]},"723":{"position":[[0,1]]}}}],["zip",{"_index":70,"t":{"102":{"position":[[15,3]]},"122":{"position":[[0,7]]}}}],["zsh",{"_index":329,"t":{"723":{"position":[[18,3]]}}}]],"pipeline":["stemmer"]}},{"documents":[{"i":4,"t":"Thanks Many people have contributed their time and support to help build this book. Thank-you to everyone who has helped on this journey! Xiaoyou Elsie JiangπŸ“– πŸ‘€ Tobias BΓΌschelπŸ‘€ Doug FooπŸ“– πŸ‘€ Sallah KokainaπŸ“– πŸ‘€ samhinton88πŸ“– πŸ‘€ Alex VinallπŸ“– πŸ‘€ Joseph KnightπŸ“– πŸ‘€ Doug ToddπŸ“– πŸ‘€ jdhzzzπŸ“– πŸ‘€ valankarπŸ“– πŸ‘€ DenpeerπŸ‘€ πŸ“– Marek BogatzkiπŸ“– πŸ‘€ MWarneckeπŸ“– πŸ‘€ πŸ› SpikeπŸ‘€ πŸ› Dong ZhouπŸ‘€ πŸ› πŸ“– Dror MamanπŸ› πŸ“– πŸ‘€ Michael ChuiπŸ‘€ Saroj SangphongamphaiπŸ‘€ Lee LiπŸ‘€ πŸ› πŸ“– Lee LiπŸ› Trevor BrownπŸ› πŸ‘€ Philipp FrischmuthπŸ‘€","s":"Thanks","u":"/appendices/thanks/","h":"","p":3},{"i":6,"t":"On this page","s":"hack-on","u":"/core-skills/what-is-a-shell/hack-on","h":"","p":5},{"i":8,"t":"I mentioned earlier on that if you make a call like fopen, the Kernel is going to provide access to a file. It's quite easy to see this in action. Check the code below: #include void main(){ void* handle = fopen(\"/tmp/some-file\"); fwrite(handle, \"some text\"); fclose(handle);} If you compile this program, then run XXX you will see the actual calls made to the Kernel. It can be very useful to use this technique to see what is going on with programs under the hood, particularly when diagnosing issues.","s":"Hack On! See those system calls!","u":"/core-skills/what-is-a-shell/hack-on","h":"#hack-on-see-those-system-calls","p":5},{"i":10,"t":"Consider a command like: docker -it Consider a command like: ssh user@remote.com my-scriptno tty Once you understand the concept of shells and terminals in a bit more detail, more obscure messages like this start to make sense. In the first instance, we are telling Docker we are interactive - i.e. we are going to use an interface to send commands. The second parameter, -t says use a TTY - which is short for teletype terminal, old fashioned lingo for the screen. TODO TTY picture","s":"Hack On! Ahah! So that's TTY?","u":"/core-skills/what-is-a-shell/hack-on","h":"#hack-on-ahah-so-thats-tty","p":5},{"i":13,"t":"For technical readers, there's quite a lot of terms which get thrown about almost interchangeably; shell, command-line, terminal, tty, command-prompt, CLI and so on. But each of these have a very specific meaning. It's important to understand exactly what each of these terms really means, where they came from, and how these different types of system or concept relate to each other.","s":"Summary; technical","u":"/core-skills/what-is-a-shell/hack-on","h":"#summary-technical","p":5},{"i":15,"t":"A computer in a nutshell. -- Could link to other layers of abstractions - such as sandboxes in webpages? -- Could link to other layers of abstraction, such as containers?","s":"Summary; non-tech","u":"/core-skills/what-is-a-shell/hack-on","h":"#summary-non-tech","p":5},{"i":17,"t":"Part 1 - Transitioning to the Shell 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.","s":"Part 1 - Transitioning to the Shell","u":"/part-1-transitioning-to-the-shell/","h":"","p":16},{"i":19,"t":"On this page","s":"Becoming a Clipboard Gymnast","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"","p":18},{"i":21,"t":"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: Command Windows Shortcut Linux Shortcut MacOS Shortcut Cut Ctrl + X Ctrl + X ⌘ + X Copy Ctrl + C Ctrl + C ⌘ + C Paste Ctrl + V Ctrl + V ⌘ + V 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 Ctrl + V a few times to paste into terminal on Ubuntu: Instead of the contents of the clipboard being dropped into the shell, we see the text ^V. Why is this? 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 Ctrl key is called the Control Key is that it is used to send 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, Ctrl + P is almost universally used as a shortcut for the 'Print' command. We tend to think of these commands as 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. 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! 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 Ctrl + C. This actually sends a signal to the program and typically the program responds by closing. We'll see signals again and again as we go through the book. The Ctrl + C combination terminates the current program. What about Ctrl + V? This is the grand-sounding \"Verbatim Insert\" command. It tells the shell to write out the next keystroke you give it. This allows you to write out 'special' characters like the escape key, left or right keys, or even the Ctrl + V combination itself. So if you type Ctrl + V twice, the shell writes out the text ^V. The hat symbol ^ represents 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 Alt key and other special keys. So why do we need to care? Well the shell already has a command for Ctrl + C and Ctrl + V, so we're going to need to work around this to use our familiar 'copy' and 'paste' commands. How this works varies across platforms. Follow the instructions below for the platform you are using. Windows If you are using a 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 not work. Instead, select the Use Ctrl+Shift+C/V as Copy/Paste option from the properties menu: You can now use Ctrl+Shift+C for copy and Ctrl+Shift+V for paste. You can also copy text by just dragging the cursor over it with the right mouse button. Linux On most Linux systems you'll be using the Gnome terminal, which means that you can use Ctrl+Shift+C for copy and Ctrl+Shift+V for paste. You can also right click on text with the cursor to select it. MacOS Mac users can just use ⌘ + C for copy and ⌘ + V for paste. The shell doesn't use the special Mac Command character ⌘, which means the default keyboard mappings on MacOS work fine in a shell as they do not clash with anything. 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.","s":"The Clipboard Essentials","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#the-clipboard-essentials","p":18},{"i":23,"t":"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. 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. To make things easier for the reader I'm going to assume you have created the pbcopy and 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! Windows Assuming you are using WSL, you will need to run the following two commands. By the time this book is published there may be a cleaner way, but for now this is a workaround for some limitations on the WSL system: alias pbcopy='clip.exe'alias pbpaste=\"powershell.exe -command 'Get-Clipboard' | tr -d '\\r' | head -n -1\" 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 - pbcopy and pbpaste, which will work in Bash on Windows. Linux Hopefully if you are Linux user the commands below will seem familiar. They install the xclip program and create shortcuts to copy and paste. You absolutely don't need to do this if you prefer to call xclip directly, these commands are just setup so that across all platforms the tutorial looks the same. sudo apt install -y xclipalias pbcopy=\"xclip -selection c\"alias pbpaste=\"xclip -selection c -o\" MacOS Nothing is required on MacOS - pbcopy and pbpaste are built in. Making these changes permanent We've used the alias command to create pbcopy and pbpaste. In Bash (and most shells) an 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. 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. 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!","s":"Preparing the Clipboard Commands","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#preparing-the-clipboard-commands","p":18},{"i":25,"t":"Now that we've created these commands, we can use them to access the clipboard. For example, if I copy the following text: Kirk Van HoutenTimothy LovejoyArtie Ziff Then I can paste it into the shell with the following command: pbpaste And we'll see something like this: 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 effective-shell/text/simpsons-characters.txt. Now we could use the cat command to show the contents of the file, and then manually select the text and copy it. Even easier though is to just pipe the contents of the file to the pbcopy command: cat ~/effective-shell/text/simpsons-characters.txt | pbcopy The output will look similar to the below (I've included the output of cat for reference as well): The vertical bar | is the pipe operator. It tells the shell to take the output from the command on the left and send it straight to the input of the program on the right. We're going to see a lot more of the pipeline operator as we continue. For now it's enough to know you can use it to 'chain' commands together. This might not seem super useful so far - but if the text file was a lot larger then it would be much harder to 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. Now we can see some real world examples of how these commands can be useful in daily tasks!","s":"Copy and Paste Basics","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#copy-and-paste-basics","p":18},{"i":27,"t":"Don't you hate it when you have to copy formatted text and don't have an easy way to paste it as unformatted text? Here's an example, I want to copy this Wikipedia page on 'bash', and paste it into a Word document: 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 into a plain text editor just to copy out the plain text. If you just run the command pbpaste | pbcopy, you can easily strip the formatting: 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 back into the clipboard, replacing the formatted text which was there before. 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.","s":"Removing Formatting","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#removing-formatting","p":18},{"i":29,"t":"Because we can 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. Let's take another look at the list of characters we have in the ~/plaground/text/simpsons-characters.txt file: $ cat ~/effective-shell/text/simpsons-characters.txtArtie ZiffKirk Van HoutenTimothy LovejoyArtie ZiffNick RivieraSeymore SkinnerHank ScorpioTimothy LovejoyJohn FrinkCletus SpucklerRuth PowersArtie ZiffAgnes SkinnerHelen Lovejoy We can easily take this text, sort it and then directly copy the results: $ cat ~/effective-shell/text/simpsons-characters.txt | sort | pbcopy The contents of the clipboard will now contain: Agnes SkinnerArtie ZiffArtie ZiffArtie ZiffCletus SpucklerHank ScorpioHelen LovejoyJohn FrinkKirk Van HoutenNick RivieraRuth PowersSeymore SkinnerTimothy LovejoyTimothy Lovejoy The 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.","s":"Sorting Text","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#sorting-text","p":18},{"i":31,"t":"Let's say someone has emailed me a list of people I need to invite to an event: The problem is: The list is in Excel and is formatted The list has duplicates I need to turn each name into an email address like 'Artie_Ziff@simpsons.com' 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. 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): Artie ZiffKirk Van HoutenTimothy LovejoyArtie ZiffNick RivieraSeymore SkinnerHank ScorpioTimothy LovejoyJohn FrinkCletus SpucklerRuth PowersArtie ZiffAgnes SkinnerHelen Lovejoy First, we copy the text to the clipboard. Now we can paste and sort: $ pbpaste | sortAgnes SkinnerArtie ZiffArtie ZiffArtie ZiffCletus SpucklerHank ScorpioHelen LovejoyJohn FrinkKirk Van HoutenNick RivieraRuth PowersSeymore SkinnerTimothy LovejoyTimothy Lovejoy Then remove the duplicates: $ pbpaste | sort | uniqAgnes SkinnerArtie ZiffCletus SpucklerHank ScorpioHelen LovejoyJohn FrinkKirk Van HoutenNick RivieraRuth PowersSeymore SkinnerTimothy Lovejoy Replace the space with an underscore: $ pbpaste | sort | uniq | tr \" \" \"_\"Agnes_SkinnerArtie_ZiffCletus_SpucklerHank_ScorpioHelen_LovejoyJohn_FrinkKirk_Van_HoutenNick_RivieraRuth_PowersSeymore_SkinnerTimothy_Lovejoy Then add the final part of the email address: $ pbpaste | sort | uniq | tr \" \" \"_\" | sed 's/$/@simpsons.com/'Agnes_Skinner@simpsons.comArtie_Ziff@simpsons.comCletus_Spuckler@simpsons.comHank_Scorpio@simpsons.comHelen_Lovejoy@simpsons.comJohn_Frink@simpsons.comKirk_Van_Houten@simpsons.comNick_Riviera@simpsons.comRuth_Powers@simpsons.comSeymore_Skinner@simpsons.comTimothy_Lovejoy@simpsons.com This looks perfect! We can now put the transformed text back onto the clipboard: $ pbpaste | sort | uniq | tr ' ' '_' | sed 's/$/@simpsons.com/' | pbcopy All in all we have the following pipeline: pbpaste - output the clipboard sort - order the output uniq - deduplicate the rows tr ' ' '_' - replace spaces with underscores sed /$/@simpsons.com - add the email domain to the end of the row 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. In fact - if you are on a Linux system, try running: cat /dev/clipboard 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. We're also going to spend a lot of time later on looking at pipelines in detail, so don't worry too much if this seems overwhelming at this stage! 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.","s":"Manipulating Text","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#manipulating-text","p":18},{"i":33,"t":"In this chapter we learnt: 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. Different operating systems access the clipboard in different ways, but we can work around this by creating an alias command (which we'll see in detail later) We can use pbcopy to copy and pbpaste to paste. We can 'chain' commands together with the | (pipe) operator. We can turn formatted text on the clipboard into plain text by just running pbpaste | pbcopy. We can sort lines of text with the sort command. There is clearly a lot more we can do with text as we save hints of with the uniq, tr and sed commands - which we'll introduce in detail later. You can treat the clipboard a bit like a file in the shell. On Linux, lots of things can be represented as files - including the clipboard (which is accessed via the /dev/clipboard file).","s":"Summary","u":"/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/","h":"#summary","p":18},{"i":35,"t":"On this page","s":"Getting Help","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"","p":34},{"i":37,"t":"If you are trying to be more effective when using the shell, it is crucial to know how to quickly look things up. There'll be many circumstances where you'll need to open a browser to search for help. But there's also a wealth of information only a few keystrokes away. Looking up parameters, checking how to run commands, C library documentation, or even useful information like ASCII charts are available directly in the shell. Being able to access this information quickly, without jumping into a browser or interrupting your flow is going to be one of the most crucial things you can do to become an effective shell user. First we're going to look at the standard help system which is available on all Unix-like systems, which is man (short for 'manual'). Then we'll see a useful tool you can installed called tldr, which might be more helpful for day-to-day use. Finally we'll take a look at the cht.sh site as an alternative source for help.","s":"Getting Help is Important!","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#getting-help-is-important","p":34},{"i":39,"t":"Most tools you encounter in the shell have manual pages available. Many people will be familiar with the man command to get help on a tool, but there is a lot more help available than people often realise.","s":"Understanding 'man'","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#understanding-man","p":34},{"i":41,"t":"The most basic way to get help on a command is with man. Here's an example: $ man cpCP(1) BSD General Commands Manual CP(1)NAME cp -- copy filesSYNOPSIS cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file target_file cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file ... target_directoryDESCRIPTION In the first synopsis form, the cp utility copies the contents of the source_file to the target_file. In the second synopsis form, the con- tents of each named source_file is copied to the destination target_directory. The names of the files themselves are not changed. If cp detects an attempt to copy a file to itself, the copy will fail.... The man command opens the manual for the given tool. These manuals should contain all command line options and details of how to use the tool. You can scroll up and down through the content with the arrow keys. This scrolling capability actually is not part of man - it is available because the information is presented in the shell pager. A pager is a tool for looking through content which might not easily fit on a screen.","s":"Getting help on a command","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#getting-help-on-a-command","p":34},{"i":43,"t":"The first thing you might notice is that you can move through the manual pages with the arrow keys. The man command finds the appropriate manual page (often shortened to 'manpages') and then opens the page in a pager tool. The pager is what is providing the keyboard interface to look through the file. On most systems, the pager will be the less program. There are lots of commands you can use to navigate through files with less, but the bare essentials are: d - Scroll down half a page u - Scroll up half a page j / k - Scroll down or up a line. You can also use the arrow keys for this q - Quit / - Search for text n - When searching, find the next occurrence N - When searching, find the previous occurrence There are many other commands, but the set above is normally what I find myself using the most. If you are interested, you can actually see what your pager is with the command below: $ echo $PAGERless The $PAGER environment variable is used to tell the shell what program to use for paging. A few more details can be found with the man man command. You can put any text content into your pager - try this: ls -al /usr/bin | less This lists the contents of the /usr/bin folder, piping the output to less so we can easily scroll through it. There are alternative pagers available (on many Unix-y systems you'll have less, more and most) but in general you'll normally get what you need with less.","s":"Using the pager","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#using-the-pager","p":34},{"i":45,"t":"Sometimes you'll look something up in the manual and get the 'builtins' page. For example: $ man cdBUILTIN(1) BSD General Commands Manual BUILTIN(1)NAME builtin, !, %, ., :, @, {, }, alias, alloc, bg, bind, bindkey, break, breaksw, builtins, case, cd, chdir, command, complete, continue,## (I've skipped the bulk of the output to save space!) This happens when the command you are looking up is not actually a program with a manual page, but a built-in shell command. Most shells have a way get help on such commands - bash for example has help: $ help cdcd: cd [-L|[-P [-e]] [-@]] [dir] Change the shell working directory. Change the current directory to DIR. The default DIR is the value of the HOME shell variable.# (I've skipped the bulk of the output to save space!) This is all I'll say about help for now. We visit it again in Chapter 10 - Understanding Commands, where we talk more about built-in commands. For now we'll go back to the man command, which works across all shells as it is a Linux feature rather than a shell specific feature!","s":"The Alternative - Help","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#the-alternative---help","p":34},{"i":47,"t":"You'll often see tools referred to in manpages with numbers after them. Take a look at man less: The number is the manual Section Number. The different sections of the manual are documented and can be found on most Unix-like systems in man's documentation, which you can check by running man man1. Here's what you'd get on Ubuntu 16: Section 1 - Executable programs or shell commands Section 2 - System calls (functions provided by the kernel) Section 3 - Library calls (functions within program libraries) Section 4 - Special files (usually found in /dev) Section 5 - File formats and conventions (e.g. /etc/passwd) Section 6 - Games Section 7 - Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) Section 8 - System administration commands (usually only for root) Section 9 - Kernel routines (Non standard) Not all of these explanations will be entirely clear to everyone, so we'll go through the sections in detail shortly. If you want to, you can specifically choose which section of the manual you are looking in by using: man
You can also get more information about the sections themselves by opening up the intro page. For example: $ man 1 introINTRO(1) BSD General Commands Manual INTRO(1)NAME intro -- introduction to general commands (tools and utilities)DESCRIPTION Section one of the manual contains most of the commands which comprise... Why would you do this, and why would you care? In general you won't need to worry about the sections unless you are looking for something which has an entry in multiple sections and you want to specify which one you use. Another reason it is useful to know about the sections is that a lot of documentation (online and offline) includes a section number after the name of a command or file. Knowing what the section is can be useful in this case. Here are a few examples of entries from each section, which illustrate what each section is for. Section 1: Programs and Shell Commands​ These are programs - probably what you are going to be looking up most regularly! For example, man 1 time shows: TIME(1) BSD General Commands Manual TIME(1)NAME time -- time command executionSYNOPSIS time [-lp] utilityDESCRIPTION The time utility executes and times utility. After the utility finishes, time writes the total time elapsed, the time consumed by system overhead, and the time used to execute utility to the standard error stream. Times are reported in seconds.... Section 2: System Calls​ You'll probably not use this section unless you are doing systems programming2. This section contains info on the available Linux Kernel system calls. For example, running man 2 chown gives: CHOWN(2) BSD System Calls Manual CHOWN(2)NAME chown, fchown, lchown, fchownat -- change owner and group of a fileSYNOPSIS #include int chown(const char *path, uid_t owner, gid_t group);... This entry shows you how you would call the function if you were programming for the Kernel. Section 3: Library Calls​ These are the manpages for the C standard library functions. For example, man 3 time: TIME(3) BSD Library Functions Manual TIME(3)NAME time -- get time of dayLIBRARY Standard C Library (libc, -lc)SYNOPSIS #include time_t time(time_t *tloc);... You would use this information if you were writing programs to run on the system. Here we can see why the sections are important to know about. There are multiple entries for time. We need to use the sections to differentiate between them. Running man time would not open the page above, because man searches the library in ascending section order, meaning that it actually finds time(1) and shows the pages for the time program, not the time C library call. Because of the potential ambiguity of names if no section number is included, in lots of Linux documentation you'll see the man section number written next to library calls, system calls, programs and so on (things will refer to sed(1) or time(3) for example. Section 4: Devices​ This section deals with the special devices which live in the /dev/* folder. For example, running man 4 random shows: RANDOM(4) BSD Kernel Interfaces Manual RANDOM(4)NAME random , urandom -- random data source devices.SYNOPSIS pseudo-device randomDESCRIPTION The random device produces uniformly distributed random byte values of potentially high quality.... Again, we see that section numbers can be important. If you just run man random, you'll see: RANDOM(3) BSD Library Functions Manual RANDOM(3)NAME initstate, random, setstate, srandom, srandomdev -- better random num- ber generator; routines for changing generatorsLIBRARY Standard C Library (libc, -lc)SYNOPSIS #include char * initstate(unsigned seed, char *state, size_t size); long random(void);... Which is the manpage for random(3), which is C library function, not the /dev/random file! We'll see more of these special files later in the book. Section 5: File Formats​ This section details special files in the system. For example, man 5 crontab shows: CRONTAB(5) BSD File Formats Manual CRONTAB(5)NAME crontab -- tables for driving cronDESCRIPTION A crontab file contains instructions to the cron(8) daemon of the gen- eral form: ``run this command at this time on this date''. Each user has their own crontab, and commands in any given crontab will be exe- cuted as the user who owns the crontab. Uucp and News will usually have their own crontabs, eliminating the need for explicitly running su(1) as part of a cron command.... Which describes the crontab file used to define scheduled tasks. Again, this is different to man crontab which would document crontab(1). Similarly, man 5 passwd is going to show something quite different to man passwd. You'll potentially use this section if you are performing system administration. Section 6: Games​ Nothing says it better than man 6 intro itself (this'll not work on a Mac sadly, but try it on another Linux system): ...DESCRIPTION Section 6 of the manual describes all the games and funny little programs available on the system.... There are probably a few silly programs available on your system, here you'll find their manuals. For example, man 6 banner on a Mac shows: BANNER(6) BSD Games Manual BANNER(6)NAME banner -- print large banner on printerSYNOPSIS banner [-d] [-t] [-w width] message ...DESCRIPTION Banner prints a large, high quality banner on the standard output. If the message is omitted, it prompts for and reads one line of its stan- dard input.... This section is going to be highly dependent on your operating system! Section 7: Miscellaneous​ This is where you'll find additional assorted documentation. For example, man 7 ascii shows: ASCII(7) BSD Miscellaneous Information Manual ASCII(7)NAME ascii -- octal, hexadecimal and decimal ASCII character setsDESCRIPTION The octal set: 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel... Section 8: System Commands​ We've actually already seen one of these commands mentioned, in the manpage for crontab(5) it mentions cron(8). Let's see, with man 8 cron: CRON(8) BSD System Manager's Manual CRON(8)NAME cron -- daemon to execute scheduled commands (Vixie Cron)SYNOPSIS cron [-s] [-o] [-x debugflag[,...]] These are commands which system administrators would normally run. You might open section eight unexpectedly, for example man chmod will open chmod(1), but man chown will open chown(8), as it is a system command. Some distributions might vary for section nine. On my Mac it contains information about the kernel interfaces, a C style guide and some more. Getting the Index of Manual Section​ Manpages are just files on the filesystem, so you can get the index of a section just by looking in the appropriate folder. For example, to index the available system calls, try ls /usr/share/man/man2: EV_SET.2FD_CLR.2FD_COPY.2FD_ISSET.2FD_SET.2FD_ZERO.2_exit.2accept.2access.2acct.2... This is quick and easy way to see what sort of entries you have on your system. If you want to work out where an entry lives, use the -w flag: $ man -w printf/usr/share/man/man1/printf.1 There are other ways to show the index of each section, but they vary a lot from system to system so showing the actual files is probably easier. Searching the Manual​ You can search the manpage titles and summaries with man -k. For example, man -k cpu shows: cpuwalk.d(1m) - Measure which CPUs a process runs on. Uses DTracedispqlen.d(1m) - dispatcher queue length by CPU. Uses DTracegasm(n), grammar::me::cpu::gasm(n) - ME assembler You can find more advanced options for searching by using your newfound man skills on man itself. You can also use the apropos or whatis commands to search through the manuals. However, for simplicity I suggest just remember man -k!","s":"Manual Sections","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#manual-sections","p":34},{"i":49,"t":"In general for this book I'm trying to avoid suggesting too many non-standard tools which don't come pre-installed on systems. However, this one is just too good to miss! Let's say I need to find and replace some text in a file. I know I can do this with the sed command, but have forgotten the syntax. So I run man sed: Wow, that's a lot of detail! And this is just page one of six! Now let's compare this to the output from tldr (which is short for \"Too Long, Didn't Read\"). All I need to do is run tldr sed: The first example is exactly what I'm looking for. Now for any more detail than a few basic examples, I'm going to have to go to the manual, but for the basics this is great. You can install the tldr tool with npm install -g tldr. It's open source and community maintained. You will need Node.js installed to install the tool, the instructions are available online. I'd recommend tldr as a first-call for checking to see how to use a command.","s":"Introducing tl;dr","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#introducing-tldr","p":34},{"i":51,"t":"One final resource which I think is worth sharing is the website www.cheat.sh. This is a fantastic online collection of 'cheat sheets'. These sheets cover almost all of the tools you will encounter, programming languages and more. But the real beauty of the tool is how it integrates into the shell. To see what I mean, just run the following command: $ curl cht.sh You will see something like this: The curl command we'll see again and again. It is a tool which lets you download content from the web. If we load the cheat.sh website (or its shortened version, cht.sh) from the shell, we get a text version of the website. We can now look at all sorts of content by following the guide shown. The Cheat.sh site aggregates many data sources - including tldr! This means we can get information on tools without even having to install a tool like tldr locally. This online cheatsheet is a wonderful resource. As well as guides for specific tools, there are entire courses on programming languages. You can even use it to search for the answers to questions, these features are powered by Stack Overflow. For example: $ curl cht.sh/\"How do I copy a folder in bash?\" You'll see something like this: Now that can be a real time saver!","s":"The Online Cheatsheet","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#the-online-cheatsheet","p":34},{"i":53,"t":"In this chapter we looked at some of the ways we can get help. To quickly summarise: The man tool can be used to look at the manual page for a topic The man pages are grouped into sections, we can see them with man man The tldr tool shows a very short description of a tool, which covers the most common use cases only The cht.sh website can be used directly from the shell to get help on tools or even ask specific questions Weirdly satisfying to run.↩ Which it is always fun to try if you get the chance, and a great way to learn more about the fundamentals of the operating system.↩","s":"Summary","u":"/part-1-transitioning-to-the-shell/getting-help/","h":"#summary","p":34},{"i":55,"t":"On this page","s":"Getting Started","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"","p":54},{"i":57,"t":"If you don't know what the shell is, then this is the place to start! 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. Here's what the shell looks like on Windows: And here's what it looks like on a Mac: And here's what it looks like on Fedora, a popular Linux distribution: 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. Why would you want to do this? There are a few reasons! 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. Secondly, there are some scenarios where you have to use a shell. Not every program or system can be operated with a 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. Finally, there are some scenarios where it can be 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 shell scripts to automate these kinds of operations. 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.","s":"What is the Shell?","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#what-is-the-shell","p":54},{"i":59,"t":"Now let's actually learn how to open the shell on your computer. 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 other shells as possible - we'll get to that in the next chapter.","s":"Opening the Shell","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#opening-the-shell","p":54},{"i":61,"t":"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\". To open the command prompt, start by clicking the start button on the bottom left hand side of the screen, and type command prompt. Open the Command Prompt program: Once the program has opened, type whoami then hit the Return key. The whoami program will show the username of the logged in user: 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.","s":"Microsoft Windows","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#microsoft-windows","p":54},{"i":63,"t":"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 terminal. Open the terminal program which is shown: Once the program has opened, type whoami then hit the Return key. The whoami program will show the username of the logged in user: That's it! In the next section we'll make some minor configuration changes to keep things consistent with the samples in the book.","s":"MacOS","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#macos","p":54},{"i":65,"t":"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.","s":"Linux / Unix","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#linux--unix","p":54},{"i":67,"t":"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. 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 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. 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. Once this is done then we are ready to get into the book properly!","s":"Configuring the Shell","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#configuring-the-shell","p":54},{"i":69,"t":"Windows is not anything like Linux under the hood. So to get a shell working, we have three options: Use a tool which provides common Linux tools which have been written to work with Windows Use a \"virtual machine\" running Linux Use the Windows Subsystem for Linux 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. 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. 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. We'll go through all options here. Option 1: Install Linux Tools​ 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. To get a Linux-like experience on a Windows machine, we'll install Cygwin. Cygwin provides a large set of programs which are generally available on Linux systems, which are designed to work on Windows. Download the Cygwin installer and start the installation process. You should see something like this: Start the installation and tell it to install from the internet (the default option): Install for all users in the default location. It is also fine to change the options if you prefer: 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\": The installer will now start downloading and installing the programs: Once Cygwin has finished installing, you will have a link to open Cygwin available on the desktop and start menu. You can use this link to start using the \"Bash\" shell, or if you prefer you can open the \"Command Prompt\" as described in Opening the Shell and run the bash program: Note that you shouldn't use the --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. At this point you have a ready-to-go bash environment and can continue on to the Summary and Next Section. Option 2: Use a Virtual Machine​ 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. 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 Ubuntu distribution of Linux. 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): While the Ubuntu software downloads, we can install VirtualBox. Go to the VirtualBox Website and download the VirtualBox installer. You will need the installer for 'Windows Hosts'. Once the installer has downloaded, run it to start the installation: Next you will be asked to configure the installation options. The defaults will be fine for most users: Then the installation will start: Once the installation is complete and the Ubuntu installer has downloaded we can move onto the next step. 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: You will be asked to setup a virtual hard disk. I would recommend the default options for most users: Once the machine has been created it will be shown in the main VirtualBox window. Select the machine and choose \"Start\": 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 .iso: If this step fails, you may need to disable \"Hyper-V\" and \"Windows Sandbox\" by going to \"Add or Remove Windows features\": After a short while you will see the Ubuntu installer start up. Choose the \"Install Ubuntu\" option: 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: 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! 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. When you have logged in, press the applications icon on the bottom-left of the screen and search for the \"Terminal\" application: You are now running the \"Bash\" shell in the terminal. You can run the whoami command to show the current user, or bash --version to see the version of Bash which is installed: That's it! You now have a virtual machine running Ubuntu and Bash which you can use to learn about the shell. Option 3: Setup the Windows Subsystem for Linux​ 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. First, open up the \"Turn Windows Features on or off\" option from the control panel: Then enable the \"Windows Subsystem for Linux\" feature: After your computer has restarted, open up the Windows App Store and search for \"Ubuntu\": Once Ubuntu has installed, open up the app. It will then initialise (which can take a little while): Choose a username and password to complete the setup: And that's it! You can now open the Ubuntu app at any time to use Ubuntu on Windows, interfacing using the Bash shell.","s":"Microsoft Windows","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#microsoft-windows-1","p":54},{"i":71,"t":"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 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 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). To install the right software, we'll use a tool called 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! First, follow the instructions online to install Homebrew: In most cases, this will require opening the Terminal program and running a snippet which looks like this: /usr/bin/ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\" However, this might have changed since the time of writing so do 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 Ruby programming language (which comes pre-installed on MacOS). Once this has installed, install Bash by running the following command in the shell: brew install bash This uses the brew command, which we have just installed, to install the bash program. 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 /usr/local/bin/bash: 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 terminal window, it will run the latest version of Bash, which you can confirm by running echo $BASH_VERSION: There is actually a more sophisticated way to change what shell is used in a system, which is the special chsh command (short for \"change shell\"). We'll see this in a later section. We'll also see what echo is in more detail shortly.","s":"MacOS","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#macos-1","p":54},{"i":73,"t":"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.","s":"Linux","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#linux","p":54},{"i":75,"t":"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 Summary.","s":"That's It!","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#thats-it","p":54},{"i":77,"t":"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. Start by opening your shell. This is covered in Opening the Shell. Your shell should be Bash - if this doesn't sound familiar, then make sure you have followed the instructions in Configuring the Shell. You should see your terminal program running your shell. You can see what the version is of your shell by running: bash --version Let's quickly dissect this. We have run the bash 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. The --version text is a parameter. Parameters affect how commands work. This is actually easier to see with an example. Let's move to the home folder. On most computers your home folder is your personal space where things like documents, photos, music, downloads and so on are kept. Let's switch to the home folder by running the following command: cd ~ Once you've done that, run the pwd command: pwd So what has happened here? The first command: cd ~ Is used to change directory - that's what cd stands for. The parameter we passed to cd was just the 'tilde' character (~). This character has a special meaning in the shell - it means \"the current user's home directory\". Finally, we ran the pwd command. This command is short for print working directory. It writes out to the screen where you currently are. On my Mac, my home directory is located at /Users/dwmkerr, which is what the command has shown me. Let's take another look at a command. Run the following in your shell: ls The ls command is short for 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. We can pass different parameters to 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 Music folder, we'd just run: ls Music Not much to see here: Many commands actually allow us to pass multiple parameters. For example, we could list the contents of my Movies and my personal applications: ls Movies Applications There's not much in either. You might wonder why Applications is so empty - that's because we're looking at the applications only installed for the current user, because we are in the user's home directory. To see the applications for everyone we'd need to use the folder where applications are kept for all users. We can do this by running ls /Applications: The trick here is that we start with a leading forward slash - this means the Applications folder in the root of the computer, not the one in my current folder. On Windows, applications are kept in different places, but we can see some of the installed applications by running ls \"c:\\program files\\\": 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 two parameters. It would think we wanted to see the contents of the c:\\program and files folders - and they don't exist! The error above shows what happens when we miss the quotation marks. Now we can take a look at how a 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 size of the files in the folder. We do this by using the -lh pass the parameter, which is short for long list, human readable: ls -lh Downloads/*.jpg Now I can see all of the jpg files (jpg files are images) in my 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: ls - List the contents of a folder -lh - This is the long list in human-readable sizes parameter, which means we see how big the files are in a friendly format (like 911K for Kilobytes, rather than showing something like 911012 which would be the number of bytes - and harder to read!) Downloads/*.jpg - Show the contents of the Downloads folder, including any files which end with .jpg - the * is a wildcard which means that we don't mind what the filename is The -lh parameter is shorthand. Many commands offer longhand parameters (such as --version) as well as shorthand (such as -v as an alternative for --version). Longhand is easier to read, shorthand is faster to type. 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! Now let's look at one more command.","s":"A Quick Demo of the Shell","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#a-quick-demo-of-the-shell","p":54},{"i":79,"t":"The 'echo' command is used to write out a message in the shell. Here's an example of how it works: echo \"Hello Shell!\" This command writes out the text Hello Shell!: Why would we do this? One of the most common reasons would be to see what the shell thinks a certain value is. For example, try this command: echo \"My home directory is at: $HOME\" You'll see something like this: The $HOME part of the text is called a variable. We can recognise variables because they start with a dollar symbol. $HOME is a built-in variable which holds the location of the current user's home directory. We're going to see all sorts of cool things we can do with echo as we continue in the book!","s":"The Echo Command","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#the-echo-command","p":54},{"i":81,"t":"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'. A useful reference might be the picture below: Here we map the shell commands to the visual interface's equivalents: pwd shows the current working directory - where you currently are in the file system ls lists the files in the current directory (or any directory you tell it) cd .. changes the directory to another location - if you use the special .. directory, you are telling it to change to the parent directory, i.e. 'go up' in the file system 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: cd ~/Downloadsopen himalayas.jpg We can see the result here: Running open himalayas.jpg has opened the photo in the application which is used for photos by default in the operating system. 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 open command will open a file on MacOS. On Windows you can use start, and on Linux you can generally use xdg-open. As a nifty trick, trying running open .1: . This will open the current folder. Every folder contains two 'special' folders. The first is .., which we've seen means 'my parent folder' and the second is ., which means 'myself'. Having this . folder is convenient, as it means we can do things like this - run a command to open the current folder. 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 concepts to remember, which are much more important than the individual commands we've see are: In the shell we run commands We can change how commands work by using parameters Some parameters just go at the end of the command - like ls Downloads Some parameters start with a hyphen, and change how the command behaves - these are often called 'flags'. An example is ls -lh, which lists the files in the current folder with a human-readable file size We've also learned: cd changes the current directory pwd prints the current directory ls lists the files in a directory echo can be used to write out text to the screen open, start and xdg-open can be used to open a file or folder on MacOS, Windows and Linux respectively Now we can start to get into more detail!","s":"Move Around","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#move-around","p":54},{"i":83,"t":"In this section we learnt: That this book is for IT professionals, hobbyists or anyone who wants to learn more about how to work with computers What the shell is, and why we might want to use it How to open the shell programs for Windows, Mac and Linux which are installed by default 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 We introduced the following commands: cd - which changes directory pwd - which prints the current working directory ls - which lists the contents of a directory echo - which writes text to the screen open - which will open a file or folder We also briefly introduced variables, which are special values which start with the dollar symbol, such as $HOME which stores the user's home directory. We saw that each directory contains two special directories - .. which represents the parent directory, and . which represents the current directory. With these tasks complete we can now move onto the next section. On Windows you might need to run start . and on Linux, xdg-open ..↩","s":"Summary","u":"/part-1-transitioning-to-the-shell/getting-started/","h":"#summary","p":54},{"i":85,"t":"On this page","s":"The Renaissance of the Shell","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"","p":84},{"i":87,"t":"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 specific increase in the popularity of keyboard and script operated systems is a challenge. 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. We'll look at three specific developments in technology: Diversity of programming languages Convergence of operating platforms DevOps 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.","s":"Is there a Renaissance of the Shell?","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#is-there-a-renaissance-of-the-shell","p":84},{"i":89,"t":"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.","s":"The Changing Technology Landscape","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#the-changing-technology-landscape","p":84},{"i":91,"t":"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. 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. 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). \"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. 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). 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. Transition to the time of writing. In the Stack Overflow 2020 Technology Survey1, the top ten languages most wanted by employers are: Python JavaScript Go TypeScript Rust Kotlin Java C++ SQL C# 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. Why does this matter for the shell? The answer is that for 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. 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 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. 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 extremely efficient when working, and that overly featured IDEs can get in the way and hide complexity. 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.","s":"The Diversity of Programming Languages","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#the-diversity-of-programming-languages","p":84},{"i":93,"t":"Whilst the variety in programming languages and developer tooling may have increased, in many ways the operating platforms engineers use have become more homogeneous. 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. 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. 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. Even Windows has increasing support for Linux-like operation, in the form of the Windows Subsystem for Linux. 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. 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. 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.","s":"Convergence of Operating Platforms","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#convergence-of-operating-platforms","p":84},{"i":95,"t":"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. 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. 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. 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. 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 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 require automation. 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 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). 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. And for these reasons and many more, learning how to use the shell effectively has never been more relevant or practical. https://insights.stackoverflow.com/survey/2020↩","s":"DevOps","u":"/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/","h":"#devops","p":84},{"i":97,"t":"On this page","s":"Managing Your Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"","p":96},{"i":99,"t":"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! 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 effective-shell.com website, right here: effective-shell.com/downloads/effective-shell-samples.zip We 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! Open your shell - if you've not yet got set up with your shell, that's OK, just check Chapter 1 - Getting Started. Now that you have your shell open, we can run the wget command (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 Chapter 2- Navigating Your System. First, we'll move to our home directory, then download the file. cdwget https://effective-shell.com/downloads/effective-shell-samples.zip You'll see something like this: When you call the 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!). As an aside, if we were not in our home folder when we called the 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 -O (Output File) flag to say explicitly where we want to download the file. As an example, if were not in the home folder, but wanted to download there, we'd just call: cdwget -O ~/playground.zip https://effective-shell.com/downloads/effective-shell-samples.zip Now that we've downloaded the file, let's look at our home directory now, with a quick call to ls ~: 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.","s":"Creating a Playground","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#creating-a-playground","p":96},{"i":101,"t":"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: file ~/effective-shell-samples.zip The file command is showing us we have a zip file - now it's time to unzip it!","s":"Finding out about files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#finding-out-about-files","p":96},{"i":103,"t":"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! Run the command: unzip ~/effective-shell-samples.zip Now let's look at what we've got with the ls command: Excellent - we've now got a folder which contains all of the files in the zip archive.","s":"Extracting the Zip","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#extracting-the-zip","p":96},{"i":105,"t":"Now that we've downloaded and unzipped the file, we don't need the zipped version any more. So let's delete this file. The rm (Remove) command can be used to delete a file. If we run: rm ~/effective-shell-samples.zipls | grep samples Then we'll see the following: Notice that the zip file is gone - just the folder is left. By the way - be really careful with the 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 rm is a little risky! However one thing it will do to try and help you not make mistakes is let you know if you are trying to delete a folder, not a file. Run the following command to try and delete the unzipped folder: rm ~/effective-shell-samples The rm command has not succeeded in this case - it's warning us that we're not deleting a file, but a whole directory. Now we can get around this by adding the -r flag, which means 'recursive' - i.e. not just the folder but everything in it. But use this with caution!","s":"Deleting Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#deleting-files","p":96},{"i":107,"t":"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! In a graphical user interface, we'd open the folders and look at the files. In the shell, we can use the tree command to show the contents of a folder. Now the tree command is not installed by default on all systems. So if you are on a Mac, run: brew install tree 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. apt-get install -y tree). Using a non-universal command is generally not our goal in this book, but in these early stages while we are transitioning from the graphical user interface, the tree command can be really helpful. Later on we'll see how to use the more universal find command to give a similar output. Try it out with: tree ~/effective-shell-samples The 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 loas-gch.JPG file by running: file ~/effective-shell-samples/pictures/loas-gch.JPG Note that the file command is already showing it is a bit more clever. It knows that the file is a JPEG file (a picture), but is giving other details as well.","s":"Examining the Contents of a Folder","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#examining-the-contents-of-a-folder","p":96},{"i":109,"t":"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 cp (_Copy) command: cp ~/effective-shell-samples/pictures/laos-gch.JPG ~/effective-shell-playground/pictures/laos-gch-copy.JPG This makes a copy of the file - if you are not sure if it has worked, just run: tree ~/effective-shell-samples We can see we've made a copy.","s":"Copying a File","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#copying-a-file","p":96},{"i":111,"t":"Wow, it's painful putting ~/effective-shell-samples before everything! From Chapter 2- Navigating Your System we already know how to change directory, so let's do that now: cd ~/effective-shell-samples Remember - cd is change directory. Excellent - until we tell our shell otherwise, this our new working directory.","s":"Saving Some Keystrokes","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#saving-some-keystrokes","p":96},{"i":113,"t":"You might have noticed that the photos have different endings - one of them ends in .JPG. Let's rename it so that it has the ending .jpeg to be consistent with the others. To do this, we use the mv (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. Rename the copy we made of the photo by running: mv pictures/loas-gch-copy.JPG pictures/loas-gch-copy.jpeg Let's run tree to see what happened. Remember - now that our working folder is the playground, we don't even need to tell tree where to look, if we give it no arguments it'll assume we're looking at the working directory: 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 mv to also change what folder a file is in.","s":"Renaming or Moving Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#renaming-or-moving-files","p":96},{"i":115,"t":"Perhaps we're not happy with the name pictures for our folder we've been playing with, maybe we'd prefer to have them all in a folder called photos? Probably the first thing we'd do in a graphical environment is create a new folder - so let's do thee same here! Run the commands: mkdir photostree And we should see: We've use the mkdir command, which is short for Make Directory. This is how we create a new folder in the shell. Now let's say we wanted to be really organised, and create a photos folder by year and topic, perhaps 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! mkdir -p 2019/outdoors/picturestree Let's see how it looks: All we had to do was add the -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 more effectively in a shell than in your graphical user interface!","s":"Creating a New Folder","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#creating-a-new-folder","p":96},{"i":117,"t":"Let's copy the photos that we have in the pictures folder into the photos/2019/outdoor/climbing folder. When we run the cp or mv command, we can use a 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: cp pictures/* photos/2019/outdoor/climbing Here's how it works for Now we need to copy over our files from the pictures folder to the 2019/outdoor/photos folder. We'll use exactly the command we used before to copy a file - cp: $ cp pictures/* photos/2019/outdoors/climbing/$ tree photosphotosβ”œβ”€β”€ 2019β”‚ └── outdoorsβ”‚ └── climbingβ”‚ β”œβ”€β”€ laos-gch-copy.jpegβ”‚ β”œβ”€β”€ laos-gch.JPGβ”‚ └── nepal-mardi-himal.jpeg└── 2020 └── outdoors └── climbing6 directories, 3 files Here we've used the wildcard symbol, which is *, to say \"everything in the folder\". Many commands can take wildcards as inputs. We'll see much more about them later!","s":"Copying or Moving Multiple Files with Wildcards","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#copying-or-moving-multiple-files-with-wildcards","p":96},{"i":119,"t":"Now that we have our more organise 2019/outdoors/photos folder, we don't need the photos folder we created. So let's delete it! Remember how rm removes a file, and mkdir creates a folder? Well rmdir will remove a folder! rmdir photostree As an important sidenote, just how rm doesn't move files to your recycle bin, so you cannot undo the operation, rmdir works the same way. So if we try to remove a directory which has things in it, such as the pictures directory, it will fail: rmdir pictures In this case, it is actually easier to just call rm -r pictures. Why is that? Well it's just like we saw in the earlier example - rm can delete files or directories. And if the directory is not empty, we just add the -r (Recursive) flag to tell it to delete the directory and everything it contains.","s":"Deleting a Folder","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#deleting-a-folder","p":96},{"i":121,"t":"Run tree and you'll see we have a quotes folder: tree We're going to use the cat (Concatenate) command to look at the Ursula Le Guin quote. Run the following command: cat quotes/ursula-le-guin.txt In the screenshot we snuck in a quick file call to see what the shell thinks the file is. Why Concatenate? We're just showing the text in the terminal, not concatenating (i.e. joining) anything! Well the reason is that the cat command 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, 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. We'll see a 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. First, let's just cat the whole folder: cat quotes/* There we see the * wildcard again. We could be more specific and use something like cat quotes/*.txt to only show files ending in .txt. Notice how the output from all of the files has been concatenated together into a single output? That's where the cat name comes from - it concatenates, i.e. joins files. As one last trick, let's use this output but instead of showing it on the screen, put it into a single all-quotes.txt file: cat quotes/* > quotes/all-quotes.txttreecat quotes/all-quotes.txt The > part of this is called a redirect operator - in short it's telling the shell not to write to the screen, but to write to a file. We've concatenated all of the individual quotes and made a single file from them. We'll look at wildcards and redirection in a lot more detail as we continue through the book!","s":"Looking at Text Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#looking-at-text-files","p":96},{"i":123,"t":"Let' say that we want to zip up the new 2019/outdoors/pictures folder. We've already seen the unzip command, let's see how to use the zip command to zip up a folder: Run the command below: zip -r 2019-outdoor-pictures.zip 2019 This is how it will look - there's a tree and ls command before and after so we can see what's happening! Great! We've created a zip. Let's dissect the command a bit: zip just means call the zip executable -r means recursive we don't just want to zip the 2019 folder, we want to zip everything inside it as well! 2019-outdoor-pictures.zip is the name of the file we want to create, we put this first... ...because everything which follows (e.g. 2019) is going to be zipped, and we can specify many files and folders if we want","s":"Zipping up Files","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#zipping-up-files","p":96},{"i":125,"t":"In this chapter we introduced the following: The wget (web get) command can download a file from the web. If we use the -O (output location) flag, we can specify where we want to download the file to. The 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). The unzip command can unzip a file for us. The rm (remove) command can delete a file. The rm command won't delete a folder which has files in it, unless you tell it to by adding the -r (recursive) flag. The tree command can show the files and folders in a given directory, or the current directory by default. The cp (copy) command can copy a file. The cp can also be given wildcards like * to copy many files. The mv (move) command can move or rename a file. The mkdir command can create a folder - it can even create a whole tree of folders if you pass the -p (create parent directories) flag. The rmdir command can delete a folder - but just like rm it will fail if the folder is not empty! When we delete files in the shell with rm or rmdir they are gone forever, no recycle bin! The cat command (concatenated) can be used to write the contents of a file to the screen. We can pass multiple files to commands like cat if we use wildcards, such as quotes/*. We can write the output to a file instead of the screen, if we use the > (redirect to file) operator.","s":"Summary","u":"/part-1-transitioning-to-the-shell/managing-your-files/","h":"#summary","p":96},{"i":127,"t":"On this page","s":"Navigating Your System","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"","p":126},{"i":129,"t":"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: pwd You should see something like this: 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. The pwd command is the Print Working Directory command. It shows the full path of the directory that you are in. There's one more way to find the working directory. It is stored in an Environment Variable called PWD. 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. Try the following command: echo \"My current working directory is: $PWD\" You should see something like this: The dollar symbol is used to tell the shell we want to use the PWD variable, not write out the text PWD. We'll see a lot more about environment variables as we continue through the book.","s":"The Working Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#the-working-directory","p":126},{"i":131,"t":"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: ls You should see something like this: The ls command is the 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. There are a lot of options for the ls command. For now, let's look at one of the most common options -l. This shows the contents as a list: ls -l 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.","s":"Listing the Contents of the Working Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#listing-the-contents-of-the-working-directory","p":126},{"i":133,"t":"In a graphical user interface, you move to a different directory by clicking on it. In the shell, you run the cd command. Try it out with: # Move to the pictures directory...cd Pictures# ...then list the contents of the directory.# Note that the '-al' flags mean show *all* files, as a *list*.ls -al 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. On my system, we'll see the following output: The cd command is the 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 (pwd for Print Working Directory, cd for Change Directory). Now that you know how the 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 paths work in systems.","s":"Changing the Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#changing-the-directory","p":126},{"i":135,"t":"In Linux, Windows and MacOS (and most other operating systems), paths are the 'addresses' of files or folders. There are two types of paths - Absolute Paths and 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: /Users/dwmkerr/repos/github/dwmkerr/effective-shell Absolute paths always start with a slash. That's how the system knows it is an absolute path. The / is the root of the file system - basically it's the folder which everything else lives in. If I have an absolute path, I know exactly where the file or folder is. Let's compare this to a relative path. Below is the relative path in my shell for the file I'm writing right now: website/content/docs/part-1-transitioning-to-the-shell This path is 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 Pictures folder, and I want to move to the 2020-photos folder, I could do it in two ways. The first is with an absolute path: cd /Users/dwmkerr/Pictures/2020-photos The second is with a relative path: cd 2020-photos In short - relative paths are often useful if you want to move to something within the current directory and absolute paths are useful if you need to move to somewhere completely different.","s":"Understanding Paths","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#understanding-paths","p":126},{"i":137,"t":"As you experiment with these commands, you might have noticed that every folder contains two other folders, one with the name . and one with the name ... Run ls -al on the pictures folder to see an example: ls -al pictures You should see something like this: This picture highlights two special folders - . and ... These are special folders which exist in every folder in the system. The first folder, ., 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: cp . /backup The cp command is the Copy command, and we'll see it in the next chapter. But the key thing to note is that we can use . to tell the command to copy the folder we are in right now. The .. folder means the parent folder. You can use this to \"go up\" to the parent folder, for example: cd ..ls . Would give: Note that we've used cd .. to change directory to the parent folder then ls to list the contents of the current folder. We could also just have used ls on its own as it defaults to the current folder. The .. 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 pictures folder and I want to move to the scripts folder, I can just use: cd ../scriptsls And we'll see this:","s":"The Special Dot and Dot Dot Folders","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#the-special-dot-and-dot-dot-folders","p":126},{"i":139,"t":"There is one more special part of the file system we have to know about. That is the Home Directory. In Linux-like systems every user has their own personal directory where they can keep their files and folders. This directory can always be accessed through the ~ 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: cd ~ls This would show something like this: 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: cd ~/Pictures Your home directory on most computers will be where you keep your documents, pictures, videos and so on. Normally this directory is not accessible to other users of the system. Each user in a system gets their own home directory. You can also see the home directory by using the special HOME environment variable: echo \"My home directory is: $HOME\" This would show something like this: One useful trick - running cd without any parameters will always take you home! So to go home, just run: cd 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!","s":"The Home Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#the-home-directory","p":126},{"i":141,"t":"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 pushd command: pushd ~/Downloadslspopd After I've checked my downloads, I can run popd to go back to where I was: The pushd command 'pushes' a new working directory onto a stack - moving you there. The 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. 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 pushd and popd would look if we were to visualise it: 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.","s":"Pushing and Popping the Working Directory","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#pushing-and-popping-the-working-directory","p":126},{"i":143,"t":"One last trick which can save time is the following command: cd - This is a special parameter for cd which tells it to go back to the last location you moved to. Here's how it might look if you use it: 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 pushd and popd instead.","s":"Going Back","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#going-back","p":126},{"i":145,"t":"In this chapter we introduced the following: The pwd (print working directory) command shows the current working directory The $PWD environment variable holds the current working directory The ls (list) command shows the contents of the current directory or a given directory The ls -l command shows the contents of the current directory as list The cd (change directory) changes the current working directory Absolute paths are paths which specify the exact location of a file or folder... ...Relative paths are paths which are relative to the current directory The . special folder means 'this folder' The .. special folder means 'the parent folder' The ~ special folder is the 'home directory' The $HOME environment variable holds the user's home directory You can run cd at any time to quickly go to your home directory You can use pushd and popd to push and pop the working directory stack You can use the cd - command to go back to the last location","s":"Summary","u":"/part-1-transitioning-to-the-shell/navigating-your-system/","h":"#summary","p":126},{"i":147,"t":"Part 2 - Core Skills 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. In this section, we'll look at core shell skills. These skills are fundamental to how the shell works, and fundamental to using it effectively. Even if you are familiar with the concepts in each chapter, I would still recommend skimming these sections to make sure there is nothing you have missed. A solid understanding of these core skills will be useful for later sections. So even though this book is designed to be something you can dip and dip out of, in any order you choose, it may be worth focusing on this section before moving to later sections.","s":"Part 2 - Core Skills","u":"/part-2-core-skill/","h":"","p":146},{"i":149,"t":"On this page","s":"Fly on the Command Line","u":"/part-2-core-skills/fly-on-the-command-line","h":"","p":148},{"i":151,"t":"Let's assume we have a very simple command we are writing, which is going to write a quote to a text file: echo \"The trouble with writing fiction is that it has to make sense,whereas real life doesn't. -- Iain M. Banks\" >> quote.txt Navigating around long lines of text is a slow process if you are only relying on the arrow keys. Let's see how we can quickly move around and manipulate text!","s":"Basic Navigation","u":"/part-2-core-skills/fly-on-the-command-line","h":"#basic-navigation","p":148},{"i":153,"t":"Quickly jump to the beginning or end of the text: Ctrl + a - Go to beginning Ctrl + e - Go to end","s":"Go to beginning / end","u":"/part-2-core-skills/fly-on-the-command-line","h":"#go-to-beginning--end","p":148},{"i":155,"t":"For a little more fine-grained movement, you can jump backwards or forwards one word at a time: Alt + b - Go back one word Alt + f - Go forward one word","s":"Move backwards / forwards one word","u":"/part-2-core-skills/fly-on-the-command-line","h":"#move-backwards--forwards-one-word","p":148},{"i":157,"t":"As this is the first operation we're seeing which changes the text, it is useful to remember how to undo any changes you make! Ctrl + w - Delete a word Ctrl + - - Undo most recent change","s":"Delete a word or undo a mistake","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-a-word-or-undo-a-mistake","p":148},{"i":159,"t":"We've seen how to delete the word on or behind the cursor, now let's see how to delete the next word: Alt + d - Delete next word Remember, just like any edit you can undo these changes with the Ctrl + - command.","s":"Delete the next word","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-the-next-word","p":148},{"i":161,"t":"In the Bash shell, you can delete all the way to the beginning of the line with Ctrl + u. However - if you are using the Z-Shell this will delete the entire line! Ctrl + u - Delete to beginning of line OR delete line","s":"Delete to beginning / clear line","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-to-beginning--clear-line","p":148},{"i":163,"t":"You can also delete all of the way to the end of the line. Ctrl + k - Delete to end When you find yourself repeatedly using the arrow or delete keys, refer back to this section to remind yourself of the shortcut - it will save a lot of time in the long run!","s":"Delete to end","u":"/part-2-core-skills/fly-on-the-command-line","h":"#delete-to-end","p":148},{"i":165,"t":"Once you have the basic navigation commands down, the next essential is searching. Let's assume we've run the following three commands: $ command1 param1 param2 param3$ command2 param4 param5 param6$ command3 param7 param8 param9 You can search backwards or forwards with Ctrl + r and Ctrl + s. This will search in the current line and then iteratively through previous lines: People often remember this as searching through the history - but remember that it actually searches the current line as well. So this is often the fastest way to move to the desired location in the current line. This is useful for searching in the current command, but can be also used to quickly search backwards and forwards through the command history: As you type, your command history is searched, the most recent commands coming first. Use the arrow keys to edit the command, press enter to execute it, or Ctrl + g to cancel the search. I think it's a little easier to see these commands in action with a more realistic example, so here's how they look with the text we used earlier.","s":"Searching","u":"/part-2-core-skills/fly-on-the-command-line","h":"#searching","p":148},{"i":167,"t":"Search backwards or forwards through the current line and also the history: Ctrl + r - Search backwards (reverse search) Ctrl + s - Search forwards","s":"Search Backwards / Forwards","u":"/part-2-core-skills/fly-on-the-command-line","h":"#search-backwards--forwards","p":148},{"i":169,"t":"This one is easy! Just hit Enter:","s":"Run the command found in a search","u":"/part-2-core-skills/fly-on-the-command-line","h":"#run-the-command-found-in-a-search","p":148},{"i":171,"t":"When you have found the command or positioned the cursor where you want it, use the Left or Right arrow keys to stop searching and to go back into the normal editing mode:","s":"Edit the command found","u":"/part-2-core-skills/fly-on-the-command-line","h":"#edit-the-command-found","p":148},{"i":173,"t":"Cancel the search and return back to the text as it was before you started with the Ctrl + g command:","s":"Stop Searching","u":"/part-2-core-skills/fly-on-the-command-line","h":"#stop-searching","p":148},{"i":175,"t":"These tips and tricks are helpful, but if you are working with a really long or complex command, you might find it useful just to jump into your favourite editor. This is one of those tricks that when you know it, you'll wonder how you lived without it! Use Ctrl + x , Ctrl + e to edit-in place, opening the current command line in your default editor: Now it's important to explain that this is the shell's default editor. This might not be the same as the default editor for your operating system. You can see what the shell is using as its default editor by printing the contents of the EDITOR environment variable. For example, my shell will show this: $ echo $EDITORvim This means vim will be used to edit the command line. Your shell might use emacs or nano as a default editor. Unless you are familiar with vim or emacs, you might not find them particularly user friendly as an editor. You can change your default editor by setting the EDITOR variable. For example, below I set the editor to code (with the -w flag which tells the code program not to return control immediately back to the shell but instead wait until I've finished editing the file): Now this works (just about), but I wouldn't recommend using a Graphical Editor like Visual Studio Code for this. The reason is that because the editor runs in a separate window, it is actually easy to lose track of it (or the shell). You pause to take a short break, come back, close the editor and the contents are either lost or written to the shell (and if you see in the example above, the shell actually executed the command, rather than just putting it in the command line ready for me to execute). The other reason to avoid a graphical editor is that if you are using a shell on another user's machine, the editor might not be present (or might be configured differently). In general however, the main reason to avoid a graphical editor is that it moves the context of the command away from where you are in the shell to another place, which can be confusing. If you see the screenshot below, I have two editors open: The top right pane has my git commit command running (which is asking me to write a description of my changes) and the bottom right pane has the command line editor running (where I am testing out the commands for this chapter). In this example, each editor has taken the place of the contents of the shell, so there's no ambiguity about which command I am editing. If I was to open a graphical editor, it would open multiple tabs for this operation and I'd have to track which tab was which. It can be daunting to learn an editor like vim or emacs. Chapter 27 goes into more detail on the terminal based text editors - for now if you are not familiar with these programs I recommend you use the nano editor. Nano is small, simple and shows the shortcuts in a convenient menu at the bottom of the screen: In Chapter 18 we'll see how to make permanent customisations to the shell, configuring things like the default editor.","s":"Editing In-Place","u":"/part-2-core-skills/fly-on-the-command-line","h":"#editing-in-place","p":148},{"i":177,"t":"Probably the shortcut I use the most is Ctrl + l, which clears the screen without trashing your current command. Here's how it looks: This is very helpful if you have a lot of noise and output on the screen and are ready to start with a fresh command.","s":"Clear the Screen","u":"/part-2-core-skills/fly-on-the-command-line","h":"#clear-the-screen","p":148},{"i":179,"t":"Just a few days ago a friend showed me a fantastic trick. If you run the history command, the shell will print the recent history of commands you have entered. But as an added bonus, you can execute any of these commands by entering an exclamation mark and the number next to the command: The number is actually just the line number in the history file. Most shells maintain a history of commands which have been entered (to allow for things like searching through old commands). Where this history file is kept will depend on your shell, configuration and operating system, but in most cases you can find the file by running: echo $HISTFILE There are many configuration options for the shell history. But the main thing to remember is that you can see recent history with the history command and quickly execute the command at line n by running !n.","s":"See the History and Execute a Recent Command","u":"/part-2-core-skills/fly-on-the-command-line","h":"#see-the-history-and-execute-a-recent-command","p":148},{"i":181,"t":"You can use the bindkey command to see a list of all keyboard shortcuts: $ bindkey\"^@\" set-mark-command\"^A\" beginning-of-line\"^B\" backward-char\"^D\" delete-char-or-list\"^E\" end-of-line\"^F\" forward-char\"^G\" send-break\"^H\" backward-delete-char\"^I\" expand-or-complete\"^J\" accept-line\"^K\" kill-line\"^L\" clear-screen... This is an extremely useful command to use if you forget the specific keyboard shortcuts, or just want to see the shortcuts which are available.","s":"Pro Tip: All The Keys!","u":"/part-2-core-skills/fly-on-the-command-line","h":"#pro-tip-all-the-keys","p":148},{"i":183,"t":"If you've mastered all of the commands here and feel like adding something else to your repertoire, try this: The Alt + t shortcut will transpose the last two words. Use Ctrl + t to transpose the last two letters: These two commands were new to me when I was researching this chapter. I can't see myself ever being able to remember the commands more quickly than just deleting the last two words (Ctrl+w twice!) or characters and re-typing them, but perhaps you'll find them useful!","s":"Pro Tip: Transposing!","u":"/part-2-core-skills/fly-on-the-command-line","h":"#pro-tip-transposing","p":148},{"i":185,"t":"All of the movement commands you've learned in this chapter apply to: Bash zsh The Python REPL The Node.js REPL And many more! The reason is that all of these programs use the same library under the hood to control reading command line input. This library is called GNU Readline. If you are ever looking to go deeper on this topic then search the web for GNU Readline. You can actually configure lower level details of how all programs which use readline work, with the .inputrc configuration file. This configuration file can be used to configure things like the shortcuts used to move around. All of these shortcuts should be familiar to Emacs users. There is in fact also 'Vi Mode' option for readline, which allows you to use vi commands to work with text. You can enter this mode with set -o vi. There's a great cheat sheet on emacs readline commands at readline.kablamo.org/emacs, which is a very useful reference if you want to dig deeper. We'll also see GNU Readline later on when we talk about writing programs which work well in the shell. I hope that was useful! Being able to rapidly move around the command line will hopefully save you time and make you a more confident user of not just the shell, but many command line programs.","s":"The Power of Readline","u":"/part-2-core-skills/fly-on-the-command-line","h":"#the-power-of-readline","p":148},{"i":187,"t":"On this page","s":"Finding Files","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":189,"t":"The 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 ~/effective-shell folder. Downloading the Samples Run the following command in your shell to download the samples: curl effective.sh | sh Let's set the current working directory to the effective-shell folder and run the find command: $ cd ~/effective-shell$ find../text./text/simpsons-characters.txt./scripts./scripts/show-info.sh./websites./websites/simple./websites/simple/index.html./websites/simple/styles.css./websites/simple/code.js... By default, find will list all of the files and folders which are present in the current working directory. It will also show the children of any folders it finds, meaning that it shows the full hierarchy of files and folders. Find not working on MacOS If you are running these samples on MacOS, you will probably see the following output: $ findusage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression] This is the find command telling you what parameters can be used. On MacOS the default find command does not assume the current working directory. This is because there is a difference between the MacOS and GNU versions of 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). To run the equivalent command on MacOS, just provide the current directory as a parameter: $ find . A better solution is to install the findtools package, which will install the GNU versions of the tools we'll be using: $ brew install findtools$ gfind If you do install findtools, remember that all of the GNU versions of the tools start with g - so when reading this chapter substitute find with gfind. For more details on what BSD and GNU are, you can check Chapter - Unix, Linux, GNU and POSIX, which covers these concepts in detail. So this is the 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. You can also provide multiple directories: $ find /usr/bin /usr/sbin/usr/bin/usr/bin/fwupdtool/usr/bin/gnome-keyring.../usr/sbin/usr/sbin/cupsd/usr/sbin/pppdump... This is the most basic use of find - showing a file and folder hierarchy. Now let's look at how to search using this command.","s":"Introducing the Find Command","u":"/part-2-core-skills/finding-files","h":"#introducing-the-find-command","p":186},{"i":191,"t":"Perhaps the most common use for 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 ~/effective-shell folder as a playground to search in.","s":"Searching with Find","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":193,"t":"The -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 -type f: $ find . -type f./text/simpsons-characters.txt./scripts/show-info.sh./websites/simple/index.html./websites/simple/styles.css./websites/simple/code.js... And for folders, using -type d (remember, d is for directory!): $ find . -type d../text./scripts./websites./websites/simple... It's important to note that when searching for folders, the find command shows folders which are normally hidden, such as the special 'dot' folder1. 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.","s":"Searching for Files or Folders only","u":"/part-2-core-skills/finding-files","h":"#searching-for-files-or-folders-only","p":186},{"i":195,"t":"We can use the -name parameter to search for files and folders by name. For example, this is how we would search for anything with the letters log in the name: $ find . -name \"*log*\"./logs./logs/web-server-logs.txt./logs/apm-logs./logs/apm-logs/apm05.logs./logs/apm-logs/apm02.logs./logs/apm-logs/apm03.logs./logs/apm-logs/apm00.logs./logs/apm-logs/apm01.logs./logs/apm-logs/apm04.logs You can see I've used a * wildcard before and after the letters log - this means that I have actually supplied a pattern, which could be read as 'any characters (including nothing), followed by the characters log, followed by any other characters (including no characters)'. If I don't use a wildcard, the logs folder will not be found - because it doesn't match the pattern log - without using a wildcard, the pattern is explicitly looking for an exact match: $ find . -name \"log\" Note the output - no files or folders were found, as none have the exact name log. The -name parameter is very specific - it will only match files or folders with the name provided. Here's an example: $ find . -name \"apm00.logs\"./logs/apm-logs/apm00.logs Here I have used -name to search for an exact name. What about if I search for apm-logs? $ find . -name \"apm-logs\"./logs/apm-logs The folder named apm-logs is found, but not the files in the folder - the names of those files don't match the pattern apm-logs. What if we make it a wildcard pattern? $ find . -name \"*apm-logs*\"./logs/apm-logs The same results! This is because for the files in the apm-logs folder they don't have apm-logs in their name anywhere - that is in their path, i.e. the full address of the file including its folder. So let's look at how to search by path next! An Important Note on Quotes Make sure you use quotes when building your patterns. This command: $ find . -name \"*log*\" Will give different output to this command: $ find . -name *log* Why is this? In the first case, we explicitly pass the text *log* to the 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. In the second case, the shell itself tries to deal with the wildcards, then passes the results to find. And the shell deals with them differently. You can see exactly what the shell expands them to with this snippet: $ parameter=(*log*)$ echo $parameterlogs 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 not to interfere with the text and instead pass it to find, so that find can deal with the wildcard. The shell is using globbing in the second case, which is covered in a later chapter.","s":"Searching by Name","u":"/part-2-core-skills/finding-files","h":"#searching-by-name","p":186},{"i":197,"t":"The -path parameter can be used to filter the results based on a pattern in the path of the file: $ find . -path \"*apm-logs*\"./logs/apm-logs./logs/apm-logs/apm05.logs./logs/apm-logs/apm02.logs./logs/apm-logs/apm03.logs./logs/apm-logs/apm00.logs./logs/apm-logs/apm01.logs./logs/apm-logs/apm04.logs Again, note that this is very specific, we've added wildcards to the pattern, making it *apm-logs*. Without the wildcards we would find nothing, because none of the results have the exact path apm-logs.","s":"Searching by Path","u":"/part-2-core-skills/finding-files","h":"#searching-by-path","p":186},{"i":199,"t":"We can provide multiple search options. For example, if we want to search only for files which end in .logs, we can do this: $ find . -type f -name \"*.logs\"./logs/apm-logs/apm05.logs./logs/apm-logs/apm02.logs./logs/apm-logs/apm03.logs./logs/apm-logs/apm00.logs./logs/apm-logs/apm01.logs./logs/apm-logs/apm04.logs By using both the -type and -name parameters, we've created an 'AND' style search - i.e. we're looking for items which match both of the given criteria. What if we want to perform a search which returns items which match either of the patterns (i.e an 'OR' search)? For that we can use the -or parameter: $ find . -name \"*.js\" -or -name \"*.html\"./websites/simple/index.html./websites/simple/code.js./programs/web-server/web-server.js In this case we show results which match either of the expressions.","s":"Combining Searches - the AND and OR operators","u":"/part-2-core-skills/finding-files","h":"#combining-searches---the-and-and-or-operators","p":186},{"i":201,"t":"Any one of the search parameters you've seen so far can be made case-insensitive by putting the letter i before the parameter name. This means that the following commands are identical: $ find . -name \"*.js\" -or -name \"*.Js\" -or -name \"*.jS\" -or name \"*.JS\" And: $ find . -iname \"*.js\" I know which one I'd rather type! You can use -iname for case-insensitive name searches, or -ipath for case-insensitive path searches.","s":"Case Insensitive Searches","u":"/part-2-core-skills/finding-files","h":"#case-insensitive-searches","p":186},{"i":203,"t":"We can build more complex expressions by grouping together patterns using brackets, or by using the -not pattern. Here's an example: $ find . \\( -name \"*.js\" -or -name \"*.html\" \\) -and -not -path \"*programs*\"./websites/simple/index.html./websites/simple/code.js The first section groups together two expressions, meaning \"files with names which match *.js or *.html, the second section then says \"and also the text programs must not be in the path\". The only annoying thing about grouping is that you must escape the brackets, by putting a \\ 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 find command.","s":"Grouping and the NOT operator","u":"/part-2-core-skills/finding-files","h":"#grouping-and-the-not-operator","p":186},{"i":205,"t":"The find command bothered me for years. The parameters looked odd - for example why is it -name instead of -n or --name, which would be more standard2? Also, why is it that I cannot type this: find -name \"something.txt\" /home/ But instead have to put the folder name before the parameters, which again is non-standard? 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 search expression. The structure of the find command is as follows: find [starting point...] [expression] The options (or parameters) for the find command are short, one-letter options which go before the file name. The -L (follow links) option is an example - it goes before the starting point of the search: find -L /usr/bin -name \"*sh\" All of the other things we've seen so far which we've described as parameters are actually used to build the expression - the actual search pattern. The -name, -and, -or, -ipath and similar constructs we've looked at are actually part of a mini 'search language', they're not parameters to the command. This might seem obvious, or it might seem silly, but either way, remembering that the structure of the find command is find [starting points...] [expression] may help you remember what order to write each part of the command in. It certainly took a few years for me to realise this was the reason, and until that point I used to get frustrated with 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. You can find details on this in the manpage for find, open it with man find.","s":"Why the Weird Parameters?","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":207,"t":"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. The find command can perform operations on the files which are found. Now before we continue, I'll warn that I'm not going to go into much detail here. The reason is that I actually recommend not using these operations. Instead, use the xargs command which is covered in Chapter 14 - Build Commands on the Fly with Xargs. The Xargs command lets you take the list of files from 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 find - and it is closer to the Unix Philosophy of having the find command 'do one thing and do it well'. 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!","s":"Performing Actions on Search Results","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":209,"t":"The -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. Here's how we'd find all files in the user's home directory with the ending .tmp and show their path: $ find ~ -name \"*.tmp\" -print/home/dwmkerr/commands1.tmp/home/dwmkerr/commands2.tmp/home/dwmkerr/commands3.tmp You are unlikely to need to use -print - but it will come in handy to know it exists later on when we look at the -print0 option (we'll see this in Chapter 14). Let's look at the other actions we can perform.","s":"Printing Paths","u":"/part-2-core-skills/finding-files","h":"#printing-paths","p":186},{"i":211,"t":"We can delete the files and folders found by using the -delete action: $ find ~ -name \"*.tmp\" -delete This can be a convenient way to delete files, but I would recommend 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 children of folders are deleted where necessary before the folders themselves. This might not be what you expect because that's not what the -print output would show (although you can force this behaviour with the -depth option). Check Chapter 14 for a better solution - in short we can use find ~ -name \"*.tmp\" -print0 | xargs -p -0 rm to instead pass the files to rm and ask the user to confirm before the deletion happens. This will be explained in a lot more detail in Chapter 14.","s":"Deleting Files","u":"/part-2-core-skills/finding-files","h":"#deleting-files","p":186},{"i":213,"t":"You can use the -exec action to execute an arbitrary command. Use the special characters {} as a placeholder for the filename. Here's an example - we'll find all text files and count the number of words in each one: $ find ~/effective-shell -name \"*.txt\" -exec wc -w {} \\;29 /home/parallels/effective-shell/text/simpsons-characters.txt20 /home/parallels/effective-shell/quotes/iain-banks.txt16 /home/parallels/effective-shell/quotes/ursula-le-guin.txt10373 /home/parallels/effective-shell/logs/web-server-logs.txt We use -exec to tell find we want to execute a command. Then we use wc -w {} to say \"call the wc (word count) command with the -w (words) flag. The {} text is expanded to the list of files. Finally, we need a semi-colon to tell find where the end of the exec command is. And because the semi-colon has a special meaning in the shell, we have to escape this semi-colon by putting a \\ backslash before it. The -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.","s":"Execute a Command","u":"/part-2-core-skills/finding-files","h":"#execute-a-command","p":186},{"i":215,"t":"Now if there is one action to learn, it is the -ok action, which works just like -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 *.txt: $ find ~/effective-shell -name \"*.txt\" -ok rm {} \\;< rm ... /home/parallels/effective-shell/text/simpsons-characters.txt > ? n< rm ... /home/parallels/effective-shell/quotes/iain-banks.txt > ? n< rm ... /home/parallels/effective-shell/quotes/ursula-le-guin.txt > ? n< rm ... /home/parallels/effective-shell/logs/web-server-logs.txt > ? n 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 n (for 'no'). Type y (for 'yes') if you want to run the command.","s":"Execute a Command with a Confirmation","u":"/part-2-core-skills/finding-files","h":"#execute-a-command-with-a-confirmation","p":186},{"i":217,"t":"It is worth briefly mentioning symlinks - because if you don't understand how find handles symlinks then you might be surprised. As an example of how this might surprise you, compare the output of the two commands below: $ find /usr/bin/usr/bin/usr/bin/uux/usr/bin/cpan/usr/bin/BuildStrings/usr/bin/loads.d/usr/bin/write...$ find /bin/bin It seems that /bin doesn't contain any files - but is that the case? Running ls /bin will probably show that you have quite a few files. If you are on MacOS instead try running find /tmp to show the same oddity - the find program doesn't seem to show the contents of the files. So why did find /bin not show the files in the /bin folder? The answer is that /bin is a symlink (or if you are on MacOS and want to test the same behaviour and are using /tmp, /tmp is a symlink). You can see this by running the command below: $ ls -l / /usr | grep binlrwxrwxrwx 1 root root 7 Aug 7 18:06 bin -> usr/binlrwxrwxrwx 1 root root 8 Aug 7 18:06 sbin -> usr/sbindrwxr-xr-x 2 root root 40960 Jan 25 17:17 bindrwxr-xr-x 2 root root 20480 Jan 25 16:42 sbin Note how the root bin and sbin folders are actually just symlinks to usr/bin and usr/sbin respectively. When using the find command just remember that it won't follow symlinks by default - provide the -L option to follow symlinks: $ find -L /bin/bin/bin/fwupdtool/bin/gnome-keyring/bin/dpkg-gencontrol/bin/prltoolsd... There are more options which control how find works with links, check man find for the details.","s":"Handling Symlinks","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":219,"t":"The find command is 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! 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 find, which might make you want to learn more, here are a few commands which show just how versatile it can be! Find large files The -size test can be used to search by file size - note how with a + and - we can set the minimum and maximum sizes: find / -size +1G -500G Find recently edited files The -mtime test can be used to find files which were recently modified: find . -not -path \"*/\\.*\" -mtime -2 Note how a -not -path test is used to skip anything which starts with a . dot (i.e files and folders which are normally considered hidden). Find files which have had permissions changed The -ctime test can be used to find files which have had their attributes (such as permissions) changed: find ~/.ssh -ctime -30 Find any executable scripts and make them non-executable We can search by permissions, as shown below: find ~ -perm /a=x -exec chmod -x {} + This example uses the -perm test, checking if 'all' (users, the owner and group) have the x (executable) bit set, then executes the chmod -x command to remove the executable bit. We also end the command with + rather than ;, which means we will execute chmod once with each file passed to the command (rather than running chmod for each file). Note that the + operator can cause an error if the list of files is too big for the command you pass it to to handle! Find empty folders and remove them with a confirmation We can use the -empty test to find empty folders: find ~ -type d -maxdepth 3 -empty -ok rmdir {} \\; This example uses the -empty test, as well as the -maxdepth parameter to limit the search to only three folders deep. These examples just scratch the surface of what find can do. The goal of this chapter is not to have an exhaustive description of the find command, but equip you with the essentials. When you feel comfortable with the essentials you can then build on your knowledge of find.","s":"Scratching the Surface","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":221,"t":"In this chapter we introduced the 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. In the next chapter we'll take a look in a bit more detail into what a shell actually is! Share - with \"why the hell are the parameters so stupid\" Blog post on linux essentials, refer to alpine for an example of why find is important Test work in progress page Footnotes If you are not sure what the 'dot' folder is, check Chapter 2 - Navigating Your System↩ This is not just a personal preference thing; this is based on the POSIX standard: 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.↩","s":"Summary","u":"/part-2-core-skills/finding-files","h":"","p":186},{"i":223,"t":"On this page","s":"Job Control","u":"/part-2-core-skills/job-control","h":"","p":222},{"i":225,"t":"Let's start with an example. I am building a simple web page. It has one index.html file, one styles.css file, and one code.js file. The index.html file looks like this: My New Project Opening the file in a browser doesn't quite work, as it won't load the code or the styles. We need a web server to serve styles and code. A super-useful one-liner to run a web server on any machine with Python installed is: python -m SimpleHTTPServer 3000 In fact, this is so useful that I normally alias this command, so that I can just type serve. We'll see aliases in a later chapter. Make sure you have the samples folder downloaded. Downloading the Samples Run the following command in your shell to download the samples: curl effective.sh | sh Now run the following commands to open the webpage: $ cd ~/effective-shell/websites/simple$ python -m SimpleHTTPServer 3000 For now, if we run this command, then we can open the webpage in a browser, with the styles and code loaded: We can also see that the server has served the HTML, JavaScript, and CSS files, this is clear from the output of the Python command we ran: $ python -m SimpleHTTPServer 3000Serving HTTP on 0.0.0.0 port 3000 ...127.0.0.1 - - [08/Jan/2021 16:33:40] \"GET / HTTP/1.1\" 200 -127.0.0.1 - - [08/Jan/2021 16:33:40] \"GET /styles.css HTTP/1.1\" 200 -127.0.0.1 - - [08/Jan/2021 16:33:40] \"GET /code.js HTTP/1.1\" 200 -127.0.0.1 - - [08/Jan/2021 16:33:40] code 404, message File not found127.0.0.1 - - [08/Jan/2021 16:33:40] \"GET /favicon.ico HTTP/1.1\" 404 - All well and good so far. But if you try and use the shell to do something else, you will encounter a problem, let’s take a look.","s":"What Is Job Control?","u":"/part-2-core-skills/job-control","h":"#what-is-job-control","p":222},{"i":227,"t":"Let's say we want to now continue using our shell, maybe to edit the website with a terminal editor like Vim or Emacs, or we want to zip up the site, or just run any shell command1. We have a problem. The python process is still running - it's serving the website. Our shell is essentially useless, until we stop the server. See what happens when I try to edit a file: In the example above, I try to run vi, but nothing is happening. Standard input is not being read by the server and not being interpreted by the shell. I have to kill the server by hitting Ctrl+C. This sends a SIGINT signal (which tells the command to stop). We saw signals briefly in Chapter 4 - Becoming a Clipboard Gymnast and we'll see more of them in as we continue. Now I need to clear my screen to get rid of all of the error messages, then start again. This is obviously not optimal. Let's look at some solutions.","s":"The Problem","u":"/part-2-core-skills/job-control","h":"#the-problem","p":222},{"i":229,"t":"In most shells, you can run a command and instruct the shell to run it in the background. To do this, you end the line with an ampersand. Here's how the example would look in this case: $ python -m SimpleHTTPServer 3000 &[1] 7025$ Serving HTTP on 0.0.0.0 port 3000 ... By ending the command with an & ampersand symbol, we instruct the shell to run the command as a background job. This means that our shell is still functional. The shell has also notified us that this command is running as a background job with a specific job number: $ python -m SimpleHTTPServer 3000 &[1] 19372 In slightly obtuse language, the shell has informed us that it has started a job in the background, with job number 1 and that this job is currently handling the process with ID 19372. The ampersand solution is a fairly common pattern used in day-to-day work. The process is in the background and our shell is available for us to use as normal, the web server will continue to run in the background.","s":"Solution 1: Start the Server in the Background","u":"/part-2-core-skills/job-control","h":"#solution-1-start-the-server-in-the-background","p":222},{"i":231,"t":"Let's say you forgot to start the command in the background. Most likely in this case you'd kill the server with Ctrl+C and then start it again with the & option. However, what if this was a large file download or a task you didn't want to abort? In the example below, we'll move the job to the background: $ python -m SimpleHTTPServer 3000Serving HTTP on 0.0.0.0 port 3000 ...^Z[1] + 7657 suspended python -m SimpleHTTPServer 3000 The process is currently in the foreground, so my shell is inactive. Hitting Ctrl+Z sends a 'suspend' signal to the process2, pausing it and moving it to the background. Let's dissect this: $ python -m SimpleHTTPServer 3000Serving HTTP on 0.0.0.0 port 3000 ...127.0.0.1 - - [03/Jun/2019 13:38:45] \"GET / HTTP/1.1\" 200 -^Z[1] + 21268 suspended python -m SimpleHTTPServer 3000 The shell echos as I type, so we see ^Z (i.e., the Ctrl+Z chord I entered). The shell responds by moving the process into a background job and suspending it. The key here is that it is suspended. The process is paused. So the web server is no longer serving. If you are following with the sample, reload your browser. The webpage fails to load, as the server process is not able to respond to requests. To continue the job, in the background, we use the bg ('background') command, with a job identifier (which always starts with a % symbol - we'll see why soon) to tell the shell to continue the job: $ bg %1[1] + 21268 continued python -m SimpleHTTPServer 3000 The shell lets us know the job is being continued, and if we load the webpage again, the content is shown as expected. As a final check, we run the jobs command to see what jobs the shell is running: $ jobs[1] + running python -m SimpleHTTPServer 3000 And there you have it - our server is running as a background job. This is exactly what we would see if we run jobs after starting the server with an & at the end. In fact, using an & is perhaps an easier way to remember how to continue a suspended job: $ %1 &[1] + 21268 continued python -m SimpleHTTPServer 3000 In the same way ending a command with & runs it in the background, ending a job identifier with & continues it in the background. There is at least one more way to move a job to the background3, but I have not yet found it useful in any scenarios, and it is overly complex to explain. See the footnote for details if you are interested.","s":"Solution 2: Move the Server to the Background","u":"/part-2-core-skills/job-control","h":"#solution-2-move-the-server-to-the-background","p":222},{"i":233,"t":"If you have a job in the background, you can bring it back to the foreground with the fg ('foreground') command. Let's show the jobs, with the jobs command: $ jobs[1] + running python -m SimpleHTTPServer 3000 Here I have a background job running a server. Any one of the following commands will bring it back to the foreground: fg %1 # Explicitly bring Job 1 into the foreground%1 # ...or in shorthand, just enter the job id...fg # ...if not given an id, fg and bg assume the most recent job. Now the job is in the foreground, and you can interact with the process again however you like.","s":"Moving Background Jobs to the Foreground","u":"/part-2-core-skills/job-control","h":"#moving-background-jobs-to-the-foreground","p":222},{"i":235,"t":"You might realise you cannot continue what you are doing because an old job is still running. Here's an example: I tried to run my web server, but there was still one running as a background job. The server failed to start because the port is in use. To clean it up, I run the jobs command to list the jobs: $ jobs[1] + suspended python -m SimpleHTTPServer 3000 There's my old web server. Note that even though it is suspended, it'll still be blocking the port it is serving on4. The process is paused, but it is still holding onto all of the resources it is using. Now that I know the job identifier (%1 in this case), I can kill the job: $ kill %1[1] + 22843 terminated python -m SimpleHTTPServer 3000 This is why job identifiers start with a percentage sign! The kill command I have used is not a special job control command (like bg or fg). It is the normal kill command, which terminates a process. But shells that support job control can normally use a job identifier in place of a process identifier. So rather than working out what the process identifier is that I need to kill, I can just use the job identifier5.","s":"Cleaning Up Jobs","u":"/part-2-core-skills/job-control","h":"#cleaning-up-jobs","p":222},{"i":237,"t":"Avoid jobs. They are not intuitive to interface with and they suffer from some challenges. The most obvious one is that all jobs write to the same output, meaning you can quickly get garbled output like this: This is what happens when I run a job, which just outputs text every second. It's in the background, but it's printing all over my commands. Even running the jobs command to try and find the job to stop it is difficult. Input is even more complex. If a job is running in the background, but requires input, it will be silently suspended. This can cause confusion. Jobs can be used in scripts but must be done so with caution and could easily confuse a consumer of the script if they leave background jobs hanging around, which cannot be easily cleaned up6. Handling errors and exit codes for jobs can be problematic, causing confusion, poor error handling, or overly complex code. If jobs should be avoided, why discuss them at all? Well sometimes you move things into the background by mistake, sometimes it can be useful to quickly shift a download or slow command into the background, and also if you are going to avoid something it's good to know why! And the challenge of managing multiple units of work on a computer has been around for a long, long time, with jobs as one of the tools in the toolkit to deal with the challenge. But given I'd suggest to avoid jobs, let's summarise with the most key takeaways and some alternatives.","s":"Why You Shouldn't Use Jobs","u":"/part-2-core-skills/job-control","h":"#why-you-shouldnt-use-jobs","p":222},{"i":239,"t":"If there are two things to take away, they would be: If you have started running a command in the foreground, and you don't want to stop it and would rather move it to the background, hit Ctrl+Z. Then Google \"job control\". And: If you think there is a job running in the background, and it is messing with your screen, type fg to bring it to the front and kill it with Ctrl+C. Repeat as needed! In either case, if you need to do something more subtle, you can return to this reference. But the first command should allow you to get your shell back while you work out how to continue the job, and the second should kill a background job that is messing with your screen.","s":"The Most Key Takeaways","u":"/part-2-core-skills/job-control","h":"#the-most-key-takeaways","p":222},{"i":241,"t":"If you are using any kind of modern terminal such as iTerm, Terminal or the GNOME Terminal, just open a new tab or split! Much easier. The benefit to this is that each tab gets its own standard input and output, so there's no risk of overwriting. And of course you can hide/reveal/rearrange the tabs however you like. The traditional alternative to a job for an operator who simply wants more than one thing going on at once would be a terminal multiplexer, such as screen or tmux: Multiplexers work in a very similar way to a modern graphical terminal - they manage many shell instances. But there are some differences. Modern terminals, such as iTerm, tend to have more intuitive GUIs and a lot of features. Multiplexers can be stateful - and manage work even when you close the shell (allowing you to 're-attach' later. We can also run them over SSH sessions to manage complex operations on remote machines. They run a client-server model, meaning many people can work with many multiplexed processes (and they can persist beyond sessions). My personal preference is both - I use a modern terminal and run everything inside it in tmux, which is a very common multiplexer (and in some ways the spiritual successor to screen, an older multiplexer). We'll look at both of these options in later chapters.","s":"Alternatives to Jobs","u":"/part-2-core-skills/job-control","h":"#alternatives-to-jobs","p":222},{"i":243,"t":"You might find that jobs are useful, or you might find that they are not. Either way, here's a quick reference of some common commands: Command Usage command & Run the command as a background job. Move the current process into a background job, suspended. jobs List all jobs. fg %1 Move background job number 1 into the foreground. bg %1 Continue background job number 1. kill %1 Terminate job number 1. wait %1 Block until job number 1 exits. If you want to find out more about the gory details of jobs, the best place to start is the Bash Manual - Job Control Section, or the 'Job Control' section of your preferred shell's manual. On Bash you can find this by using man bash and searching for the text JOB CONTROL. You can find out more about how to get help in Chapter 5 - Getting Help If you are not a heavy shell user, this might seem unlikely. But if you do a lot of work in shells, such as sysadmin, devops, or do your coding from a terminal, this happens all the time!↩ Technically, SIGTSTP signal - which is 'TTY stop'. If you have always wondered about the 'TTY' acronym, check the chapter, Interlude: Understanding the Shell.↩ The alternative method is to use Ctrl+Y, which will send a delayed interrupt, which will continue to run the process until it tries to read from stdin. At this point, the job is suspended and the control given to the shell. The operator can then use bg or kill or fg to either move to the background, stop the process, or keep in the foreground as preferred. See: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Job-Control↩ Another super-useful snippet: lsof -i -P -n | grep 8000 to find any process that has a given port open. Another one for the aliases chapter!↩ There are times this is needed. If a job runs many processes - for example, by running a pipeline - the process identifier will change as the command moves from one stage of the pipeline to the next. The job identifier will remain constant. Remember, a job is a shell command, so could run many processes.↩ To see how bad this can be, create a script that starts jobs, then run it. Then run the jobs command to see what is running. The output might surprise you!↩","s":"Quick Reference","u":"/part-2-core-skills/job-control","h":"#quick-reference","p":222},{"i":245,"t":"On this page","s":"Thinking in Pipelines","u":"/part-2-core-skills/thinking-in-pipelines/","h":"","p":244},{"i":247,"t":"Many of the programs we have seen so far follow a very similar pattern: In fact, when you get down to the details, there are very few programs which don't do something like this! As a more concrete example, we can look at the sort program - which sorts the input in alphabetic order: We can easily see this in action by just running sort in a shell. Start the sort program, enter some text, then press Ctrl+D. Ctrl+D (which is normally written as ^D is a special control character which means 'end of transmission' - in this case we use it to tell sort we've finished writing text. If you were to use ^C (which is the interrupt command) it would closes the sort program instead). $ sortDogschasecatsandcatschasemice Once you've entered the text you want to sort, hit ^D and you'll see the sorted output: Dogsandcatscatschasechasemice So by default, the sort command is reading input from the keyboard (until we send it a special message saying we're done), then writing the output to the terminal. In fact, sort is using two special files - stdin and stdout - but what does this mean?","s":"Input and Output","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#input-and-output","p":244},{"i":249,"t":"Every program has access to three 'special' files, stdin, stdout and stderr: stdin is short for 'standard input' - it's where many programs read their input from stdout is short for 'standard output' - it's where many programs write their output to stderr is short for 'standard error' - it's where some programs write error messages to Why do I say 'many' and 'some'? Well the reason is that while this is 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. Each of these files has a special number which is shown in grey in the diagram. This is known as the 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 ls -al /dev/std*: $ ls -al /dev/std*lr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stderr -> fd/2lr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stdin -> fd/0lr-xr-xr-x 1 root wheel 0 Jan 1 1970 /dev/stdout -> fd/1 If you are not familiar with ls (the list directory contents command) then check 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 /dev/ (short for device folder). We can also see the associated file descriptors. 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. When you are running programs in a shell, the shell attaches your keyboard to the program's standard input1, and attaches the standard output and standard error to the terminal display: 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. We can already see the beginnings of a pipeline here. There's a clear flow of data from the keyboard, through the stdin file, through the program, then through the output files, then to the display. Looking at some real programs in action will hopefully make this clearer!","s":"Standard Input, Output and Error","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#standard-input-output-and-error","p":244},{"i":251,"t":"Do you remember the cat command? It's the one which writes the contents of a file to the screen. For example: $ cat ~/effective-shell/text/simpsons-characters.txtArtie ZiffKirk Van HoutenTimothy LovejoyArtie ZiffNick RivieraSeymore SkinnerHank ScorpioTimothy LovejoyJohn FrinkCletus SpucklerRuth PowersArtie ZiffAgnes SkinnerHelen Lovejoy We saw in Chapter 4 - Becoming a Clipboard Gymnast that we could pipe the output of this command into the sort command to order it and then into the uniq command to remove duplicates, like this: $ cat ~/effective-shell/text/simpsons-characters.txt | sort | uniqAgnes SkinnerArtie ZiffCletus SpucklerHank ScorpioHelen LovejoyJohn FrinkKirk Van HoutenNick RivieraRuth PowersSeymore SkinnerTimothy Lovejoy The pipe operator (which is the vertical pipe symbol or |) has a very specific meaning in the shell - it attaches the stdout of the first program to the stdin of the second. This means we can now visualise the entire pipeline and see exactly what is going on: 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! 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 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. Now we'll look at stdin, stdout and 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.","s":"A Pipeline in Action","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#a-pipeline-in-action","p":244},{"i":253,"t":"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. 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. 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! The Shell You might just use code in the shell as input, for example: $ echo \"I am in the $PWD folder\" | sed 's/folder/directory/'I am in the /Users/dwmkerr/repos/github/dwmkerr/effective-shell directory Here we've just used echo to write out message including a variable and then used the sed (stream editor) program to replace the word folder with directory. We'll get a lot of practice with sed as we go through this book! Files We've already seen a few examples of using cat to write a file to stdout. A lot of the time we don't need to use 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: $ cat ~/effective-shell/text/simpsons-characters.txt | wc -l 14 Or more simply, like this: $ wc -l ~/effective-shell/text/simpsons-characters.txt 14 /Users/dwmkerr/effective-shell/text/simpsons-characters.txt In this case, we've passed the file path as an argument to the wc (word, line, character and byte count) program. But be aware - not all programs use the same convention or parameter names! Now here's a cool trick. Type rev < /dev/stdin, then enter some text and hit ^D or ^C when done. You should see something like this: $ rev < /dev/stdinRed RummuR deR What's going on here? Remember we mentioned that stdin is a special stream which represents input and that it lives at /dev/stdin? This little trick uses redirection to redirect the stdin file to the rev (reverse) command. The < operator redirects the standard input of a program to come from the given file. We could also have written cat /dev/stdin | rev. Or just enter rev and type in the input we want to reverse! The Clipboard In 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: $ pbpaste | rev | pbcopy This pipeline pastes the contents of the clipboard to stdout, which is piped to rev (reversing the text) and then pipes the output to pbcopy, which copies the results to the clipboard2. Filtered Input This is a trick a friend shared with me. He works with data scientists and whenever he shows them this command they love it! $ head -n 100 100GBFile.csv > 100linefile.csv The head (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 > symbol (the redirection symbol) means in the section lower down on Standard Output. You can also use tail in the same way to get the last lines from a file. And if you are a more advanced user, you might use something like this: $ grep -C 5 error /var/log/mylogfile.txt | less We'll see all of these commands as we go through the book, but this very cool trick uses the grep (file pattern searcher) command to search for the text error in the file /var/log/mylogfile.txt, shows five lines of context (-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 grep-ing as we go through the book so don't worry if this looks a little confusing for now. Many More! 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!","s":"Common Patterns - Standard Input","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#common-patterns---standard-input","p":244},{"i":255,"t":"Now let's look at some of the things we can do with the standard output: Some of these outputs are things we've seen before, but let's do a quick revision. Display This is what we've been doing a lot of so far. When you are working with the shell interactively this makes a lot of sense. If you have jobs which run in the 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. What about if you have a 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 pager. A pager is a program which makes it possible to interactively page through output in the shell, scrolling up and down, searching and so on. Try this out as an example: ls /usr/bin /usr/local/bin /usr/sbin | less You'll see something like this: 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 d and u keys to go down and up, or the / and ? keys to search forwards or backwards. Piping into your pager is a really useful trick - you can read more about pagers in Chapter 5 - Getting Help. File 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 > or redirection operator: $ echo \"Here's some data\" > some_file.txt It's as easy as that! Note that this will overwrite anything already in the file. Append What if you don't want to overwrite a file, but instead just add a new line? The >> or append redirection operator: $ echo \"Tuesday was good\" >> diary.txt$ echo \"Wednesday was better!\" >> diary.txt$ echo \"Thursday suuucks\" >> diary.txt$ cat diary.txtTuesday was goodWednesday was better!Thursday suuucks This example writes each line in turn to the diary.txt file, appending the text to the end of the file (and creating it if it doesn't already exist). Appending to a file is extremely useful for circumstances where you might want to build or update a log of events over time. Pipe 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! In this case, the output of our program becomes the input of the next one in the pipeline.","s":"Common Patterns - Standard Output","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#common-patterns---standard-output","p":244},{"i":257,"t":"We haven't actually seen stderr in action yet. Let's see how it works. $ mkdir ~/effective-shell/new-folder$ mkdir ~/effective-shell/new-foldermkdir: /home/dwmkerr/effective-shell/new-folder: File exists In the first call to 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. There are lots of ways to make text uppercase in the shell, let's use the tr (translate characters) program. Here's an example of how it works: $ echo 'Be quiet, this is a library!' | tr '[:lower:]' '[:upper:]'BE QUIET, THIS IS A LIBRARY! Now let's use it to shout out our error message: $ mkdir ~/effective-shell/new-folder | tr '[:lower:]' '[:upper:]'mkdir: /home/dwmkerr/effective-shell/new-folder: File exists In this case the output has not been made uppercase. What's going on? To understand, let's quickly review the three streams: When we are in the shell, the shell automatically writes the stderr stream to the screen. But the shell's pipe operator pipes stdout only - it is not piping our error output. And the mkdir command is writing this error message to stderr. The command we ran before: mkdir ~/effective-shell/new-folder | tr '[:lower:]' '[:upper:]' Actually looks like this: The pipe has piped the standard output to the tr program. But there is no standard output - the error message was written to standard error instead. The shell has still written it to the screen for us, but has not piped it to the tr program. So how do we deal with stderr? Here are some common options: Now this might be the ah-ha! moment if you have done some shell scripting before - some of these obscure sequences like 2>&1 might look familiar (even if it is just the thing you know you always have to Google to get right!). Let's take a quick look at some of these options. To Standard Output If we want to be able to pipe the error message to another command, we can use another redirection trick - we can redirect stderr to stdout. The characters 2>&1 look really obscure - let's break it down: Take the file with descriptor 2 - which is standard error Redirect it with the redirect symbol > - we saw this in the earlier section Redirect it into the file with descriptor (&) 1 - which is standard output Remember, there are three 'magic' files each process has access to: stdin, the standard input, which has the file descriptor 0 stdout, the standard output, which has the file descriptor 1 stderr, the standard error, which has the file descriptor 2 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: python <&1 | tr '[:lower:]' '[:upper:]'MKDIR: /HOME/DWMKERR/PLAYGROUND/NEW-FOLDER: FILE EXISTS Visually, what is happening is this: If you can wrap your head around this, the other options we showed for stderr might start to make a little more sense. A nice trick to remember the slightly obscure ampersand & which references a file descriptor - if you were to write this: cat some-file-that-might-not-exist 2>1 What would happen is that the shell would write stderr to a new file with the name 1! Why don't we need an ampersand before the > 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. To a File Before, we redirected to &2, which is 'the file with descriptor 2. We can also use a similar trick to redirect to any arbitrary file: mkdir ~/effective-shell/new-folder 2>./errors.txt This command just redirects all of the errors (remember, 2 is stderr) to a file called ./errors.txt. This is quite a common trick - run the program, but log the errors to a file for later review. To Nowhere What if we just don't want to see the errors at all? Well there's a special file called /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! mkdir ~/effective-shell/new-folder 2>/dev/null This just redirects all errors to the black hole of /dev/null - we won't see them on the screen or anywhere else. This is a common way to 'silence' errors3 in shell commands. Notice how we're starting to see patterns? This is just redirection, the same tricks we saw for stdout, but we're explicitly redirecting stderr (file descriptor 2). If we don't tell the shell what to redirect, it assumes stdout by default. So if we can redirect, can we append too? Append Yes! Just like we did with stdout, there's nothing stopping us appending to a file: mkdir ~/effective-shell/new-folder 2>>./all-errors.log Just like before, we use >> which means append (rather than overwrite or create). All to a File This is a really important subtlety. If you want to write both stdout and stderr to a file, you might try this: ls /usr/bin /nothing 2>&1 > all-output.txt If you run this command, you'll get stdout written to all-output.txt, but the error message cannot access '/nothing' is written to the screen, not the file. Why is this? Bash (and most bash-like shells) process redirections from left to right, and when we redirect we duplicate the source. So breaking this down: 2>&1 - duplicate file descriptor 2 (stderr) and write it to 1 - which is currently the terminal! > all-output.txt - duplicate file descriptor 1 (stdout) and write it to a file called all-output.txt To write everything to the file, try do this: ls /usr/bin /nothing > all-output.txt 2>&1 This will work. Breaking it down: Redirect stdout to the file all-output.txt Now redirect stderr to stdout - which by this point has already been redirected to a file This can be tough to remember so it's worth trying it out4. There are many variations you can play with and we'll see more as we go through the book.","s":"Common Patterns - Standard Error","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#common-patterns---standard-error","p":244},{"i":259,"t":"This is a long chapter, but I can't talk about pipelines without briefly mentioning the T pipe. Check out this command: cat ~/effective-shell/text/simpsons-characters.txt | sort | tee sorted.txt | uniq | grep '^A' This command sorts the list of Simpsons characters, removes duplicates and filters down to ones which start with the letter A. And it has the tee command in the middle. What does this do? Well the tee command is like a T-pipe in plumbing - it lets the stream of data go in two directions! The sorted.txt file contains the sets of characters after the sort operation, but before the unique and filter operation. Visually, it does this: 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.","s":"One Last Trick - The T Pipe","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#one-last-trick---the-t-pipe","p":244},{"i":260,"t":"Once you get comfortable with pipelines, a whole world of possibilities open up. 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: cat data.dat | sort | uniq | grep -v '^#' | wc -l I didn't have to find a special program which does exactly what I needed5 - 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: cat data.dat - OK, first I need to write out the file sort - now I can sort it, that'll put all the blank lines together uniq - this'll remove all of those duplicate blank lines, although it still leaves one blank one at the top! grep -v '^#' - this should get rid of all the lines which start with # wc -l - this'll count the number of lines I'm left with 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. Of course, as things get more complex you might want to build scripts, or use a programming language, or other methods, but this 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.","s":"Thinking in Pipelines","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#thinking-in-pipelines","p":244},{"i":262,"t":"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. 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! In this chapter we looked at: How each program has access to three 'standard' streams - one for input, one for output and one for reporting errors The standard input stream is available as a file at /dev/stdin, is often called stdin in programming languages, and always has the special file descriptor 0 The standard output stream is available as a file at /dev/stdout, is often called stdout in programming languages, and always has the special file descriptor 1 The standard error stream is available as a file at /dev/stderr, is often called stderr in programming languages, and always has the special file descriptor 2 The Ctrl+D sequence means 'end of transmission' - we can use it to signal that we have completed putting our input into stdin... ...but the Ctrl+C sequence means 'interrupt' and is normally used to force a program to close We can pipe the output of one program to the input of another with the pipe | symbol We can redirect a file to the standard input of a program with the < operator We can redirect the standard output of a program to create or overwrite a file with the > operator We can redirect the standard output of a program to create or append to a file with the >> operator We can redirect the standard error of a program to its standard output with 2>&1 We can redirect the standard error of a program to another file (such as the 'null' file) with 2>/dev/null We can redirect the standard error of a program to create or append to a file, just like with standard output, using the >> operator We also briefly saw some commands: sort sorts text sed can replace content in text tr can replace parts of text wc can count words or lines of text tee takes the input stream and sends it straight to the output, but also to a file (like a T-pipe in plumbing) grep can filter lines These programs can do a lot more and are workhorses we'll see in more detail through the book. 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: Writing Good Programs - How to write programs which use stdin, stdout and stderr sensibly The Unix Philosophy - Why we have so many small simple programs which we can pipe together Streams in Detail - How streams like stdin actually work, especially with things like line endings, command sequences like ^D and so on Signals - A little more on Signals (such as ^C and ^D) When these chapters are published I'll update the links here. If you want to be updated when new chapters are published, you can Join the Mailing Lits on the Homepage. Technically there is another layer here, which is the tty. You can see this by running tty in the shell. We'll talk more about this in the Interlude - What is a Shell section.↩ Check Chapter 4 - Becoming a Clipboard Gymnast for how to do this on a Linux or Windows machine.↩ Although always use tricks like this with caution! If we had a different error, perhaps one we really do want to know about, we would lose the message in this case.↩ There is a very detailed explanation of this behaviour at https://linuxnewbieguide.org/21-and-understanding-other-shell-scripts-idioms/.↩ With the correct options, sed could likely do this in a single operation, but I'd probably spend a lot longer Googling the right options for it!↩","s":"Summary","u":"/part-2-core-skills/thinking-in-pipelines/","h":"#summary","p":244},{"i":264,"t":"On this page","s":"What is a Shell?","u":"/part-2-core-skills/what-is-a-shell","h":"","p":263},{"i":266,"t":"It might come as a surprise that 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: 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 shell, and it has been a common way to interface with computers ever since the first screens and keyboards were created. 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: (By Source (WP:NFCC#4), Fair use, https://en.wikipedia.org/w/index.php?curid=58853841) Why would people choose to use such an archaic interface as a shell? Typing is fast: A skilled shell user can manipulate a system at dazzling speeds just using a keyboard. Typing commands is generally much faster than exploring through user interfaces with a mouse Shells are programmable: Users will often being programming as they work in a shell, creating scripts to automate time-consuming or repetitive processes Shells are 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. 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.","s":"Introduction for the Non-Technical Reader","u":"/part-2-core-skills/what-is-a-shell","h":"","p":263},{"i":268,"t":"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 tty? How do shells really work? Hopefully as you read this chapter you'll discover something that you didn't know about shells.","s":"Introduction for the Technical Reader","u":"/part-2-core-skills/what-is-a-shell","h":"","p":263},{"i":270,"t":"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!","s":"Let's Get Started!","u":"/part-2-core-skills/what-is-a-shell","h":"","p":263},{"i":272,"t":"The diagram below shows a simplified view of a typical computer: Already there's a lot going on. Your computer is going to have a CPU1 and memory2, and almost certainly a network adapter3 and display adapter4. 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.","s":"A Computer in a Nutshell","u":"/part-2-core-skills/what-is-a-shell","h":"#a-computer-in-a-nutshell","p":263},{"i":274,"t":"The operating system is the piece of software installed on a computer that can interface with the 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 software interface to it. 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. The operating system is generally broken down into two parts - the kernel and user space: Let's look at these in more detail.","s":"The Operating System","u":"/part-2-core-skills/what-is-a-shell","h":"#the-operating-system","p":263},{"i":276,"t":"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. Software running in the kernel has direct access to resources, so is 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. 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. (By Kkchaudhary11 - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=49069244) The operating system kernel really is the 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 possible5. And that is where user space comes in.","s":"The Kernel","u":"/part-2-core-skills/what-is-a-shell","h":"#the-kernel","p":263},{"i":278,"t":"The vast majority of programs run in 'user space' (also commonly called 'user land'). When a program starts, the kernel will allocate it a private segment of memory and provide 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. For example, a program running in user space can use the standard fopen function, which is provided on almost every operating system as part of the 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 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. Now that the key components have been introduced, we can look at the shell. The name should come as no surprise, as it is a wrapper or outer layer to the operating system (which itself contains the sensitive nugget of the kernel).","s":"User Space","u":"/part-2-core-skills/what-is-a-shell","h":"#user-space","p":263},{"i":280,"t":"So what is the shell? The shell is just a general name for any user space program that allows access to resources in the system, via some kind of interface. 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. For example, to see all of the files in a folder, the human operator 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. Here's an example, where a shell is being used to show the 'png' images in the folder I am working in6: 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, tree is a program). Let's pick these apart. Here's a diagram that more accurately shows what is going on: Let's dissect this bit by bit.","s":"The Shell","u":"/part-2-core-skills/what-is-a-shell","h":"#the-shell","p":263},{"i":282,"t":"We're not directly interacting with the 'shell' in this diagram. We're actually using a 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 terminal. 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. Why the word 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 Teletype Terminal7 became a common way for users to interface with computers. (Photograph by Rama, Wikimedia Commons, Cc-by-sa-2.0-fr, CC BY-SA 2.0 fr, https://commons.wikimedia.org/w/index.php?curid=17821795) At this time, computers were very large, complex, and expensive machines. It was common to have 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: (By ClickRick - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=6693700) 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. 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 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 secure shell - which is a secured-shell connection over a network. 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 shell program - a program that we can use to operate the computer. 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 gnome-terminal for Linux, and iTerm2 and Hyper. There's a bunch of screenshots of different setups at the end of the article.","s":"The Terminal","u":"/part-2-core-skills/what-is-a-shell","h":"#the-terminal","p":263},{"i":284,"t":"Now that we've described the terminal, we can go back and look at the shell in detail. 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. 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. 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. So what else does a shell do? Most of the features are related to helping human operators work with the system more efficiently. Quickly enter commands, see the history of commands and quickly restructure commands (see Chapter 8 - Fly on the Command Line) Navigate through the file system, moving from folder to folder (see Chapter 1- Navigating Your System), which makes it easier for an operator to navigate the file system Chain the output of commands together - for example, taking the output of one basic program, such as the tree program we saw, and writing it to a file (see Chapter 7 - Thinking in Pipelines) Offer a programming language, allowing the operator to perform more complicated tasks 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.","s":"Back to the Shell","u":"/part-2-core-skills/what-is-a-shell","h":"#back-to-the-shell","p":263},{"i":286,"t":"The last part of the diagram, which we haven't covered yet, is the command prompt. When a shell is running in 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 they have to enter commands, the shell will output some kind of prompt. 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. 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. Shell command prompts can be customised, so they will often look different from machine to machine. Below is an example that shows a lot of technical information. This is from the highly popular oh-my-zsh framework for the 'Z Shell' shell, which is very popular among developers: *(Source: https://ohmyz.sh/)","s":"The Command Prompt or Command Line","u":"/part-2-core-skills/what-is-a-shell","h":"#the-command-prompt-or-command-line","p":263},{"i":288,"t":"A lot of the 'commands' in a shell, such as 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. Some commands, such as 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 Chapter 10 - Understanding Commands). Commands will often differ between shells. 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. On most Unix-like systems, the default shell is a program called 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. 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. Let's review the earlier diagram again: We can see the real internals of what is going on in this \"Terminal -> Shell -> Program\" chain in the diagram above quite easily. Try the command pstree -psa $$ in a shell8: The first systemd process is the primary process for the OS - it is process number 1, which initialises everything else. The second 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. What is interesting is that we can see a terminal (the gnome terminal), which has started my preferred shell (which is zsh), which is running a command (the program pstree). Here we can see the exact chain as shown in the diagram earlier.","s":"Shell Commands and Different Shells","u":"/part-2-core-skills/what-is-a-shell","h":"#shell-commands-and-different-shells","p":263},{"i":290,"t":"These are the key technologies and concepts that surround a shell. If you are interested in more technical details of working with shells, then my 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. To close the article, below are some examples of different terminals, shells, command prompts and so on. Example: iTerm 2 / tmux / zsh​ In this example, we have: A MacOS operating system iTerm2 as the terminal program tmux running as a 'terminal multiplexer' (see Effective Shell: Terminal Multiplexers) zsh (Z Shell) as the shell program, using 'oh my zsh', which is easily recognised by the % sign in the command prompt. A customised command line, which shows the user and folder on one line, with only the % symbol below, to leave lots of space for the input commands9. Example: Bash​ In this example, we have: A Linux operating system (Ubuntu 14) The gnome terminal bash as the shell In the second screenshot, the user has 'root privileges', and to indicate this, bash helpfully changes the default command prompt from a dollar sign to a hash sign Example: Windows Explorer​ In this example, we have: The Windows 10 operating system No terminal The explorer.exe program showing us a graphical shell This looks different from previous examples. The program, which shows the familiar Windows interface, 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 Shell Library. I also maintain a popular library for building extensions to the graphical Windows shell - sharpshell. Example: Windows Command Prompt​ In this example, we have: The Windows 10 operating system The command prompt terminal and shell In Windows, the terminal and shell are combined into a single cmd.exe program. There's an excellent article on the internals - Microsoft DevBlogs: Windows Command-Line: Inside the Windows Console Example: Windows PowerShell​ In this example, we have: The Windows 10 operating system The PowerShell terminal 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. Example: Windows Subsystem for Linux (WSL)​ In this example, we have: The Windows 10 operating system The Bash.exe program This screenshot, from 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.","s":"That's a Wrap!","u":"/part-2-core-skills/what-is-a-shell","h":"#thats-a-wrap","p":263},{"i":292,"t":"If you enjoyed this article, please do share it! Feel free to include suggestions, improvements or corrections in the comments below. Useful References A simple Linux kernel module, showing how basic kernel programming works in Linux: github.com/dwmkerr/linux-kernel-module How Linux Works - Brian Ward StackExchange: What is the exact difference between a 'terminal', a 'shell', a 'tty', and a console? Microsoft: Inside the Windows Console Footnotes 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).↩ 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 ephemeral - everything is gone when you turn off the power to it.↩ 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.↩ This is the part of your computer you plug the screen into.↩ This is because a mistake in 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.↩ 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 - github.com/dwmkerr/dotfiles.↩ And that's where the 'TTY' acronym you will see sometimes comes from. Enter the 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.↩ $$ is a Bash internal variable. These will also be covered in a later article in the series.↩ Feel free to see my dotfiles to configure a similar setup for yourself.↩","s":"Share and Discuss","u":"/part-2-core-skills/what-is-a-shell","h":"#share-and-discuss","p":263},{"i":294,"t":"On this page","s":"Understanding Commands","u":"/part-2-core-skills/understanding-commands","h":"","p":293},{"i":296,"t":"This is really important to understand! A command in a shell is something you execute. It might take parameters. Generally it'll have a form like this: command param1 param2 We've already seen many commands during this series: ls # Show the contents of the current directorycd ~ # Move to the user's homecat file.txt # Output the contents of 'file.txt' to stdout But to be an effective shell user, you must understand that not all commands are created equal. The differences between the types of commands will affect how you use them.","s":"What Are Commands?","u":"/part-2-core-skills/understanding-commands","h":"#what-are-commands","p":293},{"i":298,"t":"There are four types of commands in most shells: Executables \"Built-Ins\" (which we'll just call builtins from now on) Functions Aliases Each is different and has its own quirks. Let's quickly dig in and see a bit more.","s":"The Different Types of Commands","u":"/part-2-core-skills/understanding-commands","h":"#the-different-types-of-commands","p":293},{"i":300,"t":"Executables are just files with the 'executable' bit set1. If I execute the cat command, the shell will search for an executable named cat in my $PATH. If it finds it, it will run the program. $ cat file.txtThis is a simple text file What is $PATH? $PATH is the standard environment variable used to define where the shell should search for programs. If we temporarily empty this variable, the shell won't find the command: $ PATH=\"\" cat file.txtbash: cat: No such file or directory Normally your $PATH variable will include the standard locations for Linux programs - folders such as /bin, /sbin, /usr/bin and so on2. If you were to print the variable, you'd see a bunch of paths (they are separated by colons; I've put them on separate lines for readability): /usr/local/bin/usr/bin/bin/usr/sbin/sbin The shell will start with the earlier locations and move to the later ones. This allows local flavours of tools to be installed for users, which will take precedence over general versions of tools. There will likely be other locations too - you might see Java folders, package manager folders and so on.","s":"Executables - Programs","u":"/part-2-core-skills/understanding-commands","h":"#executables---programs","p":293},{"i":302,"t":"Imagine we create a text file called dog in the local folder that looks like this: #!/bin/shecho \"🐢 woof 🐢\" This is a shell script (you've heard this before, but we'll see a lot more of these as we go through the book!). We mentioned that executables are any files which have the executable bit set. Let's actually do this, using the chmod (change file modes) command: $ ls -l dog-rw-r--r-- 1 dwmkerr staff 32 Oct 8 22:44 dog$ chmod +x dog$ ls -l dog-rwxr-xr-x 1 dwmkerr staff 32 Oct 8 22:44 dog I've used ls -l dog to show the file permissions of dog before and after the chmod +x dog3 command. We can see that there are some new x's in the first section. These are saying that the file is now executable by all users. Now that we have made the file executable we can run this just like any other program - as long as we tell the shell to look for programs in the current directory: $ PATH=\".\" dog🐢 woof 🐢 By the way - don't do this! Adding the special . directory to the path is generally not a safe or sensible thing to do, this is just a demonstration of how it works. More common would be to run the program by specifying the path to the file, like so: $ ./dog🐢 woof 🐢 Another option would just be to move it to a standard location that the shell already checks for programs: $ mv dog /usr/local/bin$ dog🐢 woof 🐢 Executables don't have to be compiled program code, they can be scripts. If a file starts with #! (the 'shebang'), then the system will try to run the contents of the file with the program specified in the shebang. We will look at shebangs in greater detail in a later chapter. But the key takeaway here is that we can also have executable scripts as commands.","s":"Executables - Scripts","u":"/part-2-core-skills/understanding-commands","h":"#executables---scripts","p":293},{"i":304,"t":"OK, so we've seen executables. What about a command like this? local V=\"hello\" echo $V You will not find the local executable anywhere on your system. It is a builtin - a special command built directly into the shell program. Builtins are often highly specific to your shell. They might be used for programming (local for example is used to declare a locally scoped variable), or they might be for very shell-specific features. This is where we need to take note. As soon as you are running a builtin, you are potentially using a feature that is specific to your shell, rather than a program that is shared across the system and can be run by any shell. Trying to programmatically execute local as a process will fail - there is no executable with that name; it is purely a shell construct. So how do we know if a command is a builtin? The preferred method is to use the type command: $ type locallocal is a shell builtin The type command (which is itself a builtin!) can tell you the exact type of shell command. Interestingly, you might be using more builtins than you think. echo is a program, but most of the time you are not executing it when you are in a shell: $ type -a echoecho is a shell builtinecho is /bin/echo By using the -a flag on type to show all commands that match the name, we see that echo is actually both a builtin and a program. Many simple programs have builtin versions. The shell can execute them much faster. Some commands are a builtin so that they can function in a sensible manner. For example, cd command changes the current directory - if we executed it as a process, it would change only the directory for the cd process itself, not the shell, making it much less useful. Echo is builtin because the shell can run much more quickly by not actually running a program if it has its own built in implementation. Builtins will vary from shell to shell, but many shells are 'Bash-like' - meaning they will have a set very similar to the Bash builtins, which you can see here: https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html As should be familiar from Chapter 5 - Getting Help, you can get help for builtins: $ man source # source is a builtinBUILTIN(1) BSD General Commands Manual BUILTIN(1)NAME builtin, !, %, # ...snip...SYNOPSIS builtin [-options] [args ...] However, the manual will not show information on specific builtins, which is a pain. Your shell might have an option to show more details - for example, in Bash you can use help: $ help sourcesource: source filename [arguments] Read and execute commands from FILENAME and return. The pathnames in $PATH are used to find the directory containing FILENAME. If any ARGUMENTS are supplied, they become the positional parameters when FILENAME is executed. But remember: help is a builtin; you might not find it in all shells (you won't find it in zsh, for example). This highlights again the challenges of builtins.","s":"Builtins","u":"/part-2-core-skills/understanding-commands","h":"#builtins","p":293},{"i":306,"t":"You can define your own shell functions. We will see a lot more of this later, but let's show a quick example for now: $ restart-shell () { exec -l $SHELL } This snippet creates a function that restarts the shell (quite useful if you are messing with shell configuration files or think you might have irreversibly goofed up your current session). We can execute this function just like any command: $ restart-shell And running type will show us that this is a function: $ type restart-shellrestart-shell is a functionrestart-shell (){ exec -l $SHELL} Functions are one of the most powerful shell constructs we will see; they are extremely useful for building sophisticated logic. We're going to see them in a lot more detail later, but for now it is enough to know that they exist, and can run logic, and are run as commands.","s":"Functions","u":"/part-2-core-skills/understanding-commands","h":"#functions","p":293},{"i":308,"t":"An alias is just a shortcut. Type in a certain set of characters, and the shell will replace them with the value defined in the alias. Some common commands are actually already aliases - for example, in my zsh shell, the ls command is an alias: % type -a lsls is an alias for ls -Gls is /bin/ls I make sure that when I use the ls command, the shell always expands it to ls -G, which colours the output. We can quickly define aliases to save on keystrokes. For example: $ alias k='kubectl' From this point on, I can use the k alias as shorthand for the kubectl command. Aliases are far less sophisticated than functions. Think of them as keystroke savers and nothing more, and you won't go far wrong.","s":"Aliases","u":"/part-2-core-skills/understanding-commands","h":"#aliases","p":293},{"i":310,"t":"So we now hopefully have a greater understanding of the variety of shell commands. Not all commands are executables, not all of the commands we think are executables necessarily are, and some commands might be more sophisticated. As a shell user, the key things to remember are: Executables are programs your system can use; your shell just calls out to them. Builtins are very shell-specific and usually control the shell itself Functions are powerful ways to write logic but will normally be shell-specific. Aliases are conveniences for human operators, but only in the context of an interactive shell. To find out how a command is implemented, just use the type -a command: $ type -a catcat is /bin/cat","s":"The Key Takeaways","u":"/part-2-core-skills/understanding-commands","h":"#the-key-takeaways","p":293},{"i":312,"t":"OK, for the masochistic few, you might be wondering about all of the other commands and utilities you may have seen that can tell you about programs and commands: what whatis which whence where whereis command type A lot of these are legacy and should be avoided, but for completeness sake, we'll go through them.","s":"More than You Need to Know","u":"/part-2-core-skills/understanding-commands","h":"#more-than-you-need-to-know","p":293},{"i":314,"t":"what reads out special metadata embedded in a program, generally used to identify the version of source code it was built from: $ what /bin/ls/bin/ls Copyright (c) 1989, 1993, 1994 PROGRAM:ls PROJECT:file_cmds-272.220.1 There should be almost no circumstance in which you need to use it in your day-to-day work, but you might come across it if you meant to type whatis.","s":"what","u":"/part-2-core-skills/understanding-commands","h":"#what","p":293},{"i":316,"t":"whatis searches a local help database for text. This can be useful in tracking down manual pages: $ whatis bashbash(1) - GNU Bourne-Again SHellbashbug(1) - report a bug in bash But I can't imagine it will be a regularly used tool by most users.","s":"whatis","u":"/part-2-core-skills/understanding-commands","h":"#whatis","p":293},{"i":318,"t":"which will search your $PATH to see whether an executable can be found. With the -a flag, it will show all results. $ which -a vi/usr/local/bin/vi/usr/bin/vi which originated in csh. It remains on many systems for compatibility but in general should be avoided due to potentially odd behaviour4.","s":"which","u":"/part-2-core-skills/understanding-commands","h":"#which","p":293},{"i":320,"t":"whence was added to the Korn shell. You are unlikely to use it unless you are on systems using ksh. zsh also has this command, but it should be avoided and considered non-standard. % whence brew/usr/local/bin/brew","s":"whence","u":"/part-2-core-skills/understanding-commands","h":"#whence","p":293},{"i":322,"t":"This is a shell builtin that can provide information on commands, similar to type: % where lsls: aliased to ls -G/bin/ls However, type should be preferred, as it is more standard.","s":"where","u":"/part-2-core-skills/understanding-commands","h":"#where","p":293},{"i":324,"t":"whereis is available on some systems and generally operates the same as which, searching paths for an executable: % whereis ls/bin/ls Again, type should be preferred for compatibility.","s":"whereis","u":"/part-2-core-skills/understanding-commands","h":"#whereis","p":293},{"i":326,"t":"command is defined in the POSIX standard, so should be expected to be present on most modern systems. Without arguments, it simply executes a command. With the -v argument, you get a fairly machine-readable or processable response; with the -V argument, you get a more human readable response: % command -v lsalias ls='ls -G'% command -V lsls is an alias for ls -G command can be useful in scripts, as we will see in later chapters.","s":"command","u":"/part-2-core-skills/understanding-commands","h":"#command","p":293},{"i":328,"t":"type is part of the Unix standard and will be present in most modern systems. As we've already seen, it will identify the type of command as well as the location for an executable: % type -a lsls is an alias for ls -Gls is /bin/ls This command can also be used to only search for paths: % type -p lsls is /bin/ls","s":"type","u":"/part-2-core-skills/understanding-commands","h":"#type","p":293},{"i":330,"t":"In summary, avoid anything that starts with 'w'! These are legacy commands, generally needed only when working on older Unix machines. type or command should be used instead. We will cover permissions and modes in later chapters.↩ Why these names and locations? It's a long story. The best place to start if you are interested is the Filesystem Hierarchy Standard.↩ chmod changes the mode of a file; +x means 'add the executable bit'. This tells the operating system the file can be executed.↩ Stack Exchange: Why not use β€œwhich”? What to use then?↩","s":"Summary","u":"/part-2-core-skills/understanding-commands","h":"#summary","p":293},{"i":332,"t":"Part 3 - Manipulating Text and Streams 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. This means that you may find yourself regularly manipulating text, searching through text and working with text files. There are a lot of options for how to do this! In fact, there are so many options that it can be a bit overwhelming to know what is the right tool for the job. In this section we'll look at some of the key techniques which can be used to work with text, and demonstrate this with practical examples. In each chapter I'll try to show lots of real world use cases to keep things as applicable to usual tasks as possible. Hopefully, by the time you have completed this section, you will have a great understanding of the tools available to you and how to apply them to the task at hand.","s":"Part 3 - Manipulating Text and Streams","u":"/part-3-manipulating-text/","h":"","p":331},{"i":334,"t":"On this page","s":"Regex Essentials","u":"/part-3-manipulating-text/regex-essentials/","h":"","p":333},{"i":336,"t":"Regular Expressions (or 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. 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: (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-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-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\]) Note that I have split the expression into multiple lines so that it fits on the page. This is 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. 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. So should we learn regular expressions? Are they even valuable if they are as complex as the example above?","s":"An Introduction to Regular Expressions","u":"/part-3-manipulating-text/regex-essentials/","h":"#an-introduction-to-regular-expressions","p":333},{"i":338,"t":"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 start simple and add complexity only if you need it. 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 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. 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: Create a small list of valid email address Add some items to the list which look 'kind of' valid but are not quite right Build a regular expression which matches the correct email address Refine the expression to eliminate the invalid addresses In most cases this will be sufficient. Let's start with the following set of addresses: dave@effective-shell.comdave@effective-shellto: dave@effective-shell.comdave@effective-shell.com test123.effective-shell.com@yahoo.comwhatever123@πŸ˜‚.comdave@kerr@effective.shell.com 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).","s":"Managing Complexity with Regular Expressions","u":"/part-3-manipulating-text/regex-essentials/","h":"#managing-complexity-with-regular-expressions","p":333},{"i":340,"t":"Here's how I would start building a regex for an email address: Any set of characters Followed by an @ at symbol Followed by any set of characters That regex would look like this: .*@.* The first bit, .* means 'any character' (this is what the . dot symbol means), 'any number of times (this is what the * asterisk symbol means). The second bit is just the literal @ at symbol character. The third bit is the same as the first - any characters any number of times. Let's see how that would look in regex101: 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. Now that we have a basic pattern which matches the valid address, we can refine it to eliminate invalid addresses. 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 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!","s":"Building Regexes - Start with the Basics","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---start-with-the-basics","p":333},{"i":342,"t":"The regular expression we have is very simple - .*@.*. 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. Let's see how we can refine this expression further to eliminate some of the invalid addresses. Let's start with @yahoo.com. It doesn't have anything before the at symbol. This is being matched by our pattern because our pattern allows any characters before and after the at symbol any number of times - including zero times. 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 quantifier (a 'quantifier' is the part of a pattern which says 'how many occurrences of the characters do we expect). Previously we used the * asterisk quantifier (which means 'any number of times'). Now we'll use the + plus quantifier (which means 'at least one time'). Let's see how it looks: .+@.+ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com This is better - we've eliminated some invalid addresses, test123.effective-shell.com, @yahoo.com and dave@. We have introduced a key concept - the quantifier. The quantifier we have used is the + plus symbol. This is the part of a regular expression which says 'how many times can a character be matched?'. There are a few different quantifiers, here is a quick reference: Quantifier Meaning * Any number of characters. + At least one character. ? Between zero and one character. {10} Exactly ten occurrences of the character. {10,} Ten or more occurrences of the character. {10,20} Between ten and twenty occurrences of the character. Now let's look at the character itself.","s":"Building Regexes - Quantifiers","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---quantifiers","p":333},{"i":344,"t":"When we are matching text, we match a set of characters a number of times. The set of characters we match can be a character set (which is when we explicitly say what is allowed), or a metacharacter (which is a predefined character set). This concept is far easier to explain with an example. Let's look at the address dave@kerr@effective.shell.com. This is clearly invalid, it has two at symbols. We can use character sets or metacharacters to fix this. The reason this address matches our expressions is that we are using the . dot metacharacter before and after the at symbol. The dot metacharacter means 'any character' (except a newline). This includes the at symbol character. 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. Character Sets - Ranges 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: [A-Za-z0-9] 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: [A-Za-z0-9]+@[A-Za-z0-9]+ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com This fails to match the valid email address 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. Let's see how we can do better. Character Sets - Special Characters We can add more characters to our character set. To include the dot and the hyphen, we just add them directly to the set: [A-Za-z0-9-.] That's all there is to it! We can now see our pattern is more correct: [A-Za-z0-9-.]+@[A-Za-z0-9-.]+ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com However, the expression is getting larger and larger. We can use a 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: \\w+@\\w+ \\w+@\\w+ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com The uses the 'word' metacharacter, \\w. This is just a shorthand for [a-zA-Z0-9_]. But now our pattern fails again as the \\w metacharacter doesn't include the hyphen or the dot. We can fix this easily - a character set can include metacharacters! So we can just combine \\w and the hyphen and dot: [\\w+-.]+@[\\w+-.]+ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com Character Sets - Negating Characters We can use the ^ circumflex symbol to negate a character. This allows us to build a character set which doesn't match a pattern. For example, we could rewrite our pattern like this: [\\S^@]+@[\\S^@]+ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com We've used the character set [\\S^@] which means 'any none-whitespace character' (this is the \\S part) and 'not the at symbol character' (this is the ^@ part). Notice that in this case we have more matches - because the set of characters we are using is larger than a set such as \\w. This expression now covers the email address with the emoji, because the emoji is not a whitespace character or an at symbol. Character Sets - Escaping Characters 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. Here's an example: [\\[\\]]+ This matches square brackets between one and many times. [\\^]\\+ 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 literal character, escape it by putting a slash in front of it. Character Sets - Quick Reference We've seen quite a few character sets and metacharacters, here's a quick reference for some commonly used ones: Quantifier Meaning . Any character except for a line break. \\w Any 'word' character, [a-zA-Z0-9_]. \\W Any non 'word' character. \\s Any 'whitespace' character (space, tab, etc). \\S Any non 'whitespace' character. \\d Any 'digit' character [0-9]. \\D Any non 'digit' character. There are many other metacharacters, you can find more in the manpage man re_pattern","s":"Building Regexes - Character Sets and Metacharacters","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---character-sets-and-metacharacters","p":333},{"i":346,"t":"At the moment, if we use the expression below: [\\S^@]+@[\\S^@]+ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com Then we match any line which 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? For this, we can use anchors. Anchors represent special parts of a string, such as the start or beginning of a line. If we want to only match lines which contain a complete email address, we can use the ^ circumflex (start of line) anchor and $ dollar (end of line) anchor: ^[\\S^@]+@[\\S^@]+$ dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com 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 starts with the letters to: we could just use this: ^to: .* dave@effective-shell.com dave@effective-shell to: dave@effective-shell.com dave@effective-shell.com test123.effective-shell.com @yahoo.com dave@ whatever123@πŸ˜‚.com dave@kerr@effective.shell.com 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.","s":"Building Regexes - Anchors","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---anchors","p":333},{"i":348,"t":"You can extract only part of what you match in a regular expression using 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. Here's an example: (.+)@(.+) dave@effective-shell.com Now the entire line matches, but everything surrounded by () parentheses is a capture group. This means that the regular expression has actually made three matches: dave@effective-shell.com - The first match in an expression is always the complete match dave - This is the first capture group, everything before the at symbol effective-shell.com - This is the second capture group, everything after the at symbol. 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.","s":"Building Regexes - Capture Groups","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---capture-groups","p":333},{"i":350,"t":"Regular expressions can be lazy or greedy. Whether an expression is lazy or greedy affects how it matches patterns - basically whether it stops at the earliest point the pattern is matched, or continues until the pattern no longer matches. Regular expressions are 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: <.+> This text is bold. The regular expression <.+> 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 last angled bracket on the line - i.e. everything up until the closing tag. We can create a lazy expression by using the ? 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: <.+?> This text is bold. In this example we have actually captured two results - the contents of the opening and closing tag. Because the expression <.+?> is lazy it matches only until the first closing brace it finds, meaning that the results are quite different. To get the same results without using the lazy quantifier, we'd have to have an expression like this: <[^>]+> This text is bold. 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.","s":"Building Regexes - Lazy and Greedy Expressions","u":"/part-3-manipulating-text/regex-essentials/","h":"#building-regexes---lazy-and-greedy-expressions","p":333},{"i":352,"t":"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. 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! I am not going to show examples of each of these concepts. I'll explain why after a brief summary of the concepts. 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. 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 exploits1. In short - very broad and greedy expressions such as .+ (match 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. Lookarounds are special constructs which allow you to essentially say \"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 not preceded or followed by another pattern\". As an example, the expression \\d+(?=€) matches digits (this is the \\d metacharacter), at least one or more (this is the + plus symbol), but only if the digits are followed by a Euro symbol. In this case the (?=€) part of the pattern is a 'positive lookahead'. I have not yet found a situation where I've really needed lookaround expressions. For example, I would simply write the above expression as: (\\d+)€ Which simply matches digits which precede a Euro symbol and puts them in a capture group. 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. 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 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.","s":"Avoiding Advanced Topics - Backtracking, Lookarounds and Atomic Grouping","u":"/part-3-manipulating-text/regex-essentials/","h":"#avoiding-advanced-topics---backtracking-lookarounds-and-atomic-grouping","p":333},{"i":354,"t":"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. 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). 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! 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 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. 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!","s":"A Word of Warning","u":"/part-3-manipulating-text/regex-essentials/","h":"#a-word-of-warning","p":333},{"i":356,"t":"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 learn regular expressions. 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. There is a fascinating write up of how this led to a severe Cloudflare outage in 2019 available online at: https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/↩","s":"Summary","u":"/part-3-manipulating-text/regex-essentials/","h":"#summary","p":333},{"i":358,"t":"On this page","s":"Advanced Text Manipulation","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"","p":357},{"i":360,"t":"The stream editor command takes input from a 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 stdout. This command is extremely powerful - there are many sophisticated transformations which can be applied. Seeing 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!","s":"Introducing the Stream Editor","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#introducing-the-stream-editor","p":357},{"i":362,"t":"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 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 sed. Downloading the Samples Run the following command in your shell to download the samples: curl effective.sh | sh","s":"Transformations with Sed","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#transformations-with-sed","p":357},{"i":364,"t":"The samples folder has a script which copies some configuration files to a backup folder. Let's take a quick look at it: $ cd scripts$ cat backup-config.sh#!/usr/bin/env bash# Make sure we have a backup directory.mkdir ~/backup# Copy over alicloud, aws, azure, gcp and ssh config and credentials.cp ~/.aliyun/config.json ~/backup/settings/aliyun/cp ~/.aws/config ~/backup/settings/aws/cp ~/.aws/credentials ~/backup/settings/aws/cp ~/.azure/config ~/backup/settings/azure/cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/cp ~/.ssh/config ~/backup/settings/ssh/cp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ We could use the sed command to change the name of the folder we backup to. If we wanted to change the settings folder to configuration we could run the command below: $ sed 's/settings/configuration/' backup-config.sh#!/usr/bin/env bash# Copy over alicloud, aws, azure, gcp and ssh config and credentials.cp ~/.aliyun/config.json ~/backup/configuration/aliyun/cp ~/.aws/config ~/backup/configuration/aws/cp ~/.aws/credentials ~/backup/configuration/aws/cp ~/.azure/config ~/backup/configuration/azure/cp ~/.config/gcloud/credentials.db ~/backup/configuration/gcloud/cp ~/.ssh/config ~/backup/configuration/ssh/cp ~/.ssh/id_rsa ~/backup/configuration/ssh/ # is this safe?cp ~/.ssh/id_rsa.pub ~/backup/configuration/ssh/ This shows the basics of how sed works. We give sed an 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 stdin. Let's look at the expression in detail: s/settings/dotfiles/ The s indicates that we are going to run the substitute function, which is used to replace text The / indicates the start of the pattern we are searching for... ...settings is the pattern - which is just the literal text 'settings' The second / indicates the start of the replacement we will make when the pattern is found The final / indicates the end of the replacement - we can also optionally put flags after this slash Just as we saw in Chapter 13 - Get to Grips with Grep, we can provide a regular expression as the pattern to search for. By default, sed will use basic regular expressions. We can use extended regular expressions by providing the -E flag. Basic and Extended Regular Expressions If you are not sure about the difference between Basic and Extended Regular Expressions, you can use the: man re_pattern Command to get detailed documentation.","s":"Replacing Text","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#replacing-text","p":357},{"i":366,"t":"We can use the -e flag to supply multiple expressions to sed. If we wanted to delete our backup folders, we could run two substitutions - one which changes the cp command to rmdir command and one which deletes the first parameter, which was the source directory for cp but is not needed for rmdir1. Replacing Text on Specific Lines​ Let's start with the first expression, which should change cp to rmdir: $ sed -e 's/cp/rmdir/' backup-config.sh#!/usr/bin/env bash# Make sure we have a backup directory.mkdir ~/backup# Copy over alicloud, aws, azure, grmdir and ssh config and credentials.rmdir ~/.aliyun/config.json ~/backup/settings/aliyun/rmdir ~/.aws/config ~/backup/settings/aws/rmdir ~/.aws/credentials ~/backup/settings/aws/rmdir ~/.azure/config ~/backup/settings/azure/rmdir ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/rmdir ~/.ssh/config ~/backup/settings/ssh/rmdir ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?rmdir ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ We have provided a single expression with the -e parameter. If you are providing a single expression only then this parameter is superfluous, but we will shortly add another expression. This has changed all of the cp commands to rmdir. But did you notice the bug? It has also changed the letters cp in the comment which mentions gcp to rmdir, meaning our comment line has changed. I've made the changes bold to make this easier to see below:Before, it was: # Copy over alicloud, aws, azure, gcp and ssh config and credentials. Now it is: # Copy over alicloud, aws, azure, grmdir and ssh config and credentials. 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 cp: $ sed -e '/^cp/s/cp/rmdir/' backup-config.sh#!/usr/bin/env bash# Make sure we have a backup directory.mkdir ~/backup# Copy over alicloud, aws, azure, gcp and ssh config and credentials.rmdir ~/.aliyun/config.json ~/backup/settings/aliyun/rmdir ~/.aws/config ~/backup/settings/aws/rmdir ~/.aws/credentials ~/backup/settings/aws/rmdir ~/.azure/config ~/backup/settings/azure/rmdir ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/rmdir ~/.ssh/config ~/backup/settings/ssh/rmdir ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?rmdir ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ Let's look at the expression in detail. Before the s symbol, which indicates that we are performing a substitution, we now include a line pattern. A line pattern tells sed which lines it should apply the expression to. Our line pattern is just ^cp, which is the regular expression meaning \"any line which starts with cp\". Line patterns can be quite sophisticated and are covered in the box later on in this chapter! So all in all: ^cp- use the line pattern ^cp (lines which start with cp) for the expression s - use the substitution function (which replaces text) cp - find the pattern cp rmdir - replace each occurrence with rmdir on lines which start with the text cp, replace any occurrences of the text text cp with the text rmdir We combine these parts together using a / symbol, this is needed so that sed knows where the line pattern starts and end, the function starts and end and so on2. Removing Parts of a Line​ Now we need to remove the first parameter for the rmdir command, it was used when we had cp as the command but doesn't make sense any more. Let's apply a new expression: $ sed -E -e '/^cp/s/cp/rmdir/' -e '/^rmdir/s/~[^ ]+ //' backup-config.sh#!/usr/bin/env bash# Make sure we have a backup directory.mkdir ~/backup# Copy over alicloud, aws, azure, gcp and ssh config and credentials.rmdir ~/backup/settings/aliyun/rmdir ~/backup/settings/aws/rmdir ~/backup/settings/aws/rmdir ~/backup/settings/azure/rmdir ~/backup/settings/gcloud/rmdir ~/backup/settings/ssh/rmdir ~/backup/settings/ssh/ # is this safe?rmdir ~/backup/settings/ssh/ Let's take a look at the second expression, component by component: ^rmdir - use a line pattern which matches lines which start with rmdir s - use the substitution function to replace text ~[^ ]+ - search for the tilde ~ symbol and any non-space characters, up until the first space character We actually don't even include a replacement - which is why the expression ends with // - the inside of the replacement part of the expression is empty. This means we search for a tilde ~, match everything up until the next space and then replace it with nothing - meaning we remove it! Note as well that we've used the -E flag to tell sed to use extended regular expressions. This allows us to use the + symbol without escaping it with a \\ 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. Let's look at a few other ways to use sed for some real-world tasks.","s":"Applying Multiple Expressions","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#applying-multiple-expressions","p":357},{"i":368,"t":"In the script we've used above, there are some comments. Let's use sed to remove them: $ sed -E 's/#.*$//' backup-config.shmkdir ~/backupcp ~/.aliyun/config.json ~/backup/settings/aliyun/cp ~/.aws/config ~/backup/settings/aws/cp ~/.aws/credentials ~/backup/settings/aws/cp ~/.azure/config ~/backup/settings/azure/cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/cp ~/.ssh/config ~/backup/settings/ssh/cp ~/.ssh/id_rsa ~/backup/settings/ssh/cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ Notice how we have removed the comments which started at the beginning of the file, as well as the comment after the cp ~/.ssh/id_rsa line. This is because the regular expression matches any hash symbol # and strips everything which follows it. What about if we want to get rid of those empty lines? We can use the d or delete function: $ sed -E -e 's/#.*$//' -e '/^ *$/d' backup-config.shmkdir ~/backupcp ~/.aliyun/config.json ~/backup/settings/aliyun/cp ~/.aws/config ~/backup/settings/aws/cp ~/.aws/credentials ~/backup/settings/aws/cp ~/.azure/config ~/backup/settings/azure/cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/cp ~/.ssh/config ~/backup/settings/ssh/cp ~/.ssh/id_rsa ~/backup/settings/ssh/cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ The delete command is applied to all lines which match the line pattern. In this case, the line pattern is a regular expression, ^ *$, which matches any line made up only of space characters (including zero space characters, i.e. empty lines). Line Patterns Line patterns, which can be used on many sed functions, are actually quite sophisticated. Here are a few examples: /test/ - any line matching the pattern test /test/! - any line not matching test 6 - line six $ - the last line 1-10 - lines one to ten 1-10! - lines except _one to ten Check man sed to see more about line patterns.","s":"Stripping Comments","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#stripping-comments","p":357},{"i":370,"t":"In a regular expression the dollar-sign $ symbol represents the end of a line. We can use this symbol to add content to the end of lines - we just search for $ 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. Here's how we can make sure that the cp command in the script doesn't fail: $ sed -E -e '/^cp/s/$/ || true/' backup-config.sh#!/usr/bin/env bash# Make sure we have a backup directory.mkdir ~/backup# Copy over alicloud, aws, azure, gcp and ssh config and credentials.cp ~/.aliyun/config.json ~/backup/settings/aliyun/ || truecp ~/.aws/config ~/backup/settings/aws/ || truecp ~/.aws/credentials ~/backup/settings/aws/ || truecp ~/.azure/config ~/backup/settings/azure/ || truecp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/ || truecp ~/.ssh/config ~/backup/settings/ssh/ || truecp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe? || truecp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/ || true Now we have a command which strips comments, deletes empty lines and then adds the text || true to the end of the line. This little trick ensures that the script won't fail if one of the individual cp commands fails. We'll see more about shell scripts later. What if we want to add a semicolon to the end of all lines? sed 's/$/;/' Easy!","s":"Appending Text","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#appending-text","p":357},{"i":372,"t":"In a regular expression the caret ^ symbol represents the start of a line. We can apply the same trick as with the dollar-sign $ symbol to add text to the start of a line - we just replace ^ with whatever we want the line to start with. Here's how we can use this trick! $ sed -E -e '/^cp/s/$/\"/' -e '/\"$/s/^/echo \"/' backup-config.sh#!/usr/bin/env bash# Make sure we have a backup directory.mkdir ~/backup# Copy over alicloud, aws, azure, gcp and ssh config and credentials.echo \"cp ~/.aliyun/config.json ~/backup/settings/aliyun/\"echo \"cp ~/.aws/config ~/backup/settings/aws/\"echo \"cp ~/.aws/credentials ~/backup/settings/aws/\"echo \"cp ~/.azure/config ~/backup/settings/azure/\"echo \"cp ~/.config/gcloud/credentials.db ~/backup/settings/gcloud/\"echo \"cp ~/.ssh/config ~/backup/settings/ssh/\"echo \"cp ~/.ssh/id_rsa ~/backup/settings/ssh/ # is this safe?\"echo \"cp ~/.ssh/id_rsa.pub ~/backup/settings/ssh/\" We first put at quote at the end of each line which starts with cp, then put echo \" at the beginning of each line which ends with a quote! 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!","s":"Prepending Text","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#prepending-text","p":357},{"i":374,"t":"What about if we want to extract some information from lines in a file? Let's take a quick look at our movies file in our data folder for reference: $ cd ~/effective-shell/data$ head -n 3 top100.csv\"Rank\",\"Rating\",\"Title\",\"Reviews\"\"1\",\"97\",\"Black Panther (2018)\",\"515\"\"2\",\"94\",\"Avengers: Endgame (2019)\",\"531\" 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! $ head -n 3 data/top100.csv | sed -E 's/.*\\(([0-9]+)\\).*/\\1/' \"Rank\",\"Rating\",\"Title\",\"Reviews\"20182019 This works because we match any text, then capture any digits enclosed in brackets, then replace with the \\1 text, which means 'the first match'. Given what we know about line patterns, we can also easily exclude the first line: $ sed -E -e '1d' -e 's/.*\\(([0-9]+)\\).*/\\1/' data/top100.csv 20182019... Here we have just added the 1d expression - delete the first line.","s":"Extracting Information","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#extracting-information","p":357},{"i":376,"t":"Let's look at another example. Here we have some yaml content, a set of keys and values. Note that some of the values are quoted, and some are not: title: \"Advanced Text Manipulation\"slug: advanced-text-manipulationweight: 14 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. First, let's see if we can extract the keys: $ grep -E '[^:]+:' ~/effective-shell/docs/chapter12.mdtitle: \"Advanced Text Manipulation\"slug: advanced-text-manipulationweight: 14Let'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... 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. The pattern is [^:]+:, which means 'at least one character which is not the : character, followed by :. 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: % grep -E '^[^: ]+:' ~/effective-shell/docs/chapter12.mdtitle: \"Advanced Text Manipulation\"slug: advanced-text-manipulationweight: 14\"2\",\"94\",\"Avengers: Endgame (2019)\",\"531\" Much better. The pattern now starts with ^ meaning 'start of line', and the type of characters we search for [^: ] 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: $ grep -E '^[^: \"]+:' ~/effective-shell/docs/chapter12.mdtitle: \"Advanced Text Manipulation\"slug: advanced-text-manipulationweight: 14 Awesome! Building Regular Expressions I was a hold out for years, but regular expressions are 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. 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. Now let's start using this pattern in sed. Let's print all lines which match the pattern: $ sed -E -n '/^[^: \"]+:/p' ~/effective-shell/docs/chapter12.mdtitle: \"Advanced Text Manipulation\"slug: advanced-text-manipulationweight: 14 Now there are two critical things we've added to sed here while we're working. The first is the -n flag, which means no automatic printing - meaning it will show no output unless told. Then in the pattern, we finish the command with p, meaning 'print lines which match the pattern'. These options make sed behave like grep, which is useful because we are still building the command. Now let's actually quote the result. To do that, we need to find lines where the value is not already quoted - so let's add that to our pattern: $ sed -E -n '/^[^: \"]+: +[^\"]+$/p' ~/effective-shell/docs/chapter12.mdslug: advanced-text-manipulationweight: 14 Our pattern now reads like this: ^[^ :\"]+: - match the start of a line, any characters which are not space, colon or quote, which then finish with a colon and a space. +[^\"]+$ - 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. The pattern is working - its found our two unquoted keys. Now let's get it to print the substitution. 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: $ sed -E -n 's/(^[^: \"]+:)( +[^\"]+$)/Key is \"\\1\"/p' ~/effective-shell/docs/chapter12.mdKey is \"slug:\"Key is \"weight:\" We are now not just searching for a pattern, we're using the s (substitute) function to replace all of the matched text with Key is \"\\1\". The \\1 just means 'what you found in the first capture group\". We could just as easily show the value: $ sed -E -n 's/(^[^: \"]+:)( +[^\"]+$)/Value is \"\\2\"/p' ~/effective-shell/docs/chapter12.mdValue is \" advanced-text-manipulation\"Value is \" 14\" 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: $ sed -E -n 's/(^[^: \"]+)(: +)([^\"]+$)/Key \"\\1\", Value \"\\3\", Separator \"\\2\"/p' ~/effective-shell/docs/chapter12.mdKey \"slug\", Value \"advanced-text-manipulation\", Separator \": \"Key \"weight\", Value \"14\", Separator \": \" I think this is really starting to show just how powerful sed and regular expressions are. Let's finally tie it together - add quotes around unquoted values: $ sed -E -n 's/(^[^: \"]+)(: +)([^\"]+$)/\\1\\2\"\\3\"/p' ~/effective-shell/docs/chapter12.mdslug: \"advanced-text-manipulation\"weight: \"14\" Awesome! If we wanted to actually change the file, we could remove the -n flag, so that we write out everything. This makes the p option at the end of the substitution superfluous: $ sed -E 's/(^[^: \"]+)(: +)([^\"]+$)/\\1\\2\"\\3\"/' ~/effective-shell/docs/chapter12.md > updated.md Let's peep at the top of the file we created to see if it looks right: $ head -n 10 updated.md---title: \"Advanced Text Manipulation\"slug: \"advanced-text-manipulation\"weight: \"15\"---# Chapter 15 - Advanced Text ManipulationIn [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. Impressive - we've found a very specific pattern in a large file, substituted to match what we need and then saved the results. 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: $ sed -E -n '/(^[^: \"]+:)( +[^\"0-9]+$)/p' ~/effective-shell/docs/chapter12.mdslug: advanced-text-manipulation All we've done is changed the value pattern from [^\"] (anything except quotes) to [^0-9] (anything except quotes and digits).","s":"Advanced - Surrounding Parts of Text with Quotes","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#advanced---surrounding-parts-of-text-with-quotes","p":357},{"i":378,"t":"Here's another example which I have found useful again and again. We can use sed's text replacement capabilities to create a basic templating system. For example, let's say we have the file below: apiVersion: v1kind: Secretmetadata: name: database-credentials namespace: devstringData: username: admin password: ThisIsVerySensitive! 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. Let's say we want to be able to not have the username and password stored in the file itself, because they are sensitive. We also want to make the 'namespace' value configurable. 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 sed. First, let's create the 'template' version of this file: $ cat << EOF > secret.template.yamlapiVersion: v1kind: Secretmetadata: name: database-credentials namespace: %NAMESPACE%stringData: username: %DB_USERNAME% password: %DB_PASSWORD%EOF 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 effective-shell/templates/secret.template.yaml. Now let's apply our substitution: $ sed -e 's/%NAMESPACE%/staging/' \\ -e 's/%DB_USERNAME%/admin/' \\ -e 's/%DB_PASSWORD%/secret/' \\ ~/effective-shell/templates/secret.template.yamlapiVersion: v1kind: Secretmetadata: name: database-credentials namespace: stagingstringData: username: admin password: secret I've used the \\ 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. 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: $ export NAMESPACE=production$ export DB_USERNAME=prod-admin$ export DB_PASSWORD=Dhhs22kfid9c$ sed -e \"s/%NAMESPACE%/${NAMESPACE}/\" \\ -e \"s/%DB_USERNAME%/${DB_USERNAME}/\" \\ -e \"s/%DB_PASSWORD%/${DB_PASSWORD}/\" \\ ~/effective-shell/templates/secret.template.yamlapiVersion: v1kind: Secretmetadata: name: database-credentials namespace: productionstringData: username: prod-admin password: Dhhs22kfid9c In fact, we could even take this a step further and simply replace every environment variable! file_path=\"~/effective-shell/templates/secret.template.yaml\"env_var_names=$(env | sed -E -n 's/^([^=]+)(=.*)/\\1/p')for env_var_name in ${env_var_names}; do echo \"Checking for '${env_var_name}'...\" if grep -q \"%${env_var_name}%\" \"${file_path}\"; then echo \"-> Found '${env_var_name}', expanding now...\" env_var_value=\"${!env_var_name}\" escaped_env_var_value=$(echo ${env_var_value} | sed -e 's/[\\/&]/\\\\&/g') sed -e \"s/%${env_var_name}%/${escaped_env_var_value}/\" \\ \"${file_path}\" > \"${file_path}.tmp\" mv \"${file_path}.tmp\" \"${file_path}\" fidone 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. Here's what's going on blow-by-blow: 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 env_var_names=... - we use sed to get the name of each environment variable. This comes from everything before the = sign in the output of the env command. for ... - this lets us 'loop' through each environment variable name found - we'll see more about this in the sections on scripting. if grep -q - check to see if the environment variable name is used in the file... ...and if it is, get the value of it with ${!env_var_name} - the ! exclamation mark is variable indirection and is Bash specific. It allows us to get the value of a variable which has a variable name 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 sed sed -e run the replacement, putting the results in a temporary file... ...replace the source file with the temporary one Many of these concepts, like 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. A Note on Security It is very important to be careful when running commands like this or writing scripts which use this kind of pattern. Allowing the contents of your environment variables to be put into files, or even the shell's history can be a serious security concern. As an example - note what would happen if we replaced ${DB_USERNAME} with ${USERNAME} in the script above? In Z Shell rather than the value we provided being put in the file, the actual username of the user running the script would be written (which in my case would be dwmkerr). Be very careful when working with environment variables to make sure you avoid exposing private information. Even more sensitive variables might be things like ${AWS_SECRET_ACCESS_KEY} - exposing variables like this could allow attackers to start accessing resources on your cloud environments.","s":"Advanced - Template Files","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#advanced---template-files","p":357},{"i":380,"t":"You might be aware that there is an 'in-place' feature in sed which allows you to change the file you pass it directly. This allows you to do something like the below: $ sed -i '.bak' 's/staging/production/' test.txt This would perform the substitutions and then put them in a file with .bak appended. To just overwrite the existing file, you could use: $ sed -i '' 's/staging/production/' test.txt In this case we don't append anything to the name of the overwritten file, so we end up replacing the original file itself. How the -i flag works can vary on some systems so I generally prefer to simply output the result of 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.","s":"What About 'In Place' Editing?","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#what-about-in-place-editing","p":357},{"i":382,"t":"There is another very powerful text manipulation tool - awk. Often if you trying to find out how to perform more complex text based operations, you'll see awk in the mix as a potential solution. Awk is very sophisticated and has its own language to support complex transformations and operations. My advice is to first master sed and then consider learning awk if you regularly find yourself limited by sed.","s":"What about Awk?","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#what-about-awk","p":357},{"i":384,"t":"Personally, if I have tasks which are too complex for me to solve with the fairly basic knowledge of 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. This will often be easier to maintain and understand than an extremely complex 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. 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.","s":"When to Program","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#when-to-program","p":357},{"i":386,"t":"In this chapter we: Introduced sed, the stream editor command Saw that when we need to transform or manipulate text, sed is often a great tool for the job Learnt how to perform simple substitutions with sed, using commands such as sed 's/old-text/new-text/' Saw the components which make up a sed expression - the function, the expression and when needed, the line pattern Saw how to use -e to apply multiple patterns Went deeper into extended regular expressions Saw an example of how to strip comments from a file with sed Saw how to remove empty lines with sed Looked into line patterns in more detail, showing how we can choose what lines sed operates on Saw how to append text to lines with sed Saw how to use patterns and capture groups to extract information from lines with sed Saw more advanced examples of capture groups, allowing us to capture different parts of a match and manipulate them individually, or recompose them Saw how sed can be used to implement a simple 'template' mechanism for files Saw how the -i (in place) parameter works for sed Briefly described awk as a potential alternative tool to learn about for more sophisticated text manipulation Suggested that if something is too complex to write easily in sed, it may be faster to implement it with a programming language Note that if we used rm -r instead of rmdir, which is very common, we run the risk of making a big mistake - passing the source directory for the cp command as the first parameter to remove. This would mean we run rm -r on the source files for the backup and the backup folder itself, deleting both! This is one place where using rmdir makes a bit more sense - it won't delete the source files if we make a mistake!↩ You can use any character to delimit expressions - sometimes people will use # or | rather than a forward slash, typically because they want to use the forward slash as part of a pattern.↩","s":"Summary","u":"/part-3-manipulating-text/advanced-text-manipulation/","h":"#summary","p":357},{"i":388,"t":"On this page","s":"Get to Grips with Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"","p":387},{"i":390,"t":"A quick check of the manual page for grep gives an overview: $ man grepGREP(1) BSD General Commands Manual GREP(1)NAME grep, egrep, fgrep, zgrep, zegrep, zfgrep -- file pattern searcherSYNOPSIS grep [-abcdDEFGHhIiJLlmnOopqRSsUVvwxZ] [-A num] [-B num] [-C[num]] [-e pattern] [-f file] [--binary-files=value] [--color[=when]] [--colour[=when]] [--context[=num]] [--label] [--line-buffered] [--null] [pattern] [file ...]DESCRIPTION The grep utility searches any given input files, selecting lines that match one or more patterns. By default, a pattern matches an input line if the regular expression (RE) in the pattern matches the input line without its trailing newline. An empty expression matches every line. Each input line that matches at least one of the patterns is written to the standard output. Wow. Lots of options for this command. And confusingly, lots of alternative forms as well (such as egrep, zgrep and so on). Maybe the tldr tool will provide a more concise overview? $ tldr grep grep Matches patterns in input text. Supports simple patterns and regular expressions. That is indeed a little more concise. By the way, if you are not familiar with how you can get help on commands, check out Chapter 5 - Getting Help. As the manual pages indicate, grep is used to match patterns in files. More advanced users will most likely know exactly what this means, but a more simple description is just: Grep lets you search for text or filter text. That's it. You can search in files, but you are not limited to searching in files. And you can search for literal text, such as the word 'error', or you can search for patterns. Patterns in this case means regular expressions - expressions which allow you to be more specific in how you search (such as looking for a set of 16 numbers in a row, like a credit card number, or any text which looks like an email address). You can also do the opposite - filtering out parts of text. We'll use grep to search through text. Let's get straight into it!","s":"What is Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#what-is-grep","p":387},{"i":392,"t":"Why the odd name? Grep is such a commonly used tool that the name has become a verb amongst technologists (people will often suggest you grep for something in files). The name comes from a command which was used in the original ed text editor - the command: g/re/p This command ran on all lines (g, for global), applied a regular expression (re, for regular expression) and then printed (p for print) the results. A colleague of Ken Thompson, one of the early innovators and inventors in the Unix world, needed to edit a large file - a file which was too large to fit in ed. Ken wrote the grep program overnight to allow the file's text to be filtered - and the results passed to the ed editor! You can read more about this story and some of the fascinating history of the early days of Unix in a great interview with Brian Kerninghan from Computerphile1.","s":"Why Grep?","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#why-grep","p":387},{"i":394,"t":"If you've been working through this book, you've probably entered quite a few commands in the shell. Most shells keep a history of the commands you type. Under the hood, when you use the up and down keys to look through commands you entered earlier, or use the Ctrl-R shortcut to search through earlier commands, your shell is looking through this file. If these tricks are not familiar, check Chapter 9 - Fly on the Command Line. The file which keeps the history can vary from shell to shell. For example, on my system, my history for Bash is in the file ~/.bash_history. But most 'Bash-like' shells provide a built-in environment variable which let's you find the path of the shell history. Let's at this file: cat $HISTFILE...cat ~/.ssh/configssh bastion.cloudopshelp echohelp cdexit This file will generally contain a list of the commands that the logged in user has entered in their shell. This file will likely be huge. Let's search through it using grep! Here's how we can use the grep command to search for lines which contain the text man: grep man $HISTFILE ... man socket k describe services eventstoredb-http-management man cal gcb refactor/performance/standardise-eventstore vi src/tests/handlers/test_command_handlers.py gco src/handlers/command_handlers.py gcb feat/performance/use-eventstore-writer nvim performance.md man grep Here I can see all of the commands I have recently entered which have the text man in them. Note that the text which matches is highlighted and shown in bold. Now what if you use a different shell, or forget where the history file lives? A nice trick here is to use the history command. This command prints out the history, as well as the line number. The history command writes to stdout. If we don't give grep a source file, it will simply search through stdin. Just as we learnt in Chapter 7 - Thinking in Pipelines this means we can just grep the output of the history command! Here's how that would work: history | grep man ... 9125 man socket 9188 k describe services eventstoredb-http-management 9211 man cal 9341 gcb refactor/performance/standardise-eventstore 9344 vi src/tests/handlers/test_command_handlers.py 9347 gco src/handlers/command_handlers.py 9352 gcb feat/performance/use-eventstore-writer 9355 nvim performance.md 10002 man grep This is easier to remember! There's one more cool trick - if we just type in the exclamation point symbol followed by any line number shown above, we repeat the command! For example, typing in: !9355 Would repeat line 9355 of the history (which is nvim performance.md).","s":"Searching Through Text","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#searching-through-text","p":387},{"i":396,"t":"Now as you can see from the output above, when we searched through my history, we didn't just find times I executed the man command - we found any line which has the characters man in it. What about if we only wanted to find the lines which start with man? To perform a search like this, we can use a regular expression. Here's how it would work: history | grep \"[0-9]\\+ man\" ... 9125 man socket 9211 man cal 10002 man grep Let's break this down. In this search, we are using a pattern to search for text. The pattern in this case is a basic regular expression. Regular expressions allow us to use some clever constructs to search for text. The expression we've used is made up of the following components: [0-9]\\+ At least one number - any character in the range zero to nine. man The literal text written, i.e. two spaces and the letters man. Now for anyone who is familiar with regular expressions, you might wonder why we have a slash before the + symbol, when the + symbol has a specific meaning in regular expressions (it means 'at least one of the previous characters). The reason we have a leading slash is that by default grep is using basic regular expressions. In general, this will be less familiar for users and will be different to what they are used to from different tools. To make regular expressions more 'standard', we can use the -E flag to tell grep to use Extended Regular Expressions. We can also use the egrep tool, which assumes the pattern will be an extended regular expression. Using either approach will work, and allow you to re-write the pattern as below: history | grep -E \"[0-9]+ man\"# ...or...history | egrep \"[0-9]+ man\" This is just a little hint of the power of regular expressions. They can be daunting at first, and many people never become comfortable with them, but I would strongly encourage you to start exploring them. Getting Help on Regular Expressions If you want to find out more about the difference between the slightly old-fashioned basic regular expressions and modern regular expressions, you can use: man re_format This manpage gives lots of information on regular expressions, including the differences between basic and extended patterns. If you ever want to see how a regular expression works, try using the website regex101.com. It let's you test out regular expressions and also describes exactly how they work. For example, if I enter the regular expression we just used I'll see this: We're going to see more about regular expressions as we go through the book.","s":"Using Patterns","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#using-patterns","p":387},{"i":398,"t":"If there's one command I use a lot, it's this: grep -i err The -i flag makes the search case-insensitive. This makes this a very quick way to scan through a file for any text which matches the letters err - making it a very quick way to find errors in log files. You can try this out by using some of the log files in the logs folder of the samples. Here's how you can try it out: grep -i err ~/effective-shell/logs/web-server-logs.txt ... 2020-11-29T12:50:30.594Z: info - Serving file '../../../website/public/docs/part-2-core-skills/7-thinking-in-pipelines/images/diagram-stderr-redirect.png'... 2020-11-29T12:50:31.827Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500 2020-11-29T12:50:31.827Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500 2020-11-29T12:50:31.827Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500 2020-11-29T12:50:31.848Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/edit.svg', returning a 500 2020-11-29T12:50:31.849Z: error - Unhandled error EACCES trying to read '../../../website/public/svg/edit.svg', returning a 500 This is a very useful trick. You could use this technique to search for warnings, problems, specific messages and so on.","s":"Finding Problems","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#finding-problems","p":387},{"i":400,"t":"There are three really useful parameters for grep, which I used to struggle to remember, until I realised that they are simple - ABC! Here's how they work: $ grep host -A 3 ./programs/web-server/web-server.js host: process.env.HOST || 'localhost', port: process.env.PORT || getOptonalEnvInt('PORT', 8080), root: process.env.ROOT || process.cwd(), defaultPage: 'index.html', -- httpServer.listen({host: config.host, port: config.port &brace;); log.info(`Server running on: ${config.host}:${config.port}`); } main(); A stands for after. In this example we show the three lines after each occurrence of the work host in the web-server.js script. This is a quick way to see how something you search for might be used! B stands for before - we can use this to see what comes before a match when we're searching. What can lead to us sending an error in our web server? Let's see: $ grep throw -B 5 ./programs/web-server/web-server.js // Helper to return an optional numeric environment variable or the default. function getOptonalEnvInt(name, defaultValue) { const val = process.env[name]; if (!val) return defaultValue; const intVal = parseInt(val, 10); if (isNaN(intVal)) throw new Error(`Unable to parse environment variable named '${name}' with value '${val}' into an integer`); And finally C, the most useful of them all. C stands for context, and lets you see a number of lines before and after each match. What was I up to the last time I ran the git init command? Let's see! $ history | grep -C 5 'git init' 5802 git push --follow-tags && git push origin 5803 cd ../java-maven-standard-version-sample 5804 rm -rg .git 5805 rm -rf git 5806 rm -rf .idea 5807 git init -h 5808 git remote add origin git@github.com:dwmkerr/java-maven-standard-version-sample.git 5809 git push origin -u 5810 git push -u origin 5811 git push --set-upstream origin master 5812 git rm --cached tpm Don't forget that these flags need to be capitalised! These three flags are very useful - knowing how to find context of a match can be a lifesaver when quickly searching through text.","s":"The ABC of Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#the-abc-of-grep","p":387},{"i":402,"t":"What about if you have a bunch of files you want to search? One problem we have at the moment is that everything we search through has been a single file. But if we are searching through multiple files, how can we identify where the matches come from? There's a useful pair of flags for this. -H stands for 'header', which shows the file name before each match. -n stands for 'number', which makes sure the line number is shown. Here's how we might use this command: $ grep -Hn ERROR ./logs/apm-logs/*.logs ... ./logs/apm-logs/apm02.logs:34893:2020-11-27T12:24:37.429Z ERROR [request] middleware/log_middleware.go:95unauthorized {\"request_id\": \"53a41a98-ba12-454e-aadf-72c97dc40e96\", \"method\": \"POST\", \"URL\": \"/config/v1/agents\", \"content_length\": 27, \"remote_address\": \"127.0.0.1\", \"user-agent\": \"elasticapm-python/5.9.0\", \"response_code\": 401, \"error\": \"unauthorized\"} ./logs/apm-logs/apm02.logs:34906:2020-11-27T12:25:11.415Z ERROR [request] middleware/log_middleware.go:95unauthorized {\"request_id\": \"a49d5546-b8d2-4e50-9dd0-6cbf419a365e\", \"method\": \"POST\", \"URL\": \"/config/v1/agents\", \"content_length\": 27, \"remote_address\": \"127.0.0.1\", \"user-agent\": \"elasticapm-python/5.9.0\", \"response_code\": 401, \"error\": \"unauthorized\"} Note that in this case we searched through many files - anything which matches the *.logs wildcard. To help us identify in which file the match was found, we used the -Hn flags. The beginning of the lines now start with the path of the file and the line number, for example: ./logs/apm-logs/apm02.logs:34906 You can take this even further: $ grep -R -Hn -i error ./logs Adding the -R or recursive flag tells grep to search recursively in folders if they are included in the search.","s":"Working with Multiple Files","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#working-with-multiple-files","p":387},{"i":404,"t":"As long as your remember that -i is the flag for case insensitive, it makes it a little easier to remember v for invert. This tells grep to exclude lines which match the pattern. This works kind of like a filter. Here's how I could look through my log files, excluding any messages with 'debug' in them: $ grep -v debug ./logs/web-server.logs Don't forget, you can always pipe a series of grep commands together. Rather than trying to work out a perfect pattern which searches for exactly what you want, you could just pipe a a set of commands together: $ grep -i error -R ./logs | grep -i -v memory | grep -i -v 'not found' This set of small, simple, commands is chained together to make a more sophisticated operation: First we recursively search for any error text in the ./logs folder Then we exclude anything which matches memory Then we exclude anything which matches not found This is the essence of the Unix Philosophy - with a small number of simple tools, we can compose a more complex workflow!","s":"V for Invert","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#v-for-invert","p":387},{"i":406,"t":"We've introduced a very powerful command in this chapter. For familiar users, grep becomes a verb they use regularly - you grep the output of something, or might be grepping to find something. Remember that grep, just like most of the tools in this section, works on stdin by default. So you can easily grep the output of almost anything! Here are a few simple examples just to show you how easy it is to perform more complex tasks with grep. ps -a | grep vim Show all processes, then filter the list down to only vim processes. grep -Hn -C 3 -R password ./k8s/**/*.yaml | less Search through all of the yaml files in my k8s folder, for the text 'password', show three lines of context, as well as the file name and number, and put the output in my pager so that it is easy to search through. ls -al /usr/bin /bin /usr/local/bin | grep zip Search through all of my installed programs for programs which have zip in the name. history | grep grep | tail -n 10 Show me the last ten grep commands I typed in my shell! We'll see a lot more examples as we go through the book - just remember that grep is always available to search or filter text!","s":"Don't Forget Your Pipelines!","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#dont-forget-your-pipelines","p":387},{"i":408,"t":"Grep is a very commonly used tool and has been around for a long time. It can vary a bit from system to system. Over the years a number of alternatives have been developed. Most of these alternatives are either designed to be faster, so that you can search through files much more quickly, or easier so that you don't have to remember too many flags. In general, I would advise against using alternatives - until you genuinely find you are limited by grep. Every alternative is another tool to learn, which might not be present on other systems you use. It is also less likely to be available if you are writing scripts or instructions for others. If you find yourself really struggling with performance - perhaps you often search huge folders of text or if you find yourself regularly struggling to find ways to craft your search patterns, then perhaps you can investigate some of the popular alternatives. But I would suggest that you master the core grep functionality first, before installing other tools. If you do decide you want to add some more text searching tools to your toolkit, I would suggest ripgrep, ag and ack as three potential options. Each of them offer performance improvements and additional functionality.","s":"Alternatives to Grep","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#alternatives-to-grep","p":387},{"i":410,"t":"Grep is a simple text-based search tool! If you need to find text, or want to filter text, then grep should be your go-to tool. Here's a summary of what we've covered: grep pattern file searches file for the text pattern the -E flag lets you use regular expressions for more sophisticated searches You can make the search case insensitive with the -i flag Remember the ABC flags - after, before and context, which show lines after, before and around the matches Include the filename and line number with the -Hn flags V for invert! Use the -v flag to invert the search, or filter out matches grep works great in pipelines! Use it to search or filter when working with other commands See the interview at: https://www.youtube.com/watch?v=NTfOnGZUZDk&feature=emb_title↩","s":"Summary","u":"/part-3-manipulating-text/get-to-grips-with-grep/","h":"#summary","p":387},{"i":412,"t":"On this page","s":"Shell Script Essentials","u":"/part-3-manipulating-text/shell-script-essentials/","h":"","p":411},{"i":414,"t":"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. 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.","s":"What is a Shell Script?","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#what-is-a-shell-script","p":411},{"i":416,"t":"Let's create a simple shell script that shows us our most commonly used shell commands. 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. As we go through this section of the book, we're going to extend this script and make it more useful!","s":"Creating a Basic Shell Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#creating-a-basic-shell-script","p":411},{"i":418,"t":"We're going to create a new command, called 'common', that shows the most commonly used shell commands. We should be able to do this using techniques we've seen so far. We'll do it like this: Read a large number of commands from the history Sort the commands, then count the number of duplicates Sort this list showing the most commonly run commands first Print the results to the screen. Let's get started!","s":"The 'common' Command","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#the-common-command","p":411},{"i":420,"t":"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. In your favourite editor, create a file called common.v1.sh and put it somewhere on your system. As an example, I'm going to create a folder called scripts in my home directory and create the common.v1.sh file there: # Create a directory called 'scripts'.# Using the '-p' flag means we won't get an error if the folder exists.mkdir -p ~/scripts# Create the script file.touch ~/scripts/common.v1.sh# Open the script file in my favourite editor.vi ~/scripts.sh I have called the script common.v1.sh rather than 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 common.v2.sh, common.v3.sh and so on. These commands should be familiar. The mkdir command creates a directory. The -p (create parent directories if needed) flag stops the command from returning an error if the directory already exists. The 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 Chapter 32 - A Vim Crash Course. Before we run the script, let's quickly talk about comments.","s":"Creating a Simple Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#creating-a-simple-script","p":411},{"i":422,"t":"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 – they are purely for the benefit of the reader. Any text that follows a # 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. Using comments effectively can be a huge time-saver – 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. Let’s look at some examples of comments. # This is a comment - we can use this to describe what we're trying to do.echo \"Hello Shell\" # Comments can go at the end of a line...# You can also use a comment symbol to 'comment out' a line:# echo \"Goodbye Shell\" 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 – we've just put a hash in front of some commands so that they will not be executed. 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 intent - why you are doing something. This is far more useful for the reader than what you are doing. The 'what' should be clear from the commands - the 'why' is the thing readers will likely want to understand. Here's an example of a bad comment: # Write the CSV file, reverse it, cut it, reverse it.cat ~/effective-shell/data/top100.csv | rev | cut -d',' -f1 | rev The comment just describes what the script is doing. But it doesn't explain why. A better comment would be: # Get the last field of each line in the csv file.cat ~/effective-shell/data/top100.csv | rev | cut -d',' -f1 | rev If you 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!","s":"Comments","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#commentsindex","p":411},{"i":424,"t":"Add the following commands to the common.v1.sh file: # Write the title of our command.echo \"common commands:\"# Show the most commonly used commands.tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10 This is a short script, but there is quite a lot going on. Let's look at it blow-by-blow: First we take the last 1000 lines of the ~/.bash_history file using the tail command1 Then we sort the commands. This will put all of the duplicates next to each other Then we remove all duplicates and use the -c (show count) flag to count the duplicates Then we remove the leading spaces from the output (which we need to do so that we can sort properly) Then we sort numerically and in reverse - the highest count first Finally, we limit the results to the first ten items If you need a refresher on the shell history, sort or uniq the check the Slice and Dice Text chapter. If the sed command doesn't look familiar then check the Advanced Text Manipulation with Sed chapter. If you want to see a more detailed breakdown of how the script works, check Appendix - How the Script Works. But this is not necessary for you to follow the content in this chapter. Now save the file. In your shell, run the following command to execute the file: sh ~/scripts/common.v1.sh The 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: common commands:463 vi267 gc238 ga .212 ls169 gpo122 make dev112 gl97 gcm96 gpr 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. We now have a basic shell script. Let's look at a few different ways we can run the script.","s":"Building and Testing the Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#building-and-testing-the-script","p":411},{"i":426,"t":"You can use the \\ 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. As an example, we could re-write our pipeline command to look like this: # Show the most commonly used commands.tail ~/.bash_history -n 1000 \\ | sort \\ | uniq -c \\ | sed 's/^ *//' \\ | sort -n -r \\ | head -n 10 This will probably look very familiar to anyone with a background in functional programming! Be careful when you split lines up - the continuation character must be the last character on the line. If you add something after it (such as a comment) then the command will fail.","s":"Multi-line Commands","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#multi-line-commands","p":411},{"i":428,"t":"There are a few different ways we can run shell scripts. 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: bash ~/scripts/common.v1.sh This is a perfectly valid technique. Now let's see the other ways we can run a script. 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 chmod (change file mode) command to do this: chmod +x ~/scripts/common.v1.sh If the chmod command looks unfamiliar then check the 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: ~/scripts/common.v1.sh There is a problem with this approach though. How this file is executed is going to vary depending on how your system is set up2. 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 sh program (and depending on your system, this program might just be a link to another type of shell). We want to avoid any ambiguity and be explicit about what program should run our script. We can do this using a special construct called a shebang.","s":"Running a Shell Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#running-a-shell-script","p":411},{"i":430,"t":"A 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. If we were to add a shebang to our common.v1.sh file, it would look like this: #!/usr/bin/sh# Write the title of our command.echo \"common commands:\"# Show the most commonly used commands.tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10 The shebang is the two characters - #!. 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'. Immediately after the shebang you write the full path to the program which should be used to open the file. For example, if you wanted to write a script that is run in Python, you could do this: #!/usr/bin/python3print('Hello from Python') If we wanted to explicitly use the Bash shell to run a script, we might use a shebang like this: #!/usr/bin/bashecho \"Hello from Bash\" What about Node.js? Easy! #!/usr/bin/nodeconsole.log(\"Hello from Node.js\");","s":"Using Shebangs","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#using-shebangs","p":411},{"i":432,"t":"When we use a shebang we need to provide the full path to the executable that runs the script. For example, if we want to use Ruby to run a script we could write a script like this: #!/usr/bin/rubyputs 'Hello from Ruby' 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. /usr/bin/ruby). If you do not have the Ruby program in this location the script will fail to run. How can we know where a specific program is installed? There is a common trick for dealing with this issue. We can use the env (set environment and execute command) command to run a command and it will work out the path for us. The 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 env command is that it looks through the $PATH variable to find the path of the command to execute. You can see this by running a command like the below: $ env python3Python 3.8.5 (default, Jan 27 2021, 15:41:15)[GCC 9.3.0] on linuxType \"help\", \"copyright\", \"credits\" or \"license\" for more information.>>> We've used the env command to run the python3 command - and it worked out the correct path for us. To use env in a shebang, specify the full path to env (which should be the same on all Unix-like systems) and then provide the name of the command to run: #!/usr/bin/env bashecho \"Hello from Bash\" Or another example: #!/usr/bin/env rubyputs 'Hello from Ruby' Using a shebang to specify the exact command to run, and then using the env command to allow the $PATH to be searched is generally the safest and most portable way to specify how a shell script should run.","s":"Shebangs - Dealing with Paths","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#shebangs---dealing-with-paths","p":411},{"i":434,"t":"We have discussed how to run shell scripts. You can also use the source (execute commands from a file) command to load the contents of a file into the current shell. 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. Let's see an example. We'll create a script called set_editor.sh that sets the EDITOR environment variable to nano. The script's contents are below (can also find it in the samples at ~/effective-shell/scripts/set_editor.sh): EDITOR=nanoecho \"Editor changed to: $EDITOR\" Let's run this script and see what editor looks like before and after: $ echo $EDITORvim$ ~/effective-shell/scripts/set_editor.shEditor changed to: nano$ echo $EDITORvim Notice that although we changed the 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 copy of the environment. If we want to run the commands in the file in the context of the current shell, we can use the source command to load the file: $ echo $EDITORvim$ source ~/effective-shell/scripts/set_editor.shEditor changed to: nano$ echo $EDITORnano Our existing environment has been changed. When we use source, the commands in the file are executed in the current shell, rather than in a new shell. We can see this even more clearly if we use the showpstree.sh file: $ ~/effective-shell/scripts/show-info.shbash └─sh /home/ubuntu/effective-shell/scripts/showpstree.sh └─pstree -l -a -s 2240 This script shows the current 'process tree', using the pstree (show process tree) command. We can see that the pstree command was run as a child of the sh program. This program was run with the script path, by the shell I was using, bash. This is a nice visualisation of what is going on - our bash shell has run the showpstree.sh script in a child shell. If we source the same file, we'll see that we do not create a new shell: $ source ~/effective-shell/scripts/show-info.shbash └─pstree -l -a -s 2169","s":"Sourcing Shell Scripts","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#sourcing-shell-scripts","p":411},{"i":436,"t":"There is a slightly more concise syntax to source a script - the dot sourcing notation. When the shell sees a . dot character, it will source the file that follows: $ . ~/effective-shell/scripts/show-info.shbash └─pstree -l -a -s 2169 You may encounter this syntax as you look at things like shell configuration files, which we will discuss in Chapter 24 - Configuring the Shell.","s":"Dot Sourcing","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#dot-sourcingindex","p":411},{"i":438,"t":"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. Our common.v1.sh script (with the added shebang) looks like this: #!/usr/bin/env sh# Write the title of our command.echo \"common commands:\"# Show the most commonly used commands.tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n -r | head -n 10 If we have made the script executable with the chmod command, then we can run the script by simply typing the location of the script in the shell: $ ~/scripts/common.v1.shcommon commands:97 gcm96 gpr... If we want to 'install' this script as a local command which we can run easily, we can create a symbolic link to the shell script in our /usr/local/bin folder: ln -s ~/scripts/common.v1.sh /usr/local/bin/common The ln (create link) command creates a link (which is like a shortcut in Windows and other desktop systems) in our /usr/local/bin folder, with the name common, which points to the script we have written. We can now run the common command without specifying its path: $ commoncommon commands:97 gcm96 gpr... 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 /usr/local/bin folder is in this list of paths. Why do we use the /usr/local/bin folder rather than the /usr/bin folder? This is just a convention. In general, the /usr/bin folder is for commands which are installed with package manager tools like apt or Homebrew (on MacOS). The /usr/local/bin folder is used for commands which you create for yourself on your local machine and manage yourself3.","s":"Installing Your Script","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#installing-your-script","p":411},{"i":440,"t":"In this chapter we've covered quite a few of the fundamentals of shell scripts: How to create a shell script How comments work in shell scripts How to handle long lines with continuations How to run a shell script How to make a shell script executable How shebangs work How to use the env command to make our shebangs more portable How to 'install' scripts for the current user In the next chapter we'll look at how to add logic to our shell scripts.","s":"Summary","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#summary","p":411},{"i":442,"t":"This section briefly covers how the common.v1.sh script works. Assuming we have a history that looks like this: vi README.mdgit statusgit checkout maingit statusrestart-shellgit statusopen .vi README.mdopen . First we sort, putting duplicate lines next to each other: git checkout maingit statusgit statusgit statusopen .open .restart-shellvi README.mdvi README.md Then we use uniq to remove duplicate adjacent lines, passing the -c flag to include a count: 1 git checkout main 3 git status 2 open . 1 restart-shell 2 vi README.md Now we remove the leading whitespace: 1 git checkout main3 git status2 open .1 restart-shell2 vi README.md Finally we sort numerically (by using the -n flag) and in descending order (by using the -r flag): 3 git status2 vi README.md2 open .1 restart-shell1 git checkout main 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: 1 git checkout main1 restart-shell13 git status2 open .2 vi README.md 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. The path to the shell history file is normally available in the $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 $HISTFILE variable or history command!↩ Try putting the command pstree -p $$ in a shell script and running the script - you'll see exactly what process is run.↩ 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.↩","s":"Appendix - How the Script Works","u":"/part-3-manipulating-text/shell-script-essentials/","h":"#appendix---how-the-script-works","p":411},{"i":444,"t":"On this page","s":"Slice and Dice Text","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"","p":443},{"i":446,"t":"The commands head and tail are very simple but incredibly useful. head is used to extract part of the top of a file and tail is used to extract part of the end of a file. Once you starting using these commands you'll find yourself using them regularly. Let's start with 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? $ head ~/effective-shell/data/top100.csv\"Rank\",\"Rating\",\"Title\",\"Reviews\"\"1\",\"97\",\"Black Panther (2018)\",\"515\"\"2\",\"94\",\"Avengers: Endgame (2019)\",\"531\"\"3\",\"93\",\"Us (2019)\",\"536\"\"4\",\"97\",\"Toy Story 4 (2019)\",\"445\"\"5\",\"99\",\"Lady Bird (2017)\",\"393\"\"6\",\"100\",\"Citizen Kane (1941)\",\"94\"\"7\",\"97\",\"Mission: Impossible - Fallout (2018)\",\"430\"\"8\",\"98\",\"The Wizard of Oz (1939)\",\"120\"\"9\",\"96\",\"The Irishman (2019)\",\"441\" The head command just shows the first ten lines of a file. Here we can see that this is a 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. You can use the -n flag to specify the number of lines you want to see, for example: $ head -n 3 ~/effective-shell/data/top100.csv\"Rank\",\"Rating\",\"Title\",\"Reviews\"\"1\",\"97\",\"Black Panther (2018)\",\"515\"\"2\",\"94\",\"Avengers: Endgame (2019)\",\"531\" The tail command works in the same way - but looks at the 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 recent entries. Here's how we can see the ten most recent commands we entered in our shell: $ tail $HISTFILE: 1606818280:0;ls: 1606818300:0;ln -s $(pwd) ~/effective-shell: 1606818308:0;cat ~/effective-shell/data/top100.csv: 1606818342:0;head -n 3 ~/effective-shell/data/top100.csv: 1606819062:0;head ~/effective-shell/data/top100.csv: 1606819647:0;gcd: 1606819649:0;git stash: 1606819650:0;gcd: 1606819662:0;git stash pop: 1606819803:0;tail $HISTFILE What is $HISTFILE? Most Bash-like shells keep a file called the history file. This is essentially a record of all of the commands which have been written in the shell. The 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 $HISTFILE. Enter help history for more information on the shell history. We can be more specific, just like with head, by specifying the number of lines to show: $ tail -n 3 $HISTFILE: 1606819650:0;gcd: 1606819662:0;git stash pop: 1606819803:0;tail $HISTFILE tail can also be used to show the changes to a file in real time. Add the -f flag to follow the contents of the file - this means the tail command show each new line as it gets added to the file. To try it out, run the following command in one shell: $ tail -f $HISTFILE In another terminal window, start entering commands. You'll see that the tail command in the first window is writing the updates to the terminal as they are entered in the file. Press Ctrl+C to close the tail program. Another trick I use a lot with tail is to use -n +2. This shows everything from the second line - the + 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: $ head ~/effective-shell/data/top100.csv | tail -n +2\"1\",\"97\",\"Black Panther (2018)\",\"515\"\"2\",\"94\",\"Avengers: Endgame (2019)\",\"531\"\"3\",\"93\",\"Us (2019)\",\"536\"\"4\",\"97\",\"Toy Story 4 (2019)\",\"445\"\"5\",\"99\",\"Lady Bird (2017)\",\"393\"\"6\",\"100\",\"Citizen Kane (1941)\",\"94\"\"7\",\"97\",\"Mission: Impossible - Fallout (2018)\",\"430\"\"8\",\"98\",\"The Wizard of Oz (1939)\",\"120\"\"9\",\"96\",\"The Irishman (2019)\",\"441\" Here I've taken the head of the file (otherwise the output gets quite difficult to follow), then piped the results into 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. We're going to use head and tail quite a lot when working with text. These are two crucial tools which can really speed up your work.","s":"Heads and Tails","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#heads-and-tails","p":443},{"i":448,"t":"The next tool we'll look at is tr (translate characters). This program is very simple. My most common use for tr is to perform a simple substitution of characters. Let's create a list of each of the columns in the data file we saw before to show how the command works: $ head -n 1 ~/effective-shell/data/top100.csv | tr ',' '\\n'\"Rank\"\"Rating\"\"Title\"\"Reviews\" What about if we wanted to remove the quotes? $ head -n 1 ~/effective-shell/data/top100.csv | tr ',' '\\n' | tr -d '\"'RankRatingTitleReviews Here we've seen two variations on how we can run the command. The first form is used to replace characters. Running: tr ',' '\\n' Replaces the first specified character with the second. The \\n character is the special newline character, which is used to create a line break at the end of a file. The second form uses the -d flag to specify a set of characters to delete: tr -d '\"' In the form above we delete quote (\") characters. When using tr remember that it works on characters. For example, the following might not work as you expect: $ echo \"Welcome to the shell\" | tr 'shell' 'machine'Wcicomc to tac macii The reason the output is like this is that we're specifying character replacements - so we're changing characters as shown below: s -> mh -> ae -> cl -> hl -> i There are plenty of ways to replace entire words or perform more complex operations, but we'll use sed or awk for these operations - which we'll see in the following chapter. There is one final thing it is worth mentioning about tr. It can be provided with character classes. This is easiest to explain with an example: $ echo \"Use your inside voice...\" | tr '[[:lower:]]' '[[:upper:]]'USE YOUR INSIDE VOICE... In this case we are transforming characters in the lower class (lowercase characters) to the upper class (uppercase characters). On Linux systems you can find more about character classes with 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 extended regexes) which I think are likely to be more useful to learn about instead.","s":"Replacing Text","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#replacing-text","p":443},{"i":450,"t":"The next command is one which I've used far more than I expected. The cut command splits a line of text, using a given delimiter. Let's see some examples: $ cut -d',' -f 3 ~/effective-shell/data/top100.csv | head\"Title\"\"Black Panther (2018)\"\"Avengers: Endgame (2019)\"\"Us (2019)\"\"Toy Story 4 (2019)\"\"Lady Bird (2017)\"\"Citizen Kane (1941)\"\"Mission: Impossible - Fallout (2018)\"\"The Wizard of Oz (1939)\"\"The Irishman (2019)\" This is the first way to use cut. We specify the -d flag to choose a delimiter which we will cut the text with, then -f to choose which field we want to see. In this case we show split on the command character and show the third field - the title of the film in the data file. 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: $ kubectl get podsNAME READY STATUS RESTARTS AGEelastic-operator-0 1/1 Running 0 35delk-apm-server-65b698fb8c-rzncz 1/1 Running 0 13delk-es-default-0 1/1 Running 0 35delk-kb-6f8bb6457b-bbbnn 1/1 Running 0 35dfilebeat-beat-filebeat-ccgl7 1/1 Running 1 13dfilebeat-beat-filebeat-dvf2l 1/1 Running 2 13dfilebeat-beat-filebeat-mnpms 1/1 Running 329 13dkube-state-metrics-5cb57bdc45-mqv9d 1/1 Running 0 35dmetricbeat-beat-metricbeat-2xm7t 1/1 Running 6103 35dmetricbeat-beat-metricbeat-96dkt 1/1 Running 6097 35dmetricbeat-beat-metricbeat-n7kxm 1/1 Running 6109 35d Now to get the name I can just cut the lines on the 'space' character and grab the first field: $ kubectl get pods | cut -d' ' -f 1NAMEelastic-operator-0elk-apm-server-65b698fb8c-rznczelk-es-default-0elk-kb-6f8bb6457b-bbbnnfilebeat-beat-filebeat-ccgl7filebeat-beat-filebeat-dvf2lfilebeat-beat-filebeat-mnpmskube-state-metrics-5cb57bdc45-mqv9dmetricbeat-beat-metricbeat-2xm7tmetricbeat-beat-metricbeat-96dktmetricbeat-beat-metricbeat-n7kxm And if we want to strip the first line? We can use the tail -n +2 command to tail everything from the second line onwards: $ kubectl get pods | cut -d' ' -f 1 | tail -n +2elastic-operator-0elk-apm-server-65b698fb8c-rznczelk-es-default-0elk-kb-6f8bb6457b-bbbnnfilebeat-beat-filebeat-ccgl7filebeat-beat-filebeat-dvf2lfilebeat-beat-filebeat-mnpmskube-state-metrics-5cb57bdc45-mqv9dmetricbeat-beat-metricbeat-2xm7tmetricbeat-beat-metricbeat-96dktmetricbeat-beat-metricbeat-n7kxm Bingo - we've removed the heading line. If you remember grep from the previous chapter, you might have spotted that we could also just filter the content: $ kubectl get pods | cut -d' ' -f 1 | grep -v NAMEelastic-operator-0elk-apm-server-65b698fb8c-rznczelk-es-default-0elk-kb-6f8bb6457b-bbbnnfilebeat-beat-filebeat-ccgl7filebeat-beat-filebeat-dvf2lfilebeat-beat-filebeat-mnpmskube-state-metrics-5cb57bdc45-mqv9dmetricbeat-beat-metricbeat-2xm7tmetricbeat-beat-metricbeat-96dktmetricbeat-beat-metricbeat-n7kxm With even just a few simple shell commands there are often many ways to accomplish the same goal! There is another way we can cut text. We can cut by slicing a number of characters from each line. Let's take a look at our web logs file: $ tail ~/effective-shell/logs/web-server-logs.txt2020-11-29T12:50:52.721Z: info - Request: GET /en.search.min.1f83b222e24a227c0f5763727cb9e4f3b435f08b936f6ce529c9c9359f6b61a8.js2020-11-29T12:50:52.722Z: info - Serving file '../../../website/public/en.search.min.1f83b222e24a227c0f5763727cb9e4f3b435f08b936f6ce529c9c9359f6b61a8.js'...2020-11-29T12:50:52.762Z: info - Request: GET /svg/menu.svg2020-11-29T12:50:52.763Z: info - Serving file '../../../website/public/svg/menu.svg'...2020-11-29T12:50:52.763Z: info - Request: GET /svg/calendar.svg2020-11-29T12:50:52.764Z: info - Serving file '../../../website/public/svg/calendar.svg'...2020-11-29T12:50:52.765Z: info - Request: GET /svg/edit.svg2020-11-29T12:50:52.766Z: info - Serving file '../../../website/public/svg/edit.svg'...2020-11-29T12:50:52.784Z: info - Request: GET /fonts/roboto-v19-latin-300italic.woff22020-11-29T12:50:52.785Z: info - Serving file '../../../website/public/fonts/roboto-v19-latin-300italic.woff2'... We can use the -c (characters) flag to specify the characters in the line we want to see. Let's extract the timestamp only: $ tail -n 3 ~/effective-shell/logs/web-server-logs.txt | cut -c 12-1912:50:5212:50:5212:50:52 We can also use the character option to extract everything from a specific point onwards: $ tail -n 3 ~/effective-shell/logs/web-server-logs.txt | cut -c 27-info - Serving file '../../../website/public/svg/edit.svg'...info - Request: GET /fonts/roboto-v19-latin-300italic.woff2info - Serving file '../../../website/public/fonts/roboto-v19-latin-300italic.woff2'... By cutting from the 27th character onwards (-c 27-) we remove the timestamp and just get the log message. As a nice trick you can use the same syntax when splitting by fields: $ tail -n 3 ~/effective-shell/data/top100.csv | cut -d',' -f 3-\"Pinocchio (1940)\",\"55\"\"Chinatown (1974)\",\"75\"\"The Dark Knight (2008)\",\"342\" This is field three onwards. If we just want fields two and three, we use: $ tail -n 3 ~/effective-shell/data/top100.csv | cut -d',' -f 2,3\"100\",\"Pinocchio (1940)\"\"99\",\"Chinatown (1974)\"\"94\",\"The Dark Knight (2008)\" There's a surprising amount you can do with the cut tool. As we introduce more complex tools later on, like sed and awk, we'll see other ways to accomplish the same goals, but I often find that by filtering down the content with grep first I can cut my way to what I need without having to use more complex tools.","s":"How to Cut","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#how-to-cut","p":443},{"i":452,"t":"There is a very simple command called rev which reverses the given input. For example: $ echo \"A nut for a jar of tuna\" | revanut fo raj a rof tun A At first glance this doesn't seem very useful - but there's a nice trick we can do with this: $ pwd | rev | cut -d'/' -f 1 | reveffective-shell 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: pwd /Users/dwmkerr/effective-shellrev llehs-evitceffe/rrekmwd/sresU/cut -d'/' -f 1 llehs-evitcefferev effective-shell This is a neat trick to rip all of the text from the 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!","s":"A Trick with Rev","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#a-trick-with-rev","p":443},{"i":454,"t":"Two other commands which can be really helpful are sort and uniq. Let's see sort first: $ cut -d',' -f 3 ~/effective-shell/data/top100.csv | sort | head\"12 Years a Slave (2013)\"\"A Hard Day's Night (1964)\"\"A Night at the Opera (1935)\"\"A Quiet Place (2018)\"\"A Star Is Born (2018)\"\"Alien (1979)\"\"All About Eve (1950)\"\"Argo (2012)\"\"Arrival (2016)\"\"Avengers: Endgame (2019)\" Here we've grabbed the third field in our data file (the name of the film), sorted, then shown the first ten values. You can reverse the direction of sort with the -r flag: $ cut -d',' -f 3 ~/effective-shell/data/top100.csv | sort -r | head\"Zootopia (2016)\"\"Wonder Woman (2017)\"\"Won't You Be My Neighbor? (2018)\"\"Widows (2018)\"\"War for the Planet of the Apes (2017)\"\"Us (2019)\"\"Up (2009)\"\"Toy Story 4 (2019)\"\"Toy Story 3 (2010)\"\"Toy Story 2 (1999)\" There are actually quite a few other options for sort, you can see them with 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 uniq). You might find some of them useful so don't be shy to explore some of the other options. The uniq command removes duplicate lines from a stream of text. Note that this only removes duplicate lines when they are next to each other. This means that you will often have to sort first. Here's an example of where I might use uniq - getting all unique error messages in a log file: $ cut -c 27- ~/effective-shell/logs/web-server-logs.txt | grep error | sort | uniqerror - Unhandled error EACCES trying to read '../../../website/public/docs/part-1-transitioning-to-the-shell/5-getting-help/index.html', returning a 500error - Unhandled error EACCES trying to read '../../../website/public/svg/calendar.svg', returning a 500error - Unhandled error EACCES trying to read '../../../website/public/svg/edit.svg', returning a 500info - Request: GET /docs/1-getting-started/images/ls-applications-windows-error.pnginfo - Request: GET /docs/part-1-transitioning-to-the-shell/3-managing-your-files/images/rm-error-directory.pnginfo - Serving file '../../../website/public/docs/1-getting-started/images/ls-applications-windows-error.png'...info - Serving file '../../../website/public/docs/part-1-transitioning-to-the-shell/3-managing-your-files/images/rm-error-directory.png'... Let's break this down: cut -c 27- ~/effective-shell/logs/web-server-logs.txt - extract log messages from a log file, skipping the timestamp grep error - filter down to lines which contain the text error sort - sort the output uniq - show only unique values This is a powerful technique - if we had thousands of errors in the file, this would make sure we only see distinct errors, rather than showing every error.","s":"Sort and Unique","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#sort-and-unique","p":443},{"i":456,"t":"In Chapter 5 - Getting Help we talked about the 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 before you use head or tail) then you can use the pager to avoid filling your screen and terminal with too much text. For example, when looking at the sorted list of films, I might run this: $ cut -d',' -f 3 ~/effective-shell/data/top100.csv | sort | less \"Jaws (1975)\" \"King Kong (1933)\" \"La Grande illusion (Grand Illusion) (1938)\" \"La La Land (2016)\" \"Lady Bird (2017)\" \"Laura (1944)\" /Jaws 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 less so that I can page through the data and ensure it is correct - I've also searched for the text Jaws to see where it is in the file.","s":"Don't Forget Your Pager!","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#dont-forget-your-pager","p":443},{"i":458,"t":"In this chapter we introduced a number of basic tools which let us work with text. head will show the first ten lines of a file. head -n 30 will show the first thirty lines of a file, using the -n flag to specify the number of lines. tail will show the final ten lines of a file. tail -n 3 uses the -n flag to specify three lines only. The $HISTFILE environment variable holds the path to the shell command history file. tail -f $HISTFILE uses the -f flag to follow the file, printing output as it is written to the file. tr 'a' 'b' is the translate text command, which turns one set of characters into another tr -d '!' shows how the -d or delete flag can specify characters to delete. The cut command can be used to extract parts of a line of text. cut -d',' -f 3 shows how the -d or delimiter flag is used to specify the delimiter to cut on and how the -f or field flag specifies which of the fields the text has been cut into is printed. cut -c 2-4 uses the -c or characters flag to specify that we are extracting a subset of characters in the line, in this case characters two to four. cut -c 10- cuts from character ten to the end of the line The cut command also allows for multiple fields to be specified when cutting by field, such as -f 2,3 for the second and third field, or -f 4- for fields four onwards. rev reverses text - by reversing, cutting and then re-reversing you can quickly extract text from the end of a line. sort sorts the incoming text alphabetically. The -r flag for sort reverses the sort order. The uniq command removes duplicate lines - but only when they are next to each other, so you'll often use it in combination with sort. Your pager, for example the less program can be useful when inspecting the output of your text transformation commands.","s":"Summary","u":"/part-3-manipulating-text/slice-and-dice-text/","h":"#summary","p":443},{"i":460,"t":"On this page","s":"Build Commands on the Fly","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"","p":459},{"i":462,"t":"The xargs [build and execute commands] command takes input, uses the input to create commands, then executes the commands. I tend to remember it as \"Execute with Arguments\" as the name xargs sounds a little odd! How xargs is used is probably easiest to see with an example. Let's use it to build a set of commands which will remove any empty files from a folder. Before we show xargs let's create some empty files which we'll later clean up: $ mkdir -p ~/effective-shell/tmp$ cd ~/effective-shell/tmp$ touch file{1..100}.txt We're using a nice shell trick here called Brace Expansion - the shell will expand file{1..100}.txt into file1.txt, file2.txt and so on, all the way to file100.txt. We could just look for empty files in our /tmp folder for this example, but a file in that folder might be in use, so a safer way to demonstrate xargs is to use some temporary files which we create ourselves. We could search for empty files with the command below: $ find . -emptyfile1.txtfile2.txtfile3.txtfile4.txtfile5.txt... A refresher on Finding Files In this chapter we'll be using the find (find files and folders) command a lot - if you need a refresher, check Chapter 11 - Finding Files. The find command has outputted a list of files, now we want to use the rm (remove file) command to delete each one. Let's just pipe the list of files to the rm command, check Chapter 7 - Thinking in Pipelines if you need a reminder of how piping works: $ find . -empty | rmrm: missing operandTry 'rm --help' for more information. What's going on here? Well basically the issue is that the rm command doesn't actually read the list of files from stdin, the list of files has to be passed as a parameter to the command. How can we take this list of files and pass it to rm as a set of parameters? This is what xargs is for! Before we delete the files, let's just see what happens when we pass the list to xargs: $ find . -empty | xargs./file40.txt ./file8.txt ./file35.txt ./file81.txt ... By default xargs take the input, joins each line together with a space and then passes it to the echo command. The echo command writes it out to the screen. We can change the command xargs passes the arguments to: $ find . -empty | xargs echo rmrm ./file40.txt ./file8.txt ./file35.txt ./file81.txt ... Very interesting! Now we've told xargs to pass the output to the echo rm command - this just writes out rm followed by the list of files. Putting echo before whatever command you want to run is a useful way to check the command before we commit to running it. Let's finish the job and delete each file: $ find . -empty | xargs rm Done! You can run ls to confirm that the file has been deleted. This is xargs - it constructs and executes a command using arguments from standard input. Now let's see how we can take this further.","s":"Introducing Xargs","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#introducing-xargs","p":459},{"i":464,"t":"One common challenge with xargs is how to deal with spaces. To see what I mean, let's create three files with spaces in the names: $ touch \"chapter \"{1,2,3}.md$ find . -type f./chapter 1.md./chapter 2.md./chapter 3.md What if we wanted to delete these files? Let's try that with rm: $ find . -type f | xargs rmrm: cannot remove './chapter': No such file or directoryrm: cannot remove '1.md': No such file or directory... The file name has a space in it, which is confusing rm as it thinks we're providing six paths rather than three. We can use the -t (trace) option to see what xargs actually tried to do: $ find . -type f | xargs -t rmrm ./chapter 1.md ./chapter 2.md ./chapter 3.md... Hopefully you can spot the error - the rm command thinks it needs to remove six files, because there are spaces in the filenames and there are not quotes around the filenames to let rm know this! Fortunately, find loves xargs - they are part of the same package of tools (which is called 'findutils'). And there's a special pair of options that can deal with this. For find, we are going to use the -print0 action and for xargs we'll use the -0 option. Let's see how it looks now, then describe what's going on under the hood: $ find . -type f -print0 | xargs -0 -t rmrm './chapter 1.md' './chapter 2.md' './chapter 3.md' In Chapter 11 - Finding Files we saw that the default action of the find command is -print, which writes out the path of each item found. The -print0 action is very similar - but it instead it writes out each item followed by a special 'null' character1. Now that we've told find to end each result with a special 'null' character, we just tell xargs that the 'null' character is what separates each line of input. We do this with the -0 (use NUL as separators) option. You don't need to really understand the internals - if you are a computer programmer it might make sense, this is how strings in things like the C Programming language work. All you need to know is that it means the xargs program won't get confused when it sees spaces, tabs, quotes, newlines, or anything else which might be goofy in a file name. My recommendation would be to always pair up the -print0 action with the -0 option - it means you won't get caught out by odd file names. And definitely make use of the -t (trace) option to see what xargs is actually doing!","s":"Handling Whitespace, Special Characters and Tracing","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#handling-whitespace-special-characters-and-tracing","p":459},{"i":466,"t":"By default xargs takes all of the input and passes it as a set of arguments to the provided command. We can see this below: $ touch file{1..5}$ find . -type f | xargs echo./file1 ./file2 ./file3 ./file4 ./file5 We don't need to provide echo to xargs, it is the default, but I have added it for clarity. But what is really important is that we have called echo once and once only. xargs has passed all of the arguments it has been given to the command. We can tell xargs how many lines of input it should use for the command with the -L (max lines) parameter: $ find . -type f | xargs -L 1 echo./file1./file2./file3./file4./file5 We've now called the echo command once for each line of input, meaning that echo has been called five times. In general if you can provide all of the arguments to a single command the system may be able to process the command slightly faster. However, if there are lots of arguments, the command itself might not be able to handle all of the arguments you give it. You can set -L to other values too - xargs will use up to the number of lines provided: $ find . -type f | xargs -L 3 echo./file1 ./file2 ./file3./file4 ./file5 Here we've allowed up to three input lines per command. You will probably not use the -L parameter very often, but it is really important that you understand what it does. And that is because many of the other options we'll use imply -L 1 - we'll see why in the next example.","s":"One Command or Many Commands?","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#one-command-or-many-commands","p":459},{"i":468,"t":"You have probably noticed by now that the xargs command puts the arguments it is given at the end of the command you write. What if you need the arguments to go somewhere else? For example, what if I wanted to copy every text file in a folder to another location? Here's how we might start - and what'll go wrong! $ find . -name \"*.txt\" -print0 | xargs -0 -t cp ~/backupscp /home/dwmkerr/backups ./file2.txt ./file3.txt ./file1.txtcp: target './file1.txt' is not a directory The problem is that the destination location for where we copy the files has to be the last parameter - but xargs puts the list of files at the end of the command. And by the way - we've used the -0 parameter to make sure that funny filenames are handled properly (a good habit to get into) and the -t parameter to trace - which means we see the command which will be run. So we need to tell xargs where to put the list of arguments. We can do that with the -I (replace string) parameter. This parameter lets us tell xargs exactly where we want to put the arguments: $ find . -name \"*.txt\" -print0 | xargs -0 -t -I {} cp {} ~/backupscp ./file2.txt /home/dwmkerr/backupscp ./file3.txt /home/dwmkerr/backupscp ./file1.txt /home/dwmkerr/backups Here we have set the 'replacement string' to be {}. This means when xargs sees {} in the command it will replace it with the arguments we provide as its input. The first observation you might make is that as soon as we use the -I parameter it automatically implies that we use the -L 1 parameter, i.e. we run the command once for each individual input line. For the example we have shown above, this isn't really necessary, xargs could just write all of the arguments. The reason xargs does this is that we are not actually limited to using the replacement once only - we can use it multiple times. Here's a similar example, but in this one we put .bak at the end of each filename as we copy it: $ find . -name \"*.txt\" -print0 | xargs -0 -t -I {} cp {} ~/backups/{}.bakcp ./file2.txt /home/dwmkerr/backups/./file2.txt.bakcp ./file3.txt /home/dwmkerr/backups/./file3.txt.bakcp ./file1.txt /home/dwmkerr/backups/./file1.txt.bak Because we can use the replacement string multiple times, xargs splits up the commands so it is one command per input argument. If it didn't do this and we tried the command above it would not work properly. The -I parameter is incredibly powerful, it lets us construct complex commands. You don't need to use the {} letters as the replacement string, any sequence of characters will work. For example: $ env | xargs -I % echo \"You have env var: % set!\"You have env var: SHELL=/bin/bash set!You have env var: COLORTERM=truecolor set!You have env var: EDITOR=vi set! In this example we used % as the replacement string. You might wonder why is {} so commonly used in examples or the manpages. The reason is that this is the default replacement string used by find if we perform an action like -exec: $ find . -type f -empty -exec stat {} \\; The {} characters are used as the placeholder for files found with the find command, so people often use the same placeholder for xargs, but you are not required to use these characters.","s":"Constructing more complex commands with the 'I' Parameter","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#constructing-more-complex-commands-with-the-i-parameter","p":459},{"i":470,"t":"The -p (prompt) option tells xargs to ask the user to confirm each command before it is run. Let's test this out by deleting a set of 'pods' from a Kubernetes cluster. You don't have to worry about what a Kubernetes cluster is, I'm just using this as an example to highlight that you don't have to be limited to using find as the input for xargs! On my machine I can show the pods available to me in my cluster with this command: $ kubectl get pods -o namepod/my-apppod/nginxpod/postgres Three pods are shown. I could use this command to build input to xargs to let me to chose which pods to delete, interactively: $ kubectl get pods -o name | xargs -L 1 -p kubectl deletekubectl --context minikube delete pod/my-app?...nkubectl --context minikube delete pod/nginx?...ypod \"nginx\" deletedkubectl --context minikube delete pod/postgres?...n This is fantastic! We've used -L 1 to make sure that we only deal with one pod at a time (rather than trying to delete all three at once) and the -p flag to ask the user to press 'y' or 'n' in each case. The xargs command helpfully shows us what it is going to do and asks for confirmation first. I think this really hints at the true power of xargs - yes it can be combined with find to perform operations on files, but it can also be used with other tools to build more complex operations.","s":"Requesting Confirmation with the Prompt Option","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#requesting-confirmation-with-the-prompt-option","p":459},{"i":472,"t":"We can ask the user whether they want to see files in all of their 'path' locations with the command below: $ echo $PATH | xargs -d ':' -p -L 1 lsls /home/dwmkerr/.pyenv/shims ?...nls /home/dwmkerr/.nvm/versions/node/v14.15.1/bin ?...n The $PATH environment variable holds all of the folders the shell will search in for binaries - and each folder is separated by a : colon character (you can read more about $PATH in Chapter 10 - Understanding Commands. We use the -d (delimiter) parameter to tell xargs that each argument in the input is separated with a colon. We also use the -L 1 and -p parameters to process this input one folder at a time and ask the user if they want to see the contents of the folder.","s":"Splitting up Input with a Delimiter","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#splitting-up-input-with-a-delimiter","p":459},{"i":474,"t":"In this chapter we introduced xargs, a powerful command which allows us to build other commands on the fly. We can trace, showing how the resulting command will look, ask the user for confirmation, control how many commands we run and more. There are more options for the xargs command, you can read all about them with man xargs. But I think if you learn the key parameters we've shown in this chapter you'll be well equipped to use xargs in your day to day wok. In the next chapter we'll look at some of the advanced features which are built into most shells which allow us to manipulate text. The character is ASCII NUL, which is the number zero. This is often used in programming to represent 'null' or 'nothing at all', not the digit zero as is used when printing to the screen, which is actually represented by number 30. You can see the actual ASCII table with man ascii.↩","s":"Summary","u":"/part-3-manipulating-text/build-commands-on-the-fly/","h":"#summary","p":459},{"i":476,"t":"Part 4 - Shell Scripting 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. Shell scripting is the process of writing re-usable scripts, which you can use to automate repetitive work, create new programs and manage your environment. In this section we'll look at the fundamentals of how shell scripts work and how to write and structure shell scripts. We'll also look at some more advanced techniques to deal with more complex scenarios, tricks for debugging shell scripts and more.","s":"Part 4 - Shell Scripting","u":"/part-4-shell-scripting/","h":"","p":475},{"i":478,"t":"On this page","s":"Variables, Reading Input, and Mathematics","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"","p":477},{"i":480,"t":"Variables are places where the system, the shell, or shell users like ourselves can store data. We've already seen variables a few times in this book. For example in Chapter 5 - Getting Help we saw the $PAGER variable that is used to specify what pager program should be used in the shell. When we want to use a variable in the shell, we use the $ dollar symbol to specify the variable name: echo \"Your pager is: $PAGER\" If you run this command you will see something like this: Your pager is: less By convention, if a variable is in uppercase then it is an environment variable or a built in variable that comes from the shell. An environment variable is a variable that is set by the system. They often contain useful values to help configure your system. Here's a few common environment variables we might use: echo \"Your shell is: $SHELL\"echo \"Your user is: $USER\"echo \"Your user's home directory is: $HOME\" Your output will look similar to the below: Your shell is: /bin/bashYour user is: dwmkerrYour user's home directory is: /home/dwmkerr","s":"Variables","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#variables","p":477},{"i":482,"t":"You can create or set your own variables by simply entering the name you would like to use and putting an = equals symbol after the variable, followed by the value you would like to use. This is the one of the few times that you will use a variable name without putting a dollar symbol before it! name=\"Dave\"location=\"Singapore\"echo \"Hello $name in $location\" This will produce the output: Hello Dave in Singapore By convention, variables that you define yourself should be lowercase. This helps to distinguish between environment variables and your own variables. It is a good habit to use lowercase for variable names. Using uppercase will work, but when you use uppercase you run the risk of 'overwriting' the value of an environment variable and causing unexpected results later. For example, in this snippet I accidentally overwrite the USER variable. If a later part of the script expects the USER variable to contain the Linux username of the user then there will likely be an error because I have set it to something else! # Don't do this!USER=\"Dave Kerr\"# If I wanted to go to my home directory, this command would fail. That's# because USER should be 'dwmkerr' but I've set it to something else!cd \"/home/$USER\" If you set a system variable to something incorrect, the impact will be limited to only the script you are running or the shell session you are in, or any commands you run from the script or session - other running programs will not have their copy of the variable changed. You can read more about this in the Processes chapter of the Linux Fundamentals section.","s":"Setting Variables","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#setting-variables","p":477},{"i":484,"t":"The variables we create in the Shell are called Shell Variables. They are accessible in the current shell session that we are running. Shell variables are isolated to the current process. If we run another process from our shell, such as another shell script or program, our shell variables are not inherited by this process. This is by design - these shell variables are expected to be used for our local session only. If you want to ensure that a variable is available to all child processes, you can use the export (set export attribute) builtin to tell the shell to export the variable as an Environment Variable. Environment Variables are always inherited by child processes - so if you need to provide some kind of configuration or context to a child process, you will likely want to export your variable. As an example, let's set a variable to indicate whether we want to show some kind of extra diagnostic information to the user when running scripts: export DEBUG_MODE=1sh -c 'echo \"Debug Mode is: ${DEBUG_MODE}\"' Note that we are not using the DEBUG_MODE variable in the current script, we have provided a literal command to the sh program which will run in its own process. This process will inherit the shells environment and therefore can use the value of the DEBUG_MODE variable. If we did not use the export keyword then the value would be undefined in the child process. We also see another convention here - environment variables are generally capitalized. This can make them a bit more noticeable. These variables should be used with care, you could potentially overwrite a variable in your environment (and therefore the child environments) that another program has set. You can see a list of the current environment variables that are set with the env (set or print environment) command: envSHELL=/bin/zshLSCOLORS=ExFxBxDxCxegedabagacadCOLORTERM=truecolorPYENV_SHELL=bash# etc","s":"Shell Variables and Environment Variables","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#shell-variables-and-environment-variables","p":477},{"i":486,"t":"We can use a subshell to run a command and store the result in a variable. For example, if we had a variable which held a user's password and wanted to show it on the screen in a 'masked' form, where all of the characters are replaced with an asterisks symbol, we could write the password variable into the sed command and replace every character with an asterisks symbol like so: password=\"somethingsecret\"masked_password=$(echo \"$password\" | sed 's/./*/g')echo \"Setting password '${masked_password}'...\" The output of this script will look like this: Setting password '***************'... To execute a set of commands in a 'sub shell', we can use the $() sequence. Everything inside the brackets will be executed in a new shell. We can then store the output of the commands in a variable by using the = equals symbol.","s":"Storing the Output of a Command into a Variable","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#storing-the-output-of-a-command-into-a-variable","p":477},{"i":488,"t":"You can use curly braces around the name of a variable to be more explicit about what the variable name is. Let's take a look at why you might need to do this: echo \"Creating backup folder at: '$USER_backup'\"mkdir $USER_backup This script shows the output: Creating backup folder at: ''usage: mkdir [-pv] [-m mode] directory ... Rather than creating a folder called dwmkerr_backup (which is my $USER variable followed by the text _backup), the script has actually failed. That is because it is looking for a variable called USER_backup - which does not exist! To get around this we would surround the variable name with curly braces like so: echo \"Creating backup folder at: '${USER}_backup'\"mkdir \"${USER}_backup\" This script will show the correct output: Creating backup folder at: 'dwmkerr_backup' If there is ever any potential ambiguity with a variable name you should enclose it with curly braces to be on the safe side. Some people will use curly braces in all circumstances to be as explicit as possible about what the variable name is and reduce the risk of mistakes if someone later comes along to change the code. This script would be improved with the use of a variable of our own to avoid us having to repeat the ${USER}_backup text: backupdir=\"${USER}_backup\"echo \"Creating backup folder at: '${backupdir}'\"mkdir \"${backupdir}\" In this case creating a variable to save us from creating the backup directory folder name each time we want to use it. We've looked at environment variables and our own local variables. Now let's look at how we can read input from the user and store it in a variable for later usage.","s":"Being Explicit with Variable Names","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#being-explicit-with-variable-names","p":477},{"i":490,"t":"Arrays are variables that can store multiple values. An array is created by using the equals symbol and putting the array values in parenthesis, like so: days=(\"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\" \"Sunday\") Once you have defined your array you can retrieve an element at a given index by using the square bracket notation shown below: echo \"The first day is: ${days[0]}\"echo \"The last day is: ${days[6]}\" Arrays in Bash start at index zero. Arrays in the Z-Shell start at index one - this can cause confusion and mistakes in scripts so it is something you might have to consider if you are writing scripts that can be used by either shell. There are a number of useful operations you can perform on arrays. An example of each is shown below: Operation Syntax Example Create Array array=() days=(\"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\" \"Sunday\") Get Array Element ${array[index]} echo ${days[2]} # prints 'Wednesday' Get All Elements ${array[@]} echo ${days[@]} # prints 'Monday Tuesday Wednesday Thursday Friday Saturday Sunday' Set Array Element array[index]=value days[0]=\"Mon\" Get Array Indexes ${!array[@]} arr=(); arr[3]=\"apple\"; arr[5]=\"pear\"; echo ${!arr[@]} # prints 3 5 Get Array Length ${#array[@]} echo ${#days[@]} # Prints 7 Append to Array array+=(val1 val2 valN) fruits=(); fruits+=(\"Apples\"); fruits+=(\"Pears\" \"Grapes\"); echo ${fruits[@]} # prints 'Apples Pears Grapes' Get a subset of elements ${array[@]:start:number} echo ${days[@]:5:2} # prints 'Saturday Sunday' It's important to use curly braces around your array expressions. Note that in the examples above when we set an array value we don't use braces or the dollar symbol - this is consistent with what we've seen so far - variable names do not have a dollar symbol when we are setting a value. You might have noticed from the examples that arrays in Bash can be sparse - that means that you can have 'gaps' in your array. Arrays can also have a mixture of strings or numbers - not every element in an array has to be of the same type. We'll see arrays in more detail in the chapter on Loops.","s":"Arrays","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#arrays","p":477},{"i":492,"t":"More recent versions of Bash support the concept of Associative Arrays. These are arrays where rather than having a numeric index associated with each value, you can have a string. This allows you to create a 'map' or 'hash table' structure. An associative array is created using the declare (set variable) command: # Create an associative array called 'book'.declare -A book# Set some values on the array.book[title]=\"Effective Shell\"book[author]=\"Dave Kerr\"# Show one of the values.echo \"Book details: ${book[title]} - ${book[author]}\" Running this command will show the output: Book details: Effective Shell - Dave Kerr If you find yourself using associative arrays, I expect that there is a good chance you are trying to do something that is more complex than is suitable for a shell script. In this circumstance I'd suggest you read Chapter 30 - How to Avoid Scripting to see how I'd look at alternative options!","s":"Associative Arrays","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#associative-arrays","p":477},{"i":494,"t":"There is often a lot of confusion about a specific topic in the shell - when should you surround a variable in quotes? This might sound like a purely stylistic question, but surrounding a variable in quotes can dramatically change how your script works. We're going to look at each type of quoting and when it should be used in the examples below. But if you ever need a reminder, run man bash and search for the text QUOTING.","s":"Quoting Variables and Values","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#quoting-variables-and-values","p":477},{"i":496,"t":"Use single quotes when declaring a variable or using a value if you want to use literal text. The shell will not expand special characters or variables: message=' ~~ Save $$$ on with ** \"this deal\" ** ! ~~ 'echo \"$message\" This script will show: ~~ Save $$$ on with ** \"this deal\" ** ! ~~ Note that the shell has not tried to expand the ~ tilde into /home/dwmkerr. It has not expanded the * asterisks into a wildcard pattern and it has not tried to use the $ dollar symbol to reference an array. Single quotes should be used when you want to put special characters into a variable, or call a command that includes whitespace or special characters.","s":"Single Quotes - Literal Values","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#single-quotes---literal-values","p":477},{"i":498,"t":"There is a special form of single quotes called 'ANSI C Quoting' that allows you to use escape sequences from the C language. ANSI C quoting is single quoting that starts with a dollar symbol. You can use it to use special characters like newlines in a variable: message1='Hello\\nWorld'message2=$'Hello\\nWorld'echo \"Message 1: $message1\"echo \"Message 2: $message2\" This snippet will show the following results: Message 1: Hello\\nWorldMessage 2: HelloWorld","s":"Single Quotes - ANSI C Quoting","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#single-quotes---ansi-c-quoting","p":477},{"i":500,"t":"Double quotes work in a very similar way to single quotes except that they allow you to use parameter expansion with the $ dollar symbol and escaping with the \\ symbol. The ```` backtick symbol is also treated differently. Let's see some examples: deal=\"Buy one get one free\"message=\"Deal is '$deal' - save \\$\"echo \"$message\" The output of this snippet is: Deal is 'Buy one get one free' - save $ Notice that the $deal value has been expanded into the contents of the $deal variable. The last dollar symbol has been escaped with a \\ backslash - the shell knows that this means we want to use the literal value of the dollar symbol at the end of the message. The backslash has been removed. The backtick character is also treated differently, as the backtick can be used to run a sub-shell: $ echo \"The date is `date`\"The date is Sun 23 May 2021 11:36:54 AM +08 However, you should not use the backtick character to run a sub-shell, it is harder to read than using the dollar and parenthesis syntax we've already seen: $ echo \"The date is $(date)\"The date is Sun 23 May 2021 11:36:54 AM +08","s":"Double Quotes - Parameter Expansion","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#double-quotes---parameter-expansion","p":477},{"i":502,"t":"If you don't include quotes around a variable or value, then the shell will perform a series of operations called Shell Expansion. This includes many options we've seen so far, let's take a look at some examples: home=~tilde=\"~\"echo \"My home is: $home\"echo \"A tilde is: $tilde\" This snippet shows the results: My home is: /home/dwmkerrA tilde is: ~ In the first case the shell has expanded the ~ tilde to the home directory. We do not use quotes around a variable or a value if we want the shell to shell expansion. The following expansions will be performed: Brace expansion: touch file{1,2,3} is expanded to touch file1 file2 file3 Tilde expansion: cd ~ is expanded to cd /home/dwmkerr Parameter and variable expansion echo $SHELL is expanded to echo /usr/bin/sh (note that this expansion also occurs with double quotes) Command substitution: echo $(date) is expanded to echo the results of the date command (this also occurs with double quotes) Arithmetic expansion: square=$((4 * 4)) has the value 4 * 4 evaluated mathematically (we see this at the end of this chapter) Word splitting: this is a more complex topic discussed in Chapter 21 - Loops and working with Files and Folders Pathname expansion: ls *.txt is expanded to all filename that match the wildcard pattern *.txt We are going to see more detail on Shell Expansion as we continue through this part of the book. There is also a detailed explanation in Chapter 29 - Understanding Shell Expansion final section of the book and an appendix with a quick reference.","s":"No Quotes - Shell Expansion","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#no-quotes---shell-expansion","p":477},{"i":504,"t":"Quoting can seem confusing - but remember these tips and you will generally be on the right path: Use double quotes most of the time - they will handle variables and sub-shells for you and not do weird things like word splitting Use single quotes for literal values Use no quotes if you want to expand wildcards","s":"Quoting Tips","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#quoting-tips","p":477},{"i":506,"t":"Shell Parameter Expansion is the process by which the shell evaluates a variable that follows the $ dollar symbol. In most of our examples we simply expand the variable into its value, like so: $ echo \"My shell is $SHELL\"My shell is: /usr/bin/sh But there are a number of special features we can use when expanding parameters. There are many options available and you can find them all by running man bash and searching for the text EXPANSION. Let's take a look at some of the most common ones. Length The ${#var} operator returns the length of the variable var: var=\"The quick brown fox jumps over the lazy dog\"length=${#var}echo \"Length: $length\"# Prints: 43 Set Default Value The ${var:-default} operator returns the value of the variable var or the text default if it is not found: read -p \"Enter your username: \" userusername=${user:-$USER}echo \"Username: $username\"# Prints what you typed or the value of $USER otherwise Substring The ${var:start:count} operator returns a subset of the var variable, starting at position start and extracting up to count characters. If count is omitted everything from start to the end of the string is returned. path=\"~/effective-shell\"echo \"${path:0:2}\"# Prints ~/echo \"${path:2}\"# Prints effective-shell Make Uppercase The ${var^^} operator returns the value of var with the text transformed to uppercase: message=\"don't shout\"echo ${message^^}# Prints: DON'T SHOUT Make Lowercase The ${var,,} operator returns the value of var with the text transformed to lowercase: message=\"DON'T SHOUT\"echo ${message,,}# Prints: don't shout Variable Indirection The ${!var_name} operator returns the value of the variable with the name in specified in the var_name variable. This is useful if you want to get the value of a variable but don't know the name of the variable: read -p \"Enter a variable name: \" var_nameecho \"The value of '${var_name}' is: ${!var_name}\" The output of this snippet would look like this: $ Enter a variable name: SHELLThe value of 'SHELL' is: /bin/bash Notice the similarity to the Array operators such as ${#array[@]} to get the length of an array. There are a number of other operators that exist. They allow you to extract parts of a string, apply regular expressions, manipulate the case and perform a number of complex operations. I would avoid these techniques if possible as they are fairly specific to Bash and likely will be confusing to readers. Some of these substitutions are not available in older versions of Bash. If you need to manipulate text I would recommend that you use the techniques described in Part 3 - Manipulating Text. It is generally enough to know that if you see special symbols inside a ${variable} expression then the writer is performing some kind of string manipulation. Hopefully they have included a comment that describes what they are doing to make it easier to follow! You can find out more about these features in the manual under the EXPANSION section1.","s":"Shell Parameter Expansion","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#shell-parameter-expansion","p":477},{"i":508,"t":"The read (read from standard input) command can be used to read a line of text from standard input. When the text is read it is put into a variable, allowing it to be used in our scripts. Let's see how this look in action! echo \"What is your name?\"readecho \"Hello, $REPLY\" Run the script - when you have finished writing your name, press 'enter'. This is needed because read will keep on reading until it reaches the end of a line, so we need to press 'enter' to complete the input. What is your name?DaveHello, Dave The read command reads a line of text from standard input and stores the result in a variable called REPLY. We can then use this variable to use the text that was read. Why is the variable in uppercase? That's because even though we are setting the variable itself, it is still a 'special' variable defined by the shell. It is the variable that read puts its input into if we don't explicitly tell read what the variable name should be. Reading into a Variable​ We can tell the read command to put the input it reads into a variable with a name of our choice by specifying the variable name after the command, like so: echo \"What is your name?\"read nameecho \"Hello, ${name}\" In general you should provide a variable name for read - it will make your script a little easier to understand. Not every user will know that the $REPLY variable is the default location, so they might find it confusing if you don't provide a variable name. By specifying a variable name explicitly we make our script easier to follow. This also shows good coding practices - your variable names should be descriptive, and inform the reader of what they are likely to be used for. This makes the script easier to follow and maintain over time. This is another time that we use a variable name without putting a dollar before it. It might be helpful to remember that the dollar is used when we want to use the variable and the dollar is omitted when we want to set the variable. Prompting for Input​ Before you run the read command you are probably going to write a message to the user letting them know they need to enter some input. We can either write out a message first to prompt the user, using the echo command as shown above, or we can use the special -p (prompt) parameter: read -p \"Please enter your name: \" nameecho \"Hello, $name\" Now the output will look like this: Please enter your name: DaveHello, Dave Z-Shell Note If you are using the Z-Shell, then this command will fail as zsh does not use the -p parameter for prompt. To prompt a user for input with the read command in zsh, just put a line of text after the command that starts with a question mark: read \"?Please enter your name: \"echo \"Hello, $REPLY\" Reading Secrets​ The -s (silent) flag can be used to hide the input as it is being written. This is useful if you want to read a secret such as a password: read -s -p \"Enter a new password: \" passwordmasked_password=$(echo \"$password\" | sed 's/./*/g')echo \"\"echo \"Your password is: $masked_password\" The output of this script will be something like the below: Enter a new password:Your password is: ******** This uses the same trick as before to mask the characters. Note that when we use the -s flag, the read command does not print what we've typed - meaning we don't print the 'enter' symbol that the user presses to finish entering text. This means we don't see a new line after the read command. So we use echo \"\" to write a newline before we show the output. Limiting the Input​ There may be times where you don't want to have the user press 'enter' to indicate that they have finished writing input. There are a couple of ways we can limit the input. The first is to use the -n (number of characters) parameter to limit the number of characters that are read: read -n 1 -p \"Continue? (y/n): \" yesornoecho \"\"echo \"You typed: ${yesorno}\" This script will only wait for the user to type a single character as we used the -n flag with the value 1 to specify that we want to read a single character only. Because the user doesn't press 'enter' at the end of their input, we need to add a blank newline before we show the output - otherwise it would look like this: Continue? (y/n): nYou typed: n It's only when we read a full line of text that we don't need to write an empty line. That's because when we read a full line of text we finish by pressing 'enter', which moves the cursor down to the next line for us. The other way to limit the input is to specify a character that is to use a delimiter to indicate when read should stop reading input: read -d ' ' -p \"Enter your favourite word (then a space): \" wordecho \"\"echo \"Your favourite word is: ${word}\" Because we used the -d ' ' parameter, the read command will read up until it finds a 'space' symbol. This can be confusing for users however - if they press enter then read will read it as a newline and continue waiting for a space. So you should let the user know to finish input with the delimiter you have chosen! In general using anything other than a newline as the delimiter may be confusing to the user, and also causes some problems when the user wants to type special characters such as backspace, so I would suggest that you avoid this trick. Instead, let the user type their input and then use something like sed to extract everything up to the point that you want. There are a number of other options for the read command that you can read about by typing help read. But these are the ones that I think you will see most commonly used.","s":"The Read Command","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#the-read-command","p":477},{"i":510,"t":"The shell has some built in features that let you perform mathematical operations. You will commonly perform these operations on variables. You might assume that you can use symbols like + directly in the your scripts to perform mathematical operations - but they may not perform as expected. For example, here's what happens if you try to add two numbers together with the + plus symbol: read -p \"Enter a number: \" number1read -p \"Enter another number: \" number2sum=$number1+$number2echo \"The sum of $number1 and $number2 is $sum\" If you run this script you'll see something like this: Enter a number: 23Enter another number: 34The sum of 23 and 34 is 23+34 The result we see is not the sum of the two numbers - it is the two numbers with the literal + plus symbol between them. To tell the shell that we want to perform an arithmetic operation, rather than just write out a mathematical operator, we use the 'double parenthesis' syntax shown below: read -p \"Enter a number: \" number1read -p \"Enter another number: \" number2sum=$(($number1 + $number2))echo \"The sum of $number1 and $number2 is $sum\" The output of this script will be something like the below: Enter a number: 23Enter another number: 34The sum of 23 and 34 is 57 There is an alternative syntax - we can use the let keyword to indicate to the shell that we want to perform an arithmetic operation. This would look like this: let sum=$number1 + $number2 I've included the let keyword here for completeness, but I would recommend that you use the double-parenthesis where possible as I think that it is probably the more commonly used construct. There are many arithmetic operators available. Here's a table showing a few common ones and how they are used: Operator Meaning Example + Addition echo $((3+4)) # prints 7 - Subtraction echo $((4-2)) # prints 2 * Multiplication echo $((4*2)) # prints 8 / Division echo $((4/2)) # prints 2 ** Exponent echo $((4**3)) # prints 64 % Modulus echo $((7%3)) # prints 1 ++i Prefix Increment i=1; echo $((++i)) # prints 1, i is set to 2 i++ Postfix Increment i=1; echo $((i++)) # prints 2, i is set to 2 --i Prefix Decrement i=3; echo $((--i)) # prints 3, i is set to 2 i-- Postfix Decrement i=3; echo $((i--)) # prints 2, i is set to 2 i+=n Increment i=3; echo $((i+=3)) # prints 6, i is set to 6 i-=n Decrement i=3; echo $((i-=2)) # prints 1, i is set to 1 If you want to find the complete set of arithmetic operators available or find more details on how arithmetic works in the shell, use man bash and search for the text ARITHMETIC\\ EVALUATION (the backslash is needed to escape the space between the words when searching in the manual). The script below shows how you can use a combination of operators to convert a value in degrees Celsius to Fahrenheit: read -p \"Enter a value in Celsius: \" celciusfahrenheit=$(( (celcius * 9/5) + 32 ))echo \"${celcius} degrees Celsius is ${fahrenheit} degrees Fahrenheit\" Note that you can use brackets in your arithmetic expressions to be explicit about the order in which the calculations should be performed. The order that is used if you don't use brackets is detailed in the manual page, but in general using brackets will make things clearer to the reader.","s":"Mathematics","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#mathematics","p":477},{"i":512,"t":"With our new understanding of variables, we can improve the 'common' command we created in the previous chapter by extracting certain values into variables so that they can be more easily changed. Let's look at our original 'common' command: # Write the title of our command.echo \"common commands:\"# Show the most commonly used commands.tail ~/.bash_history -n 1000 | sort | uniq -c | sed 's/^ *//' | sort -n | tail -n 10 We could improve on this by making the number of lines of text in the history we search through and the number of commands to show variables, so that they can be more easily changed. Create a copy of the common.v1.sh script and call it common.v2.sh and update it like so: # Write the title of our command.echo \"common commands:\"# The following variables control how the command runs.history_lines=1000 # The number of lines of history to search throughcommand_count=10 # The number of common commands to show.# Show the most commonly used commands.tail ~/.bash_history -n ${history_lines} \\ | sort \\ | uniq -c \\ | sed 's/^ *//' \\ | sort -n -r \\ | head -n ${command_count} We have replaced two 'hard-coded' values (the number of lines of history to search and the number of common commands to show) with variables, which are now easier to find and change. We have also split the command into multiple lines so that it is easier to read (as the line is quite long otherwise). If you want to replace the installed common command with this new one, update the symlink in your /usr/local/bin folder: ln -sf $HOME/effective-shell/scripts/common.v2.sh /usr/local/bin/common Note that in this command we use the -f flag to force the creation of the symlink even if one already exists in the given location.","s":"Updating the 'Common' Command","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"#updating-the-common-command","p":477},{"i":514,"t":"In this chapter we looked at how environment variables work and how we can use our own variables. We saw how to read input from the user and how to perform arithmetic operations. We've seen a few new constructs in this chapter that will appear again and again, these are summarised below so that you can recognise them! ${variable} gets the value of variable - the braces surround the variable name $(echo \"$PAGER\") runs the echo command in a subshell - the single parenthesis indicates we are running a subshell $(($left + $right)) adds the values in the variables left and right - the double parenthesis indicate that we are performing arithmetic In the next chapter we are going to see how to perform logic in scripts - running commands only when certain conditions are met. This is an incredibly powerful technique and will let you create much more sophisticated scripts! There is also a very good discussion on the differences in quoting options in the following Stack Overflow thread: https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable↩","s":"Summary","u":"/part-3-manipulating-text/variables-reading-input-and-mathematics/","h":"","p":477},{"i":516,"t":"On this page","s":"Functions, Parameters and Error Handling","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"","p":515},{"i":518,"t":"A function has the following structure: { } 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. Let's take a look at a very simple function in action: title() { echo \"My Script version 1.0\"} This script defines a very simple function called 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: $ titleMy Script version 1.0\" Easy! Functions let you structure commands into logical blocks and can help make your scripts easier to read and manage.","s":"Creating a Function","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#creating-a-function--index-","p":515},{"i":520,"t":"A function can read and write to any variables in the current shell session. Here's an example: # Set some variables.title=\"My Cool Script\"version=\"1.2\"succeeded=0# Create a function that writes a message and changes a variable.title() { # Note that we can read variables... title_message=\"${title} - version ${version}\" echo \"${title_message}\" # ...and set them as well. succeeded=1}# Show the value of 'succeeded' before and after the function call.echo \"Succeeded: ${succeeded}\"titleecho \"Succeeded: ${succeeded}\"echo \"Title Message: ${title_message}\" The output of this script will be: Succeeded: 0My Cool Script - version 1.2Succeeded: 1Title Message: My Cool Script - version 1.2 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.","s":"Variables in Functions","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#variables-in-functions","p":515},{"i":522,"t":"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 dynamic scoping. Many common programming languages like Python, JavaScript, C, Java and others use an alternative mechanism called lexical scoping. 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. 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: run_loop() { local count=0 for i in {1..10}; do # Update our counter. count=$((count + 1)) done echo \"Count is: ${count}\"} Let's see what happens if we run this function: $ run_loopCount is: 10$ echo \"Count: ${count}\"Count: Notice that because we declared the count variable using the 'local' keyword, it is only available inside the run_loop function. If we try and access it outside of the function it is undefined. 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: # Set a count variable somewhere in our script...count=3# Call our 'run_loop' function.run_loop# Write out the value of 'count'.echo \"The 'count' variable is: ${count}\" The output of this script is: Count is: 10The 'count' variable is: 3 Notice that even though we used a variable named count in the 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: Count is: 10The 'count' variable is: 10 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.","s":"Variable Scoping","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#variable-scoping-index","p":515},{"i":524,"t":"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: sum() { local value1=$1 local value2=$2 local result=$((value1 + value2)) echo \"The sum of ${value1} and ${value2} is ${result}\"} Let's see how we can pass parameters to this function: $ sum 3 6The sum of 3 and 6 is 9$ sum 10 33The sum of 10 and 33 is 43 In this script we have used the special $1 and $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: # Create a function that calculates the sum of two numbers.sum() { echo \"The sum of $1 and $2 is $(($1 + $2))\"} 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.","s":"Passing Parameters to Functions","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#passing-parameters-to-functions","p":515},{"i":526,"t":"There are a number of special parameter variables that the shell provides. Let's see a few in action: # Create a function that sums a set of numbers.sum() { local total=0 for value in $@; do total=$((total + value)) done # Write out the result. echo \"Summed $# values for a total of: ${total}\"} We can call this function with any number of parameters: $ sum 1 2 3 4 5Summed 5 values for a total of: 15 In this script we've used two special variables. The $@ variable is expanded into a list of all of the function parameters. The $# variable contains the number of parameters provided to the function. 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 Chapter 19 - Variables, Reading Input, and Mathematics. You can actually use some of the array-style operators with the special parameters variables: # Show the top 'n' values of a set.show_top() { local n=$1 local values=${@:2:n} echo \"Top ${n} values: ${values}\"} We can call this function with any number of parameters. The first parameter specifies how many of the subsequent parameters we will show: $ show_top 3 10 20 30 40 50Top 3 values: 10 20 30 We have used the 'range' operator on the $@ 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. The table below shows some of the common variables you can use when working with function parameters: Variable Description $1 The first parameter $2 The second parameter ${11} The 11th parameter - if the parameter is more than one digit you must surround it with braces $# The number of parameters $@ The full set of parameters as an array $* The full set of parameters as a string separated by the first value in the $IFS variable ${@:start:count} A subset of 'count' parameters starting at parameter number 'start' The $@ and @* parameters look quite similar. The first one is an array, just like we saw in 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 $IFS variable).","s":"Parameter Variables","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#parameter-variables","p":515},{"i":528,"t":"We can use the shift (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. This is a little hard to describe so let's see how we can use it to simplify our show_top function: # Show the top 'n' values of a set.show_top() { # Grab the number of values to show, then shift. local n=$1 shift # Get the set of values to show. Notice that we start in position 1 now. local values=${@:1:n} echo \"Top ${n} values: ${values}\"} 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!","s":"Parameter Shifting","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#parameter-shifting","p":515},{"i":530,"t":"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: is_even() { local number=$1 # A number is even if when we divide it by 2 there is no remainder. # Set 'result' to 1 if the parameter is even and 0 otherwise. if [ $((number % 2)) -eq 0 ]; then result=1 else result=0 fi} A function could set any number of variables to provide output. Here's how we could use the is_even function: $ number=33$ is_even $number$ echo \"Result is: $result\"Result is: 0 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. A more common way to return a value from a function is to write its result to stdout - let's look at this in detail.","s":"Return Values","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#return-values","p":515},{"i":532,"t":"If we write our result to stdout, then we can capture the result of a function in a far more readable way: lowercase() { local params=\"$@\" # Translate all uppercase characters to lowercase characters. echo \"$params\" | tr '[:upper:]' '[:lower:]' } In this example we write the result of the function to stdout. This means that we can capture the result and put it in another variable by simply executing the command in a subshell: $ result=$(lowercase \"Don't SHOUT!\")$ echo \"$result\"don't shout! If you have a programming background it might seem very strange that you write results in a function by writing to 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 ls does, what find does, what cat does and so on. When we echo a result from a function, we are really just following the Unix standard of writing the results of a program to the screen. This is important - if we run our function directly in a shell, we can see the result written to the screen: $ lowercase \"PLEASE don't SHOUT!\"please don't shout! Remember - shell functions are designed to behave in a similar way to shell commands. They write their output to stdout.","s":"Writing Results to Stdout","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#writing-results-to-stdout","p":515},{"i":534,"t":"Although it might feel a bit clunky, writing the results of a command to 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! # This function creates a temporary folder for today and returns its path.temp_today() { # Get today's date in the format YYYY-MM-DD. local today=$(date +\"%Y-%m-%d\") # Create a temporary directory for today and return it. tmpdir_today=\"/tmp/${today}\" echo \"Creating folder '${tmpdir_today}'...\" mkdir -p \"${tmpdir_today}\" echo \"${tmpdir_today}\"} 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: # Go to today's temporary folder.folder=$(temp_today)cd \"${folder}\" This script fails, with the output: 'Creating folder \\'/tmp/2021-05-28\\'...\\n/tmp/2021-05-28': No such file or directory What's going on here? Well in the 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 all of the text we have written. It is important to remember that any command you call in a function that might write to stdout could cause problems as it could write text to your output: command_exists() { if type \"$1\"; then echo \"1\" else echo \"0\" fi} What happens when we try and store the result of the function in a variable? $ result=$(command_exists \"touch\")$ echo \"Result is: ${result}\"Result is: touch is hashed (/usr/bin/touch)1 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 type is used to find out whether a command exists it returns success if the command exists but also writes to stdout. In 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: command_exists() { if type \"$1\" >> /dev/null; then echo \"1\" else echo \"0\" fi} Now if we run this command we will not get the output from the type command in our result - the output was redirected to the null device.","s":"Dealing with Output in Commands","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#dealing-with-output-in-commands","p":515},{"i":536,"t":"The return (return from shell function) command causes a function to exit with a given status code. 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 status code of the function. What is a status code? We actually touched on this in Chapter 20 - Mastering Conditional Logic. When a command runs, we expect it to return a status code of 'zero' to indicate success. Any non-zero status code is used to specify an error code. Let's see how we could re-write the command_exists function to set a status code: command_exists() { if type \"$1\" >> /dev/null; then return 0 else return 1 fi} Now that our command sets a status code properly, we can use it in an 'if statement' like so: if command_exists \"common\"; then echo \"The 'common' command is installed on your system\"else echo \"The 'common' command is not installed on your system\"fi 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 stdout or if you must, set the value of a global variable. The result of the last executed command is always available in the special variable $?. Here's how you could use it: $ type \"test\"test is a shell builtin$ echo \"Result: $?\"Result: 0","s":"Returning Status Codes","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#returning-status-codes","p":515},{"i":538,"t":"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. 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: #!/usr/bin/env sh# Get today's date in the format YYYY-MM-DD.today=$(date +\"%Y-%m-%d\")# Create the path to today's temp folder and then make sure the folder exists.temp_path=\"/tmp/${today}\"mkdir -p \"${temp_path}\"# Now that we've created the folder, make a symlink to it in our homedir.ln -sf \"${temp_path}\" \"${HOME}/today\" # Write out the path we created.echo \"${temp_path}\" Now we can run the script to create temporary folder for the current day and a link to it in our home directory: $ chmod +x ./today.sh$ ./today.sh/tmp/2021-05-28$ cd ~/today In this example we created a new directory in the tmp folder and created a link to it in our home directory. But what happens if we cause one of the commands to fail? First, let's clean up the folder we created: $ rm -rf $(./today.sh)$ rm ~/today Now we'll create a file where we want to put our 'today' folder: $ touch \"/tmp/$(date +\"%Y-%m-%d\")\" If we run our script now, we can see a problem: $ ./today.shmkdir: /tmp/2021-05-28: Not a directory/tmp/2021-05-28$ cd ~/todaybash: cd: /home/dwmkerr/today: Not a directory The mkdir command failed - because there was a 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 today folder we get another error - it is a link to a file not a folder. 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. You can use the set (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: #!/usr/bin/env sh# Exit if any command fails.set -e# ... 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'. Now let's clean up again: $ rm -rf $(./today.sh)$ rm ~/today And finally, we'll run the same script after creating the file that will cause a failure: $ touch \"/tmp/$(date +\"%Y-%m-%d\")\"$ ./today.shmkdir: /tmp/2021-05-28: Not a directory In this case the script stopped running as soon as there was a failure - after the mkdir command failed. One thing to be aware of is that the set -e option only affects the final command of a pipeline. This means that if you have a pipeline such as the below: grep '[:space:]*#' ~/effective-shell/scripts/common.sh | tr 'a-z' 'A-Z' Then the script will still run if the grep command fails. To ensure that the shell terminates if a command in a pipeline fails we must set the pipefail option: set -o pipefail 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 might fail are properly handled. 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 if block to 'catch' the error and show a warning: if ! [ rm ~/my-file.text ]; then echo \"warning: unable to delete file\"fi Another option would be to use a conditional expression: rm ~/my-file.txt || true This expression always evaluates to 'true' so will not stop the script if an error occurs when running the rm command.","s":"Error Handling","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#error-handling","p":515},{"i":540,"t":"In some scripts you might see functions defined using the function keyword, as below: function title() { echo \"My Script version 1.0\"} 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 alias with the same name as the function you are declaring. However, the drawback is that it is less standard and therefore less portable. 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!","s":"The Function Keyword","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#the-function-keyword","p":515},{"i":542,"t":"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 $1, $2 and so on. The only difference is that instead of using the return command when we want to exit a script with a status code, we use the exit (exit the shell) command. The exit command exits the current shell with the provided status code. Be careful when using the exit command - if you are running a script then it is fine to use exit, it will simply close the subshell that the script is running in. But if you type exit in your shell that you are using interactively, it will close it.","s":"Parameters and Status Codes for Scripts","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#parameters-and-status-codes-for-scripts","p":515},{"i":544,"t":"In the previous chapter we created the common.v4.sh command, that shows common commands from the users shell history. If you need a refresher on what is in the script, you can view it in your pager with: less ~/effective-shell/scripts/common.v4.sh The output of the command will look something like: 1: 280 gst2: 144 vi3: 84 gc4: 72 ga .5: 62 gl6: 54 ls7: 50 gpo8: 48 gcm9: 45 make dev10: 44 gpr Let's make a couple of changes. First, let's make sure we exit the script if one of the commands fails: # Exit if any command fails.set -e 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: # ...command_count=${1:-10} # The number of common commands to show# ... Here we are using the $1 variable. But we are also using Shell Parameter Expansion as described in Chapter 19 - Variables, Reading Input, and Mathematics to provide a default value to use if the parameter is not set. 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 after the text and in brackets: write_command_then_count() { # Get the command and count, this will be text that looks like: # '43 git commit' # Then write the command and the count afterwards. local line=\"$1\" local count=$(echo \"${line}\" | cut -d' ' -f1) local command=$(echo \"${line}\" | cut -d' ' -f2-) echo \"${command} (${count})\"} We can now re-write our loop to make it a little cleaner: for command in $commandsdo echo \"$counter: $(write_command_then_count \"$command\")\" counter=$((counter + 1))done The updated script is in the samples folder at ~/effective-shell/scripts/common.v5.sh, you can update your link to point to this version by running the ln command: ln -s ~/effective-shell/scripts/common.v5.sh /usr/local/bin/common 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 after the command text itself: $ common 5common commands:1: gst (139)2: vi (74)3: gc (42)4: ga . (36)5: gl (31)","s":"Updating the 'common' Command","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#updating-the-common-command","p":515},{"i":546,"t":"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. 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.","s":"Summary","u":"/part-4-shell-scripting/functions-parameters-and-error-handling","h":"#summary","p":515},{"i":548,"t":"On this page","s":"Mastering Conditional Logic","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"","p":547},{"i":550,"t":"We can use the if statement to perform operations in shell scripts only when certain conditions are met. The if statement has the following structure: if then fi The if statement will run the 'test commands'. If the result of the commands are all zero (which means 'success'), then each of the 'conditional' commands will be run. We 'close' the if statement with the fi keyword, which is if written backwards. Let's see how the if statement is used with a simple example. We will try and create a folder using mkdir. The mkdir command will return zero if the folder is created successfully: if mkdir ~/backupsthen echo \"Successfully created the 'backups' folder\"fi If you don't have a folder called backups in your home directory then the command will run successfully. The mkdir command will return zero and the conditional statements will be run and you will see the output below: Successfully created the 'backups' folder If you then run the script again, the mkdir command will fail. In this case it does not return zero and the conditional commands are not executed. We will see an error message from the mkdir command: mkdir: /home/dwmkerr/backups: File exists This is the basics of how the if statement works. We provide test commands, if the test commands succeed, a set of conditional commands are then executed. You might be surprised to hear that the result of the test commands has to be zero for the conditional commands to run. This is the opposite of how most programming languages work - normally zero would be considered 'false'. The reason for this - is that for computer programs that run, 'zero' generally means success. Any non-zero value is typically used to indicate an error code. So whilst inside a programming language, an if statement will check for a value to be 'true', just remember that in the shell an if statement will check for a command to be successful.","s":"The If Statement","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-if-statement-index-","p":547},{"i":552,"t":"The test (evaluate expression) command is used to check whether a certain condition is true or not. If the condition is true then the test command returns zero to indicate success. We could improve our earlier if statement example by only creating the 'backups' folder if it doesn't already exist, using the test command: if ! test -d ~/backupsthen echo \"Creating backups folder\" mkdir ~/backupsfi The test command evaluates an expression. In this case the expression is: -d ~/backups This expression uses the -d (file exists and is a directory) operator to check if the provided path is a directory. We want to create the directory only if it doesn't exist, so we use the 'not' operator to 'invert' the result of test. The 'not' operator is written with the ! exclamation point symbol. You can surround an expression with square bracket and the shell will evaluate the expression with the test command. This can make your scripts far more compact: if ! [ -d ~/backups ]then echo \"Creating backups folder\" mkdir ~/backupsfi This square bracket syntax is very commonly used - but just remember it is shorthand for the test command. One of the most useful manual pages is the page for the test command as it shows all of the available operators. Open the page with man test.","s":"The Test Command","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-test-command","p":547},{"i":554,"t":"You will often see 'if' and 'then' statements on the same line as below: if ! [ -d ~/backups ]; then mkdir ~/backupsfi The shell assumes that each individual line is a single statement. If you want to put more than one statement on a line then you need to let the shell know when one statement ends and another starts. We can use a semi-colon for this. The shell uses the semi-colon as a 'command separator' symbol. If you don't include a semi-colon at the end of a command then the shell assumes that the entire line is a single statement. If you try and run the script without the semi-colon you will get an error: bash: syntax error near unexpected token `fi' I would suggest you start by writing your if statements with the if and the then on separate lines. Once you are more familiar with the syntax, you can start to combine the lines if you prefer. You can put as many statements on a single line as you like - you could even write the script like so: if ! test -d ~/backups; then mkdir ~/backups; fi The then doesn't require a semi-colon as it is a keyword rather than a command. I think that in general keeping things on separate lines will be a bit more readable for other users, but sometimes you may prefer a more compact form.","s":"Using Multiple Statements in a Single Line","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#using-multiple-statements-in-a-single-line","p":547},{"i":556,"t":"You can use the else statement to define a series of statements that should be executed if the condition in the if statement is not true. Here's how we can write a script that informs the user of whether they have installed the 'common' command or not: if [ -e /usr/local/bin/common ]then echo \"The 'common' command has been installed in the local bin folder.\"else echo \"The 'common' command has not been installed in the local bin folder.\"fi In this case we used the -e (file or folder exists) operator to check whether a file or folder exists in the location /usr/local/bin/common. The 'common' command is the command we created in Chapter 18 - Shell Script Essentials. Now if you run the script and you don't have the 'common' command installed you will see the following output: The 'common' command has not been installed in the local bin folder. Note that we still need to use the 'fi' keyword to close the 'if' statement.","s":"The Else Statement","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-else-statement-index-","p":547},{"i":558,"t":"The elif statement (which is short for 'else if') can be used to create additional checks and define statements that should run if other conditions are true. Let's see this in action by updating our script to check whether the 'common' command is executable, using the -x (is executable) operator: if [ -x /usr/local/bin/common ]; then echo \"The 'common' command has been installed and is executable.\"elif [ -e /usr/local/bin/common ]; then echo \"The 'common' command has been installed and is not executable.\"else echo \"The 'common' command has not been installed.\"fi The message you see will depend on whether you have installed the 'common' command in your local binaries folder and whether the script is executable. If you want to see each of the different messages, you might find the following snippets useful to add or remove the command or change its executable permissions: ln -s $HOME/effective-shell/scripts/common.v1.sh /usr/local/bin/common - Create a link to the 'common' command in the local binaries folder chmod -x $HOME/effective-shell/scripts/common.v1.sh remove the 'executable' flag from the 'common' command, making it not executable chmod +x $HOME/effective-shell/scripts/common.v1.sh add the 'executable' flag from the 'common' command, making it executable rm /usr/local/bin/common remove the link to the 'common' command from the local binaries folder The elif statement looks very similar to the if statement. The statement takes a set of commands. These commands could be normal shell commands, test commands, or test commands written with the square brackets short-hand notation. It is very important to think about the order in which the if and elif statement are executed. If we had written the script like this, it would not work: if [ -e /usr/local/bin/common ]; then echo \"The 'common' command has been installed and is executable.\"elif [ -x /usr/local/bin/common ]; then echo \"The 'common' command has been installed and is not executable.\"else echo \"The 'common' command has not been installed.\"fi In this script we check to see if the file exists first. If the file exists then the condition -e operator will return true, and we will not run the check in the elif statement. This means we'll never successfully evaluate the statements in the elif block (because for the file to be executable it must exist, so the first condition in the if statement will always take precedence. So it is important to think about the order of the statements!","s":"The Elif Statement","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#the-elif-statement-index-","p":547},{"i":560,"t":"There are many operators that can be used in a test expression. You can find the full list by running man test. Here are the most common operators you should know about! Operator Usage -n True if the length of a string is non-zero. -z True if the length of a string is zero. var True if the variable var is set and is not empty. s1 = s2 True if the strings s1 and s2 are identical. s1 != s2 True if the strings s1 and s2 are not identical. n1 -eq n2 True if the numbers n1 and n2 are equal. n1 -ne n2 True if the numbers n1 and n2 are not equal. n1 -lt n2 True if the number n1 is less than n2. n1 -le n2 True if the number n1 is less than or equal to n2. n1 -gt n2 True if the number n1 is greater than n2. n1 -ge n2 True if the number n1 is greater than or equal to n2.","s":"Common Test Operators","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#common-test-operators","p":547},{"i":562,"t":"One of the great things about the test command is the presence of a number of operators that are specifically used to work with the filesystem. These operators are very handy when you are building shell scripts! Here are some of the most useful ones: Operator Usage -d True if the file exists and is a folder. -e True if the file exists, regardless of the file type. -f True if the file exists and is a regular file. -L True if the file exists and is a symbolic link. -r True if the file exists and is readable. -s True if the file exists and has a size greater than zero. -w True if the file exists and is writable. -x True if the file exists and is executable - if it is a directory this checks if it can be searched. file1 -nt file2 True if file1 exists and is newer than file2. file1 -ot file2 True if file1 exists and is older than file2. file1 -ef file2 True if file1 and file2 exist and are the same file. There are plenty of other operators that you can use when working with files, you can see them all by running man test.","s":"Common Test Operators for Files","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#common-test-operators-for-files","p":547},{"i":564,"t":"Often you will want to check multiple conditions. You can use the && 'and' operator and the || 'or' operator to check for multiple conditions: if [ $year -ge 1980 ] && [ $year -lt 1990 ]; then echo \"$year is in the 1980s\"fi This script checks to see whether the variable 'year' is greater than or equal to 1980 and less than 1990. You can use 'and' or 'or' in a single test statement by using the special -a (and) and -o (or) operators. This is how the script would look using the -a operator: if [ $year -ge 1980 -a $year -lt 1990 ]; then echo \"$year is in the 1980s\"fi These operators can lead to some subtle problems so I would not recommend that you use them. A better option is 'Conditional Expressions' which are described in the next section. However, it is important to be able to recognise these operators so that they don't surprise you if you see them in someone else's script.","s":"Combining Tests","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#combining-tests","p":547},{"i":566,"t":"'Conditional Expressions' are a feature of Bash, and bash-like shells, that offer a more sophisticated option to perform conditional checks. Conditional expressions use two square brackets rather than one: if [[ $year -ge 1980 && $year -lt 1990 ]]; then echo \"$year is in the 1980s\"fi Conditional expressions have a number of benefits over plain test commands. Some of the most important ones are: You can use the && and || operators directly in the expression If you use an || expression and the left hand side of the expression is true, the right hand side will not be evaluated - this is not always the case with older versions of Bash when using the -o operator (this is a subtle difference but can help avoid potentially incorrect behaviour) Numbers are correctly compared even if they are in different formats (for example, you can compare hexadecimal and octal numbers, this does not work in the standard test expression) You can use the incredibly useful =~ operator to use a regular expression in your condition (we'll look at this next) You can find more details on conditional expressions by using man bash and searching for \\[\\[ (this is the double square brackets with each one escaped with a backslash). Some people prefer to use single brackets so that their script is more portable, as the double brackets are specific to Bash and Bash-like shells. Others prefer to use the double brackets so that they can use the additional features offered. Whether you use single or double brackets will partly be down to preference and whether it is more important in your use case to have portability or whether it is more important to have the more 'correct' behaviour.","s":"Conditional Expressions","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#conditional-expressions","p":547},{"i":568,"t":"When you use the double square brackets conditional expression syntax you can use the =~ operator to test for a regular expression. This can be extremely useful. If you need a reminder on how regular expressions work check Chapter 13 - Regex Essentials. In the example below we check to see if the user's shell is 'zsh' by seeing whether the path of the shell ends with the text zsh: zsh_regex=\"zsh$\"if [[ $SHELL =~ $zsh_regex ]]; then echo \"It looks like your shell '$SHELL' is Z-Shell\"fi If you are running Z-Shell you will see the output below: It looks like your shell '/bin/zsh' is Z-Shell It is best to declare the regular expression in a variable rather than including it directly in the expression, this makes it easier to handle special characters such as the dollar symbol. You can use capture groups in your regular expression to help you extract text. For example, we could get the name of the current shell binary with the code below: shell_regex=\"([^/]+)$\"if [[ $SHELL =~ $shell_regex ]]; then echo \"Your shell binary is: ${BASH_REMATCH[1]}\"else echo \"Unable to extract your shell binary\"fi On my machine this script shows the following output: Your shell binary is: bash The $BASH_REMATCH variable is an array - the first result value in the array is the entire match, each subsequent value in the array is the result of each capture group in the expression. Double check Chapter 19 - Variables, Reading Input, and Mathematics if you need a reminder on how arrays work in Bash.","s":"Using Regexes in a Conditional Expression","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#using-regexes-in-a-conditional-expression","p":547},{"i":570,"t":"You can 'chain' commands together in the shell, this allows you to run a command based on the result of a previous command. Let's take a look at how this would work: mkdir -p ~/backups && cd ~/backups In this case we have chained two commands together using the && operator. The shell will only run the second command if the first command succeeds. It evaluates the result of the first command - if it is successful, then it evaluates the second command. It does this because we are trying to evaluate the combination of both commands. Or, if we were to write this in pseudo-code: does (command1 and command2) succeed? If command fails, the shell doesn't need to evaluate the second command - the overall result must be false, as the first command has already failed. Contrast this to the || operator: [ -d ~/backups ] || mkdir ~/backups In this case we evaluate the second command only if the first command fails. Let's look at the pseudo code: does (command1 or command2) succeed? If the first command succeeds, the shell doesn't need to evaluate the second command. However, if the first command fails, the shell does have to evaluate the second command, to see if either of them succeed. In summary, here's how command chaining works: # Run command1, if it succeeds run command2.command1 && command2# Run command1, if it does not succeed run command2.command1 || command2 You will see this syntax a lot in shell scrips as it is very succinct. It can also be very useful when using the shell interactively. For example, it is almost second nature for me to write the following commands: make build && make deploy Here I am using the make (build programs) command. If the 'build' step for a project succeeds, I want to run the 'deploy' step. But I don't want to run the 'deploy' step if the 'build' step fails!","s":"Chaining Commands","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#chaining-commands","p":547},{"i":572,"t":"If you find yourself writing overly complex 'if statements', you might use a case statement to simplify your code. A case statement is a bit like an 'if statement'. The structure is as follows: case in pattern1) ;; pattern2 | pattern3) ;; *) ;;esac Typically you will provide the 'case' statement a variable and use it to check against a number of values. Here's a common example you'll see - checking to see whether a response is 'yes' or 'no': read -p \"Yes or no: \" responsecase \"${response}\" in y | Y | yes | ok) echo \"You have confirmed\" ;; n | N | no) echo \"You have denied\" ;; *) echo \"'${response}' is not a valid response\" ;;esac The example above shows very simple text patterns, but any text pattern can be used: read -p \"Yes or no: \" responsecase \"${response}\" in [yY]*) echo \"You have (probably) confirmed\" ;; [nN]*) echo \"You have (probably) denied\" ;; *) echo \"'${response}' is not a valid response\" ;;esac In this example the first pattern is [yY]* which means either the 'y' or 'Y' character followed by zero or more characters, this will match things like 'yes' 'YES' or 'yay'. We have a similar pattern for the negative response. The case statement can look quite complex, I often think that even if it takes more lines to write the logic using 'if statements' it will be more readable, but this is common pattern nonetheless and good to know about!","s":"Case Statements","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#case-statements","p":547},{"i":574,"t":"Now that we know how to use if statements, we can update the 'common' command that we have been improving as part of each chapter. We will update it to check whether the user is using Bash or Z-Shell and search through the history for common commands appropriately. As a reference, let's look at the common.v2.sh command we created in the previous chapter: # Write the title of our command.echo \"common commands:\"# The following variables control how the command runs.history_lines=1000 # The number of lines of history to search throughcommand_count=10 # The number of common commands to show# Show the most commonly used commands.tail ~/.bash_history -n ${history_lines} \\ | sort \\ | uniq -c \\ | sed 's/^ *//' \\ | sort -n \\ | tail -n ${command_count} We'll create a new version of this script called common.v3.sh that checks the user's shell to work out what file to use to find the history of commands: # The following variables control how the command runs.shell_binary=\"\" # We will work out what shell we are in later.history_file=\"\" # We will work out the history file later.history_lines=1000 # The number of lines of history to search throughcommand_count=10 # The number of common commands to show# Check if we can work out the name of the shell binary.shell_regex=\"([^/]+$)\"if [[ $SHELL =~ $shell_regex ]]; then # Depending on the name of the shell binary, set the history file path. shell_binary=${BASH_REMATCH[1]} if [[ $shell_binary == \"bash\" ]]; then history_file=~/.bash_history elif [[ $shell_binary == \"zsh\" ]]; then history_file=~/.zsh_history fifi# If we are searching through the bash history, we can look at the history file# to get the most common commands.if [[ $shell_binary == \"bash\" ]]; then # Show the most commonly used commands. tail \"${history_file}\" -n ${history_lines} \\ | sort \\ | uniq -c \\ | sed 's/^ *//' \\ | sort -n -r \\ | head -n ${command_count}elif [[ $shell_binary == \"zsh\" ]]; then # Z-Shell history lines look like this: # : 1621135004:0;uname -a # So we run the same command as above, but use the 'rev | cut | rev' trick # to extract everything _after_ the semi-colon, which is the command text. tail \"${history_file}\" -n ${history_lines} \\ | rev \\ | cut -d';' -f1 \\ | rev \\ | sort \\ | uniq -c \\ | sed 's/^ *//' \\ | sort -n -r \\ | head -n ${command_count}else # Show a warning to the user that we don't know where the history file is # for their shell. echo \"Sorry, I don't know where to find the history for '${SHELL}'\"fi In this script we now first check if we can extract the name of the shell binary from the shell path. If we can, we store the name of the shell binary and its associated history in a pair of variables. Then when we come to actually search through the history, we check the shell binary. If it is bash, we run the same command as before. If it is zsh we run a similar command, but account for the fact that the Z-Shell history file has some extra content which needs to be removed. Note that as well as showing how to use more variables and if statements, as well as nested if statements (when one if statement is inside another) we can also see that we have very descriptive comments. Each comment is giving clear information on what we are trying to accomplish, which should make the script easier to maintain. If you want to replace the installed common command with this new one, update the symlink in your /usr/local/bin folder: ln -sf $HOME/effective-shell/scripts/common.v3.sh /usr/local/bin/common Note that in this command we use the -f flag to force the creation of the symlink even if one already exists in the given location.","s":"Updating the 'Common' Command","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#updating-the-common-command","p":547},{"i":576,"t":"In this chapter we looked at the If statement - an extremely important statement that allows us to perform conditional logic. In the next chapter we will look at another crucial logical feature of the shell - loops. You can find most of the documentation for conditional logic in the manual, just run man bash and search for GRAMMAR.","s":"Summary","u":"/part-4-shell-scripting/mastering-conditional-logic","h":"#summary","p":547},{"i":578,"t":"On this page","s":"Loops and working with Files and Folders","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"","p":577},{"i":580,"t":"We can use the for loop to run a set of commands for each item in a list. The for loop has the following structure: for in do done The for loop executes a sequence of commands for every item in a list. In the documentation you will see that this list is called 'words'. There's a technical (and complex) reason for this that we'll discuss in the end of the chapter. Let's see how the for loop works by showing a simple example. We will loop through every item in a folder and print its name to the screen: for item in ~/effective-shell/*do echo \"Found: $item\"done As long as you have the effective-shell folder in your home directory, you will see output that looks like this: Found: /home/dwmkerr/effective-shell/dataFound: /home/dwmkerr/effective-shell/docsFound: /home/dwmkerr/effective-shell/logsFound: /home/dwmkerr/effective-shell/picturesFound: /home/dwmkerr/effective-shell/programsFound: /home/dwmkerr/effective-shell/quotesFound: /home/dwmkerr/effective-shell/scriptsFound: /home/dwmkerr/effective-shell/templatesFound: /home/dwmkerr/effective-shell/textFound: /home/dwmkerr/effective-shell/websites Notice how the shell is smart enough to expand the wildcard expression that we have included in the for loop. In just the same way we can use wildcards in commands such as ls or cp or mv, we can also use them in for loops - or in fact any statement1! You will also see that when we specify the name of the variable to use in the loop (which in this example was item) we don't need to use a dollar symbol. Remember - when we are setting a variable, we don't use a dollar symbol, we only use the dollar symbol when we want to get the value of the variable. The for loop is closed with the done keyword. Here we can also see an inconsistency with the shell syntax - for the if statement, the statement is closed with if backwards (fi). But the for loop is closed with done. The shell is an old platform and there are some oddities like that that you might not see in more modern programming languages.","s":"The For Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#the-for-loop-index-","p":577},{"i":582,"t":"In Chapter 19 - Variables, Reading Input, and Mathematics we saw how to create arrays. We can easily loop through the items in an array with a for loop. Here's an example: days=(\"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\" \"Sunday\")for day in ${days[@]}do echo -n \"$day, \"doneecho \"happy days!\" If we run this script we'll see the following output: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, happy days! It's important to remember that we want to go through every item in the array, so we have to use the ${days[@]} syntax. This is the syntax for 'all of the members of the array'. The -n (don't output a trailing newline) flag of the echo command is used inside the for loop so that we don't write each day on its own line.","s":"For Loops - Arrays","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---arrays","p":577},{"i":584,"t":"The for loop documentation names the input to the loop as 'words'. We can see this by running help for: $ help forfor: for NAME [in WORDS ... ] ; do COMMANDS; done Execute commands for each member in a list.... The reason that the items are called 'words' is that the shell splits up the input into a set of words and loops though each - this can be a real surprise if you come from a programming background. Let's see what this means with an example: sentence=\"What can the harvest hope for, if not for the care of the Reaper Man?\"for word in $sentencedo echo \"$word\"done The output of this will be: Whatcantheharvesthopefor,ifnotforthecareofthereaperman? Z-Shell - if you are using Z-Shell then the sentence will not be split up into words. There is an appendix at the end of the chapter that describes the differences between Z-Shell and Bash-like shells (which tend to be closer to the Posix standard). The for loop has split up the sentence variable into a set of words. This might seem illogical, as the shell is making quite a big assumption (that the operator wants their input split up), but we'll see with a few examples how this is often what is needed. This is not how most programming languages would work, so why does the shell do this? The reason is that the shell is a text based environment and the designers have taken this into account. Most of the time when we are running shell commands in a terminal we are running commands that simply output text. If we want to be able to use the output of these commands in constructs like loops, the shell has to decide how to split the output up. For example, let's see how the ls command would write its output: $ ls ~/effective-shelldata docs logs pictures programs quotes scripts templates text websites The output of the ls program is plain text. It is not an array, it is just a set of files separated by spaces. What would we expect the shell to do if we ran the following command? files=$(ls ~/effective-shell)for file in $filesdo echo \"Found: $file\"done The output is: Found: dataFound: docsFound: logsFound: picturesFound: programsFound: quotesFound: scriptsFound: templatesFound: textFound: websites Here we see why the shell splits up words in a sentence. It is making a best effort with plain text - trying to split plain text up into sensible 'chunks'. When we operate in a shell for day to day work we don't have to use the more specific syntax that would be used in a programming language - the shell has more of an emphasis on terseness of statements and the ability to quickly work with files. It is not designed as a general purpose programming tool, so it makes assumptions like this. We go into detail in word splitting nearer the end of this chapter.","s":"For Loops - Words","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---words","p":577},{"i":586,"t":"One of the most common scenarios for using a for loop is to loop through a set of files or folders. The most simple way to do this is to use a simple wildcard pattern in the for loop statement, like so: for script in ~/effective-shell/scripts/*.shdo echo \"Found script: $script\"done We will see output that looks like this: Found script: /home/dwmkerr/effective-shell/scripts/common.mac.shFound script: /home/dwmkerr/effective-shell/scripts/common.shFound script: /home/dwmkerr/effective-shell/scripts/common.v1.shFound script: /home/dwmkerr/effective-shell/scripts/common.v2.shFound script: /home/dwmkerr/effective-shell/scripts/common.v3.shFound script: /home/dwmkerr/effective-shell/scripts/show-info.sh We have to be careful with scripts like this - there is a bug. By default, if the shell doesn't find anything with a wildcard pattern it does not expand it. This is very confusing - so let's see an example. Take a look at the sample below - what would you expect it to show? for script in ~/bad-shell/scripts/*.shdo echo \"Found: $script\"done You might think the logical result is that nothing is printed - there is not a bad shell folder, so the pattern should not find any files. But instead, we see the following output: Found: ~/bad-shell/scripts/*.sh By default, if a shell 'glob' (a pattern that includes a wildcard) does not match any files, the shell simply leaves the pattern as-is. There are two ways we can deal with this problem. The first way is to enable the 'nullglob' (return null for unmatched globs) option: shopt -s nullglobfor script in ~/bad-shell/scripts/*.shdo echo \"Found: $script\"done The shopt (set and unset shell option) command is used to configure shell options. We will be looking at shell options in detail in Part 5. The 'nullglob' option changes the shell behaviour so that if a wildcard pattern does not match any results, it is set to null string2. The second way we can deal with this problem is to just use a test command. I think that this is actually far more readable than the shopt solution. Here's how it would look: for script in ~/bad-shell/scripts/*.shdo # If the file / folder doesn't exist, skip it. if ! [ -e \"$script\" ]; then continue; fi echo \"Found: $script\"done Here we use the -e (exists) operator in a test command to check whether the file exists. If it does not exist, we run the continue statement. The continue statement 'skips' the current item in the loop and moves to the next one. We will see it a little more later on.","s":"For Loops - Files with Wildcards","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---files-with-wildcards","p":577},{"i":588,"t":"If the files that you are trying to loop through are too complex to match with a shell pattern, you can use the find command to search for files, then loop through the results. If you are not familiar with the find command, check Chapter 11 - Finding Files. Let's use the find command to run a loop that prints every symlink in the user's home directory. But before we run the loop we'll create a symlink with a space - this will cause some interesting output in our script: # Create a symlink to 'effective-shell' that has a space in it...ln -s ~/effective-shell ~/effective\\ shell# Find all symlinks and print each one.links=$(find ~ -type l)for link in $linksdo echo \"Found Link: $link\"done You will see a few different links shown when you run this script, depending on how your system is set up. But you will also certainly see the results below: ...Found Link: /home/dwmkerr/effective-shell/effectiveFound Link: shell... This is clearly a problem - the shell has taken the path that has a space - /home/dwmkerr/effective-shell/effective shell and performed word splitting and turned it into two separate items. This is a persistent headache for anyone who needs to build shell scripts. There are a large number of ways to solve this problem, and none of them are particularly intuitive. I am going to demonstrate one common solution, which is not perfect but should cover most cases. I'll then suggest a better work-around. The solution that we will use is to temporarily change the values that the shell uses to split text into words. We will set it to only split on newlines. The find command puts each file it finds on its own line. This means we will not split up files with spaces or other whitespace in the name: # Save the current value of IFS - so we can restore it later. Split on newlines.old_ifs=$IFSIFS=$'\\n'# Find all symlinks and print each one.links=$(find ~ -type l)for link in $linksdo echo \"Found Link: $link\"done# Restore the original value of IFS.IFS=$old_ifs If you run this command now you will see the correct output: ...Found Link: /home/dwmkerr/effective-shell/effective shell... This will cover you in most cases. However, this method is not ideal for a number of reasons: It is quite verbose - we have to store the current value of $IFS and then reset it later It is not quite foolproof - filenames on some systems can have a newline character and this script would fail for those files We have to use the complex looking 'ANSI C Quoting' syntax to set $IFS to a newline3 If the reader doesn't know what $IFS is then the entire script will be difficult to follow The $IFS variable can be complex to work with and discussed at the end of the chapter. I believe that in this case it is probably best to not use a shell script. There is no solution that is particularly clean or simple. In this case I think you might be better off using a programming language. Check Chapter 30 - How to Avoid Scripting for more details on this.","s":"For Loops - Files with Find","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---files-with-find","p":577},{"i":590,"t":"If you have used programming languages like C, C++, Python, Java and others, you may well be familiar with the 'C style loop' structure that is shown below: for (( expression1 ; expression2 ; expression3 ))do done This loop structure uses three arithmetic expressions to run the loop. The first is in 'initialise' expression, this is typically used to setup the initial state of the loop. The second is the 'conditional' expression, this is used to check whether the loop is complete. The third is the 'iterate' expression, this is evaluated after the loop commands are completed. Here's how we can use a C style for loop to iterate through five numbers: for (( i = 1; i <= 5; i++ ))do echo \"Loop ${i}\"done The output of this script is: Loop 1Loop 2Loop 3Loop 4Loop 5","s":"For Loops - C Style Loops","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---c-style-loops","p":577},{"i":592,"t":"Another common way to use a for loop is with brace expansion. Brace expansion we have already seen a number of times so far - we can use it to generate a sequence of values. Here is how we might create three files using brace expansion: touch {coffee,tea,milkshake}-menu.txt This will create three files: $ ls -1 *-menu.txtcoffee-menu.txtmilkshake-menu.txttea-menu.txt Brace expansion can be used in for loops, and brace expansion can be used to create sequences. For example, the loop below could be used as a way to loop through the numbers from one to ten: for i in {1..10}do echo \"Loop ${i}\"done Brace expansion can be used to loop through a sequence of values or a range of numbers. You can even specify the 'increment' used in a sequence. For example, this loop iterates through a sequence of numbers adding five each time: for i in {0..25..5}do echo \"Loop ${i}\"done The output of this loop would be: Loop 0Loop 5Loop 10Loop 15Loop 20Loop 25","s":"For Loops - Looping over Sequences","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#for-loops---looping-over-sequences","p":577},{"i":594,"t":"The while loop is a loop that executes commands until a certain condition is met. The while loop has the following structure: while do done As long as the test commands return success, the loop will run the conditional commands. After the conditional commands have been run, the loop goes 'back to the start' and evaluates the test commands again. Here's an example of how a while loop can be used to generate a list of random numbers: # Create an empty array of random numbers.random_numbers=()# As long as the length of the array is less than five, continue to loop.while [ ${#random_numbers[@]} -lt 5 ]do # Get a random number, ask the user if they want to add it to the array. random_number=$RANDOM read -p \"Add $random_number to the list? (y/n): \" choice # If the user chose 'y' add the random number to the array. if [ \"$choice\" = \"y\" ]; then random_numbers+=($random_number); fidone# Show the contents of the array.echo \"Random Numbers: ${random_numbers[@]}\" When you run this script, you can choose to add a number to the list by typing 'y' - once there are five items in the list the while loop condition fails and the loop ends: Add 14718 to the list? (y/n): yAdd 2646 to the list? (y/n): nAdd 11898 to the list? (y/n): yAdd 31506 to the list? (y/n): yAdd 32436 to the list? (y/n): yAdd 6803 to the list? (y/n): nAdd 25811 to the list? (y/n): yRandom Numbers: 14718 11898 31506 32436 25811 The $RANDOM variable is a built-in variable in the shell that returns a random number. You would typically use a while loop when you don't know how many iterations you will perform and you need to re-evaluate at each iteration whether you should continue to loop.","s":"The While Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#the-while-loop","p":577},{"i":596,"t":"You can use a while loop to iterate through each line in a file, without having to load the entire file into memory. Here's an example of how to iterate through the lines of a file: while read line; do echo \"Read: $line\"done < ~/effective-shell/data/top100.csv The output will look like this: Read: \"Rank\",\"Rating\",\"Title\",\"Reviews\"Read: \"1\",\"97\",\"Black Panther (2018)\",\"515\"Read: \"2\",\"94\",\"Avengers: Endgame (2019)\",\"531\"... This uses shell redirection to redirect the contents of the ~/effective-shell/data/top100.csv file into the read command in the while loop. The read command will read the file, line by line, until it finds the final line. This script has some issues: If the last line is does not end with a newline, then it is not read Backlashes will be treated as escape sequences and lead to broken output Leading whitespace will be removed It is possible to avoid these issues, but the resulting script is a lot harder to read: while IFS=\"\" read -r line || [ -n \"$line\" ]; do echo \"Read: $line\"done < ~/effective-shell/data/top100.csv In this case we've had to use some complex tricks to avoid each issue: The || [ -n \"$line\"] test ensures that the loop iterates as long as the line read is not zero-length, ensuring we read the last line even if it doesn't have a newline The -r (do not escape) option for read ensures that backlashes are not interpreted as escape sequences The IFS=\"\" temporarily disables any word splitting in the loop, meaning that we do not lose leading whitespace However this still has issues - if commands in the loop read from standard input then the loop will still have errors. For this reason, I would again suggest you follow the advice in the How to avoid scripting! Chapter to see better ways to read files! Even though I would recommend using a programming language to read the lines of a file, I have kept this example here because it is something you are likely to come across if you see scripts written by others. And for simple scenarios, where you are fairly sure of structure of a file, it might be useful. But this is definitely a case where you should consider using a programming language if you want to create more maintainable solutions to problems!","s":"While Loops - Looping through the lines in a file","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#while-loops---looping-through-the-lines-in-a-file","p":577},{"i":598,"t":"There are times that you may want to loop forever. For example you might be writing a script that reads an option from the user, processes it, and then starts again. Here's an example of an infinite loop - we use the true command, which always returns success: while truedo echo \"1) Move forwards\" echo \"2) Move backwards\" echo \"3) Turn Left\" echo \"4) Turn Right\" echo \"5) Explore\" echo \"0) Quit\" read -p \"What will you do: \" choice if [ $choice -eq 0 ]; then exit fi # The rest of the game logic would go here! # ...done This example shows a common pattern for an infinite loop - offering a menu of options which the user can call repeatedly until they decide to quit.","s":"While Loops - The Infinite Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#while-loops---the-infinite-loop","p":577},{"i":600,"t":"The until loop operates just like the while loop, except that it runs until the test commands return success. The structure of the until loop is just like the while loop: until do done As long as the test commands do not return success, the loop will run the conditional commands. After the conditional commands have been run, the loop goes 'back to the start' and evaluates the test commands again. Here's an example of an until loop that builds a random number that is at least 15 characters long: # Create an empty random number string - we're going to build it up in the loop.random_number=\"\"# Keep on looping until the random number is at least 15 characters long.until [ \"${#random_number}\" -ge 15 ]do random_number+=$RANDOMdoneecho \"Random Number: ${random_number}\" When you run this script you will see something like this: Random Number: 364272371462227929 Note that we've used the string-length parameter expansion function to get the length of the random_number_ variable here. If this is not familiar, check Chapter 19 - Variables, Reading Input, and Mathematics. In general I would recommend using while loops rather than until loops. While loops are going to be more familiar to readers as they exist in many programming languages - until loops are a little more rare. And you can easily turn any until loop into a while loop by simply inverting the test commands you are running. For example, we could re-write the loop created before like so: random_number=\"\"while [ \"${#random_number}\" -lt 15 ]do random_number+=$RANDOMdoneecho \"Random Number: ${random_number}\" In this case we've changed the condition from -ge 15 (greater than or equal to fifteen) to -lt 15 (less than fifteen). The while loop version of the script will probably be a little easier for most readers to parse.","s":"The Until Loop","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#the-until-loop","p":577},{"i":602,"t":"We briefly saw that the continue (resume loop) statement can be used to 'skip' an iteration in a loop. break (exit loop) statement that can be used to stop running the loop. When we use the continue statement, we are telling the shell that we want to stop processing the current 'iteration' of the loop and move onto the next item. You can use as many continue statements as you like in a loop. Here's an example of a script that let's the users show the contents of a directory. If the directory is empty it uses the continue statement to skip to the next directory. If the users chooses to cancel the operation, it uses the break statement to stop iterating: echo \"For each folder, choose y/n to show contents, or c to cancel.\"for file in ~/*do # If the file is not a directory, or it cannot be searched, skip it. if ! [ -d \"$file\" ] || ! [ -x \"$file\" ]; then continue; fi # Ask the user if they want to see the contents. read -p \"Show: $file? [y/n/c]: \" choice # If the user chose 'c' for cancel, break. if [ \"$choice\" = \"c\" ]; then break; fi # If the user choice 'y' to show contents, list them. if [ \"$choice\" = \"y\" ]; then ls \"$file\"; fidone Using break and continue can simplify our loops - otherwise it would be much harder to write the loop above.","s":"Continue and Break","u":"/part-4-shell-scripting/loops-and-working-with-files-and-folders","h":"#continue-and-break","p":577},{"i":604,"t":"In each example in this chapter we have split the loop constructs so that there is one statement per line. But just as with the if statement, we can combine any of these lines, as long as we use a semi-colon to let the shell know where each statement ends. A common pattern you will see is the do keyword on the same line as the for or while statement: numbers=(0 1 1 2 3 5)for num in ${numbers[@]}; do echo \"$num\"done If you are simply typing in the shell in a terminal, rather than writing a script, you might write the loop on a single line: for script in *.sh; do touch \"$script\"; done This one-liner updates the last access and modified of all files that end with *.sh in the current folder. Just like with the if statement I would recommend that you keep each statement on its own line until you are 100% familiar with the syntax. Then when it is second-nature to be able to write a loop, you can use the more compact syntax if it is appropriate. When you are running the shell interactively, i.e. actually typing in the shell rather than writing a shell script, you can still use multiple lines. If you type for script in *.sh and press enter, the shell will let you type the next line. You can keep on adding lines until you type done and press enter. If you want to make a really compact for loop, you can actually skip the in part. If in is omitted then the special 'all parameters' variable $@ is used. We will look at this special parameter in the next chapter. But this will be confusing to readers so I would recommend that you are always explicit with the in Fourfunction revcut { rev | cut \"$@\" | rev} You can find out more about functions in Chapter 22 - Functions, Parameters and Error Handling. Shell Options​ The ~/.bashrc file is the ideal place to configure shell options to suit your preferences: # If we enter a directory name on its own, assume we want to 'cd' into it.shopt -s autocd In this example we use the shopt (set shell option) command to set the autocd option. This option allows you to enter the name of a directory as if it was a command, when you press 'enter' the shell will cd into the directory. You can set an option using the -s (set option) flag and unset an option with the -u (unset option) flag. You can list the options available to set by running shopt -p, or searching the man bash page for shopt. Some of the most useful options are: Option Description autocd Enter a directory name as a command and the shell will cd to it. cdable_vars Allows you to cd into a variable, such as repos=~/repos; cd repos cdspell The shell will try to fix typos to the cd command. checkjobs Show the status of stopped and running jobs before exiting the shell. cmdhist Save multi-line commands in the shell history as single entries, rather than an entry per line. dirspell Try to correct typos when auto-completing directory names. globstar Support recursive globbing such as **/*.py to find files in subdirectories. histappend Append to the history file when the shell exists, rather than overwriting it. As well as the options that can be set using the shopt command, there are also many variables that are used to configure the shell. We've seen some of these variables already, such as the EDITOR variable that defines what text editor to use and the PAGER variable that defines what pager program to use. Changing the Command Prompt​ The command prompt is the information that is shown to the left of the caret in the shell where you enter commands. It will often look something like this: dwmkerr@ip-172-31-28-144:~/effective-shell$ This command prompt in this example is made up of the following parts: ubuntu - the name of the current user ip-172-31-28-144 - the hostname of the machine ~/effective-shell - the current directory $ - an indicator showing that we are using Bash (this will be # if we are a super user) The structure and format of the command prompt can be configured using the PS1 variable. This is a large enough topic that the whole of the next chapter is dedicated to customising the command prompt. Source Files​ Another common pattern for the ~/.bashrc file is to simply source another file. For example, you might want to create a set of common functions that you keep in a file called shell-functions.sh. You could source this file as part of your shell configuration: # Load my common shell functions.source ~/shell-functions.sh In fact, a lot of the shell startup files do exactly this. For example, in the default ~/.bashrc file on Ubuntu 20, you will see these lines: if [ -f ~/.bash_aliases ]; then . ~/.bash_aliasesfi This line uses the -f test to see whether a file named ~/.bash_aliases exists. If it does, it is loaded (using dot sourcing as the notation). There are lots of different ways to manage your shell configuration. This can range from the simple, such as adding an alias to the ~/.bashrc file, to the complex, such as sourcing the contents of an entire directory, or configuring a shell dynamically based on what tools are installed on a system. Configure Your System​ You might have particular commands you want to ensure are run when you start a shell. For example, let's say that you want to always have a folder named ~/today that links to a temporary folder which is updated daily. To do this, you could add the following commands to the ~/.bashrc file: # Get today's date in the format YYYY-MM-DD.today=$(date +\"%Y-%m-%d\")# Create the path to today's temp folder and then make sure the folder exists.temp_path=\"/tmp/${today}\"mkdir -p \"${temp_path}\"# Now that we've created the folder, make a symlink to it in our homedir.ln -sf \"${temp_path}\" \"${HOME}/today\" If I add this code to my ~/.bashrc file then whenever I start a new shell, a folder will be created with today's date in the /tmp/ directory, and a link will be created to this folder at ~/today. This provides a convenient way to have a temporary working folder for the day. You can then go back and refer to old temporary folders if you need to. Configuration Tips​ There are a few things that you should pay attention to when working with startup files. Do not print output It is considered bad practice to print output during startup of the shell. Avoid running commands like echo or printf. If you call commands that write to stdout then silence the output by piping it to /dev/null. Do not run long operations You might have written a cool scripts that pulls down information on stocks or weather from a website, ready to show in your shell. But avoid running anything in a startup file that can take a lot of time. Every time you start your shell you'll have a delay while the command runs and this can really slow you down! Be careful not to break things Don't run so many commands that you might cause errors or failures on startup. This can make your shell difficult to use or slow to start up. If your startup logic is failing it can be hard to debug, so try not to make it too complex! Clean up after yourself! Remember, any variables you set will be set for all shells that read the startup file. If there are variables that you only need during the processing of the file, consider using the unset command to unset the variable at the end of startup. Expect commands to be run multiple times Write your startup files with the assumption that they will be run multiple times. If you start a new shell from your current shell, your configuration file will be loaded again. Your configuration should not cause errors if it is run multiple times!","s":"Common Shell Configurations","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#common-shell-configurations","p":619},{"i":628,"t":"In most cases you will only need to work with the ~/.bashrc file to configure your shell. However, the shell actually uses a number of different configuration files (which are called 'startup files') depending on how the shell is being used. You may have seen references to files such as /etc/profile, ~/.bash_profile, ~/.bash_logout and more. The different files that are used can be quite confusing. For the rest of this chapter we're going to go into the details of exactly how the shell uses these different files.","s":"Shell Startup","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#shell-startup","p":619},{"i":630,"t":"For us to be able to understand how shells are configured, we need to understand the different types of shells that can run. This does not mean different shell programs, such as bash, zsh or dash, but instead the differences between interactive and non-interactive shells, as well as login shells. A lot of people get confused by how the shell is configured because they don't fully understand what these different types of shells are. So let's introduce each one, what it is and how it is used.","s":"Different Types of Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#different-types-of-shells","p":619},{"i":632,"t":"An interactive shell is any shell that has its input, output and error standard streams connected to a terminal. This sounds complicated, but it really just means that an interactive shell is one that you interact with via the keyboard and display! When we type commands into our shell, we're using an interactive shell.","s":"Interactive Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#interactive-shells","p":619},{"i":634,"t":"Any shell that does not have its standard input, output and error streams attached to a terminal is generally called a non-interactive shell. The most common example we've seen so far for non interactive shells are the shells that run shell scripts! Let's run the showpstree.sh script from the samples to show the process tree for the current process. This script shows the process tree for the shell process it is running in and looks like this: # GNU pstree; use the long form (-l) show the command line (-a) and the# details for a specific process (-s).pstree -a -s $$ Here's the output when we run this script: ~$ ./effective-shell/scripts/showpstree.shsystemd └─sshd └─sshd └─sshd └─bash └─sh ./effective-shell/scripts/showpstree.sh └─pstree -a -s 1675 The output will look different depending on what system you are using, but the key section to focus on are the final three processes: pstree -a -s 1675 - this is just the pstree (show process tree) command that is run in the showpstree.sh script sh ./effective-shell/scripts/showpstree.sh - this is a non-interactive shell that is running our shell script bash - this is the interactive shell that we used to invoke our shell script When you run a shell script, it runs in a non-interactive shell. This is really important to remember! Shell scripts are run in non-interactive shells. This means that anything you define in ~/.bashrc will not be loaded, so don't try and use aliases or other customisations that you have made. In fact, on many distributions you will see the following lines in the default ~/.bashrc: # If not running interactively, don't do anythingcase $- in *i*) ;; *) return;;esac The first section of the script checks the current shell parameters (which are stored in the special $- variable) to see whether the i (interactive) parameter is present. If it is not present, the return command runs. This check for the shell parameters ensures that even if a non-interactive shell does load the run commands file for some reason, it stops reading it right away. If you need a refresher on how the case statement works, check Chapter 20 - Mastering Conditional Logic. Another way to show a non-interactive shell in action is to simply invoke the shell program with a specified command from the command like: $ sh -c \"echo $((5 + 5))\"10 In this example we started the sh (shell) program and provided a command via the -c (command) flag. This starts a non-interactive shell. Why do non-interactive shells not load the configuration file? There are two reasons. The first is that it doesn't make sense for scripts to rely on user-level customisations. If one user has an alias and refers to it in a script, then the script will not run for another user unless they have the same alias. The second reason is for performance - when using a shell to run a script the shell can start much more quickly if it doesn't need to load configuration or customisations.","s":"Non-Interactive Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#non-interactive-shells","p":619},{"i":636,"t":"When you login to a computer with a shell, entering credentials such as a username and password, then you are using a login shell. A login shell will normally run some initial setup of your environment and provide the bare minimum configuration required to work with the system. For example, most shells set up the $PATH variable as part of the initialisation of the login shell. For systems that don't have a graphical interface, any shell you create will be a child of the login shell, so will inherit the login shell's configuration. For graphical interfaces to systems, such as KDE or Gnome, when you log in with the graphical interface, the desktop manager normally configures the environment using the same configuration as is used for a login shell. The desktop manager process will therefore have variables like the $PATH set up just as if you had logged in at the command line. When you ssh onto a remote machine, you will be running a login shell. In most cases, when you switch users with commands like su (set user), you will start a login shell as well1. The key thing to remember about login shells is that they are normally run once, when you start working with a computer, and all of the other shells you then run will be children of the login shell. You can see whether your shell is a login shell by examining the $0 variable. This variable holds the parameters that were provided to start the shell. By convention, if the parameter starts with a - dash symbol, you can assume that you are in a login shell. Let's see an example of this in action by logging into a virtual machine (if you would like to set up your own Linux virtual machine you can follow the guide in Appendix - Setting Up a Linux Virtual Machine: $ ssh effective-shell-ubuntu-20Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-1045-aws x86_64)...To run a command as administrator (user \"root\"), use \"sudo \".See \"man sudo_root\" for details.~$ echo \"$0\"-bash Here we can see that the parameter that the shell was started with was -bash. This starts with a - dash symbol, indicating that it is a login shell. Login shells are normally interactive shells, but it is possible to run a non-interactive login shell (it's just quite an unusual thing to do). In the early days of Unix, executing any commands could be time consuming. The login shell would perform the most essential configuration only once when a user logs in and all subsequent shell processes could start more quickly as they would inherit the login configuration and then load the user specific configuration.","s":"Login Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#login-shells","p":619},{"i":638,"t":"When the shell starts, it reads a set of startup files. These files are shell scripts that are sourced by the shell. A script that is sourced is loaded into the current shell process, rather than running in a new shell process. For many people, the various different files that are loaded can cause confusion. But as long as you understand the different types of shells that exist, it is actually quite straightforward to understand the process. When a login shell starts, the following steps are taken: The shell attempts to load the profile file The profile file will normally load the run commands file When an interactive shell starts, the following steps are taken: The shell attempts to load the run commands file When a non-interactive shell starts, it doesn't load any configuration files, unless one has been specified in the BASH_ENV variable. Let's take a look at these files in detail.","s":"Shell Startup Files","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#shell-startup-files","p":619},{"i":640,"t":"When a login shell is started, the shell loads and executes commands from the /etc/profile file. The profile file contains the most essential configuration that is required. It is often used to set things like the $PATH environment variable, which will sometimes have different values depending on the operating system you are using. The shell will then attempt to read each of the following files. If the shell finds one that is readable, it reads it and executes its commands, and then does not attempt to read the others: ~/.bash_profile ~/.bash_login ~/.profile There are very few circumstances in which you should change any of these files. It is based to think of the profile files as essential operating system specific configuration that is needed to have a functional login shell. When a login shell closes, it will run any commands in the ~/.bash_logout file. However, users might terminate the shell process forcibly, which means that you cannot be sure this file will always be sourced as the shell exits. The configuration that the startup files perform varies from distribution to distribution, but in general they will do at least the following: Set the $PATH variable to include the appropriate folders for tools for your distribution Set the shell prompt, the characters that are shown to the user to show they need to enter input (such as ~$ when we are in the home folder, or ~# if we are in the home folder as a super user) Set up auto-completion (the feature that allows you to press 'tab' to see suggestions when entering commands) Load the run commands file - we'll look at this file next The key thing to remember about the profile files is that you normally don't need to change them and they normally load the run command file for you.","s":"The Shell Profile File","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#the-shell-profile-fileindex","p":619},{"i":642,"t":"When an interactive non-login shell is started, the shell loads and executes the commands from the /etc/bash.bashrc file and then the ~/.bashrc file (if they exist). It is also convention that you can have two rc files - one that is for all users (in this case, /etc/bash.bashrc) and one that is user specific (in this case, ~/.bashrc). The ~/.bashrc file is where you can put your own commands to configure the shell to suit how you want to use it. This is the file we spent the first half of the chapter looking at in detail.","s":"The Shell Run Commands File","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#the-shell-run-commands-fileindex","p":619},{"i":644,"t":"If you need to load a startup file for a non-interactive shell, you can set the BASH_ENV variable to the path of the file that you want to load. In general you should be very careful when doing this, as shell commands or shell scripts should be written so that they can operate without a startup file being loaded.","s":"Startup Files for Non-Interactive Shells","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#startup-files-for-non-interactive-shells","p":619},{"i":646,"t":"Before the advent of the graphical user interface, almost all shell processes would be children of a login shell, as you had to use a login shell to access the system. For modern systems that use a desktop environment such as Gnome or KDE, the desktop manager process normally loads the /etc/profile file. This means that when you open a terminal program use as the Gnome Terminal or Konsole, the shell is a child of a process which has loaded the profile. Even if you don't use a login shell to access a system, you can normally be sure that the profile will have been loaded by the desktop manager. Different distributions and operating systems may handle this in slightly different ways. For example, on MacOS when you run the Terminal program it actually starts a login shell2. Again, this means that you can be sure that the profile has been loaded (which in turn will load the RC files).","s":"Login Shells and Desktop Managers","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#login-shells-and-desktop-managers","p":619},{"i":648,"t":"You can see the shell that is currently set as the default shell for a user by checking the /etc/passwd file. Here's how I could see what shell is used when the dwmkerr user logs in: $ grep 'dwmkerr' /etc/passwddwmkerr:x:1001:1001:Dave Kerr,,,:/home/dwmkerr:/bin/bash The /etc/passwd file keeps track of the local user accounts on the system. The final item on a line is the shell that is used for the user. When a user logs in, their shell is set in the SHELL environment variable, we can write this value out with the echo command: $ echo \"My shell is: $SHELL\"My shell is: /bin/bash There are a few ways that you can change your shell. However, before you change your shell, you need to make sure that the shell you want to use is listed in the 'available shells' file. This file is kept at /etc/shells: $ cat /etc/shells# /etc/shells: valid login shells/bin/sh/bin/bash/usr/bin/bash/bin/rbash/usr/bin/rbash/bin/dash/usr/bin/dash/usr/bin/tmux If the shell you want to use is not listed in /etc/shells you will need to add it to the list. Once you have installed the shell you want to use and added it to the /etc/shells list you can run the chsh (change shell) command to change the shell for a given user: $ chsh -s /bin/sh dwmkerr The -s (shell) parameter is used to specify the shell path. After this we provide the name of the user we are changing the shell for. On many systems users are allowed to change their own shell as long as it is in the /etc/shells list. To change the shell for another user, or to use a shell that is not in the /etc/shells list the chsh command will need to be run as a super-user. You can also change the shell for a user by editing the /etc/passwd file. Changing your shell is an advanced topic - if you prefer to use another shell you could also simply start the shell from your login shell, for example by running sh from your Bash shell session. As an end-to-end example, here's how you would install zsh and set it for the current user on a Debian based system: # Elevate privileges to super-user.sudo su# Update the apt databases and install 'zsh'.apt update -yapt install zsh# Add 'zsh' to the list of shell.echo \"/bin/zsh\" >> /etc/shells# Return to normal user mode.exit# Change the current user's shell to 'zsh'.chsh -s \"/bin/zsh\" $USER Be careful when changing your shell - if you get this wrong then you may inadvertently lock yourself out of your account, if logging in tries to start a shell that is not properly configured. Always test that the new shell works before you set it!","s":"Changing Your Shell","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#changing-your-shell","p":619},{"i":650,"t":"In this chapter we saw how to customise shell configuration with the ~/.bashrc file. We also looked in detail at the differences between login and non-login shells, interactive and non-interactive shells, and how these different shells load startup files. You can find all of the detail on how the shell starts up in the man bash page, just search for ^INVOCATION. In the next chapter we will look at how you can set up your command prompt to suit your preferences. There are three excellent discussions on login and non-login shells and interactive shells here, here and here.↩ There is a very good discussion on this topic at https://unix.stackexchange.com/questions/119627/why-are-interactive-shells-on-osx-login-shells-by-default.↩","s":"Summary","u":"/part-5-building-your-toolkit/configuring-the-shell","h":"#summary","p":619},{"i":652,"t":"On this page","s":"Managing Remote Git Repositories and Sharing Your Dotfiles","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"","p":651},{"i":654,"t":"So far all of the changes we have made are stored in a local Git repository. The repository's files are stored on your local machine in a folder named .git in the location where you initialised the repository. A remote is a repository that is on another machine. You can push and pull your changes to and from a remote repository as a way to back-up your repository. You can share this remote with other people so that they can collaborate on it, or download it to other machines you work on. There are a number of services you can use that allow you to host public repositories (which can be seen by anyone) and private repositories (which have more restricted access). To see how remotes work I will show how to create a repository using the popular GitHub service. GitHub is free for individuals and is an extremely popular Git provider and online collaboration platform. To create a remote, you will first need to sign-up for a GitHub account. When you have done that you will be offered the option to create a repository: Choose the 'Create a repository' option. You will then be asked to provide a name for your repository. I'm going to use this repository to host my dotfiles, so I have chosen \"dotfiles\": If you don't want members of the public to be able to see your repository, choose the 'private' option. If you already have a local repository then don't check any of the boxes under \"Initialise this repository with\" - we want to create an empty repository which we will then 'push' our changes to. Once you have chosen the \"Create Repository\" option you will be shown some commands that you can use to configure your local repository to point to this newly created remote repository: We already have a repository, so we will follow the instructions in the section \"...or push an existing repository from the command line\". Copy the commands (there is a button that copies the command text to the clipboard) and run them in your shell, from the ~/dotfiles folder: $ git remote add origin https://github.com/dwmkerr-effective-shell/dotfiles.git$ git branch -M main$ git push -u origin mainUsername for 'https://github.com': dwmkerr+effective-shell@gmail.comPassword for 'https://dwmkerr+effective-shell@gmail.com@github.com':Enumerating objects: 39, done.Counting objects: 100% (39/39), done.Delta compression using up to 16 threadsCompressing objects: 100% (36/36), done.Writing objects: 100% (39/39), 12.83 KiB | 1.83 MiB/s, done.Total 39 (delta 7), reused 0 (delta 0), pack-reused 0remote: Resolving deltas: 100% (7/7), done.To https://github.com/dwmkerr-effective-shell/dotfiles.git * [new branch] main -> mainBranch 'main' set up to track remote branch 'main' from 'origin'. When you run the third command (the one that starts with git push) you will be asked for your username and password. Once you enter them your local changes will be 'pushed' to the remote repository. {{< hint warning >}} Avoid Using Passwords To keep this example simple I have authenticated with a username and password. However, I would strongly recommend that as soon as possible you set up an SSH key to authenticate with GitHub. SSH keys are far more secure than usernames and passwords. GitHub have an excellent guide on how to setup SSH keys at: https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh I also describe how to setup SSH keys in the chapter The Secure Shell. ::: Let's look at each command in detail. git remote add origin https://github.com/dwmkerr-effective-shell/dotfiles.git first we tell Git that we would like to add a new 'remote' called origin and we provide its address. The remote in this case is the repository we created in GitHub. git branch -M main now we rename our current branch to main - because our current branch is already called main this shows no output as nothing has changed. git push -u origin main finally, we 'push' our main branch to the remote named origin. The -u flag is used to track changes - we'll see what this means shortly. At this stage if you refresh your browser you'll see your dotfiles repository, with all of our changes we've made so far! Each of the files and folders we have created is shown, we can view any of the files, look at the commit history, see the log messages and more. Before you make too many changes, we'll cover three important commands you need to be aware of when working with remotes - git push, git fetch and git pull.","s":"Git Remotes","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#git-remotes","p":651},{"i":656,"t":"We've already used the git push command once. This command pushes the changes we have made locally to a remote. It is a common convention to call the primary remote that you work with origin - but you can use any name you want. You can also have multiple remotes. For example, you could sign up with GitLab, another Git services provider, create a repository with their service and add that as a remote and call it gitlab. You can show your remotes with the git remote (managed remote repositories) command. If we run the command with the -v (verbose) parameter each remote will be shown, along with the address used when we push changes, as well as the address used when we 'fetch' changes (which we'll look at next): $ git remote -vorigin https://github.com/dwmkerr-effective-shell/dotfiles.git (fetch)origin https://github.com/dwmkerr-effective-shell/dotfiles.git (push) The command we used to push our changes was: $ git push -u origin main The -u (set upstream) option tells Git that we want to associate our local main branch with the remote main branch. This means that we don't need to specify the remote name for each subsequent git push command - Git knows that our 'upstream' branch that we push to is called main and is in the origin remote.","s":"The Git Push Command and Remotes","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#the-git-push-command-and-remotes","p":651},{"i":658,"t":"The git fetch (get remote changes) command downloads all of the information about the changes that have been made to the remote. This command does not download the actual changes to your working tree! All it does is download the information about the changes. To see what I mean, let's run the fetch command: $ git fetch There will be no output - the remote has not changed. Let's make a change to the remote so we can see how fetch works. Open the repository in the GitHub website. There's a message saying that we should add a 'README' file: Press the \"Add a README\" button and add a description of your project. By convention, the file named README.md in a repository is shown on the home page of the project online and typically should include instructions on how to use the repository. This is a plain text file, you can use a plain text styling language called \"Markdown\" to show headings, bullets, code samples and so on (search for \"GitHub Flavored Markdown\" to find out about the syntax): Once you are happy with the content (you can choose 'Preview' to see how it will look) scroll down to the \"Commit\" button. Provide a commit message: Once you have pressed \"Commit New File\" you will be taken back to the repository page. The contents of the README.md file will be shown: We have now created a commit on the origin remote. Now when we run git fetch we will see that the remote has changed: $ git fetchremote: Enumerating objects: 4, done.remote: Counting objects: 100% (4/4), done.remote: Compressing objects: 100% (3/3), done.remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0Unpacking objects: 100% (3/3), 962 bytes | 240.00 KiB/s, done.From https://github.com/dwmkerr-effective-shell/dotfiles 2532277..4a28994 main -> origin/main When we run git fetch, Git looks at the upstream associated with the current branch and checks to see if there are any changes. The information about any changes is downloaded - but the changes themselves are not yet downloaded. If you run git log you will not see the new commit that includes the README.md file - we have not checked out this commit yet. At the moment, our repository looks like this: The local branch HEAD is exactly where it was before we ran git fetch Because we have run git fetch, Git knows that our upstream has changed - in fact it even told us what the changes are, the message includes the text 2532277..4a28994 main -> origin/main which means 'new commits from 2532277 to 4a28994 have been fetched for main which tracks origin/main To actually download this (and any other) new commit we will need to run the git pull command.","s":"The Git Fetch Command","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#the-git-fetch-command","p":651},{"i":660,"t":"The git pull (download from remote) command integrates the changes from a remote into the current branch. Because we've already told Git what the 'upstream' for the main branch is, we can just run the git pull command to move to the latest commit: $ git pullUpdating 2532277..4a28994Fast-forward README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 README.md The git pull command tells us what commit we have moved from and to, and gives a summary of the files that have changed. We can see that a file named README has been created - if you look at your local files you'll see README.md is now present: $ lsREADME.md install.sh shell.d shell.sh Finally, if we run git log --graph --oneline we can see that our HEAD is at the tip of the main branch, and that this is also the tip of the origin remote's main branch: $ git log --graph --oneline* 4a28994 (HEAD -> main, origin/main) add a simple 'README' file* 2532277 Merge branch 'glog_alias'|\\| * a8cbb15 (glog_alias) add the 'glog' alias* | 31548e4 add the 'glog' alias|/* 138b404 Merge branch 'more_aliases'|\\| * a51ae1a (more_aliases) add a file to store 'zsh' aliases| * 63ea74f add a file to store 'bash' aliases* | a95bd90 add the 'gm' alias for 'git merge'|/* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'* f61369d (more_changes) add alias 'gs' for 'git status'* d7e1bb9 add the 'shell.d' folder* 01e7a10 add the 'install' and 'shell' scripts Congratulations - you have now created a local repository, staged and unstaged changes to and from the index, created commits, created branches, handled merging and merge conflicts and even learnt how to setup a remote and push and pull changes from it! You can pull any branch into your current branch - just provide the name of a remote and the name of a branch: git pull If you don't provide any parameters to git pull it will pull from the 'upstream' branch. But you can also use Git pull to merge other branches into your current branch.","s":"The Git Pull Command","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#the-git-pull-command","p":651},{"i":662,"t":"If you have been following through with these examples, you will now have a 'dotfiles' repository available on your GitHub account. You can make this repository public and allow people to copy your code or propose changes. If you want to clone your 'dotfiles' repository onto another machine, you can do so with this one-line command: git clone git://github.com//dotfiles.git The git clone (download a repository) command downloads the repository into the current folder. If your repository is private you will have to authenticate to be able to do this, but if your repository is public then you (or anyone else) can download your dotfiles. Hosting your dotfiles on GitHub is a very convenient way of making your dotfiles accessible anywhere. With a single command you can download them to any machine, and then run the install.sh script from the dotfiles folder to setup the shell startup files. If you search online for 'dotfiles', you will find many articles and users who have shared their dotfiles online, you can look over these for inspiration!","s":"Sharing Your Dotfiles","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#sharing-your-dotfiles","p":651},{"i":664,"t":"Forking and Pull Requests are features offered by popular Git hosting services like GitHub, BitBucket and GitLab. They are not actually Git features, but have become so widely used that you are likely to come across the terminology when working with online Git repositories. A fork is a copy of a Git repository. Typically you will fork a repository if you want to take a copy of someone else's code and work on it yourself. I have created a simple dotfiles repository on GitHub that you can use as a starting point for your own dotfile configuration. Let's use this to see how forking works. First, we open up the GitHub project that we'd like to fork, which in this case is at www.github.com/effective-shell/dotfiles. Notice that there is a 'Fork' option on the top-right of the screen: Choose the 'fork' option and GitHub will create a copy of the repository in your own account. You can now clone this repository, make changes and work on it as if it was your own. The original repository is tracked by GitHub, meaning that you can update from it at any time. If you have made changes to a fork, you can then open a Pull Request. A pull request is a request to merge a set of changes from one branch into another, or from one fork into the original repository. So if you were to improve upon the dotfiles that you forked, and wanted to share your changes back, you could open a pull request to do so. Typically when a pull request is opened, the project maintainer will then review the changes, make suggestions or discuss the proposal, and then either merge the pull request or reject it. In the screenshot below I am opening a pull request from my clone of the dotfiles repository to the original dotfiles repository, this pull requests adds an uninstall script to the repository. A maintainer of the target repository will then review the changes: This model of forking and pull requests is really just a nice user interface on top of the underlying capabilities Git has to track remotes and manage branches. Services like GitHub offer functionality to discuss changes, run arbitrary pipelines project maintainers might create to test the code and so on. GitHub has become a remarkably popular site for people to collaborate on projects together. At the time of writing the microsoft/vscode repository, which is the popular Visual Studio Code open-source editor, has had contributions from more than 19,000 individual contributors!","s":"Forking and Pull Requests","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#forking-and-pull-requests","p":651},{"i":666,"t":"When you push a branch to a remote on GitHub, GitLab, BitBucket and a number of other Git services providers, a message is shown in the command prompt with a link to open a Pull Request: $ git push -u origin fix/fix-shell-configuration...remote:remote: Create a pull request for 'fix/fix-shell-configuration' on GitHub by visiting:remote: https://github.com/dwmkerr/dotfiles/pull/new/fix/fix-shell-configuration... We can write a shell function that runs the git push command, reads its output, and then if it finds a web address, open it in a browser. In fact, the dotfiles repository at github.com/effective-shell/dotfiles has exactly this function - just run the command gpr to open a pull request! This function is actually quite straightforward - the code for it is below (with some of the comments and code to colour the output removed for legibility): gpr() { # Get the current branch name, or use 'HEAD' if we cannot get it. branch=$(git symbolic-ref -q HEAD) branch=${branch##refs/heads/} branch=${branch:-HEAD} # Pushing take a little while, so let the user know we're working. printf \"Opening pull request for ${branch}...\\n\" # Push to origin, grabbing the output but then echoing it back. push_output=`git push origin -u ${branch} 2>&1` printf \"\\n${push_output}\" # If there's anything which starts with http, it's a good guess it'll be a # link to GitHub/GitLab/Whatever. So open the first link found. link=$(echo ${push_output} | grep -o 'http.*' | head -n1 | sed -e 's/[[:space:]]*$//') if [ ${link} ]; then printf \"\\nOpening: ${GREEN}${link}${RESET}...\" python -mwebbrowser ${link} fi} This snippet first gets the name of the current branch, or HEAD if we cannot work the name out. When then run the git push origin command, and record the output of the command into a variable. Once the push command has completed, we write its output to the screen, and search for the first hyperlink, using grep and sed, then use python to open the link in a browser. I use the gpr function many times per day and it has been a real time-saver!","s":"A Script to Open a Pull Request","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#a-script-to-open-a-pull-request","p":651},{"i":668,"t":"In Chapter 26 - Customising Your Command Prompt we saw how to customise the command prompt by setting the PS1 variable. As you start using Git more you might find it convenient to show some information about the repository in your prompt. As an example, while I am writing this chapter, my command prompt looks like this: github/dwmkerr/effective-shell feat/managing-git-remotes ! 1 in stash$ This is easier to see in the screenshot below as it shows the colour: I spread my prompt over two lines and leave the prompt indicator as the only thing on the line where I type. This means my cursor does not become too indented. My command prompt shows the following information: Content Description github/dwmkerr/effective-shell My current folder, and up to two parent folders. feat/managing-git-remotes My current Git branch, if I am in a Git repository. ! A red exclamation mark is shown if I have uncommitted changes. 1 in stash If I have anything in my Git stash, a message is shown in amber. You can try out this style of command prompt by calling the set_ps1 function: source ~/dotfiles/shell.d/set_ps1.shset_ps1 dwmkerr If you want to change back to your previous prompt, just run set_ps1 again without any parameters. If you are interested in showing Git information in your command prompt you can take a look at the set_ps1.sh file to see the full details. We'll take a look at a snippet as it is interesting to see how to get this Git information. _git_info() { # Don't write anything if we're not in a folder tracked by git. if ! [ \"$(git rev-parse --is-inside-work-tree 2>/dev/null)\" == \"true\" ] then return fi # Get the branch name, changes, and number of stashes. local git_branch_name=\"$(git branch --show-current)\" local git_any_local_changes=\"$(git status --porcelain=v1 2>/dev/null)\" local git_stash_count=\"$(git rev-list --walk-reflogs --count \\ refs/stash -- 2>/dev/null)\" # Ignore error when no stashes local git_info=\"\" if [ \"${git_branch_name}\" = \"main\" ]; then git_info=\"${fg_green}${start_underline}${git_branch_name}${reset}\" else git_info=\"${fg_green}${git_branch_name}${reset}\" fi if ! [ -z \"${git_any_local_changes}\" ]; then # Note that we have to be careful to put the exclamation mark # in single quotes so that it is not expanded to the last command! git_info=\"${git_info} ${fg_red}\"'!'\"${reset}\" fi if [ \"${git_stash_count:-0}\" -gt 0 ]; then git_info=\"${git_info} ${fg_yellow}${git_stash_count} in stash${reset}\" fi printf \"${git_info}\"} This script does the following: The git rev-parse is used to check whether we are in a folder that is part of a Git working tree. If we are not in a Git working tree, we don't show anything. Get the current branch name with git branch --show-current). Check to see if there are any changes by looking at git status. The --porcelain=v1 line ensures that the status is machine-readable (this is easier to work with when scripting). Check the number of 'stash' revisions. Don't worry about stashes for now or the git rev-list command - these are more advanced features. The rest of the script is formatting only - underlining the branch name if it is the main branch, showing the exclamation mark if we have changes and so on. You can take the script as a starting point for your own customisations for the command prompt if you will be using Git a lot!","s":"Showing Git Information in the Command Prompt","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#showing-git-information-in-the-command-prompt","p":651},{"i":670,"t":"We have only seen the absolute basics of Git in this chapter. Git is an amazingly powerful tool, I cannot recommend enough that you take the time to really learn how the commands work. Many users will use a graphical tool to work with Git - this is perfectly fine if it works for you. But to be an effective shell user you should really spend the time getting familiar with the core Git commands using the command-line. Git can sometimes seem overwhelming to people and has a reputation for being complex. This is somewhat unfair - version control of files is itself an inherently complex topic. No matter what tool you use, there will always be the challenges of managing changes across environments, dealing with conflicts, integrating work and so on. The basic Git functionality is incredibly good at making 99% of this work simple and straightforward, and Git gives you the tools to make the other 1% at least manageable. Spend some time getting familiar with the core Git commands that we have introduced in this chapter. As you find yourself becoming more familiar, here are the next set of topics I would recommend learning about: The gitignore file: this special file can be used to tell Git not to track changes for certain files. The .gitconfig file: this is Git's own dotfile and can be used to fine-tune Git configuration. Tags: these are labels you can add to commits, which are ideal for tracking releases for projects or other metadata. Diffs: knowing how to use git diff to see changes between branches, commits, the index and working tree and more will certainly help you as you use Git more. Stashes: If you want to save your changes, but they are currently not ready to be committed (perhaps because they are only partially complete), you can use the git stash command to store working tree changes. This lets you store the changes away, then checkout other branches, then restore the changes later when you are ready. Git Clean: The git clean command is very useful to help you remove unneeded files from your working tree. Interactive Staging: You can interactively stage files, parts of files (called 'hunks') or even individual lines directly from the shell, this can be invaluable when making sure that exactly the right changes are going into the index. Patch staging or checkout: I probably use the git add -p command to 'patch' changes dozens of times a day, this is my preferred mechanism of reviewing my changes as I stage them. Merge Strategies: Understanding how 'squashing' works (and its drawbacks) can be very useful when working with branches. Merge strategies are a useful topic to go deeper on. Rebasing: Rebasing can be used as a merge strategy but can also help in other scenarios, I would definitely recommend learning about rebasing if you have multiple people working on a repository, it can save a lot of trouble when integrating complex changes. Commit and Tag signing: Great for security sensitive users, you can use special keys to 'sign' your commits and improve the security of your repositories. GitHub Flow: A common workflow used with GitHub projects. There are numerous articles and online books on Git - I also recommend the excellent book \"Pro Git\" by Scott Chacon and Ben Straub.","s":"Scratching the Surface","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#scratching-the-surface","p":651},{"i":672,"t":"Before we finish the chapter, let's do a quick review of the key commands for working with Git. You can refer to the illustration below: As a reminder, the core concepts are: Concept Description The Working Tree The folder you are working in and tracking change to. The Index The 'staging' area for building commits. The Repository The full set of all commits, branches and metadata. A Fork A copy of an entire repository, including its history and all branches. A Pull Request A proposal to merge one branch into another, or a branch in a fork to the upstream. git init Creates a local repository. git clone Downloads a remote repository. git add Stage a change from the working tree to the index. git reset Unstage a change from the index. git commit Create a new commit. git checkout -b Create a new branch. git merge Merge a branch into the current branch. git checkout Move the current HEAD to a new branch or commit. git push Push changes to an upstream branch. git fetch Retrieve information about changes to a remote. git pull Download and merge changes from a remote.","s":"An Overview of the Git Commands","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#an-overview-of-the-git-commands","p":651},{"i":674,"t":"In this chapter we learned how to use GitHub to host a remote repository, how to push, fetch and pull changes, and how remotes work. We also looked at pull requests, forks, how to show Git information on the command line, and some of the more advanced topics that you might want to explore as you use Git more. Although we've only scratched the surface of what Git can do, you should now have the tools to work with repositories, share content like your dotfiles, collaborate with others and manage your own changes. In the next part of the book we will look at some advanced techniques that can help you as a shell user.","s":"Summary","u":"/part-5-building-your-toolkit/managing-rempte-git-repositories/","h":"#summary","p":651},{"i":676,"t":"On this page","s":"Controlling Changes with Git","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"","p":675},{"i":678,"t":"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. Git is a 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. 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 CVS (Concurrent Version System) and SVN (Subversion, an system similar to CVS but with some improvements). There were also a number of proprietary and commercial solutions. 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.","s":"What is Git","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#what-is-git","p":675},{"i":680,"t":"All of the information about a set of files or folders that you are tracking the changes for is stored in a Git repository. We can create a Git repository by running the git init (initialise Git repository) command. 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 Chapter 27 - Managing Your Dotfiles or you can download the Effective Shell samples to get a copy of the 'dotfiles' folder. Downloading the Samples Run the following command in your shell to download the samples: curl effective.sh | sh If you have installed the samples, you can copy the ~/effective-shell/dotfiles folder to your home directory - this is where we will create our Git repository and start using the Git commands: $ cp -r ~/effective-shell/dotfiles ~/dotfiles$ cd ~/dotfiles We have created the ~/dotfiles folder in our home directory from the samples and moved into it. Now we will initialise a Git repository with the git init command, and choose a 'branch' name with the git checkout command: $ git initInitialized empty Git repository in /home/dwmkerr/dotfiles/.git/$ git checkout -b main We'll see the 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. Initialising a Git Repository with a Branch Name If you are using Git 2.2 or later, you can initialise a repository and set the branch name with a single command: $ git init -b main If you see the error message error: unknown switch 'b' then this means that you are using a version of Git that does not have the -b (initial branch name) flag (any version of Git lower than 2.2).","s":"Creating a Git Repository","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#creating-a-git-repository","p":675},{"i":682,"t":"We now have an empty Git repository. We can use the git status (show the working tree status) command to show some information on the files in the working tree. The working tree is the folder that we are using Git to track changes for, in our case ~/dotfiles: $ git statusOn branch mainNo commits yetUntracked files: (use \"git add ...\" to include in what will be committed) install.sh shell.d/ shell.shnothing added to commit but untracked files present (use \"git add\" to track) The first thing that git status tells us is the name of the branch we are on. We'll look at branches in detail shortly. The next thing we see is that there are no 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 install.sh and shell.sh files as well as the shell.d folder. 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 git add (add file contents to index) command: $ git add . The git add command takes a list of file paths. We have used the special dot folder to represent the entire current directory. Let's take a look at the status again: $ git statusOn branch mainNo commits yetChanges to be committed: (use \"git rm --cached ...\" to unstage) new file: install.sh new file: shell.d/set_ps1.sh new file: shell.sh Git is now telling us that we have three new files which are ready to be committed. At the moment these files are in the 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. 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. If we were to visualise what we've done so far, it would look like this: Our working tree is the folder associated with our Git repository, this is the ~/dotfiles folder. Our index is initially empty. When we run the 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. 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 git reset (reset changes) command. Let's reset the ~/dotfiles/shell.d/set_ps1.sh file and check the status: $ git reset shell.d/set_ps1.sh$ git statusOn branch mainNo commits yetChanges to be committed: (use \"git rm --cached ...\" to unstage) new file: install.sh new file: shell.shUntracked files: (use \"git add ...\" to include in what will be committed) shell.d/ The 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. Here's how we can visualise the changes we've made: You can also reset changes by using the git rm --cached (remove changes from index) command. However, I think this is a little harder to work with as you have to remember to use the --cached flag to tell Git that you are removing from the index and not the repository. We'll see the git rm command a little later in the chapter. 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'. Now let's look at how we can commit our changes with the git commit command.","s":"Adding and Resetting Changes to the Index","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#adding-and-resetting-changes-to-the-index","p":675},{"i":684,"t":"Once we are happy with the set of changes in the index, we can use the git commit (record changes to the repository) command. Now run the git commit command: $ git commit At this stage your shell editor will open up, with the text shown below: # Please enter the commit message for your changes. Lines staring# with '#' will be ignored, and an empty message aborts the commit.## On branch main## Initial commit## Changes to be committed:# new file: install.sh# new file: shell.sh## Untracked files:# shell.d/# The reason you shell editor opens is that the git commit command would like you to provide a message describing your changes. Type a short description, such as: add the 'install' and 'shell' scripts# Please enter the commit message for your changes. Lines staring# with '#' will be ignored, and an empty message aborts the commit.## On branch main## Initial commit## Changes to be committed:# new file: install.sh# new file: shell.sh## Untracked files:# shell.d/# Note that below the cursor there is some information that starts with the # 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. Save the file by pressing Ctrl+W and close the editor with Ctrl+X (these are the commands for the nano editor, if you are using a different editor use whatever commands are needed to save and close). When the editor closes you'll see a confirmation below the git commit command: $ git commit[main (root-commit) 01e7a10] add the 'install' and 'shell' scripts 2 files changed, 90 insertions(+) create mode 100755 install.sh create mode 100644 shell.sh 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: We 'staged' a set of changes and then 'committed' these changes. We now have a single commit in our repository. Our files are still unchanged, but our Git repository now has a single commit in it that tracks the two files we added. Let's run git status again: $ git statusOn branch mainUntracked files: (use \"git add ...\" to include in what will be committed) shell.d/nothing added to commit but untracked files present (use \"git add\" to track) The 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 git commit command below, enter a message to describe the commit: $ git add .$ git commit[main d7e1bb9] add the 'shell.d' folder 1 file changed, 228 insertions(+) create mode 100644 shell.d/set_ps1.sh We have now created a second commit - our timeline will look like this: If we run the git status command one last time we will see that everything in the working tree is tracked in Git: $ git statusOn branch mainnothing to commit, working tree clean 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.","s":"Committing Changes","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#committing-changes","p":675},{"i":686,"t":"You can use any text you like for a commit message. However, there are a couple of things that you should bear in mind: 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. 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. 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! Now let's take a look at how we can work on changes to our files with branches.","s":"Commit Messages","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#commit-messages","p":675},{"i":688,"t":"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. We can create branches using the git branch (list, create or delete branches) or git checkout (switch branches or restore working tree) command. To show these features in action, we'll create a new branch called aliases and add some files to it: $ git checkout -b aliasesSwitched to a new branch 'aliases'$ git statusOn branch aliasesnothing to commit, working tree clean We have used the git checkout command to 'switch' to another branch. The -b (new branch) option tells Git that we want to create a new branch. The git status command now shows the new branch name when we run it. Let's create a new file which includes an alias for the git status command, then let's see what git status tells us about the status of the working tree: $ echo 'alias gs=\"git status\"' >> ./shell.d/git_aliases.sh$ git statusOn branch aliasesUntracked files: (use \"git add ...\" to include in what will be committed) shell.d/git_aliases.shnothing added to commit but untracked files present (use \"git add\" to track) Excellent - we have a new file and Git knows that it is not currently tracked. Let's stage this file and then commit it: $ git add .$ git commit -m \"add alias 'gs' for 'git status'\"[aliases f61369d] add alias 'gs' for 'git status' 1 file changed, 1 insertion(+) create mode 100644 shell.d/git_aliases.sh In the example above I used the -m (commit message) parameter for the git commit command, this means my editor will not open up as we've already provided a commit message. We now have a series of commits that looks like this: Our new ~/dotfiles/shell.d/git_aliases.sh file has been committed to the aliases branch. We can switch back to the main branch at any time with git checkout: $ git checkout mainSwitched to branch 'main'$ tree.β”œβ”€β”€ install.shβ”œβ”€β”€ shell.dβ”‚ └── set_ps1.sh└── shell.sh When we switch back to the main branch and look at our working tree we can see that the 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 git checkout command we can switch branches. If we are on the main branch we don't see the git_aliases.sh file, because the commit that added is was not on the main branch. To go back to the aliases branch we can just checkout again: $ git checkout aliasesSwitched to branch 'aliases'$ tree.β”œβ”€β”€ install.shβ”œβ”€β”€ shell.dβ”‚ β”œβ”€β”€ git_aliases.shβ”‚ └── set_ps1.sh└── shell.sh As a nice little tip, you can always go back to the last branch you were on by running git checkout - just like you can use cd - to change to the last directory you visited! The dash character is a shortcut for the last branch you were on. Let's add another alias to the file and create another commit: $ echo 'alias gcm=\"git checkout main\"' >> ./shell.d/git_aliases.sh$ git add .$ git commit -m \"add alias 'gcm' for 'git checkout main'\"[aliases b9ae0ad] add alias 'gcm' for 'git checkout main' 1 file changed, 1 insertion(+) Our branches will now look like this: You can create as many branches as you like - just remember that when you run git checkout -b, you branch from the current branch (and in fact, the current HEAD, which we will see a little later). If you want to create a branch, but don't want to switch to it, you can run git branch . This command will create a branch from your current position, but will not move to it.","s":"Creating Branches","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#creating-branches","p":675},{"i":690,"t":"You can use the git merge (join two or more branches) command to take the changes from one branch and bring them into another. We can merge the changes from our aliases branch into the main branch of the repository by first checking out the branch we want to merge into, and then running git merge: $ git checkout main$ git merge aliasesUpdating d7e1bb9..b9ae0adFast-forward shell.d/git_aliases.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 shell.d/git_aliases.sh When we run the git merge command Git tells us what type of merge it has performed. In this case we have a 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 aliases branch can be applied sequentially to the main branch: One the merge is complete, our branches look like this: The main branch and aliases branch contain the exact same set of commits. Let's look at a more common merge scenario - merging branches that have diverged.","s":"Merging","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#merging","p":675},{"i":692,"t":"Let's create set of commits that show a case where we our branches have diverged - the branches both have their own new commits: # Create a branch called 'more_aliases', add a file to it, then commit.git checkout -b more_aliasestouch ./shell.d/bash_aliases.shgit add .git commit -m \"add a file to store 'bash' aliases\"# Create another file, add it, then commit.touch ./shell.d/zsh_aliases.shgit add .git commit -m \"add a file to store 'zsh' aliases\" This snippet checks out a new branch called more_aliases and adds two new empty files, as two separate commits. Now we'll go back to our main branch and change a file: # Go back to the 'main' branch, add and commit another file.git checkout mainecho 'alias gm=\"git merge\"' >> ./shell.d/git_aliases.shgit commit -a -m \"add the 'gm' alias for 'git merge'\" I have added a new alias to the shell.d/git_aliases.sh file. By adding the -a (all changes) flag to the git commit command I was able to add the changes to the index and commit them with a single command. Our branches now look like this: Let's merge the more_aliases branch into the main branch: $ git merge more_aliases At this point your shell editor will open up with a request for a commit message: Merge branch 'more_aliases'# Please enter a commit message to explain why this merge is needed# especially if it merges an updated upstream into a topic branch.## Lines starting with '#' will be ignored, and an empty message will abort# the commit. Git is going to create a new commit on the main branch that brings in the changes from the 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 more_aliases. 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: $ git merge more_aliasesMerge made by the 'recursive' strategy. shell.d/bash_aliases.sh | 0 shell.d/zsh_aliases.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 shell.d/bash_aliases.sh create mode 100644 shell.d/zsh_aliases.sh Git now tells us that we have made a recursive merge and our branches will look like this: At this stage both the main branch and the more_aliases branch have the full set of changes that we made to both of the branches. Git merged the two branches together and created a new commit that joins them.","s":"Merging Diverged Branches","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#merging-diverged-branches","p":675},{"i":694,"t":"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 SHA, which is a hash. A hash is a sequence of letters and numbers that uniquely identify each commit. You can use the git log (show commit logs) command to see the log of commits and their SHAs: $ git logcommit 138b40418d5658bc64421e7bcf2680c8339f8350 (HEAD)Merge: a95bd90 a51ae1aAuthor: Dave Kerr Date: Tue Jun 15 21:00:28 2021 +0800 Merge branch 'more_aliases'commit a95bd90e3656b2e55b8708193d387c80c282a6adAuthor: Dave Kerr Date: Tue Jun 15 21:00:22 2021 +0800 add the 'gm' alias for 'git merge'commit a51ae1aa42432c2f391ca782c1c20b3793c232ab (more_aliases)Author: Dave Kerr Date: Tue Jun 15 20:53:01 2021 +0800 add a file to store 'zsh' aliases You can see that the log shows each of the commits, the branch the commit was on, the message, the date and so on. If you want to see a more compact log you can use the --oneline (show one line per commit) flag: $ git log --oneline138b404 (HEAD) Merge branch 'more_aliases'a95bd90 add the 'gm' alias for 'git merge'a51ae1a (more_aliases) add a file to store 'zsh' aliases63ea74f add a file to store 'bash' aliasesb9ae0ad (aliases) add alias 'gcm' for 'git checkout main'f61369d add alias 'gs' for 'git status'd7e1bb9 add the 'shell.d' folder01e7a10 add the 'install' and 'shell' scripts 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. 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 --graph (show commit graph) flag: $ git log --oneline --graph* 138b404 (HEAD) Merge branch 'more_aliases'| \\| * a51ae1a (more_aliases) add a file to store 'zsh' aliases| * 63ea74f add a file to store 'bash' aliases* | a95bd90 add the 'gm' alias for 'git merge'|/* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'* f61369d add alias 'gs' for 'git status'* d7e1bb9 add the 'shell.d' folder* 01e7a10 add the 'install' and 'shell' scripts Each commit is shown with an * asterisk symbol - we can also see when we created the more_aliases branch and when we merged it back in. The Git log is very useful to help you understand the changes that have happened in the repository.","s":"The Git Log","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#the-git-log","p":675},{"i":696,"t":"One of the most important features of any version control system is the ability to manage conflicts. Conflicts occur when a set of changes are made that cannot be merged without some kind of manual intervention to decide which of the changes are correct. Here are a few common scenarios that might lead to merge conflicts: 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? In one branch a file is edited and in another branch the same part of the file is edited in a different way - which edit should we keep? Should we keep both? 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? 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 main branch while you are working on another branch, you are drifting from the main branch. You should either regularly update your branch with the changes in main or merge your changes into main. There are many different ways for version control systems to manage conflicts. Let's see how Git does it by creating a conflict. First, we will create a branch that adds a new alias to the git_aliases.sh file: # Create a 'glog_alias' branch and commit a file.git checkout -b glog_aliasecho 'alias glog=\"git log --graph --oneline\"' >> ./shell.d/git_aliases.shgit commit -a -m \"add the 'glog' alias\" Now we'll go back to the main branch and add another alias: # Go back to 'main' and commit a change to the same file as the last one.git checkout mainecho 'alias glog=\"git log\"' >> ./shell.d/git_aliases.shgit commit -a -m \"add the 'glog' alias\" We've changed the same file in two branches - if we try to merge we will get a conflict: $ git merge glog_aliasAuto-merging shell.d/git_aliases.shCONFLICT (content): Merge conflict in shell.d/git_aliases.shAutomatic merge failed; fix conflicts and then commit the result. 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 index. We have to now manually fix these files. Let's see what the status shows: $ git statusOn branch mainYou have unmerged paths. (fix conflicts and run \"git commit\") (use \"git merge --abort\" to abort the merge)Unmerged paths: (use \"git add ...\" to mark resolution) both modified: shell.d/git_aliases.shno changes added to commit (use \"git add\" and/or \"git commit -a\") 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 shell.d/git_aliases.sh file, then use git add to stage the changes and commit the result. To resolve the conflict, we need to edit the file. If you open the file in an editor it will look like this: alias gs=\"git status\"alias gcm=\"git checkout main\"alias gm=\"git merge\"<<<<<<< HEADalias glog=\"git log\"=======alias glog=\"git log --graph --oneline\">>>>>>> glog_alias Many modern editors will immediately recognise the sequence of symbols that Git uses to indicate conflicts and highlight them. The <<<<<< HEAD line indicates the changes in the current branch, the ======= line is a separator, and the >>>>>> glog_alias line indicates that everything since the ======= line is the changes in the glog_alias branch. 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. In your editor, update the file to look like this: alias gs=\"git status\"alias gcm=\"git checkout main\"alias gm=\"git merge\"alias glog=\"git log --graph --oneline\" We have chosen the version of the glog alias that was in the 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 <<<, === and >>>. 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. In the example above we deleted one of the glog aliases, but we could have also just changed the name of one of them: alias gs=\"git status\"alias gcm=\"git checkout main\"alias gm=\"git merge\"alias glog=\"git log\"alias ggraph=\"git log --graph --oneline\" We can now use git add to mark the file as resolved, and run git commit: $ git add shell.d/git_aliases.sh The editor will open showing a sensible commit message: Merge branch 'glog_alias'# Conflicts:# shell.d/git_aliases.sh## It looks like you may be committing a merge.# If this is not correct, please run# git update-ref -d MERGE_HEAD# and try again.# Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit.## On branch main 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. Our Git log will now show this new merge commit: $ git log --graph --oneline* 2532277 (HEAD -> main) Merge branch 'glog_alias'|\\| * a8cbb15 (glog_alias) add the 'glog' alias* | 31548e4 add the 'glog' alias|/* 138b404 Merge branch 'more_aliases'|\\| * a51ae1a (more_aliases) add a file to store 'zsh' aliases| * 63ea74f add a file to store 'bash' aliases* | a95bd90 add the 'gm' alias for 'git merge'|/* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'* f61369d add alias 'gs' for 'git status'* d7e1bb9 add the 'shell.d' folder* 01e7a10 add the 'install' and 'shell' scripts 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.","s":"Merge Conflicts","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#merge-conflicts","p":675},{"i":698,"t":"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. 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. 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.","s":"Other Merge Strategies","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#other-merge-strategies","p":675},{"i":700,"t":"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. Let's see both ways in action. First we'll use the git rm (remove files from the working tree and index) command: $ git rm install.shrm 'install.sh' The 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 git status: $ git statusOn branch mainChanges to be committed: (use \"git restore --staged ...\" to unstage) deleted: install.sh The deletion is only in the index at this stage - we need to run git commit to commit the deletion. If we want to restore the file, we can use git checkout to get the file back from Git: $ git checkout HEAD install.sh We use 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 main). Another common way to delete a file is to simply remove it from the file-system: $ rm install.sh$ git statusOn branch mainChanges not staged for commit: (use \"git add/rm ...\" to update what will be committed) (use \"git restore ...\" to discard changes in working directory) deleted: install.sh At this point Git knows the file is missing, but the deletion is not yet 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: $ git rm install.sh We could also run git add install.sh - think of this like saying 'stage my change to index.sh' - we are adding a change to the index. I often just run 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. Let's restore the file: $ git reset .Unstaged changes after reset:D install.sh$ git checkout .Updated 1 path from the index This is another way to restore the file - first we reset all of the changes to the index with git reset ., then we checkout all of the files in the current commit with git checkout .. But be careful with this method - it will also reset any other changes you have made. I prefer the git checkout HEAD method as it is more explicit and only restores one file. What about if we rename a file? Let's rename the install file and see what the status is: $ mv install.sh install_dotfiles.shChanges not staged for commit: (use \"git add/rm ...\" to update what will be committed) (use \"git restore ...\" to discard changes in working directory) deleted: install.shUntracked files: (use \"git add ...\" to include in what will be committed) install_dotfiles.shno changes added to commit (use \"git add\" and/or \"git commit -a\") We've renamed the file, but when we run 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 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: $ git add .$ git statusOn branch mainChanges to be committed: (use \"git restore --staged ...\" to unstage) renamed: install.sh -> install_dotfiles.sh Let's restore the file by renaming it back to what it was and adding these changes: $ mv install_dotfiles.sh install.sh$ git add .$ git statusOn branch mainnothing to commit, working tree clean We can also use the git mv (move or rename a file) command to move or rename a file and stage the changes in one go: $ git mv install.sh install_dotfiles.shChanges to be committed: (use \"git restore --staged ...\" to unstage) renamed: install.sh -> install_dotfiles.sh We can also restore the changes by using git mv: $ git mv install_dotfiles.sh install.sh$ git statusOn branch mainnothing to commit, working tree clean These commands work equally well with folders, or lists of files and folders.","s":"Deleting and Renaming Files","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#deleting-and-renaming-files","p":675},{"i":702,"t":"We've already seen how the git checkout command can be used to switch branches. We can also use the 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. Let's take a quick look at our Git log so far. $ git log --graph --oneline* 2532277 (HEAD -> main) Merge branch 'glog_alias'|\\| * a8cbb15 (glog_alias) add the 'glog' alias* | 31548e4 add the 'glog' alias|/* 138b404 Merge branch 'more_aliases'|\\| * a51ae1a (more_aliases) add a file to store 'zsh' aliases| * 63ea74f add a file to store 'bash' aliases* | a95bd90 add the 'gm' alias for 'git merge'|/* b9ae0ad (aliases) add alias 'gcm' for 'git checkout main'* f61369d add alias 'gs' for 'git status'* d7e1bb9 add the 'shell.d' folder* 01e7a10 add the 'install' and 'shell' scripts 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. Notice that the most recent commit is marked with the text HEAD. The HEAD is where Git is currently 'pointing' to - we will see how to move the head shortly (the term HEAD also refers to the most recent commit of a branch). At the moment we can visualise our Git log, with the commits, branches and HEAD like so: We can restore the working tree to the state of any of the commits by running git checkout : $ git checkout f61369dNote: switching to 'f61369d'.You are in 'detached HEAD' state. You can look around, make experimentalchanges and commit them, and you can discard any commits you make in thisstate without impacting any branches by switching back to a branch. This snippet moved our HEAD to the third commit in our repository. If you look at the files in your working tree you'll see that they are in exact state that they were when we made our third commit. Visually, we have moved the HEAD as shown below: The warning that says we are in a 'detached HEAD' state is telling us that our current position in the history is not at the 'tip' of a branch - this means we cannot create a new commit without first starting a new branch. If we want to move back to the 'tip' of our main branch (or any other branch), we can use git checkout : $ git checkout main This moves us back to the 'tip' of the main branch. Git has a very convenient syntax we can use to move backwards. We can specify a branch name, a SHA or 'HEAD', use a ~ tilde character, and then provide the number of commits we want to move 'backwards': $ git checkout HEAD~1 This command moves the 'head' backwards one commit. This is useful if you realise you have made a mistake with your commit or recent commits and want to go backwards. Remember - whenever you run git checkout Git moves the HEAD to the tip of the branch by default.","s":"Restoring Your Working Tree","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#restoring-your-working-tree","p":675},{"i":704,"t":"In this chapter we looked at some of the core concepts behind Git - the repository, the working tree and the index. We saw how to stage and unstage changes, commit changes, create branches, merge branches, deal with conflicts and remove and rename files. In the next chapter we will look at how to work with 'remote' repositories - this will allow us to push our changes up to a location on the internet and share these changes with other users, or to download these changes to other machines we might work on. We've introduced a lot of commands in this chapter - you can use this table as a quick reference: Command Description git init Initialise a new Git Repository. git status Show the status of the working tree and index. git add Stage files - you can use patterns and wildcards. git reset Unstage files - you can use patterns and wildcards. git rm --cached Unstage files - you can use patterns and wildcards. git commit Create a commit from the current index - the shell editor will open for the commit message. git commit -m 'message' Create a commit with message message. git commit -a Stage and commit all changes in the working tree. git checkout Checkout a branch called branch. git checkout -b branch Create and checkout a new branch called branch. git branch Create a branch called name but do not check it out. git branch -m Change the current branch name to new_name. git merge Merge the branch named branch into the current branch. git log Show the log of commits. git log --oneline --branch Show the log of commits, one line per commit, with the branch graph. git rm Stage the removal of files from the repostiry - you can use patterns and wildcards. git mv Stage the movement of source to destination. git checkout 8342bec Checkout a commit with SHA 834bec. git checkout HEAD~1 Move the current HEAD back one commit. git checkout ~3 Checkout branch, move back three commits from the tip.","s":"Summary","u":"/part-5-building-your-toolkit/controlling-changes-with-git","h":"#summary","p":675},{"i":706,"t":"On this page","s":"Customising Your Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"","p":705},{"i":708,"t":"The command prompt is the text that is shown to the left of your cursor to show that the shell is waiting for you to type a command. Each distribution comes with its own configuration for the command prompt, but the default is often similar to the one shown below: dwmkerr@effective-shell-ubuntu-20:~$ This is the prompt on an Ubuntu virtual machine I have set up. If you want to set up a free virtual machine yourself, you can follow the guide at Appendix - Setting Up a Linux Virtual Machine. Let's take a look at each of the components that make up the prompt: dwmkerr - The first thing that is shown is the name of the current user @ - Next we have an 'at symbol' character that is used as a separator between the username field and the following field effective-shell-ubuntu: This is the hostname of the machine : - A colon separates the hostname from the next field ~ - Next we have the current working directory $ - Finally we have the prompt itself, the $ symbol shows we are a normal user, rather than a 'root' user If we change directory, our prompt will be updated: dwmkerr@effective-shell-ubuntu-20:~$ cd effective-shelldwmkerr@effective-shell-ubuntu-20:~/effective-shell$ If we change to the 'super' user we can see that the username changes to root and the $ dollar symbol changes to a # hash symbol: dwmkerr@effective-shell-ununtu-20:~/effective-shell$ sudo suroot@effective-shell-ununtu-20:/home/dwmkerr/effective-shell# The # symbol is a useful reminder that we are the root user. It is important to be careful when running commands as the root user as we could easily break things by changing system files. So out-of-the box on most systems our command prompt shows a number of useful fields. But we can actually customise this prompt to include almost any kind of information we would like to see. Let's take a look!","s":"The Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#the-command-promptindex","p":705},{"i":710,"t":"The structure of the command prompt is specified in the PS1 shell variable. This stands for 'Prompt String 1'. The shell uses this variable to write out the command prompt. We can see the contents of this variable by using echo or printf to write it to the screen: dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ echo $PS1\\[\\e]0;\\u@\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\[\\033[01;32m\\]\\u@\\h\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ This looks extremely complicated - but don't worry, by the time we've finished this chapter you'll be able to understand what this mess of special characters means! The easiest way to see how these special prompt strings work is to start using them, so let's get started and customise our prompt.","s":"Customising the Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#customising-the-command-prompt","p":705},{"i":712,"t":"You can set your own prompt string by setting the PS1 variable: dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ PS1=\"---> \"---> The shell will use the contents of the PS1 variable to display the prompt. We can use plan text as shown above, but there's also a lot more that we can do to customise this prompt!","s":"The Prompt String","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#the-prompt-string","p":705},{"i":714,"t":"When the shell reads the PS1 variable, it allows certain special characters to be specified. These characters can be used to customise how the prompt string looks. The special characters that the shell uses are listed below: Characters Usage \\a The special 'beep' character, that tells the shell to play a beep sound through the speakers. \\d The date in \"Weekday Month Date\" format, for example: 'Tue May 26') \\D{format} The date in a format specified by the format value. \\e An ASCII escape character (033). This is used to print special characters. \\h The hostname up to the first . dot. \\H The hostname. \\j The number of jobs currently managed by the shell. \\l The basename of the shell's terminal device. \\n A newline character. \\r A carriage return character. \\s The name of the shell, the basename of $0, for example: -bash. \\t The current time in 24-hour HH:MM:SS format. \\T The current time in 12-hour HH:MM:SS format. \\@ The current time in 12-hour am/pm format. \\A The current time in 24-hour HH:MM format. \\u The username of the current user. \\v The version of bash, for example: '5.0'. \\V The release of bash, with the patch level, for example: '5.0.17'. \\w The current working directory, with $HOME abbreviated with a ~ tilde symbol. \\W The current working directory name (rather than the entire path as is used for \\w). \\! The history number of this command. \\# The command number of this command \\$ The $ dollar symbol, unless we are a super-user, in which case the # hash symbol is used. \\nnn The character corresponding to the octal number nnn, used to show special characters. \\\\ A \\ backslash character. \\[ The 'start of non-printing characters' sequence. \\] The 'end of non-printing characters' sequence. Some of these sequences are reasonably self-explanatory, some are a little more complex. Let's use some of them now to see how we can customise the prompt. Z-Shell The zsh shell uses different sequences. However, I suggest that you follow this chapter through to understand how Bash-like shells work and then you can apply the same techniques using Z-Shell. The Z-Shell documentation links are at the end of the chapter. Later on in this chapter we will introduce a function to help set the prompt, this function automatically converts to the prompt into Z-Shell format if needed. So the techniques you learn here should still be able to be used in Z-Shell. To change the prompt, all we need to do is set the PS1 variable. Let's start by changing the prompt so that it shows the date, time and the $ or # prompt symbol: dwmkerr@effective-shell-ubuntu-20:~$ PS1='\\d \\@ \\$ 'Sun Jun 06 12:43 PM $ In this example we've used the \\d (current date), \\@ (current time in am/pm format) and \\$ (prompt) and a space for our prompt. Notice that once we set PS1 in the shell, the prompt immediately changed. How about if we want to show the number of jobs, then the command number, then the prompt? Easy! Sun Jun 06 04:43 AM $ PS1='[\\j] (\\#) \\$ '[0] (4) $ sleep 10 &[1] 27598[1] (5) $ sleep 10 &[2] 27600[2] (6) $ sleep 10 &[3] 27601[3] (7) $ In this example we've used the \\j (current job) sequence, and surrounded it with square brackets. Then we used # (command number), surrounded by parentheses, then the \\$ shell prompt. I also started some background jobs, that just run the sleep (wait for a number of seconds) command, so that we can see that the number of jobs is changing. If you need a refresher on jobs, check Chapter 9 - Job Control. Note that we are using single quotes when specifying the value of the PS1. If we didn't use single quotes, then the shell would see the dollar symbol and think that we were trying to use a variable. For a reminder on how quoting works, check Chapter 19 - Variables, Reading Input, and Mathematics. If you are following along or trying this out in your own shell, you might have noticed that we don't have any colours for the new prompts we have set, everything is shown in white. To set the colour of the prompt we need to use some special characters.","s":"Special Characters","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#special-characters","p":705},{"i":716,"t":"In the earlier part of this chapter we saw that the default prompt on systems like Ubuntu contains lots of special characters. For reference, here is the value of the PS1 variable on a clean Ubuntu 20 installations: dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ echo $PS1\\[\\e]0;\\u@\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\[\\033[01;32m\\]\\u@\\h\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ Some of these characters we might now be able to recognise, such as \\u for the username and \\h for the host. The characters that start with the sequence \\033 are ANSI color codes. ANSI stands for American National Standards Institute, an organisation that was set up to attempt to set common standards for computing platforms. In the early days of Unix, each vendor developed their own special characters that could be used to control the visual formatting of output. These characters would vary from platform to platform, which made trying to create scripts or functionality that worked across multiple platforms complex. To deal with this, the ANSI organisation defined a common set of codes that could be printed to a terminal to control the visual style of the output. To tell a terminal that we want to use a special sequence to control the formatting or output of text, we can use these ANSI Escape Sequences. First we write out the characters \\033 or \\e. This is the sequence that represents the 'escape' key. The first version is the 'escape' key code written in 'octal' format (octal is a format where numbers are written in base eight, rather than base ten). The second sequence is an alternative way of writing the 'escape' key. When a terminal sees the escape sequence, it knows that the following sequence is used to define the formatting. The table below shows some of the different formats that can be used: Sequence Meaning Foreground Color \\033[30m Set foreground to 'black'. \\033[31m Set foreground to 'red'. \\033[32m Set foreground to 'green'. \\033[33m Set foreground to 'yellow'. \\033[34m Set foreground to 'blue'. \\033[35m Set foreground to 'magenta'. \\033[36m Set foreground to 'cyan'. \\033[37m Set foreground to 'white' (normally light grey) Foreground Color (Bold) \\033[1;30m Set foreground to 'bright black' (grey, or bold black) \\033[1;31m Set foreground to 'bright red' (or bold red). \\033[1;32m Set foreground to 'bright green' (or bold green). \\033[1;33m Set foreground to 'bright yellow' (or bold yellow). \\033[1;34m Set foreground to 'bright blue' (or bold blue). \\033[1;35m Set foreground to 'bright purple' (or bold purple). \\033[1;36m Set foreground to 'bright cyan' (or bold cyan). \\033[1;37m Set foreground to 'bright white' (or bold white). Background Color \\033[0;40m Set background to 'black'. \\033[0;41m Set background to 'red'. \\033[0;42m Set background to 'green'. \\033[0;43m Set background to 'brown'. \\033[0;44m Set background to 'blue'. \\033[0;45m Set background to 'purple'. \\033[0;46m Set background to 'cyan'. \\033[0;47m Set background to 'white' (normally light grey). Reset Colors \\033[0m Reset the text colors. Notice that each sequence starts with the 'escape' character, followed by [ or [1;. [ will use the 'normal' colour, [1; will use the 'bright' colour (how this is shown depends on your terminal emulator, in many modern emulators the text is shown in the same colour but is bold). You can also use [0; to clear any changes to the foreground or background before you set the new one. After this there are one of eight colours that can be used, specified by the characters in the range from 30m to 37m. The sequences in the range 40m to 47m set the background color. The sequence 0m resets the colors. With these codes we can print coloured text. When we write text that uses escape sequences, we need to tell the shell that our text needs to have these escape sequences processed properly. We can use the printf command or echo -e to do this. printf should be preferred as not all systems support the -e parameter for echo: printf \"\\033[31mRED\\033[0m\\n\"printf \"\\033[1;31mLIGHT RED\\033[0m\\n\"printf \"\\033[0;30m\\033[42mBLACK ON GREEN\\033[0m\\n\" The output of these commands will be the text below: REDLIGHT REDBLACK ON GREEN However, the colour of the foreground and background should change on each line. The exact formatting will change depending on the terminal emulator you use. Some terminal emulators use bold text for the 'bright' colours. With our new knowledge of how to use ANSI Escape Sequences to set the format of text, we can update our PS1 variable to show a prompt in colour. As an example, the code below sets the prompt to show the username in blue and the name of the current working directory in green, followed by a white $ prompt symbol, followed by the 'reset' sequence so that the text we type afterwards does not have its colour changed: dwmkerr@effective-shell-ubuntu-20:~/effective-shell$ PS1='\\033[34m\\u \\033[32m\\W \\033[37m\\$ 'dwmkerr effective-shell $ The prompt above will be shown in color on modern terminals. There is one snag to this. If you set your prompt in this way and press the 'up' and 'down' keys to cycle through previously entered commands, you might see that your shell prompt gets overwritten. The reason for this is that we need to tell the shell that colour and formatting sequences are 'non-printing' characters - the sequences don't actually produce written text in the terminal. To deal with this we need to surround each colour sequence with the special characters \\[ and \\]. This tells the shell when a 'non-printing' sequence starts and when it ends. To fix our PS1 variable, we can use the value below: PS1='\\[\\033[34m\\]\\u \\[\\033[32m\\]\\W \\[\\033[37m\\]\\$ \\[\\033[0m\\]' Phew! This is a lot of work to go to just to format the colour of the prompt. Later in this chapter we'll build a script that will make it far easier to work with colours and text formatting!","s":"Changing the Colour and Text Formatting","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#changing-the-colour-and-text-formatting","p":705},{"i":718,"t":"When we set the PS1 variable, we are simply setting it to a string. This string could be anything, for example: dwmkerr@effective-shell-ubuntu-20:~$ PS1='-Ready?---> '-Ready?---> We don't need to limit ourselves to the special sequences we've seen so far in this chapter - we can run any commands we like to build a command prompt. For example, we could use the use the ls (list directory contents) and wc (count lines and words) commands to count the number of files and folders in the current directory and show that in the prompt: PS1=\"$(ls -al | wc -l | tr -d '[:space:]') \\\\$ \" When I run this command, my prompt will look something like this: 32 $ We have used the $() notation to run a sub-shell that lists the contents of the current directory and then pipes them to wc -l, which counts the number of lines. Finally we pipe the result into tr -d '[:space:] to remove the whitespace around the line count. To use the $() notation, or any shell variable, we have to use double quotes in the string, otherwise the shell will write out those characters literally. And because we are using double quotes, we need an extra backslash before last \\$ character to escape it, so that the shell doesn't try to treat it as a variable. However - there's a subtle bug in this PS1 configuration! Let's see what happens when we change directories: 32 $ cd effective-shell/32 $ touch newfile-{1..10}32 $ In the session above I changed to the effective-shell directory. But the count is still showing as 32. This is suspicious. After creating ten new files with touch newfile-{1..10} the count still shows 32. The reason for this is that 32 was the number of files and folders in the current directory at the time the PS1 variable was set. We changed the PS1 variable once - what we really need to do is have the prompt count the files each time the prompt is shown. Fortunately, there is a special syntax for this! We just put a \\ backslash character in front of the $ dollar symbol for the sub-shell: PS1=\"\\$(ls -al | wc -l | tr -d '[:space:]') \\\\$ \" The backslash before the sub-shell tells the shell that it should evaluate the sub-shell each time the prompt is shown: 32 $ touch newfile-{1..10}42 $ This is where the real power of the PS1 variable comes into play. Because we set it using the shell itself, we can run any commands that we find useful and integrate their output into our command prompt. Let's see this in action by creating a script to make customising our prompt far easier and more intuitive!","s":"Adding Data to the Command Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#adding-data-to-the-command-prompt","p":705},{"i":720,"t":"We can write a script to make it much easier to customise our shell prompt. Rather than having to remember each of the colour sequences, we can store them in variables to make them easier to refer to. We can also run any commands that we'd like to run to allow us to show extra information. There is a script in the Effective Shell samples at ~/effective-shell/scripts/set_ps1.sh that we can use to set our PS1 variable in a much more user-friendly way. Downloading the Samples Run the following commands in your shell to download the samples: curl effective.sh | sh The set_ps1.sh script is quite long, so let's go through it bit-by-bit. # Keep track of the original PS1 value._original_ps1=\"${PS1}\"set_ps1() { # Foreground colours. local fg_black=$(tput setaf 0) # \\033[30m local fg_red=$(tput setaf 1) # \\033[31m local fg_green=$(tput setaf 2) # \\033[32m local fg_yellow=$(tput setaf 3) # \\033[33m local fg_blue=$(tput setaf 4) # \\033[34m local fg_magenta=$(tput setaf 5) # \\033[35m local fg_cyan=$(tput setaf 6) # \\033[36m local fg_white=$(tput setaf 7) # \\033[37m # Background colours. local bg_black=$(tput setab 0) # \\033[40m local bg_red=$(tput setab 1) # \\033[41m local bg_green=$(tput setab 2) # \\033[42m local bg_yellow=$(tput setab 3) # \\033[43m local bg_blue=$(tput setab 4) # \\033[44m local bg_magenta=$(tput setab 5) # \\033[45m local bg_cyan=$(tput setab 6) # \\033[46m local bg_white=$(tput setab 7) # \\033[47m First, we store the current value of PS1 in a variable named _original_ps1. This is so that later on if we have changed the PS1 variable, we can change it back to what it was set to originally. The _ underscore in the variable name is a convention that indicates that this variable is used internally in the script. Next, we define a function called set_ps1. Then we use the tput command (query terminfo database) to get the exact escape sequences for the foreground and background colours. For easy reference the escape sequences are shown to the right of each command as a comment. Next, we get the escape sequences for some of the other formatting options, such as 'bold' (which will be 'bright' on some terminals): # Text styles and reset. Note that on some terminals 'bold' will produce # light colours for bright colours, on others it will actually show the text # in bold. local bold=$(tput bold) # \\033[1m local dim=$(tput dim) # \\033[2m local start_underline=$(tput smul) # \\033[4m local stop_underline=$(tput mmul) # \\033[24m local reset=$(tput sgr0) # \\033[0m You might recall the tput command from the section 'colourising output' in Chapter 23 - Useful Patterns for Shell Scripts. After this we use a case statement to set the PS1 variable based on the value of the first parameter that was provided to the function: # Depending on the name of the theme provided, set the prompt. case $1 in debian) # Debian/Ubuntu style: # \\u@\\h - username@host (bold/green) # \\w - working directory (bold/blue) # \\$ - prompt (# if root, otherwise $) (bold/white) PS1=\"\\[${bold}${fg_green}\\]\\u@\\h:\\[${fg_blue}\\]\\w\\[${fg_white}\\]\\\\$\\[${reset}\\] \" ;; datetime) # A style that shows the date and time: # \\D{%Y-%m-%d} - the year/month/date (in white) # \\@ - the time (in green) # \\$ - prompt (# if root, otherwise $) (bold/white) PS1=\"\\[${fg_white}\\]\\D{%Y-%m-%d} \\[${bold}${fg_green}\\]\\@\\[${fg_white}\\] \\\\$\\[${reset}\\] \" ;; # Add your own themes here! *) # Restore PS1 to its original value. PS1=\"${_original_ps1}\" ;; esac # If we are in Z-Shell convert the PS1 to use Z-Shell format. [ -n \"$ZSH_VERSION\" ] && PS1=$(_to_zsh \"$PS1\")} In this code we check the first parameter of the function $1. If it matches the string debian we set the PS1 variable to a format that is similar to what is used by Debian Linux distributions. If it matches the string datetime we set PS1 to a prompt that shows the current date and time. If any other value is used, we reset the PS1 variable back to its original value. Before we complete the function, we check to see if ZSH_VERSION is set - this is to check whether we are in a zsh shell. If we are, then we use the _to_zsh function to convert the PS1 string into the format used by Z-Shell. Finally, we use the } to complete the definition of the function. Z-Shell The zsh shell differs considerably from Bash and Bash-like shells in how it handles the PS1 variable. There is no need for the \\[ or \\] sequences, there are built in color variables such as $fg[red] for 'red' and the special sequences are different (for example, rather than \\u for username, Z-Shell uses %n). The set_ps1 function in the samples converts the PS1 string to Z-Shell format if it is running in Z-Shell. However, this conversion is not perfect as some of the sequences shown in this chapter do not have an equivalent in Z-Shell. If you want to customise a Z-Shell prompt you can check the manual page man zshmisc and search for PROMPT\\ SEQUENCES. Notice how much easier it is to specify the values for the PS1 string when we have the colours and formatting defined in variables! We still need to wrap the formatting characters with \\[ and \\] to make sure that the shell knows how long the command prompt is, but this is far easier to read than the samples we saw before where we provide the ANSI Escape Sequences. To use this script, we can simply source it into our current session and then change the prompt by calling the set_ps1 function: $ dwmkerr@effective-shell-ubuntu-20:~$ source ~/effective-shell/scripts/set_ps1.shdwmkerr@effective-shell-ubuntu-20:~$ set_ps1 datetime2021-06-06 04:10 PM $ set_ps1 debiandwmkerr@effective-shell-ubuntu-20:~$ This script has a placeholder in the case statement for you to add your own 'themes' that you want to be able to use in your shell. For example, one 'theme' I often use is below: git) # A style that shows some git information. # Build a string that shows: # - The branch (underlined if 'main') in green # - A red exclamation if there are any local changes not committed # - An indicator of the number of stashed items, if any. _git_info() { # Git details. local git_branch_name=\"$(git branch --show-current)\" local git_any_local_changes=\"$(git status --porcelain=v1 2>/dev/null)\" local git_stash_count=\"$(git rev-list --walk-reflogs --count \\ refs/stash -- 2>/dev/null)\" # Ignore error when no stashes local git_info=\"\" if [ \"${git_branch_name}\" = \"main\" ]; then git_info=\"${bold}${fg_green}${start_underline}${git_branch_name}${reset}\" else git_info=\"${bold}${fg_green}${git_branch_name}${reset}\" fi if ! [ -z \"${git_any_local_changes}\" ]; then # Note that we have to be careful to put the exclamation mark # in single quotes so that it is not expanded to the last command! git_info=\"${git_info} ${bold}${fg_red}\"'!'\"${reset}\" fi if [ \"${git_stash_count:-0}\" -gt 0 ]; then git_info=\"${git_info} ${bold}${fg_yellow}${git_stash_count} in stash${reset}\" fi printf \"${git_info}\" } # Now show a Debian style prompt with the git info above it. PS1=\"\\$(_git_info)\\n\\\\[${bold}${fg_green}\\]\\u@\\h:\\[${fg_blue}\\]\\w\\[${fg_white}\\]\\\\$\\[${reset}\\] \";; Don't worry if you are not familiar with 'git', we will see it in a couple of chapters. The important thing is that this snippet shows that you can add almost any kind of information that you might find useful to your command prompt. When I run set_ps1 git, my prompt looks like this: feat/chapter-26-customise-your-command-prompt ! 3 in stashdwmkerr@effective-shell-ubuntu-20:~/repos/github/dwmkerr/effective-shell$ My prompt is now spread across two lines - the first shows me the branch I am on, a red exclamation point if I have made changes but not saved them, and the number of items I have in my 'stash'. The second line shows the standard Debian prompt. The code shown above has been slightly simplified to make it more readable, you can see the exact version in the samples. You can use the ~/effective-shell/scripts/set_ps1.sh file to build your own 'themes' and easily change between them in the shell. If you want to always source this file into your shell on startup, just add the following like to ~/.bashrc: source \"~/effective-shell/scripts/set_ps1.sh\" You could also set the default PS1 variable immediately after sourcing the script if you like: # Source the set_ps1 function and set our 'theme' to Debian.source \"~/effective-shell/scripts/set_ps1.sh\"set_ps1 \"debian\" In the next chapter we will look at some sensible ways we can organise files like the set_ps1.sh script and the ~/.bashrc file so that we can easily manage our customisations and share them across different machines.","s":"A Shell Script to Customise the Prompt","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#a-shell-script-to-customise-the-prompt","p":705},{"i":722,"t":"There are some other variables that you might want to use to configure your prompt: Variable Description PS2 This is shown when performing 'continuation' and is normally set to >. PS3 This is shown when the select command is used and is normally not set, so the default #? is used. PS4 This is shown when tracing with set -x and is normally set to +. PROMPT_DIRTRIM This can be set to limit the number of directories shown with using \\w or \\W in your prompt. PROMPT_COMMAND This can be set to limit the number of directories shown with using \\w or \\W in your prompt. Let's take a look at how each one can be used. PS2 If we have a long line of text in the shell, we can start a 'continuation' by entering the backlash symbol: $ echo \"This is a really really \\> long \\> long line of text\"This is a really really long long line of text The > symbol is shown when we press 'enter' after entering a \\ backslash symbol. This symbol is used to remind us that we are not entering a new command, we are just continuing the current command on a new line. You can change the text shown by setting PS2. PS3 PS3 allows you to specify the prompt used by the select command: $ PS3=\"Your choice? : \"$ select fruit in Apples Pears; do echo \"$fruit\"; done1) Apples2) PearsYour choice? : The PS3 variable is not set by default. If it is not set, then the select statement uses #? for the prompt. PS4 When you enable 'tracing' by setting the -x option, each traced line starts with a + symbol: $ set -x$ echo \"The date is $(date)\"++ date+ echo 'The date is Sun 06 Jun 2021 08:49:07 AM UTC'The date is Sun 06 Jun 2021 08:49:07 AM UTC You can change this symbol by setting the PS4 option. PROMPT_DIRTRIM If you set a value in the PROMPT_DIRTRIM variable, the shell will not show the entire contents of the working directory when you use the special \\w sequence in a prompt variable. Instead, it will limit the number of directories shown to the value in PROPMT_DIRTRIM and use an 'ellipses' for the rest (and ellipses is written as three dots). For example, if I was in the folder ~/effective-shell/logs/apm-logs and had PROMPT_DIRTRIM set to 2, then on Debian my command prompt would look like this: dwmkerr@effective-shell-ubuntu-20:~/.../logs/apm-logs$ Note that only the last two parts of the path to the folder are shown. PROMPT_COMMAND The PROMPT_COMMAND variable can be used to specify a command or set of commands to run before the prompt is shown. A common use for the PROMPT_COMMAND is to save and reload the shell command history before each command is run: PROMPT_COMMAND=\"history -a; history -c; history -r; $PROMPT_COMMAND\" In this example, we used the history (display or manipulate history list) command three times. First with -a to append the lines from the current session to the history file, then -c to clear the shell history in the session, then -r to reload it. For many shells the history of commands is only updated when the shell is closed, this change means that even if the shell is terminated unexpectedly, each command we have executed will still have been written to the history.","s":"Additional Prompt Configuration","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#additional-prompt-configuration","p":705},{"i":724,"t":"Z-Shell does not use the same sequences to format the prompt-string variables. However, the set_ps1.sh script included in the Effective Shell samples will convert the Bash-style PS1 variable into Z-Shell formatted prompt strings automatically. For Z-Shell users, you might also consider the very popular \"Oh-My-Zsh\" project. This is a collection of themes and plugins that add many more aliases, functions, autocompletions and more to the shell. One of the most popular features of \"Oh-My-Zsh\" is its large collection of themes that customise how the prompt looks. However, just like with most things in computing, I would strongly recommend that you learn how the fundamentals work as they are described in this chapter before using \"Oh-My-Zsh\" themes. This will help you understand how things like \"Oh-My-Zsh\" actually work under the hood. You might also realise that you don't need to install an additional package to get the styling you want. For example, my own shell prompt includes information on Git, the working directory (trimmed to only show up to three entries), but only requires a few lines of setup and works consistently in Bash-like shells and Z-Shell. Enjoy playing around with the prompt customisation! It can be a lot of fun and the options are almost limitless!","s":"Z-Shell and Oh-My-Zsh","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#z-shell-and-oh-my-zsh","p":705},{"i":726,"t":"In this chapter we looked at how you can customise the command prompt with the PS1 variable, the shell's special sequences for useful information like \\u for the current user, and how to configure the visual formatting of the prompt. We also looked at a script that makes configuring the command prompt a little easier to manage. We've now seen quite a few ways to configure the shell, in the next chapter we'll look at some sensible practices that you can use to organise your shell configuration files. To find all of the information on how to control the command prompt in the manual, run man bash and search for ^PROMPTING. For Z-Shell, run man zshmisc and search for PROMPT\\ SEQUENCES.","s":"Summary","u":"/part-5-building-your-toolkit/customising-your-command-prompt","h":"#summary","p":705},{"i":728,"t":"Part 6 - Advanced Techniques 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. 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!","s":"Part 6 - Advanced Techniques","u":"/part-6-advanced-techniques/","h":"","p":727},{"i":730,"t":"On this page","s":"Managing your Dotfiles","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"","p":729},{"i":732,"t":"Any file or folder on your system that starts with a . 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 ls, unless you provide flags such as -a (show all files and folders) flag. In desktop environments such as Gnome, KDE and MacOS X dotfiles are also hidden by default. 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. You will mostly see dotfiles in your 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 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. As an example, a user's personal Bash configuration is stored in ~/.bashrc, but the global Bash configuration applied to all users is stored in /etc/bash.bashrc. The second configuration file does not need a dot in front of it - the /etc folder is where configuration is kept so there is no need to differentiate it from other files like a user's personal files. Nowadays, when a user says \"my dotfiles\", they typically mean their 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 ~/.bashrc for your shell configuration, ~/.ssh/config for your SSH configuration and so on. 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.","s":"Dotfiles","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#dotfiles","p":729},{"i":734,"t":"On many platforms the default ~/.bashrc file will contain a number of customisations out-of-the-box. Let's take a look at the ~/.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 ~/.bashrc file the contents may be different as it will vary from distribution to distribution: # don't put duplicate lines or lines starting with space in the history.# See bash(1) for more optionsHISTCONTROL=ignoreboth# append to the history file, don't overwrite itshopt -s histappend# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)HISTSIZE=1000HISTFILESIZE=2000 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. # If set, the pattern \"**\" used in a pathname expansion context will# match all files and zero or more directories and subdirectories.#shopt -s globstar The shopt -s globstar command has been commented out, so that users can quickly remove the comment symbol to enable pathname expansion across subdirectories. # set a fancy prompt (non-color, unless we know we \"want\" color)case \"$TERM\" in xterm-color|*-256color) color_prompt=yes;;esac# uncomment for a colored prompt, if the terminal has the capability; turned# off by default to not distract the user: the focus in a terminal window# should be on the output of commands, not on the prompt#force_color_prompt=yesif [ -n \"$force_color_prompt\" ]; then if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then # We have color support; assume it's compliant with Ecma-48 # (ISO/IEC-6429). (Lack of such support is extremely rare, and such # a case would tend to support setf rather than setaf.) color_prompt=yes else color_prompt= fifiif [ \"$color_prompt\" = yes ]; then PS1='${debian_chroot:+($debian_chroot)}\\[\\033[01;32m\\]\\u@\\h\\[\\033[00m\\]:\\[\\033[01;34m\\]\\w\\[\\033[00m\\]\\$ 'else PS1='${debian_chroot:+($debian_chroot)}\\u@\\h:\\w\\$ 'fiunset color_prompt force_color_prompt This rather complex looking code determines whether the shell supports colour, and if so, updates the command prompt appropriately1. # enable color support of ls and also add handy aliasesif [ -x /usr/bin/dircolors ]; then test -r ~/.dircolors && eval \"$(dircolors -b ~/.dircolors)\" || eval \"$(dircolors -b)\" alias ls='ls --color=auto' #alias dir='dir --color=auto' #alias vdir='vdir --color=auto' alias grep='grep --color=auto' alias fgrep='fgrep --color=auto' alias egrep='egrep --color=auto'fi If the shell supports colour, then aliases are used so that common commands like ls will show their output in colour. # some more ls aliasesalias ll='ls -alF'alias la='ls -A'alias l='ls -CF'# Add an \"alert\" alias for long running commands. Use like so:# sleep 10; alertalias alert='notify-send --urgency=low -i \"$([ $? = 0 ] && echo terminal || echo error)\" \"$(history|tail -n1|sed -e '\\''s/^\\s*[0-9]\\+\\s*//;s/[;&|]\\s*alert$//'\\'')\"'# Alias definitions.# You may want to put all your additions into a separate file like# ~/.bash_aliases, instead of adding them here directly.# See /usr/share/doc/bash-doc/examples in the bash-doc package.if [ -f ~/.bash_aliases ]; then . ~/.bash_aliasesfi More aliases are added as shortcuts for useful commands. We also are sourcing the ~/.bash_aliases file if it exists. 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. 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.","s":"The Default Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#the-default-shell-dotfile","p":729},{"i":736,"t":"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 source it from the ~/.bashrc file. The other great thing about keeping your shell configuration in its own file is that you can then use it for 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. 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. 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! First, let's start by creating a sensible location for all of our per-user personal configuration files, a folder called ~/dotfiles: mkdir ~/dotfiles 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.","s":"Creating a Dotfiles Folder","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#creating-a-dotfiles-folder","p":729},{"i":738,"t":"Rather than changing the system-provided ~/.bashrc file to contain all of our customisations, let's create our own shell configuration file in the dotfiles folder: touch ~/dotfiles/shell.sh 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: I have not put a dot in front of the name! This is because within the ~/dotfiles folder I don't actually want this file to be hidden - if I am looking in the ~/dotfiles folder I want to see this file I have not used the name of a shell program in this file - this is because I will make this file work with any shell that I regularly use - so whether I am using zsh, bash or sh, this file should still be able to be loaded I have put .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 Now let's edit the ~/dotfiles/shell.sh file to add some configuration that might be useful for our shell: # Note: there is no shebang in this script. This script sets my preferred shell# configuration and should be able to be sourced from any Bash-like shell or# from Z shell.# If we are not running interactively do not continue loading this file.case $- in *i*) ;; *) return;;esac We'll start the file with a comment that clearly explains why this file does not have a 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 $- parameter) to see if the i (interactive) parameter is set. If the interactive parameter is not set then we call return to stop loading the script. 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). Next, let's set our preferred editor: # Set our editor. Some tools use 'VISUAL', some use 'EDITOR'.VISUAL=nanoEDITOR=nano There are two variables are used by the shell and command line programs to run an editor. The first, and original, variable was EDITOR. This was originally often a 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 VISUAL variable was used to specify the editor that could be used for 'full screen' terminal editing. Some programs use EDITOR and some use VISUAL so it is best to set both. I have used the nano editor in this example as it available on many distributions and is a little easier than vi or emacs, but you can use whatever you like. For my personal dotfiles I use vi. At this stage you can start to go a bit over the top - for example here's an alternative way to set the editor: # Set our preferred editor to the first available editor in the array below.preferred_editors=(nano vi)for editor in ${preferred_editors[@]}; do if command -v \"${editor}\" >/dev/null 2>&1; then # Note that 'VISUAL' can be a full screen terminal editor. On legacy # systems 'EDITOR' was normally a line mode editor but there is # generally no need to differentiate any more. VISUAL=\"$(command -v ${editor})\" EDITOR=\"${VISUAL}\" break fidoneunset editor preferred_editors In this method we specify an array of editors, go through each one, check to see if it exists2, 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! You'll notice that in the ~/effective-shell/dotfiles/shell.sh folder I 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. Another useful option to set is stty -ixon: # Allow us to use Ctrl+S to perform forward search, by disabling the start and# stop output control signals, which are not needed on modern systems.stty -ixon 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. Now let's set some sensible settings for working with folders: # Set a shell option but don't fail if it doesn't exist!safe_set() { shopt -s \"$1\" >/dev/null 2>&1 || true; }# Set some options to make working with folders a little easier. Note that we# send all output to '/dev/null' as startup files should not write to the# terminal and older shells might not have these options.safe_set autocd # Enter a folder name to 'cd' to it.safe_set cdspell # Fix minor spelling issues with 'cd'.safe_set dirspell # Fix minor spelling issues for commands.safe_set cdable_vars # Allow 'cd varname' to switch directory.# Uncomment the below if you want to be able to 'cd' into directories that are# not just relative to the current location. For example, if the below was# uncommented we could 'cd my_project' from anywhere if 'my_project' is in# the 'repos' folder.# CDPATH=\"~:~/repos\" If we run this script on an older shell, some of these options might not be present. This is why we have created a safe_set function that won't fail if the shopt function fails and will pipe any output to /dev/null. You can use these settings or remove them, it's really up to you. Each one is described below: Setting Description autocd Allows you to simply type a directory name or path and press enter to cd to it. cdspell When running commands like cd dirname, have the shell fix minor typos. dirspell When running commands like cat dirname/test, have the shell fix minor typos. cdable_vars If you create a var like repos=\"$HOME/repos, then cd repos to move into it. I have also left a comment on how you can use the CDPATH shell variable to allow you to 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. Finally, let's set some common shell history options: # Configure the history to make it large and support multi-line commands.safe_set histappend # Don't overwrite the history file, append.safe_set cmdhist # Multi-line commands are one entry only.PROMPT_COMMAND='history -a' # Before we prompt, save the history.HISTSIZE=10000 # A large number of commands per session.HISTFILESIZE=100000 # A huge number of commands in the file.# HISTCONTROL=\"ignorespace:ignoredup\" # Ignore starting with space or duplicates?# export HISTIGNORE=\"ls:history\" # Any commands we want to not record?# HISTTIMEFORMAT='%F %T ' # Do we want a timestamp for commands? These shell options and variables can be used to fine-tune how the history works. Here's a description of each one: Setting Description shopt -s histappend When we write to the history file, append entries, don't overwrite the old file. shopt -s cmdhist If we have a multi-line command, record it as one entry, not one per line. PROMPT_COMMAND Before we show the PS1 prompt, update the history file. HISTSIZE Store up to 10000 items in the history for the current session. HISTFILESIZE Store up to 100000 items in the history file for all sessions. HISTCONTROL Uncomment to ignore commands that start with a space, or ignore duplicated commands. HISTIGNORE Uncomment to not record certain commands in the history. HISTTIMEFORMAT Uncomment to keep a date and time next to each command in the history file. 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. Now let's look at how we could test this file, before we actually source it.","s":"Creating a Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#creating-a-shell-dotfile","p":729},{"i":740,"t":"Before we 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! From your shell, just run the command below: $ sh -iex ~/dotfiles/shell.sh+ case $- in+ EDITOR=vi+ VISUAL=vi+ safe_set autocd+ shopt -s autocd... What we have done is run a shell program, in this case the sh program, and passed the following flags: i - this makes the shell interactive - our script only runs in interactive shells so we need this to test it! e - this causes the shell to exit if a command fails x - this sets the tracing output By running this script in a shell this way we can see 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 bash or zsh. 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.","s":"Testing the Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#testing-the-shell-dotfile","p":729},{"i":742,"t":"Now that we have a working shell dotfile, we can source it as part of our shell startup. Rather than having our shell startup file know about our ~/dotfiles folder, we will create a symlink to the shell script from our home directory: Finally, we can create a symlink in our home directory that points to our ~/dotfiles/shell.sh file and we are good to go! $ ln -sf \"$HOME/dotfiles/shell.sh\" \"$HOME/.shell.sh\" Note that in this example we used the ln -sf command to create a symlink, the -s flag ensures we create a normal symlink (rather than a 'hard' link) and the -f flag forces the creation of the link by overwriting any link that already exists. Now all we need to do is add the following lines to our ~/.bashrc (or for Z-Shell, ~/.zshrc file): # Source our shell configuration if it exists.[ -r ~/.shell.sh ] && source ~/.shell.sh This command uses the -r (does file exist and is it readable) test to check whether we have a ~/.shell.sh file and if it exists, sources it. 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.","s":"Sourcing the Shell Dotfile","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#sourcing-the-shell-dotfile","p":729},{"i":744,"t":"A common pattern with Linux and Unix systems is to allow multiple configuration files to be stored in a folder. A convention is to have a folder with the letters .d at the end, to differentiate between a single configuration file and a configuration folder. 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. Here are some common examples: Configuration File Configuration Directory Notes /etc/crontab /etc/cron.d Configuration for scheduled tasks. /etc/bash_completion /etc/bash_completion.d Configuration for Bash auto-complete. /etc/sudoers /etc/sudoers.d Configuration for super-users. 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 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 PS1 variable in our shell configuration. First, let's make a directory to hold our shell configuration files: $ mkdir ~/dotfiles/shell.d Now let's copy over our ~/effective-shell/scripts/set_ps1.sh file: $ cp ~/effective-shell/scripts/set_ps1.sh ~/dotfiles/shell.d Now let's update our shell.sh file to source all of the files in the ~/.shell.d folder: # If we are not running interactively do not continue loading this file.case $- in *i*) ;; *) return;;esac# Source any files in our ~/.shell.d folder.if [ -x ~/.shell.d ]; then for shellfile in ~/.shell.d/*; do [ -r \"$shellfile\" ] && source \"$shellfile\" done unset shellfilefi 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 -x test), and then we loop through each file in the directory. If the file can be read (using the -r test) then we source it. At the end of the shell.sh file we can now call the set_ps1 function to set our theme: # Set the theme. Do not fail if the function doesn't exist.set_ps1 \"debian\" || true Finally, let's create a symlink in our home directory for the shell configuration files: $ ln -sf \"$HOME/dotfiles/shell.d\" \"$HOME/.shell.d\" At this stage we've now successfully created a dotfiles folder to store our configuration, symlinks in our $HOME directory that point to our dotfiles and we have also updated our ~/.bashrc or ~/.zshrc to load our shell configuration. If you want to see the new links you've created you can run the ls command just like so (I've abbreviated the output to make it more readable): $ ls -al ~ | grep shelllrwxr-xr-x dwmkerr .shell.d -> /home/dwmkerr/dotfiles/shell.dlrwxr-xr-x dwmkerr .shell.sh -> /home/dwmkerr/dotfiles/shell.sh","s":"Sourcing Files from a Folder","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#sourcing-files-from-a-folder","p":729},{"i":746,"t":"The manual steps we performed to setup the links for our dotfiles can be easily run using a shell script. The script below shows how we can easily setup the links to the dotfiles, and source the appropriate files from our shell configuration: #!/usr/bin/env sh# This script installs the dotfiles locally. Note that it should be run from the# dotfiles folder so that the links are set properly!# Create links for the shell configuration.ln -sf \"$PWD/shell.sh\" \"$HOME/.shell.sh\"ln -sf \"$PWD/shell.d\" \"$HOME/.shell.d\"# Source our shell configuration in any local shell config files.config_files=(~/.bashrc ~/.zshrc)for config_file in ${config_files[@]}; do # Skip config files that don't exist. [ -r \"${config_file}\" ] || continue # If we don't have the 'source ~/.shell.d' line in our config, add it. source_command=\"[ -r ~/.shell.sh ] && source ~/.shell.sh\" if ! grep -q \"${source_command}\" \"${config_file}\"; then echo \".shell.sh is not sourced in '${config_file}' adding this now...\" echo \"${source_command}\" >> \"${config_file}\" fidone This script creates the symlinks to our dotfiles and loops through a set of shell configuration files, adding a line to source the ~/.shell.sh in the configuration file if it doesn't exist. Note how we use the grep -q command to search through the shell configuration file to see if the line that sources our dotfile exists. The grep command returns 0 if it finds a result and 1 otherwise, meaning we can easily use it in an 'if' statement This script can be run from the dotfiles folder like so: $ cd ~/dotfiles$ ./install.sh.shell.sh is not sourced in '/home/dwmkerr/.bashrc' adding this now... And that is it - we now have a ~/dotfiles folder with our configuration, a sensible set of options for the shell, and the ability to quickly configure our dotfiles for different shells. The dotfiles that we have a created are available in the ~/effective-shell/dotfiles folder from the samples. The install script shown above is also in that folder.","s":"A Dotfile Install Script","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#a-dotfile-install-script","p":729},{"i":748,"t":"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. 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. If you are curious, the debian_chroot variable is set when you are running as a user that has run the chroot (change root) command. The 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. chroot is an advanced topic and out of the scope of this book, but the debian_chroot command in the PS1 variable is used to help make it clear when running a shell if you are in a 'changed root' environment.↩ For a reminder on how to check whether a command is available, see Checking for Installed Programs in Chapter 23 - Useful Patterns for Shell Scripts.↩","s":"Summary","u":"/part-5-building-your-toolkit/managing-your-dotfiles","h":"#summary","p":729},{"i":750,"t":"On this page","s":"A Vim Crash Course","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"","p":749},{"i":752,"t":"A Terminal Editor is any text editor that is designed to run inside a terminal, often from a shell. This means it is designed to be used without a mouse, on a smaller screen, and on a system that might have no graphical user interface or windowing or desktop environment. Some of the most popular terminal editors you might have heard of are GNU nano, Vim and Emacs. Let's take a look at why we might want to know how to use a terminal editor.","s":"What is a Terminal Editor","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#what-is-a-terminal-editor","p":749},{"i":754,"t":"Many technologists will be familiar with Graphical Text Editors, such as Visual Studio Code, Atom and Sublime. Software engineers will likely be familiar with Integrated Development Environments such as Visual Studio or the JetBrains family of IDEs which include dedicated environments for many languages, such as IntelliJ IDEA for Java. So, with such a selection of powerful and user-friendly text editing options available, why would you choose to use a Terminal Editor? There are many reasons, but perhaps the most compelling one is that you will not always have a graphical environment available! If you are working on a remote server using the ssh tool as described in Chapter 31 - The Secure Shell, you will not have a desktop or windowed environment to run a graphical tool. The only options available for you to edit text, code or scripts in such an environment are terminal editors - because the terminal is your only interface to the server. There are other times where you might not have easy access to a graphical environment - for example if you are editing a file in a Docker container. There are some other reasons that being able to use a terminal editor is powerful: Maintaining flow - if you are working in a shell already and need to edit text, being able to quickly do so without leaving the shell, and ideally without even having to touch the mouse, will allow you to maintain your 'flow' while you work. Speed of editing - editors such as Vim and Emacs allow you to be incredibly efficient at editing text, once you get over the initial learning curve. Vim in particular is designed to try and keep your fingers on the home row of the keyboard as much as possible to make it possible to manipulate text very quickly. Customisation - terminal editors can be customised to suit your working style - you can then manage your customisations through dotfiles that you share across environments as described in Chapter 26 - Managing Your Dotfiles. Improving Efficiency in Graphical Editors and other tools - if you learn how to use Vim or Emacs and find yourself able to edit text very efficiently using their idioms, then you can install plugins in your graphical editors or IDEs, allowing you to edit text using the same commands. There are even browser plugins that let you navigate webpages using Vim style movement commands. All in all I believe it is extremely useful to get to grips with at least the basics of Vim - you will be more efficient when working with remote machines, when quickly editing text from the shell, and might even find it replaces your other tools for many tasks.","s":"Why use a Terminal Editor?","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#why-use-a-terminal-editor","p":749},{"i":756,"t":"I have chosen Vim as it has the reputation for being the most complex of the terminal text editors to work with, but actually with an hour or two of practice you'll find that it is quite straightforward to use for simple tasks. With just a little knowledge you'll be effective - and as you invest more time in learning you'll become increasingly efficient. Vim is installed on most Linux distributions out of the box and is an extremely popular editor. But it has a style of editing, called Modal Editing that can be a bit confusing at first.","s":"Introducing Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#introducing-vim","p":749},{"i":758,"t":"Vim is a modal text editor. This means that it runs in different 'modes' - and in each mode the keyboard has different functions. For example, when you are in Command Mode the keyboard is used to enter sequences that manipulate text or move the cursor. In Insert Mode your keyboard is used just like with a normal text editor - typing edits the text on the screen.","s":"What is Modal Editing?","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#what-is-modal-editing","p":749},{"i":760,"t":"First, we'll open Vim by running the vi command: $ viβ–ˆ~~~~~ VIM - Vi IMproved~~ version 8.2.4314~ by Bram Moolenaar et al.~ Modified by ~ Vim is open source and freely distributable~~ Help poor children in Uganda!~ type :help iccf for information~~ type :q to exit~ type :help or for on-line help~ type :help version8 for version info~~~~~ 0,0-1 All When Vim is started it either displays the file that it has been asked to open or if no file has been provided it shows the welcome message above. Vim starts in Command Mode. This means that the keys on the keyboard are not used to enter text - they are used to enter commands. One command that is going to be useful is the 'insert' command, which tells vim to enter Insert Mode, before the cursor. The position of the cursor in the code block above is shown with a full-block β–ˆ symbol. Let's insert some text. Enter the characters in the first column of the table below - the output should match what is shown on the right: Keystrokes Output iEnter insert mode Hello Vim!Enter text Hello Vim! The i command tells Vim to enter Insert Mode. Insert mode is the mode that will be most familiar to you - when Vim is in insert mode your keystrokes enter text in the current file. When Vim is in insert mode it behaves more like a non-modal editor. We can enter some text as shown above. You might have noticed that when you entered insert mode Vim updated its status bar to show you what mode it is in: Hello Vim!β–ˆ~~~-- INSERT -- 1,11 All Vim will show you what mode you are in on the status line at the bottom of the screen. Vim's default mode is 'Command Mode' - when Vim is in command mode the mode is not shown - it's what you assume Vim is in unless it tells you otherwise. To return to Command Mode, press Control and 'C', or 'Escape'. In Vim's documentation a 'chord', which is more than one key pressed at the same time, is shown in angled brackets, to differentiate it from a 'sequence' which is a set of keystrokes pressed one after the other. Control and C is written as in the documentation for Vim. We will use the same style to describe chords and sequences. Let's exit insert mode: Keystrokes Output Command mode Hello Vim! You will notice that the -- INSERT -- text in the status bar is no longer shown - meaning we are back in Command Mode. Let's exit Vim. When we are in Command Mode, the keystrokes when enter are used to manipulate text or move around. If we want to perform an administrative task, such as saving a file or calling another program, we use what is called an Ex Command. Ex is short for 'execute'. When we press the colon : keystroke, we enter 'Ex Mode'. This mode is a bit like Insert Mode, but for writing commands: Keystrokes Output :Start ex command : q!Quit without saving :q! Keystrokes we enter after entering Ex mode will show in the status line, showing the text for the command we are building. At this point the status bar for Vim will look like this: Hello Vim!~ ~ ~ :q!β–ˆ Vim is showing us that our current command is q!. This command translates to 'quit without saving'. Press the Enter key to execute the command - Vim will now close. We have seen the basics of modal editing - showing Vim's Command Mode, Insert Mode and Ex Mode. We can also close Vim - which is famously a task that people who haven't used Vim before struggle with! Now let's start building our own Vim Cheatsheet - using Vim - to show more of what it can do!","s":"Modal Editing in Action","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#modal-editing-in-action","p":749},{"i":762,"t":"We're going to build a Vim Cheatsheet - this will be a great way to show what Vim can do at the same time as documenting our learnings as we go along. You'll then be able to extend your personal Cheatsheet over time with the commands that you find useful. Let's start by creating a folder to keep the cheat sheet in and initialing an empty Git Repository in the folder so that we can track changes to the cheat sheet. If you need a reminder on how Git works, check Chapter 27 - Controlling Changes with Git. First we'll create a new folder, move into it and then initialise an empty Git repository with the main branched named main: $ mkdir ~/vim-cheatsheet`$ cd ~/vim-cheatsheet$ git init -b mainInitialized empty Git repository in /home/dwmkerr/vim-cheatsheet/.git/","s":"Building a Vim Cheat Sheet","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#building-a-vim-cheat-sheet","p":749},{"i":764,"t":"We now have a new repository ready to hold our cheatsheet. Let's open Vim and tell it that we want to work on a file named cheatsheet.md: $ vi cheatsheet.mdβ–ˆ~ ~ \"cheatsheet.md\" [New] 0,0-1 All The text we are editing in Vim is called a buffer. A buffer can be thought of as a view on a file - when we open a file we load the context into a buffer - it is this buffer that Vim shows. When we want to create a new file, we enter text in a new buffer and then save it when we are ready. Vim is showing us in the status line that we have a buffer named cheatsheet.md and that this is a 'New' buffer - it has not been saved. The extension md at the end of the file is short for 'Markdown'. Markdown is a plain text format that is great for documentation. You write text as normal - you can also use some special characters such as Hash # to format things like headings, or dash - for bullets. Markdown will be rendered in a very reader-friendly way when we look at on things like GitHub. Let's enter insert mode, and create a title for our cheatsheet, then exit insert mode. Keystrokes Output iEnter insert mode # Vim CheatsheetEnter text # Vim Cheatsheet Enter command mode # Vim Cheatsheet Now let's use the w (write) ex-mode command to save the file. Enter :w - the Vim status line will to tell us it has written the buffer to disk: # Vim Cheatsheet~~~\"cheatsheet.md\" [New] 1L, 17B written 1,16 All Exit Vim by entering :q. Let's add this new cheatsheet file to our repository: $ git add cheatsheet.md Let's tell our shell to use Vim as our text editor. We're going to use Vim to work with Git for the duration of this chapter. When you are more familiar with Vim, being able to work with Git repositories and commands without leaving the shell and using Vim as the editor will start to feel extremely efficient: $ export EDITOR=vi$ git commitβ–ˆ# Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit.## Committer: dwmkerr## On branch main## Initial commit## Changes to be committed:# new file: cheatsheet.md#~~\"~/vim-cheatsheet/.git/COMMIT_EDITMSG\" 13L, 325B 1,0-1 All Git has opened Vim to ask us to provide a commit message. Git has also provided some helpful information on the changes we are committing - it's told us that we have a new file called cheatsheet.md in the commit. Let's use Vim to enter a commit message: Keystrokes Output iEnter insert mode add the cheatsheetEnter text add the cheatsheet Enter command mode add the cheatsheet Now type :wq to write and quit - Git will use the message we have provided for the commit: 1 file changed, 1 insertion(+) create mode 100644 cheatsheet.md We've used Vim to create the initial cheatsheet file as well as to quickly set the Git commit message - all without leaving our shell!","s":"Creating a File in Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#creating-a-file-in-vim","p":749},{"i":766,"t":"Vim commands that move the cursor are called 'Motions'. Understanding how motions work is essential to becoming effective at moving around Vim buffers. Let's open our cheatsheet in Vim: $ vi cheatsheet.md Now update the file so that it looks like the version below, this will give us some text to play with: # Vim Cheatsheet## Vim MotionsVim motions are commands used to move the cursor. Now that we have this text, let's see some of the motion commands in action. The table below shows what I think are the most essential Vim motions: Motion Usage Motion Usage gg Go to beginning of buffer. h Go left. G Go to end of buffer. j Go down. 0 Go to beginning of line. k Go up. $ Go to end of line. l Go right. w Go forwards one word. ) Go forwards one sentence. b Go backwards one word. ( Go backwards one sentence. Let's see a few of these motions in action. Make sure to use to exit Insert Mode and enter Command Mode before entering the keystrokes below: Keystrokes Output ggGo to beginning of buffer ## Vim Motions## Vim Motions lGo right ## Vim Motions## Vim Motions hGo left ## Vim Motions## Vim Motions jGo down ## Vim Motions ## Vim Motions kGo up ## Vim Motions## Vim Motions The first motion is gg - go to the beginning of the buffer. The next four motions are hjkl - move left, down, up and right. Vim uses these keys as they are all next to each other and on the home row for the right hand. Although it takes a little getting used to, once you use hjkl instead of arrow keys to move around you'll wonder how you lived without them - being able to navigate without moving your right hand from the home row lets you navigate text incredibly quickly. We can use the 0 and $ motions to go to the beginning and end of a line: Keystrokes Output 0Go to beginning of line ## Vim Motions $Go to end of line ## Vim Motions The w and b motions move backwards and forwards by one word: Keystrokes Output 0Go to beginning of line ## Vim Motions wGo forward one word ## Vim Motions bGo to back one word ## Vim Motions","s":"Vim Motions and Movement Commands","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-motions-and-movement-commands","p":749},{"i":768,"t":"Many Vim commands can be provided with a 'count', which indicates how many times the command should be run. This makes motions far more flexible - instead of pressing jjjjj to move down five lines, we can just press 5j. Let's see how the cursor moves when we add counts to motion commands: Keystrokes Output ggGo to beginning of buffer ## Vim Motions## Vim Motions 4jGo down four lines ## Vim Motions## Vim Motions 3wGo forwards three words ## Vim Motions## Vim Motions","s":"Vim Command Counts","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-command-counts","p":749},{"i":770,"t":"Now that we know how to move the cursor with the movement commands, we can move to where we want to insert text and use the i command to enter Insert Mode. However, Vim has a set of commands that enter Insert Mode in specific positions - and these can save us moving around unnecessarily! The most essential 'enter insert mode' commands are: Command Description i Insert at cursor. I Insert beginning of current line. a Append text after the cursor. A Append text at the end of the current line. o Open a new line below the position. O Open a new line above the current line. These commands make it much faster to quickly enter Insert Mode just where you like. Let's see some of them in action: Keystrokes Output ggGo to beginning of buffer ## Vim Motions oOpen line below current line # Vim Cheatsheet HelloEnter text, enter command mode # Vim CheatsheetHello IInsert at beginning of line # Vim CheatsheetHello Welcome and Enter text, enter command mode # Vim CheatsheetWelcome and Hello AAppend at end of line # Vim CheatsheetWelcome and Hello Vim!Enter text, enter command mode # Vim CheatsheetWelcome and Hello Vim! OOpen line above current line, enter command mode # Vim Cheatsheet Welcome and Hello Vim We use the chord to quickly go back into Command Mode when we have inserted text. This should start to become a habit as you use Vim - it is faster to enter small amounts of text, then go back to command mode and reposition the cursor, than to enter lots of text and use the arrow keys to move around in Insert Mode1. Now that we have seen motions and text insert commands, let's look at some essential Vim operators.","s":"Vim Text Insert Commands","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-text-insert-commands","p":749},{"i":772,"t":"A Vim operator is any command that can be applied to a range of text. We can combine counts, motions and operators to rapidly manipulate a specific part of the buffer. Some of the operators that are particularly useful are listed in the table below - we'll see them in action afterwards: Operator Description c Change the range, i.e. delete the characters and move into insert mode at the beginning of the range. d Delete the range. y Yank the range - copy it to a register ready to paste later. g~ Swap case. gu Make lowercase ('go lower'). gU Make uppercase ('go upper'). ! Send through external program. We can use operators with a motion to alter a range of text. Let's see some of the basic operators: Keystrokes Output ggGo to the beginning of the buffer Welcome to Vim! wMove forwards one word Welcome to Vim! c2wChange two words Welcome to to Terminal Editing!Enter new text, go to Command Mode Welcome to Terminal Editing! gU0Go uppercase to the beginning of the line WELCOME TO TERMINAL EDITING! Hopefully from these examples you are starting to get an idea of just how powerful modal editing is - you are able to express complex changes to text, ranges of text, and movements in the buffer with just a few keystrokes. These operators can also quickly be applied to the current line - just type them twice! Operator Description cc Change current line, i.e. delete the line and move into insert mode at the beginning of the line. dd Delete the current line. yy Yank the current line - copy it to a register ready to paste later. g~~ Swap case for the current line. guu Make lowercase ('go lower') for the current line. gUU Make uppercase ('go upper') for the current line.","s":"Vim Operators","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vim-operators","p":749},{"i":774,"t":"Some of the most powerful motions are the ones that can be used to search for a specific character. These 'search' motions can be used to quickly select a range of characters for an operator to perform on. Let's see a few of the most essential search motions: Motion Description f{character} Find the next specified character. F{character} Find the previous specified character. t{character} 'To' the next specified character. T{character} 'To' the previous specified character. Now let's see how these motions look in action, by quickly moving around a little bit of Python code: keystrokes output f(go to next '(' character def search_for_word(word): 2fdgo to second previous 'd' character def search_for_word(word): t_go to first '_' character def search_for_word(word): t go to previous space character def search_for_word(word): Just like any motion, we can add a {count} to indicate how many times we want the motion to run. Two other powerful motions are 'in' and 'a' - these motions are special because they come after the operator. Let's change some text using these motions: Keystrokes output f(Find first '(' character def search_for_word(word): gUi)Go uppercase in brackets def search_for_word(WORD): guuMake entire line lowercase def search_for_word(word): f(da(Find first bracket, delete a 'brackets' def search_for_word: When you have searched for a character, you can use the following keystrokes to find the next or previous result: Motion Description ; Move to the next result for the search motion. , Move to the previous result for the search motion.","s":"Search Motions and the 'In' and 'A' Operators","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#search-motions-and-the-in-and-a-operators","p":749},{"i":776,"t":"The 'in' and 'a' modifiers for an operator are particularly useful when you want to change the contents of something like brackets, parenthesis, braces and so on. If you use ca) for example, you are saying 'Change a Brackets' - the brackets are removed and you are put into insert mode at the position the brackets were in. In comparison, ci{ changes 'in' braces, removing the text inside the braces, but keeping the braces themselves. If you use an open bracket or brace, a space is added before and after the text you insert, if you use a close bracket or brace, no spaces are used: Keystrokes Output f(Go to first open bracket def search_for_word(word): ci)Change in brackets def search_for_word(): word = \"default\"Enter text, return to Command Mode def search_for_word(word = \"default\"): ca\"Change a quotes def search_for_word(word = ): 'sample'Enter text, return to Command Mode def search_for_word(word = 'sample'):","s":"The 'In' and 'A' Modifiers","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#the-in-and-a-modifiers","p":749},{"i":778,"t":"We're going to update our cheatsheet with a few notes - at this stage you can add any notes that you think are the most relevant, here are the first few lines of what I have added: # Vim Cheatsheet## Vim MotionsVim motions are commands used to move the cursor. Essential motions are:* `hjkl` - move the cursor left/down/up/right* `w` - forward one word* `b` - back one word* `(` - back one sentence* `)` - forwards one sentence* `0` - beginning of line* `$` - end of line* `gg` - beginning of buffer* `G` - end of bufferMotions can take a `{count}` - this indicates how many motions we want to make, such as `3w` to move forwards three words. Before we continue we should note that there are three new Markdown syntax features we have used: The ## characters, which indicate a sub-heading The * character, which indicates an item in a bullet list (a dash - can also be used for this) The backtick ` character, which can surround text to indicate that it is code and should be rendered as such These simple formatting features will make the cheatsheet look excellent when it is viewed on GitHub or in a suitable editor - here's how it looks on GitHub at the moment: Let's look at another way we can use Vim in our day to day work. We'll enter a shell command that has a mistake, and quickly edit the command and fix the mistake in Vim. Let's add these changes to Git - and show one more way to use Vim. Enter the line of text below in the shell but don't press 'Enter' to execute it yet! $ git commit -m 'added more detail on motions' This command will commit the changes - but we haven't added any yet! Rather than fixing this by moving around the command line, let's just open the current shell command line in Vim. Press ^x^e (Control X, Control E) - this is the shell command to open the current line in the editor: git commit -m 'added more detail on motions'~~~\"/tmp/bash-fc-5094983766\" 1L, 45B 1,1 All Let's fix up the command with our new Vim skills: Keystrokes Output iEnter insert mode git commit -m 'added more detail on motions' git add . && Add text, enter command mode git add . && git commit -m 'added more detail on motions' :wqWrite and quit (Vim closes and git commit completes) Being able to rapidly edit the current shell command in Vim is a huge time saver.","s":"Editing Commands in Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#editing-commands-in-vim","p":749},{"i":780,"t":"Vim is a huge topic - we've barely scratched the surface but I hope have focused on the features that you can use most immediately. As you use Vim more, I would recommend learning about the following features in order: The dot command Visual mode Yank and paste and registers Search and replace Customising Vim with the ~/.vimrc file Macros There are a few resources that I would recommend. The first is vimtutor and the next are some excellent online resources and books.","s":"Next Steps with Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#next-steps-with-vim","p":749},{"i":782,"t":"Vim comes installed with a very useful vimtutor program. You can run this program to open Vim with a special file that describes the key functionality of Vim. Use this program regularly while you are learning Vim to see how familiar you are getting and refresh your skills!","s":"Vimtutor","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vimtutor","p":749},{"i":784,"t":"The excellent website Vimcasts by Drew Neil has some superb videos on how to use Vim - from the basics to some really advanced functionality. Drew Neil is an extremely skilled Vim user and does a superb job of making a complex subject easy to follow.","s":"Vimcasts","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#vimcasts","p":749},{"i":786,"t":"If you are enjoying using Vim, Drew Neil's books \"Practical Vim\" and \"Modern\" Vim should be on your bookshelf! They are truly excellent and again, just like his Vimcasts, make a complex subject much more user friendly.","s":"Practical Vim & Modern Vim","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#practical-vim--modern-vim","p":749},{"i":788,"t":"Something I found useful when learning Vim was to have a wallpaper with the most common shortcuts. There are a number available on the web if you search for \"Vim Wallpaper\" - the one I currently use is below: Unfortunately I have not been able to find the name of the original author. I found this file at: https://wallha.com/wallpaper/vim-cheat-sheet-minimalism-linux-1250034","s":"The Vim Shortcuts Wallpaper","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#the-vim-shortcuts-wallpaper","p":749},{"i":790,"t":"In this chapter we introduced the concept of the terminal editor. We looked at the essentials of Vim, Insert Mode, Command Mode, motions, operators and more. We also introduced some resources that will help with your learning journey. In the next chapter we'll look at another essential tool - the Terminal Multiplexer. There is also another benefit to short commands - it makes them easier to repeat with the Dot Command which is not covered in this chapter but something that you will learn about as you use Vim more.↩","s":"Summary","u":"/part-6-advanced-techniques/a-vim-crash-course/","h":"#summary","p":749},{"i":792,"t":"On this page","s":"How to Avoid Scripting - A Dictionary Lookup in Python","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"","p":791},{"i":794,"t":"Shell scripts can be very powerful. As a quick and dirty way to solve a simple problem, they often cannot be beat. When you combine bring in powerful tools that are built in on most systems such as awk (which can perform advanced text manipulation) they can be even more powerful. But there are reasons you might want to avoid using a shell script: If the problem you are solving is quite complex, the script will be large and hard to manage Shell scripts become hard for others to reason about when they become complex Shell scripts are limited to the features of the shell and the tools on your system - but not every system has the same tools, so complex scripts may not be portable Shell scripts are sometimes the only sensible tool to use, for example if we wanted to write a script that showed the shell's options and let the user toggle them on and off, a script would be ideal. This is because another technology, such as a Python script, would not know what shell you are using. This would make the Python script needlessly complex. But in general, as soon as a script gets to more than about a page of code, I tend to think that this a good point at which to consider using an alternative tool.","s":"When should you avoid shell scripting?","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#when-should-you-avoid-shell-scripting","p":791},{"i":796,"t":"There are hundreds of programming languages that exist to help you solve technical problems. But not all of them are ideal as an alternative to a shell script. Some of the questions you might ask are: Is the programming language going to be available on almost any machine? Simple shell scripts run almost anywhere without having to install other tools - will the language give me this functionality? Is the language designed for handling the kind of problem I want to solve? Does it support console based input and output? Is it easy to write shell-style tools in this language? Is the language simple and popular? Can others understand or adapt the script without too much intervention? Some languages jump to mind as good options for shell scripts: Python - it is installed by default on almost every system, highly popular, simple to use and read and works well to write input-processing-output programs. Ruby - again, installed on many systems by default. This is a simple language and also highly popular, but perhaps less well-known than Python. C - most platforms have a C compiler installed, and C is great for working with low-level system libraries. But it requires compilation, may behave quite differently on different systems, and is fairly complex for others to use. NodeJS - Node.js uses Javascript as its language, which is highly popular. It is event-driven, meaning it can be very fast. But the version installed across systems varies considerably, and this can cause headaches when sharing scripts. Perl - installed almost universally on any system, very powerful, but possibly less well known nowadays and therefore perhaps less likely to be understood by others. Now when you are writing complex tools or programs, the criteria will change, you want to use a language and platform that really suits the problem you are solving, or is used already by the team you are working with. But in this chapter we're looking at alternatives to shell scripts to write tool that work well when used in the shell. Given it's almost universal presence on systems, its huge (and increasing) popularity, and robust standard library (which allows you to use many features without having to have users download additional packages), Python is an excellent choice for writing shell friendly tools.","s":"What are the alternatives?","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#what-are-the-alternatives","p":791},{"i":798,"t":"When we are writing a tool that is aimed at shell users, it makes sense to follow the conventions set by other shell tools. This means that users will be able to use the tool in a familiar way, and complement it by combining it with existing tools on their system. So what makes a shell-friendly tool? Being able to read from standard input - this allows us to pipe inputs from other tools into our programs (see Thinking in Pipelines for more on this), we also want to read and process line-by-line, in case the input is very large Being able to write to standard output - this sounds obvious, but it means making sure that our output can be read by a human operator, but also ideally be processed by other tools such as cut, sed, rev and so on, it also means thinking about how colour will or will not be used in output, and avoiding superfluous output that might make it harder to process the output (such as titles, version numbers and so on) Being able to specify options using sensibly defined flags - there are many common conventions for how flags or parameters work in tools, using these patterns (rather than inventing our own) will make our tool easier to use. For example, having an -h flag to show help is a very common convention1 Being able to run on different systems - shell users are used to being able to use tools like grep, sed and so on in a similar way across platforms, a well-written tool will do the same Handling errors using shell idioms - shell-friendly tools use 0 as a status code to indicate success, and define error codes in their documentation, so that people using the tools know how to handle exceptional circumstances There are many other conventions and ideas that could be added, but these are some of the fundamentals.","s":"What makes a shell-friendly tool?","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#what-makes-a-shell-friendly-tool","p":791},{"i":800,"t":"As an example of how to write a shell-friendly tool, we're going to create a simple program that takes a list of words and provides a definition loaded from the free and fantastic Free Dictionary API. This is a good example of a tool that would be overly complex to write with a shell script. We need to handle input, parse and process it, make HTTP requests to download pages from the internet, parse and process those requests, format the output, and provide some options for the user on how the output looks. A highly experienced shell programmer would likely be able to create this tool with a shell script without breaking a sweat, but it would likely be quite hard for a less experienced user to follow. Python is easy to write and read, has a wealth of online learning resources, and is available on almost any platform. Our requirements for the tool will be quite simple: Allow the user to provide a set of words to be looked up as a text file or from standard input Download the definition of the words Write the words to standard output, with the option to format how this output looks Offer help to the user on how to use the tool We'll also limit ourselves to 'raw' Python using only the standard library - meaning users will not have to install any packages to make this tool work. Help! I don't know Python! That's OK! All of the chapters in the section 'Advanced Techniques' will likely stretch you and require some additional learning and experimentation. Don't worry if you don't know Python - I'll explain as much as I can as I go through the chapter. You should be able to take away the key lessons to be learnt without a particular familiarity with the language. Each of the files you'll see in this section are in the ~/effective-shell/programs/lookup folder. Downloading the Samples Run the following command in your shell to download the samples: curl effective.sh | sh OK - let's get started.","s":"Writing a Dictionary Lookup Tool in Python","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#writing-a-dictionary-lookup-tool-in-python","p":791},{"i":802,"t":"First, let's check if Python is installed. There are two versions of Python that are commonly used. Python 3 is the current latest version and should be preferred. This is what we will use. Python 2 is still used by many people, and a lot of existing code is written in Python 2, but where possible Python 2 code should be upgraded to Python 3. Python 2 official went out of support in January 2020. Check Python 3 is installed by running: % python3 --versionPython 3.9.10 In general I would recommend that you explicitly make it clear you are using Python 3 by using the python3 tool. On many systems the python tool points to python3, but it is safer to by explicit and use python3. If Python is not installed If you see a message such as command not found: python3, then you will need to install Python. Shell tools take input, process it and produce output. So let's take our structure and create a first cut. This first cut will not perform any processing - it'll just take the input and produce simple output. But it will give us a working starting point to incrementally add more features to. At this stage I'll share the code in the form of snippets - you can see the code as it evolves by looking in the ~/effective-shell/programs/lookup/ folder. lookup-v1.py import sys# Read standard input until there is nothing left to read.while True: # Read a line of input. word = sys.stdin.readline() # If the user hits 'Ctrl+D' to end transmission, readline returns an # empty string and we can stop reading. if not word: break # If the input is an empty line or whitespace, skip it. if word.isspace(): continue # Add the word to our list of lookups, and strip any whitespace from the # beginning and end of it. For now, we don't have a definition. word = word.strip() definition = '' # Write the result. print(\"{} - {}\".format(word, definition)) Let's test this out and then we'll dissect the code. First, we'll just run the program, type some words, then press Ctrl D to signal end-of-transmission (check Thinking In Pipelines if you need a reminder on what 'end-of-transmission' means). You can also press Ctrl C to close the program. $ python3 ~/effective-shell/programs/lookup/lookup-v1.pyoneone -twotwo -threethree - The program successfully reads our input, and writes out a result for each word. We can also test that the program can receive input piped from a file: $ cat ~/effective-shell/data/words.txt | python3 ~/effective-shell/programs/lookup/lookup-v1.pylouche -liana -lieder -Manchu -Nankeen -naevi -Ness - So we have a program that can read from standard input, either interactively or from a file. Let's break down the code section by section. First we create a loop that will run continuously, reading lines from standard input: lookups = []while True: # Read a line of input. word = sys.stdin.readline() If the input is completely empty, then that means we've reached the end of the file or the user has signaled 'end-of-transmission' by pressing Ctrl+D: if not word: break if word.isspace(): continue If the input is just whitespace, such as a newline or tab, we skip it. Now we record the value of the word with the whitespace that might surround it stripped, note that has not been found and set the definition of the word to an empty string: word = word.strip() definition = '' Now we write out the word and its definition: print(\"{} - {}\".format(word, definition)) Now let's look at actually downloading the definition.","s":"Version 1 - Basic Structure","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#version-1---basic-structure","p":791},{"i":804,"t":"Now that we've got the list of words, we can try and download a definition of each one by using the excellent https://dictionaryapi.dev/ website. This site searches a number of online dictionaries, including Wiktionary. We will add a new function to the script. You can see the complete script in the file ~/effective-shell/programs/lookup/lookup-v2.py. The new function downloads the definition of a word from the dictionaryapi.dev site: lookup-v2.py def search_for_word(word): # Encode the word for HTML. encoded_word = urllib.parse.quote(word.encode('utf8')) # Try and download the definition using the amazing dictionaryapi.dev site. try: url = \"https://api.dictionaryapi.dev/api/v2/entries/en/{}\".format(encoded_word) response = urllib.request.urlopen(url) if response.status == 404: print(\"NOT FOUND\") sys.exit(1) with urllib.request.urlopen(url) as response: raw_json_data = response.read().decode('utf-8') # If the word is not found, return an empty definition. except urllib.error.HTTPError as http_error: if http_error.code == 404: return '' raise # Now try and parse the data. data = json.loads(raw_json_data) first_definition = data[0]['meanings'][0]['definitions'][0]['definition'] # Return the result. return first_definition I'm not going to go through this blow-by-blow, it's a fairly rough and ready way to try and get the definition of a word from an online resource. In a nutshell it does the following: Make sure that have the right address to search for the word Search for the word and download the result If the word is not found, return an empty result If the word is found, try and decode the definition and return it With this new function, we can update the main loop of our program to look like this: lookup-v2.py # Strip whitespace from the word and find the definition. word = word.strip() stripped_word = word.strip() definition = search_for_word(stripped_word) # Write the result. print(\"{} - {}\".format(word, definition)) If we pass some test words into the program our output looks like this: $ cat ~/effective-shell/data/words.txt | python3 ~/effective-shell/programs/lookup/lookup-v2.pylouche - To make (an alcoholic beverage, e.g. absinthe or ouzo) cloudy by mixing it with water, due to the presence of anethole. This is known as the ouzo effect.liana - A climbing woody vine, usually tropical.lieder - An art song, sung in German and accompanied on the piano.Manchu -Nankeen - A type of cotton cloth originally from Nanking in China.naevi - A pigmented, raised or otherwise abnormal area on the skin. Naevi may be congenital or acquired, and are always benign.Ness - A promontory; a cape or headland. (Frequently used as a suffix in placenames.) Pretty cool - our program can find a reasonable definition for most of the words in the test data set we have. Now let's look at cleaning up the output.","s":"Version 2 - Downloading the Definition","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#version-2---downloading-the-definition","p":791},{"i":806,"t":"Our program is working quite well, but we can improve on it by making the output a little friendlier to read. We can show the word in a different colour to the definition, separate the definition with a colon which will make it easier for us to process it with other tools, or even limit the length of the definition so that it fits on the screen. We're also going to let the user provide a 'crop' value if they want to. This is a number that limits the length of the output each line, which could be useful if the user wants to fit the definitions on the screen without them spilling over to the next line. There is a special module in Python called argparse that helps you parse the arguments for a program, we'll use this to specify and parse the 'crop' argument. You can see the complete script in the file ~/effective-shell/programs/lookup/lookup-v3.py. import argparseparser = argparse.ArgumentParser()parser.add_argument( '-c', '--crop', help='crop the output line length', type=int, nargs='?', const=80, # Default value if -c is supplied default=None) # Default value if -c is not suppliedargs = parser.parse_args() The argparse module is very sophisticated, you can read more about it online if you'd like to discover more. But for now, it's enough to know that this code defines an optional argument named crop, that can be provided with a number, or without a number. We'll see it in action shortly. Next we'll add a function that writes a word and its definition in a nicer way: def write_definition(word, definition): # Check if stdout is a terminal - if it is we'll colour the output. is_terminal = sys.stdout.isatty() # We will separate the word and the definition with a colon and space. separator = \": \" # If the 'crop' argument is set, use it. if args.crop: output_length = len(word) + len(separator) + len(definition) if output_length > args.crop: # We need to chop some letters off the end of the definition, but # leave space for '...' to indicate the output is cropped. new_length = len(definition) - 3 - (output_length - args.crop) definition = definition[:new_length] + '...' # If we are in a terminal make the word green and the separator white. if is_terminal: word = \"\\033[92m\" + word + \"\\033[0m\" separator = \"\\033[37m\" + separator + \"\\033[0m\" # Write out the word, separator and definition. print(word + separator + definition) This code first checks to see whether stdout is a terminal. This is useful because if we are in a terminal, we can show colour codes, but if the output is something like a file, we can skip the colour codes (which would look messy in the resulting file). Then we do some arithmetic if the crop argument is provided, shortening the definition if needed. The weird looking characters such as /033[92m are ANSI control codes to set the colour of the output - you can read all about them in Useful Patterns for Shell Scripts in the section 'Colouring Output'. With this function added, and called in the right place in our program, we can now lookup definitions, have the output printed in colour, and specify a 'crop' value: $ cat ./effective-shell/data/words.txt | python3 ./effective-shell/programs/lookup/lookup-v3.py -c 60louche: To make (an alcoholic beverage, e.g. absinthe or ...liana: A climbing woody vine, usually tropical.lieder: An art song, sung in German and accompanied on th...Manchu:Nankeen: A type of cotton cloth originally from Nanking i...naevi: A pigmented, raised or otherwise abnormal area on ...Ness: A promontory; a cape or headland. (Frequently used ... The nice thing about using the argparse module is that our program automatically gets a --help or -h option that can be used to provide instructions: $ python3 ./samples/programs/lookup/lookup-v3.py -husage: lookup-v3.py [-h] [-c [CROP]]optional arguments: -h, --help show this help message and exit -c [CROP], --crop [CROP] crop the output line length We've really just scratched the surface of what can be done here. You can find this version of the program in ~/effective-shell/programs/lookup/lookup-v3.py","s":"Version 3 - Formatting the Output","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#version-3---formatting-the-output","p":791},{"i":808,"t":"The great thing about a Python script like the one we have built is that it is standalone, and anyone can install it as tool on their system with very little effort. All we need to do is first tell the shell that if it encounters this script and is asked to execute it, it needs to use the python3 program. We can do this by putting a shebang at the top of the file: lookup.py #!/usr/bin/env python3# ...the rest of the code goes here, it's been omitted for brevity! This shebang uses the env program to locate the python3 program. This is important as python3 might be installed in different locations on different systems. You can read more about how to use env in shebangs in the chapter Shell Script Essentials under 'Using Shebangs'. Now that we have a shebang, we can make the file executable using the chmod program and link to it from our personal bin folder: chmod +x ~/effective-shell/programs/lookup/lookup.pyln -s ~/effective-shell/programs/lookup/lookup.py /usr/local/bin/lookup If you need a reminder how to use the chmod tool and ln tool to install scripts, check the chapter Shell Script Essentials under the section 'Installing Your Script'. Now that we have the tool in our local binaries folder, we can call it like so: $ lookup -c -- effective shelleffective: A soldier fit for dutyshell: A hard external covering of an animal. Note that the lookup.py script, which is the final version of the script, has some additional features which are described at the end of the chapter. One of these features is that we can just provide a word or list of words as positional arguments to the command. Note that the -- in the command shown above is the 'end of options marker' - this is the standard Linux pattern to indicate that the list of flags is complete, and that what follows is the list of positional parameters. If we didn't have this, the tool would think that we are providing effective as the value of the -c flag. The -- removes this ambiguity. Many Linux tools support this separator, you can check man bash to find out more.","s":"Installing the Lookup Tool","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#installing-the-lookup-tool","p":791},{"i":810,"t":"One of the fun things about coding is thinking about all of the exciting additional features you can add! The final version of the script, which is in the ~/effective-shell/programs/lookup/lookup.py folder has a set of additional features that you might find useful to explore when building your own programs. These features are: Feature Description More robust error handling There are exception handlers in the key places the program may fail. Graceful handling of Ctrl+C Ensure we close cleanly on Ctrl+C without a noisy error message. See KeyboardInterrupt in the code for this. More detailed help The help text has examples, see argparse in the code. There are all sorts of other features you could add as a coding and learning exercise! Here are a few that I considered: A 'browse' flag - this could open the user's browser to the full definition online Manpages - an option to install a manpage for the tool, meaning that we can run man lookup Clearer interactive mode - when stdin is a terminal, meaning the user is interactive, show a prompt and instructions A verbose flag - a --verbose flag to show detailed error messages if they are encountered If you find yourself writing more complex command-line tools in Python, you might also explore the excellent Click Python package. This is a very popular package among Python developers and is used by a number of large and well-established projects. The Typer package is also worth exploring. The urllib package I have used works, but it can be quite unwieldy when dealing with more complex options - many developers will prefer alternative packages.","s":"Improving the Lookup Program","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#improving-the-lookup-program","p":791},{"i":812,"t":"In this chapter we looked at alternatives to shell scripts and when we might consider them. We looked at what makes a tool 'shell-friendly'. We also looked at how we can use the highly popular Python language to write a simple but useful shell-friendly tool. There is a detailed description of how options should be specified for GNU tools at http://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table↩","s":"Summary","u":"/part-6-advanced-techniques/how-to-avoid-scripting/","h":"#summary","p":791},{"i":814,"t":"On this page","s":"Master the Multiplexer","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"","p":813},{"i":816,"t":"A terminal multiplexer is a program that that allows a user, or many users, to run many programs at once. The multiplexer manages 'sessions' - these sessions run the programs, independently of your shell or terminal. Users can switch between sessions, save them, resume them, or even invite others to join the same session to allow collaboration. Multiplexers also normally offer 'window management' capabilities, allowing you to break your terminal into separate panes, tabs or windows. The visual below shows a multiplexer in action - the one I am using while I write this chapter: In this image I am running the Tmux multiplexer in my terminal. I have split the window into three panes - a large one on the left that contains the text for the chapter, and two smaller ones on the right. The upper right pane builds the effective shell website locally while I write, to check for errors, and the lower right contains a script I was working on. When I want to switch between the tasks I'm working on, I don't need to start or stop any of the program, I can just switch between the 'panes' in my multiplexer. At the bottom of the screen there are also two tabs - one that contains the website and the windows I'm currently working in, and another that has some windows for the manuscript for the book. If I close my shell, the Tmux server is still running the sessions and I can re-open my terminal and resume. If I was to close my computer, when I restart my multiplexer will restart these programs for me, allowing me to pick up where I left off.","s":"What is a Terminal Multiplexer?","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#what-is-a-terminal-multiplexer","p":813},{"i":818,"t":"It is not essential to know how to use a multiplexer. But knowing how to use one can certainly help you become a more effective shell user. Some of the key benefits to using a multiplexer are: Window Management Much like modern 'tabbed interfaces', multiplexers can support multiple windows, tabs and panes within a window. This means you can arrange your workspace exactly how you want it. You can save the configuration of your windows and use it later. You can organise different windows into different sessions, allowing you to have many projects running at once, that you can quickly switch between, each one with a layout configured to suit your needs. Persistence of Sessions Your programs are run independently from your terminal. If your terminal crashes, or freezes, your programs still run. If you are working with a remote machine, then you can run a multiplexer on it to manage your programs. This means that if your connection to the machine is reset, the programs will not stop running. You can re-attach to the session at later point and pick up where you left off. Session management is incredibly useful - you'll wonder how you lived without it if you use the shell a lot. Configuration Multiplexers offer many configuration options to allow you to customise how programs are run and interfaced with, allowing you to set up the ideal environment for you to be effective. Collaboration A multiplexer is a 'client-server' program. This means you as a user are a client and connect to a server that runs the multiplexer. But other users can connect as well - this means you can easily collaborate with other users, sharing your own work, connecting to theirs, or both working on a shared remote machine. We'll look at a few of the most immediately useful features of multiplexers in this chapter - this will only really be scratching the surface but should be enough to help you decide whether it is a topic you'll investigate further on your own and add to your toolkit.","s":"Why use a Multiplexer?","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#why-use-a-multiplexer","p":813},{"i":820,"t":"The two most popular multiplexers are probably GNU screen and Tmux. GNU screen was created in the late 80s and has been used widely ever since. It is pre-installed on many Linux systems. Tmux is a more modern multiplexer, created in 2007. It has most of the features of GNU screen, is also open source, and adds some very useful features to make it a little more user friendly. For the rest of this chapter we are going to look at Tmux in detail. I've chosen Tmux rather than screen because I think its user-friendliness makes it a little better for people who are new to multiplexers. Once you are familiar with Tmux you might decide to switch to GNU screen - if so it will seem very familiar.","s":"Introducing Screen and Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#introducing-screen-and-tmux","p":813},{"i":822,"t":"Tmux may already be installed on your system. You can check by trying the tmux -V command, which shows the current version: $ tmux -Vtmux 3.2a If it is not installed, or you do not have version 3 or higher, you should install the latest version of Tmux. Install Tmux with the package manager for your system. The package name on most distributions is just tmux. As an example, on a Debian-based distribution we would install Tmux with: apt install -y tmux","s":"Installing Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#installing-tmux","p":813},{"i":824,"t":"Now that Tmux is installed let's see what we can do with it.","s":"Tmux Techniques","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#tmux-techniques","p":813},{"i":826,"t":"One thing you'll notice when reading about Tmux and GNU screen is the Leader. The leader is a key combination that prefixes any commands for the multiplexer. We just this leader combination so that the multiplexer key bindings don't clash with any existing ones. Typically you will use the leader keys to tell the multiplexer you are about to enter a command, then strike the keys that correspond to the command. For Tmux, the default leader is Ctrl + B. For GNU screen it is Ctrl + A. You can easily remember these by remembering that Tmux is a successor to GNU screen, so the leader key letter is one letter further in the alphabet!","s":"The Leader","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#the-leader","p":813},{"i":828,"t":"Run the tmux program by entering the command below: tmux We are now going to create some 'splits' - these will split our current window into separate panes. Each pane can run its own program. Let's take a look at a few commands for managing splits and panes. Creating Splits and Moving Between Splits​ We can create a vertical split by pressing Ctrl+B followed by the % percent symbol. We can create a horizontal split by pressing Ctrl+B followed by the \" quotes symbol. Finally, to move between splits, press Ctrl+B followed by an arrow key. The short video demonstrates both types of split and moving between panes: Zooming Panes​ You can 'zoom' a pane so that it takes up the entire window by moving to the pane you want to zoom and pressing Ctrl+b z. To 'unzoom' a pane, you can just move to another pane (even if it is hidden) or simply press Ctrl+B z again. This what it looks like when we zoom a pane: Creating Windows and Moving Between Windows​ If you want to run a program in an entirely new window, use the Ctrl+b c command to create a window. To switch windows, press Ctrl+b w to choose from the list of available windows: Quickly Navigating Between Windows​ The ^B w command shows all of the windows (and sessions!) for Tmux and let's you rapidly switch between them, this is extremely useful if you have a lot of windows! Here's how it looks in action - in this demo I have a few windows - notice that I've given them more clear names with ^B ,, this also makes it easier to sort and manage your windows. Quick Reference​ There are some other useful commands when working with windows: Command Description ^b n Move to the next window ^b p Move to the previous window ^b 0 Select the window numbered '0' - use the number of any window from the status pane ^b & Close the current window ^b , Rename the current window ^b w Show the window navigator The 'rename window' is very useful if you have a lot of windows and want to give each one a descriptive name. Remember, Tmux is stateful so will remember these settings even if you detached and later re-attach (we'll see this in the next section).","s":"Window Management","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#window-management","p":813},{"i":830,"t":"Sessions in Tmux are a collection of independently managed windows. They are great for creating lots of 'projects' - each session can be a project with the appropriate windows for the work you are doing. Let's start Tmux, but name the session effective-shell by using the tmux new -s effective-shell command. Once we've done this, we'll create a couple of windows, then create a new session by using Ctrl+b :. This command opens the Tmux command pane which is shown at the bottom of the screen. This command pane allows us to run a Tmux command. When we do this, we don't need to put tmux at the beginning. Let's create a second session named my-project. We can now switch between sessions with Ctrl+b s. This opens the list of sessions and lets us select one using the arrow keys. Tmux gives us a preview of each session as we move the selection over them. The commands we will run are below. Remember, ^B means 'Control B', i.e. hold the Ctrl key and press 'b': # Create a new tmux session with the name 'effective shell'.tmux new -s effective-shell# Create a new Window.^B c# Enter tmux command mode.^B :# Create a second session.new -s my-project# Select from the list of sessions.^B s We can see visually what this looks like below:","s":"Session Management","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#session-management","p":813},{"i":832,"t":"The great thing about sessions is that we can set them up, then detach from them to do other work. The sessions will keep running and we can re-attach to them later. This means you can close your terminal and re-open it and programs will still run. If we are in the shell and want to open Tmux and attach to whatever was the last session, we can just run: tmux attach When we are in a Tmux session, we can detach from it by entering command mode with Ctrl+B : and using the detach command: ^B :detach We could close our terminal at this point - the sessions are persisted by the Tmux server. Now if we re-open a terminal, we can re-attach with the same command as before: tmux attach A video of how this looks is below: If you use Tmux a lot you might find you end up with lots of sessions - you can delete sessions by pressing ^B s to show the session list, scroll to the session you want to delete and just press x. Tmux will ask for confirmation before it closes the session. Some useful shortcuts for sessions are: Sessions are extremely powerful for organising your work. Some useful commands for working with sessions are: Command Description tmux attach Attach to the last used session tmux new -s name Start a new tmux session named name ^b : new -s another-name Enter command mode, start session named another-name ^b $ Rename the current session ^b s Show the session list. Close the selected session with x ^b ) Move to next session ^b ( Move to the previous session ^b w Show all windows - this command also shows all sessions!","s":"Attaching and Detaching from Sessions","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#attaching-and-detaching-from-sessions","p":813},{"i":834,"t":"Tmux's out-of-the-box configuration is normally going to be fine for everyday use. However, if you find yourself using Tmux a lot you might want to look at some of the configuration options available to help you fine-tune the program to suit your preferences. Tmux follows a very standard Unix-style configuration pattern - a dotfile is used to configure the program. If you are not familiar with dotifles, check Chapter 26 - Managing Your Dotfiles - this chapter also goes into detail on how you can version control and save your dotfiles as well as share them across many machines easily. To configure Tmux, create a file named .tmux.conf in your home directory: touch ~/.tmux.conf Now open this file in your preferred editor. As we have just created it there will be nothing in the file. There are a raft of configuration options available, you can check the manpages with man tmux to see details, or search for any of the excellent online guides on how to configure Tmux. I'll share what I think are some of the most useful configuration options for Tmux1. Let's add some configuration options, I'll explain each as we go along. The Default Shell The first thing I do is to tell Tmux to use my current shell program. This means if I am using Z-Shell, Tmux will open windows with Z-Shell. If I am using Bash, it will use Bash. It will also source my shell dotfiles, so that each window that is opened has the same PS1 and configuration as my standard shell. # Set the default shell, and set the default command to use our shell (this# means we source things properly, show the correct PS1 etc).set -g default-shell ${SHELL}set -g default-command ${SHELL} Open windows in the Working Directory By default when you create a new window with ^B c, Tmux will set the working directory of the window to the home directory. In general I prefer to have the window open in my current working directory: # Open new panes and splits in the same working directory.bind c new-window -c \"#{pane_current_path}\" Stable window names and sequential windows Tmux will try to be smart and change the name of each window to the program it is currently running. This means window names change as you use them. I find this distracting, so disable the automatic renaming of windows - in general I rename a window as soon as I have opened it with ^B , and give it a descriptive name. I also set Tmux to start windows from number 1 rather than 0, and when windows are created or deleted ensure that Tmux re-numbers them so that they are sequential, without gaps. If you don't do this you'll rapidly find yourself running into windows with double-digit numbers which are harder to select (you can only use ^B .bind-key b send-prefix Mouse Support If you have a mouse, which will normally be the case if you are working with your local machine, then you can enable the mouse for Tmux, allowing you to drag panes to resize them, select panes and windows with the mouse and so on. If you are feeling like you want to really get your 'mouseless' flow working - disable this option! It'll force you to learn the commands. # Enable mouse mode (tmux 2.1 and above)set -g mouse on Vim Mode! I set a number of configuration options to help Tmux interface more seamlessly with Vim, and also use Vim directions rather than arrow keys to move around. This means I use ^B j to go to the pane below, ^B l to go to the pane to the right. I have also configured a number of keybindings to make resizing panes a little more intuitive to a Vim user, as well as keybindings for Vim style selection of text. These are more advanced options and only going to be of interest to Vim users however, so I'll let you explore them if you are interested in my dotfiles project at github.com/dwmkerr/dotfiles","s":"Configuration","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#configuration","p":813},{"i":836,"t":"We have really only touched the most basic of configuration options. Tmux can be customised in almost any way imaginable. The visual style of the status bar, the colours, the information shown, all of these settings can be changed. There are also plugin-managers for Tmux to make it easier to install plugins that provide more advanced configurations. This is a more advanced topic and one I would only suggest exploring once you are familiar with 'vanilla' Tmux!","s":"Advanced Configuration","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#advanced-configuration","p":813},{"i":838,"t":"So far we have run all of our sessions on our local machine. This is great for organising your local work. But you can run Tmux on another machine or server, then connect to it from your machine. This is another quite advanced topic, but to show just how powerful this can be, the video below demonstrates some of Tmux's features and how they can make working across many machines so much easier. In this video, the following actions are performed: We start a local Tmux session with tmux We split the window, giving us space to open a secure shell, using ^b % In our new split, we ssh onto the box we created in Chapter 31 - The Secure Shell On this box, we start a new Tmux session In this Tmux session we start creating a new Python script Now we connect to this session from our local machine over ssh using ssh -t effective-shell-aws-linux tmux attach This attaches us to our session on the server - and we're in Vim ready to work on the script! To complete the demo, I connect from another machine, my Macbook - and then edit the script live In this demo we have two separate machines connecting to our server, able to collaborate real-time on a Tmux session that will persist even if we shut down the machines we are connecting from. This just touches the surface of what is possible! In this example we used the command ssh -t effective-shell-aws-linux tmux attach. The ssh program allows you to run a command on the server. The command we are running is tmux attach. Now by default if we ask ssh to run a command it will not connect the input of our terminal to the server - the idea is that sometimes we are just using ssh to run one-off commands and don't need to stay connected. But for this command we actually want to stay attached to the server, so we use the -t request tty` flag to attach our terminal input to the SSH session. You can setup your SSH config file to automatically attach to the Tmux session. This is how my ~/.ssh/config file entry is setup for the virtual machine we created in Chapter 31 - The Secure Shell: Host effective-shell-aws-linux HostName ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com User ec2-user IdentityFile ~/.ssh/effective-shell RequestTTY yes # ensure that we attach our terminal input RemoteCommand tmux attach # attach to the tmux session This configuration means that when we SSH onto the effective-shell-aws-linux box we will run the tmux attach command and attach our terminal. To connect to Tmux on the server now we only need to run: ssh effective-shell-aws-linux","s":"Collaboration","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#collaboration","p":813},{"i":840,"t":"We've really only touched the surface of what Tmux can do. There are some truly incredible things you can do with a multiplexer like Tmux as you start to use it more. Selecting text from the shell without touching the mouse, seamless integration of Tmux splits and Vim splits, sending commands to multiple machines at once, using plugin managers to add advanced features, the list goes on. Tmux also lets you rapidly resize panes, break a pane into its own window or session, re-order panes, swap panes and so on. These commands are probably the next ones to start becoming familiar with! If you have enabled mouse mode you can also resize panes with the mouse if you have one available. I'd highly recommend using Tmux as part of your standard workflow - get familiar with the basic features shown in this chapter and then as you start to find limitations and want to do more explore some of the great books and blog posts out there that go into more advanced features.","s":"Going Further with Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#going-further-with-tmux","p":813},{"i":842,"t":"You can quickly see all of the Tmux commands by running ^b ?. The output of this command will look something like this: C-b C-b Send the prefix keyC-b C-o Rotate through the panesC-b C-z Suspend the current clientC-b Space Select next layoutC-b ! Break pane to a new windowC-b \" Split window verticallyC-b # List all paste buffersC-b $ Rename current sessionC-b % Split window horizontallyC-b & Kill current windowC-b ' Prompt for window index to selectC-b ( Switch to previous clientC-b ) Switch to next clientC-b , Rename current windowC-b . Move the current windowC-b / Describe key bindingC-b 0 Select window 0C-b 1 Select window 1... A quick reference of command commands below: Command Description Essential Tmux Commands tmux ls List sessions tmux new [-s name] Start a new tmux (optionally with a session name) tmux attach [-t name] Attach to the last used session, or the target session with -t tmux kill-session [-t name] Kill a session named name tmux kill-session -a Kill all sessions except the current session ^b d Detach from the current session ^b : new -s another-name Enter command mode, start session named another-name ^b ? Show command help *Sessions ^b $ Rename the current session ^b s Show the session list. Close the selected session with x ^b ) Move to next session ^b ( Move to the previous session ^b w Show all windows - this command also shows all sessions! Windows ^b n Move to the next window ^b p Move to the previous window ^b 0 Select the window numbered '0' - use the number of any window from the status pane ^b & Close the current window ^b , Rename the current window ^b w Show the window navigator ^b $ Kill the current window Splits and Panes ^b % Create a horizontal split ^b \" Create a vertical split ^b Move to the pane in the direction of an arrow key ^b z Zoom in or out of a pane ^b ! Convert pane to window","s":"Getting Help","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#getting-help","p":813},{"i":844,"t":"To learn more about Tmux I recommend the excellent book tmux 2: Productive Mouse-Free Development by Brian Hogen. This is suitable for anyone, from beginner to expert, you can regularly go back to it as you become more familiar with the basics of Tmux and learn how to take your skills to the next level!","s":"Going Further with Tmux","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#going-further-with-tmux-1","p":813},{"i":846,"t":"In this chapter we introduced the concept of Terminal Multiplexers, in particular GNU screen and Tmux. We saw how to manage windows, panes and sessions. We learned how to configure Tmux to suit your personal working style. We also looked at how we can use Tmux to manage sessions on remote machines and even collaborate real time with other users. You can find my complete set of dotfiles at github.com/dwmkerr/dofiles if you would like to see how I configure other programs.↩","s":"Summary","u":"/part-6-advanced-techniques/master-the-multiplexer/","h":"#summary","p":813},{"i":848,"t":"On this page","s":"Useful Patterns for Shell Scripts","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"","p":847},{"i":850,"t":"By default, shell scripts will continue to execute if a command fails. This behaviour makes sense when running an interactive shell - we don't want the shell to close if a command fails. For a shell script however, continuing after an error has occurred is most likely going to lead to unexpected behaviour. There are two options that you will often see set at the top of a script: set -eset -o pipefail The first option ensures that the shell script will abort if a command fails. The second option ensures that if a command that is part of a pipeline fails, then the shell script fails. Even when the set -e option is set, only the final command in a pipeline will cause the script to exit if it fails. Here's an example showing why these options are useful: # Create the effective shell folder.mkdir -p ~/effective-shell# Download and untar the effective shell samples.samples_uri='https://https://effective-shell.com/downloads/effective-shell-samples.tar.gz'$ sudo wget -c \"${samples_uri}\" -O - | tar -xz -C ~/effective-shell If we don't include the set -e option, then if the mkdir -p command fails, the script will continue to run. We will then attempt to download and untar a file into a folder that does not exist. Why would mkdir -p fail? Although mkdir -p succeeds even if the folder exists, it will still fail if there is a file in the location specified, or there are not permissions to create the folder and so on. So even commands that you assume should run successfully you have to be careful with. In the second part of this snippet, we use the wget (web get) command to download the samples and pipe the results to tar to extract them. If we have only set set -e, then if wget fails (for example if the address is wrong or we are offline) then the shell will not abort the script and continue trying to run the subsequent commands, which are not going to work as expected. If you have a command that you expect may fail, but want to continue execution even if it does fail, then use the || operator: # Remove the shell configuration.rm \"$HOME/.shell.sh\" || true In this case I have used a conditional operator, as described in Chapter 20 - Mastering Conditional Logic, to ensure that even if the rm command fails for some reason, the overall result of the statement will be true and the script will not exit. Check Chapter Chapter 22 - Functions, Parameters and Error Handling if you need a refresher on the set -e or set -o pipefail commands.","s":"Ensuring Exit on Failure","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#ensuring-exit-on-failure","p":847},{"i":852,"t":"You can use the set (set option) command to set the trace option. This option is incredibly useful for debugging shell scripts. When the trace option is set, the shell will write out each statement before it is evaluated. Let's see just how useful this is with an example! # today.sh - creates a 'today' symlink in the home directory folder to a fresh# temporary folder each day.# Enable tracing in the script.set -x# Get today's date in the format YYYY-MM-DD.today=$(date +\"%Y-%m-%d\")# Create the path to today's temp folder and then make sure the folder exists.temp_path=\"/tmp/${today}\"mkdir -p \"${temp_path}\"# Now that we've created the folder, make a symlink to it in our homedir.ln -sf \"${temp_path}\" \"${HOME}/today\"# Disable tracing now that we are done with the work.set +x# Write out the path we created.echo \"${temp_path}\" Notice that we use set -x to enable tracing early on in the script, and set +x to disable tracing towards the end. If we run this script, we'll see the following output: $ ~/effective-shell/scripts/today.sh++ date +%Y-%m-%d+ today=2021-05-29+ temp_path=/tmp/2021-05-29+ mkdir -p /tmp/2021-05-29+ ln -sf /tmp/2021-05-29 /home/dwmkerr/today+ set +x/tmp/2021-05-29 Each command that the shell executes is written to stdout before it is executed. The parameters are expanded, which can make it far easier to see what is going on and troubleshoot issues. The + symbol is written at the start of each trace line, so that you can differentiate it from normal output that you write in your script1. The final line of output in the example above does not have a + in front of it - because it is actual output from an echo command, rather than a trace line. The number of + symbols indicates the 'level of indirection' - this is how many sub-shells you are in. Each subshell is traced on its own line. This makes tracing complex commands far easier: set -xecho \"Name of home folder is $(basename $(echo ~) )\" The output of this command is: +++ echo /home/dwmkerr++ basename /home/dwmkerr+ echo 'Name of home folder is dwmkerr'Name of home folder is dwmkerr Notice that each subshell command is shown with an additional plus as it gets more nested. The nested commands are shown in the order that they are evaluated. I often start my shell scripts with a snippet like this: # Fail on errors in commands or in pipelines.set -eset -o pipefail# Uncomment the below if you want to enable tracing to debug the script.# set -x This combines the first two patterns we've seen - failing on errors and having the option to trace a script.","s":"Debugging Shell Scripts","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#debugging-shell-scripts","p":847},{"i":854,"t":"The declare (set variable values and attributes) command can be used to explicitly declare that we are creating a variable. We saw in Chapter 19 - Variables, Reading Input, and Mathematics that sometimes this command is required - if we want to create an associative array for example. There are a number of options for the 'declare' command, but one that is particularly useful is the -p (display attributes and value) option. This can be used to show all of the variables of a certain type. Here's an example to show all associative arrays that have been created: $ declare -p -Adeclare -A BASH_ALIASES=()declare -A BASH_CMDS=() You can also use this command to validate whether a variable has been set or not: if declare -p -A my_options > /dev/null 2>&1; then echo \"'my_options' exists\"else echo \"'my_options' does not exist\"fi We have to silence the error output of the declare command unless we want it to print an message if the variable doesn't exist. This technique can be useful to use before setting variables to ensure that they are not already in use, or check that the variable exists. Functions are also variables - so we can use this trick to show all functions that are declared, the value of a function, or check if a function exists.","s":"Checking for Existing Variables or Functions","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#checking-for-existing-variables-or-functions","p":847},{"i":856,"t":"If you are writing a script that should clean up after itself, you might want to use the unset (unset values and attributes) command. This can be useful if you want to create a script that leaves behind no variables or functions that could cause issues for later users: # Remove the 'is_even' function from the shell session.unset -f is_even","s":"Unsetting Values","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#unsetting-values","p":847},{"i":858,"t":"You can use the trap (trap signals and events) command to specify a set of commands to run when the shell receives signals, or at certain points such as when the script exits or a function returns. A very common use for traps is to create a 'cleanup' function that is executed when the script exits or if the user aborts execution by pressing Ctrl+C (which sends the SIGINT signal). Here's an example of how a trap can be set to cleanup a temporary folder when a script exits or is interrupted: # Create a temporary folder for the effective shell download.source=\"https://effective-shell.com/downloads/effective-shell-samples.tar.gz\"tmp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'effective-shell')tmp_tar=\"${tmp_dir}/effective-shell.tar.gz\"# Define a cleanup function that we will call when the script exits or if# it is aborted.cleanup () { if [ -e \"${tmp_tar}\" ]; then rm \"${tmp_tar}\"; fi if [ -d \"${tmp_dir}\" ]; then rm -rf \"${tmp_dir}\"; fi}# Cleanup on interrupt or terminate signals and on exit.trap \"cleanup\" INT TERM EXIT# Download the samples.curl --fail --compressed -q -s \"${source}\" -o \"${tmp_tar}\"# Extract the samples.tar -xzf \"${tmp_tar}\" -C \"${tmp_dir}\" In this script we have defined a function called 'cleanup'. We then use the trap command to ensure that we call the function if INT is sent, TERM is sent or when the script exits. This is very useful in scripts that can take a while. This script downloads the effective shell samples from the internet. If the user is having connectivity issues then this might take a while and they may end up aborting the script. If they do so in this case we will still clean up the temporary folder we created. Traps provide a very convenient way to handle things like cleanup, provide more diagnostic information or even disable a user from interrupting your script. In the example below we force the user to press Ctrl+C twice if they want to interrupt the script: interrupt_count=0on_interrupt() { if [ $interrupt_count -lt 1 ]; then echo \"Aborting this operation can cause errors.\" echo \"Press Ctrl+C again if you are sure you want to cancel.\" interrupt_count=$((interrupt_count + 1)) else # Convention is to use the status code 130 for interrupted scripts. echo \"Aborting long operation\" exit 130 fi}trap on_interrupt INTtotal_time=0while true; do echo \"Long operation: ${total_time} seconds elapsed\" sleep 3 total_time=$((total_time + 3))done If we run this script we can see that the user must press Ctrl+C twice to abort the operation: $ ~/effective-shell/scripts/long-operation.shLong operation: 0 seconds elapsedLong operation: 3 seconds elapsedLong operation: 6 seconds elapsed^CAborting this operation can cause errors.Press Ctrl+C again if you are sure you want to cancel.Long operation: 9 seconds elapsedLong operation: 12 seconds elapsed^CAborting long operation Some other things that you might want to be aware of for the trap command are: The SIG at the beginning of the name of a signal is optional but not supported in all shells, and a signal number can also be used - this means that SIGINT, INT and 2 are all equivalent options for trap, but INT is the most portable and easiest to read! You can list the signals available with trap -l or kill -l - but remember that special conditions such as EXIT and RETURN are not listed, you can find these with help trap You can stop a signal from being processed with trap \"\" INT - this means that no command will be executed when we receive a INT You can reset a trap by running trap - INT, this will remove any trap handler You can test your traps by sending a signal explicitly to your script with kill -s INT, providing the name of the signal","s":"Traps","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#traps","p":847},{"i":860,"t":"You can use the getopts (parse option arguments) command to process the arguments for a script or function Let's imagine we wanted to update our 'common' command to support the following options: -h for 'help', which shows command help -e for 'execute', which takes the number of a command from the list which will be executed The 'getopts' command takes two parameters. The first is an 'option string', which is a list of the parameter letters that are allowed. The option string for 'getopts' expects that each letter followed by a colon represents an option that requires a value. The second parameter specifies the name of the variable that will be set to the currently processed option. If the option string starts with a colon, it affects how 'getopts' assigns values to the shell variables specified by 'name' and 'OPTARG'. For more detailed information, please refer to the 'man getopts' manual. Typically this command is used in a while loop, as it will return 'success' until the final option has been processed. A case statement is typically used to process the option: # Helper function to show how the command should be invoked.show_help() { echo \"usage:\" echo \" common [-h] [-e ] count\"}# Process the options.while getopts \":he:\" option; do case ${option} in # Handle the 'help' option. h ) show_help exit 0 ;; # Handle the 'execute command' option by storing the value provided # for the option. e ) execute_command=${OPTARG} ;; # If we have an invalid argument, warn and fail. \\? ) echo \"The value '${OPTARG}' is not a valid option\" exit 1 ;; # If we are missing a required argument, warn and exit. : ) echo \"The option '${OPTARG}' requires an argument\" ;; esacdone There are a few things to point out from this script: The option string starts with a colon - any option letter that is followed by a colon expects an argument If an invalid option letter is set, the value of the option variable is set to \\? - we can then handle this in our case statement If a letter is provided without an argument that is required, the value of the option variable is set to : - we can then handle this in our case statement For complex option processing you might see scripts where multiple loops are used to process sets of options. It is common to end option processing with the following line: shift $((OPTIND - 1)) The ${OPTIND} variable stores the index of the last option processed. By shifting by this value minus one, we remove the processed options from the $@ (all parameters) array. This means we don't try to process the same options again. The ~/effective-shell/scripts/common.sh script processes parameters using the getopts command. You can use this as an example to help you with your own scripts.","s":"Handling Options","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#handling-options","p":847},{"i":862,"t":"There are special escape sequences that can be used in the shell to colour the output of the text shown. For example, in many terminals the following text will be shown in green: green='\\e[0;32m'reset='\\e[0m'echo -e \"Do you like ${green}apples${reset}?\" On most terminals you will see the text below, with the word 'apples' rendered in green: Do you like apples? Note that it is important to provide the -e flag to the 'echo' command so that it correctly processes the colour codes. In fact, a better option is to use the printf (format and print arguments) command, as it is more portable and behaves more consistently across different versions of Unix and Linux. The colour codes are ANSI escape sequences that have been defined to control the formatting of content in a terminal. There are number of formatting options - such as foreground and background colours, bold, underline and so on. These codes can be quickly found online if you search for \"ANSI color codes\". It is important to be careful when using colour codes - you don't want them in all circumstances. Let' see an example. The 'rainbow' function below writes out a message in a number of colours: rainbow () { local message=\"$1\" local reset='\\e[0m' for ((colour=31; colour<=37; colour++)) do colour_code=\"\\\\e[0;${colour}m\" printf \"${colour} - ${colour_code}${message}${reset}\\n\" done} If we run this function in most terminals, we'll see the provided message with the colour number in seven different colours: $ rainbow test31 - test32 - test33 - test34 - test35 - test36 - test37 - test We have to be careful when formatting output. It can be helpful for a user in an interactive shell (on many systems for example even the ls command is actually an alias for ls --color=auto meaning that the ls command uses colours in its output). But there are circumstance when we don't want to use colour codes. Let's see what we get when we write the rainbow output to a file: $ rainbow hello >> text.txt$ cat -v text.txt31 - ^[[0;31mhello^[[0m32 - ^[[0;32mhello^[[0m33 - ^[[0;33mhello^[[0m34 - ^[[0;34mhello^[[0m35 - ^[[0;35mhello^[[0m36 - ^[[0;36mhello^[[0m37 - ^[[0;37mhello^[[0m The '-v' parameter tells cat to make escape characters visible. If you open the in a text editor you will see the same escape characters written in the file. This shows the problem with the rainbow function - it adds the colour escape sequences even when we are writing the results to a file. In most cases this is not going to be what we want. Commands like ls do not include colour codes when writing to a file. There is not an entirely fool-proof way to avoid this issue, but the most common pattern I have seen is to check whether the standard output file descriptor is associated with a terminal. We can do this using the -t expression of the test command: if [ -t 1 ]; then echo \"We are writing to a terminal\"else echo \"We are not writing to a terminal\"fi You will see -t 1 in a number of scripts as a way to check whether the output is going to a terminal device. The -t test returns success if the provided file descriptor is associated with a terminal device. The file descriptor '1' is the descriptor for the stdout stream (if this is unfamiliar, check Chapter 7 - Thinking in Pipelines). Here's how we could use the test in our rainbow function: rainbow () { local message=\"$1\" local reset='\\e[0m' for ((colour=31; colour<=37; colour++)) do colour_code=\"\\\\e[0;${colour}m\" if [ -t 1 ]; then printf \"${colour} - ${colour_code}${message}${reset}\\n\" else printf \"${colour} - ${message}\\n\" fi done} This version of the function will not write the ANSI escape sequences if the output device is not a terminal, meaning that if we run: $ rainbow test > text.txt Then the output file will not contain escape sequences. You can find out more about the -t test by running man test. As a final tip - if you are formatting output you should consider using the tput (query terminfo database) command to make your code more readable and portable: green=$(tput setaf 2) # set ansi foreground to '2' (green)reset=$(tput sgr0) # reset the coloursecho -e \"Do you like ${green}apples${reset}?\" The 'tput' command is quite advanced, but you can search online for more details (the manual pages for the command are hard to decipher as it can be used for many operations and is complex). The ~/effective-shell/scripts/common.sh script includes colourised output and also checks to see whether colour codes should be printed based - you can use this as a reference for your own scripts.","s":"Colouring Output","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#colouring-output","p":847},{"i":864,"t":"Different flavours of Unix and Linux can behave quite differently. A common requirement is to write scripts that are portable and can be used across systems. However, this is not always possible. There are times when we need to check to see whether we are on a specific operating system and take a specific action. You will often see the uname (show operating system name) command used to check the operating system: case \"$(uname)\" in Darwin) os=\"OSX\" ;; Linux) os=\"Linux\" ;; CYGWIN*|MINGW32*|MSYS*|MINGW*) os=\"Windows\" ;; SunOS) os=\"Solaris\" ;; *) echo \"Unsupported operating system\" exit 1 ;;esacecho \"Your OS is: ${os}\" The ~/effective-shell/scripts/common.sh script checks to see whether the operating system is OSX and if so, temporarily aliases the text commands such as sed to their GNU equivalent, as the OSX versions of the commands are based on BSD so have slightly different parameters. You can use this script as an example of how to deal with OSX in shell scripts that are designed to be used on Linux as well as OSX.","s":"Checking the Operating System","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#checking-the-operating-system","p":847},{"i":866,"t":"As we saw in Chapter 10 - Understanding Commands there are many different ways to determine whether a command is available. The most correct and portable way to test to see whether a command is available is to use the command -v command as shown below: if ! command -v \"curl\" >/dev/null 2>&1; then echo \"'curl' is not installed, please install and try again\"fi Note that when we're using the command command, we silence error output and standard output. This is required because otherwise we would see an error message written to the screen if the command doesn't exit or would see the details of the command if it does exist. The ~/effective-shell/scripts/common.sh script checks to see whether certain GNU versions of tools are installed when running on OSX. You can refer to this script for an example of checking for the presence of commands.","s":"Checking for Installed Programs","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#checking-for-installed-programs","p":847},{"i":868,"t":"The select compound command prints a menu and allows the user to make a selection. It is not part of the Posix standard, but is available in Bash and most Bash-like shells. Here's how we could ask a user to select their favourite fruit from a list: select fruit in Apple Banana Cherry Duriando echo \"You chose: $fruit\" echo \"This is item number: $REPLY\"done If we run these commands we will see output like the below: 1) Apple2) Banana3) Cherry4) Durian#? 1You chose: AppleThis is item number: 1#? 3You chose: CherryThis is item number: 3#? 4You chose: DurianThis is item number: 4#? ^D Notice that select will run just like an infinite loop - after the statements in the select body are run, the selection is offered again. The user can either end transmission with the ^D character or press ^C to quit. You will normally see the select used with a case statement to process the selection. This is something you may come across in scripts so is useful to be aware of.","s":"Using 'Select' to Show a Menu","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#using-select-to-show-a-menu","p":847},{"i":870,"t":"You will often see a nice little trick that allows you to change the current directory for a specific command, without affecting the current directory for the shell. Here's how this trick will look: (mkdir -p ~/new-project; cd ~/new-project; touch README.md) The brackets around the statements mean that these commands are run in a sub-shell. Because they run in a sub-shell, they change the directory in the sub-shell only, not the current shell. This means we don't need to change back to the previous directory after the commands have completed. This sequence of commands would create a new folder (we use mkdir -p so that if the folder exists the command does not fail), then change to the folder, then create a new file called README.md.","s":"Running Commands in Subshells","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#running-commands-in-subshells","p":847},{"i":872,"t":"Anti-patterns are techniques that you may see but should be avoided. I have noted a few here as you will likely see them in your travels and should know why they are problematic.","s":"Anti-Patterns","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#anti-patterns","p":847},{"i":874,"t":"You will sometimes see shebangs in shell scripts that contain options, like so: #!/usr/bin/bash -ex# Script contents below... It is possible to specify the arguments to the program that is used to execute the script in the shebang. In the case above, the -ex flags are passed to the bash program, enabling the 'exit on error' and 'trace' options. I include this pattern because it is possible you will see it in other scripts, but please do not do this. There are two particular reasons that it is risky. The first is that pattern requires that you know the path to the shell. As we saw in Chapter 18 - Shell Script Essentials, we should use the #!/usr/bin/env program so that we search the $PATH for the shell rather than assuming that we know the location of the shell program. The second reason is that multiple parameters are not handled consistently across operating systems. For example, on some Unix systems the following shebang will run bash with the -e parameter: #!/usr/bin/env bash -e However, on many Unix distributions only one parameter is passed. This would mean that the -e parameter would be silently ignored, which would be very confusing for the reader.","s":"Configuring Options in Shebangs","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#configuring-options-in-shebangs","p":847},{"i":876,"t":"The shell is amazing. Considering how long it has been around, it has in many ways changed remarkably little in the last few decades. This is a testament to the genius of the design of Unix systems and the shell in general. However, the shell is not generally going to be the best choice for any kind of complex logic or work. Shell scripts are great for automating simple tasks, creating utilities to help you out, but come with many challenges. The syntax can be confusing, making scripts work across multiple systems can be challenging, and there are not many features to help you write robust code. Perhaps the biggest anti-pattern in shell scripts is to simply let them get too large and do too much with them. There comes a certain point where you will almost certainly create a more portable, performant and maintainable solution to your problem using a dedicated programming language like Python (which is available on almost all systems) or one of the many other languages available. This is a topic we discuss in detail in the Chapter 30 - How to Avoid Scripting, but for now I would just say that as soon as your script starts to get longer than a page, or takes more than a few minutes to reason about, then you might be reaching the point that a programming language could be a better option.","s":"Complex Logic in Shell Scripts","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#complex-logic-in-shell-scripts","p":847},{"i":878,"t":"You might find shell scripts that do not have a shebang at the top. This is something that you should avoid. The shebang gives you a way to be very explicit about what shell is required to run your script. For example, if I see a shebang like this: #!/usr/bin/env sh Then my assumption would be that this script can run on any Posix compliant shell, i.e. it is as compatible as possible. However, if I see this: #!/usr/bin/env bash Then the assumption would be that this script is Bash-specific and uses \"Bash-isms\", such as the if [[ conditional ]] construct. Finally, if I was to see a shebang like this: #!/usr/bin/env zsh Then I would expect that this script has been explicitly written to be used with Z-Shell. If you do not include a shebang in a script, then the behaviour can be ambiguous. For example, at the time of writing, if you run a shell script without a shebang from a Bash shell, then the script will be run using a new instance of Bash. However, if you run a shell script without a shebang from Z-Shell then Z-Shell will use sh from your path. But depending on your system, sh may be a symlink to dash, or bash, or another shell. Scripts that do not have shebangs are inherently ambiguous and will run using different shells depending on the shell used to execute the script as well as the operating system. If you want to experiment and see what shell runs a script, create a script such as the ~/effective-shell/scripts/nobang.sh script that looks like this: # nobang: This script shows an anti-pattern - not using a shebang in a shell# script. It shows the process tree to for the shell that runs the script:pstree $$ If I run this script from MacOS, the output below is shown: -+= 00001 root /sbin/launchd \\-+= 07995 dwmkerr tmux \\-+= 31195 dwmkerr /bin/zsh \\-+= 49833 dwmkerr sh ./samples/script/nobang.sh \\-+- 49834 dwmkerr pstree -p 49833 \\--- 49835 root ps -axwwo user,pid,ppid,pgid,command Although I ran the script from a Z-Shell session, it was executed with sh, which is the Bourne Shell (version 3 on my system). The only time you should omit the shebang is if you expect the script to be sourced - we will see sourcing in the next part of the book.","s":"Scripts without Shebangs","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#scripts-without-shebangs","p":847},{"i":880,"t":"In this chapter we saw an assortment of common patterns that can be useful when building shell scripts. In the next part of the book we're going to look at how you can customise your shell and environment to build your own toolkit! The value shown before each trace line can be configured by setting the $PS4 variable.↩","s":"Summary","u":"/part-4-shell-scripting/useful-patterns-for-shell-scripts","h":"#summary","p":847},{"i":882,"t":"On this page","s":"The Secure Shell","u":"/part-6-advanced-techniques/the-secure-shell/","h":"","p":881},{"i":884,"t":"SSH is the Secure Shell Protocol. It is a protocol that has been used for decades as a way to establish a secure connection to a remote machine, on your network or across the internet. The program named ssh is the 'secure shell' - it is a program that helps you establish SSH connections, manage credentials, and open a shell on a remote machine. When you connect to a remote machine, you will typically have to authenticate. It is possible to authenticate with a username and password. In most scenarios however you will have to use Asymmetric Encryption to authenticate with the server. Asymmetric encryption uses special 'keys', called 'public keys' and 'private keys' to manage authentication and secure data. This might be new to you if you are used to using simple authentication protocols such as username and password. But public/private key based security is extremely secure and a great thing to know about! So let's start by looking at how public and private keys work and then create our own key pair which we will later use to secure our connections.","s":"What is SSH?","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#what-is-ssh","p":881},{"i":886,"t":"The details of symmetric and asymmetric encryption complex and beyond the scope of this book1. We'll cover the only essential concepts and techniques in this chapter. Before we look at asymmetric encryption, let's take a quick look at the challenges of symmetric encryption - which will help explain why asymmetric encryption is preferred.","s":"An Authentication Crash Course","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#an-authentication-crash-course","p":881},{"i":888,"t":"When we use symmetric encryption, we have a shared 'secret', normally something like a username and password. If a user 'Alice' wants to send a message to another user, 'Bob', we have to share the secret so that Bob can decrypt the message that Alice sends: The diagram above is a hugely simplified view, but essentially shows that when the users want to exchange a message they both need to know the secret - one user uses the secret to encrypt the message, the other uses the same secret to decrypt it. When you connect to a remote machine using symmetric authentication, you are the client (Alice) and the machine is the server (Bob). Now in reality there are ways to obscure and protect this secret somewhat, but no matter how many clever tricks are used, we are still in the situation where the recipient has sensitive data - the user's secret. This is actually quite problematic because it means if we don't trust this user, or their machine or environment, we might not want to share a secret.","s":"Symmetric Encryption for Authentication","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#symmetric-encryption-for-authentication","p":881},{"i":890,"t":"When we use asymmetric encryption, Alice first creates a 'key pair'. This is two files - a public key and a private key. Alice keeps the private key and sends the public key to Bob: Now that Bob has the public key, he creates a secret that only he knows and then encrypts it with Alice's public key. He then sends this secret to Alice. The secret was encrypted by Alice's Public Key - meaning only the associated Private Key can decrypt it. This means even if someone intercepts the secret they cannot decrypt it! Only Alice can, as only Alice has the private key: Now Alice has Bob's secret and only she can decrypt it. Once she decrypts it both she and Bob have a shared secret - that no one has been able to intercept. They can now use Symmetric Encryption to exchange messages, safe in the knowledge that the secret is kept just between them. The fantastic thing about this mechanism is that only Alice can decrypt messages encrypted with her public key. Alice can keep her key private. Her public key is not sensitive - it can only be used to encrypt messages for Alice. Alice can also encrypt messages with her Private Key - anyone who has the Public Key can decrypt them. This means that this is not a secure way to encrypt a message - but it is a very good way to sign a message. Given that only Alice has the private key, only she can encrypt messages with it. This means if she sends a message encrypted with her private key anyone who has the public key can decrypt it to assert it was sent by Alice - she's the only person with the private key. This method of signing messages with the Private Key is not used to keep the message private but instead to verify the identity of the sender. Most modern day cryptography protocols are based on this technique. In many of them Bob will not actually send back a secret - instead he'll generate his own key-pair and return the Public Key. This is called a 'key exchange'. Almost all modern day encryption is built on this mechanism - when you open a secure connection to a website, an exchange of keys is made between you and the server2. When we use the secure shell to communicate with a remote machine, we will give the remote machine our public key, and we will encrypt our communications with the private key. This is essential because other users might have access to the remote machine - we don't want them seeing our sensitive data such as passwords or private keys. Let's look at how to create a key-pair now.","s":"Asymmetric Encryption for Authentication","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#asymmetric-encryption-for-authentication","p":881},{"i":892,"t":"We can use the ssh-keygen (OpenSSH authentication key utility) to create a key pair. We are going to use this key pair to communicate with a server that we will create on Amazon Web Services or AWS. AWS requires that we use a particular format known as PEM, so let's create a key pair in that format now. We will move to the ~/.ssh folder first, which is where users often store their SSH keys: cd ~/.sshssh-keygen -m PEM When you run this command, it will ask you where you would like to save the file: Generating public/private rsa key pair.Enter file in which to save the key (/home/dwmkerr/.ssh/id_rsa): I suggest that you use the default location (the ~/.ssh folder), but name the key pair 'effective-shell': Enter file in which to save the key (/home/dwmkerr/.ssh/id_rsa): effective-shellEnter passphrase (empty for no passphrase): At this point you will be asked to provide an optional passphrase. We will leave this blank as we are not going to use the server or key for any particularly sensitive data. However, if you are creating keys that will encrypt sensitive data you should definitely add a passphrase. If you add a passphrase, you'll be required to enter each time you load the key. This adds a layer of security - if someone steals your private key they would have to know the passphrase to open it. Once you have skipped the passphrase and passphrase confirmation by pressing the enter key twice, you'll see the final output: Your identification has been saved in effective-shellYour public key has been saved in effective-shell.pubThe key fingerprint is:SHA256:HcqIl3ZhRz9jvhYO3g64FEYT3mAoDc6P4mnh4aKuY08 dwmkerr@macbookThe key's randomart image is:+---[RSA 3072]----+| .o .+ . || o. oo = . || o. * + = || + * * + o || + o * S o o || + = o o + + o ||. *E o o = ||o+. . . + ||*o.. . . |+----[SHA256]-----+ The randomart shown is designed to provide a more user-friendly way to identify a key - if you have many keys you can see the randomart for each to pick the one you want. I have not yet met anyone who remembers their randomart so I think it's safe for you to ignore it for now! The essential thing is that we now have two new files - our public and private keys: $ ls | grep effectiveeffective-shelleffective-shell.pub The public key is the key with .pub at the end. Now let's create a new virtual machine in the cloud and provide it with our public key so that we can access it. To do this, we're going to set up a virtual machine using Amazon Web Services - a popular cloud computing platform. We'll use the free tier, meaning this should not cost you any money!","s":"Creating a Key Pair","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#creating-a-key-pair","p":881},{"i":894,"t":"We're going to create a virtual machine in the cloud to connect to and test out ssh. We'll do this using Amazon Web Services, which is probably the most popular cloud services provider. If you already have an account you can skip this part of the chapter. If not, we'll look at how to setup an account now. To sign up for an AWS account, open the site at: https://aws.amazon.com/account/sign-up Choose the 'Create Free Account' option: When you sign up you will be asked to provide quite a few personal details, as well as credit card details. Make sure you select the \"Basic Support Plan\", which is free. Avoiding Credit Card Bills Amazon requires your credit card details in case you use paid services. In this chapter we will be using 'free tier' services that have no costs, but be very careful if experimenting or playing with other services on AWS - most of them are will have some cost associated with their usage. The costs are generally low and there are safeguards in place to ensure you don't accidentally create expensive resources, but keep your AWS credentials very safe. If someone has access to them they could run expensive services on your account. Once you have signed up successfully, you will see the 'congratulations' message. You can now press the 'Go to the AWS Management Console' button to go to the management console. You will be asked for your password again. Congratulations - you now have an AWS account that you can use to run services in the cloud! Now let's create a virtual machine that we can connect to.","s":"Setting up an AWS Account","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#setting-up-an-aws-account","p":881},{"i":896,"t":"Use the search bar in the AWS Management Console to search for 'EC2'. EC2 is the name AWS uses for its virtual machine services. The first thing we are going to do is upload our public key to AWS so that it can be used when we create our virtual machine. Select the 'Key Pairs' option from the menu on the left or the dashboard: When the key pairs view is open, choose 'Actions > Import Key Pair': Give the key pair a sensible name and upload the public key file, which will be in ~/.ssh/effective-shell.pub if you have been following with the tutorial. Alternatively you can open that file on your local machine and copy its contents: Now that the key pair has been imported we can go back to the EC2 dashboard and choose 'Launch Instance': We are now going to go through a set of options to specify the details of the virtual machine AWS will create for us. Step 1: Choose an Amazon Machine Image (AMI) The first option should be Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type, with a 'Free Tier Eligible' label on the left. Press the blue 'Select' button on the right of this machine to choose this instance type. Step 2: Choose an Instance Type Select the default t2.micro instance type. This is free-tier eligible and more than powerful enough for our needs. Now press 'Review and Launch' - we do not need to configure any of the advanced options. Step 3: Review Instance Launch There will be a warning saying that 'your security group is open to the world' on this page. We can safely ignore that as we are not putting anything sensitive on this instance. This message is telling us that anyone who knows the address of our instance can try and connect to it. This is not a problem for what we are doing, as we are creating this machine to experiment with and not putting sensitive data on it. Press the 'Launch' button on the bottom right - another screen will pop up, don't dismiss this screen, it is where we will choose our key pair! We will be asked to provide a key pair. Make sure the key pair you just imported is selected! You will be required to select the check box that says \"I acknowledge that I have access to the private key\" - this is AWS warning us that if we don't have the private key associated with the public key we uploaded, we will not be able to connect to the instance. Once you have checked the checkbox, you can choose \"Launch Instances\". Launching the instance will take a few seconds. Press the \"View Instances\" button when this is done. Select the checkbox next to this new instance: Once you have selected the instance, press the 'Connect' button. In the screen that pops up, choose 'SSH Client': Keep this browser window open, it contains the details we need to know for the next steps. Shut down your Virtual Machine when you are done with it! When you have finished experimenting with your virtual machine, you should power it down. You can select it in the AWS Console and choose the 'Instance State > Stop Instance' option. When you are completely finished with the instance, choose 'Instance State > Terminate'. You should not be billed for this instance as it is in the free tier, but better safe than sorry!","s":"Creating a Virtual Machine on AWS","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#creating-a-virtual-machine-on-aws","p":881},{"i":898,"t":"Now that we have created a virtual machine, we can use the ssh program to open a connection to it and run a secure shell. When we want to SSH onto a machine, we need to provide a few details: The address of the machine, which is its hostname The username for the user we are going to connect with Credentials for the user we are going to connect with The address of the machine we have created will be shown in the 'Connect to instance' page we should still have open from the AWS dashboard: AWS is giving us some hints here about how to connect, it is even showing the specific command we will run with ssh. From our own shell we will now run the ssh command to connect to our instance. To connect, using the details for my new virtual machine, I would run this command: ssh -i ~/.ssh/effective-shell ec2-user@ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com You can see that we have provided three pieces of information: The credentials, by using the -i (identity file) flag, providing our private key file The username, which is the ec2-user part of the command, coming before the at symbol @ The hostname, which is the address that follows the @ When I run this command a warning is shown: The authenticity of host 'ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com (13.213.71.135)' can't be established.ED25519 key fingerprint is SHA256:8wq6Xu4xEk/BO3diae+BWUFTTKunzvCz4XidFYpl6F8.This key is not known by any other namesAre you sure you want to continue connecting (yes/no/[fingerprint])? This is my SSH client telling me that I haven't connected to this machine before so it cannot be sure that this machine is one I want to connect to. Once I continue, by entering yes and pressing enter, my SSH client will record the IP address of this machine, as well as it's hostname, meaning that in the future when I connect it will recognise it. If the IP address changes my SSH client will warn me - this is a useful security feature to protect against someone 'swapping' the machine you are connecting to for another one! After typing yes and pressing enter to continue connecting, I will see Bash running in my AWS Linux virtual machine! Warning: Permanently added 'ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com' (ED25519) to the list of known hosts. __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\\___|___|https://aws.amazon.com/amazon-linux-2/[ec2-user@ip-172-31-23-196 ~]$ I have a welcome message from AWS and are running a shell on my virtual machine, via the ssh program from my local machine! We can see the current shell being used by checking the SHELL variable: [ec2-user@ip-172-31-23-196 ~]$ echo $SHELL/bin/bash We are now ready to run any commands that we like on this machine - at this point you might install programs, manipulate files and so on. When I want to disconnect, I can just run the exit command to close the connection to the virtual machine. Hopefully if you have followed this far, you also have access to a virtual machine to play with. Now let's look at some of the things we can do with the SSH to make accessing these machines even easier!","s":"Using SSH to connect to a virtual machine","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#using-ssh-to-connect-to-a-virtual-machine","p":881},{"i":900,"t":"It is possible that when you try to use a private key to connect to a virtual machine, you will see an error message like the below: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: UNPROTECTED PRIVATE KEY FILE! @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Permissions 0644 for 'my-key.pem' are too open.It is recommended that your private key files are NOT accessible by others.This private key will be ignored.bad permissions: ignore key: my-key.pemPermission denied (publickey). In this case the server is warning you that your private key could be opened by other users on the system. You can verify this by running ls -a to check the file permissions: $ ls -al-rw-r--r-- 1 dwmkerr dwmkerr 1103 Apr 19 12:02 my-key.pem If you recall that the permissions are the first thing that is shown, you'll see that they are -rw-r--r--. This translates to: -rw - read and write for the owner, which is the dwmkerr user -r- - read to the group the owner belongs to, which is the dwmkerr group -r- - read to all users We can see that this is not ideal - other users on the system would be allowed to open the file, and members of the same group would also be allowed to read the file. There is a quick fix for this - set the permissions to read and write for the owner only: $ chmod 400 my-key.pem$ ls -al-rw------- 1 dwmkerr dwmkerr 1103 Apr 19 12:02 my-key.pem The chmod (change file permissions) command is used here to set the permissions to 400 - which means read and write for the current user. Once this change is made you will be able to SSH into the server without it complaining about the permissions on your key.","s":"Dealing with Key Permission Errors","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#dealing-with-key-permission-errors","p":881},{"i":902,"t":"It can be difficult to remember the details such as the host name, location of the key and username of your virtual machines. One thing you can do to make it far easier to connect is to create an entry in your SSH Config file that stores this information. This will let you connect much more quickly. To create an entry for my virtual machine, I would add the following text to the ~/.ssh/config file: Host effective-shell-aws-linux HostName ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com User ec2-user IdentityFile \"~/.ssh/effective-shell.pem\" The first part of this configuration is the host 'alias' - how you will refer to the host when you want to connect to it. It can be convenient to give this a short but descriptive name. After this, we have a set of settings, each indented by a tab or two spaces, the settings we have are: HostName the full address of the host User the name of the user to connect as IdentityFile the path to the private key file used to connect Now if I want to connect to the virtual machine, all I need to do is run the following command: ssh effective-shell-aws-linux In fact the ssh program supports shell completion, meaning I can just type ssh and a few letters of the host then the tab key - the shell will suggest the hosts from my config, so I don't even need to remember the name I set for it: ssh e # when I press tab, the shell expands this to:ssh effective-shell-aws-linux There are many other options available for the SSH config file, you can see them all with man ssh_config. We'll see some other options in Chapter 33 - Master the Multiplexer.","s":"Configuring SSH Hosts","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#configuring-ssh-hosts","p":881},{"i":904,"t":"You don't need to actually run a shell on a remote machine over SSH to execute commands. You can simply provide the commands that you want to run to the ssh program and it will execute them on the server. Here's an example: $ ssh effective-shell-aws-linux 'curl effective.sh | ES_EXISTING_FOLDER_ACTION=o sh'...effective-shell: installed samples version 0.25.1 to '/home/ec2-user/effective-shell'effective-shell: read 'effective shell' online at: www.effective-shell.com In this example we downloaded and ran the Effective Shell samples installer on the server. Now normally when we install the samples, the installer will ask the user whether to overwrite, delete or keep the existing samples. This means that it will be requesting input from the terminal. The ssh program is not actually attaching stdin to the remote machine, so we use the ES_EXISTING_FOLDER_ACTION=o option to tell the installer to overwrite the samples. If we wanted to be able to interact with the server, using our terminal to provide input, we can use the -t (request TTY) parameter: $ ssh -t effective-shell-aws-linux 'curl effective.sh | sh'...effective-shell: downloaded samples, version 0.25.1effective-shell: preparing to install the 'effective-shell.com' samples...effective-shell: the '/home/ec2-user/effective-shell' folder already exists, would you like to:effective-shell: [d]elete - remove the existing foldereffective-shell: [o]verwrite - extract over the existing foldereffective-shell: [q]uitYour choice (d/o/q): d In this example my terminal is attached to the remote server via SSH, meaning I can use the keyboard to provide input to the installer script.","s":"Running SSH Commands","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#running-ssh-commands","p":881},{"i":906,"t":"One thing that will soon become a pain if you are regularly SSH-ing into virtual machines is disconnections. This can occur when you lose network connectivity. You might not even notice that a disconnection has occurred - I find it is more common that the ssh session is simply frozen and not responding to any input at all. It is annoying when this happens - often the shell is so unresponsive that you cannot even free it by pressing ^D or ^C. There is actually an escape sequence that you can use to end a broken session, which is the Enter then ~ then .. In Chapter 33 - Master the Multiplexer we'll look at some great ways to improve upon this, for now if you forget the Enter/Tidle/Dot escape sequence the easiest thing to do is to close your terminal program. When you disconnect from your SSH session, the commands you are running will be terminated. This is because your shell will send the 'hang up' signal. Again, this can be frustrating if you actually want to keep something running on the server. We'll also look at how to work around this behaviour in Chapter 33.","s":"Handling Disconnections","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#handling-disconnections","p":881},{"i":908,"t":"We can use the scp (OpenSSH secure file copy) program to copy files to and from any remote machine that we have access to. The format of the scp command is: scp [parameters] We can provide scp with parameters, many of which are the same as the ssh command, such as -i for the Identity File. The source and destination are normally a pair of designations, one local, one remote. However, you can also copy between two remote machines. Some examples of how a command might look like are: scp -i ~/.ssh/my-project.pem ~/project/output.zip dwmkerr@myserver.com:~ This uses the -i flag to specify a key The source is a file called output.zip on the local machine The destination is a server with the hostname myserver.com, connecting with a user named dwmkerr, and putting the file in the user's home folder scp -P 8022 effective-shell.com:~/downloads/backup.zip ~/backups This copies the downloads/backups folder to the local ~/backups folder No username or credentials are specified, so the current user's name is used and any keys that are loaded in the SSH agent will be used to try and authenticate We use the -P (port number) flag to specify port 8022, rather than the default port which is 22 Let's see this in action by copying the lookup tool we created in Chapter 30 - How to Avoid Scripting to our server. Then we'll run it, save some definitions, and copy them back to our local machine. First we'll use scp to copy our lookup program to our remote machine: $ cd ~/effective-shell/samples$ scp ./programs/lookup/lookup.py effective-shell-aws-linux:~lookup.py 100% 4485 10.5KB/s 00:00 We have provided the path to the local file, and specified the server we want to copy the file to, as well as the location for the file (which is the home directory). Note that because we have created the alias effective-shell-aws-linux in our SSH config, we can use this alias for scp as well as ssh. Let's connect to the server now and run the script: $ ssh effective-shell-aws-linuxLast login: Tue Apr 5 19:03:00 2022 from bb116-15-249-218.singnet.com.sg __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\\___|___|https://aws.amazon.com/amazon-linux-2/[ec2-user@ip-172-31-23-196 ~]$ ./lookup.py cryptographycryptography: The discipline concerned with communication security (eg, confidentiality of messages, integrity of messages, sender authentication, non-repudiation of messages, and many other related issues), regardless of the used medium such as pencil and paper or computers. We can see that our lookup program has been copied to our server and we can run it to find a definition for the word 'cryptography'. Let's save this definition then close our connection to the server, then copy the definition back to our local machine: [ec2-user@ip-172-31-23-196 ~]$ chmod +x ./lookup.py[ec2-user@ip-172-31-23-196 ~]$ ./lookup.py cryptography > definition.txt[ec2-user@ip-172-31-23-196 ~]$ exitlogoutConnection to ec2-13-213-71-135.ap-southeast-1.compute.amazonaws.com closed.$ scp effective-shell-aws-linux:~/defintion.txt .definition.txt 100% 277 1.0KB/s 00:00$ cat definition.txtcryptography: The discipline concerned with communication security (eg, confidentiality of messages, integrity of messages, sender authentication, non-repudiation of messages, and many other related issues), regardless of the used medium such as pencil and paper or computers. Before we ran the script on the server, we used the chmod (change file permissions) command to ensure the script can be executed. That's all there is to it! Copying files and folders to and from remote machines is remarkably easy to do with scp once you know the basics of how ssh works. There are many other operations that you can perform with scp, you can read more about the tool with man scp.","s":"Transferring Files with SCP","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#transferring-files-with-scp","p":881},{"i":910,"t":"Once you are familiar with the core techniques introduced in this chapter, I would suggest that you look into configuration for bastion hosts, port forwarding and VPN tunneling - these are two more advanced scenarios for SSH. As a teaser for when you might want to use some of these techniques, consider the diagram below: In this scenario we have two hosts, host1.myproject and host2.myproject in a private part of a network, that is not open to the internet. To access these hosts, we use a 'jump box' or 'bastion' - a host that is running in a part of the network that is accessible to external users. We carefully control access to this box and use it as way to connect to the private host. Now you could SSH onto the bastion, and then SSH onto either of the hosts. But you can speed this process up by setting your ~/.ssh/config like so: Host bastion HostName bastion.myproject.com IdentityFile ~/.ssh/myproject.pem ForwardAgent yesHost *.myproject ProxyJump bastion With this configuration we can connect to either of the private hosts by simply using the ssh command with the host name: ssh host1.internal The ssh program recognises that host1.internal matches the patter *.myproject from our configuration file. It then sees that we have set the ProxyJump option, which instructs ssh to connect through another host. The other host is the bastion, which is accessible to the user, and has the key specified. We also tell the bastion that it should have its authentication forwarded, so that our private key is used when connecting to the host in the private network (without this option when we connect to the bastion we will not have our keys accessible). This network configuration is very common and you can see the SSH configuration file supports quickly and conveniently configuring the SSH agent to use the bastion. There are many advanced options for SSH that will make more complex tasks like this easier to manage.","s":"Next Steps","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#next-steps","p":881},{"i":912,"t":"In this chapter we discussed the SSH protocol, and how keys are used to protect connections to remote servers. We saw how to setup an AWS account, create a virtual machine with a given public key, connect to it with the ssh program, and configure SSH with an alias to make future connections faster. We also saw some of the challenges we can face with network connectivity - which we'll see techniques to handle in Chapter 33. Finally, we looked at how to copy files to and from remote machines. If you want to go deeper, my favourite book on this topic is \"Applied Cryptography: Protocols, Algorithms, and Source Code in C - Bruce Schneier\". There are more details at the end of the chapter and in the Recommended Reading section.↩ This process is very useful to know about, it is called Diffie–Hellman key exchange. There are many great articles online that explain it in detail.↩","s":"Summary","u":"/part-6-advanced-techniques/the-secure-shell/","h":"#summary","p":881},{"i":914,"t":"Work in Progress! 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! 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. 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).","s":"Work in Progress!","u":"/work-in-progress","h":"","p":913},{"i":916,"t":"On this page","s":"essential-manpages","u":"/xx-appendices/essential-manpages","h":"","p":915},{"i":918,"t":"As an appendix, or printed reference, list of the top ten manpages? man re_pattern - basic and extended regex patterns man test is an excellent way to quickly check common tests (existence of a file etc) man set is super useful when checking options like set -ex in scripts man re_format man getopt man XXX show signal commands (Ctrl+V etc) man bash search for ARITHMETIC\\ EVALUATION to find how arithmetic operators work in bash man bash search GRAMMAR for pipelines, if statements, conditionals, loops, lists and so on man bash search for ^EXPANSION to see all shell expansion operators man bash search for ^INVOCATION to find details on startup and the startup files that are read man bash search for ^[ ]+shopt to find descriptions of shell options Essential Bash Manpages Manpage Search Description man bash ^PROMPTING Details on how to set the PS1 prompt. Essential Z-Shell Manpages Manpage Search Description man zshmisc PROMPT\\ SEQUENCES Details on how to set the PS1 prompt.","s":"The Most Important Manpages","u":"/xx-appendices/essential-manpages","h":"#the-most-important-manpages","p":915},{"i":921,"t":"On this page","s":"Installing the Samples","u":"/xx-appendices/installing-samples/","h":"","p":920},{"i":923,"t":"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: https://effective-shell.com/downloads/effective-shell.zip You can also download them as a tarball from: https://effective-shell.com/downloads/effective-shell.tar.gz","s":"Manually Downloading the Samples","u":"/xx-appendices/installing-samples/","h":"#manually-downloading-the-samples","p":920},{"i":925,"t":"If you have installed the samples to the default location, you can safely delete them with the following command: rm -rf ~/effective-shell","s":"Deleting the Samples","u":"/xx-appendices/installing-samples/","h":"#deleting-the-samples","p":920},{"i":927,"t":"index-of-commands Command Description cd ls pwd mkdir rm rmdir cd cd pstree -p $$ Show the process tree for the current shell process. Shell Configuration shopt Set or unset a shell option. chsh Change the shell for a user. User Management useradd -m name Add user with the name name. -m creates a home directory name. passwd name Set the password for user name. userdel name Remove user with the name name. usermod -aG sudo name Make name a sudoer. --------------------------------- --------------------------------------------------------------------------------------------- Git git init Initialise a new Git Repository. git status Show the status of the working tree and index. git add Stage files - you can use patterns and wildcards. git reset Unstage files - you can use patterns and wildcards. git rm --cached Unstage files - you can use patterns and wildcards. git commit Create a commit from the current index - the shell editor will open for the commit message. git commit -m 'message' Create a commit with message message. git commit -a Stage and commit all changes in the working tree. git checkout Checkout a branch called branch. git checkout -b branch Create and checkout a new branch called branch. git branch Create a branch called name but do not check it out. git branch -m Change the current branch name to new_name. git merge Merge the branch named branch into the current branch. git log Show the log of commits. git log --oneline --branch Show the log of commits, one line per commit, with the branch graph. git rm Stage the removal of files from the repostiry - you can use patterns and wildcards. git mv Stage the movement of source to destination. git checkout 8342bec Checkout a commit with SHA 834bec. git checkout HEAD~1 Move the current HEAD back one commit. git checkout ~3 Checkout branch, move back three commits from the tip. grep mkdir rm rmdir touch cat watch tail head less more most echo timeout until pwd -P (physical, shows symlinks)","s":"index-of-commands","u":"/xx-appendices/index-of-commands","h":"","p":926},{"i":929,"t":"On this page","s":"Posix","u":"/xx-appendices/posix","h":"","p":928},{"i":931,"t":"Really good reading here: https://stackoverflow.com/questions/5725296/difference-between-sh-and-bash Note that on modern Distros you are often using dash: $ file -h /bin/sh/bin/sh: symbolic link to dash","s":"Posix Shells","u":"/xx-appendices/posix","h":"#posix-shells","p":928},{"i":933,"t":"On this page","s":"Recommended Reading","u":"/xx-appendices/recommended-reading/","h":"","p":932},{"i":935,"t":"The Changelog - Making the command line glamorous The Changelog - Warp wants to be the terminal of the future The Changelog - The terminal as a platform GitHub The ReadME Project - Building the future of the command line GitHub Copilot for the Command Line is amazing!","s":"The Future of the Shell","u":"/xx-appendices/recommended-reading/","h":"#the-future-of-the-shell","p":932},{"i":937,"t":"The Changelog - Efficient Linux at the CLI","s":"General Shell Skills","u":"/xx-appendices/recommended-reading/","h":"#general-shell-skills","p":932},{"i":939,"t":"Each of these books would make a great addition to your bookshelf if you are a technologist. Many of these books I have read multiple times and still go back to as a reference. Applied Cryptography: Protocols, Algorithms, and Source Code in C - Bruce Schneier This is the absolute best book around on cryptography - from concepts, protocols all the way to advanced topics. There are code examples in C that allow you to really see how these concepts work in practice. This is an excellent book for someone who wants to learn about cryptography but also have the option to go deep into the topics that interest them. Pro Git - Scott Chacon and Ben Straub Superb book on Git, that is essential reading for the new user, but also goes into great depth on topics that will be relevant for expert readers. Should be on everyone's bookshelf.","s":"Fantastic Books","u":"/xx-appendices/recommended-reading/","h":"#fantastic-books","p":932},{"i":941,"t":"Short and sweet, this is a good book for absolute shell scripting beginners.","s":"Shell Scripting by Jason Cannon","u":"/xx-appendices/recommended-reading/","h":"#shell-scripting-by-jason-cannon","p":932},{"i":943,"t":"Ideal for systems administrators and power users who can benefit from automating processes across many platforms. This book contains a lot of tips on how to standardise the behaviour of programs across Linux and Unix systems. If you find yourself regularly shell scripting and want to start to build a library of your own scripts to run across machines, this is a great book to read. It will be particularly useful for anyone who faces challenges on incompatibilities and inconsistencies between programs on different systems.","s":"Wicked Cool Shell Scripts - Dave Taylor & Brandon Perry","u":"/xx-appendices/recommended-reading/","h":"#wicked-cool-shell-scripts---dave-taylor--brandon-perry","p":932},{"i":945,"t":"Absolutely the best book I've read on Vim, perfect for users of all levels. Written by Drew Niel, who is the author of the amazing Vimcasts series.","s":"Practical Vim: Edit Text at the Speed of Thought, Drew Niel","u":"/xx-appendices/recommended-reading/","h":"#practical-vim-edit-text-at-the-speed-of-thought-drew-niel","p":932},{"i":947,"t":"This is a wonderful repository, which aims to help you \"Master the command line, in one page\". This page is full of useful resources and is a superb reference for users from novice all the way to advanced!","s":"Joshua Levy - The Art of the Command Line","u":"/xx-appendices/recommended-reading/","h":"#joshua-levy---the-art-of-the-command-line","p":932},{"i":949,"t":"These resources are available online and are particularly useful.","s":"Essential Online Resources","u":"/xx-appendices/recommended-reading/","h":"#essential-online-resources","p":932},{"i":951,"t":"An open-source guide to help you write better command-line programs, taking traditional UNIX principles and updating them for the modern day. This is an excellent online resource that describes the principles, but also practical patterns, that you can use to design CLI programs that interface well to other programs and make sense to human operators. This is great reading if you are building any kind of CLI app. https://clig.dev/","s":"Command Line Interface Guidelines","u":"/xx-appendices/recommended-reading/","h":"#command-line-interface-guidelines","p":932},{"i":953,"t":"The introduction in the site says it better than I could! Classes teach you all about advanced topics within CS, from operating systems to machine learning, but there's one critical subject that's rarely covered, and is instead left to students to figure out on their own: proficiency with their tools. We’ll teach you how to master the command-line, use a powerful text editor, use fancy features of version control systems, and much more! https://missing.csail.mit.edu/ Thanks to Lennart R. Wilke for sharing this with me!","s":"The Missing Semester of Your CS Education","u":"/xx-appendices/recommended-reading/","h":"#the-missing-semester-of-your-cs-education","p":932},{"i":955,"t":"This is an excellent and very detailed resource on bash. It goes into a lot of detail on how things are implemented and is a great resource to find all of the low level details you might be interested in. There is also a more modern version being currently drafted at https://guide.bash.academy/.","s":"Maarten Billemont - Bash Guide","u":"/xx-appendices/recommended-reading/","h":"#maarten-billemont---bash-guide","p":932},{"i":959,"t":"This is a fascinating video from the late 60s - you might be amazed at how much of the stuff you see here is still fundamental to how we work with computers today. The shell, pipelines, the file system and more:","s":"The UNIX Operating System","u":"/xx-appendices/recommended-reading/","h":"#the-unix-operating-system","p":932},{"i":961,"t":"Another fascinating video from the late 60s - see the mouse, hypertext, word processing and more:","s":"The Mother of All Demos","u":"/xx-appendices/recommended-reading/","h":"#the-mother-of-all-demos","p":932},{"i":963,"t":"Professor Brian Kerninghan explains where the grep tool came from:","s":"Where Grep Came From - Computerphile","u":"/xx-appendices/recommended-reading/","h":"#where-grep-came-from---computerphile","p":932},{"i":965,"t":"The #! magic, details about the shebang/hash-bang mechanism on various Unix flavours - Excellent article on the internals of how different Unix platforms handle shebangs.","s":"Advanced","u":"/xx-appendices/recommended-reading/","h":"#advanced","p":932},{"i":967,"t":"The following articles and links were particularly useful when writing this book: Great source of shell tricks and tips: https://twitter.com/krisnova/status/1109618657305333761?s=11 Useful Linux commands: https://www.thegeekstuff.com/2010/11/50-linux-commands/","s":"Research","u":"/xx-appendices/recommended-reading/","h":"#research","p":932},{"i":969,"t":"Books I'm reading as part of the research for this book. How Linux Works, 2nd Edition: What Every Superuser Should Know - Brian Ward Wicked Cool Shell Scripts, 2nd Edition: 101 Scripts for Linux, OS X, and UNIX Systems - Dave Taylor The Linux Command Line: A Complete Introduction - William E. Shotts Jr. Modern Vim: Craft Your Development Environment with Vim 8 and Neovim, Drew Niel Serious Cryptography: A Practical Introduction to Modern Encryption by Jean-Philippe Aumasson","s":"TODO","u":"/xx-appendices/recommended-reading/","h":"#todo","p":932},{"i":971,"t":"On this page","s":"shell-parameter-expansion","u":"/xx-appendices/shell-parameter-expansion","h":"","p":970},{"i":973,"t":"Need to update Chapter 19 to link to this.","s":"TODO","u":"/xx-appendices/shell-parameter-expansion","h":"#todo","p":970},{"i":975,"t":"the-future 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. Scattered notes so far: A Terminal for the Graphical Age - Great article on an ASCII terminal with modern features. Very interesting project. Another interesting project is Nu: https://github.com/nushell/nushell There's a great podcast about Nu here: https://changelog.com/podcast/363","s":"the-future","u":"/xx-appendices/the-future","h":"","p":974},{"i":977,"t":"On this page","s":"Images and Diagrams","u":"/zz-developer-guide/images-and-diagrams","h":"","p":976},{"i":979,"t":"Not in Use Ideal Image is not currently enabled as it appears to clash with native Docusaurus lazy-loading. The @docusaurus/plugin-ideal-image is used to allow lazy loading and zoom of images. Use the Image tag as shown: import Image from '@theme/IdealImage'; This will render an image as shown below: ::: warn Currently Disabled :::","s":"Images","u":"/zz-developer-guide/images-and-diagrams","h":"#images","p":976},{"i":981,"t":"Render a Draw.io diagram like so: import Drawio from '@theme/Drawio'import asymmetricEncryption from '!!raw-loader!./images/asymmetric-encryption.drawio'; This would render as below:","s":"Diagrams","u":"/zz-developer-guide/images-and-diagrams","h":"#diagrams","p":976},{"i":983,"t":"On this page","s":"Components","u":"/zz-developer-guide/components","h":"","p":982},{"i":985,"t":"The AsciinemaPlayer component is renders an asciinema recording. Note that the recording file must be available to be served to the browser, so it will normally live somewhere in the static folder. First, import the component: import AsciinemaPlayer from '@site/src/components/AsciinemaPlayer/AsciinemaPlayer.tsc'; Use the component as below: Result Loading... Live Editor The bulk of the properties that are exposed are directly from the asciinema-player component - this page has a more detailed description of many of the properties listed below. The following properties are exposed: Property Usage src The location of the cast file, must be available from the browser. style Any additional CSS styles to apply. cols The number of columns in the player's terminal. rows The number of rows in the player's terminal. autoPlay Set this option to true if playback should start automatically. preload Set this option to true if the recording should be preloaded on player's initialization. loop 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. startAt Start playback at a given time. speed Playback speed. The value of 2 means 2x faster. idleTimeLimit Limit terminal inactivity to a given number of seconds. theme Terminal color theme. poster Poster (a preview frame) to display until the playback is started. fit Controls the player's fitting (sizing) behaviour inside its container element. fontSize Size of the terminal font.","s":"AsciinemaPlayer","u":"/zz-developer-guide/components","h":"#asciinemaplayer","p":982},{"i":987,"t":"The 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. First, import the component: import AnnotatedCommand from '@site/src/components/AnnotatedCommand/AnnotatedCommand.tsc'; Use the component as below: Result Loading... Live Editor gg2cw gg2cw The following properties are exposed: Property Usage annotation The text to show beneath the command.","s":"AnnotatedCommand","u":"/zz-developer-guide/components","h":"#annotatedcommand","p":982},{"i":989,"t":"The 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. First, import the component: import Caret from '@site/src/components/Caret/Caret.tsx'; Use the component as below: Result Loading... Live Editor def search_for_word(word): def search_for_word(word): The following properties are exposed: Property Usage caretStyle The style of the cursor, block by default, or line for an 'insert mode' cursor.","s":"Caret","u":"/zz-developer-guide/components","h":"#caret","p":982},{"i":991,"t":"Check the following resources for useful tips on Component Development: Docusaurus - Styling and Layout Docusaurus - Code Blocks - Interactive code editor","s":"Tips for Developing Components","u":"/zz-developer-guide/components","h":"#tips-for-developing-components","p":982},{"i":993,"t":"On this page","s":"Recording Terminal Sessions","u":"/zz-developer-guide/recording-terminal-sessions","h":"","p":992},{"i":995,"t":"The asciinema tool can record the output of terminal sessions. You can see the results in action in pages like Chapter 33 - Master the Multiplexer. Some tips for working with asciinema: To record a Tmux session, you will need to start detached from Tmux and then attach. You can do this by hand, simply using tmux attach, but this adds some noise to the beginning of the recording. A better way is to use the command below: asciinema rec --command \"tmux attach [-t session-name]\"","s":"Asciinema","u":"/zz-developer-guide/recording-terminal-sessions","h":"#asciinema","p":992},{"i":997,"t":"Record a shell session by running: # Start recording...script recording.txt# ...run your commands...# Finish the recording.exit Once you have this recording, you can use it to rapidly record an asciinema file: asciinema rec --command \"tmux attach [-t session-name] && scriptreplay recording.txt\" It can be helpful to 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 scriptreplay_ng tool.","s":"Script Recording","u":"/zz-developer-guide/recording-terminal-sessions","h":"#script-recording","p":992},{"i":999,"t":"On this page","s":"Markdown","u":"/zz-developer-guide/markdown-guide","h":"","p":998},{"i":1001,"t":"TODO: links should be explicit e.g. 00-index.md rather than index.md","s":"Links","u":"/zz-developer-guide/markdown-guide","h":"#links","p":998},{"i":1003,"t":"TODO: Use single stars for italics and double stars for bold - because Markua confusingly uses single underscores for underline.","s":"Emphasis","u":"/zz-developer-guide/markdown-guide","h":"#emphasis","p":998},{"i":1005,"t":"TODO","s":"Asides / Admonitions / Blurbs","u":"/zz-developer-guide/markdown-guide","h":"#asides--admonitions--blurbs","p":998},{"i":1007,"t":"TODO frontmatter marks content that is considered part of the introduction or preface. It is numbered in the manuscript with numerals such as i, ii, xii. mainmatter indicates content that is part of the main book, it is numbered with numerals. backmatter todo","s":"Markua Tags","u":"/zz-developer-guide/markdown-guide","h":"#markua-tags","p":998},{"i":1009,"t":"Frontmatter","s":"Titles","u":"/zz-developer-guide/markdown-guide","h":"#titles","p":998},{"i":1011,"t":"TODO image names must be unique","s":"Images","u":"/zz-developer-guide/markdown-guide","h":"#images","p":998},{"i":1013,"t":"Not in Use Ideal Image is not currently enabled as it appears to clash with native Docusaurus lazy-loading. The @docusaurus/plugin-ideal-image is used to allow lazy loading and zoom of images. Use the Image tag as shown: import Image from '@theme/IdealImage'; This will render an image as shown below: ::: warn Currently Disabled :::","s":"Images","u":"/zz-developer-guide/markdown-guide","h":"#images-1","p":998},{"i":1015,"t":"Render a Draw.io diagram like so: import Drawio from '@theme/Drawio'import asymmetricEncryption from '!!raw-loader!./images/asymmetric-encryption.drawio'; This would render as below:","s":"Diagrams","u":"/zz-developer-guide/markdown-guide","h":"#diagrams","p":998},{"i":1017,"t":"On this page","s":"Understanding Shell Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"","p":1016},{"i":1019,"t":"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. The seven operations that the shell performs are: Brace Expansion - expanding values between braces, such as file{1..3} into file1 file2 file3 Tilde Expansion - expanding the ~ tilde symbol for the home directory into the path to the home directory, such as ~/effective-shell into /home/dwmkerr/effective-shell Parameter Expansion - expanding terms that start with a $ symbol into parameter values, such as $HOME into the value of the variable named HOME Command Substitution - evaluation of the contents of $(command) sequences, which are used to run commands and return the results to the shell command line Arithmetic Expansion - evaluation of the contents of $((expression)) sequences, which are used to perform basic mathematical operations 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 Pathname Expansion - the shell expands wildcards and special characters in pathnames, such as file*.txt into the set of files that are matched by the sequence If you want to see each of these operations in the manual, you can run man bash and search for the text ^EXPANSION. Now let's see how each operation works in more detail.","s":"What is Shell Expansion?","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#what-is-shell-expansion","p":1016},{"i":1021,"t":"Let's take a look through each of the forms of shell expansion that are available to use.","s":"Shell Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#shell-expansion","p":1016},{"i":1023,"t":"Brace expansion is the first shell expansion operation that occurs, it expands a simple expression that represents a sequence or range of characters. 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: mkdir /tmp/{one,two,three}# The line above is expanded to:mdkir /tmp/one /tmp/two /tmp/three Expansions of sets like this are a great way to perform operations that work on multiple files or folders at once. We can also create sequences of numbers or characters: touch file{1..5}.txt# The line above is expanded to:touch file1.txt file2.txt file3.txt file4.txt file5.txt 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: for x in {0..10..2}; do print $x; done# The line above is expanded to:for x in 0 2 4 6 8 10; do print $x; done","s":"Brace Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#brace-expansion","p":1016},{"i":1025,"t":"If a word starts with a ~ tilde character, then the shell will expand the tilde into the value of the $HOME variable: cd ~/effective-shell# The line above is expanded to:cd $HOME/effective-shell If we were to unset the $HOME variable, then the expansion would use the current user's home directory: unset HOMEcd ~/effective-shell# The line above is expanded to:cd /home/dwmkerr/effective-shell Tilde expansion is very simple!","s":"Tilde Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#tilde-expansion","p":1016},{"i":1027,"t":"When the dollar symbol $ is used, this indicates that the shell is going to perform parameter expansion, which expands variables or the parameters of a script. It can also be used to indicate command substitution or arithmetic expansion - which we will see once we've looked at parameter expansion. A lot of these expansions are covered in detail in Chapter 19 - Variables, Reading Input, and Mathematics but I have included each of the available expansions here for reference. In its most simple form, parameter expansion simple replaces the name of a variable or parameter with its value: fruit=applesecho \"I like $fruit\"# The line above is expanded to:echo \"I like apples\" 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: echo \"My backup folder is: ${HOME}backup\"# The line above is expanded to:echo \"My backup folder is: /home/dwmkerrbackup\" If we had not used braces, then the shell would expand the expression like so: echo \"My backup folder is: $HOMEbackup\"# The line above is expanded to:echo \"My backup folder is: \" 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 HOMEbackup - the braces used in the first example make it clear to the shell that the parameter name is HOME and that the text backup should be added at the end of the expanded value. 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. Default Values The expression ${parameter:-default} will expand to the value of the parameter named parameter - but if that value is not set, then the value 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. Assign Default Values The expression ${parameter:=default} will expand to the value of the parameter named parameter - but if that value is not set, then the value default is used. In this case, parameter is also set to default. This means that this expression works just like the 'default values' expression above, but also sets the parameter at the same time. Display Error if Null or Unset The expression ${parameter:?message} tells the shell to expand to the value of parameter, and if that value is null or unset, to instead write the message message to standard error and exit (unless the shell is interactive, in which case the shell is not closed). 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: backup_location=${BACKUP_DIR:?Please set BACKUP_DIR to use this script}cp -r ~/effective-shell ${BACKUP_DIR} In this script we copy the ~/effective-shell folder to the folder set in the 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 BACKUP_DIR parameter must be set. Use Alternate Value The expression ${parameter:+alternate} expands to an empty string if parameter is null or unset. However, if parameter has a value, then the value of alternate is used instead. Offset and Length You can tell the shell to expand only a subset of the value of a parameter by using the ${parameter:offset} expression. In this case, the shell will expand the value of parameter, but skip offset number of characters from the beginning: echo \"My home folder name is: ${HOME:6}\"# The line above is expanded to:echo \"My home folder name is: dwmkerr\" You can also specify how many characters should be used by providing a length value after the offset with the expression ${parameter:offset:length}: echo \"The error message is: ${error_message:0:64}\" In the expression above, only up to the first 64 characters of the parameter error_message will be shown. The offset and length values can also be used with arrays: days=(\"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\" \"Sunday\")echo \"${days[@]:2:3}\"# The line above is expanded to:echo \"Tuesday Wednesday Thursday\" It is important to note that when using this technique with arrays, you must specify the array name and then [@] after the array name, to indicate that you want to work with all of the members of the array. If you don't do this, the entire array is converted into a single string and the resulting string has the offset and length applied. Expand Variable Names The ${!name*} expression evaluates to the name of every parameter that starts with the text name. You can use this expression to find the full set of parameters that match a certain pattern. 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: _es_download_folder=~/downloads_es_backup_folder=~/backups_es_download_address=https://effective-shell.com/downloads/effective-shell-samples.tar.gz# At this point we might have a script that uses the variables above...# Now clean up any variables we created.for var_name in ${!_es_*}do echo \"Cleaning up: ${var_name}...\" unset ${var_name}done This is rather an advanced technique but it does show how the 'expand variable names' expansion can be useful. Array Expansion This topic is covered in detail in Chapter 19. The expression ${!array[@} expands to the indices (or 'keys') for each item in an array: days=(\"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\" \"Sunday\")echo \"${!days[@]}\"# The line above is expanded to:echo \"0 1 2 3 4 5 6\" This expansion is convenient if you do not know the keys that make up an array and want to loop through them. Parameter Length The ${#parameter} expression expands to the length of the value in the parameter named parameter. You can also use this expression to find the length of an array - just add the [@] subscript like so ${#array[@]}: days=(\"Monday\" \"Tuesday\" \"Wednesday\" \"Thursday\" \"Friday\" \"Saturday\" \"Sunday\")echo \"There are ${#days[@]} days in the array\"# The line above is expanded to:echo \"There are 7 days in the array\" You may have noticed at pattern by this point - many of the expansions that can be performed on a parameter can also be performed on an array, just by adding the [@] 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. Remove Pattern from Front You can use the ${parameter#pattern} expression to expand the value of parameter, removing pattern from the front of the value: address=https://effective-shell.comecho \"Address: ${address#https://}\"# The line above is expanded to:echo \"Address: effective-shell.com\" You can also tell the shell to remove as many sequential matches of pattern as possible, by using the ${parameter##pattern} expression. This can be useful to strip out all of the characters up to a certain point in a parameter: folder=/home/dwmkerr/backups/2021-10-19echo \"Today's backup folder is: ${folder##*/}\"# The line above is expanded to:echo \"Today's backup folder is: 2021-10-19\" Notice that in this example we are using an asterisk * 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 / is found. Remove Pattern from Back The ${parameter%pattern} expression works exactly like the expression above, but removes text from the end of a parameter: echo \"My working directory is: ${PWD}\"echo \"My parent folder is: ${PWD%/*}\"# The lines above are expanded to:echo \"My working directory is: /home/dwmkerr/repos/github/dwmkerr/effective-shell\"echo \"My parent folder is: /home/dwmkerr/repos/github/dwmkerr\" In this example we used an asterisk * wildcard in the pattern to remove all of the text from the back of the parameter, up to and including the first forward-slash / symbol found. We can also remove as many matches as possible, by using the expression ${parameter%%pattern}: archive=effective-shell.tar.gzecho \"Name of archive is: ${archive%%.*}\"# The line above is expanded to:echo \"Name of archive is: effective-shell\" Notice that in this case the removal of the characters did not stop at the first period . symbol, it removed as many characters as possible until the last period . symbol was found. Pattern Replacement You can also replace a pattern in a parameter by using the expression ${parameter/pattern/string}. This can be used to perform substitutions: message=\"Hello Dave\"echo \"${message/Hello/Goodbye}\"# The line above is expanded to:echo \"Goodbye Dave\" 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 tr or 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. For suggestions on alternative ways to manipulate text check Chapter 15 - Slice and Dice Text or Chapter 16 - Advanced Text Manipulation with Sed. Lowercase or Uppercase You can use the ${parameter^^} expression to return the value of parameter converted to uppercase. You can also use the ${parameter,,} expression to return the value of parameter converted to lowercase. An example is below: message=\"Hello Reader\"echo ${message^^}echo ${message,,} The output of this script is: HELLO READERhello reader Parameter Indirection If you want to get the value of a parameter that has an arbitrary name you can use the ${!parameter_name} expression. This will return the value of the parameter that has the name of the value of parameter_name - you can see this in action like so: parameter_name=\"HOME\"echo \"${!parameter_name}\" The output of this script is: /home/dwmkerr This can be very useful if you are writing scripts that will work with arbitrary or variable parameter names. 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 Chapter 19 - Variables, Reading Input, and Mathematics.","s":"Parameter Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#parameter-expansion","p":1016},{"i":1029,"t":"The second form of expansion that starts with a dollar $ symbol is command substitution. This form of expansion instructs the shell to run a specific command. The syntax is simply $(comand). We have seen command substitution throughout the book - in the example below we expand the date command to print the current date: echo \"The date is: $(date)\"# The line above is expanded to:echo \"The date is: Tue Oct 19 16:49:07 +08 2021\" 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: archives=$(find ~/downloads -type f -name \"*.tar.gz\") In this command we store the results of the find operation in the parameter named archives. 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: archives=`find ~/downloads -type f -name \"*.tar.gz\"` You may see this syntax from time to time, however I would suggest that you avoid it. The reason is that you cannot 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 result=$(command1 $(command2)).","s":"Command Substitution","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#command-substitution","p":1016},{"i":1031,"t":"The final form of shell expansion that starts with a dollar symbol $ is arithmetic expansion. This expansion can be used to perform simple arithmetic expressions: echo \"The result of 23*4 is: $((23*4))\"echo \"The result of 23*4 is: 92\" Arithmetic expansion is covered in detail in Chapter 19 - Variables, Reading Input, and Mathematics.","s":"Arithmetic Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#arithmetic-expansion","p":1016},{"i":1033,"t":"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 any expansion that starts with a dollar symbol $ and that does not occur within double quotes. The fact that word splitting only occurs if a substitution does 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. 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: days=\"Monday Tuesday Wednesday Thursday Friday Saturday Sunday\"for day in \"$days\"do echo \"${day}\"done The output of this script is: Monday Tuesday Wednesday Thursday Friday Saturday Sunday In the expression for day in \"$day\" we are using shell parameter expansion to expand the days parameter. We have surrounded $day in quotes - this means that we are telling the shell 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. Now let's run the same script but this time we will not surround $days in quotes, meaning that the shell will perform word splitting: days=\"Monday Tuesday Wednesday Thursday Friday Saturday Sunday\"for day in $daysdo echo \"${day}\"done The output of this script is: MondayTuesdayWednesdayThursdayFridaySaturdaySunday In this case we can see that word splitting has occurred. The shell has performed the following operations: First, it searches through each character in the input Every time it finds a character in the IFS (Input Field Separator) special variable, it splits the word If there are multiple instances of a separator character, they are removed, and replaced with a single instance only By default, the IFS variable is set to . 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 Wednesday value), they are replaced with a single instance of the first character (a space in this case) and then the splitting occurs. 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: programs=\"/usr/bin/bash /usr/bin/zshell /usr/bin/new shell\"for program in $programsdo echo \"${program}\"done The output of this script is: /usr/bin/bash/usr/bin/zshell/usr/bin/newshell The final command, which has a space in the name, has been split into two words. You could avoid this issue by temporarily changing the value of IFS to use a different separator for words: programs=\"/usr/bin/bash;/usr/bin/zshell;/usr/bin/new shell\"OLDIFS=$IFSIFS=';'for program in $programsdo echo \"${program}\"doneIFS=$OLDIFS The output of this script is: /usr/bin/bash/usr/bin/zshell/usr/bin/new shell In this script we saved the original value of IFS into a parameter called OLDIFS, changed 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 IFS back to its original value. You should be careful when changing IFS to make sure that you change it back to its original value straight afterwards - other programs or commands might expect IFS to be set to the default value so it should only be changed with caution. If you were to look at the contents of the PATH variable, which specifies the locations the shell should search for commands, you will see that they are actually separated by colons: $ echo $PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games 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 IFS to a colon character to get each of the paths - even if they contain spaces: OLDIFS=$IFSIFS=\":\"for path in $PATHdo echo \"${path}\"doneIFS=$OLDIFS The output of this script will look something like this: /usr/local/sbin/usr/local/bin/usr/sbin/usr/bin/sbin/bin/usr/games/usr/local/games 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.","s":"Word Splitting","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#word-splitting","p":1016},{"i":1035,"t":"When the shell encounters the asterisk *, question mark ? or open square brackets [ characters, it marks the beginning of an expression that will have 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: $ ls ~/downloads/*.tar.gz/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64 (1).tar.gz/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64.tar.gz/home/dwmkerr/downloads/dotnet-sdk-3.1.412-osx-x64.tar.gz/home/dwmkerr/downloads/effective-shell-playground.tar.gz/home/dwmkerr/downloads/effective-shell-samples (1).tar.gz/home/dwmkerr/downloads/effective-shell-samples (2).tar.gz/home/dwmkerr/downloads/effective-shell-samples.tar.gz This script shows all of the files in the ~/downloads folder that match the pattern *.tar.gz. The results you see will depend on what you have in your own ~/downloads folder! It is important to remember that the shell performs all of the types of expansion that we have described in order. This means that word expansion is performed 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: for $path in ~/downloads/*.tar.gzdo echo \"${path}\"done The result of this script is: /home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64 (1).tar.gz/home/dwmkerr/downloads/aspnetcore-runtime-3.1.18-osx-x64.tar.gz/home/dwmkerr/downloads/dotnet-sdk-3.1.412-osx-x64.tar.gz/home/dwmkerr/downloads/effective-shell-playground.tar.gz/home/dwmkerr/downloads/effective-shell-samples (1).tar.gz/home/dwmkerr/downloads/effective-shell-samples (2).tar.gz/home/dwmkerr/downloads/effective-shell-samples.tar.gz Note that the spaces in the path names have been preserved - pathname expansion happens after word splitting - so the paths themselves are left as-is. As well as the asterisk * character, which can be used as a wildcard character in pathname expansion, there is also the question mark ? character which means 'any single character'. You can also use expressions such as [abc] to match on a range of characters. The exact details of how these special characters are used can be found in man bash. One feature of pathname expansion that people can sometimes be surprised by is what happens if the shell finds no files that match the pattern. You can see this in action below: $ echo ~/effective-shell/*.txt/home/dwmkerr/effective-shell/*.txt There are no files in the ~/effective-shell folder that match the pattern *.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! For example, if I wanted to run the touch command on a set of files, I would do the following: for file in ~/effective-shell/*.txt # If the file / folder doesn't exist, skip it. if ! [ -e \"$file\" ]; then continue; fi touch \"$file\"do In this script we first check to see whether the file or folder exists by using the -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 Chapter 21 - Loops and working with Files and Folders. Pathname expansion has limitations - if you need a more sophisticated way to search for a set of files, check Chapter 11 - Finding Files.","s":"Pathname Expansion","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#pathname-expansion","p":1016},{"i":1037,"t":"In this chapter we went into the lower level details of how 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 what the shell does with the input you provide it in your commands. 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. 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.","s":"Summary","u":"/part-6-advanced-techniques/understanding-shell-expansion/","h":"#summary","p":1016}],"index":{"version":"2.3.9","fields":["t"],"fieldVectors":[["t/4",[0,8.402,1,1.819,2,3.397,3,6.546,4,2.011,5,3.795,6,3.096,7,3.011,8,2.499,9,5.824,10,6.546,11,7.173,12,7.173,13,7.173,14,1.274,15,7.173,16,7.173,17,9.207,18,7.173,19,7.173,20,7.173,21,7.173,22,7.173,23,7.173,24,7.173,25,6.546,26,7.173,27,7.173,28,7.173,29,7.173,30,7.173,31,7.173,32,7.173,33,7.173,34,7.173,35,7.173,36,7.173,37,7.173,38,7.173,39,7.173,40,7.173,41,7.173,42,9.207,43,9.207,44,7.173,45,6.133,46,6.546,47,7.173]],["t/6",[48,3.262]],["t/8",[14,0.807,49,5.521,50,4.713,51,1.58,52,2.889,53,7.165,54,6.669,55,2.234,56,2.568,57,3.638,58,1.484,59,3.153,60,2.883,61,3.943,62,1.329,63,3.423,64,2.466,65,3.024,66,2.201,67,3.048,68,7.852,69,3.943,70,7.852,71,3.804,72,7.852,73,7.852,74,1.8,75,7.852,76,6.375,77,2.31,78,1.121,79,7.165,80,2.499,81,3.456,82,2.372,83,0.447,84,3.943,85,5.115,86,5.521,87,5.002,88,7.852,89,4.899]],["t/10",[51,1.587,55,1.807,83,0.448,90,5.399,91,0.927,92,8.356,93,4.061,94,7.884,95,7.884,96,7.469,97,3.087,98,3.34,99,4.061,100,0.732,101,3.357,102,3.653,103,1.381,104,2.279,105,6.741,106,3.061,107,1.879,108,4.23,109,1.807,110,5.713,111,2.614,112,3.505,113,4.291,114,3.505,115,4.115,116,3.113,117,2.708,118,4.494,119,3.959,120,7.195,121,5.136,122,7.195,123,7.884,124,3.194,125,5.906,126,5.023]],["t/13",[59,3.36,60,3.073,82,2.528,91,0.885,96,5.884,98,3.546,99,4.311,100,0.777,101,2.874,127,5.582,128,4.934,129,3.963,130,2.737,131,7.345,132,8.369,133,8.369,134,1.994,135,4.008,136,7.155,137,2.718,138,3.39,139,2.484,140,3.45,141,4.151,142,3.304,143,7.637,144,2.664,145,2.48,146,2.197,147,6.268]],["t/15",[148,4.118,149,7.92,150,5.407,151,8.373,152,9.205,153,3.316,154,7.92,155,7.521,156,4.118]],["t/17",[6,2.742,9,6.621,100,1.001,107,1.943,114,3.625,157,2.781,158,2.613,159,6.108,160,3.041,161,5.195,162,2.402,163,3.219,164,6.341,165,3.703,166,4.256,167,4.95,168,6.972,169,2.3,170,3.778,171,4.648,172,3.874,173,5.578,174,5.578,175,2.596,176,5.311,177,4.438,178,6.972,179,3.166,180,2.218,181,5.733,182,2.781,183,4.256,184,6.972,185,4.438]],["t/19",[48,3.262]],["t/21",[4,1.72,8,1.045,14,1.178,50,1.8,52,0.889,55,1.115,57,1.39,62,0.885,63,1.307,66,1.363,74,1.904,77,1.672,78,0.694,80,1.548,83,0.506,91,0.908,100,0.967,101,2.107,103,0.687,109,0.687,111,1.613,114,2.727,115,3.202,116,1.184,121,1.954,124,1.215,128,1.768,130,0.981,134,0.715,135,1.436,139,1.192,144,1.548,145,0.889,146,1.277,148,1.333,160,3.398,165,1.03,166,1.565,169,1.914,170,2.253,185,2.647,186,2.737,187,1.954,188,2.728,189,5.457,190,4.496,191,2.575,192,2.999,193,2.737,194,2.999,195,2.246,196,1.432,197,0.968,198,1.768,199,2.184,200,2.288,201,3.682,202,1.871,203,6.351,204,3.08,205,3.656,206,3.341,207,5.291,208,5.736,209,1.14,210,0.787,211,1.506,212,1.099,213,2,214,1.498,215,1.03,216,1.768,217,2.042,218,1.068,219,1.488,220,2.999,221,1.759,222,2.564,223,2.7,224,1.307,225,1.172,226,2.332,227,1.283,228,4.01,229,3.802,230,1.8,231,2.02,232,3.42,233,2.332,234,1.045,235,2.737,236,2.485,237,1.347,238,2.435,239,1.225,240,3.42,241,1.146,242,1.347,243,2,244,3.643,245,2.999,246,2.109,247,3.782,248,2.919,249,1.383,250,1.445,251,2.109,252,1.32,253,1.225,254,1.117,255,1.11,256,2.202,257,1.525,258,1.529,259,1.682,260,2.564,261,0.552,262,1.609,263,1.871,264,2.246,265,1.404,266,1.911,267,2.332,268,2.573,269,2.332,270,3.244,271,1.835,272,2.564,273,1.361,274,1.92,275,0.961,276,2.728,277,0.883,278,2.737,279,2.109,280,2.999,281,1.768,282,1.901,283,2.626,284,0.974,285,1.768,286,1.32,287,0.961,288,2.107,289,1.738,290,1.632,291,2.905,292,2.332,293,2.999,294,1.695,295,2.728,296,1.32,297,1.565,298,2.042,299,2.332,300,2.052,301,0.748,302,2.999,303,0.878,304,2.435,305,1.545,306,1.8,307,2.818,308,1.8,309,2.173,310,3.244,311,0.961,312,2.772,313,2.999,314,2.999,315,0.878,316,2.435,317,1.027,318,4.863,319,4.863,320,2.737,321,2.975,322,1.283,323,2,324,2.246,325,2.052,326,2.109,327,3.327,328,1.295,329,1.045,330,2.564,331,2.332,332,1.453,333,0.831,334,1.236,335,2.173,336,1.347]],["t/23",[1,1.115,4,1.836,8,2.283,14,0.674,51,1.868,52,1.303,55,1.007,57,2.037,62,0.821,66,1.836,74,1.007,77,1.041,78,0.628,82,1.328,83,0.446,91,0.967,100,0.727,101,1.51,103,0.621,108,2.358,109,1.007,128,2.591,129,3.102,130,1.437,144,2.085,145,1.303,146,1.72,158,1.409,162,1.295,167,2.18,169,1.013,172,1.706,175,1.399,182,2.67,189,3.796,190,4.989,199,3.516,200,2.92,201,4.701,205,2.883,206,1.679,207,4.173,210,2.056,225,1.887,230,1.627,234,1.532,236,1.78,249,2.092,250,1.845,258,1.952,261,0.809,265,1.752,266,4.173,275,2.099,297,2.294,301,0.676,303,1.286,307,5.379,308,4.701,311,2.099,312,3.734,317,1.384,333,1.217,334,1.812,336,1.974,337,3.09,338,2.931,339,2.18,340,0.944,341,4.011,342,2.523,343,3.569,344,2.742,345,2.387,346,1.39,347,1.994,348,2.689,349,0.885,350,2.428,351,2.689,352,3.565,353,2.236,354,6.288,355,6.5,356,5.094,357,5.601,358,5.601,359,2.283,360,3.758,361,4.011,362,4.395,363,2.392,364,4.201,365,4.395,366,4.395,367,2.8,368,1.812,369,1.974,370,2.105,371,1.898,372,2.742,373,2.8,374,3.418,375,3.185,376,1.916,377,3.185,378,2.591,379,2.591,380,2.405,381,6.551,382,3.007,383,2.059,384,3.862,385,3.418,386,3.418,387,2.689,388,4.395,389,4.395,390,4.395,391,4.395,392,2.591,393,2.505,394,2.325,395,2.325,396,1.121,397,5.601,398,2.811,399,1.935,400,2.358,401,3.292,402,1.291,403,1.75,404,4.907,405,2.392,406,1.457,407,2.334,408,2.129,409,1.935,410,1.75,411,1.196]],["t/25",[14,0.792,55,1.26,57,2.547,58,1.466,59,2.208,62,1.208,66,1.541,67,2.134,74,2.208,77,1.303,83,0.505,91,0.942,100,0.895,103,1.088,111,1.823,115,2.869,130,2.519,162,1.619,167,3.82,179,2.134,180,1.496,188,3.084,190,3.503,199,2.469,205,4.24,207,3.503,212,1.38,218,2.744,219,2.727,221,1.988,225,1.325,230,2.85,231,1.81,234,1.916,256,2.764,258,1.92,275,1.762,283,1.65,290,2.992,291,2.604,301,0.846,312,4.39,317,2.034,323,5.136,333,1.523,336,2.469,342,2.117,347,2.494,349,1.107,353,1.325,354,5.58,355,4.118,379,3.241,411,2.095,412,5.498,413,4.464,414,4.464,415,4.464,416,5.498,417,3.983,418,2.604,419,2.652,420,2.494,421,1.394,422,1.786,423,5.017,424,5.58,425,5.769,426,4.961,427,2.727,428,3.667,429,4.192,430,2.241,431,2.633,432,2.796,433,2.469,434,4.275,435,4.118,436,1.716,437,4.118,438,2.083,439,2.761,440,3.363,441,3.866,442,2.95,443,3.667,444,3.866,445,2.396,446,3.866,447,4.118,448,3.581,449,5.498,450,4.464,451,2.604,452,1.786,453,4.275,454,3.363,455,3.667,456,5.017]],["t/27",[1,1.74,14,1.024,48,2.276,61,3.446,74,2.619,77,1.626,78,0.98,82,2.073,83,0.41,91,0.781,101,2.357,103,0.969,153,2.114,179,2.665,180,1.867,182,2.34,189,3.977,190,6.722,197,2.214,205,4.383,207,6.722,208,3.977,209,1.608,210,2.349,212,1.23,218,3.188,230,3.311,232,7.419,250,2.108,254,1.249,283,2.686,311,2.199,346,2.17,354,4.972,355,5.141,429,4.87,457,6.263,458,5.268,459,6.863,460,6.863,461,2.804,462,4.12,463,5.572,464,3.535,465,3.146,466,3.325,467,4.695,468,2.244,469,3.535,470,2.78,471,3.582,472,2.643,473,3.25,474,2.463,475,3.735,476,3.85]],["t/29",[14,1.067,58,1.001,60,2.416,62,0.825,71,3.187,74,1.995,77,1.559,83,0.302,91,0.574,130,2.152,139,1.612,144,2.094,156,2.925,190,5.545,196,1.938,205,2.896,215,2.26,218,3.1,231,2.166,265,1.472,297,3.434,301,1.012,310,4.388,315,1.926,317,1.838,329,2.292,333,1.822,338,4.388,354,4.767,413,7.067,414,5.342,415,5.342,419,2.396,422,2.137,424,6.306,425,6.519,426,4.483,429,3.581,436,2.716,466,4.216,477,1.856,478,2.212,479,2.473,480,1.269,481,2.197,482,6.579,483,6.004,484,5.625,485,5.625,486,5.625,487,5.625,488,5.625,489,7.441,490,5.625,491,5.625,492,5.625,493,5.625,494,5.625,495,7.441,496,4.897,497,2.26,498,6.579,499,5.625,500,7.942,501,5.625,502,5.625,503,5.625,504,5.625,505,5.625,506,5.625,507,5.625,508,5.625,509,6.004,510,4.024,511,2.276,512,4.105,513,4.285]],["t/31",[2,1.825,4,1.662,6,1.296,8,2.066,14,1.22,51,0.776,55,1.656,58,0.902,62,0.743,66,1.08,67,1.496,71,1.867,74,1.656,78,0.55,84,1.935,91,0.708,99,1.985,100,0.671,104,2.089,109,0.883,130,2.364,137,1.478,146,1.012,157,1.314,160,1.437,163,2.341,167,1.912,179,2.302,182,1.314,190,6.339,200,2.211,205,2.61,207,3.778,209,0.903,210,1.898,214,1.826,218,1.373,225,1.955,230,1.426,234,1.343,242,1.731,249,1.629,254,1.079,256,1.383,258,1.478,261,0.709,265,1.003,268,2.039,274,2.853,275,1.235,282,0.944,283,1.78,284,1.252,295,2.162,296,1.696,297,2.011,298,3.034,301,1.112,317,1.527,347,1.748,354,2.792,355,6.93,367,5.17,372,3.7,375,2.792,378,2.272,379,2.272,402,1.168,407,2.575,413,5.868,414,3.129,415,3.129,422,2.348,426,1.985,430,1.495,439,2.977,445,1.68,458,2.039,468,1.26,470,2.402,471,3.095,475,2.097,480,0.743,484,3.295,485,3.295,486,3.295,487,3.295,488,3.295,489,6.179,490,3.295,491,3.295,492,3.295,493,3.295,494,3.295,495,6.179,496,4.646,499,5.07,500,5.411,501,5.07,502,5.07,503,5.07,504,5.07,505,5.07,506,5.07,507,5.07,508,5.07,509,3.517,510,3.627,512,2.404,514,2.51,515,6.777,516,3.517,517,3.129,518,2.011,519,2.067,520,2.404,521,0.983,522,3.877,523,3.854,524,2.233,525,2.886,526,3.559,527,2.792,528,3.129,529,2.997,530,1.985,531,1.846,532,3.854,533,3.854,534,1.575,535,3.517,536,2.585,537,5.07,538,4.871,539,3.854,540,2.022,541,1.534,542,5.62,543,3.349,544,3.854,545,1.484,546,2.709,547,3.854,548,2.162,549,3.854,550,4.611,551,3.854,552,3.854,553,1.363,554,2.997,555,2.57,556,1.172,557,1.935,558,3.517,559,1.805,560,1.889,561,3.295,562,3.129,563,3.295,564,2.039,565,1.935,566,1.648,567,2.51,568,2.747]],["t/33",[14,0.92,57,4.146,58,1.515,62,0.861,67,2.665,74,2.418,78,0.98,82,2.073,83,0.41,91,0.979,100,0.831,102,3.18,103,0.969,104,2.587,130,2.926,134,1.635,144,2.848,146,1.802,182,2.34,188,3.85,190,6.722,200,2.56,205,3.939,207,5.701,223,3.021,225,2.156,232,4.825,242,3.082,250,1.617,265,1.16,295,3.85,303,2.009,335,4.972,353,1.654,354,6.483,355,6.702,364,3.682,367,4.372,402,1.352,407,3.188,411,2.434,429,3.735,431,3.287,441,4.825,442,3.682,458,3.631,496,4.493,520,4.282,538,4.12,543,3.18,555,4.577,558,6.263,569,2.756,570,5.337,571,4.825,572,3.582,573,4.695]],["t/35",[48,3.262]],["t/37",[1,1.686,6,3.504,52,1.97,55,2.008,57,3.08,62,0.834,64,2.088,78,0.949,83,0.478,91,0.58,100,0.91,103,1.238,109,1.524,117,2.283,119,3.338,129,3.148,146,2.301,162,1.958,163,2.624,169,1.532,179,3.402,180,2.384,206,2.539,214,2.047,215,2.283,219,3.297,225,2.112,230,2.46,249,1.499,256,2.386,261,1.223,285,3.919,297,3.47,298,2.79,301,1.603,303,2.565,380,2.441,406,2.204,419,2.413,427,3.297,436,2.074,462,3.99,478,2.235,479,3.294,541,2.647,568,4.06,574,6.814,575,6.066,576,4.547,577,5.845,578,2.034,579,5.398,580,4.232,581,4.674,582,4.547,583,4.979,584,6.648,585,4.674,586,5.169,587,5.169,588,3.148,589,2.602,590,3.08,591,2.765,592,5.169,593,5.684,594,4.547,595,3.566,596,2.79]],["t/39",[1,2.282,2,5.014,6,3.56,48,2.984,91,0.785,100,0.836,103,1.271,130,2.944,236,3.646,427,4.465,478,3.56,479,3.98,591,3.744,597,6.329,598,6.003]],["t/41",[6,2.024,14,1.234,58,1.248,80,1.916,83,0.276,91,0.874,100,0.559,104,1.74,109,1.379,116,2.376,124,2.438,134,1.434,137,1.5,156,2.675,157,2.052,160,2.245,180,1.637,197,1.942,205,4.611,212,1.078,218,3.322,237,3.681,250,1.418,268,3.184,275,2.627,301,0.926,315,1.762,334,2.481,369,3.681,371,3.539,396,1.535,406,1.996,427,4.625,447,6.141,466,2.915,478,3.135,479,2.262,521,2.091,531,2.882,580,2.598,591,4.164,599,6.018,600,4.232,601,2.097,602,6.018,603,6.407,604,6.018,605,5.223,606,4.169,607,4.751,608,8.198,609,10.011,610,8.198,611,6.018,612,8.198,613,4.751,614,4.508,615,6.018,616,6.018,617,4.68,618,6.018,619,4.232,620,6.018,621,4.117,622,2.819,623,2.85,624,3.834,625,4.508,626,3.755,627,5.223,628,4.36]],["t/43",[1,1.993,14,0.929,48,3.517,56,1.851,58,1.196,60,2.079,62,0.71,66,1.587,74,1.801,77,1.862,80,1.802,83,0.47,91,0.894,100,0.526,103,1.11,104,1.636,109,1.297,111,1.877,114,2.517,130,1.851,134,1.349,146,2.064,160,2.931,180,2.138,188,3.176,209,2.286,214,1.744,215,1.944,218,2.8,243,3.776,249,1.276,275,3.126,284,1.839,298,3.299,301,0.871,303,1.657,340,1.215,368,2.334,371,3.393,387,3.463,406,1.877,421,1.436,422,1.839,427,3.899,429,3.081,430,1.427,447,7.307,466,2.742,478,1.903,479,2.128,511,1.958,531,3.764,545,2.18,569,3.156,578,2.985,590,2.623,591,3.756,595,3.037,601,1.973,624,5.008,627,6.931,629,2.651,630,4.597,631,3.873,632,5.464,633,3.398,634,4.84,635,2.711,636,7.173,637,3.607,638,4.597,639,4.24,640,4.24,641,6.112,642,3.227,643,2.421,644,4.597,645,3.227,646,1.587,647,5.661,648,2.421,649,1.577,650,2.623,651,2.421,652,3.873,653,5.377]],["t/45",[6,3.375,14,1.217,48,2.73,55,1.388,77,1.435,80,1.928,91,0.984,98,2.566,100,1.006,103,0.856,138,2.453,153,1.865,171,4.693,180,2.24,196,1.784,200,2.259,212,1.475,225,1.459,234,2.11,237,2.72,242,3.697,250,1.427,265,1.392,274,2.391,277,1.784,298,2.542,301,1.266,311,1.941,317,1.739,329,2.11,347,2.748,348,3.705,364,3.249,395,4.356,396,2.1,402,1.193,427,4.64,430,2.076,432,4.187,470,2.453,530,3.12,536,3.589,561,5.178,591,3.425,600,4.258,601,2.11,606,3.08,649,1.688,654,3.346,655,6.777,656,6.057,657,5.527,658,5.527,659,4.71,660,5.178,661,5.527,662,6.057,663,3.775,664,6.057,665,2.777,666,6.685,667,3.12,668,6.057,669,2.748,670,9.354,671,2.674,672,2.174,673,2.777,674,3.12,675,3.945,676,3.306]],["t/47",[4,2.697,8,0.606,9,1.413,14,0.836,48,1.362,49,2.155,52,2.41,54,4.259,55,1.135,56,0.569,58,1.277,60,0.639,61,0.874,62,0.896,64,0.547,67,2.417,77,1.859,78,1.12,80,1.308,83,0.413,91,0.828,100,0.285,103,0.699,104,0.886,106,1.19,114,1.363,116,0.687,118,0.992,130,1.344,134,0.415,135,0.833,137,1.234,138,0.705,139,0.426,140,1.264,144,1.308,146,2.666,156,2.201,157,0.593,158,1.586,161,1.109,162,1.21,165,0.598,167,0.863,169,0.706,170,2.294,172,4.415,174,1.19,175,0.554,180,0.834,191,0.73,196,0.513,197,0.561,200,1.532,204,0.874,206,2.378,209,1.16,212,1.544,214,0.944,215,1.053,216,1.026,224,0.759,225,0.739,227,0.744,231,0.573,234,1.068,239,1.253,243,1.161,248,1.045,249,0.926,250,0.722,251,1.224,252,0.766,254,0.748,255,1.832,259,1.72,261,0.911,265,0.518,268,1.622,274,1.21,275,0.982,282,0.751,283,0.522,284,0.565,286,0.766,288,1.7,296,0.766,298,2.821,301,0.868,303,0.509,305,0.896,306,1.84,308,1.045,309,2.977,315,0.509,327,2.81,333,0.482,340,0.374,342,0.67,349,1.637,350,0.961,368,0.717,372,1.086,392,1.026,393,0.992,396,0.782,403,1.636,406,1.872,407,0.62,409,1.349,411,0.473,421,0.777,427,4.772,430,0.439,433,1.377,436,0.543,438,0.659,458,2.173,462,3.738,472,0.67,477,1.896,478,1.031,479,1.861,480,0.336,481,1.023,496,0.874,521,0.782,548,0.976,553,0.615,556,0.529,569,0.699,578,1.905,580,2.137,582,5.739,583,3.077,589,2.437,590,0.806,591,4.354,596,0.73,600,5.515,601,1.967,613,1.008,614,3.708,619,1.224,623,0.824,631,4.259,632,0.896,639,3.077,643,0.744,650,1.42,651,0.744,672,0.625,677,1.74,678,1.74,679,1.261,680,1.413,681,2.394,682,1.826,683,1.261,684,2.27,685,2.621,686,2.619,687,2.155,688,1.588,689,3.677,690,4.517,691,2.466,692,4.108,693,1.588,694,1.045,695,1.74,696,1.74,697,2.889,698,3.077,699,1.086,700,1.161,701,2.797,702,0.874,703,1.353,704,0.896,705,1.026,706,1.224,707,0.992,708,4.108,709,1.74,710,1.74,711,1.353,712,1.74,713,1.74,714,1.008,715,1.875,716,4.511,717,0.699,718,0.874,719,1.588,720,1.334,721,1.413,722,0.976,723,3.065,724,1.74,725,1.74,726,1.74,727,1.74,728,1.064,729,1.488,730,1.588,731,1.303,732,1.74,733,0.711,734,1.086,735,1.488,736,1.026,737,1.74,738,1.303,739,4.108,740,1.74,741,1.74,742,1.74,743,1.74,744,1.74,745,2.621,746,1.875,747,1.74,748,1.74,749,1.588,750,1.74,751,3.749,752,0.644,753,1.74,754,1.74,755,3.065,756,1.74,757,1.74,758,3.065,759,3.065,760,1.74,761,1.74,762,1.74,763,1.74,764,1.74,765,2.617,766,1.303,767,1.74,768,3.481,769,1.953,770,5.804,771,1.74,772,1.74,773,1.74,774,0.885,775,1.74,776,1.588,777,1.74,778,1.353,779,1.74,780,1.747,781,1.413,782,2.489,783,3.065,784,3.065,785,1.74,786,1.74,787,1.74,788,1.74,789,1.74,790,1.559,791,1.488,792,1.74,793,1.74,794,1.74,795,1.74,796,1.588,797,1.224,798,1.74,799,1.261,800,1.74,801,1.588,802,7.519,803,3.065,804,1.74,805,1.109,806,1.413,807,1.74,808,4.108,809,3.065,810,1.74,811,1.74,812,1.353,813,1.74,814,1.588,815,1.74,816,1.353,817,1.064,818,1.74,819,4.95,820,1.42,821,0.908,822,2.797,823,1.74,824,1.588,825,2.797,826,1.488,827,1.74,828,1.452,829,1.588,830,1.588,831,6.226,832,1.74,833,1.74,834,1.72,835,1.74,836,2.422,837,1.74,838,1.224,839,1.74,840,1.74,841,1.109,842,1.008,843,0.976,844,1.588,845,1.74,846,1.74,847,2.489,848,1.588,849,1.74,850,1.74,851,1.74,852,1.488,853,1.74,854,1.74,855,1.74,856,1.74,857,1.74,858,1.74,859,1.74,860,1.74,861,1.74,862,1.74,863,1.74,864,1.588,865,1.74,866,1.74,867,0.717,868,1.74,869,1.74,870,1.74,871,1.74,872,0.751,873,1.74,874,1.588,875,1.588,876,1.134,877,1.74,878,1.74,879,1.588,880,0.947,881,1.224,882,0.874,883,2.919,884,1.413,885,1.74,886,1.74,887,0.806,888,0.649,889,1.74,890,1.064,891,3.512,892,1.74,893,1.74,894,0.629,895,1.74,896,1.74,897,1.74,898,1.064,899,1.74,900,1.74,901,1.588,902,0.815,903,1.74,904,1.74,905,1.413,906,1.74,907,0.896]],["t/49",[1,1.663,8,2.285,48,2.174,52,1.943,55,1.99,58,0.998,59,2.633,62,0.822,64,2.059,74,1.503,78,1.24,83,0.301,91,0.758,103,0.926,104,2.51,109,1.99,119,3.292,130,2.144,141,3.252,146,1.721,162,1.931,209,1.536,210,1.721,212,1.556,214,2.019,215,2.252,224,2.858,247,5.098,249,2.195,261,1.597,301,1.008,308,3.936,317,1.834,334,3.58,351,5.956,380,3.959,399,2.886,406,2.174,427,3.252,430,1.653,448,4.271,471,3.422,478,3.274,479,2.465,480,1.675,543,4.511,556,1.993,589,2.567,591,2.727,592,8.385,596,2.752,601,2.285,702,3.292,718,3.292,907,3.377,908,2.752,909,3.334,910,4.911,911,6.556,912,3.422,913,5.605,914,3.568,915,5.605,916,5.323,917,3.038,918,6.556,919,4.177,920,4.911,921,4.177,922,5.098,923,4.75,924,3.213]],["t/51",[1,1.566,4,1.731,14,0.858,62,1.269,67,2.397,74,1.415,77,1.976,78,0.882,83,0.283,91,0.728,100,0.775,103,0.872,138,2.501,139,2.044,163,2.437,205,2.718,212,1.106,218,2.972,221,2.233,225,1.488,226,4.801,234,2.907,241,2.358,246,4.341,256,2.994,258,2.079,261,1.136,274,3.293,298,2.592,301,0.95,311,1.979,317,1.762,380,2.267,400,3.312,418,2.924,421,1.566,454,5.102,478,3.662,496,3.1,541,2.458,578,1.889,580,2.665,592,6.487,593,7.132,594,4.224,596,2.592,597,4.341,630,5.013,676,2.479,704,3.18,718,4.189,774,3.14,881,5.865,925,4.755,926,4.341,927,3.222,928,5.475,929,6.174,930,4.801,931,4.625,932,5.013,933,6.773,934,3.222,935,3.784,936,6.174,937,4.341,938,6.301,939,3.464,940,3.64,941,3.026,942,8.342,943,3.293,944,2.303,945,6.174,946,2.745,947,4.473,948,4.022,949,4.341,950,2.831,951,4.341,952,4.801,953,6.174,954,4.801]],["t/53",[6,3.228,48,3.184,62,0.961,78,1.094,82,2.315,83,0.481,100,0.712,103,1.082,119,3.848,138,3.104,146,2.012,172,2.975,179,2.975,196,2.257,214,2.36,250,2.262,256,2.75,297,3.999,301,1.477,349,1.542,402,1.51,411,2.084,427,3.801,452,2.489,478,3.696,560,3.756,566,3.277,591,4.572,592,5.959,593,6.551,746,4.687,828,3.629,917,3.55,928,4.441,934,3.999,949,5.388,955,6.551,956,3.712,957,7.663,958,7.663,959,3.407,960,6.551,961,6.551,962,4.6]],["t/55",[48,3.262]],["t/57",[1,1.531,4,1.692,6,2.762,8,2.863,74,1.883,77,2.375,83,0.516,97,2.363,100,1.073,103,1.161,107,1.439,114,4.809,142,2.383,146,1.585,148,4.809,153,1.86,162,1.778,165,2.822,169,1.893,170,2.797,172,2.344,177,3.286,183,3.151,185,3.286,197,3.013,199,2.711,200,2.252,210,1.585,215,2.074,227,2.582,228,2.768,230,3.04,254,1.099,265,1.389,282,1.48,284,1.961,298,2.534,301,1.436,317,1.275,326,4.245,327,4.13,347,2.739,411,2.727,433,2.711,445,2.632,524,3.499,541,2.404,566,3.513,567,3.933,569,2.424,675,6.083,731,4.522,780,3.441,963,2.905,964,4.126,965,6.037,966,3.11,967,5.509,968,4.13,969,4.374,970,5.509,971,5.509,972,5.352,973,3.441,974,2.467,975,4.027,976,3.933,977,4.13,978,4.902,979,1.248,980,4.374,981,3.56,982,2.995]],["t/59",[51,1.786,80,2.825,97,3.474,100,0.975,148,3.946,225,2.139,249,2.001,250,2.091,263,5.537,284,2.883,317,1.875,333,2.458,396,2.264,398,3.185,402,1.749,406,2.943,451,4.203,480,1.712,566,3.795,982,4.402,983,5.537]],["t/61",[8,2.587,51,1.494,52,2.2,77,2.575,83,0.432,91,0.948,97,2.906,100,1.009,103,1.049,107,2.243,124,3.007,135,5.204,145,2.79,160,2.769,169,1.711,172,2.882,199,3.334,200,2.769,225,2.268,247,5.773,284,2.411,290,4.041,324,5.561,326,5.22,333,2.056,334,3.061,349,1.494,380,2.726,398,2.665,406,3.427,477,2.094,589,2.906,908,3.116,914,4.041,983,4.632,984,5.561,985,5.379,986,5.561,987,5.561,988,8.047,989,4.951,990,3.145,991,4.302,992,3.639,993,3.334]],["t/63",[8,2.77,51,1.6,77,2.639,78,1.135,83,0.365,91,0.694,97,3.112,100,0.738,101,3.662,145,2.912,160,3.664,169,1.832,172,3.087,225,1.916,249,1.792,263,4.96,284,2.582,303,2.327,327,5.438,349,1.6,396,2.028,398,2.853,406,3.535,420,3.607,531,3.807,536,3.465,914,4.327,944,2.965,988,8.399,989,5.302,990,3.368,991,4.607,992,3.897,994,4.96,995,3.851,996,6.797,997,3.607]],["t/65",[8,3.05,83,0.528,100,0.813,101,3.006,146,2.298,169,2.017,200,3.264,212,1.568,236,3.545,263,5.461,311,2.805,348,5.354,352,5.668,406,2.902,440,5.354,590,4.056,686,4.631,943,3.455,998,5.701,999,7.988,1000,5.838]],["t/67",[4,2.172,8,3.369,51,1.116,56,1.814,66,1.555,77,1.314,78,1.106,80,2.466,83,0.483,84,4.485,85,3.612,86,3.899,91,0.484,97,2.171,100,1.041,114,2.465,144,2.843,146,2.84,157,1.891,162,1.633,172,2.153,199,4.342,200,4.182,201,4.651,215,1.905,219,2.751,225,2.152,234,1.932,236,2.246,240,3.899,250,1.307,252,2.441,257,3.94,258,1.382,265,0.938,273,2.516,282,1.359,303,1.623,306,3.329,310,3.699,311,2.862,317,1.171,340,1.19,342,2.136,349,1.116,352,3.018,383,2.597,384,4.568,394,2.934,398,1.99,411,2.767,450,4.503,472,2.136,481,1.852,524,3.214,565,2.785,588,2.626,590,3.59,600,3.899,601,2.7,665,2.542,780,3.161,881,3.899,924,2.718,943,3.059,962,3.329,964,2.785,966,3.991,982,2.751,984,4.154,993,2.49,1001,5.546,1002,5.546,1003,4.312,1004,5.546,1005,3.064,1006,5.061,1007,3.699,1008,4.503,1009,5.546,1010,5.061,1011,5.546,1012,3.699,1013,4.503,1014,2.542,1015,3.533,1016,3.699,1017,4.312,1018,4.503,1019,4.741,1020,4.503,1021,3.283,1022,5.614,1023,3.612]],["t/69",[1,0.61,4,1.472,7,1.01,48,0.798,50,1.444,55,1.412,56,1.717,58,0.799,60,0.883,62,0.658,69,2.035,77,1.244,78,1.279,80,0.766,83,0.469,85,1.567,86,1.691,91,0.354,97,2.919,100,0.805,101,1.392,103,0.34,107,2.249,109,1.203,111,0.798,114,1.069,116,0.95,119,1.208,124,0.974,135,1.152,145,1.201,146,1.379,148,3.058,150,2.035,154,3.465,158,0.771,161,1.532,162,0.708,163,2.073,169,1.828,170,1.114,172,0.934,174,1.645,175,1.671,176,3.42,179,0.934,180,2.028,199,4.698,200,3.717,208,1.394,210,0.632,212,0.431,213,1.604,216,5.68,225,1.265,228,1.103,234,1.412,244,1.802,249,1.551,250,1.452,254,1.252,261,0.442,262,1.29,264,3.035,265,1.042,275,0.771,277,0.708,282,0.589,284,1.705,286,1.059,287,0.771,290,2.858,296,1.059,301,0.37,307,1.394,310,2.703,311,2.872,312,2.993,315,3.091,317,1.302,329,2.903,332,1.165,340,1.476,347,1.091,349,0.484,353,1.265,358,2.056,376,1.048,380,4.185,383,1.898,384,4.055,396,1.339,398,0.863,400,1.29,403,0.958,406,2.874,408,1.165,410,4.122,411,0.654,418,4.562,422,0.781,436,1.638,451,1.139,452,0.781,466,1.165,468,0.787,472,1.56,478,2.072,479,1.974,511,0.832,512,1.501,520,1.501,521,1.034,524,1.394,526,1.444,534,1.656,540,0.82,541,2.09,559,1.127,566,1.029,568,2.855,578,1.24,594,1.645,601,0.838,622,1.127,643,1.029,648,1.733,665,3.153,672,0.863,676,2.762,679,1.743,682,1.069,694,3.152,707,5.108,715,2.479,717,1.627,720,1.316,728,1.471,733,0.983,736,1.418,780,2.31,820,1.114,834,2.273,894,0.87,914,2.206,917,1.114,924,1.986,928,1.394,935,1.091,943,2.432,944,0.897,950,1.858,956,3.332,966,1.239,974,0.983,981,1.418,984,1.802,985,1.743,988,2.056,991,2.348,992,2.574,995,1.963,997,1.091,1017,4.792,1020,1.953,1022,2.936,1024,1.114,1025,5.379,1026,1.223,1027,3.465,1028,1.471,1029,1.691,1030,1.953,1031,1.501,1032,7.456,1033,1.444,1034,1.691,1035,1,1036,1.029,1037,2.405,1038,2.936,1039,0.897,1040,1.953,1041,2.405,1042,1.329,1043,1.471,1044,1.532,1045,2.195,1046,1.645,1047,1.394,1048,2.405,1049,7.932,1050,1.29,1051,1.87,1052,1.645,1053,1.567,1054,3.502,1055,1.152,1056,2.405,1057,1.371,1058,5.003,1059,2.195,1060,4.052,1061,2.405,1062,1.691,1063,2.195,1064,1.567,1065,2.405,1066,2.582,1067,1.645,1068,2.405,1069,4.615,1070,1.691,1071,1.567,1072,2.195,1073,1.691,1074,3.035,1075,1.802,1076,2.405,1077,1.567,1078,4.263,1079,1.103,1080,1.645]],["t/71",[4,1.936,8,2.846,51,1.644,52,1.4,55,1.583,59,1.896,62,1.024,64,1.483,77,2.129,78,1.473,80,2.198,81,2.078,83,0.458,91,0.784,97,2.704,100,0.982,101,3.086,102,2.188,103,1.269,104,1.996,108,2.533,109,1.082,119,2.371,146,1.24,148,2.099,149,4.037,162,2.034,163,1.864,169,1.088,170,2.188,172,2.681,175,1.503,183,2.464,196,1.391,199,2.12,201,4.902,210,1.24,212,0.846,225,2.165,247,3.672,248,2.834,249,1.065,250,1.113,254,0.859,258,2.036,265,1.168,274,1.864,275,1.513,282,1.157,283,1.417,288,1.622,291,2.236,301,0.726,308,4.146,310,3.149,311,3.306,317,0.997,327,3.23,329,3.131,334,1.947,340,1.014,373,4.4,380,4.168,383,2.212,394,2.498,396,2.698,406,2.29,407,2.909,470,1.913,472,1.818,476,2.649,478,2.322,541,1.88,589,1.848,635,2.261,646,1.936,669,2.142,682,2.099,694,2.834,706,3.32,718,2.371,907,2.432,908,2.899,928,2.736,935,2.142,938,3.149,943,3.547,959,2.099,979,0.976,987,3.537,1014,2.165,1022,5.004,1035,1.964,1044,3.008,1052,4.725,1078,3.834,1079,2.165,1081,4.722,1082,3.672,1083,4.309,1084,4.722,1085,2.236,1086,2.888,1087,7.452,1088,1.913,1089,2.533,1090,4.309,1091,4.722,1092,4.722,1093,3.834,1094,5.905,1095,4.722,1096,3.537,1097,2.888,1098,4.722,1099,4.037]],["t/73",[8,3.181,78,1.304,100,0.992,101,3.136,200,3.405,218,3.253,243,6.089,258,2.276,311,2.926,346,2.887,352,4.969,384,5.383,406,3.027,527,6.615,1100,5.291]],["t/75",[55,2.044,62,1.118,77,2.113,100,0.978,101,3.063,103,1.487,144,3.567,174,6.1,225,2.149,252,4.635,317,1.883,407,3.176,472,3.434,511,3.085,512,5.563,524,5.167]],["t/77",[1,1.183,8,0.994,9,2.315,14,0.704,51,0.574,55,1.07,58,1.303,59,1.145,62,1.289,67,1.107,69,1.432,74,0.653,77,1.106,78,1.359,80,1.486,83,0.346,91,0.947,97,1.116,98,1.977,100,0.924,101,0.979,103,0.659,104,1.713,107,1.632,109,0.653,117,3.53,119,2.975,124,1.155,126,2.973,129,2.21,130,1.938,139,1.851,140,1.175,144,1.486,145,0.845,148,3.044,153,1.437,157,1.592,162,0.84,163,1.126,169,1.076,172,1.107,177,1.552,179,1.107,180,1.27,199,1.28,208,1.652,210,1.798,212,0.836,217,1.197,218,2.889,221,2.143,224,2.034,225,1.65,230,1.727,231,1.537,234,1.626,236,1.155,239,1.165,241,1.783,246,2.005,249,1.052,254,1.078,258,1.477,261,0.858,265,1.277,277,2.225,279,2.005,282,0.699,283,0.856,284,0.926,286,2.054,287,0.914,288,0.979,301,1.317,303,1.366,308,1.711,311,1.899,317,1.595,327,3.193,328,1.231,329,0.994,333,0.79,334,1.175,340,0.612,342,1.797,346,1.476,349,1.378,359,1.626,372,1.779,373,1.816,380,1.714,395,1.508,396,1.19,398,1.023,406,1.547,407,1.663,409,1.255,418,3.243,421,2.642,422,2.635,432,1.45,436,1.456,445,2.034,446,2.005,458,1.508,462,1.711,468,0.932,473,1.35,477,0.804,479,1.072,480,1.565,481,0.952,511,0.986,513,3.859,536,1.243,541,1.135,553,1.008,556,1.419,559,1.335,567,1.857,569,1.145,595,1.53,643,1.219,651,3.955,663,3.139,667,2.404,671,2.781,673,3.718,699,1.779,711,2.217,720,2.224,733,1.165,736,1.681,781,2.315,799,4.293,826,2.438,887,1.321,888,2.21,910,2.136,914,2.54,934,1.488,943,2.982,944,1.063,948,1.857,961,2.438,982,1.414,998,1.857,1005,1.575,1021,1.208,1035,1.941,1074,7.127,1086,1.744,1101,2.136,1102,2.217,1103,2.315,1104,2.136,1105,2.705,1106,2.005,1107,6.848,1108,4.293,1109,3.859,1110,2.066,1111,3.574,1112,1.95,1113,2.578,1114,2.912,1115,2.851,1116,3.99,1117,1.35,1118,1.779,1119,1.625,1120,2.066,1121,4.667,1122,2.136,1123,5.925,1124,3.697,1125,1.231,1126,2.315,1127,6.249,1128,3.113,1129,2.618,1130,4.667,1131,5.407,1132,3.281,1133,2.602,1134,2.851,1135,2.851,1136,2.851,1137,2.851,1138,4.667,1139,3.629,1140,2.136,1141,1.779,1142,2.851,1143,2.851,1144,1.599,1145,2.438,1146,1.902,1147,4.438,1148,1.744,1149,4.667]],["t/79",[8,2.577,52,2.192,55,1.695,62,1.293,74,2.152,83,0.339,91,0.9,100,1.008,106,2.871,107,1.762,157,2.522,166,3.86,197,2.386,212,1.682,214,2.278,227,3.163,230,2.737,234,2.577,241,2.825,261,1.36,265,1.25,277,2.178,282,2.301,283,2.818,294,2.577,298,3.104,303,2.165,347,3.355,395,3.912,452,2.402,496,3.714,646,3.042,649,2.874,671,3.05,672,2.654,673,5.135,994,4.614,1035,3.076,1113,4.085,1150,6.262,1151,4.932,1152,3.86,1153,5.059]],["t/81",[14,0.988,51,0.855,52,1.259,55,2.202,56,1.389,58,1.652,62,1.147,66,1.191,74,0.974,78,1.218,83,0.47,91,0.916,99,2.188,100,0.593,103,1.084,104,1.846,107,2.033,109,0.974,111,2.117,113,2.312,116,1.677,117,2.635,124,1.721,126,2.707,130,1.389,139,1.88,140,1.751,144,2.033,146,2.24,156,1.889,158,1.361,160,1.584,180,1.737,197,1.371,199,2.868,200,2.382,201,3.834,212,0.761,214,1.309,223,2.811,230,2.363,239,1.736,246,2.987,254,0.773,261,1.175,263,2.651,265,1.297,277,3.09,282,1.041,283,1.275,288,2.193,303,1.869,317,0.897,329,1.48,330,3.632,333,2.125,334,1.751,349,0.855,359,1.48,378,2.505,396,2.333,400,2.279,406,3.723,407,1.513,411,1.737,418,3.024,421,2.848,422,2.492,433,1.908,435,3.182,445,1.852,452,1.38,468,1.389,473,3.024,480,0.82,481,1.419,497,1.459,511,2.209,522,2.279,541,1.691,553,1.502,566,1.817,601,1.48,644,3.45,646,1.191,651,3.649,663,3.518,671,3.408,720,2.074,799,3.078,867,1.751,888,1.584,939,2.383,973,4.374,983,2.651,1000,2.834,1035,1.767,1074,3.182,1103,3.45,1106,5.395,1109,4.16,1126,3.45,1127,3.877,1128,2.834,1129,2.383,1154,2.651,1155,2.312,1156,4.248,1157,3.182,1158,4.369,1159,4.248,1160,6.386,1161,2.906,1162,4.248,1163,5.828,1164,4.248,1165,2.599,1166,2.767,1167,3.877]],["t/83",[8,3.012,14,1.063,58,0.991,74,1.492,77,1.543,78,0.93,83,0.299,91,0.568,100,0.901,103,0.92,107,2.06,124,2.637,137,1.623,148,2.895,153,2.006,156,2.895,167,3.23,172,3.356,199,4.357,200,3.619,218,2.32,239,2.661,249,1.468,250,1.534,254,1.185,258,2.155,265,1.462,277,2.546,282,1.596,284,2.115,287,2.087,288,2.969,294,2.269,295,4.849,317,1.375,327,5.913,329,2.269,335,4.718,359,2.269,380,2.391,396,1.661,398,2.337,406,3.427,421,1.651,422,2.115,511,2.253,512,4.063,566,2.785,572,4.511,646,1.825,649,1.814,651,2.785,663,2.985,665,2.985,671,3.722,672,2.337,673,3.963,970,5.942,983,4.063,1046,4.454,1079,2.985,1109,4.241,1113,3.597,1152,3.398,1158,4.454,1163,5.942,1168,6.511,1169,3.909,1170,4.578,1171,3.118]],["t/85",[48,3.262]],["t/87",[77,1.57,83,0.304,90,3.66,91,0.763,100,0.909,103,0.936,127,5.831,131,4.8,134,2.083,137,1.651,138,3.541,139,1.623,146,1.739,148,2.945,161,4.221,162,1.951,188,3.716,215,2.275,217,2.781,224,2.888,225,2.107,249,1.971,261,1.219,265,1.12,301,1.019,307,3.839,322,2.833,396,2.23,402,1.305,411,2.378,431,3.173,478,2.94,481,2.212,571,4.658,596,2.781,601,3.047,765,4.221,774,4.446,920,4.962,935,3.006,951,4.658,952,5.151,966,5.041,969,6.335,979,1.37,1024,3.069,1031,4.133,1036,4.185,1057,3.776,1172,6.625,1173,7.61,1174,3.286,1175,6.625,1176,5.664,1177,4.962,1178,6.625,1179,6.045,1180,8.743,1181,6.625,1182,7.475,1183,4.315,1184,5.379,1185,5.664,1186,5.664,1187,3.006,1188,5.151,1189,4.221,1190,5.348,1191,6.625,1192,5.664,1193,6.625,1194,5.664]],["t/89",[90,5.092,100,0.856,140,3.8,160,3.438,301,1.418,322,3.942,396,2.351,480,1.778,966,4.748,969,6.679,998,6.005,1100,5.342,1186,7.881,1195,5.534]],["t/91",[1,2.619,4,1.622,5,3.061,8,1.301,54,2.554,55,1.326,56,2.317,57,2.681,62,0.468,65,1.437,76,4.698,77,2.333,78,0.533,82,1.128,83,0.265,91,0.326,100,0.802,103,1.001,108,3.104,114,2.572,121,2.432,127,2.49,134,0.889,138,2.343,142,1.474,146,1.519,159,2.796,163,1.474,169,1.333,172,1.449,175,1.842,179,1.449,180,1.574,196,1.099,206,3.489,207,2.378,209,1.356,226,2.903,236,2.869,246,2.625,250,0.88,254,0.679,257,3.602,261,0.687,265,0.631,282,0.915,296,1.643,307,3.353,317,0.788,322,1.596,344,2.329,345,2.108,346,1.18,380,2.124,400,2.003,407,2.524,411,1.015,422,1.213,444,2.625,445,1.627,451,1.768,452,1.213,453,2.903,455,2.49,465,1.711,470,1.512,472,1.437,477,1.998,478,2.903,479,1.403,481,1.932,566,1.596,568,1.73,579,3.031,588,1.768,598,2.49,601,1.301,648,3.692,654,2.062,676,2.323,694,3.473,718,2.905,782,3.031,799,2.705,841,4.513,901,3.407,920,2.796,922,2.903,925,2.128,935,5.224,937,2.625,951,2.625,952,2.903,959,1.66,963,1.32,966,2.98,969,2.705,973,3.298,977,2.554,984,2.796,987,2.796,993,1.676,1005,2.062,1016,2.49,1025,2.241,1034,2.625,1064,2.432,1088,2.343,1093,3.031,1100,2.163,1114,2.329,1141,2.329,1173,4.499,1182,4.947,1184,3.031,1186,3.192,1188,2.903,1190,5.828,1195,4.79,1196,2.705,1197,3.733,1198,3.192,1199,5.786,1200,2.201,1201,3.733,1202,3.733,1203,3.031,1204,3.192,1205,3.733,1206,3.733,1207,3.407,1208,3.246,1209,3.192,1210,3.733,1211,3.192,1212,2.378,1213,3.733,1214,3.733,1215,5.28,1216,3.733,1217,3.733,1218,5.786,1219,3.407,1220,3.733,1221,3.733,1222,3.733,1223,3.733,1224,3.192,1225,5.306,1226,3.733,1227,3.733,1228,2.903,1229,3.733,1230,3.473,1231,2.329,1232,3.733,1233,3.557,1234,4.499,1235,3.733,1236,5.786,1237,3.733,1238,3.733,1239,3.192,1240,5.28,1241,3.733,1242,3.733,1243,3.733,1244,3.733,1245,2.98,1246,4.192,1247,2.625,1248,3.733,1249,3.733,1250,3.733,1251,5.752,1252,3.733,1253,4.499,1254,3.192,1255,3.733,1256,1.808,1257,5.28,1258,3.192,1259,3.407,1260,2.903,1261,3.733,1262,2.094,1263,3.733,1264,3.192,1265,3.733,1266,3.733,1267,3.733,1268,3.407,1269,2.903,1270,2.796,1271,2.705,1272,3.407,1273,3.733,1274,3.192]],["t/93",[1,2.397,4,1.477,5,2.788,77,1.249,78,1.067,81,3.29,83,0.398,92,7.427,100,0.878,103,1.464,137,1.314,146,2.799,148,2.343,156,4.437,161,4.762,169,1.214,199,3.357,200,4.239,211,2.646,215,1.81,222,4.506,223,3.29,224,2.297,249,1.188,250,1.761,255,2.766,256,3.582,257,3.801,273,2.391,303,1.542,307,5.034,395,2.788,406,1.747,408,2.553,411,2.9,452,2.428,453,4.098,478,1.772,479,1.981,511,1.823,568,2.442,574,4.098,581,3.705,588,2.496,590,2.442,596,3.138,613,3.054,648,4.433,702,2.646,780,3.004,841,5.534,935,2.391,966,2.714,969,3.818,993,2.367,1013,4.279,1017,4.098,1020,4.279,1036,2.254,1074,3.947,1088,2.135,1173,5.813,1182,4.506,1190,4.572,1208,2.956,1211,4.506,1245,2.714,1254,4.506,1256,2.553,1269,5.813,1275,6.391,1276,4.809,1277,5.27,1278,3.705,1279,7.927,1280,3.605,1281,7.475,1282,3.224,1283,5.27,1284,6.822,1285,4.809,1286,4.098,1287,5.27,1288,3.705,1289,3.705,1290,5.27,1291,5.27,1292,5.27,1293,5.27,1294,3.605,1295,5.27,1296,7.475,1297,4.279,1298,6.391,1299,3.433]],["t/95",[1,1.988,5,3.472,7,3.647,77,1.555,81,1.939,83,0.301,89,2.749,90,3.625,98,1.866,100,1.002,103,1.425,114,2.917,134,1.05,139,1.08,140,1.816,146,2.281,150,2.212,153,2.021,167,2.185,181,3.098,183,2.299,196,1.298,211,3.295,224,1.92,227,1.884,250,1.038,252,1.939,255,2.428,256,2.355,257,2.24,261,0.81,265,0.745,274,1.739,276,2.471,295,2.471,317,0.931,336,2.947,340,0.946,345,2.391,356,3.426,359,1.535,376,1.92,394,3.472,396,1.124,400,2.364,411,2.65,419,1.213,452,1.431,455,4.376,457,4.02,466,2.134,479,1.656,481,1.471,554,3.426,557,2.212,566,1.884,573,5.364,588,3.107,594,3.014,621,3.014,648,3.716,676,2.635,701,4.02,733,1.8,746,4.797,765,2.807,782,3.577,824,4.02,921,2.807,941,2.159,963,1.558,966,4.039,979,2.143,980,7.93,993,1.978,1003,3.426,1005,2.434,1026,2.24,1036,1.884,1052,6.663,1088,3.519,1100,2.553,1101,3.3,1173,3.426,1190,5.958,1192,7.428,1208,3.681,1211,3.767,1219,7.929,1269,7.224,1286,3.426,1294,3.014,1300,3.577,1301,4.406,1302,7.841,1303,4.406,1304,4.406,1305,3.767,1306,4.406,1307,4.406,1308,3.3,1309,2.87,1310,4.406,1311,5.328,1312,1.978,1313,3.426,1314,4.02,1315,4.02,1316,2.749,1317,3.3,1318,4.406,1319,7.156,1320,3.767,1321,4.406,1322,3.577,1323,4.406,1324,4.406,1325,4.02,1326,1.884,1327,4.406,1328,4.02,1329,6.562,1330,4.406,1331,4.406,1332,2.807,1333,3.767,1334,4.406,1335,4.489,1336,5.61,1337,4.406,1338,4.797,1339,1.959,1340,4.406,1341,4.406,1342,4.406,1343,3.767,1344,4.406]],["t/97",[48,3.262]],["t/99",[6,1.653,8,1.713,14,0.506,52,2.714,58,1.766,59,1.974,62,0.616,64,2.234,78,0.702,83,0.383,87,3.132,91,0.729,100,0.993,107,1.991,109,1.127,112,2.186,130,1.608,141,2.439,146,1.291,158,1.576,162,1.448,167,2.439,171,2.802,180,2.273,205,2.164,210,1.291,212,0.881,217,2.064,225,1.185,230,1.819,234,1.713,249,1.108,254,1.667,265,1.413,268,2.601,277,2.095,283,1.476,286,2.164,291,2.328,298,2.064,301,0.756,317,2.052,333,1.362,340,1.055,346,1.555,349,0.99,350,2.716,353,1.714,373,3.132,392,4.193,402,1.401,403,1.957,406,2.771,418,5.365,419,1.958,420,2.231,421,2.464,430,1.24,432,2.5,479,1.848,480,1.612,511,2.461,513,4.633,522,2.638,568,2.278,577,4.744,633,2.951,651,2.103,671,2.31,673,4.643,720,1.597,736,2.899,817,3.007,882,2.469,887,2.278,888,1.834,914,2.676,928,2.849,940,4.927,1018,3.992,1021,2.083,1105,2.849,1140,3.683,1153,3.363,1289,3.457,1326,2.103,1345,2.231,1346,3.279,1347,4.204,1348,2.566,1349,6.439,1350,3.203,1351,3.457,1352,6.498,1353,7.145,1354,5.153,1355,3.363,1356,7.145,1357,7.113,1358,6.491,1359,3.363,1360,4.204,1361,4.204,1362,3.007,1363,4.917,1364,3.992]],["t/101",[4,2.399,58,1.777,59,3.436,83,0.392,91,0.747,100,0.954,103,1.209,111,2.837,214,2.635,230,3.166,261,1.574,283,2.568,303,2.504,317,2.17,333,2.37,349,1.722,419,2.356,480,1.651,645,4.877,956,4.145,1021,3.625,1349,6.016,1353,7.316,1354,6.199]],["t/103",[55,1.861,58,1.638,61,4.077,78,1.159,83,0.372,91,0.868,100,0.924,114,3.61,146,2.132,156,3.61,169,1.871,185,4.419,230,3.005,249,1.83,274,3.205,291,3.845,301,1.249,317,2.273,326,5.709,333,2.756,419,2.235,421,2.059,480,1.566,519,4.356,601,2.829,651,3.472,1349,6.997,1350,5.289,1353,6.942,1354,5.883,1364,6.593,1365,4.705,1366,8.119,1367,5.173]],["t/105",[6,2.11,14,0.645,51,1.263,58,1.701,59,2.52,62,1.058,78,1.204,83,0.387,91,0.889,100,0.783,103,0.886,113,3.416,114,2.79,139,1.538,142,2.477,162,1.848,185,3.416,196,1.848,210,1.648,214,2.934,223,2.762,225,2.032,230,2.322,249,1.415,250,1.987,258,2.102,261,1.154,266,3.998,290,3.416,303,1.837,305,3.232,317,1.781,333,1.738,369,2.818,374,4.88,376,2.736,396,1.601,402,1.237,407,2.236,410,2.499,418,2.972,419,2.322,420,3.826,421,2.583,472,2.417,480,1.211,534,2.564,545,2.417,553,2.219,559,2.939,629,2.939,671,2.038,714,3.637,888,2.341,943,2.477,946,2.79,959,2.79,1345,4.964,1349,5.93,1354,6.111,1368,6.138,1369,6.276,1370,2.972,1371,4.88,1372,5.366,1373,4.413,1374,6.276,1375,5.366,1376,3.637,1377,5.366,1378,4.293,1379,5.727,1380,3.839,1381,4.413,1382,4.88]],["t/107",[6,1.992,8,2.065,58,1.731,62,1.017,78,1.158,83,0.456,91,0.961,100,0.924,102,2.745,103,1.471,104,1.713,114,3.606,126,3.775,142,2.339,146,1.556,148,2.634,159,4.438,162,1.745,169,1.869,185,4.415,191,3.405,200,2.21,209,1.901,210,1.556,212,1.062,214,1.825,215,2.035,218,2.111,221,2.143,225,1.428,238,6.586,250,1.396,261,1.09,282,1.452,283,2.435,286,4.071,301,1.247,317,1.251,327,4.053,329,2.065,349,1.862,376,2.583,380,3.396,386,4.607,387,3.624,406,1.965,407,2.111,419,2.233,420,3.68,421,2.345,430,2.045,431,2.837,436,1.849,480,1.143,513,3.859,564,3.135,601,2.065,687,4.166,694,3.557,702,2.975,738,4.438,780,3.377,956,2.87,1035,2.465,1039,2.21,1088,2.4,1094,5.066,1278,4.166,1308,4.438,1383,3.557,1384,5.748,1385,5.925,1386,5.925,1387,6.935,1388,5.925,1389,4.607,1390,5.407]],["t/109",[51,2.035,58,1.268,62,1.045,78,1.19,81,3.668,83,0.382,91,0.727,100,1.011,142,3.289,205,4.791,254,1.517,265,1.409,333,2.308,419,2.997,420,3.78,466,4.036,480,1.607,603,6.185,1021,3.53,1106,5.858,1300,6.765,1384,4.349,1387,7.124,1391,8.332,1392,8.332,1393,8.332,1394,7.124,1395,7.604]],["t/111",[59,3.36,100,1.013,111,2.775,146,2.197,162,2.465,175,2.664,191,3.513,265,1.415,317,1.768,346,2.646,396,2.586,402,1.649,403,3.332,419,2.791,420,4.599,480,1.614,519,4.49,545,3.223,553,2.959,559,3.92,633,5.024,663,4.647,671,3.542,913,7.155,1348,4.368,1396,6.795,1397,4.77]],["t/113",[58,1.61,59,2.776,62,0.867,78,1.284,81,3.043,83,0.412,91,0.785,111,2.292,144,2.2,175,2.2,182,3.066,183,3.608,205,3.957,210,1.815,230,2.558,249,1.558,256,2.481,261,1.654,263,4.313,265,1.52,286,3.043,301,1.383,317,1.899,352,3.762,396,1.763,411,1.88,421,2.534,445,3.013,468,3.268,480,1.734,511,3.456,521,1.763,531,3.311,553,2.444,629,3.238,667,3.561,671,2.245,908,2.902,993,3.104,1005,3.819,1106,6.321,1131,6.308,1260,5.375,1346,6.664,1347,5.91,1359,4.729,1384,4.692,1390,6.308,1394,7.687,1395,6.308,1398,3.878,1399,6.835,1400,8.99,1401,6.308,1402,4.313,1403,6.308]],["t/115",[4,1.771,51,1.705,52,1.872,58,0.961,59,2.537,61,3.172,62,1.198,78,0.902,83,0.29,91,0.739,100,0.948,103,0.892,107,1.505,109,1.448,114,3.766,119,3.172,126,4.025,137,1.575,139,1.548,142,2.494,162,2.495,169,1.952,170,2.927,175,2.697,180,1.718,182,2.154,185,5.202,191,2.652,230,3.135,237,3.804,254,1.15,261,1.162,266,4.025,301,0.972,303,2.479,317,1.789,328,2.727,333,2.346,340,1.356,353,2.566,383,2.959,405,3.438,419,1.739,421,2.84,473,2.992,480,1.844,513,6.225,521,1.611,540,2.154,560,3.096,648,2.702,671,2.052,720,2.052,736,3.725,888,2.356,950,2.896,1106,5.956,1125,2.727,1158,4.322,1195,3.792,1208,4.752,1309,5.518,1350,4.115,1404,5.129,1405,5.129,1406,6.317,1407,5.128,1408,5.765,1409,5.765,1410,6.317,1411,6.317,1412,6.317]],["t/117",[1,1.657,14,1.194,58,1.639,62,0.819,78,0.933,83,0.494,91,0.848,103,0.923,126,6.594,141,3.241,197,2.795,205,4.741,225,2.088,249,1.473,265,1.105,294,2.277,312,3.724,317,1.38,322,3.705,333,1.81,346,2.066,407,2.328,409,2.876,421,2.731,436,2.039,438,2.476,445,2.848,474,2.345,480,1.26,511,2.26,559,3.06,603,6.331,671,2.122,682,2.905,717,2.624,720,2.122,963,2.31,1106,6.091,1133,5.963,1144,5.807,1228,5.081,1384,3.41,1387,5.586,1394,5.586,1399,4.47,1401,5.963,1413,8.664,1414,6.534,1415,6.534,1416,6.534,1417,5.081,1418,7.906,1419,5.586,1420,8.664,1421,6.534,1422,6.534,1423,6.534]],["t/119",[50,4.258,52,2.102,58,1.539,59,2.848,80,2.258,103,1.002,111,2.352,126,6.444,140,2.924,153,2.185,156,3.153,182,2.419,196,2.089,210,1.862,212,1.271,214,2.185,221,3.306,249,1.599,250,1.671,265,1.199,303,2.076,317,1.498,328,3.062,342,2.731,353,2.203,369,4.105,411,1.929,421,2.709,480,1.368,511,2.454,534,4.133,540,2.419,553,2.508,559,3.322,622,3.322,671,3.592,888,2.645,1106,4.987,1117,3.359,1171,3.397,1309,4.62,1345,4.589,1368,5.994,1372,6.064,1373,4.987,1381,4.987,1407,3.805,1408,6.473,1424,7.093,1425,8.003,1426,7.093,1427,6.064]],["t/121",[8,1.816,14,0.888,52,2.198,55,1.195,58,1.726,59,2.978,62,1.246,74,1.195,78,1.059,81,2.294,83,0.396,91,0.753,100,0.689,101,1.79,103,1.22,104,1.507,109,1.195,111,1.728,113,4.699,119,2.617,124,3.497,130,2.426,138,2.111,157,1.777,179,2.024,213,3.476,217,2.188,218,1.857,221,1.885,225,1.787,227,2.229,230,2.745,234,1.816,241,1.991,250,1.228,258,1.299,261,1.588,265,0.881,266,3.321,274,2.058,275,1.67,282,2.116,298,3.113,301,1.328,317,1.101,329,1.816,332,2.525,333,1.444,337,3.665,347,2.365,349,1.738,393,2.971,407,1.857,411,1.418,421,1.881,426,5.693,430,2.506,436,1.627,442,3.979,452,1.693,468,1.705,473,3.512,480,1.666,521,1.329,545,3.325,629,2.441,728,3.188,887,2.415,908,2.188,914,2.837,1144,4.161,1166,3.395,1187,2.365,1312,3.877,1384,3.871,1428,5.054,1429,9.699,1430,5.212,1431,6.022,1432,5.212,1433,5.212,1434,4.757,1435,5.212,1436,6.256,1437,4.757,1438,3.776,1439,5.212,1440,3.904,1441,7.417,1442,7.417,1443,5.212,1444,5.215]],["t/123",[1,1.753,52,2.049,55,1.584,58,1.368,62,1.127,66,1.938,78,0.987,83,0.317,91,0.957,102,3.203,109,1.584,129,3.274,139,2.203,175,2.2,180,2.446,191,2.902,210,1.815,221,2.5,254,1.996,255,2.558,258,1.723,301,1.063,333,2.49,346,2.186,353,2.166,369,4.037,421,2.683,480,1.734,521,1.763,545,2.662,559,4.211,651,2.956,667,3.561,687,4.86,717,2.776,867,2.85,917,3.203,1104,5.178,1349,8.322,1354,5.008,1381,4.86,1384,3.608,1409,6.308,1417,8.529,1418,8.204,1445,8.99,1446,4.86,1447,4.076]],["t/125",[1,1.448,14,0.813,58,1.829,60,2.097,83,0.488,91,1.016,100,0.735,111,1.894,124,3.203,146,1.5,153,1.759,200,2.13,205,3.993,217,2.398,218,2.035,237,2.565,241,2.182,254,1.04,256,2.05,258,1.424,266,3.639,277,1.682,282,1.938,329,1.99,349,1.15,353,2.186,369,2.565,374,4.442,376,2.49,392,3.368,402,1.125,409,2.514,411,1.554,418,3.745,421,2.697,426,4.074,430,1.994,468,1.868,511,2.736,534,2.334,572,2.981,603,4.838,622,2.675,623,3.745,671,2.947,714,3.31,715,3.494,717,2.294,888,3.384,940,4.663,956,2.767,1035,2.376,1111,4.128,1117,2.705,1144,4.437,1158,3.907,1345,4.443,1346,3.809,1354,5.73,1356,4.884,1368,5.494,1371,4.442,1372,4.884,1373,4.016,1375,4.884,1381,4.016,1384,4.128,1399,3.907,1407,3.064,1425,5.924,1428,2.904,1429,5.212,1444,4.016]],["t/127",[48,3.262]],["t/129",[6,2.138,8,2.216,52,1.885,57,2.947,62,1.202,74,1.458,83,0.47,91,0.894,98,2.694,100,1.02,102,2.947,103,1.202,107,1.515,111,2.109,114,3.783,129,3.012,130,2.08,138,2.576,146,2.234,169,1.466,182,2.169,185,4.631,209,1.49,214,1.959,218,2.266,225,1.532,234,2.965,239,2.599,250,2.005,253,2.599,254,1.158,258,2.121,261,1.17,265,1.622,275,2.038,277,1.873,282,1.558,283,2.554,294,2.216,340,1.365,347,2.885,349,1.28,353,1.532,396,1.622,406,3.18,421,2.158,565,3.193,633,5.108,646,1.783,648,4.101,649,2.853,671,3.568,721,5.164,752,2.353,774,3.234,959,3.783,1029,4.471,1079,2.915,1109,7.155,1152,3.319,1208,3.568,1262,3.568,1448,4.142,1449,6.359]],["t/131",[58,1.388,62,1.338,91,0.931,100,0.656,103,1.426,104,2.919,114,4.054,117,2.427,130,2.311,153,2.176,169,2.102,185,4.963,210,2.394,218,4.029,225,1.703,234,2.462,258,1.761,261,1.3,265,1.195,274,2.79,277,2.974,286,3.11,301,1.087,315,2.669,317,1.493,349,2.223,407,2.517,421,2.313,422,3.28,452,2.295,472,2.721,480,1.363,606,4.637,651,4.563,671,3.587,814,6.449,1448,5.94,1450,4.713]],["t/133",[14,0.863,51,1.69,58,0.949,59,2.504,62,1.19,67,2.421,78,0.89,81,2.744,82,1.884,83,0.286,91,0.99,100,0.943,102,2.889,103,0.881,107,1.486,109,1.429,114,2.772,119,3.131,126,5.349,139,1.528,140,2.57,144,2.673,145,1.848,146,2.205,162,1.836,169,1.437,179,2.421,180,1.696,185,3.393,198,3.676,209,1.461,210,1.637,214,1.92,218,2.221,223,2.744,225,1.502,239,2.548,242,2.8,249,1.406,250,1.469,258,1.554,265,1.605,283,1.872,294,2.173,317,1.317,326,4.384,342,2.401,349,1.255,396,2.142,421,1.581,422,2.025,430,1.572,464,3.211,472,2.401,474,2.238,511,3.284,559,2.92,564,3.299,652,5.744,663,4.657,671,3.444,675,4.061,720,2.025,752,2.307,828,2.952,888,2.325,979,1.736,1039,3.132,1089,3.345,1109,4.061,1129,4.71,1333,5.331,1451,6.235,1452,6.235,1453,3.972,1454,5.245,1455,5.062,1456,3.498]],["t/135",[8,2.084,14,0.615,51,1.204,58,1.59,59,2.401,66,1.677,83,0.426,100,0.927,107,1.425,108,3.209,109,1.371,116,2.361,119,3.003,126,3.81,138,2.422,139,1.466,141,2.967,144,1.904,145,1.773,146,2.44,148,2.659,158,1.916,159,4.48,162,2.404,199,2.686,200,2.23,201,3.59,212,1.072,234,2.084,249,1.348,250,1.409,254,1.486,261,1.1,265,1.011,277,2.404,282,2,286,2.632,291,2.832,317,1.263,334,2.465,351,3.658,359,2.844,382,7.688,411,1.627,421,2.736,480,1.154,511,3.215,522,3.209,559,2.801,663,3.742,665,2.742,671,3.019,683,4.333,699,3.731,752,4.455,769,3.81,914,3.255,916,4.856,959,2.659,1035,2.488,1075,8.08,1106,6.534,1120,4.333,1228,6.348,1438,4.333,1457,3.658,1458,5.458,1459,5.981,1460,5.981]],["t/137",[6,1.95,14,1.19,55,1.329,62,1.236,78,0.828,83,0.49,91,0.902,109,1.329,111,1.923,126,6.279,139,1.421,146,1.522,156,2.578,160,2.163,180,1.577,205,4.025,212,1.766,218,2.066,221,2.097,225,1.926,234,2.021,249,1.307,250,1.366,254,1.665,261,1.47,264,4.343,277,2.903,284,1.884,286,2.552,288,2.745,291,3.785,295,3.253,303,1.697,317,1.225,329,2.021,333,1.606,359,2.785,396,1.479,402,1.143,421,3.081,422,1.884,511,2.006,521,2.038,603,4.889,629,2.716,633,3.481,651,4.421,652,5.467,654,3.203,663,4.192,671,1.884,720,1.884,887,2.687,979,1.199,1035,2.412,1039,2.981,1125,2.503,1157,4.343,1158,6.256,1461,4.343,1462,4.712,1463,4.509,1464,5.799]],["t/139",[14,0.631,51,1.235,55,1.903,57,3.848,58,1.264,61,3.08,62,0.769,78,1.441,82,1.853,83,0.381,91,0.535,100,0.57,103,0.867,117,2.107,126,5.999,137,1.529,146,2.768,148,2.727,157,2.092,162,2.446,163,2.422,169,2.17,200,2.288,212,1.488,218,2.185,223,2.7,231,2.02,234,2.894,258,1.529,261,1.528,275,1.966,288,3.234,317,1.296,327,4.196,347,2.783,349,1.895,382,4.196,421,2.388,436,1.914,462,3.682,473,2.905,511,2.873,566,2.623,569,2.463,633,3.682,646,1.72,648,2.623,649,1.709,651,2.623,663,4.626,671,3.797,673,5.4,752,3.073,882,3.08,959,4.186,997,3.768,1075,4.595,1105,3.555,1247,4.313,1465,4.445,1466,4.445,1467,5.652]],["t/141",[52,2.034,55,2.282,58,1.044,64,2.811,78,0.98,80,2.185,83,0.41,91,0.921,130,2.244,144,2.185,148,3.051,175,2.185,179,3.474,197,2.214,212,1.23,221,2.482,227,2.935,249,1.547,250,1.617,254,1.812,261,1.831,265,1.784,274,2.709,301,1.376,303,2.009,346,2.17,402,1.352,418,4.237,431,3.287,432,3.49,470,4.033,480,1.324,481,2.291,511,3.444,512,4.282,671,3.234,951,7.89,1035,3.722,1155,4.87,1230,4.12,1468,9.021,1469,6.863,1470,8.513,1471,5.968,1472,5.572,1473,6.483,1474,6.863,1475,5.141]],["t/143",[4,2.819,55,2.485,83,0.497,91,0.721,111,2.739,117,2.837,197,2.665,217,3.467,242,3.709,249,2.267,258,2.059,261,1.519,275,2.647,288,2.837,301,1.27,409,3.636,470,4.074,473,3.912,511,2.858,663,4.611,671,3.267,1035,3.436,1187,4.92,1468,7.062,1470,7.062,1476,5.262,1477,3.912]],["t/145",[4,1.813,14,0.994,55,1.972,58,0.984,78,0.923,83,0.395,91,0.9,139,2.109,179,2.511,218,3.065,239,2.643,258,1.612,265,1.815,277,3.251,288,3.321,349,1.946,382,4.424,396,2.195,402,1.274,421,2.799,422,2.795,470,2.62,572,3.375,606,3.289,623,3.063,648,3.68,649,2.398,651,3.68,663,4.433,671,3.88,673,4.726,717,2.597,752,3.815,951,4.547,994,5.369,1035,3.579,1075,6.445,1109,5.605,1113,3.573,1158,4.424,1187,2.934,1457,3.956,1468,5.529,1470,5.529,1471,4.313,1472,5.251]],["t/147",[8,3.17,51,1.417,83,0.462,98,2.983,99,3.626,100,1.025,109,1.614,114,3.13,137,1.755,157,3.102,159,5.273,161,6.788,165,3.125,167,3.492,169,1.623,172,4.283,178,6.019,184,7.778,185,3.832,225,1.696,234,2.453,236,3.685,250,1.659,256,3.265,265,1.19,283,2.113,301,1.399,346,2.226,393,4.013,402,1.387,403,2.803,407,3.241,419,1.938,428,4.695,511,2.436,514,4.586,548,3.95,572,3.674,707,4.013,910,5.273,924,3.451,926,4.95,962,5.461,993,3.162,1015,6.422,1021,2.983,1033,4.226,1478,7.04,1479,7.04,1480,6.019,1481,7.04,1482,7.04,1483,8.302]],["t/149",[48,3.262]],["t/151",[14,0.835,51,1.634,55,1.861,58,1.236,62,1.018,74,2.467,82,2.453,91,0.709,134,1.935,160,3.028,179,3.152,223,4.381,224,3.539,282,2.637,328,3.505,352,4.419,454,4.967,475,4.419,480,1.92,511,2.809,624,5.173,633,4.874,646,2.276,894,2.936,963,2.871,1264,6.942,1428,4.129,1484,6.942,1485,8.119,1486,8.119,1487,8.119,1488,8.119,1489,4.485,1490,8.119,1491,8.119,1492,6.593]],["t/153",[14,1.104,55,2.461,74,2.113,179,3.579,203,6.33,468,3.511,585,6.481,669,4.182,1493,5.68]],["t/155",[4,2.454,14,1.071,55,2.387,103,1.236,261,2.045,304,8.456,310,5.838,461,4.543,470,3.545,472,3.371,585,6.154,1119,5.936,1288,6.154,1476,5.576,1494,8.753,1495,4.696,1496,4.696]],["t/157",[14,1.083,51,1.795,62,1.118,74,2.044,83,0.409,109,2.044,203,6.208,230,3.3,396,2.858,411,2.426,461,3.643,553,3.153,836,5.257,1100,5.167,1345,4.045,1427,9.002]],["t/159",[14,1.071,62,1.098,91,0.764,203,5.161,284,3.383,304,7.107,317,1.849,321,5.354,333,2.424,368,3.608,396,2.232,461,4.543,480,1.688,553,3.095,867,3.608,1050,4.696,1345,5.044,1427,7.484,1497,6.806]],["t/161",[14,1.077,83,0.405,100,0.972,134,2.75,203,6.174,250,2.082,311,2.831,637,6.671,704,4.55,1085,4.183,1345,5.236,1493,5.54]],["t/163",[4,2.431,14,0.892,78,1.238,83,0.398,130,2.837,134,2.067,160,3.235,172,3.367,189,5.026,198,5.114,203,5.114,209,2.032,224,3.781,242,3.895,250,2.044,433,3.895,464,5.335,468,3.387,470,3.513,624,5.526,639,6.497,1345,5.024,1498,7.915]],["t/165",[2,2.982,14,1.049,50,3.78,59,2.528,62,0.79,63,2.745,74,1.443,78,0.899,80,2.004,83,0.468,91,0.992,97,2.465,103,0.889,109,1.443,134,2.43,145,1.866,160,2.348,179,2.445,197,2.031,203,5.625,212,1.128,221,2.277,241,2.405,250,1.484,253,2.573,255,2.33,258,1.57,269,4.896,275,3.058,277,3.004,284,2.045,301,0.968,333,1.744,334,2.596,342,2.425,352,3.427,369,2.828,472,2.425,480,1.215,511,2.178,531,3.015,553,2.989,578,3.525,624,4.011,633,3.78,635,3.015,642,3.589,872,2.718,908,2.643,919,4.011,995,3.05,1024,2.917,1035,2.619,1050,3.378,1100,3.649,1119,4.818,1179,5.746,1476,5.385,1477,4.518,1499,5.113,1500,5.746,1501,5.746,1502,6.296,1503,5.383,1504,6.296,1505,6.296,1506,6.296,1507,6.296,1508,6.296,1509,6.296,1510,6.296,1511,4.307,1512,6.296,1513,6.296]],["t/167",[14,1.089,134,2.145,203,6.242,275,2.884,277,2.651,369,4.042,578,3.553,872,3.886,1119,6.035,1476,6.745,1477,4.262,1514,6.329]],["t/169",[61,4.864,253,3.958,261,1.782,989,6.46]],["t/171",[55,2.083,83,0.417,91,0.793,160,3.389,254,1.654,290,4.946,291,4.303,321,5.558,470,3.68,569,3.649,578,2.781,624,5.789,650,4.21,1050,4.875,1055,4.352,1515,5.669,1516,4.62]],["t/173",[14,0.962,74,2.144,91,0.816,107,2.229,203,5.515,269,7.274,346,2.958,470,3.789,578,2.862,919,5.959,990,3.963]],["t/175",[6,1.368,14,0.858,51,0.819,58,0.619,59,1.633,61,2.042,62,1.047,65,2.878,66,1.732,69,2.042,74,0.932,77,1.464,78,1.067,80,1.967,83,0.464,87,2.591,91,0.922,100,0.998,101,1.397,103,0.575,104,1.176,111,1.349,119,2.042,124,1.647,129,1.926,134,1.989,137,1.014,139,0.997,140,1.677,142,1.606,144,1.295,146,1.068,162,1.198,163,1.606,169,0.937,177,4.067,182,1.387,185,4.543,186,3.712,189,2.357,199,1.826,203,3.643,204,2.042,209,1.448,212,1.495,213,2.713,217,1.707,218,2.662,224,1.773,225,0.98,227,3.196,228,1.865,236,2.503,239,1.662,244,3.046,255,2.287,261,0.748,265,1.045,273,1.845,277,1.198,282,0.997,283,1.221,291,2.926,296,1.79,298,1.707,303,1.19,317,1.578,329,3.423,340,1.326,344,2.538,345,1.482,349,1.244,359,1.417,396,1.576,397,3.477,398,2.218,399,2.72,402,1.472,406,2.767,408,1.97,409,1.79,410,1.619,411,1.681,432,2.068,436,1.269,465,5.424,470,2.503,473,2.926,481,2.063,511,1.407,524,2.357,530,2.095,545,1.566,566,1.739,581,2.86,585,2.86,601,1.417,626,2.538,643,1.739,646,1.14,648,1.739,649,1.722,669,1.845,715,2.488,728,2.488,766,3.046,769,2.591,828,1.926,836,2.398,888,1.517,908,1.707,923,2.947,924,3.029,948,2.649,956,1.97,963,1.438,964,2.042,973,2.318,985,4.477,990,1.723,1012,2.713,1026,2.068,1050,4.478,1051,3.163,1113,2.247,1141,2.538,1165,2.488,1189,2.591,1230,2.442,1253,3.163,1326,1.739,1348,2.123,1517,2.538,1518,2.947,1519,2.442,1520,2.86,1521,3.712,1522,3.539,1523,5.597,1524,5.811,1525,3.163,1526,3.302,1527,3.466,1528,3.477,1529,4.067,1530,2.591,1531,2.152,1532,4.227,1533,1.808,1534,2.095,1535,3.046,1536,4.025,1537,3.712,1538,2.86,1539,2.442]],["t/177",[6,2.957,14,0.904,82,2.656,83,0.403,91,0.911,107,2.095,124,4.23,130,2.876,163,3.471,170,4.074,189,5.096,197,2.837,203,5.184,277,2.59,301,1.352,430,2.217,524,5.096,606,4.471,705,5.184,1540,8.793,1541,8.024,1542,8.024]],["t/179",[1,1.792,58,1.537,62,0.886,69,3.548,78,1.442,80,2.249,91,1.018,100,0.991,121,4.603,134,2.173,146,1.855,179,2.744,196,2.081,209,1.656,215,2.427,239,2.887,253,4.126,255,3.375,275,2.264,284,2.295,287,2.264,303,2.669,315,2.068,349,1.422,371,3.937,376,3.08,398,3.273,411,1.922,473,3.346,477,2.848,553,2.499,568,3.274,578,2.162,646,1.981,842,4.095,921,4.502,930,5.495,1100,5.285,1108,5.12,1124,4.409,1239,6.042,1305,6.042,1477,5.522,1543,7.066,1544,5.12,1545,5.495]],["t/181",[14,0.814,62,1.229,83,0.488,91,0.855,115,4.132,124,3.207,138,3.207,188,5.497,189,6.167,254,1.441,339,3.927,340,1.7,422,2.572,468,2.589,479,2.976,661,8.942,705,4.668,751,7.225,1067,5.416,1119,4.513,1124,4.94,1245,4.078,1345,4.446,1476,6.243,1493,4.189,1546,7.917,1547,7.917,1548,7.917,1549,7.917,1550,7.917,1551,7.917,1552,7.917,1553,7.917,1554,7.917,1555,7.225,1556,7.917,1557,5.567,1558,7.917]],["t/183",[14,1,62,0.981,83,0.446,91,0.924,103,1.105,118,5.543,145,2.318,175,2.489,179,3.036,189,4.532,203,4.61,209,1.832,214,2.408,219,3.879,231,2.575,234,2.725,292,6.081,298,3.283,304,6.349,359,3.858,373,4.982,376,3.409,402,1.541,405,4.256,461,3.973,553,2.765,644,6.349,720,2.54,1187,4.801,1208,4.387,1345,3.548,1456,4.387,1559,4.982,1560,4.982,1561,7.82,1562,9.724,1563,7.136,1564,6.081,1565,7.136]],["t/185",[1,2.079,4,1.687,51,1.211,52,1.784,55,1.379,58,1.248,62,0.755,74,1.379,77,2.372,80,1.916,82,1.818,83,0.507,85,3.92,86,4.232,91,0.943,100,0.761,103,1.158,104,1.74,129,2.85,134,2.222,169,1.889,182,2.052,189,4.751,219,2.985,221,2.176,223,3.609,225,1.45,227,2.574,228,2.759,236,2.438,242,2.703,253,2.459,254,1.095,265,1.577,282,1.475,287,1.929,301,0.926,303,1.762,311,1.929,315,1.762,340,1.292,373,3.834,378,3.548,392,3.548,398,3.593,402,1.186,407,2.144,433,2.703,438,2.28,511,2.836,556,1.83,557,3.022,560,2.95,565,3.022,566,2.574,578,1.842,582,5.608,675,3.92,917,2.788,922,4.68,932,4.887,933,4.887,940,3.548,975,4.014,976,3.92,1055,3.926,1233,3.022,1288,4.232,1455,4.887,1523,6.141,1566,3.1,1567,8.198,1568,5.94,1569,9.863,1570,5.608,1571,6.018,1572,6.378,1573,6.018,1574,5.492,1575,4.36,1576,6.018]],["t/187",[48,3.262]],["t/189",[8,1.594,14,1.16,56,2.622,58,1.503,62,0.846,63,1.994,64,1.437,67,1.776,78,1.347,83,0.481,91,0.934,99,2.356,100,0.822,103,0.953,104,1.951,107,1.09,111,1.517,117,2.318,139,1.121,144,2.148,146,1.201,157,1.56,165,1.571,170,2.12,200,2.517,201,5.928,209,2.667,225,1.102,237,2.054,252,2.014,258,1.682,265,1.496,277,2.78,301,0.704,317,0.966,329,2.351,334,1.886,340,1.449,349,1.614,352,2.49,380,3.25,402,1.33,409,2.014,411,1.836,418,3.195,419,2.207,420,3.637,421,2.658,422,2.192,430,1.153,451,2.166,478,2.696,480,1.547,497,1.571,553,1.618,556,1.391,578,2.453,590,2.12,600,4.744,605,2.914,606,2.326,623,2.166,626,2.854,653,3.129,654,2.527,663,2.097,671,3.405,694,2.746,752,2.497,780,2.608,790,2.326,919,2.914,934,2.388,938,3.051,943,3.165,964,2.297,1094,3.911,1157,3.427,1262,2.566,1299,2.98,1361,3.911,1496,2.454,1568,6.507,1577,3.314,1578,2.49,1579,4.575,1580,4.575,1581,4.575,1582,5.247,1583,5.769,1584,4.575,1585,4.575,1586,1.837,1587,3.714,1588,2.798,1589,8.017,1590,6.748,1591,2.697,1592,3.314,1593,4.575,1594,4.575]],["t/191",[58,1.576,83,0.508,100,0.805,209,2.032,250,2.044,287,2.779,301,1.334,315,2.539,419,2.388,421,2.199,452,3.364,477,2.446,480,1.673,497,2.979,578,3.51,944,3.235,1208,4.866,1347,7.415,1595,5.526,1596,7.042]],["t/193",[14,1.116,51,1.489,58,1.429,59,2.97,62,0.927,63,3.224,83,0.473,91,0.819,103,1.045,109,1.695,117,3.225,140,3.049,145,3.32,153,2.278,177,4.025,209,2.417,225,1.782,241,2.825,288,2.54,349,1.489,368,3.87,421,2.84,472,2.848,480,1.427,553,2.615,569,2.97,578,3.321,671,2.402,717,2.97,838,5.2,1005,5.187,1039,2.758,1129,4.149,1467,5.636,1496,3.968,1597,7.395,1598,7.395,1599,7.395,1600,7.395,1601,5.751,1602,7.395]],["t/195",[7,2.641,14,1.169,51,1.266,56,1.362,58,1.507,62,0.789,67,2.944,74,1.442,80,1.326,82,1.258,83,0.467,91,0.662,100,0.92,109,0.955,113,2.267,116,2.993,117,2.902,138,1.687,139,1.021,140,1.717,141,2.066,144,2.003,162,1.227,163,1.644,165,1.431,182,1.42,196,2.488,197,1.344,209,2.532,210,1.991,212,1.127,214,1.938,217,1.748,231,2.782,249,0.939,258,1.568,268,3.328,275,1.335,284,1.353,286,1.833,301,0.968,328,2.716,332,3.048,333,1.154,339,2.066,346,1.317,350,4.667,393,2.374,402,0.821,407,1.484,421,2.585,430,1.586,432,2.118,474,3.554,480,0.803,497,2.161,521,2.938,522,2.235,556,1.266,578,2.919,646,1.168,650,3.513,671,1.353,720,1.353,752,2.328,817,3.848,934,2.174,992,5.611,1021,1.765,1039,2.346,1089,2.235,1111,3.957,1144,5.854,1189,2.654,1256,2.018,1262,2.337,1389,3.239,1428,4.296,1456,3.529,1457,4.638,1465,3.018,1603,4.165,1604,4.165,1605,3.801,1606,3.561,1607,3.561,1608,3.561,1609,3.561,1610,3.561,1611,3.561,1612,3.382,1613,3.756,1614,3.239,1615,4.165,1616,4.165,1617,8.466,1618,3.801,1619,3.801,1620,2.337,1621,4.165,1622,4.165,1623,2.267,1624,3.239,1625,3.12,1626,4.165,1627,3.561]],["t/197",[14,1.021,51,1.627,58,1.23,82,2.442,83,0.371,117,2.777,138,3.275,163,3.192,209,2.326,274,3.192,333,2.239,376,3.524,393,4.608,474,3.563,497,3.409,752,4.145,964,4.06,992,4.865,1039,3.015,1144,5.569,1457,4.946,1595,5.151,1605,7.378,1606,6.912,1607,6.912,1608,6.912,1609,6.912,1610,6.912,1611,6.912,1614,6.287,1617,9.184,1619,7.378]],["t/199",[14,1.116,56,2.419,58,1.125,83,0.43,113,5.11,117,3.225,145,2.783,165,2.54,196,2.178,209,2.2,212,1.325,230,2.737,254,1.709,301,1.137,315,2.165,333,2.048,349,1.489,353,1.782,409,3.255,468,2.419,474,2.654,497,2.54,521,2.768,578,3.427,623,3.502,880,4.025,990,3.133,992,3.625,1005,5.187,1496,3.968,1586,2.97,1606,6.323,1607,6.323,1608,6.323,1609,6.323,1610,6.323,1611,6.323,1613,4.586,1618,6.749,1628,5.441,1629,6.749,1630,6.323,1631,7.395,1632,6.749,1633,6.749]],["t/201",[14,1.142,81,3.47,83,0.362,91,0.688,117,3.357,139,1.932,145,2.337,162,2.322,196,3.129,209,2.29,258,1.965,261,1.798,336,3.541,346,2.493,373,5.023,521,2.967,545,3.036,578,3.25,752,2.918,867,3.25,923,5.713,1456,4.423,1630,9.759,1634,8.625,1635,6.131,1636,9.774,1637,7.195]],["t/203",[7,3.129,14,1.155,58,1.134,74,1.708,77,1.766,83,0.433,91,0.65,100,0.876,103,1.053,109,1.708,111,2.471,116,2.942,139,2.312,172,3.663,197,2.405,209,2.211,212,1.335,217,3.129,230,2.758,288,2.56,289,4.319,303,2.181,332,3.61,345,2.716,346,2.357,359,2.597,442,5.062,474,3.386,521,2.641,545,2.87,746,6.333,752,3.492,1111,3.89,1586,3.789,1613,3.313,1630,8.067,1638,5.808,1639,7.661,1640,7.453,1641,6.372,1642,4.971,1643,6.052]],["t/205",[6,1.802,7,2.249,55,1.228,58,0.815,60,1.968,80,3.032,83,0.437,91,0.955,97,2.098,103,0.757,104,1.549,107,2.089,108,2.875,117,3.58,119,2.691,137,1.336,145,1.588,150,2.691,157,2.579,179,2.081,209,2.697,212,1.355,215,1.841,217,3.175,227,3.235,233,5.882,250,1.263,258,1.885,261,0.986,265,0.906,274,2.115,282,1.313,300,3.666,301,1.163,303,1.568,315,2.94,326,3.768,333,2.638,336,3.937,342,2.064,346,2.772,371,2.313,379,5.169,406,1.777,421,1.359,431,2.566,474,1.923,521,2.732,545,2.064,548,3.006,553,3.1,568,3.505,569,2.152,578,2.914,589,2.098,591,2.229,598,3.574,606,3.846,631,3.666,653,3.666,673,2.457,702,2.691,820,2.483,830,4.89,867,3.118,935,2.431,963,1.895,974,3.891,1023,3.491,1101,4.014,1155,4.771,1195,4.541,1348,2.797,1456,3.006,1539,3.217,1578,2.917,1586,3.824,1637,4.89,1644,5.359,1645,5.359,1646,5.359,1647,4.351,1648,3.217,1649,5.359,1650,4.167,1651,4.014,1652,4.89,1653,4.89,1654,4.582]],["t/207",[4,1.807,7,2.706,8,2.246,55,2.213,58,1.567,62,1.077,80,2.052,83,0.443,91,0.962,103,1.213,104,1.863,130,2.108,165,2.214,205,2.837,209,2.512,214,1.985,217,2.706,221,2.331,225,1.553,227,2.756,234,2.246,241,3.279,250,1.519,261,1.186,265,1.09,298,3.604,301,0.991,303,1.887,315,1.887,317,1.361,333,1.785,336,2.894,342,2.482,346,2.038,347,2.924,348,3.943,351,3.943,353,1.553,400,3.458,402,1.27,407,2.296,411,2.917,421,1.634,422,2.094,436,2.679,445,2.81,452,2.094,480,1.243,511,2.23,553,3.036,566,2.756,578,2.627,590,2.986,650,3.978,720,2.094,887,2.986,924,3.159,934,3.364,939,3.616,979,1.332,1050,3.458,1208,3.616,1345,2.924,1380,3.943,1383,3.869,1655,4.67,1656,6.604,1657,4.828,1658,5.233,1659,3.674,1660,5.511,1661,5.233,1662,5.511]],["t/209",[14,0.982,51,1.53,58,1.157,62,0.953,63,4.778,65,2.927,67,2.951,83,0.349,103,1.074,162,2.239,165,2.611,197,2.452,209,2.239,225,1.832,239,3.904,249,2.154,250,1.791,286,3.346,301,1.469,315,2.225,328,3.282,329,2.649,349,1.923,402,1.498,407,2.708,468,2.486,480,1.466,513,4.951,521,1.939,671,2.469,673,3.485,717,3.052,752,2.813,817,4.65,908,3.191,912,3.967,959,3.379,979,1.571,1113,4.199,1125,3.282,1129,4.264,1371,5.911,1560,4.843,1657,5.694,1663,7.156,1664,7.602,1665,6.172,1666,6.172]],["t/211",[14,1.093,58,1.528,63,3.046,64,2.195,83,0.415,91,0.61,103,0.987,104,2.02,119,3.509,130,2.286,169,1.611,209,2.122,211,3.509,217,2.934,237,3.138,239,2.856,250,1.647,315,2.046,346,2.863,349,1.822,396,1.782,402,1.784,421,2.547,430,1.762,497,2.4,521,2.309,548,3.921,619,4.914,650,3.238,667,3.6,790,3.554,894,2.527,914,3.804,924,3.425,956,4.386,1097,5.539,1111,3.648,1165,4.275,1203,5.675,1245,3.6,1299,4.552,1345,5.207,1368,5.079,1376,4.05,1382,5.435,1519,4.195,1582,5.435,1656,4.781,1657,6.782,1663,6.782,1666,5.675,1667,4.552,1668,4.781,1669,5.064,1670,5.975,1671,3.238]],["t/213",[7,2.617,14,1.123,52,1.848,58,1.278,62,0.782,63,3.66,74,1.924,82,1.884,83,0.486,91,0.953,100,0.78,103,0.881,111,2.784,137,1.554,139,1.528,142,2.461,197,2.011,209,2.38,212,1.117,225,2.023,231,2.053,241,2.381,249,1.406,250,1.469,254,1.135,255,3.107,261,1.147,288,2.884,289,3.613,339,3.093,346,1.971,407,2.221,419,1.716,422,2.025,424,4.517,461,3.879,468,2.039,472,2.401,477,1.758,521,1.59,541,2.482,545,2.401,553,2.205,836,5.597,888,2.325,950,2.858,1146,4.158,1256,3.02,1431,5.062,1440,4.67,1642,4.158,1648,3.743,1662,5.331,1672,7.942,1673,6.289,1674,5.062,1675,4.639,1676,7.383,1677,5.062,1678,9.27,1679,6.235,1680,5.69,1681,6.235,1682,5.69,1683,6.235,1684,4.848,1685,4.848,1686,7.383,1687,6.049]],["t/215",[14,1.249,58,1.041,63,3.891,78,1.275,83,0.314,91,0.597,100,0.635,109,2.046,137,2.225,145,2.646,165,2.349,169,1.576,196,2.014,197,2.206,209,1.602,214,2.106,239,2.794,254,1.245,261,1.258,265,1.156,301,1.052,317,1.444,346,2.162,371,4.719,387,4.183,411,2.428,419,1.883,424,4.955,425,5.122,432,3.477,468,2.236,521,1.744,566,2.924,956,4.325,1039,2.55,1097,5.461,1256,3.313,1345,3.102,1355,6.107,1368,6.132,1431,5.552,1434,6.24,1440,6.687,1672,5.122,1678,9.615,1680,6.24,1682,6.24,1684,5.318,1685,5.318,1688,6.838,1689,4.955]],["t/217",[14,1.103,49,3.789,56,1.762,58,1.453,60,1.979,62,0.676,64,1.693,66,2.129,71,2.611,77,1.277,78,1.255,80,1.715,83,0.348,91,0.767,98,2.283,103,0.761,104,1.558,150,2.706,156,2.396,158,2.434,170,2.497,182,2.59,187,4.947,196,1.587,201,4.559,204,3.813,209,2.645,210,1.415,212,0.966,214,1.66,215,1.851,217,2.262,218,1.92,226,4.191,228,2.471,254,0.981,258,1.893,265,0.911,315,2.223,328,3.279,329,1.878,349,1.922,359,1.878,379,4.477,403,3.023,421,1.926,430,1.359,553,1.906,591,2.242,606,4.471,651,3.248,653,3.686,691,5.279,697,3.789,699,6.95,714,3.123,916,4.376,926,3.789,1039,2.01,1167,4.918,1170,3.789,1326,2.305,1370,2.552,1373,7.545,1376,3.123,1663,6.587,1690,6.836,1691,5.389,1692,5.389,1693,4.608,1694,5.389,1695,5.389,1696,7.594,1697,7.594,1698,5.389,1699,8.025,1700,5.389,1701,6.166,1702,5.389,1703,6.493,1704,6.493,1705,5.389,1706,5.389,1707,5.389,1708,5.389,1709,4.918,1710,5.389,1711,5.389,1712,5.389]],["t/219",[7,1.934,8,1.606,14,1.172,51,1.365,55,1.056,58,1.659,64,1.447,66,1.292,71,2.232,78,0.658,83,0.469,90,2.545,91,0.893,93,2.373,97,1.804,102,3.143,103,0.651,104,1.332,107,1.098,113,2.508,117,1.583,137,1.691,139,1.129,145,1.366,153,1.419,162,1.357,169,1.062,171,2.626,204,4.043,209,2.74,212,1.442,215,1.583,221,1.666,250,1.086,254,0.839,255,3.664,262,2.472,276,2.585,315,1.986,332,2.232,333,1.276,334,1.9,336,2.069,340,1.456,349,0.927,353,1.11,363,2.508,368,1.9,396,1.73,402,0.908,403,1.834,411,1.845,421,2.252,422,1.497,468,1.507,469,2.373,534,2.772,566,1.97,569,1.85,571,3.24,578,2.464,635,3.249,682,2.048,702,2.314,704,2.373,720,1.497,733,1.883,745,3.94,746,2.819,752,2.51,765,2.936,799,6.434,828,2.182,834,2.585,876,5.785,924,2.258,944,2.53,950,2.112,979,0.953,982,2.286,1008,3.741,1024,2.135,1039,3.003,1050,2.472,1097,2.819,1100,3.931,1111,3.541,1117,4.483,1140,3.451,1294,3.152,1308,3.451,1326,4.378,1355,3.152,1425,3.451,1450,3.073,1467,2.766,1560,2.936,1601,3.583,1672,3.451,1713,2.819,1714,3.94,1715,4.608,1716,4.205,1717,4.608,1718,4.608,1719,4.608,1720,6.784,1721,5.508,1722,6.784,1723,3.741,1724,3.24,1725,6.784,1726,4.608,1727,6.784,1728,4.205,1729,3.24,1730,3.073,1731,4.205,1732,3.741]],["t/221",[48,2.378,51,1.444,58,1.092,63,3.127,64,2.253,80,2.283,83,0.329,91,0.803,100,0.666,102,3.323,103,1.013,104,2.073,117,2.464,138,2.905,140,2.957,146,1.883,165,2.464,169,1.653,200,2.675,209,2.157,212,1.285,225,1.728,263,4.475,265,1.213,284,2.33,287,2.299,301,1.103,303,2.099,340,1.54,345,2.614,383,3.359,402,2.004,403,2.856,421,2.335,433,3.221,436,2.238,474,2.574,478,2.412,497,2.464,572,3.744,578,2.818,589,2.808,633,4.306,635,3.435,924,3.516,927,3.744,939,4.024,950,3.288,963,2.536,964,3.602,1018,5.824,1021,3.039,1105,4.157,1326,3.067,1467,4.306,1586,2.88,1592,5.197,1713,4.388,1733,7.173,1734,7.173,1735,6.133,1736,6.133,1737,7.173,1738,6.133,1739,7.173,1740,7.173,1741,5.373]],["t/223",[48,3.262]],["t/225",[7,2.135,14,1.054,48,1.687,51,1.024,58,1.606,60,1.868,62,0.914,65,3.582,78,1.328,83,0.391,91,0.859,100,0.677,106,1.975,107,1.212,145,1.508,150,2.555,155,5.919,175,1.619,212,0.911,214,1.567,221,1.84,225,1.226,234,1.773,249,1.147,258,1.817,261,1.711,265,0.86,301,1.121,317,1.54,328,2.196,336,2.284,364,2.729,370,3.491,380,1.868,402,1.002,406,2.825,407,1.812,410,2.025,418,4.034,419,1.4,420,3.865,421,1.29,430,1.283,436,1.587,443,3.393,480,0.981,518,2.655,557,2.555,569,2.043,577,4.862,597,3.577,663,2.332,705,2.999,714,2.948,880,4.636,909,2.587,938,3.393,940,5.022,941,3.573,963,1.799,979,1.052,1021,2.155,1114,3.174,1233,4.945,1234,3.956,1256,4.127,1489,4.706,1577,3.686,1578,2.769,1639,5.919,1742,6.652,1743,7.289,1744,7.289,1745,5.087,1746,5.087,1747,5.087,1748,5.087,1749,5.087,1750,5.087,1751,4.642,1752,5.087,1753,5.087,1754,5.827,1755,4.642,1756,6.381,1757,6.381,1758,2.691,1759,5.087,1760,4.642,1761,4.642,1762,3.81,1763,4.349,1764,3.577,1765,7.954,1766,9.847,1767,9.847,1768,8.49,1769,7.774,1770,6.652,1771,5.087,1772,5.087,1773,4.642]],["t/227",[58,1.026,59,2.707,62,1.109,78,1.409,83,0.309,91,0.588,100,0.973,101,2.316,103,0.952,106,2.617,107,1.606,111,2.235,115,3.519,124,2.731,180,1.834,190,4.295,212,1.208,214,2.724,219,4.387,225,1.624,249,1.52,254,1.61,270,5.899,274,2.661,301,1.037,317,1.868,347,4.013,393,3.843,402,1.328,438,2.554,465,3.091,480,1.706,518,3.519,556,2.05,588,3.193,589,2.639,594,4.612,635,3.229,643,2.883,667,4.556,684,3.724,705,3.975,733,2.755,894,2.438,928,5.125,989,4.496,993,3.027,1050,4.745,1170,4.74,1171,3.229,1233,3.385,1256,4.782,1299,4.391,1348,3.519,1349,4.74,1405,5.474,1499,5.474,1516,4.497,1522,3.193,1523,5.05,1557,4.74,1572,4.612,1754,4.612,1774,6.742,1775,5.474,1776,4.885,1777,6.152,1778,5.474,1779,5.764,1780,6.742,1781,6.742]],["t/229",[14,1.07,71,3.209,78,1.545,83,0.401,91,0.908,100,1.052,107,1.578,134,1.578,138,2.683,139,1.623,158,3.136,196,1.951,197,2.137,212,1.187,265,1.12,277,1.951,294,2.308,301,1.019,308,5.248,347,3.006,452,2.152,468,2.859,474,2.378,477,2.466,479,2.49,568,4.051,569,2.66,580,2.86,681,2.36,894,3.162,935,3.006,940,3.906,993,2.975,1233,4.39,1251,5.379,1256,3.209,1280,4.532,1299,4.315,1489,4.83,1667,4.315,1754,4.532,1756,6.549,1757,7.33,1762,4.962,1763,5.664,1764,4.658,1782,6.334,1783,8.931,1784,6.625,1785,6.67,1786,6.045,1787,8.743,1788,6.625]],["t/231",[14,1.188,58,0.672,62,1.166,64,1.387,66,1.238,78,1.392,83,0.36,91,0.759,100,0.905,103,0.624,104,1.276,107,2.073,111,1.464,113,2.403,115,2.305,141,2.19,145,1.309,155,5.337,158,2.106,160,1.647,162,1.301,167,2.19,182,1.506,196,1.301,211,2.217,212,0.791,218,1.573,219,2.19,225,1.584,250,1.85,253,1.804,254,0.804,258,1.101,261,0.812,270,2.945,272,5.62,274,2.595,277,1.301,294,1.539,315,1.292,342,1.7,345,1.609,347,4.581,401,3.307,418,2.091,420,2.003,448,2.876,468,2.568,480,1.268,511,3.009,541,1.758,553,1.561,557,2.217,577,2.945,622,2.068,645,2.517,646,1.238,650,2.046,659,5.112,679,3.199,720,1.434,834,2.477,894,3.146,939,2.477,940,2.603,941,3.222,944,1.647,959,1.963,972,2.876,989,2.945,1028,2.701,1085,2.091,1104,3.307,1208,2.477,1233,5.07,1256,4.505,1271,3.199,1489,5.578,1519,2.651,1528,5.62,1557,3.105,1738,3.775,1754,3.021,1756,7.563,1757,7.563,1761,5.999,1762,4.924,1763,5.62,1764,4.622,1765,3.775,1768,4.03,1769,4.03,1776,3.199,1782,6.043,1785,6.727,1789,4.416,1790,3.105,1791,6.573,1792,4.416,1793,7.581,1794,3.199,1795,4.03,1796,5.62,1797,4.416,1798,4.416,1799,4.416,1800,7.852,1801,3.775,1802,4.03,1803,2.813,1804,4.384,1805,4.03,1806,3.775,1807,4.416]],["t/233",[14,1.16,78,1.361,91,0.91,112,3.366,158,3.054,253,3.094,258,1.887,261,1.393,274,2.989,317,1.599,349,1.524,352,4.121,470,3.86,480,1.461,623,3.585,659,5.888,720,2.459,817,4.631,894,2.738,1100,4.388,1147,5.671,1233,3.802,1251,6.148,1256,3.668,1489,4.183,1756,5.671,1757,5.671,1782,5.432,1785,7.097,1794,7.93,1806,6.473,1808,7.556,1809,8.468,1810,7.571,1811,7.571]],["t/235",[5,3.266,14,1.04,78,1.444,83,0.485,91,0.883,100,0.573,101,2.865,107,1.988,121,5.433,129,2.924,158,1.979,162,1.818,177,3.36,180,1.68,196,1.818,197,1.992,212,1.106,214,1.902,228,3.824,249,1.392,256,2.216,261,1.136,265,1.044,283,1.853,288,2.121,317,1.304,347,2.801,422,2.005,428,4.118,512,3.852,569,3.35,598,4.118,622,2.892,659,4.801,736,3.64,894,3.659,925,3.519,940,4.918,993,4.543,994,3.852,1039,2.303,1042,3.411,1233,4.189,1256,4.576,1359,4.224,1489,4.608,1528,5.279,1557,7.43,1754,4.224,1756,6.248,1757,6.248,1764,5.865,1782,3.519,1785,7.025,1793,6.487,1804,7.048,1805,5.634,1806,5.279,1809,5.013,1812,4.341,1813,6.174,1814,6.174,1815,6.174,1816,3.934,1817,6.174]],["t/237",[4,1.656,55,1.354,59,3.708,65,3.117,71,3.921,74,1.354,78,1.318,83,0.371,91,0.806,103,0.834,114,2.626,116,2.332,139,1.447,148,2.626,160,2.203,162,1.74,179,3.142,182,2.014,209,1.384,214,1.819,221,2.136,223,3.563,224,3.528,234,2.058,239,2.413,256,2.905,261,1.489,265,0.999,282,1.447,303,1.729,322,2.526,345,2.949,350,3.263,377,4.279,394,3.125,399,4.372,409,2.6,418,2.797,430,2.328,438,3.067,463,4.796,466,3.921,469,4.169,478,1.986,480,1.139,511,2.043,526,3.546,595,3.169,623,2.797,654,4.471,667,3.042,731,4.424,733,3.307,907,3.042,909,3.003,923,4.279,955,5.05,979,1.673,982,2.93,1042,3.263,1088,2.392,1183,6.014,1200,3.482,1271,4.279,1378,4.04,1382,4.593,1492,4.796,1516,3.003,1531,4.885,1650,4.593,1741,4.424,1782,5.931,1785,7.027,1793,4.593,1818,5.906,1819,5.906,1820,4.424,1821,4.796,1822,5.39,1823,5.906,1824,3.042,1825,4.796,1826,5.39,1827,5.05,1828,5.39]],["t/239",[78,1.378,91,0.842,100,0.717,103,1.091,107,1.841,109,1.77,116,3.049,124,3.909,145,2.29,196,2.275,210,2.028,228,3.541,234,2.692,241,2.951,249,2.176,254,1.406,265,1.306,283,2.319,287,2.475,303,2.261,347,3.505,359,2.692,404,5.786,433,3.469,436,2.411,470,3.129,511,2.672,581,5.431,989,5.152,990,3.273,1516,3.928,1557,6.785,1776,5.597,1782,5.999,1785,6.617,1794,5.597,1796,6.604,1808,5.597,1809,6.272,1829,6.604,1830,8.251,1831,5.431,1832,6.007]],["t/241",[1,2.361,2,2.841,55,1.375,78,1.329,82,2.471,83,0.375,93,3.09,97,2.349,100,0.76,101,3.795,103,1.156,110,4.347,124,3.313,129,2.841,130,1.962,137,1.495,139,1.47,144,1.91,153,2.867,175,1.91,183,3.131,184,5.129,185,3.265,225,1.446,240,4.218,246,4.218,250,1.928,256,2.153,257,5.084,261,1.104,265,1.574,273,2.722,287,1.923,301,0.923,303,1.756,315,1.756,322,2.566,325,4.104,342,2.31,345,2.186,383,2.81,402,1.182,405,3.265,406,1.989,407,2.914,410,2.388,411,2.225,430,1.513,431,2.873,438,2.273,445,2.615,452,1.949,525,4.494,559,2.81,589,2.349,595,3.219,676,2.409,797,4.218,882,3.013,894,2.17,1005,4.519,1088,3.77,1105,3.477,1256,2.906,1339,2.667,1447,3.537,1536,6.063,1741,4.494,1785,3.601,1833,6.994,1834,2.976,1835,4.218,1836,4.494,1837,3.537,1838,5.999,1839,5.475,1840,6.737,1841,4.519,1842,3.67,1843,4.27,1844,3.131,1845,5.475,1846,4.494,1847,4.871,1848,5.999,1849,5.475,1850,4.494]],["t/243",[1,1.717,4,1.898,6,2.277,14,0.696,62,1.008,64,1.444,65,1.77,74,1.054,78,1.499,83,0.434,91,0.893,96,4.761,98,1.947,100,0.878,101,2.326,103,0.957,104,1.329,107,1.613,115,2.399,127,3.066,130,1.503,153,1.416,158,3.364,169,1.059,172,2.629,176,2.994,177,2.502,187,2.994,197,1.483,209,2.318,212,0.824,214,1.416,228,4.335,237,2.064,249,1.036,250,1.083,254,0.837,261,1.246,265,0.777,270,3.066,277,1.354,283,2.033,284,1.493,311,2.576,347,3.072,353,1.108,371,1.984,379,2.71,383,3.172,396,1.172,402,1.584,406,1.524,411,1.25,422,1.493,427,3.359,430,1.159,433,2.064,439,3.4,443,3.066,452,1.493,477,2.502,481,2.261,511,3.069,553,1.625,556,1.398,564,2.432,578,1.407,586,3.574,591,1.912,595,2.466,623,3.207,659,5.266,667,2.368,686,2.432,882,2.308,887,2.13,894,3.699,948,2.994,959,2.044,974,1.878,979,0.95,997,2.085,1031,2.868,1089,2.466,1192,3.93,1348,3.534,1370,2.177,1371,3.574,1516,3.443,1520,3.232,1525,3.574,1557,4.761,1758,2.432,1764,3.232,1782,5.39,1785,7.049,1793,5.266,1794,4.906,1796,3.93,1804,4.516,1809,5.498,1812,3.232,1824,2.368,1851,4.597,1852,4.597,1853,4.597,1854,4.597,1855,4.195,1856,4.195,1857,3.066,1858,4.597,1859,4.195,1860,2.868,1861,4.597,1862,4.597,1863,4.597,1864,4.597,1865,3.93,1866,4.597,1867,3.732]],["t/245",[48,3.262]],["t/247",[1,1.624,14,0.658,58,0.974,62,1.072,63,2.791,74,2.205,77,2.535,78,0.914,82,2.582,83,0.441,91,0.746,97,2.506,100,0.595,101,2.199,103,0.904,104,1.851,106,2.486,107,1.525,111,2.123,115,3.341,139,2.095,188,3.592,196,1.886,206,2.445,210,1.681,212,1.147,215,2.199,217,2.687,228,2.935,230,2.369,231,2.108,234,2.231,253,3.492,254,1.165,258,1.596,273,2.905,282,2.095,288,3.305,296,2.818,298,2.687,301,0.985,329,2.231,333,1.773,336,2.875,359,2.231,368,3.524,373,4.079,430,2.155,431,3.066,438,3.239,466,3.101,468,2.094,474,2.298,496,5.864,531,3.066,548,3.592,556,1.947,557,4.292,569,2.571,586,4.978,728,3.916,867,2.639,982,3.176,989,4.27,995,3.101,1070,4.501,1348,3.341,1860,3.994,1868,6.402,1869,5.198,1870,7.308,1871,4.978,1872,6.402,1873,6.402,1874,3.71]],["t/249",[1,1.997,14,0.897,51,0.893,57,3.056,58,1.644,59,3.162,62,1.224,63,1.934,64,1.394,77,2.598,78,0.942,83,0.203,91,0.387,99,2.285,100,0.731,101,1.524,106,1.722,109,1.017,119,4.378,124,1.797,129,2.101,137,1.644,139,1.087,140,1.829,142,1.751,145,1.315,146,1.732,157,1.513,158,3.129,180,1.207,188,4.417,191,1.862,200,1.655,204,3.954,218,1.58,221,2.385,225,2.101,227,1.897,230,1.642,236,1.797,238,3.602,258,1.644,259,4.417,261,0.816,274,2.604,275,2.523,282,2.283,288,2.994,295,2.489,297,2.315,301,1.014,303,1.298,349,0.893,378,2.616,402,1.3,403,1.766,407,1.58,421,1.125,422,1.441,430,2.55,438,3.303,439,2.228,454,2.714,477,1.251,478,1.492,556,2.005,559,2.078,580,1.915,587,3.45,589,3.823,590,2.055,633,2.663,651,3.367,652,4.512,671,1.441,685,3.793,699,4.913,705,2.616,707,2.529,720,1.441,722,2.489,733,3.562,768,3.119,774,2.256,944,1.655,962,2.663,1015,2.826,1016,2.959,1024,3.056,1035,1.845,1047,2.571,1169,2.663,1212,2.826,1360,3.793,1398,2.489,1493,2.347,1539,2.663,1671,3.648,1677,3.602,1701,7.929,1703,6.732,1842,4.035,1860,4.913,1874,3.822,1875,5.129,1876,4.436,1877,4.048,1878,2.89,1879,5.356,1880,4.436,1881,4.436,1882,7.874,1883,7.874,1884,4.048,1885,4.436,1886,3.793,1887,4.436,1888,4.048,1889,4.436,1890,3.602,1891,2.285,1892,4.436,1893,4.436,1894,4.035,1895,3.793]],["t/251",[6,1.734,8,1.797,14,1.018,55,2.145,58,1.566,59,2.07,61,2.589,62,0.923,77,2.034,82,2.223,83,0.236,91,0.817,98,2.184,100,0.683,103,1.04,104,1.49,109,1.182,116,2.035,124,2.088,130,2.807,137,1.285,138,2.088,139,2.103,141,2.558,142,2.035,146,1.354,160,1.923,162,2.168,180,1.403,182,1.758,190,3.285,200,1.923,212,0.924,218,1.837,221,1.865,225,1.773,229,2.527,230,1.908,249,1.162,256,1.851,258,1.285,261,1.354,265,1.451,275,1.652,282,1.804,284,1.675,288,1.771,294,1.797,301,0.793,303,1.509,317,1.555,340,1.107,345,1.879,353,1.242,402,1.016,411,2.335,413,5.976,414,4.186,415,4.186,419,2.026,424,5.332,425,3.862,426,4.421,429,5.094,430,2.164,434,4.009,438,1.954,439,4.7,472,1.985,481,1.721,483,4.705,484,4.408,485,4.408,486,4.408,487,4.408,488,4.408,489,6.292,490,4.408,491,4.408,492,4.408,493,4.408,494,4.408,495,6.292,496,3.696,499,4.408,501,4.408,502,4.408,503,4.408,504,4.408,505,4.408,506,4.408,507,4.408,508,4.408,510,3.154,513,3.358,534,2.107,535,4.705,536,2.247,538,3.095,548,2.892,553,1.823,556,1.568,580,2.226,588,2.442,684,2.848,704,2.656,720,1.675,734,3.217,834,2.892,894,1.865,914,2.806,1042,2.848,1054,4.908,1140,3.862,1171,2.469,1473,3.736,1778,4.186,1842,3.154,1860,4.592,1874,4.265,1875,4.009,1896,4.066,1897,4.705]],["t/253",[1,1.308,4,0.905,8,2.566,14,1.142,49,2.269,51,0.65,55,1.845,58,1.612,62,1.01,64,1.014,65,1.243,66,0.905,67,1.253,74,1.688,77,2.288,82,0.975,83,0.464,91,0.703,100,0.684,102,1.495,103,1.04,104,0.933,106,1.253,109,1.183,111,1.07,117,1.772,130,2.108,134,2.148,137,1.607,139,1.804,145,1.529,169,0.744,172,1.253,175,1.027,182,1.759,190,5.129,191,1.355,196,1.52,197,1.665,205,1.421,206,2.462,207,2.056,210,1.692,212,1.442,215,1.109,218,1.838,225,1.94,231,1.063,234,1.798,249,0.728,250,0.76,253,2.108,254,0.587,259,1.811,261,0.594,265,0.872,275,2.36,282,1.58,283,0.969,288,1.109,294,1.798,295,1.811,296,1.421,297,1.684,301,0.793,303,0.945,317,1.09,332,2.499,333,2.039,346,1.021,349,1.039,354,3.738,355,2.417,368,1.331,370,2.471,371,1.393,372,3.219,402,0.636,406,1.07,411,0.878,419,1.42,421,1.308,422,1.048,424,4.67,425,4.828,426,3.793,429,2.808,430,1.857,431,1.546,437,2.417,438,3.658,439,2.591,452,1.048,458,1.708,461,2.108,465,1.48,471,1.684,472,1.987,473,4.066,474,1.158,477,0.91,480,0.995,497,1.772,521,0.823,531,1.546,534,1.319,543,2.986,545,1.987,546,2.269,553,1.141,557,1.621,578,0.988,588,1.528,589,2.883,596,1.355,606,2.624,623,1.528,627,3.287,632,1.662,645,1.84,646,1.446,649,0.899,671,1.676,684,1.783,686,2.73,720,1.676,721,2.621,733,2.108,734,4.022,752,1.909,769,2.056,774,1.641,781,2.621,820,1.495,867,1.331,887,1.495,902,1.512,908,1.355,927,1.684,974,1.319,975,2.153,982,1.601,989,2.153,1000,2.153,1088,1.307,1096,2.417,1109,2.102,1111,1.684,1153,3.529,1171,1.546,1187,1.464,1239,2.759,1300,2.621,1338,1.974,1339,1.435,1370,3.052,1402,2.014,1444,5.177,1446,2.269,1458,2.945,1514,5.177,1530,2.056,1531,1.708,1555,2.945,1595,2.056,1613,1.435,1657,3.865,1675,2.85,1676,5.012,1729,2.269,1730,2.153,1731,2.945,1778,2.621,1860,3.219,1874,2.99,1886,5.511,1894,1.974,1898,3.227,1899,3.227,1900,2.945,1901,6.166,1902,3.227,1903,3.227,1904,3.227,1905,3.227,1906,2.945,1907,3.865,1908,3.227,1909,3.227,1910,2.338,1911,2.417,1912,2.102,1913,5.159,1914,3.227,1915,2.269,1916,2.759,1917,2.417]],["t/255",[4,1.279,6,1.534,7,1.916,14,1.134,48,1.513,51,1.356,55,1.046,58,1.628,59,1.833,60,1.676,61,2.292,62,0.845,74,1.046,77,2.234,78,0.962,80,1.453,83,0.405,100,0.821,101,2.314,103,0.645,108,2.448,112,2.994,130,2.618,134,1.605,137,1.138,142,1.802,146,1.198,153,1.406,160,2.512,175,1.453,180,1.832,191,2.828,196,2.358,197,1.472,210,1.198,212,1.207,214,1.406,217,1.916,224,1.989,234,1.59,239,1.865,254,1.226,261,0.839,265,0.772,275,2.566,282,1.962,283,1.37,284,1.482,297,2.382,298,2.828,301,1.036,303,1.972,317,0.964,322,1.952,328,1.97,332,2.211,333,2.217,336,2.049,346,1.443,353,1.1,368,1.881,395,2.414,402,1.327,411,2.178,422,1.482,426,2.351,429,4.811,430,2.641,438,2.552,439,2.292,447,5.045,451,2.161,468,1.492,473,2.161,480,1.299,481,1.524,517,3.706,520,2.847,531,3.226,540,1.556,556,1.388,576,3.122,578,2.45,588,2.161,589,3.46,627,6.007,632,2.351,637,2.908,646,2.478,651,1.952,653,3.122,686,2.414,736,2.691,774,2.321,790,2.321,842,2.645,867,1.881,882,2.292,887,2.115,909,2.321,992,2.237,1014,2.092,1039,1.702,1057,2.601,1119,2.601,1125,1.97,1245,2.351,1339,2.029,1444,4.736,1462,2.691,1476,2.908,1709,4.165,1782,2.601,1785,4.043,1837,3.971,1842,2.792,1894,2.792,1918,4.165,1919,4.564,1920,4.564,1921,4.564,1922,3.706,1923,3.418,1924,4.564,1925,5.632,1926,3.549,1927,8.839,1928,3.549,1929,3.549,1930,6.736,1931,4.564,1932,4.564,1933,4.564,1934,4.165]],["t/257",[1,0.565,4,0.625,5,1.179,8,0.776,14,1.018,50,1.338,51,1.324,52,1.95,55,1.508,57,1.76,58,1.748,59,0.895,60,0.818,62,0.959,63,0.971,65,0.858,74,0.87,77,1.812,78,0.542,80,1.209,81,0.981,83,0.411,91,0.667,98,0.944,100,0.808,101,0.765,103,0.536,105,4.244,106,2.968,107,1.183,108,1.195,109,0.511,111,0.739,116,0.88,124,2.375,129,1.799,130,0.729,137,0.947,139,0.931,140,0.919,142,1.959,146,1.303,156,0.991,158,2.295,162,1.119,170,1.032,172,0.865,173,2.598,175,1.209,179,0.865,182,1.295,183,1.163,191,1.594,196,0.656,197,1.225,210,1.303,212,0.68,214,2.026,217,0.935,221,0.806,223,0.981,225,0.537,229,1.092,230,1.405,231,1.251,236,0.903,249,0.502,250,1.17,254,0.903,256,0.8,258,0.555,265,0.839,266,1.42,275,1.217,277,0.656,282,2.133,283,0.669,286,0.981,288,0.765,290,1.213,291,1.799,294,1.323,296,2.581,298,0.935,301,1.012,303,1.112,311,1.217,315,1.453,317,1.238,328,0.962,329,0.776,344,1.39,346,2.08,349,0.764,350,1.231,352,1.213,353,0.915,367,5.353,369,1.001,370,1.067,393,3.342,402,0.439,403,3.206,406,1.646,407,1.353,411,1.595,419,2.217,421,2.274,426,1.148,429,4.573,430,2.118,431,1.067,433,1.001,436,1.185,438,1.439,452,1.612,472,1.463,473,3.116,474,0.8,477,0.628,480,1.475,481,1.268,497,0.765,510,3.036,521,0.568,530,2.557,531,2.377,553,2.073,557,1.119,559,1.044,569,0.895,582,2.598,589,3.289,596,0.935,629,1.044,645,1.27,646,0.625,651,1.624,653,2.598,667,1.956,682,0.991,714,1.291,720,0.724,733,4.046,734,2.37,770,1.809,774,1.133,801,2.033,817,1.363,843,1.25,867,0.919,887,1.032,894,1.373,914,1.213,926,1.567,959,0.991,973,1.27,974,0.91,975,3.911,979,1.026,982,1.105,992,1.092,993,1.001,997,1.011,1005,1.231,1024,2.3,1036,0.953,1114,1.39,1125,2.84,1171,1.819,1184,1.809,1233,2.944,1247,1.567,1350,1.451,1359,1.524,1407,4.669,1444,7.365,1446,1.567,1465,1.614,1475,1.669,1516,1.133,1520,1.567,1527,1.25,1625,1.669,1667,1.451,1671,1.76,1673,1.669,1689,1.614,1783,4.53,1829,1.905,1837,1.314,1860,2.37,1874,5.652,1875,7.584,1879,8.04,1925,3.736,1935,1.614,1936,8.703,1937,2.228,1938,2.845,1939,1.614,1940,2.228,1941,3.233,1942,1.614,1943,3.466,1944,2.228,1945,1.905,1946,3.798,1947,1.451,1948,1.733,1949,2.228,1950,2.228,1951,1.567,1952,5.364,1953,3.466,1954,2.228,1955,2.228,1956,2.228,1957,2.228,1958,2.228,1959,2.228,1960,1.905,1961,2.033,1962,2.033,1963,2.228,1964,2.228,1965,1.451,1966,2.228,1967,1.809,1968,2.228,1969,2.228,1970,2.228,1971,2.67,1972,1.905,1973,1.733,1974,2.033,1975,2.228,1976,1.733,1977,2.228,1978,2.228,1979,2.228,1980,2.228,1981,7.162,1982,2.228,1983,2.228,1984,2.033]],["t/259",[14,1.081,49,4.774,55,2.037,58,1.352,59,2.726,61,3.409,64,2.133,77,1.609,83,0.407,91,0.952,103,0.959,107,1.618,118,5.646,156,3.018,163,2.68,221,2.455,224,2.96,231,2.926,261,1.249,282,1.664,283,2.038,303,1.987,340,1.458,345,2.474,346,2.147,359,2.366,402,1.338,411,2.417,419,1.869,422,2.205,423,6.196,424,4.919,425,5.086,426,3.497,429,5.391,437,5.086,439,4.462,481,2.267,496,4.974,510,4.153,531,3.252,534,2.774,538,4.076,553,2.401,675,4.423,734,4.236,774,3.452,939,3.809,972,4.423,973,3.87,1028,4.153,1170,4.774,1270,5.086,1370,3.215,1397,3.87,1456,3.809,1473,4.919,1564,5.28,1595,5.661,1894,4.153,1985,9.039,1986,8.886,1987,6.196,1988,6.196,1989,5.086]],["t/260",[1,1.39,4,1.537,7,3.226,14,1.109,58,1.17,67,2.128,77,2.103,83,0.251,97,2.146,103,1.086,107,1.306,109,1.256,124,2.22,129,2.596,130,1.793,134,2.502,137,1.916,141,2.719,170,2.54,172,2.128,179,2.128,180,1.491,208,4.454,209,1.801,225,1.321,226,4.263,246,3.854,249,1.236,250,1.292,254,0.998,261,1.863,265,0.927,266,3.493,282,1.884,283,2.307,288,1.883,290,2.984,303,1.605,317,1.623,344,3.42,345,1.998,346,1.733,347,2.487,351,3.353,376,2.39,395,2.9,400,2.941,402,1.08,406,1.818,426,3.959,439,3.86,441,3.854,442,4.123,448,3.571,451,2.596,455,3.656,477,1.546,496,4.457,510,3.353,526,3.291,534,3.141,538,4.614,545,2.111,568,2.54,590,2.54,606,3.908,632,3.959,675,3.571,774,3.908,790,2.788,826,6.571,887,3.561,894,1.982,935,2.487,950,2.513,963,1.938,974,2.24,979,1.133,993,2.462,1008,4.451,1117,2.596,1161,3.75,1189,3.493,1230,3.291,1348,2.861,1355,3.75,1359,3.75,1370,3.64,1437,5.003,1454,3.028,1652,5.003,1661,4.451,1668,5.258,1675,3.028,1676,5.977,1693,4.687,1779,4.687,1857,3.656,1989,4.106,1990,4.451,1991,7.014,1992,7.686,1993,5.482,1994,4.263,1995,5.003,1996,5.482,1997,7.811,1998,5.003,1999,5.482]],["t/262",[1,0.879,6,1.166,8,1.209,14,0.858,52,2.003,55,0.795,57,1.607,58,1.619,62,0.961,64,1.089,74,1.757,77,2.627,78,0.495,80,1.104,82,1.048,83,0.406,91,0.477,96,3.841,98,2.314,99,2.814,100,0.712,103,1.251,104,2.216,106,1.346,115,1.81,118,1.977,127,2.313,130,2.507,134,1.61,137,0.865,139,1.339,142,2.156,144,1.104,148,1.542,150,1.742,151,2.697,153,1.682,157,1.183,158,1.111,162,1.021,164,2.697,170,1.607,172,1.346,173,2.372,175,1.104,179,1.346,190,2.21,196,1.021,199,1.557,200,1.293,206,1.325,209,0.813,210,0.911,212,0.621,215,1.191,218,1.235,225,1.847,229,3.312,254,1.23,261,1.534,265,0.924,270,4.507,273,1.573,274,2.156,275,1.111,282,1.339,288,2.321,291,1.642,294,1.209,301,0.533,303,1.015,315,1.599,333,0.961,344,2.164,348,2.121,353,1.628,360,4.67,367,2.21,368,2.252,372,2.164,401,2.598,402,1.893,403,1.381,407,2.407,410,1.381,411,2.269,419,0.955,429,4.173,430,2.422,436,1.705,437,2.598,438,3.356,439,2.743,442,1.861,461,1.417,464,1.786,468,1.786,471,2.851,472,1.335,473,2.587,479,2.54,481,2.256,496,2.743,543,2.531,545,1.335,562,2.816,569,1.393,572,2.851,586,2.697,588,1.642,589,4.165,590,1.607,665,1.59,675,2.259,684,1.916,703,2.697,720,1.774,733,3.62,734,5.993,735,2.965,908,1.456,909,1.764,923,2.513,935,3.066,959,3.408,962,2.082,963,1.226,979,0.717,1014,2.504,1024,1.607,1170,3.841,1171,1.661,1208,1.946,1215,3.165,1312,1.557,1322,2.816,1370,1.642,1376,2.01,1382,2.697,1436,2.513,1444,6.518,1526,2.816,1560,2.21,1595,2.21,1659,1.977,1661,2.816,1668,2.372,1669,2.513,1671,1.607,1675,1.916,1676,2.697,1776,2.513,1778,2.816,1829,2.965,1837,2.045,1856,3.165,1860,4.783,1870,2.965,1871,2.697,1874,3.166,1875,4.248,1879,5.487,1884,3.165,1886,2.965,1888,3.165,1891,1.786,1925,3.48,1952,2.598,1973,2.697,1985,3.165,1988,3.165,2000,3.468,2001,2.513,2002,3.165,2003,3.165,2004,3.468,2005,3.468,2006,3.468,2007,2.965,2008,2.082]],["t/264",[48,3.262]],["t/266",[1,1.993,2,3.722,4,2.531,52,1.678,59,2.273,74,1.297,77,1.341,82,1.71,83,0.509,91,0.494,100,1.03,103,0.8,109,1.297,114,5.07,124,2.293,127,5.242,130,1.851,145,2.676,146,2.371,148,4.337,153,3.005,161,5.008,169,2.362,181,3.98,183,4.102,187,3.687,188,4.409,199,2.542,219,2.808,250,2.128,261,1.041,265,1.329,275,1.814,282,1.387,301,1.209,322,2.421,323,3.776,349,1.139,353,1.894,396,1.444,411,2.138,419,1.558,431,2.711,445,3.936,452,1.839,475,3.081,562,6.382,567,3.687,574,4.402,596,2.376,601,1.973,623,2.681,666,4.597,698,4.24,707,3.227,722,3.176,731,4.24,774,2.879,867,2.334,894,2.047,902,2.651,908,2.376,963,2.002,964,2.843,977,3.873,978,4.597,979,1.17,980,4.102,1024,2.623,1154,3.532,1187,2.568,1188,4.402,1224,4.84,1317,4.24,1906,5.166,2009,6.72,2010,5.661,2011,4.402,2012,5.661,2013,5.166,2014,5.661,2015,5.661,2016,4.84,2017,5.661,2018,3.687,2019,5.661,2020,5.661,2021,3.607,2022,5.661]],["t/268",[83,0.405,96,6.211,98,3.743,99,4.55,100,1.072,101,3.034,104,2.554,142,3.487,144,2.812,162,2.602,234,3.078,236,3.578,265,1.494,298,3.708,378,5.208,402,1.741,448,5.754,554,6.869,556,2.686,1620,4.956]],["t/270",[91,0.812,98,3.944,100,0.864,101,3.197,107,2.218,135,4.458,147,6.972,148,4.138,249,2.099,257,4.733,265,1.574,334,3.837]],["t/272",[55,2.317,66,2.336,129,3.946,130,2.725,148,4.839,153,2.566,188,4.674,191,3.498,261,1.533,267,6.479,271,5.097,323,5.557,349,1.677,575,7.604,673,3.82,806,6.765,1057,4.749,1058,6.765,1448,5.427,1651,6.241,1878,5.427,1894,5.097,2023,6.241,2024,8.332,2025,8.332,2026,5.858,2027,8.332,2028,8.332,2029,7.604,2030,8.332,2031,7.604,2032,8.332,2033,7.604]],["t/274",[51,1.386,54,4.712,56,3.262,71,3.337,77,1.632,103,0.973,104,1.991,107,1.641,114,4.697,129,3.262,138,2.79,144,2.192,146,3.003,148,3.987,152,7.669,153,2.763,157,2.349,162,2.029,163,2.719,167,3.417,169,1.587,180,1.874,185,3.749,195,5.159,210,1.808,249,1.553,265,1.165,297,3.595,299,5.356,301,1.059,359,2.4,380,2.529,411,3.055,445,3.002,480,1.329,531,3.299,536,3.002,569,2.766,581,4.843,601,2.4,635,3.299,675,4.486,768,4.843,806,5.593,891,5.889,1021,2.918,1023,4.486,1052,6.136,1058,5.593,1166,4.486,1190,4.213,1298,5.889,2026,4.843,2034,5.593,2035,9.998,2036,5.593,2037,5.593,2038,5.889,2039,6.286,2040,5.889]],["t/276",[54,7.667,57,3.934,59,2.545,62,0.795,66,1.777,77,2.012,78,1.367,80,2.018,83,0.389,100,0.788,102,2.937,107,1.51,114,2.818,142,2.502,146,2.799,148,3.774,153,1.952,157,3.265,167,3.144,169,2.206,180,1.724,212,1.136,225,1.527,249,1.429,252,2.79,254,1.154,265,1.072,299,4.929,301,0.975,373,5.409,380,3.118,399,2.79,411,2.782,479,2.383,536,3.701,706,4.457,768,5.969,796,5.784,908,2.661,925,5.457,1015,4.038,1046,4.336,1052,6.995,1088,2.567,1204,7.259,1245,3.265,1270,6.359,1289,4.457,1313,4.929,1620,4.763,1987,5.784,2034,5.146,2041,6.731,2042,6.338,2043,5.784,2044,5.784,2045,4.929,2046,8.49,2047,6.338,2048,5.784,2049,6.338,2050,5.146,2051,6.338,2052,6.338,2053,5.419,2054,5.784,2055,6.338,2056,6.338,2057,6.338]],["t/278",[51,1.263,52,3.019,53,5.727,54,6.516,56,2.758,57,4.719,58,1.617,77,2.693,78,1.204,83,0.387,85,4.088,86,4.413,100,0.583,107,1.495,146,2.79,151,4.88,153,2.598,154,5.366,156,2.79,157,2.14,160,2.341,169,2.449,187,4.088,195,4.701,206,2.397,212,1.124,268,3.32,286,2.762,287,2.703,301,0.965,303,1.837,317,1.326,363,4.59,406,2.796,411,2.771,445,2.736,521,1.601,536,4.44,572,3.275,582,5.769,589,3.302,621,4.293,623,2.972,635,3.006,658,5.727,681,3.004,768,4.413,908,2.634,925,4.807,964,3.151,1036,2.684,1064,4.088,1259,5.727,1355,4.293,1721,4.293,1942,4.547,2041,4.413,2058,5.727,2059,3.577,2060,5.366,2061,3.998,2062,6.276,2063,6.276,2064,5.727,2065,6.276,2066,6.276,2067,6.276]],["t/280",[1,1.561,51,1.239,52,2.467,55,1.41,56,2.013,57,3.857,58,1.267,62,1.044,77,2.636,83,0.282,91,0.726,100,1.065,101,2.859,102,3.857,103,1.176,112,2.736,114,3.7,144,2.649,145,2.467,146,2.476,148,2.736,153,1.896,156,2.736,157,2.099,163,2.429,167,4.129,169,1.918,183,3.212,197,2.685,206,2.351,212,1.69,215,2.114,219,3.053,229,3.016,250,1.45,254,1.12,265,1.041,282,2.04,287,1.972,349,1.675,400,3.302,411,2.264,421,2.111,427,3.053,480,1.606,511,2.129,521,1.57,536,3.628,568,3.857,569,2.471,573,5.694,601,2.9,643,3.559,887,2.851,908,2.583,925,3.508,935,2.792,978,4.997,979,1.272,1104,4.61,1128,5.551,1132,5.852,1148,3.765,1384,3.212,1457,3.765,1878,4.009,2068,4.997,2069,6.154,2070,6.154,2071,6.154,2072,4.997,2073,6.154,2074,6.154]],["t/282",[1,2.005,2,3.14,4,1.252,51,0.899,55,1.024,56,2.168,57,2.07,60,2.435,62,0.831,77,2.564,78,0.947,80,1.422,82,2.389,83,0.428,100,0.941,101,3.998,103,0.631,108,2.397,109,1.024,112,2.948,114,4.353,120,4.077,124,3.202,129,3.14,144,2.111,146,1.173,148,4.728,169,1.822,188,5.243,196,1.316,200,1.666,201,2.682,204,2.243,209,1.047,210,1.173,212,0.8,213,2.98,215,1.534,222,3.82,223,1.966,230,2.454,234,1.557,250,1.562,254,0.813,257,2.272,265,1.479,267,3.474,268,2.364,275,1.432,282,1.095,287,1.432,288,1.534,291,2.116,297,3.461,301,0.687,303,1.308,322,1.91,325,3.056,334,1.842,344,2.787,345,1.628,384,2.634,393,2.546,394,2.364,410,3.721,411,2.151,430,1.672,438,3.315,444,3.141,445,2.89,452,2.568,461,1.825,468,1.461,481,2.214,497,1.534,556,1.358,569,2.662,573,4.536,589,1.749,774,2.272,834,3.72,927,2.332,963,2.345,993,2.977,1035,1.858,1063,4.077,1078,3.627,1111,2.332,1128,2.98,1169,2.682,1224,5.669,1246,3.237,1312,2.006,1316,4.137,1383,2.682,1564,3.474,1654,3.82,1844,3.461,1878,2.91,1894,5.351,1896,4.368,2026,3.141,2031,4.077,2037,3.627,2053,6.76,2054,7.215,2075,4.467,2076,4.467,2077,4.467,2078,4.467,2079,4.467,2080,6.63,2081,6.63,2082,4.467,2083,4.077,2084,4.467,2085,4.467,2086,4.467,2087,4.467,2088,4.467,2089,4.467,2090,4.077,2091,3.237,2092,4.467,2093,4.077,2094,4.077]],["t/284",[6,1.782,8,1.847,51,1.067,55,1.72,58,1.58,62,1.188,77,2.53,78,1.353,83,0.4,87,3.376,91,0.932,100,1.045,101,3.255,103,1.231,104,1.532,107,1.263,112,2.356,114,2.356,124,2.146,130,2.454,134,1.263,145,1.571,146,2.488,147,3.969,148,2.356,153,2.311,158,1.698,165,1.82,166,2.766,167,3.723,169,1.729,179,2.914,180,1.442,188,2.973,212,0.949,241,2.024,250,1.249,253,2.165,261,0.975,265,0.896,266,3.376,275,1.698,282,1.839,287,1.698,301,0.815,317,1.119,333,1.468,334,2.185,342,2.041,380,1.946,402,1.717,411,2.721,421,2.21,430,2.389,436,2.957,438,3.591,439,2.661,441,3.726,442,2.843,470,2.146,511,1.833,557,3.768,569,2.128,601,1.847,626,3.306,633,5.23,667,2.729,676,2.128,691,3.181,697,3.726,714,3.071,722,2.973,820,2.455,914,2.884,935,2.404,950,2.429,977,3.625,979,1.551,980,5.437,981,3.124,1035,2.204,1042,2.927,1128,3.534,1148,3.241,1171,2.538,1189,3.376,1384,2.766,1438,3.839,1463,4.121,1477,2.509,1658,4.303,1894,3.241,2021,3.376,2095,5.299,2096,5.299,2097,4.121]],["t/286",[1,1.686,51,1.338,62,0.834,66,1.864,67,2.581,77,2.076,78,0.949,82,2.008,83,0.449,91,0.99,100,1.069,101,2.283,112,3.896,114,2.955,127,4.434,130,2.174,135,5.512,144,2.789,157,2.267,162,2.581,169,1.532,172,2.581,183,4.574,210,1.745,212,1.191,213,4.434,219,3.297,253,2.716,255,2.46,287,2.13,301,1.348,340,1.427,349,1.973,408,3.22,410,3.489,411,2.667,430,1.676,432,3.38,468,2.174,571,4.674,580,2.87,596,2.79,841,4.235,934,3.47,966,4.514,973,3.789,979,1.374,1021,2.816,1039,2.479,1085,3.148,1128,4.434,1187,3.016,1190,4.066,1383,3.99,1566,3.424,1878,4.33,1935,4.816,2098,5.684,2099,6.648,2100,6.648]],["t/288",[1,1.736,14,0.919,50,4.109,52,2.029,54,3.191,55,1.069,58,0.71,60,1.713,62,1.12,77,2.435,78,0.977,80,1.485,83,0.314,91,0.954,98,1.976,100,1.097,101,3.068,103,0.659,104,1.348,107,1.932,109,1.069,114,3.605,116,1.841,130,1.525,142,1.841,144,2.581,146,2.129,148,2.073,153,2.108,158,1.495,169,2.059,173,3.191,180,1.269,182,2.765,206,1.782,211,2.342,214,1.437,215,1.602,218,1.662,223,2.053,250,1.099,252,2.053,256,1.674,261,0.858,263,2.91,265,0.789,274,2.702,282,1.143,301,0.717,306,2.8,311,1.495,317,0.985,325,3.191,329,1.625,340,1.001,345,2.494,349,0.939,353,1.649,383,3.798,395,2.468,396,1.746,398,1.674,402,0.919,406,1.546,407,2.438,408,2.259,411,1.269,426,2.402,441,4.813,451,2.209,452,1.515,454,2.853,466,2.259,469,2.402,477,1.316,480,0.9,481,1.557,521,1.746,557,2.342,559,2.185,590,2.161,623,2.209,643,1.995,645,2.659,663,2.138,671,1.515,674,2.402,676,2.748,681,1.662,698,3.494,720,1.515,736,2.75,821,2.434,841,2.972,894,3.596,898,2.853,944,1.74,959,2.073,963,2.42,968,4.682,983,2.91,1012,3.111,1080,3.191,1085,2.209,1110,3.379,1113,2.577,1148,2.853,1169,2.8,1200,2.75,1247,3.279,1383,4.109,1405,3.787,1457,2.853,1477,2.209,1566,2.402,1758,2.468,1878,5.281,2021,2.972,2038,3.988,2101,3.191,2102,4.664,2103,3.787,2104,4.256,2105,4.664,2106,3.988,2107,4.959,2108,4.664,2109,4.664,2110,6.845,2111,3.988,2112,2.91,2113,4.664]],["t/290",[4,1.145,6,1.373,7,1.715,14,0.86,51,0.822,57,1.893,62,0.512,65,1.573,66,1.738,77,2.331,78,1.07,83,0.344,84,2.051,91,0.884,99,2.104,100,1.06,101,3.565,103,1.058,104,1.181,112,2.756,114,3.331,116,1.613,127,2.724,129,1.934,130,1.336,134,1.785,135,4.534,144,1.973,146,2.661,148,1.816,160,1.523,169,1.727,175,1.3,185,3.374,199,5.186,200,3.12,201,2.452,212,1.997,213,4.135,216,2.408,221,1.477,236,1.654,257,2.077,261,0.751,265,1.048,273,1.853,276,2.291,282,1.519,287,1.309,294,1.423,301,0.628,311,2.68,325,2.794,329,1.423,349,1.684,358,3.492,396,1.042,403,1.626,408,1.979,411,2.676,419,1.707,421,1.036,438,1.548,445,1.78,466,1.979,519,2.191,526,2.452,536,1.78,557,2.051,560,2.002,582,4.241,588,1.934,590,1.893,601,1.423,642,2.328,645,2.328,666,3.317,674,4.308,676,3.008,681,1.455,699,2.549,921,2.602,925,2.328,949,2.872,956,1.979,966,2.104,968,2.794,969,2.959,977,2.794,979,0.844,984,3.06,1013,3.317,1017,4.821,1075,3.06,1085,1.934,1148,3.792,1151,2.724,1152,2.132,1154,2.549,1174,2.026,1183,2.661,1308,3.06,1312,1.834,1336,3.492,1383,3.721,1447,2.408,1453,2.602,1539,2.452,1566,3.859,1620,2.291,1657,3.06,1670,3.492,1816,4.773,1833,3.492,1840,3.868,1841,3.425,2021,3.95,2029,3.728,2094,3.728,2098,3.492,2106,3.492,2114,3.728,2115,4.085,2116,3.728,2117,3.728,2118,6.199,2119,3.728,2120,4.085,2121,4.085,2122,4.085,2123,3.176,2124,7.492,2125,2.408,2126,2.291,2127,4.085,2128,4.085,2129,3.492]],["t/292",[1,1.27,14,0.515,54,6.695,57,3.339,58,0.762,62,1.059,65,2.775,66,1.404,67,1.944,74,1.148,77,1.708,80,1.594,81,2.204,83,0.23,89,3.124,91,0.437,96,5.936,100,0.465,101,1.72,103,0.707,114,2.226,115,2.613,124,2.919,137,1.248,144,1.594,146,1.315,148,3.753,151,3.894,152,4.281,157,2.458,162,1.475,177,2.725,180,1.362,195,6.324,199,3.236,200,3.149,228,2.295,242,2.248,252,2.204,253,2.046,265,1.562,282,1.227,296,2.204,298,2.102,303,1.466,308,3.006,311,1.605,332,2.426,334,2.064,349,1.008,374,3.894,380,1.839,384,4.978,398,1.797,407,2.567,408,2.426,419,1.378,431,2.398,433,2.248,462,3.006,464,2.579,469,2.579,479,1.882,520,3.124,536,2.183,559,3.375,560,2.454,588,2.371,635,2.398,649,1.395,654,2.766,718,2.514,733,2.046,769,3.19,774,2.546,797,3.52,806,4.066,880,2.725,882,2.514,891,4.281,894,2.606,907,2.579,908,3.025,914,2.725,927,2.613,934,2.613,950,2.295,963,2.548,968,3.425,973,2.854,984,3.75,1047,4.176,1052,3.425,1055,2.398,1057,2.854,1071,3.261,1079,2.295,1247,3.52,1262,2.809,1348,2.613,1360,4.281,1378,3.425,1383,4.326,1447,2.952,1454,2.766,1457,3.063,1560,4.591,1738,4.281,1827,4.281,1842,3.063,1855,4.569,1896,2.766,2008,3.006,2021,4.591,2026,6.493,2033,4.569,2035,4.569,2036,4.066,2090,4.569,2093,6.577,2123,5.604,2125,2.952,2130,4.281,2131,6.577,2132,5.007,2133,3.894,2134,4.569,2135,5.007,2136,5.007,2137,5.007,2138,5.007,2139,3.19,2140,5.007,2141,5.007,2142,4.281,2143,5.007,2144,4.281,2145,5.007,2146,4.569,2147,4.066,2148,5.007,2149,5.007,2150,4.569,2151,3.866,2152,4.281,2153,4.569,2154,4.281]],["t/294",[48,3.262]],["t/296",[1,1.975,14,1.137,83,0.357,91,0.993,98,4.109,100,0.901,117,2.675,140,3.211,142,3.074,144,2.479,145,2.308,169,1.795,191,3.269,218,3.455,234,2.714,252,3.428,255,2.882,277,2.294,333,2.157,349,1.568,353,1.877,419,2.144,430,1.964,436,2.43,511,2.694,601,2.714,613,4.513,651,3.33,867,3.211,998,5.073,1030,6.323,1113,4.302,1359,5.327,1500,7.107,1501,7.107,1874,4.513,2021,4.962,2101,5.327,2155,7.788,2156,7.788,2157,8.851]],["t/298",[52,2.631,62,1.113,91,0.774,100,0.824,102,4.112,103,1.254,137,2.212,144,2.825,145,2.631,179,3.446,225,2.139,255,3.284,317,1.875,395,4.695,480,1.712,655,6.43,681,3.162,1574,8.099,1758,4.695,2158,6.901,2159,8.875,2160,8.875]],["t/300",[14,0.885,50,3.882,58,1.472,62,1.079,67,2.511,74,1.482,77,2.291,78,0.923,83,0.297,91,0.751,100,0.957,102,2.997,107,1.541,134,1.541,153,2.65,169,1.49,200,2.412,209,2.016,239,2.643,255,3.815,261,1.19,267,5.029,287,2.072,322,2.766,380,2.375,407,2.304,421,2.452,426,5.529,432,3.289,436,2.018,478,2.893,511,2.237,521,1.649,545,2.49,569,2.597,578,2.633,589,3.369,601,2.254,648,2.766,649,2.872,653,4.424,671,2.101,679,4.686,694,3.882,714,3.748,752,4.084,821,3.375,943,2.553,946,2.875,963,2.287,1026,4.376,1035,4.023,1088,2.62,1117,3.063,1129,3.628,1225,4.844,1373,4.547,1687,4.12,1699,5.902,2068,5.251,2161,6.467,2162,6.467,2163,4.844,2164,6.467,2165,5.902,2166,6.467,2167,5.529]],["t/302",[8,1.768,14,1.16,49,3.567,52,1.504,55,1.163,58,1.671,62,0.912,64,1.594,65,1.954,74,1.163,76,4.12,77,2.426,78,1.214,80,1.615,81,2.233,83,0.334,91,0.742,100,0.79,102,2.351,103,1.028,104,1.467,107,1.209,109,1.163,111,1.682,130,1.659,146,1.332,158,2.331,160,1.892,169,1.169,172,1.97,175,1.615,191,2.13,204,4.271,210,1.91,214,1.563,218,1.807,224,2.212,225,1.223,250,1.195,255,3.788,265,0.858,275,1.626,277,1.494,288,1.743,301,1.308,303,1.485,315,1.485,317,1.537,340,1.089,346,2.301,349,1.021,353,1.223,369,3.267,373,3.232,376,2.212,396,1.294,402,1,407,1.807,421,1.287,432,2.58,452,1.648,480,0.979,481,1.694,511,1.755,589,1.986,601,1.768,606,4.325,651,3.637,671,2.363,697,5.115,717,2.921,720,1.648,752,3.148,876,5.541,946,2.256,979,1.758,1035,2.11,1044,3.232,1055,2.43,1070,3.567,1196,3.676,1399,3.471,1659,2.892,1701,4.12,1721,3.471,1828,4.63,1917,3.8,1923,3.8,2168,11.397,2169,5.074,2170,9.292,2171,4.63,2172,4.63,2173,4.45,2174,7.275,2175,5.907,2176,6.639,2177,7.275,2178,5.074,2179,5.074,2180,5.074,2181,3.384,2182,5.541]],["t/304",[1,1.614,4,1.186,6,3.07,14,1.098,51,0.851,62,0.798,77,2.359,78,1.216,80,2.026,82,1.923,83,0.481,91,0.946,100,1.072,103,1.081,104,1.223,111,1.402,117,1.453,138,3.448,139,1.037,145,2.843,146,1.671,156,1.88,162,1.246,179,1.642,208,2.451,209,1.995,212,1.525,214,1.303,236,1.713,241,1.616,249,0.954,255,3.878,268,3.367,274,1.67,277,1.246,288,1.453,297,2.208,306,2.539,311,2.452,315,1.863,333,1.171,340,0.908,349,1.54,383,1.981,395,3.367,396,1.623,402,0.833,427,3.157,431,2.026,436,1.32,445,3.336,521,1.623,553,1.496,556,1.286,567,2.755,580,1.826,588,2.003,591,1.759,596,3.212,600,2.974,601,1.474,622,1.981,632,2.179,646,2.387,649,1.179,655,8.41,657,3.86,663,2.918,671,2.486,676,2.556,681,1.507,686,2.238,714,2.451,720,1.374,752,1.565,765,2.695,841,2.695,867,1.744,882,2.124,888,1.577,894,2.767,927,2.208,943,1.67,946,4.059,963,1.496,990,1.792,1005,2.337,1028,2.587,1039,1.577,1146,5.678,1183,2.755,1355,2.893,1396,3.434,1402,3.971,1457,2.587,1461,3.168,1465,3.065,1515,2.639,1566,2.179,1612,3.434,1613,1.88,1648,2.539,1659,2.411,1857,2.821,2183,4.23,2184,3.065,2185,3.289,2186,4.23,2187,3.434,2188,4.23,2189,4.23,2190,4.23,2191,4.23,2192,4.23,2193,4.23,2194,3.434,2195,4.23,2196,4.23,2197,4.23,2198,4.23,2199,4.23,2200,4.23,2201,3.065]],["t/306",[7,2.881,14,1.175,55,1.573,58,1.044,60,2.52,62,1.248,78,1.422,83,0.41,91,0.781,100,1.098,103,1.264,104,1.984,130,2.926,145,2.652,162,2.021,180,1.867,212,1.23,230,2.54,241,2.621,255,2.54,261,1.262,277,2.021,317,1.89,349,1.801,353,1.654,398,2.463,407,3.188,440,4.198,476,3.85,480,1.324,606,4.55,681,3.898,821,3.582,887,3.18,950,3.146,1069,7.903,1089,3.682,1125,2.963,1245,3.535,1648,4.12,1672,6.702,1830,5.868,1843,3.582,2202,6.863,2203,6.863,2204,6.863,2205,6.863,2206,5.474]],["t/308",[14,0.951,51,1.455,55,1.656,80,2.3,83,0.424,91,0.939,100,0.948,103,1.021,145,2.742,166,3.772,179,2.806,189,4.188,191,3.034,212,1.658,231,2.38,241,2.76,242,3.245,285,5.455,336,4.155,339,3.585,340,1.551,364,6.104,393,4.119,430,1.822,452,2.347,471,3.772,476,4.054,632,3.722,639,5.413,651,4.601,672,2.594,681,2.575,714,4.188,821,4.829,919,4.604,954,5.62,959,3.213,974,2.953,1021,3.062,1147,5.413,1566,3.722,1758,5.399,2207,5.62,2208,6.179,2209,6.595,2210,4.604,2211,6.595,2212,5.868,2213,5.868]],["t/310",[14,0.769,52,2.218,77,1.773,82,2.26,83,0.434,91,1.002,98,3.17,100,1.083,103,1.057,112,3.326,138,3.831,145,2.803,146,1.965,160,2.791,169,1.724,209,1.753,228,3.43,241,2.858,250,1.763,255,3.838,268,3.959,282,1.834,283,2.839,303,2.19,309,5.421,317,1.58,378,4.411,411,2.035,476,4.197,553,2.646,569,3.004,655,5.421,681,2.666,950,3.43,1010,6.828,1128,4.99,1165,4.577,1196,5.421,1276,6.828,1530,4.767,1758,3.959,2194,6.075,2206,4.577,2214,7.482,2215,7.482]],["t/312",[55,2.025,77,2.093,91,0.974,111,2.929,130,2.889,145,2.618,215,3.034,225,2.129,275,2.831,399,3.888,614,6.617,665,4.05,867,3.642,905,7.173,948,5.754,999,8.062,1355,6.043,2216,8.834,2217,8.062,2218,8.062,2219,7.553]],["t/314",[14,0.868,65,3.251,77,2,83,0.467,145,2.502,206,3.225,249,1.903,265,1.428,283,2.534,288,2.9,395,4.467,556,2.567,568,4.722,576,5.776,596,3.544,601,2.942,905,6.855,908,3.544,943,3.333,1804,5.631,2220,7.219,2221,8.443,2222,8.443,2223,7.705,2224,8.443,2225,8.443,2226,8.443,2227,8.443,2228,8.443,2229,8.443,2230,8.443]],["t/316",[6,2.916,14,0.892,48,2.876,74,1.988,83,0.475,169,1.999,274,3.424,311,2.779,427,4.302,478,2.916,531,4.154,578,2.654,722,4.866,735,7.415,905,8.41,946,3.856,1527,4.866,1564,6.745,1568,5.526,1917,6.497,2103,7.042,2231,6.745,2232,8.673,2233,8.673,2234,7.042]],["t/318",[1,2.24,14,0.908,62,1.108,146,2.319,255,3.269,300,6.043,349,1.778,399,3.888,497,3.034,578,2.703,601,3.078,650,4.093,752,3.269,765,5.628,888,3.295,1036,3.778,1587,7.173,1865,7.553,2126,4.956,2235,8.834,2236,8.834,2237,8.062,2238,8.834]],["t/320",[14,0.926,83,0.485,90,4.972,91,0.785,100,0.836,146,2.363,376,3.923,399,3.962,589,3.524,702,4.52,715,5.506,1371,6.999,1566,4.636,2104,8.214,2217,9.662,2239,9.001,2240,9.001]],["t/322",[14,0.939,56,2.986,91,0.797,100,0.848,103,1.29,145,3.164,383,4.276,431,4.372,580,3.942,589,3.574,651,3.904,655,6.615,1758,4.83,2207,7.1,2241,9.13]],["t/324",[14,0.939,145,2.706,146,2.397,182,3.114,255,3.379,274,3.604,383,4.276,411,2.484,479,3.432,578,2.794,601,3.181,752,3.379,1587,7.413,2218,9.743,2242,9.13]],["t/326",[14,0.828,62,1.009,83,0.369,91,1.002,103,1.137,146,2.114,163,3.178,208,6.482,211,4.043,255,2.979,257,4.094,364,4.319,402,1.586,407,2.868,410,3.205,589,3.152,626,5.023,651,3.443,821,4.202,894,2.911,919,6.308,979,1.664,1128,5.369,1129,5.554,1280,5.507,1313,7.699,1339,3.579,1402,6.69,1592,5.833,2207,6.26,2243,8.051,2244,7.347]],["t/328",[14,1.051,83,0.389,91,0.892,145,3.375,146,2.227,157,2.892,191,3.56,221,3.067,237,3.808,255,3.138,257,4.312,333,2.349,364,4.55,578,2.595,589,3.32,590,3.929,626,5.291,651,3.627,752,3.138,867,3.496,1035,3.528,1804,5.656,2207,7.946,2208,7.251,2209,9.325]],["t/330",[58,1.476,59,3.127,83,0.484,91,0.846,102,3.608,107,2.311,111,2.582,139,1.909,145,2.308,146,2.045,174,5.327,176,5.073,177,4.239,204,3.911,217,3.269,224,3.395,249,1.756,255,3.589,265,1.317,332,3.773,396,1.986,399,3.428,402,1.535,407,2.774,410,3.101,411,2.118,521,1.986,540,2.656,589,3.049,590,3.608,601,2.714,645,4.439,836,4.592,876,5.073,884,6.323,934,4.065,951,5.476,1035,3.239,1055,4.645,1583,6.658,1721,5.327,1850,5.833,2219,6.658,2245,6.056,2246,6.323]],["t/332",[1,1.759,4,1.945,58,1.523,74,2.626,83,0.413,84,3.484,98,2.939,102,3.215,130,2.947,137,1.729,139,1.7,146,2.366,157,3.073,160,3.361,162,2.043,167,4.47,172,3.499,196,2.043,200,2.588,209,1.626,212,1.243,214,2.137,225,1.672,265,1.692,275,2.223,291,3.285,295,3.892,301,1.067,303,2.031,309,5.027,315,2.638,348,4.244,349,1.396,378,4.09,398,2.49,402,1.367,451,3.285,454,4.244,455,4.627,464,3.574,475,4.905,478,3.03,479,2.608,557,3.484,559,4.221,563,5.932,565,3.484,578,2.123,590,3.215,665,3.181,682,3.084,722,3.892,734,4.329,917,3.215,963,2.453,986,5.197,997,3.148,1074,5.197,1338,4.244,1785,4.165,2181,4.627]],["t/334",[48,3.262]],["t/336",[1,1.965,2,2.626,4,1.555,48,1.839,60,2.036,62,0.972,74,1.271,82,1.675,83,0.355,134,1.321,153,1.708,168,4.741,169,1.278,170,2.57,187,3.612,209,1.816,212,1.388,219,2.751,224,2.417,227,3.314,233,4.312,234,1.932,250,1.307,254,1.009,256,3.205,288,1.905,322,2.372,345,3.523,396,1.414,399,2.441,409,2.441,474,1.99,480,1.07,515,3.899,522,2.975,554,4.312,566,3.314,578,1.697,621,3.794,628,4.018,643,3.314,650,2.57,700,5.956,820,2.57,867,2.286,887,2.57,950,2.542,1013,4.503,1034,3.899,1036,2.372,1039,2.068,1057,3.161,1155,3.018,1176,4.741,1195,3.329,1245,2.856,1271,4.018,1339,2.465,1377,4.741,1450,3.699,1586,4.344,1613,2.465,1659,3.161,1673,4.154,1713,3.392,1834,2.751,1922,4.503,2011,4.312,2097,4.312,2146,5.061,2247,5.292,2248,3.899,2249,5.546,2250,7.748,2251,5.061,2252,3.064,2253,3.612,2254,10.364,2255,7.748,2256,5.546,2257,7.748,2258,5.546,2259,5.546,2260,7.748,2261,7.748,2262,5.546,2263,7.748,2264,5.546,2265,5.546,2266,7.748,2267,7.748,2268,7.748,2269,7.748,2270,7.748,2271,7.748,2272,5.546,2273,5.546,2274,5.546,2275,5.546,2276,5.546,2277,5.546,2278,5.546,2279,3.794,2280,3.794]],["t/338",[4,1.631,7,3.845,60,2.136,71,2.818,77,1.378,83,0.452,103,0.822,107,2.182,115,3.036,153,1.791,170,2.695,180,1.582,183,3.036,196,2.359,212,1.641,227,2.487,249,1.806,250,1.37,258,1.996,261,1.473,265,0.984,282,1.425,287,1.864,291,2.754,301,0.895,334,2.398,340,1.719,344,3.629,345,2.918,353,1.93,376,2.535,422,2.601,436,1.815,478,1.956,480,1.545,514,3.789,515,7.521,522,6.079,530,2.996,531,2.786,540,2.731,557,2.921,601,2.027,643,2.487,667,2.996,676,2.336,816,4.523,894,2.103,909,2.958,928,4.641,939,3.263,963,2.057,1021,2.464,1040,4.723,1054,3.879,1256,3.88,1326,2.487,1351,4.09,1511,3.979,1586,4.401,1596,4.723,1613,2.586,1628,3.371,2003,7.309,2008,3.491,2247,5.371,2253,6.968,2281,4.973,2282,5.308,2283,5.308,2284,4.357,2285,5.816,2286,6.228,2287,8.009,2288,5.816,2289,3.789,2290,5.816,2291,5.816,2292,5.308,2293,5.308,2294,5.816,2295,4.723]],["t/340",[4,2.328,7,2.575,14,1.118,51,1.235,62,1.363,74,1.903,78,0.876,83,0.281,102,4.363,107,1.462,109,1.903,116,2.422,134,1.979,137,2.515,139,2.473,141,3.043,157,3.596,182,2.092,197,1.979,212,1.099,213,4.091,217,2.575,225,2.001,231,3.579,258,2.07,291,2.905,294,3.516,301,1.277,303,1.796,317,1.296,334,2.529,340,1.783,342,2.362,349,1.235,474,2.981,477,2.342,480,1.183,497,2.107,515,4.313,522,5.052,556,1.865,703,4.77,720,1.993,816,4.77,928,4.813,974,2.507,1039,2.288,1047,3.555,1208,3.441,1461,4.595,1467,3.682,1519,3.682,1560,3.908,1586,2.463,1596,4.981,1613,4.486,2147,4.981,2248,6.62,2253,3.996,2284,4.595,2296,4.313,2297,3.908,2298,4.313,2299,7.645,2300,5.245,2301,4.981,2302,4.445,2303,5.598]],["t/342",[1,2.21,4,2.804,14,1.122,62,0.941,67,2.057,71,2.567,82,2.267,83,0.435,99,2.729,100,0.492,103,0.749,107,1.263,139,2.135,141,2.629,144,2.389,157,2.559,160,1.976,196,1.561,211,2.661,215,1.82,219,2.629,225,1.277,231,3.747,240,3.726,249,1.195,252,3.835,261,1.743,268,2.804,287,2.405,294,3.302,301,1.154,317,1.585,328,2.288,332,2.567,333,1.468,345,1.931,346,2.755,396,1.352,433,2.38,474,3.127,477,2.673,480,1.929,522,4.026,557,2.661,572,2.766,641,7.369,674,3.865,720,1.721,790,2.695,816,5.836,887,2.455,908,2.224,963,1.874,1231,5.436,1351,7.032,1362,3.241,1480,4.531,1586,3.805,1596,4.303,1613,3.336,2247,4.375,2284,5.621,2286,7.369,2289,5.676,2296,3.726,2304,4.836,2305,3.726,2306,7.45,2307,4.59,2308,8.483,2309,5.836,2310,3.969,2311,6.416,2312,4.531,2313,4.531,2314,5.299,2315,5.299]],["t/344",[1,1.181,4,1.305,7,1.193,14,0.95,51,0.572,60,1.044,62,0.741,67,2.654,74,0.652,80,0.905,83,0.456,99,1.464,100,0.7,103,1.065,104,0.822,107,0.677,109,0.652,134,0.677,137,1.161,139,1.675,153,0.876,157,1.588,182,0.969,196,0.837,197,1.502,209,0.666,212,1.351,215,1.599,217,1.193,227,1.991,231,3.959,249,0.641,250,0.67,252,2.049,254,0.517,258,1.161,261,1.088,274,1.122,276,1.595,287,1.492,288,2.348,289,3.426,294,3.11,295,1.595,297,1.484,301,0.909,317,1.249,328,2.01,333,1.289,336,1.277,340,2.419,342,1.793,346,0.899,349,0.572,359,0.991,368,1.919,433,2.091,444,4.157,461,2.416,466,1.377,468,0.93,474,2.707,477,1.313,480,1.319,515,3.273,522,3.667,530,1.464,536,1.239,540,1.588,545,1.793,555,1.896,556,0.864,570,2.21,591,1.182,622,2.769,629,1.331,631,1.945,700,5.703,702,2.969,790,1.445,817,1.739,836,5.262,867,1.172,872,2.951,882,1.427,887,2.157,914,1.547,934,1.484,974,1.162,1085,2.205,1120,4.284,1126,6.577,1147,2.129,1161,3.185,1317,2.129,1351,7.74,1362,1.739,1398,1.595,1456,2.612,1467,4.862,1519,1.706,1536,1.852,1586,2.745,1613,4.418,1614,2.21,1638,3.317,1671,2.157,1816,1.811,1831,1.999,1935,2.06,1941,1.852,2008,1.706,2059,1.62,2247,1.427,2248,1.999,2253,1.852,2254,8.144,2284,2.129,2286,8.56,2289,6.192,2292,4.249,2297,2.966,2306,6.448,2308,2.21,2309,2.21,2310,5.649,2311,6.448,2312,6.448,2313,6.925,2316,9.068,2317,2.843,2318,3.185,2319,1.739,2320,4.559,2321,4.157,2322,2.843,2323,8.925,2324,1.999,2325,3.943,2326,2.843,2327,4.656,2328,4.656,2329,4.656,2330,5.396,2331,2.843,2332,2.594,2333,4.806,2334,2.43]],["t/346",[14,0.948,51,1.185,60,2.162,62,0.738,66,1.651,74,1.35,83,0.477,100,0.856,103,0.832,107,2.364,134,2.707,138,2.385,153,1.814,156,3.591,157,2.008,166,3.073,212,1.055,249,1.327,254,1.678,287,1.887,288,2.022,295,3.303,353,1.419,468,3.015,474,2.113,515,6.482,522,4.946,665,3.703,974,2.406,1122,4.41,1152,3.073,1245,3.033,1351,8.227,1456,3.303,1493,4.273,1586,3.702,1613,4.621,1951,4.14,2247,2.957,2252,3.253,2286,9.098,2289,6.994,2306,7.882,2310,6.905,2311,7.882,2312,7.882,2313,7.882,2330,5.373,2332,7.37,2335,5.034,2336,5.373,2337,10.737]],["t/348",[14,0.927,55,2.065,62,0.87,80,2.869,81,3.054,83,0.413,100,0.644,103,0.98,104,2.006,109,2.065,116,2.739,134,1.653,139,1.7,157,3.073,180,1.887,197,2.238,212,1.243,230,2.567,261,1.276,284,2.254,294,3.14,297,3.621,317,1.903,346,2.194,402,1.367,411,1.887,419,1.91,445,3.024,530,3.574,559,4.688,665,3.181,704,4.642,714,4.021,746,7.009,939,3.892,959,3.084,1024,3.215,1351,7.037,1365,4.021,1586,4.254,1613,5.003,1620,3.892,1911,5.197,2247,4.525,2286,7.008,2289,4.519,2338,7.506,2339,5.932]],["t/350",[14,1.082,60,2.025,74,2.04,80,1.755,83,0.408,98,3.269,109,1.264,113,3.001,128,3.251,134,1.314,139,2.182,144,2.456,163,2.177,180,1.5,182,1.88,196,1.624,209,1.292,212,1.383,215,1.894,218,2.749,231,2.932,250,1.818,252,2.427,261,1.014,273,4.605,294,1.921,301,0.848,329,2.689,333,1.527,334,2.273,342,2.123,347,2.502,353,1.329,359,1.921,396,1.406,401,4.13,406,1.828,445,2.403,451,3.654,468,1.803,474,3.643,480,1.064,497,3.058,513,3.591,559,2.582,650,2.555,949,3.877,974,2.253,998,3.591,1036,3.807,1057,3.143,1124,3.44,1177,6.669,1187,3.501,1285,5.032,1348,5.298,1516,2.804,1586,4.604,1613,4.899,1638,4.994,1639,4.477,2247,5.097,2308,6.923,2338,6.77,2340,8.398,2341,9.263,2342,8.903,2343,8.125,2344,5.514,2345,4.632]],["t/352",[4,1.143,14,0.769,51,1.506,52,1.208,55,0.934,56,1.333,62,0.511,65,1.57,81,1.794,82,1.231,83,0.383,97,1.596,99,3.188,103,1.336,109,0.934,119,3.757,129,1.93,131,2.953,137,1.016,142,1.609,144,1.297,146,1.07,147,3.053,153,1.255,157,1.39,162,1.201,165,1.4,170,1.889,171,2.323,172,1.582,174,2.788,180,1.109,196,1.823,209,2.216,212,1.341,214,1.255,215,1.4,217,1.711,221,1.474,224,1.777,227,1.743,231,2.038,241,1.557,249,0.919,250,0.96,256,1.463,258,1.543,261,0.75,265,1.047,275,1.306,282,1.834,283,1.224,284,1.324,286,1.794,287,1.306,288,1.4,294,2.607,305,2.099,317,0.861,332,1.975,334,1.68,340,0.875,342,1.57,345,2.255,346,1.957,348,3.786,349,1.246,368,3.085,399,3.294,409,1.794,417,2.953,432,2.073,433,1.83,438,2.345,451,1.93,469,2.099,474,3.796,478,1.37,481,2.79,518,2.127,529,3.17,530,2.099,545,1.57,556,1.239,560,1.998,629,1.909,635,3.583,643,2.647,650,1.889,720,1.324,731,3.053,746,5.496,820,1.889,841,2.597,894,3.022,898,2.493,902,2.899,908,3.507,963,1.441,964,2.047,979,0.843,1003,3.17,1044,2.597,1054,2.718,1118,3.862,1195,2.447,1208,2.287,1209,3.485,1258,7.144,1269,4.813,1320,3.485,1326,1.743,1339,2.752,1362,2.493,1398,2.287,1515,2.543,1519,2.447,1539,2.447,1586,4.428,1613,4.611,1648,3.715,1714,3.485,1732,3.31,1804,2.718,2167,5.292,2247,4.196,2248,4.352,2252,3.419,2299,3.31,2308,3.17,2309,3.17,2316,5.648,2325,4.99,2338,4.128,2340,4.813,2341,5.648,2346,6.189,2347,3.72,2348,8.984,2349,3.72,2350,4.076,2351,6.189,2352,4.076,2353,3.485,2354,4.076,2355,4.076,2356,4.076,2357,8.355,2358,7.482,2359,6.189,2360,3.72,2361,6.189,2362,5.648,2363,4.076,2364,4.076,2365,4.076,2366,3.485]],["t/354",[8,2.065,51,1.633,52,1.756,64,2.905,80,2.582,83,0.456,103,1.306,115,3.092,144,3.424,145,1.756,153,2.498,166,3.092,169,1.869,179,2.3,180,2.206,182,2.02,209,1.388,214,1.825,218,2.111,250,1.911,252,2.608,265,1.682,283,1.778,286,2.608,287,1.899,288,2.035,311,1.899,333,1.641,345,2.159,356,4.607,402,1.167,407,2.111,432,3.013,438,3.073,451,2.806,476,4.55,478,3.344,511,2.05,515,5.703,521,2.069,522,4.351,530,4.178,553,2.095,597,4.166,601,2.826,613,3.433,676,3.714,746,5.657,828,2.806,867,2.442,882,2.975,887,2.745,894,3.345,902,2.775,912,4.233,928,4.7,935,2.688,963,2.095,997,2.688,998,3.859,1021,2.51,1097,3.624,1155,3.225,1233,2.975,1234,4.607,1257,5.407,1361,5.066,1396,4.811,1586,3.994,1667,3.859,1832,4.607,1911,4.438,2008,3.557,2144,5.066,2247,4.644,2248,4.166,2253,3.859,2299,4.811,2338,6.168,2366,5.066,2367,5.407,2368,4.811,2369,5.925]],["t/356",[2,3.585,62,0.949,67,2.94,74,1.735,99,3.9,100,0.703,145,2.244,162,2.23,180,2.06,215,2.6,225,1.824,231,2.493,250,2.245,282,1.855,284,2.459,286,3.333,317,1.599,333,2.097,334,3.121,378,4.464,395,4.006,402,1.492,403,3.014,475,4.121,478,2.546,479,2.846,566,4.075,676,3.04,718,4.785,746,4.631,962,4.545,1102,5.888,1209,6.473,1314,6.909,1389,5.888,1417,7.41,1586,4.188,2247,5.237,2299,6.148,2308,5.888,2338,5.05,2370,7.571,2371,6.148,2372,6.148,2373,7.571,2374,9.529,2375,9.529,2376,7.571,2377,7.571]],["t/358",[48,3.262]],["t/360",[1,2.537,51,1.648,58,1.246,62,1.254,74,1.877,91,0.943,107,1.951,165,2.813,196,2.412,225,1.973,282,2.007,298,3.438,301,1.259,334,3.376,345,2.984,378,4.828,411,2.228,419,2.254,430,2.065,436,2.555,438,3.103,465,3.754,472,3.153,476,4.594,478,2.753,543,3.794,546,5.758,556,2.49,565,4.112,734,6.242,950,3.754,1245,4.218,1339,3.641,1874,4.746,2378,8.189]],["t/362",[14,0.831,55,1.853,62,1.014,77,1.916,78,1.154,83,0.371,91,0.706,100,0.751,137,2.015,167,4.01,212,1.449,214,2.49,258,2.015,287,2.591,301,1.243,303,2.366,315,2.366,342,3.113,349,1.627,351,4.946,418,4.701,420,4.503,452,2.626,454,4.946,455,5.392,480,1.56,543,4.599,681,2.88,888,3.015,938,5.392,997,3.668,1104,6.056,1338,4.946,1577,5.858,1578,4.4,2368,6.565,2379,8.085,2380,8.085,2381,8.085,2382,6.056]],["t/364",[14,1.068,51,1.413,55,1.107,56,2.296,58,1.068,66,1.354,74,1.609,78,1.002,83,0.442,91,0.793,93,3.616,104,2.03,107,1.673,116,1.907,144,1.537,165,1.659,205,3.641,252,2.126,254,1.278,265,0.817,286,2.126,301,1.08,311,2.25,315,1.414,322,3.002,329,1.683,333,1.338,334,3.744,340,1.949,349,0.972,396,1.791,398,2.52,402,0.952,411,1.314,420,2.191,421,2.303,426,2.488,436,1.507,462,2.899,468,1.579,471,4.317,474,3.259,480,1.354,521,1.232,541,1.923,543,4.47,545,1.86,565,3.526,578,2.149,591,2.009,650,2.238,663,2.214,669,2.191,681,1.72,717,1.939,820,2.238,867,1.991,872,2.085,887,2.238,888,2.619,979,1.451,1021,2.975,1120,3.499,1171,2.313,1174,4.505,1370,2.287,1462,5.935,1586,4.357,1591,2.847,1860,3.013,2247,4.845,2297,3.077,2334,4.129,2383,5.701,2384,3.921,2385,5.701,2386,4.295,2387,5.701,2388,5.701,2389,4.937,2390,6.407,2391,5.46,2392,4.407,2393,5.46,2394,6.407,2395,5.46,2396,5.46,2397,4.407,2398,5.46,2399,4.407,2400,4.803,2401,4.407,2402,5.46,2403,5.46,2404,7.021,2405,5.46,2406,4.829,2407,4.829,2408,7.021,2409,4.829,2410,4.829,2411,4.829,2412,7.021,2413,4.829,2414,3.396,2415,4.407,2416,5.816]],["t/366",[14,1.114,49,2.07,51,1.546,56,1.567,60,1.081,62,0.369,67,1.86,74,1.989,77,0.697,78,0.684,80,0.937,81,1.296,83,0.475,91,0.67,93,3.956,100,0.273,103,0.677,104,0.851,107,2.156,108,1.579,109,1.761,111,1.588,116,1.162,117,2.397,134,2.53,137,0.734,138,1.192,139,1.71,157,2.065,162,0.867,163,1.162,165,1.011,167,1.46,175,0.937,180,1.303,205,3.381,209,0.69,210,0.773,215,1.011,221,1.732,231,1.577,234,1.026,249,1.365,250,0.694,254,0.536,256,1.056,261,1.114,265,0.498,276,1.651,284,0.956,287,0.943,289,1.706,294,2.432,301,0.931,311,1.941,317,1.279,322,3.285,328,1.271,333,0.815,334,1.213,342,1.133,346,0.931,359,1.026,393,1.678,396,2.214,402,0.58,407,1.049,409,1.296,421,0.746,432,1.497,436,0.919,442,1.579,454,1.801,455,3.195,468,1.981,471,4.722,474,3.247,476,1.651,480,1.588,481,0.983,534,2.475,536,2.64,540,1.004,543,4.192,559,1.379,565,2.405,578,1.466,596,1.236,601,1.026,603,6.496,629,1.379,641,3.725,669,4.104,671,0.956,681,2.158,702,1.478,706,2.07,790,1.497,872,2.615,888,1.786,924,1.443,934,1.536,935,2.173,959,1.309,1021,2.566,1039,1.098,1064,3.12,1073,3.368,1112,3.277,1117,1.394,1174,1.46,1189,1.875,1312,2.151,1345,2.173,1348,2.5,1425,7.203,1447,1.735,1454,2.646,1456,2.687,1462,5.67,1586,4.348,1591,4.115,1612,2.39,1613,2.13,1660,2.517,1890,2.39,1967,2.39,2059,1.678,2165,2.686,2234,3.889,2247,3.505,2302,2.133,2383,4.918,2384,4.918,2385,6.237,2386,4.699,2387,6.237,2388,5.667,2389,5.401,2391,3.725,2393,3.725,2395,3.725,2396,3.725,2398,3.725,2400,3.277,2402,3.725,2403,6.403,2405,3.725,2416,4.258,2417,2.944,2418,2.944,2419,4.79,2420,6.057,2421,6.057,2422,8.234,2423,6.057,2424,6.057,2425,6.057,2426,6.057,2427,2.944,2428,2.944,2429,4.79,2430,2.014,2431,2.944]],["t/368",[1,1.363,14,1.103,58,0.818,60,1.973,62,0.674,64,1.688,67,2.086,80,1.711,81,2.365,83,0.437,91,0.469,103,0.759,107,1.28,113,2.925,134,2.818,158,2.429,180,1.462,196,1.583,212,0.963,215,1.846,221,1.943,231,2.496,254,0.978,258,1.34,261,0.988,294,1.873,333,1.488,368,3.124,467,3.676,474,3.745,476,3.015,480,1.037,534,3.097,536,3.304,543,4.659,559,2.517,565,2.698,591,2.235,603,3.287,629,2.517,643,2.298,669,4.326,674,3.904,681,2.7,689,3.5,720,1.746,872,3.272,915,4.595,979,1.111,1117,3.589,1187,2.438,1189,3.424,1231,4.729,1326,4.078,1345,3.439,1453,3.424,1454,4.85,1462,4.468,1493,2.843,1586,3.043,1613,4.47,1779,4.595,2247,3.806,2307,3.287,2318,3.676,2391,5.894,2392,6.916,2393,5.894,2394,8.702,2395,5.894,2396,5.894,2397,6.916,2398,5.894,2399,6.916,2400,5.185,2401,8.702,2402,6.828,2403,5.894,2405,5.894,2432,7.579,2433,7.579,2434,5.374]],["t/370",[14,1.217,51,1.612,61,2.921,62,0.729,74,1.333,83,0.367,91,0.799,93,2.996,100,0.54,103,0.822,134,2.66,166,3.036,197,1.876,205,2.56,218,2.072,225,1.401,254,1.667,261,1.07,262,3.12,294,2.791,295,3.263,311,1.864,317,1.229,322,2.487,328,2.511,363,3.166,407,2.072,467,3.979,468,3.385,471,3.036,472,2.24,473,2.754,474,2.088,540,3.124,543,3.711,565,2.921,578,1.78,603,4.899,622,4.29,669,3.633,714,3.371,872,2.511,979,1.894,1021,3.393,1044,3.706,1117,2.754,1152,3.036,1166,3.789,1345,2.639,1454,3.213,1462,5.4,1586,3.216,1655,4.214,1816,3.706,2247,2.921,2383,4.723,2384,4.723,2385,4.723,2386,3.558,2387,4.723,2388,4.723,2389,4.09,2390,5.308,2391,4.523,2393,4.523,2395,4.523,2396,4.523,2398,4.523,2400,3.979,2402,4.523,2403,7.123,2405,4.523,2435,5.308,2436,5.221,2437,5.816,2438,10.959,2439,8.009,2440,5.816,2441,5.816,2442,5.816]],["t/372",[14,1.134,50,3.579,51,1.2,58,0.907,74,1.366,80,1.898,83,0.373,91,0.52,93,3.071,107,2.376,109,1.867,116,2.354,124,2.415,134,2.568,137,2.03,140,2.458,182,2.033,197,1.923,205,3.585,239,2.436,250,1.405,254,1.085,283,1.79,284,1.937,294,2.838,295,3.345,305,3.071,311,1.911,317,1.259,322,2.55,328,3.517,409,2.624,468,2.664,471,3.112,473,3.857,530,3.071,540,2.033,543,2.762,545,3.137,548,3.345,565,4.09,603,6.968,646,1.671,669,4.21,979,1.233,1020,4.841,1021,2.526,1152,3.112,1428,4.142,1462,5.47,1493,3.154,1586,4.328,1655,4.32,1816,3.798,2181,3.976,2247,2.994,2383,4.841,2384,4.841,2385,4.841,2386,3.647,2387,4.841,2388,4.841,2389,4.192,2391,4.636,2393,4.636,2395,4.636,2396,4.636,2398,4.636,2400,4.078,2402,4.636,2403,6.334,2405,4.636,2435,5.441,2443,5.097,2444,5.962,2445,5.962,2446,5.962,2447,8.146,2448,5.962,2449,5.962,2450,5.962,2451,5.962,2452,5.441]],["t/374",[14,1.076,58,1.343,61,3.373,74,2.022,109,2.258,134,2.493,137,1.675,139,1.646,158,2.153,162,1.979,209,1.574,214,2.069,252,2.957,254,1.223,265,1.136,283,2.017,301,1.033,353,1.619,370,4.226,371,3.809,376,2.928,419,1.849,421,1.704,433,3.017,436,2.096,466,3.254,471,3.506,474,2.411,480,1.702,543,4.088,580,2.9,623,3.181,663,3.08,669,4.748,672,2.411,682,3.923,720,2.182,774,3.416,887,3.113,1116,7.544,1195,4.033,1345,3.048,1365,3.893,1586,3.543,1613,4.381,1638,4.95,2247,3.373,2325,4.48,2335,5.744,2338,4.48,2453,6.718,2454,6.718,2455,5.455,2456,6.131,2457,5.032,2458,5.455,2459,4.867,2460,8.824,2461,8.824,2462,8.824,2463,6.718,2464,6.131,2465,8.824,2466,6.718]],["t/376",[4,0.628,6,1.282,7,2.091,14,1.193,51,1.003,55,0.874,58,1.228,59,0.899,62,0.625,66,0.628,67,1.934,74,2.519,78,0.32,80,1.214,82,1.152,83,0.351,91,0.576,102,1.767,103,0.316,107,1.826,109,1.512,111,0.742,116,0.884,130,1.247,134,1.826,137,0.558,138,1.544,139,1.759,141,1.11,142,1.505,145,0.664,157,1.3,158,1.596,160,3.252,165,0.769,167,1.891,183,1.168,191,0.94,196,0.659,197,0.722,202,1.397,209,0.525,210,0.588,212,0.892,218,1.774,221,0.81,223,0.985,224,0.976,230,2.657,231,2.172,232,1.574,233,1.741,237,2.641,239,2.696,241,0.855,242,1.005,249,1.326,250,1.174,254,0.907,256,0.803,258,0.558,261,0.701,265,1.214,273,1.016,282,0.549,283,1.145,291,2.359,301,1.015,303,0.655,315,1.116,317,1.842,332,2.413,333,1.629,334,0.923,340,1.069,346,0.708,349,1.003,353,0.539,359,1.329,367,1.426,370,1.826,371,3.764,375,1.622,376,0.976,378,1.32,396,1.683,399,0.985,402,0.751,419,2.473,430,0.564,436,1.19,442,1.201,445,0.976,448,1.458,451,1.06,453,2.965,466,1.084,468,1.247,471,1.168,474,3.559,475,2.711,476,1.256,477,0.631,478,0.753,480,1.733,481,0.747,497,1.31,513,1.458,534,0.915,536,3.34,540,1.699,541,0.891,543,4.364,556,0.681,557,1.124,559,1.049,566,1.631,568,1.767,572,1.168,578,2.019,629,1.049,650,3.057,669,4.075,672,3.38,674,1.153,681,0.798,682,1.695,700,1.493,715,1.369,718,1.915,720,1.91,728,2.333,746,3.598,790,1.138,816,1.741,834,1.256,872,3.1,888,1.422,890,3.047,902,4.765,917,1.767,934,1.168,946,0.995,950,1.026,963,1.348,979,0.788,982,1.11,983,1.397,993,1.005,994,1.397,1024,1.037,1026,3.355,1039,0.835,1195,1.344,1230,2.289,1260,1.741,1280,1.531,1338,1.369,1362,1.369,1365,1.297,1370,2.785,1428,4.434,1462,1.32,1586,2.883,1591,3.89,1613,3.407,1620,1.256,1638,1.256,1647,3.096,1657,5.74,1671,1.767,1687,4.203,1713,2.333,1729,1.574,1730,1.493,1890,1.818,1912,1.458,1947,1.458,1967,1.818,2008,2.289,2044,2.043,2125,2.248,2151,1.201,2247,2.501,2253,1.458,2304,2.043,2305,1.574,2318,2.608,2325,1.493,2338,4.4,2368,1.818,2457,1.677,2458,1.818,2467,4.545,2468,7.18,2469,7.18,2470,1.741,2471,5.882,2472,2.239,2473,1.677,2474,3.813,2475,3.813,2476,2.239,2477,2.239,2478,1.741,2479,2.239,2480,3.813,2481,2.043,2482,1.818,2483,2.043,2484,1.574,2485,4.981,2486,3.48,2487,2.239,2488,3.813,2489,2.239,2490,3.813,2491,3.813,2492,2.239,2493,2.239,2494,2.239,2495,2.239,2496,2.239,2497,2.239,2498,3.813,2499,2.239,2500,2.239,2501,3.813,2502,2.239,2503,2.856,2504,2.239,2505,2.239,2506,2.239,2507,1.914,2508,2.239,2509,2.239]],["t/378",[1,0.745,14,1.139,51,1.218,55,0.674,56,0.961,57,1.362,58,1.597,60,1.079,61,1.476,62,0.874,64,1.503,65,1.132,66,0.824,74,1.096,78,0.864,80,1.523,82,2.107,83,0.429,91,0.757,99,2.464,100,0.444,103,0.855,104,1.383,107,0.7,109,1.387,130,0.961,134,1.442,137,1.193,138,1.19,140,1.212,142,2.388,146,0.772,169,0.677,172,1.141,180,0.799,183,1.534,191,1.234,196,0.866,197,1.543,200,1.096,209,1.121,210,0.772,212,1.249,217,1.234,218,1.047,219,1.458,225,1.153,230,1.088,231,1.575,249,1.079,254,1.101,256,2.172,261,0.541,265,0.809,268,1.555,274,1.889,275,0.942,282,1.172,287,1.939,288,1.009,296,1.294,301,0.452,303,0.86,305,2.464,311,0.942,317,1.278,328,1.269,333,0.814,334,1.212,339,1.458,342,1.132,344,1.834,346,0.929,349,0.592,353,1.68,371,1.269,398,1.055,399,1.294,402,0.579,407,1.704,409,2.106,411,0.799,417,3.466,419,1.666,420,1.333,426,1.514,430,0.741,432,1.494,436,0.917,442,1.577,471,4.719,472,1.132,474,2.172,476,1.649,478,0.988,480,1.345,481,0.981,497,1.009,521,2.385,543,4.332,545,2.685,557,1.476,559,2.241,565,2.402,578,0.899,580,1.269,596,1.234,625,2.201,640,3.583,643,1.257,646,1.341,648,3.998,649,2.924,650,2.803,667,1.514,669,4.361,672,3.113,720,0.955,867,1.212,908,1.234,925,1.675,939,1.649,943,1.16,950,1.347,963,1.039,979,1.442,991,5.026,994,1.834,1021,1.245,1039,1.784,1054,1.96,1057,1.675,1066,4.89,1073,4.903,1079,1.347,1085,1.392,1089,1.577,1124,1.834,1139,3.72,1155,1.6,1247,2.066,1274,2.513,1286,2.285,1297,2.386,1316,2.985,1339,1.307,1370,2.266,1399,2.01,1446,2.066,1477,1.392,1520,2.066,1531,1.555,1544,2.129,1568,1.872,1591,1.733,1642,1.96,1650,2.285,1674,2.386,1816,1.872,1834,1.458,1961,2.682,1962,4.366,2013,2.682,2041,3.364,2045,3.72,2061,1.872,2173,1.798,2231,5.422,2305,2.066,2510,2.939,2511,4.09,2512,2.939,2513,6.973,2514,6.973,2515,7.675,2516,2.939,2517,6.973,2518,2.939,2519,1.733,2520,3.885,2521,2.939,2522,2.939,2523,4.784,2524,2.939,2525,2.939,2526,2.939,2527,2.939,2528,2.939,2529,4.784,2530,2.939,2531,2.513,2532,4.913,2533,2.939,2534,2.939,2535,2.939,2536,2.939,2537,2.939,2538,2.939,2539,2.939,2540,2.939,2541,2.939,2542,2.939,2543,2.939,2544,2.939,2545,8.674,2546,6.363,2547,2.939,2548,2.939,2549,2.939,2550,2.939,2551,2.939,2552,4.784,2553,2.386,2554,2.201,2555,2.193,2556,3.885,2557,2.939,2558,3.466,2559,2.513,2560,2.682,2561,2.682,2562,2.939,2563,2.939]],["t/380",[14,1.059,58,1.74,62,0.924,66,2.065,83,0.472,121,4.798,146,1.934,162,2.17,165,2.53,175,2.345,177,4.01,180,2.004,196,2.17,210,1.934,212,1.32,234,2.567,261,1.355,265,1.246,268,3.897,287,3.001,297,3.845,306,4.422,332,3.569,383,3.45,396,1.879,430,1.857,468,2.409,471,4.888,497,2.53,521,1.879,543,5.021,545,2.837,601,2.567,676,2.958,888,3.493,1000,4.913,1111,3.845,1125,3.18,1339,3.275,1591,4.343,1837,4.343,1925,5.967,2126,4.133,2564,8.547,2565,9.366,2566,9.366,2567,6.723]],["t/382",[5,4.296,62,1.018,74,2.281,82,3.007,90,4.485,103,1.147,109,1.861,165,2.789,209,2.332,214,2.501,283,2.437,298,3.408,345,3.627,363,4.419,411,2.707,464,4.182,475,4.419,476,4.555,478,2.73,481,2.711,543,4.611,546,5.709,566,3.472,722,4.555,765,5.173,935,3.684,950,3.722,964,4.077,1299,5.289,1559,5.173,2281,6.942,2568,8.724,2569,6.942]],["t/384",[7,3.166,51,1.518,52,2.235,77,2.466,83,0.346,91,0.83,98,3.195,100,0.7,103,1.065,134,1.797,167,3.741,196,2.8,217,3.166,221,2.727,225,1.817,236,3.055,265,1.275,282,2.33,301,1.16,334,3.109,342,2.904,344,4.705,345,3.464,377,5.464,402,1.486,407,2.687,439,3.787,442,4.046,478,2.536,481,2.518,511,2.609,543,4.404,601,2.628,782,6.123,921,4.805,935,4.313,976,4.912,983,4.705,1105,4.37,1233,3.787,1245,3.884,1280,5.159,1294,5.159,1586,3.817,2064,6.882,2091,5.464,2280,5.159,2382,5.649,2559,6.448,2570,7.541,2571,7.541]],["t/386",[2,2.316,14,0.729,51,1.839,58,1.39,74,2.094,77,1.159,78,1.012,82,1.478,83,0.499,91,0.727,102,2.266,103,1.29,104,1.414,108,2.624,109,1.121,117,2.434,134,2.409,139,1.199,144,1.557,153,1.507,157,2.417,165,1.68,177,3.857,180,1.331,212,1.27,217,2.053,231,1.611,234,1.704,249,1.598,254,0.89,261,0.9,265,0.827,268,2.588,271,2.992,282,1.199,287,1.567,301,0.752,335,3.544,345,1.782,349,0.985,369,3.182,402,0.964,409,2.153,411,1.331,421,1.24,452,1.589,465,2.242,466,2.37,467,3.346,474,3.48,475,4.535,476,2.744,478,2.383,534,2.895,543,5.293,546,3.439,565,2.456,566,2.092,567,3.186,572,2.553,580,2.112,595,2.624,596,3.498,603,2.992,654,2.702,669,2.219,671,1.589,681,1.743,707,2.788,714,2.835,734,3.052,746,4.335,765,3.116,820,2.266,902,2.291,907,2.519,917,2.266,935,2.219,963,2.506,1005,2.702,1039,1.824,1064,3.186,1111,2.553,1117,2.316,1119,4.039,1120,5.134,1140,3.664,1166,3.186,1170,3.439,1171,5.294,1345,3.215,1365,2.835,1368,3.975,1378,4.847,1425,5.308,1454,2.702,1462,4.178,1570,3.346,1586,3.668,1591,2.884,1613,2.175,1785,2.936,1836,3.664,1925,3.116,2091,3.544,2194,5.754,2247,2.456,2338,5.557,2416,3.439,2511,4.182,2568,3.804,2572,4.891,2573,4.891,2574,4.182,2575,4.891,2576,3.804]],["t/388",[48,3.262]],["t/390",[5,2.456,6,2.293,14,1.182,48,2.261,56,1.518,58,1.561,64,2.142,74,2.352,83,0.313,91,0.706,103,1.402,130,2.23,134,2.365,137,1.157,138,1.88,139,1.671,141,2.302,153,2.49,157,1.583,162,1.367,163,1.832,169,1.07,196,1.367,221,1.678,225,1.118,236,1.88,250,1.094,261,1.255,275,1.487,283,2.047,286,2.043,287,1.487,296,2.043,301,1.049,312,2.646,315,1.359,329,1.617,340,0.996,363,2.526,402,0.915,405,2.526,427,4.011,430,1.17,437,3.477,438,3.597,461,1.897,472,1.787,474,4.021,477,1.924,478,1.561,480,0.895,515,3.263,522,2.49,550,3.609,578,3.221,589,1.817,591,1.931,592,5.303,595,2.49,600,3.263,601,1.617,613,2.69,614,3.477,623,2.198,669,2.106,680,3.769,686,2.456,733,1.897,791,5.831,828,3.23,882,2.331,887,2.151,902,2.174,913,3.968,914,2.526,939,2.604,963,2.412,1117,2.198,1174,2.302,1370,5.087,1405,3.769,1495,2.49,1496,2.49,1586,3.812,1595,4.345,1613,4.561,2001,3.363,2037,3.769,2247,4.06,2297,2.957,2319,2.839,2347,6.224,2577,4.641,2578,4.641,2579,6.224,2580,4.641,2581,6.82,2582,4.641,2583,4.641,2584,4.641,2585,4.641,2586,4.641,2587,3.363,2588,4.641,2589,4.641,2590,4.641,2591,4.641,2592,3.968,2593,3.263,2594,4.236,2595,4.236,2596,6.224,2597,4.641,2598,3.968,2599,4.236]],["t/392",[2,3.25,58,1.515,74,2.051,77,1.626,83,0.41,91,0.869,103,0.969,134,1.635,153,2.114,234,2.391,237,3.082,239,3.656,249,1.547,261,1.262,287,2.199,300,4.695,405,3.735,455,4.577,465,4.102,478,2.308,497,3.073,521,2.539,556,2.087,565,3.446,568,3.18,588,3.25,590,4.146,628,4.972,834,5.02,907,3.535,908,2.881,917,3.18,919,4.372,1050,3.682,1111,3.582,1114,4.282,1176,5.868,1278,6.292,1370,4.715,1477,3.25,1586,3.593,1595,4.372,1990,5.572,2011,5.337,2059,3.912,2126,3.85,2133,5.337,2245,5.337,2247,4.493,2372,5.572,2600,6.263,2601,9.957,2602,6.863,2603,5.141,2604,6.863,2605,8.948,2606,6.863,2607,6.863,2608,6.863,2609,6.863,2610,6.863,2611,6.263,2612,6.263,2613,6.863]],["t/394",[8,1.477,14,0.788,50,3.827,56,1.386,58,1.56,60,1.557,62,0.532,64,1.332,74,1.756,83,0.44,85,2.761,86,2.981,91,0.97,100,0.974,103,0.599,129,2.007,134,2.178,139,1.039,144,1.349,145,2.271,146,1.113,156,2.834,160,1.581,169,0.977,170,1.964,180,1.153,189,2.457,197,2.057,203,2.499,209,0.993,210,1.113,212,1.142,215,1.456,221,1.533,236,1.717,239,1.732,241,1.619,253,3.483,258,1.057,261,0.78,265,1.078,275,3.077,282,1.039,283,1.272,286,1.866,294,1.477,301,0.981,306,2.545,311,2.043,317,0.895,335,3.071,338,2.827,342,1.632,369,1.904,373,4.062,395,2.243,402,1.256,404,4.775,422,1.377,426,2.183,430,1.069,439,2.129,473,3.629,477,1.798,480,1.478,531,2.03,553,1.499,578,2.608,591,4.444,596,1.779,601,1.477,639,4.775,643,1.813,648,1.813,649,1.181,691,2.545,700,2.827,720,2.071,752,1.569,769,2.701,820,2.954,944,2.378,974,1.732,992,2.078,997,2.893,1039,1.581,1067,2.9,1088,2.582,1100,2.457,1153,2.9,1335,4.361,1339,1.885,1370,4.854,1461,3.175,1477,5.138,1544,3.071,1545,3.296,1572,4.361,1613,1.885,1658,3.442,1762,4.775,1860,2.645,1874,2.457,1965,2.761,2142,5.451,2302,3.071,2614,2.981,2615,4.239,2616,4.239,2617,4.239,2618,4.239,2619,4.239,2620,6.376,2621,5.818,2622,8.523,2623,6.376,2624,8.523,2625,6.376,2626,6.376,2627,6.376,2628,6.376,2629,5.818,2630,7.663,2631,7.663,2632,3.868,2633,4.239,2634,3.868,2635,4.239,2636,4.239,2637,4.239,2638,4.239,2639,7.663,2640,3.868]],["t/396",[1,1.085,2,2.025,4,1.199,6,1.438,8,1.49,14,0.991,51,0.861,55,1.471,62,1.074,66,1.199,67,1.661,74,1.766,81,1.883,83,0.511,91,0.373,103,1.088,107,1.529,109,0.98,111,1.418,113,2.328,121,2.786,122,3.903,130,1.399,134,1.529,138,1.732,139,1.573,141,2.122,144,2.725,165,1.469,169,0.986,180,1.163,196,1.26,197,1.38,209,1.805,212,0.766,214,1.317,227,1.829,230,1.583,231,2.537,236,2.6,252,2.825,253,1.748,254,1.402,255,1.583,257,2.175,258,1.066,261,1.181,265,1.448,275,2.057,282,1.048,283,1.927,286,1.883,287,2.057,294,2.237,296,1.883,317,1.356,329,1.49,333,1.185,334,3.53,346,1.352,348,2.616,352,2.328,359,1.49,405,2.328,430,1.078,448,2.786,472,1.647,474,3.293,477,1.206,478,2.158,480,1.238,530,2.203,531,2.048,536,1.864,571,3.007,578,2.807,580,1.846,588,2.025,589,1.674,591,4.581,601,1.49,631,2.926,632,2.203,642,2.438,643,1.829,650,1.982,669,2.912,700,5.71,820,1.982,879,3.903,882,2.148,888,1.595,928,2.479,948,2.786,950,1.961,1008,3.473,1064,2.786,1082,3.326,1101,3.204,1118,2.669,1120,4.65,1154,2.669,1169,2.567,1315,3.903,1326,1.829,1370,4.345,1389,3.326,1456,2.399,1477,3.648,1537,3.903,1586,4.669,1648,2.567,1667,2.786,1671,3.967,2142,3.657,2247,5.808,2283,3.903,2297,2.725,2307,2.616,2320,2.853,2416,5.417,2579,5.857,2621,3.903,2632,3.903,2634,3.903,2640,3.903,2641,3.903,2642,4.277,2643,3.903]],["t/398",[14,0.6,51,1.847,58,1.504,59,2.343,74,1.337,82,2.772,83,0.453,84,2.93,91,0.509,106,2.265,129,2.763,130,1.908,138,2.363,196,1.718,197,1.882,209,1.367,214,3.378,241,2.228,250,1.891,261,1.073,275,1.87,283,2.409,403,2.323,419,1.606,420,2.647,421,1.479,473,2.763,518,3.045,556,3.15,578,2.456,733,4.734,738,4.37,887,3.719,888,2.176,990,4.389,992,4.497,1015,3.717,1228,8.326,1256,2.826,1370,3.801,1380,3.569,1456,3.273,1613,2.594,1634,4.737,1684,4.537,1685,4.537,1754,3.991,1875,4.537,2644,9.174,2645,5.834,2646,7.528,2647,5.834,2648,5.324,2649,5.834,2650,5.834,2651,5.834,2652,9.174,2653,9.455,2654,9.455,2655,8.372,2656,10.361,2657,5.834,2658,7.324,2659,5.834]],["t/400",[4,1.244,14,1.153,62,1.168,69,2.228,74,1.017,78,0.633,82,1.34,83,0.448,91,0.387,115,2.315,117,1.524,134,1.572,137,1.644,142,1.751,162,1.307,164,3.45,175,1.412,179,1.722,180,1.207,197,1.431,206,3.008,209,1.04,210,1.165,212,0.795,230,1.642,234,1.546,249,1,250,1.045,258,1.106,265,1.115,275,1.422,315,1.298,329,1.546,340,0.952,346,2.49,349,0.893,420,2.013,477,1.251,480,1.272,521,1.682,540,1.513,541,1.766,553,1.569,578,2.409,589,2.582,598,2.959,605,2.826,637,4.202,641,3.45,648,2.821,649,1.838,663,2.034,672,1.592,674,2.285,681,1.58,682,1.972,686,3.49,699,2.768,733,1.813,887,2.055,888,2.46,908,1.862,939,2.489,940,3.889,943,2.604,963,1.569,979,0.917,990,2.794,1024,3.648,1053,5.129,1067,3.035,1110,5.705,1114,2.768,1118,2.768,1177,3.323,1187,2.013,1225,3.323,1256,2.149,1348,2.315,1368,4.891,1370,4.129,1471,6.214,1477,2.101,1495,3.539,1530,4.202,1533,4.934,1559,2.826,1613,3.5,1632,6.019,1633,7.185,1742,4.048,1764,4.638,1844,2.315,2126,5.227,2279,3.035,2345,2.663,2459,3.214,2660,3.793,2661,6.596,2662,4.436,2663,4.436,2664,4.436,2665,4.436,2666,4.436,2667,4.436,2668,4.436,2669,4.436,2670,4.436,2671,4.436,2672,4.436,2673,4.436,2674,4.436,2675,6.596,2676,4.048,2677,4.436,2678,6.596,2679,6.596,2680,7.874,2681,4.436,2682,4.436,2683,4.436,2684,4.436,2685,4.436,2686,4.436,2687,5.898,2688,4.436,2689,4.436,2690,6.596,2691,4.436,2692,4.436,2693,4.436,2694,5.356,2695,4.436,2696,3.45,2697,4.436,2698,4.436,2699,4.436,2700,4.436,2701,4.436,2702,4.436,2703,4.436,2704,3.323,2705,4.436,2706,3.602,2707,4.436,2708,4.436,2709,4.436]],["t/402",[1,1.382,6,1.833,14,0.91,51,1.097,58,1.638,67,2.116,83,0.406,91,0.476,107,1.299,111,1.807,129,2.581,134,2.108,137,1.359,169,1.764,196,1.605,197,1.758,212,0.977,254,0.992,256,1.956,261,1.003,267,4.239,275,2.835,317,1.151,332,2.64,346,1.724,349,1.097,369,3.438,371,2.353,376,2.376,409,2.399,421,1.382,436,1.701,477,2.496,518,2.845,521,1.39,559,2.553,578,3.207,605,3.473,650,2.525,733,4.283,752,2.017,888,3.3,908,2.288,944,2.033,992,3.752,1021,2.309,1039,2.033,1110,5.546,1144,3.058,1312,2.448,1370,4.19,1381,5.382,1493,2.884,1538,5.382,1613,4.266,1736,6.545,1765,6.545,1803,4.877,1804,5.106,1857,5.106,1951,3.832,2305,3.832,2646,5.382,2710,3.832,2711,4.974,2712,7.565,2713,9.596,2714,5.451,2715,5.451,2716,5.451,2717,7.655,2718,7.655,2719,5.451,2720,5.451,2721,5.451,2722,5.451,2723,5.451,2724,6.986,2725,7.655,2726,7.655,2727,7.655,2728,6.545,2729,7.655,2730,7.655,2731,7.655,2732,7.655,2733,7.655,2734,5.451,2735,5.451,2736,5.451,2737,5.451,2738,5.451,2739,5.451,2740,5.451,2741,5.451]],["t/404",[14,1.059,51,1.728,58,0.981,74,1.477,91,0.842,103,1.213,106,2.502,109,1.477,111,2.137,134,1.536,141,3.197,183,3.364,195,6.431,196,1.898,197,2.079,208,5.966,210,1.692,214,1.985,224,2.81,254,1.173,265,1.452,275,2.065,283,1.935,301,0.991,332,4.159,340,1.843,342,2.482,344,5.357,345,2.349,369,2.894,375,4.67,411,1.753,421,1.634,429,4.673,441,4.532,442,5.179,472,2.482,474,3.082,476,3.616,477,1.818,478,2.167,553,3.036,578,2.627,590,2.986,650,3.978,733,3.508,888,2.404,959,2.865,963,3.036,992,4.732,1067,4.409,1370,5.222,1381,4.532,1595,4.106,1613,4.292,1634,5.233,1661,5.233,2021,4.106,2335,8.802,2559,5.511,2742,5.233,2743,6.676,2744,6.445,2745,6.445,2746,6.445,2747,5.511]],["t/406",[8,2.071,14,1.072,55,1.362,58,1.237,61,4.082,62,0.745,74,1.863,77,1.926,82,1.795,83,0.273,91,0.709,100,0.552,103,1.148,130,1.944,134,1.416,145,1.762,165,2.041,167,2.948,169,1.37,172,2.307,206,2.27,209,1.393,212,1.456,215,2.041,221,2.149,225,1.432,234,2.832,236,2.407,265,1.005,275,3.191,329,2.071,332,2.879,333,1.646,345,2.166,349,2.005,369,2.669,371,2.566,380,2.182,402,1.171,421,1.507,422,1.93,430,2.336,466,2.879,477,1.676,478,1.998,479,2.234,521,2.073,531,2.846,545,2.289,553,2.874,572,3.102,578,3.048,588,2.814,627,3.786,632,3.061,651,2.542,652,4.066,653,4.066,674,3.061,682,2.642,720,1.93,722,3.334,894,2.939,950,2.725,959,2.642,963,2.102,1024,2.754,1066,5.178,1187,2.696,1231,3.708,1349,5.715,1370,5.587,1373,4.179,1477,2.814,1522,3.849,1530,3.786,1595,5.178,1860,3.708,1912,3.871,1923,4.452,2154,5.081,2467,5.424,2600,5.424,2712,5.081,2748,5.943,2749,5.943]],["t/408",[1,1.613,4,1.783,58,0.968,74,1.95,82,1.921,83,0.44,102,2.947,103,1.202,109,1.458,137,1.585,142,2.511,146,2.518,164,6.617,165,2.923,179,2.469,209,2.399,210,1.67,223,2.799,224,2.772,250,1.498,254,1.158,275,2.038,282,1.558,306,3.817,308,3.817,315,1.861,322,2.72,338,4.241,342,2.449,346,2.011,363,3.461,377,4.608,380,2.335,421,1.613,445,2.772,464,4.383,474,2.283,477,1.794,478,3.443,479,2.391,481,2.123,540,2.169,553,2.249,566,2.72,567,4.142,578,3.133,595,5.726,601,2.216,626,3.968,632,3.276,681,3.031,722,3.568,765,4.052,843,3.568,864,5.803,888,2.372,907,4.383,966,3.276,979,1.315,1015,4.052,1024,2.947,1033,3.817,1148,3.89,1190,3.89,1195,3.817,1208,4.773,1320,5.437,1348,3.319,1370,4.541,1398,3.568,1559,4.052,2059,3.625,2125,3.749,2280,4.35,2366,5.437,2750,5.437,2751,5.803,2752,5.803,2753,6.359,2754,5.437]],["t/410",[51,1.417,55,1.614,58,1.384,62,0.883,67,2.733,74,2.442,83,0.462,91,0.614,103,0.994,134,2.168,174,4.816,196,2.074,197,2.271,208,5.272,209,1.65,223,3.099,249,1.587,254,1.281,265,1.538,283,2.113,333,1.95,346,2.877,349,1.417,439,3.535,474,3.265,476,3.95,477,1.986,478,3.059,553,2.49,578,3.458,669,3.194,888,4.114,917,3.262,934,3.674,939,3.95,963,2.49,964,3.535,1146,4.695,1370,5.045,1530,4.485,1586,2.827,1595,6.422,1613,4.044,1634,5.716,2247,3.535,2611,6.425,2660,6.019,2712,6.019,2742,7.387,2755,7.04]],["t/412",[48,3.262]],["t/414",[4,2.881,7,3.08,51,1.477,58,1.738,74,1.682,78,1.334,83,0.428,91,0.975,100,0.868,137,1.829,139,1.798,145,2.175,156,3.262,182,2.502,205,3.23,209,2.189,210,1.927,214,2.26,217,3.08,229,3.597,242,4.616,249,1.654,254,1.336,283,2.203,322,3.138,336,3.295,340,1.575,342,2.826,404,5.497,410,2.922,464,3.78,465,3.364,466,3.555,477,2.07,540,2.502,820,3.4,926,5.16,927,4.876,979,2.237,1014,3.364,1028,4.489,1333,6.274,1398,4.117,1454,4.054,1518,5.317,1835,5.16]],["t/416",[6,2.777,7,3.467,8,3.505,51,1.663,55,2.305,83,0.461,91,0.878,98,3.499,100,0.934,103,1.167,172,3.207,191,3.467,230,3.057,249,1.862,275,2.647,348,5.053,349,1.663,353,1.99,480,1.593,529,6.423,530,4.255,531,3.956,963,2.921,979,2.242,993,3.709,1040,6.707,1139,7.821,1200,4.87,2059,4.708,2416,5.808]],["t/418",[52,2.427,55,1.877,78,1.169,83,0.459,84,4.112,91,1.007,100,0.761,107,1.951,109,1.877,124,3.317,175,2.607,225,1.973,230,3.03,239,3.346,333,2.268,336,3.677,349,2.014,353,1.973,422,2.66,452,2.66,477,2.822,480,1.58,496,5.024,497,2.813,510,5.009,556,2.49,834,4.594,867,3.376,1477,3.878,1675,4.524,2059,5.703]],["t/420",[14,0.827,52,2.933,55,2.106,58,1.631,59,2.35,62,0.734,78,1.148,83,0.421,91,0.905,100,0.543,107,1.394,137,1.459,139,1.434,146,1.537,172,2.272,179,2.272,191,2.457,212,1.048,225,1.41,236,2.37,237,4.126,246,4.115,249,1.319,274,3.175,291,2.771,346,1.85,351,3.58,353,2.766,396,1.493,402,1.811,406,3.047,407,2.085,421,2.039,436,1.826,465,4.213,477,1.651,480,1.551,521,1.493,541,2.33,545,2.253,585,4.115,623,2.771,671,3.369,673,2.683,675,3.812,714,3.391,733,3.755,888,2.999,943,2.31,979,2.345,990,2.479,1103,4.752,1117,2.771,1125,2.526,1158,4.003,1407,3.14,1438,4.24,1454,3.233,1516,2.976,1518,5.827,1522,4.352,1891,3.014,2125,3.45,2175,4.752,2756,5.852,2757,6.883,2758,5.852,2759,5.852,2760,4.752,2761,5.852,2762,5.852,2763,5.852,2764,5.003,2765,5.003,2766,5.003]],["t/422",[1,1.189,4,1.314,6,1.576,14,1.176,55,1.574,58,0.713,65,3.131,74,2.052,77,1.628,83,0.457,91,0.599,98,2.91,100,0.925,103,1.149,109,1.074,116,1.85,128,5.279,130,1.533,134,2.373,137,1.712,139,1.149,145,1.389,166,2.446,172,1.82,179,1.82,180,1.275,197,1.512,202,5.073,210,1.231,212,1.231,214,2.504,215,1.61,225,1.129,230,1.734,234,2.394,241,1.79,250,1.104,254,0.853,255,1.734,256,1.682,258,1.168,266,2.986,282,1.683,283,2.062,294,2.394,298,1.967,301,1.056,303,1.372,328,2.023,332,2.271,333,1.298,336,2.105,338,3.126,343,3.806,368,2.832,379,2.763,407,1.67,419,2.239,420,2.126,436,1.463,468,2.247,472,1.805,518,2.446,540,2.343,545,1.805,555,3.126,568,2.172,598,3.126,601,1.633,646,1.314,703,3.645,705,2.763,736,4.794,790,2.383,820,3.767,908,1.967,909,2.383,954,3.645,974,1.915,979,1.851,1024,2.172,1036,2.004,1067,3.206,1150,3.126,1187,2.126,1305,4.007,1332,2.986,1338,2.867,1453,5.18,1454,6.532,1514,4.83,1519,4.123,1650,5.342,1773,4.277,1775,5.578,1782,2.672,1831,3.296,1835,3.296,1867,3.806,1901,6.125,1990,3.806,2034,3.806,2112,2.924,2187,3.806,2298,3.296,2382,3.511,2767,3.511,2768,4.687,2769,4.277,2770,4.687,2771,6.869,2772,4.687,2773,4.977,2774,5.578,2775,3.296,2776,4.687,2777,4.687,2778,4.687]],["t/424",[14,1.073,51,0.987,55,1.124,58,1.392,60,1.801,62,1.046,64,2.621,74,1.627,78,1.306,83,0.383,91,0.965,100,0.969,103,0.693,104,1.418,107,1.692,109,1.913,119,3.565,130,1.604,134,1.168,137,1.222,144,1.561,175,1.561,206,2.712,215,2.438,218,1.747,234,1.709,236,1.986,242,2.202,249,1.601,250,1.156,254,0.893,255,1.815,258,2.08,261,0.902,265,1.2,282,1.202,284,1.593,301,1.407,317,1.5,328,2.117,334,2.022,349,1.429,363,2.669,369,2.202,370,2.349,371,3.603,402,1.644,406,1.626,430,1.79,436,1.53,452,2.71,475,2.669,480,1.37,496,5.082,497,1.684,510,5.104,534,2.901,536,2.138,538,4.262,540,1.672,541,1.952,543,3.866,545,1.888,674,2.526,752,1.815,872,2.117,888,1.829,890,3,902,2.297,919,3.124,979,2.158,1023,3.194,1111,2.56,1118,3.06,1139,5.521,1187,2.225,1203,3.982,1231,3.06,1398,2.751,1477,2.322,1499,3.982,1514,3.448,1522,2.322,1533,2.18,1578,3.864,1628,2.842,1675,4.61,1758,2.594,1824,2.526,1912,3.194,2059,2.795,2303,4.475,2459,3.553,2507,4.193,2614,4.992,2757,3.673,2760,3.982,2779,3.813,2780,3.673,2781,5.521,2782,4.904,2783,3.271,2784,4.193,2785,3.553,2786,4.904,2787,4.904,2788,4.904,2789,4.193,2790,4.904,2791,4.904,2792,4.904,2793,4.904,2794,4.904,2795,4.475,2796,3.982,2797,4.475]],["t/426",[14,1.243,77,1.674,82,2.135,83,0.418,91,0.881,100,0.656,111,2.343,134,2.542,153,2.176,170,3.274,180,2.481,206,2.699,212,1.266,224,3.08,231,3.325,234,2.462,236,2.862,249,1.593,282,1.732,287,2.264,301,1.403,305,3.64,347,4.137,349,1.422,353,1.703,369,3.173,370,3.384,371,4.359,405,3.846,409,3.11,439,3.548,496,4.579,530,3.64,538,4.242,540,2.41,543,3.274,622,3.31,674,3.64,681,2.517,872,3.051,1169,4.242,1187,3.206,1436,5.12,1454,3.904,1642,4.713,1782,4.028,1834,3.505,2059,4.028,2614,4.968,2780,5.293,2781,5.495]],["t/428",[50,3.601,51,1.208,52,1.778,55,1.375,58,1.682,62,0.752,64,1.884,77,2.369,78,1.647,81,2.641,83,0.458,84,3.013,91,0.872,98,2.542,100,0.972,102,2.78,109,1.375,110,4.347,111,1.989,117,2.061,139,1.47,144,1.91,145,1.778,146,2.444,150,3.013,175,1.91,197,1.936,204,3.013,212,1.668,215,2.061,250,2.193,253,2.451,254,1.092,255,3.7,284,1.949,288,2.061,301,0.923,306,3.601,311,2.983,317,1.728,340,1.288,353,1.446,376,2.615,396,2.086,399,2.641,402,1.182,428,4.001,480,1.157,481,2.731,518,3.131,529,4.665,752,2.22,766,4.494,842,4.741,876,6.063,979,2.358,1055,2.873,1085,2.841,1111,3.131,1339,2.667,1362,3.67,1578,3.265,1648,3.601,1721,4.104,2182,3.908,2253,3.908,2641,5.475,2760,7.558,2798,5.475,2799,5.999]],["t/430",[14,1.144,52,2.467,58,1.538,61,3.09,77,2.235,78,1.347,83,0.484,91,0.537,100,0.572,109,1.41,111,2.041,116,2.429,146,1.616,206,2.351,212,1.103,231,2.027,254,1.515,282,2.311,288,2.114,294,3.791,301,0.946,311,2.667,340,1.321,349,1.239,359,2.9,369,2.764,370,2.947,371,4.072,406,2.041,442,3.302,452,1.999,472,2.37,496,4.18,521,2.123,538,3.694,540,2.099,543,2.851,545,2.37,630,4.997,654,4.598,674,3.17,752,2.277,817,3.765,842,3.566,872,2.657,890,3.765,908,2.583,922,6.472,974,3.401,979,1.721,1012,4.105,1150,4.105,1233,4.18,1262,3.452,1453,3.921,1493,3.256,1530,3.921,1544,6.031,2059,3.508,2182,7.246,2614,4.327,2757,4.61,2779,4.786,2780,4.61,2781,4.786,2800,5.262,2801,8.323,2802,7.596,2803,6.154,2804,6.154,2805,6.154]],["t/432",[6,1.827,14,0.559,56,2.499,62,0.682,66,1.524,77,2.093,78,1.593,83,0.512,89,3.391,91,1.015,100,0.505,103,0.768,113,2.958,138,2.201,146,1.427,162,1.601,182,1.853,209,1.274,212,1.369,249,1.225,250,1.281,254,0.989,255,3.546,261,1,265,1.494,275,1.742,282,1.332,283,2.293,287,1.742,301,0.836,311,1.742,329,1.894,333,1.505,340,1.167,349,1.094,350,3.003,380,2.805,452,1.765,473,2.574,481,1.815,518,2.837,521,1.386,578,1.663,580,2.346,590,2.518,601,1.894,622,2.546,648,3.778,649,2.129,676,2.182,717,3.848,720,1.765,752,3.98,979,2.087,1035,3.178,1090,4.96,1093,8.199,1150,5.892,1262,4.286,1450,3.625,1457,3.325,1538,3.821,1665,4.413,1673,4.071,1703,4.647,2008,3.263,2018,3.54,2182,6.242,2223,4.96,2554,8.224,2598,4.647,2806,5.435,2807,5.435,2808,5.435,2809,4.071,2810,5.435,2811,5.435,2812,5.435,2813,5.435,2814,4.226,2815,5.535,2816,5.435,2817,5.435,2818,5.435]],["t/434",[14,0.936,52,1.439,55,1.113,58,1.583,62,1.212,66,1.361,77,1.67,78,1.485,83,0.462,91,0.908,100,1.076,103,0.686,137,1.757,139,1.19,153,1.495,175,2.641,182,1.655,205,2.137,209,1.137,212,0.87,218,2.51,225,1.698,234,1.691,254,0.884,255,2.608,256,1.742,277,2.97,301,0.747,311,2.258,340,1.042,346,1.535,349,1.418,353,1.999,396,2.654,419,2.661,420,2.202,465,4.432,480,1.359,553,1.716,557,2.437,570,3.775,596,4.233,606,3.583,629,2.274,646,2.552,648,4.312,649,2.312,752,1.796,872,3.042,882,2.437,894,3.292,941,3.454,979,2.246,998,3.162,1114,3.029,1125,2.096,1200,2.862,1384,3.678,1473,3.517,1521,7.571,1524,6.451,1530,3.093,1578,3.835,1624,6.451,1668,3.321,1846,3.636,1965,3.162,2107,6.596,2819,4.854,2820,4.854,2821,4.854,2822,4.854,2823,7.047,2824,4.854,2825,6.431,2826,6.025,2827,6.431,2828,4.854,2829,4.43,2830,4.854,2831,4.43]],["t/436",[14,1.115,58,1.53,62,1.036,100,1.007,103,1.167,231,2.72,258,2.059,301,1.27,303,2.418,398,3.61,402,1.628,419,2.274,596,4.552,597,5.808,606,4.2,872,3.566,912,5.249,979,1.708,1200,4.87,1467,6.037,1667,5.38,2107,5.985,2596,7.538,2826,7.062,2827,7.538,2831,7.538,2832,6.423,2833,7.062]],["t/438",[14,1.125,62,0.672,78,1.36,81,2.359,83,0.437,91,0.971,100,0.933,145,1.588,146,1.407,150,4.402,163,2.115,189,3.105,199,2.406,201,3.217,206,2.047,209,1.256,225,1.291,254,0.975,255,1.983,259,3.006,261,0.986,265,0.906,275,1.717,282,1.313,283,1.609,294,1.867,296,2.359,301,1.163,317,1.132,346,1.694,349,1.079,353,2.295,369,2.406,370,2.566,371,3.785,376,2.336,380,3.219,386,4.167,410,2.133,421,2.774,422,1.741,436,1.672,452,2.848,464,2.76,466,2.596,474,1.923,478,1.802,496,3.798,521,1.367,538,3.217,541,2.133,543,2.483,578,1.64,601,1.867,648,2.292,649,1.493,653,5.174,674,2.76,694,3.217,717,2.152,728,3.278,752,3.244,872,3.266,876,3.491,890,3.278,946,3.897,962,3.217,974,2.19,979,2.3,1035,2.229,1038,3.883,1087,4.89,1088,3.064,1339,2.382,1578,2.917,1923,7.523,2059,4.311,2182,3.491,2587,3.883,2614,3.768,2757,4.014,2760,4.351,2779,4.167,2780,4.014,2781,4.167,2795,6.903,2796,6.142,2815,3.883,2834,5.359,2835,7.564,2836,5.045,2837,3.883,2838,5.359,2839,5.359]],["t/440",[51,1.977,60,2.919,71,3.851,78,1.135,83,0.365,91,0.694,100,1.082,103,1.123,134,1.894,169,1.832,215,2.731,224,3.465,225,1.916,255,2.942,265,1.661,277,2.342,284,2.582,301,1.223,333,2.202,347,3.607,353,1.916,380,2.919,402,1.936,540,2.711,934,4.149,962,4.772,979,2.442,1454,4.392,2018,5.179,2182,6.399,2206,4.863,2554,5.955]],["t/442",[14,0.822,55,1.043,58,0.693,62,0.843,64,1.43,67,1.768,69,3.376,78,1.261,83,0.432,91,0.587,100,0.947,103,1.129,107,1.602,109,1.043,112,3.925,134,2.104,137,1.135,138,1.844,141,2.258,158,3.159,162,1.341,165,1.564,172,2.611,196,1.341,200,2.982,206,1.739,210,1.195,214,1.402,217,1.911,225,1.097,237,2.044,254,1.224,259,2.554,261,0.837,265,0.77,284,1.479,298,1.911,301,1.034,317,0.962,329,1.586,340,0.977,352,2.478,369,2.044,371,1.966,376,1.985,402,0.897,403,3.515,406,3.123,407,1.622,421,1.705,430,1.148,448,2.966,470,1.844,477,1.284,479,1.711,496,5.121,497,1.564,510,4.113,534,2.747,538,2.733,541,1.813,545,2.589,548,2.554,569,1.828,589,1.782,648,1.947,649,2.228,672,1.634,682,2.989,702,4.015,704,2.345,752,1.685,821,2.376,884,5.46,888,2.982,894,1.646,908,1.911,934,3.509,948,2.966,962,2.733,979,1.825,1028,2.785,1039,1.698,1069,7.055,1111,2.376,1118,2.841,1170,3.201,1312,2.044,1477,3.786,1497,3.54,1533,4.752,1545,5.229,1572,6.744,1583,3.893,1675,2.515,2107,3.299,2325,3.036,2333,3.201,2414,3.201,2459,5.792,2757,3.41,2840,4.553,2841,7.995,2842,6.03,2843,6.724,2844,4.553,2845,4.553,2846,6.724,2847,4.553,2848,4.553,2849,4.553,2850,7.169,2851,4.553,2852,2.315,2853,4.553,2854,7.995,2855,4.155,2856,4.553,2857,4.553,2858,4.553,2859,6.724,2860,4.553,2861,4.553]],["t/444",[48,3.262]],["t/446",[4,1.811,6,1.089,14,0.964,51,0.652,52,1.533,55,0.742,58,1.665,60,1.9,61,1.626,62,1.078,74,1.186,77,0.767,78,0.462,80,1.031,82,0.978,83,0.486,91,0.883,97,1.268,100,0.799,101,1.777,103,0.913,107,1.539,109,1.481,116,2.042,130,2.113,134,2.303,137,1.29,138,1.312,139,0.794,141,1.606,142,1.278,157,1.764,162,1.524,170,1.5,175,1.031,180,0.881,181,2.277,182,1.104,196,0.954,197,1.669,199,2.323,209,1.212,210,0.85,212,0.58,214,0.997,218,2.628,225,0.78,230,1.198,250,0.763,253,3.015,254,1.176,258,1.61,261,0.596,265,1.248,273,1.469,282,1.268,283,0.972,288,1.112,294,1.128,296,1.425,297,1.69,298,2.172,301,0.994,311,1.038,322,1.385,349,1.817,359,1.128,370,4.847,371,3.896,376,1.412,379,1.909,396,1.319,402,0.638,403,2.06,419,2.485,422,1.68,429,1.763,430,0.817,432,1.647,436,1.011,454,1.981,464,1.668,467,2.215,468,1.692,473,1.534,475,1.763,477,1.822,478,1.089,480,0.625,481,1.727,497,1.112,521,0.826,534,1.323,540,1.104,559,3.026,574,2.518,580,1.398,623,1.534,635,1.551,649,0.902,672,1.162,682,2.872,684,2.858,716,2.346,717,2.077,720,1.68,722,1.817,774,1.647,834,1.817,872,1.398,882,2.598,887,1.5,888,1.929,890,1.981,963,1.145,992,1.587,995,1.569,997,1.469,1014,1.485,1026,1.647,1035,1.347,1100,2.998,1109,2.109,1116,2.769,1174,1.606,1230,3.106,1231,3.228,1365,2.998,1397,1.846,1472,4.201,1477,3.494,1496,2.776,1535,2.426,1545,7.018,1713,1.981,1776,2.346,1820,2.426,1907,2.426,1910,2.346,1912,6.908,1917,2.426,1922,4.201,2181,2.16,2245,4.023,2301,4.201,2455,5.245,2456,5.895,2457,4.839,2458,2.63,2478,4.023,2711,2.955,2773,5.345,2862,2.769,2863,5.173,2864,5.173,2865,5.173,2866,5.173,2867,4.423,2868,5.173,2869,4.721,2870,5.173,2871,4.721,2872,5.173,2873,4.721,2874,4.721,2875,5.173,2876,4.721,2877,5.173,2878,2.955,2879,3.238,2880,5.173,2881,3.238,2882,3.238,2883,3.238,2884,3.238,2885,3.238,2886,3.238,2887,3.238,2888,3.238,2889,3.238,2890,3.238,2891,5.245,2892,5.173,2893,5.173,2894,5.173,2895,2.16,2896,3.238,2897,2.63]],["t/448",[14,1.205,49,3.178,55,1.533,56,2.187,58,1.018,62,0.567,66,1.267,77,1.071,78,0.955,82,1.366,83,0.479,91,0.584,100,0.621,103,1.125,109,1.533,116,2.64,134,1.077,137,1.127,146,1.187,158,2.143,165,2.297,196,1.331,200,1.686,209,1.059,211,2.27,212,1.198,217,1.898,225,1.918,227,1.933,230,2.475,231,3.839,241,1.727,250,1.876,254,0.823,258,1.667,261,0.832,265,1.346,284,1.468,288,1.553,301,0.695,303,1.958,333,1.252,340,0.97,345,1.647,346,1.429,349,0.91,353,1.612,359,1.575,367,7.014,368,3.282,370,3.203,371,4.054,396,1.153,402,0.891,411,1.819,419,1.841,422,1.468,430,1.14,452,1.468,461,1.847,468,1.478,471,4.592,478,1.52,480,0.872,530,2.328,534,1.847,541,1.8,543,2.094,546,3.178,553,1.598,564,2.392,566,1.933,591,1.88,613,4.614,643,1.933,646,1.875,691,2.714,704,2.328,717,3.533,720,1.468,774,2.299,867,1.864,872,1.952,888,1.686,926,3.178,944,1.686,963,2.815,975,4.461,1029,3.178,1102,3.515,1171,2.165,1345,3.035,1428,3.401,1447,3.943,1519,2.714,1570,3.092,1591,2.665,1869,3.67,1941,2.944,1942,3.275,1948,3.515,1984,4.125,2248,4.703,2319,2.765,2324,3.178,2325,3.015,2416,3.178,2568,3.515,2773,4.846,2897,3.67,2898,4.52,2899,4.52,2900,3.515,2901,4.52,2902,4.52,2903,4.52,2904,4.52,2905,4.52,2906,4.52,2907,4.52,2908,8.57,2909,6.689,2910,4.52]],["t/450",[1,0.616,14,1.128,25,3.73,58,1.325,62,0.867,74,1.212,78,1.352,83,0.424,91,0.654,100,0.226,103,0.747,106,0.943,109,1.421,116,0.959,129,1.151,134,1.899,137,0.606,138,0.984,158,1.695,163,0.959,182,1.804,187,1.583,196,0.716,202,6.404,206,2.369,208,1.408,209,0.569,211,1.22,212,0.435,215,0.835,218,1.456,225,0.585,231,2.624,249,0.548,250,1.63,254,1.129,256,0.872,258,0.606,261,0.447,284,0.789,301,0.374,315,0.711,317,0.513,329,2.161,333,0.673,336,1.091,343,3.318,345,1.489,349,0.823,359,0.847,368,3.285,370,1.164,371,3.236,400,1.304,402,0.479,403,1.627,407,0.866,411,1.687,419,2.063,432,1.236,436,0.758,467,1.662,473,1.151,477,0.685,478,1.778,480,1.196,481,0.811,521,1.042,524,1.408,531,1.164,534,1.67,536,1.059,543,1.126,553,0.859,559,1.914,572,1.268,623,1.151,642,1.385,682,3.332,684,1.342,707,2.33,717,1.641,738,6.926,774,1.236,797,4.361,888,1.524,890,1.486,912,1.268,940,1.433,963,0.859,974,0.993,992,2.003,1024,1.894,1069,1.82,1256,3.861,1260,1.889,1297,1.973,1308,3.061,1365,2.368,1370,2.505,1417,1.889,1496,4.276,1538,2.874,1595,2.604,1617,5.302,1671,3.693,1684,4.113,1685,3.178,1754,5.452,1803,4.775,1834,2.624,1910,1.76,1912,5.45,1922,1.973,1965,1.583,2212,5.036,2245,1.889,2298,1.708,2301,5.036,2455,1.973,2457,1.82,2478,1.889,2568,1.889,2576,3.178,2646,6.324,2773,3.832,2775,5.27,2784,2.077,2852,1.236,2867,2.077,2869,2.217,2871,2.217,2873,2.217,2874,2.217,2876,2.217,2911,2.43,2912,2.43,2913,2.43,2914,2.217,2915,2.43,2916,2.43,2917,2.43,2918,2.43,2919,2.43,2920,2.43,2921,6.313,2922,2.217,2923,2.43,2924,2.43,2925,9.247,2926,4.087,2927,6.202,2928,2.43,2929,2.43,2930,6.202,2931,6.202,2932,6.202,2933,2.43,2934,2.43,2935,9.951,2936,9.469,2937,2.43,2938,4.087,2939,2.43,2940,2.43,2941,2.43,2942,2.43,2943,6.202,2944,6.202,2945,2.43,2946,5.289,2947,9.469,2948,2.43,2949,2.43,2950,2.43,2951,2.43,2952,6.202,2953,2.43,2954,2.43,2955,2.43,2956,7.495,2957,5.289,2958,5.289,2959,5.289,2960,5.289,2961,5.289,2962,5.289,2963,5.289,2964,5.289,2965,2.43,2966,2.43,2967,2.217,2968,2.43,2969,2.43,2970,2.43,2971,2.43,2972,2.43,2973,2.43,2974,2.43,2975,2.43,2976,4.087,2977,2.43,2978,2.43,2979,2.43,2980,2.43,2981,2.43,2982,2.43,2983,2.43,2984,2.43,2985,2.43,2986,4.087,2987,6.202,2988,6.202,2989,2.43,2990,2.43,2991,4.087,2992,4.087,2993,3.494,2994,2.077,2995,2.43,2996,2.43,2997,2.43,2998,2.43,2999,2.43,3000,2.43,3001,2.43,3002,4.087,3003,2.43,3004,2.43,3005,2.43,3006,2.43,3007,2.43]],["t/452",[14,1.151,52,2.012,59,2.726,74,1.556,82,2.992,83,0.407,91,0.775,100,0.825,103,0.959,109,2.037,129,3.215,137,1.692,158,2.847,197,2.19,198,4.003,202,5.544,212,1.216,231,2.236,241,2.593,265,1.148,274,2.68,277,2,328,2.931,368,3.663,379,4.003,419,1.869,436,2.119,438,2.573,439,3.409,441,4.774,442,3.643,473,4.208,541,2.703,564,3.592,623,3.215,641,5.28,645,3.87,646,1.903,667,3.497,671,2.205,720,2.205,963,3.142,1109,5.788,1446,4.774,1496,4.767,1514,6.964,1900,6.196,1901,6.078,1965,4.423,2048,6.196,2775,4.774,3008,6.79,3009,6.79,3010,6.79,3011,6.79,3012,6.79,3013,6.79,3014,6.79,3015,6.79,3016,6.79,3017,6.79,3018,8.886,3019,6.79,3020,6.79,3021,6.79,3022,6.79]],["t/454",[6,1.4,14,1.094,51,1.266,58,1.451,60,1.529,62,0.951,74,1.442,80,1.326,83,0.288,84,2.091,91,0.549,106,2.443,109,1.738,134,2.013,137,1.038,139,1.021,142,1.644,153,1.283,156,1.852,158,2.43,159,5.679,165,1.431,171,2.374,177,2.267,197,1.344,199,2.825,202,5.271,206,2.403,209,0.976,210,1.094,212,0.746,214,2.335,215,1.431,284,1.353,315,1.841,333,1.154,349,1.266,359,1.451,368,2.594,369,2.825,403,1.658,419,2.326,430,1.05,478,1.4,480,1.214,496,5.499,510,3.848,521,1.062,530,2.145,531,3.013,534,2.571,538,5.444,556,2.305,591,1.733,672,2.258,681,1.484,682,3.37,684,2.301,720,1.353,733,4.474,734,2.599,774,2.118,882,4.242,888,1.553,944,1.553,950,1.909,990,3.212,992,3.716,1021,1.765,1039,1.553,1057,2.374,1074,4.712,1088,2.548,1154,2.599,1195,2.5,1231,2.599,1256,3.048,1270,3.12,1365,2.414,1370,2.979,1417,3.239,1496,3.375,1514,2.928,1538,4.424,1595,2.654,1684,4.892,1685,4.892,1754,4.304,1803,4.008,1910,3.018,1943,3.801,1989,5.679,2245,5.896,2298,2.928,2457,3.12,2478,3.239,2648,5.741,2653,6.919,2654,6.919,2655,3.801,2658,3.801,2773,4.558,2775,2.928,2914,3.801,2993,3.561,3023,4.165,3024,4.165,3025,4.165,3026,4.165,3027,6.291,3028,4.165,3029,4.165,3030,4.165,3031,4.165,3032,3.801,3033,4.165,3034,4.165,3035,4.165,3036,4.165,3037,4.165,3038,4.165,3039,4.165,3040,4.165,3041,4.165,3042,4.165,3043,4.165,3044,4.165,3045,4.165,3046,4.165,3047,4.165,3048,4.165,3049,4.165,3050,4.165,3051,4.165,3052,4.165,3053,4.165,3054,4.165,3055,4.165,3056,4.165,3057,4.165,3058,6.291,3059,4.165,3060,4.165,3061,6.291,3062,4.165,3063,4.165,3064,6.291,3065,6.291,3066,4.165,3067,4.165,3068,4.165,3069,4.165,3070,4.165,3071,4.165]],["t/456",[4,1.72,6,2.063,7,2.575,14,0.968,48,2.754,51,1.235,58,1.433,62,1.181,74,2.313,77,1.454,78,0.876,81,2.7,83,0.463,100,0.57,101,2.107,124,2.485,202,5.182,210,1.611,212,1.488,214,1.889,254,1.117,262,3.291,265,1.037,275,2.661,278,7.579,283,1.841,286,2.7,301,1.277,315,1.796,342,3.198,346,1.94,368,2.529,370,2.938,399,2.7,402,1.209,419,1.689,422,1.993,429,3.339,430,1.547,432,4.788,439,3.08,444,4.313,445,2.674,496,4.728,497,2.853,511,2.122,521,1.565,578,2.541,627,5.999,632,4.278,667,3.16,675,3.996,682,2.727,686,3.245,774,4.223,882,3.08,1067,4.196,1119,3.497,1208,3.441,1476,3.908,1496,3.291,1714,5.245,1897,5.598,1911,4.595,1912,3.996,2008,3.682,2060,5.245,2478,6.458,2773,4.445,2867,5.245,3072,9.416,3073,6.134,3074,6.134,3075,6.134,3076,6.134,3077,8.593,3078,8.305,3079,6.134,3080,6.134,3081,6.134,3082,6.134,3083,6.134,3084,6.134]],["t/458",[14,0.502,58,1.537,74,2.446,77,1.156,83,0.489,91,0.882,100,0.453,109,1.621,116,1.926,134,2.632,137,1.216,153,1.503,157,1.664,179,1.894,196,1.437,202,6.893,206,3.178,212,0.874,231,3.326,239,2.89,258,1.216,261,0.897,265,0.825,276,2.737,284,1.585,287,1.563,296,2.148,298,2.048,334,2.011,340,1.047,349,1.95,359,1.7,367,4.506,368,3.762,369,2.191,370,3.387,371,3.939,402,0.961,403,1.942,405,2.655,409,2.148,430,1.783,468,2.313,477,1.995,478,1.64,481,1.629,496,4.864,510,2.984,520,3.044,534,1.994,538,2.929,541,1.942,546,3.43,548,2.737,572,2.546,627,3.108,632,2.513,648,2.086,649,1.359,674,2.513,682,3.144,684,3.907,717,4.184,752,1.805,888,3.981,994,3.044,1024,2.261,1231,5.191,1345,3.209,1365,4.822,1477,2.31,1495,2.618,1496,5.42,1514,6.416,1545,5.5,1724,3.43,1869,3.961,1901,3.338,1912,5.42,1942,3.535,2158,5.5,2298,3.43,2301,3.961,2576,5.5,2775,7.103,3085,4.879,3086,3.794,3087,4.879,3088,4.879,3089,4.879]],["t/460",[48,3.262]],["t/462",[6,1.285,7,2.474,14,1.161,52,1.133,55,0.876,58,1.74,62,0.902,64,2.259,66,1.072,78,1.027,80,1.217,82,1.155,83,0.477,89,2.385,91,0.998,100,0.547,103,0.54,117,2.024,124,1.548,130,1.25,134,0.911,137,1.792,170,1.771,180,1.04,198,2.254,209,2.436,212,1.056,221,1.382,225,1.42,230,1.415,237,1.717,240,2.688,241,1.46,249,1.329,250,1.694,254,1.073,255,2.99,258,0.953,261,0.703,265,0.646,279,2.688,282,1.444,283,1.769,300,2.615,301,0.588,317,1.519,328,1.65,329,1.332,333,1.059,334,1.576,339,1.896,340,1.265,346,2.555,349,0.769,353,1.733,396,0.975,402,1.417,407,1.362,419,1.622,421,2.049,422,3.122,429,3.207,430,1.486,436,2.522,438,3.062,439,1.92,442,2.051,472,1.472,473,1.81,480,1.779,521,0.975,534,2.408,536,1.666,545,1.472,553,1.352,556,1.162,578,1.17,580,1.65,589,1.496,645,2.179,646,2.447,651,1.635,663,1.752,667,1.969,691,2.295,720,1.914,728,2.338,910,2.863,982,1.896,1029,2.688,1042,2.112,1097,2.338,1111,4.813,1117,4.699,1345,3.666,1368,5.567,1402,4.487,1407,2.051,1436,2.77,1446,2.688,1534,1.969,1623,2.081,1648,2.295,1655,2.77,1656,7.441,1663,2.863,1785,2.295,1860,2.385,1891,1.969,1965,2.49,2181,2.55,2305,2.688,2345,2.295,2484,2.688,2558,2.77,2646,2.688,2783,3.93,3090,5.893,3091,5.893,3092,3.268,3093,3.268,3094,3.823,3095,3.488,3096,3.104,3097,3.823,3098,5.378,3099,3.823,3100,3.823,3101,5.893,3102,5.893,3103,5.893,3104,3.823]],["t/464",[14,1.115,51,1.279,52,1.251,55,1.456,56,1.38,58,1.594,62,1.144,63,3.707,77,1.506,80,2.023,82,1.275,83,0.418,85,2.749,86,2.967,91,0.555,98,1.788,108,2.264,111,1.399,118,4.847,129,1.999,134,1.006,137,2.12,139,1.873,142,1.666,145,2.521,148,1.876,153,1.957,157,1.439,162,1.872,180,1.148,182,1.439,206,1.612,209,2.454,210,1.108,214,1.957,217,1.772,223,1.858,225,1.017,230,1.562,231,2.092,239,1.724,241,2.427,249,1.723,254,0.768,258,1.052,261,0.776,265,0.714,282,1.557,283,2.294,288,2.625,300,2.887,301,0.649,303,1.235,315,2.806,317,1.342,329,1.471,332,2.044,333,1.169,350,3.51,353,1.017,378,2.488,402,2.064,431,2.021,438,1.599,452,1.371,468,1.38,478,1.419,480,1.474,497,1.45,521,2.169,534,3.123,536,3.976,650,1.955,671,1.371,694,2.533,714,3.682,733,1.724,736,2.488,752,2.351,820,1.955,852,3.608,915,5.433,924,2.069,935,1.915,959,1.876,968,2.887,1024,2.944,1026,3.231,1146,4.238,1171,2.021,1183,2.749,1300,3.427,1345,1.915,1368,4.771,1428,3.231,1446,2.967,1496,4.1,1531,3.362,1536,2.749,1628,3.682,1656,7.3,1666,6.905,1671,3.94,1891,2.174,2001,5.537,2009,3.608,2252,2.331,2319,2.582,2484,2.967,2519,2.488,2646,2.967,2710,4.468,2967,3.851,3098,6.974,3105,4.22,3106,4.22,3107,4.22,3108,4.22,3109,7.642,3110,4.22,3111,7.642,3112,4.468,3113,6.354,3114,3.608,3115,4.22,3116,4.22,3117,4.22,3118,4.22]],["t/466",[1,1.985,4,1.578,14,1.157,52,2.668,56,3.182,62,0.982,66,1.578,71,2.726,82,1.7,83,0.446,91,0.967,97,3.524,98,2.384,111,1.866,117,2.689,130,1.84,134,2.437,137,1.403,139,1.379,140,3.227,142,3.09,145,2.668,146,1.478,158,2.509,170,2.607,180,2.129,209,2.109,210,1.478,212,1.008,225,1.886,249,1.269,268,2.977,284,1.828,286,2.477,287,1.803,315,1.647,317,1.189,329,2.728,333,2.168,340,1.68,376,2.453,436,1.756,438,3.687,477,1.587,567,3.666,601,1.961,606,5.384,623,2.665,646,2.728,672,2.02,682,2.502,720,1.828,894,2.035,1024,2.607,1111,4.086,1312,2.527,1402,6.381,1496,4.829,1656,7.577,1667,3.666,1891,2.899,1915,3.957,3119,5.628,3120,7.828,3121,6.356,3122,4.811,3123,5.628,3124,7.828,3125,5.628,3126,5.628,3127,5.628,3128,5.628,3129,3.666,3130,5.136]],["t/468",[2,1.81,4,1.652,14,1.215,51,1.186,55,1.351,56,1.25,58,1.23,62,0.739,63,1.666,71,1.852,74,0.876,78,0.841,80,1.217,83,0.499,91,0.923,97,2.307,107,0.911,109,0.876,111,1.954,113,2.081,117,3.408,118,4.606,134,0.911,137,1.469,139,1.444,141,1.896,142,1.509,145,1.133,158,1.225,165,1.313,170,1.771,180,1.04,182,1.304,197,1.901,205,3.165,209,2.161,210,1.004,212,1.652,214,1.177,227,2.52,229,1.874,231,2.368,249,1.621,250,0.901,254,1.073,261,1.084,265,0.996,282,1.444,317,0.807,329,1.332,333,1.059,340,1.265,345,1.393,363,2.081,394,2.022,409,2.594,421,0.969,422,1.914,431,1.831,438,2.725,448,2.49,468,2.352,471,5.179,481,1.276,518,1.995,521,1.834,545,3.361,603,4.399,606,1.944,617,2.973,623,1.81,629,1.79,631,2.615,643,2.52,646,1.072,650,1.771,671,1.242,720,1.242,829,3.488,909,1.944,939,3.306,944,1.426,948,2.49,950,1.752,1021,1.62,1023,3.838,1028,2.338,1035,2.451,1117,1.81,1146,3.93,1166,2.49,1187,1.734,1203,3.104,1402,5.996,1438,2.77,1440,5.386,1456,2.144,1496,2.051,1648,2.295,1656,7.441,1666,5.839,1671,3.744,1672,4.414,1674,4.785,1713,2.338,1834,1.896,1890,3.104,1947,2.49,2059,2.179,2213,3.104,2252,5.095,2554,6.538,2564,3.488,3092,6.148,3093,6.148,3112,2.688,3129,2.49,3130,3.488,3131,3.823,3132,5.893,3133,5.378,3134,6.562,3135,3.823,3136,3.268,3137,3.268,3138,5.893,3139,3.823,3140,3.823,3141,3.823,3142,3.823,3143,3.823,3144,6.561,3145,3.823,3146,3.823,3147,5.893,3148,3.823,3149,3.268,3150,3.823]],["t/470",[4,1.631,7,3.362,14,0.942,51,1.171,55,1.333,58,0.885,78,0.83,83,0.475,91,0.861,97,2.277,103,0.822,109,1.333,111,1.929,112,2.586,135,2.786,137,1.996,142,2.296,158,2.566,165,1.998,169,1.846,196,1.713,209,1.877,210,2.103,212,1.042,214,1.791,237,4.113,241,2.222,261,1.07,276,3.263,283,1.746,315,1.702,333,1.611,340,1.249,345,2.119,346,1.839,349,1.612,350,3.213,351,3.558,363,3.166,371,2.511,372,3.629,387,3.558,392,4.722,410,2.316,411,2.179,438,3.035,478,1.956,479,2.186,480,1.122,521,1.483,571,4.09,606,4.072,888,2.169,930,4.523,944,2.169,950,2.666,956,4.437,995,2.818,1021,2.464,1024,3.711,1097,4.899,1297,6.503,1326,2.487,1345,4.853,1461,4.357,1530,5.836,1656,7.317,1689,4.214,2117,5.308,2212,7.437,2436,3.315,2921,10.001,2922,8.359,3151,5.816,3152,5.816,3153,4.723,3154,5.816,3155,9.16,3156,5.816,3157,5.816,3158,5.816,3159,5.816,3160,5.816,3161,5.816]],["t/472",[4,2.011,14,1.103,58,1.092,62,1.155,66,2.011,83,0.422,91,0.803,98,3.039,100,0.666,103,1.013,111,2.378,117,3.162,137,2.295,158,2.951,169,2.122,218,2.555,231,2.362,237,4.135,254,1.676,261,1.319,368,3.796,371,3.097,402,1.413,421,2.721,438,3.489,556,2.181,578,2.195,606,4.682,646,2.011,648,3.067,649,1.999,674,3.694,752,3.97,894,2.594,956,4.46,994,4.475,1026,4.682,1035,2.984,1036,3.067,1402,4.475,1656,6.299,1687,5.866,2207,5.578,2576,5.578,2587,5.197,3162,7.173,3163,7.173,3164,7.173]],["t/474",[1,1.772,7,2.934,62,0.876,74,1.602,77,1.656,78,0.998,80,2.882,83,0.461,91,0.96,100,0.649,103,1.279,117,2.4,124,2.831,160,2.606,169,1.611,221,2.527,225,1.684,228,3.204,231,2.301,239,2.856,241,2.669,284,2.27,287,2.902,295,5.079,298,2.934,301,1.393,315,2.046,333,1.936,349,1.407,393,3.984,395,3.697,402,1.979,475,3.804,477,2.554,497,2.4,556,2.125,566,2.989,568,4.195,572,3.648,583,7.523,591,3.766,676,2.806,805,4.453,852,5.975,902,3.273,944,2.606,950,3.204,956,3.386,1097,4.275,1656,7.268,1658,5.675,1724,4.914,1732,5.675,2001,5.064,2307,5.539,2325,4.661,3112,4.914,3165,6.989]],["t/476",[4,2.106,6,2.526,74,1.722,77,1.78,83,0.344,84,3.772,100,1.067,103,1.542,157,2.562,161,4.786,172,2.916,175,2.391,221,2.716,225,2.285,265,1.603,282,2.324,301,1.678,317,1.587,333,2.08,345,2.737,350,4.15,353,1.81,405,4.088,419,2.068,473,3.557,648,3.212,684,4.15,894,2.716,902,3.518,962,5.692,972,4.893,978,6.099,979,2.412,980,5.442,1015,4.786,1088,3.841,1155,4.088,2743,5.841,3166,7.512]],["t/478",[48,3.262]],["t/480",[4,1.844,6,2.926,8,2.292,14,0.677,62,0.825,66,1.844,77,1.559,78,0.939,83,0.509,91,0.574,100,1.051,146,2.561,156,2.925,169,2.248,177,3.581,191,2.762,197,2.122,212,1.179,215,2.989,234,2.292,254,1.197,259,3.691,294,2.292,301,1.012,333,1.822,340,1.412,395,3.481,398,2.361,402,1.296,430,1.659,431,3.151,452,2.137,521,1.678,627,6.877,632,3.389,646,2.44,648,4.171,649,3.296,671,2.827,672,2.361,673,4.471,686,3.481,717,3.495,774,3.345,867,2.712,882,3.304,908,2.762,1079,3.016,1113,4.808,1152,3.434,1171,3.151,1941,4.285,3096,5.342,3167,5.625,3168,6.579,3169,6.579,3170,6.579,3171,5.116]],["t/482",[4,1.651,6,1.98,14,0.831,55,1.35,77,1.395,78,1.416,83,0.492,91,0.705,100,0.547,103,0.832,146,1.546,156,2.618,157,2.008,163,2.324,169,2.286,172,2.286,200,3.012,205,2.592,210,1.546,211,2.957,212,1.055,215,2.022,234,3.212,252,2.592,253,2.406,254,1.072,258,1.468,259,3.303,261,1.083,265,0.996,294,2.814,340,2.129,346,1.862,353,1.419,363,3.205,396,1.502,402,1.16,407,2.877,430,1.485,432,2.994,464,3.033,469,3.033,497,2.022,521,2.53,545,3.11,556,1.79,622,2.758,648,3.454,649,3.26,671,1.913,672,2.899,673,2.699,733,2.406,778,4.579,821,3.073,894,2.129,909,2.994,914,3.205,962,3.535,979,1.906,991,3.412,1035,2.449,1089,3.159,1150,5.387,1152,3.073,1194,5.034,1339,2.618,1836,4.41,1837,4.762,1843,4.215,1941,5.261,2101,4.028,2173,3.602,2289,3.835,2310,4.41,2324,5.679,3137,5.034,3172,5.888,3173,5.888,3174,5.373,3175,4.14,3176,5.034,3177,5.888,3178,5.888,3179,5.888,3180,5.373]],["t/484",[51,1.091,52,1.606,56,2.494,57,2.511,62,0.956,77,2.09,78,1.367,83,0.48,91,0.665,100,1.05,102,2.511,103,0.766,111,1.797,153,1.669,169,1.249,183,3.979,206,2.07,211,2.721,212,0.971,239,2.215,249,1.222,254,1.606,259,3.04,262,2.908,277,2.819,305,2.792,340,2.165,349,1.091,353,1.306,398,1.945,422,1.76,479,2.037,480,1.045,481,3.196,580,2.34,601,1.889,629,2.538,646,1.519,648,4.692,649,3.314,655,3.927,672,2.737,720,1.76,765,3.453,894,4.034,946,2.409,959,2.409,979,1.824,1033,3.253,1036,2.318,1039,2.021,1055,2.596,1122,4.059,1174,2.688,1317,4.059,1530,3.453,1578,2.95,1624,7.843,1723,4.401,1837,3.195,1843,3.979,2297,3.453,2532,8.495,2554,4.059,2743,4.214,3181,4.634,3182,8.049,3183,4.946,3184,5.42,3185,8.821,3186,6.191,3187,3.707,3188,4.946,3189,5.42,3190,5.42]],["t/486",[14,1.114,78,1.052,83,0.472,91,0.946,100,0.87,124,2.984,175,2.345,212,1.32,229,3.611,231,3.084,254,1.341,255,3.466,282,1.805,294,3.588,301,1.133,340,2.21,349,1.483,430,2.361,471,4.888,497,2.53,543,4.339,559,3.45,613,4.269,649,3.019,979,1.523,1066,7.127,1079,4.294,1113,4.07,1447,4.343,1638,4.133,2101,5.039,2296,6.585,3191,5.728,3192,7.367,3193,6.723,3194,7.367,3195,6.723,3196,6.723,3197,5.18]],["t/488",[2,2.689,4,1.592,14,0.93,52,2.335,65,2.186,74,1.805,80,1.807,83,0.448,103,0.802,137,1.415,169,1.815,196,1.672,223,3.467,242,2.55,249,1.28,254,1.033,258,1.415,301,1.502,317,1.199,333,1.573,349,1.585,353,2.675,396,1.448,399,2.499,400,3.046,404,4.253,407,2.806,421,2.761,430,1.986,436,1.772,438,2.151,451,2.689,480,1.519,514,3.698,521,2.708,527,4.114,556,1.726,576,3.884,622,2.659,646,2.208,648,2.428,649,3.24,671,2.558,765,3.617,766,4.253,908,2.383,946,2.524,979,2.019,987,4.253,1031,4.914,1044,3.617,1055,2.719,1079,2.603,1125,2.451,1362,4.818,1378,3.884,1407,3.046,1462,6.26,1489,3.137,1620,3.185,1836,4.253,2008,3.408,2125,3.348,2345,5.863,2464,5.181,3198,8.351,3199,5.678,3200,7.876,3201,5.678,3202,7.876,3203,5.678,3204,5.678,3205,7.876,3206,5.181,3207,5.678,3208,5.678,3209,5.678]],["t/490",[14,1.031,59,1.915,62,0.598,66,1.95,83,0.46,90,2.635,97,1.867,100,0.646,103,0.674,104,1.379,107,1.658,109,1.093,137,1.189,139,1.169,140,1.966,145,1.414,165,1.638,182,1.626,210,1.252,212,1.617,223,2.099,225,1.149,234,1.662,239,4.095,261,0.877,263,2.976,282,1.169,294,2.862,311,2.229,333,1.321,336,2.142,340,1.763,353,1.676,402,0.94,409,2.099,411,1.892,469,2.456,477,1.962,521,1.216,545,1.836,568,4.183,623,2.258,629,2.234,643,2.039,646,2.901,649,1.938,672,3.24,682,2.12,686,2.523,691,2.863,821,2.489,867,1.966,883,5.323,898,2.917,912,2.489,944,2.595,979,1.438,1039,1.779,1079,2.186,1085,2.258,1152,3.631,1187,2.164,1378,3.262,1531,2.523,1586,1.915,1638,2.675,1647,8.138,1925,3.038,1926,6.386,1928,7.02,1929,6.386,2101,3.262,2252,2.635,2307,2.917,2321,3.353,2345,4.176,2555,2.186,2832,3.709,3086,3.709,3198,4.077,3210,7.344,3211,3.709,3212,5.948,3213,6.669,3214,7.33,3215,7.719,3216,4.352,3217,4.769,3218,4.769,3219,4.769,3220,4.769,3221,4.077,3222,4.769,3223,4.769,3224,6.957,3225,4.769,3226,4.769,3227,4.769,3228,4.769,3229,4.769,3230,5.649,3231,4.769,3232,4.769,3233,6.957,3234,3.709,3235,4.352,3236,4.769,3237,4.769,3238,4.769,3239,4.352,3240,4.769]],["t/492",[5,3.605,8,3.457,14,0.701,52,2.02,62,0.854,78,0.973,83,0.408,91,0.777,99,3.51,100,0.827,103,1.258,104,2.575,137,1.699,209,1.597,211,3.422,214,2.099,234,2.374,261,1.253,287,2.184,301,1.048,311,2.184,315,1.994,330,5.826,340,1.912,345,2.483,349,1.793,353,2.391,399,2.999,400,3.656,402,1.343,419,1.876,430,1.718,464,3.51,556,2.072,576,4.661,595,3.656,649,1.899,672,3.197,805,4.341,883,4.017,907,3.51,909,3.465,923,6.453,943,2.69,961,5.826,979,1.841,1100,3.949,1155,3.709,1212,6.956,1453,4.341,1724,4.791,2184,4.937,2252,3.764,2289,4.438,2310,6.671,2459,4.937,3210,6.679,3241,6.814,3242,6.814,3243,6.814,3244,6.814,3245,6.814,3246,6.814,3247,5.826]],["t/494",[55,1.91,66,2.336,74,1.91,78,1.19,83,0.382,100,0.774,130,2.725,137,2.077,138,3.375,145,2.47,198,4.913,212,1.493,230,3.083,249,1.878,265,1.409,279,5.858,301,1.281,311,2.67,396,2.125,560,4.084,578,2.55,591,3.466,649,2.817,949,5.858,979,1.723,1428,5.755,1531,4.408,1620,5.672,2187,6.765,3248,8.332,3249,8.332]],["t/496",[14,1.257,52,2.15,67,2.817,74,1.663,83,0.511,91,0.633,100,0.861,106,3.601,214,2.857,231,3.367,242,4.165,254,1.688,288,3.511,294,2.528,339,5.071,349,1.46,350,5.124,433,3.258,474,2.604,545,2.794,646,2.034,649,2.849,672,2.604,979,1.5,1039,2.706,1112,4.963,1144,4.07,1152,3.786,1312,4.165,1428,4.716,2184,5.256,2296,5.101,2297,4.622,2333,5.101,3171,5.641,3210,4.438]],["t/498",[52,2.386,83,0.492,106,4.163,107,1.918,158,3.173,206,4.095,229,3.946,231,2.651,258,2.007,287,2.58,288,3.4,289,4.666,294,2.805,349,1.62,403,3.942,497,2.765,613,4.666,649,2.243,935,3.653,1089,4.319,1152,4.202,1312,4.446,1428,5.688,2319,4.925,3250,7.173,3251,8.051,3252,8.051,3253,8.051,3254,8.051,3255,8.051]],["t/500",[14,1.147,62,0.779,78,1.196,82,1.877,83,0.486,100,0.88,106,3.253,117,2.134,139,1.523,144,2.667,162,1.83,191,2.609,212,1.113,218,2.214,231,2.759,242,3.762,248,6.089,250,1.464,254,1.131,261,1.866,265,1.051,287,1.991,289,4.855,294,3.69,333,1.721,339,3.083,350,5.604,430,1.567,431,2.976,446,4.369,468,2.032,480,1.199,534,2.539,555,5.588,556,1.889,629,2.911,646,2.657,649,1.732,672,3.007,867,2.562,912,3.243,1047,3.601,1089,3.334,1152,5.294,1187,2.819,1312,2.791,1367,3.959,1428,4.26,1623,3.382,1642,5.588,2297,3.959,2318,4.251,2809,6.275,3197,5.891,3211,4.832,3256,8.673,3257,6.214,3258,6.214,3259,6.214,3260,6.214,3261,7.163,3262,6.275,3263,8.378,3264,7.646,3265,6.214]],["t/502",[1,1.39,8,2.678,14,1.042,52,1.625,55,1.256,58,0.834,62,0.964,67,2.984,83,0.251,91,0.671,98,2.323,100,1.022,103,1.086,104,2.222,109,1.256,117,1.883,157,1.869,165,2.64,172,2.128,196,1.615,210,1.439,212,0.982,223,3.383,248,4.614,254,0.998,258,1.367,265,0.927,275,1.757,301,0.843,315,1.605,333,1.518,336,2.462,339,5.209,345,1.998,347,2.487,349,1.103,402,1.749,411,1.491,421,1.39,433,2.462,436,1.711,461,2.24,468,1.793,474,1.968,480,1.057,497,2.64,541,2.183,560,2.687,646,2.697,649,2.473,651,2.344,663,3.524,671,1.781,672,3.186,673,4.069,684,4.903,703,4.263,867,2.26,887,2.54,1039,2.045,1089,2.941,1112,6.929,1144,3.075,1146,3.656,1200,3.232,1367,4.897,1428,4.892,1440,5.757,1591,3.232,1613,2.437,1623,6.234,1677,4.451,1834,2.719,1891,3.959,2021,3.493,2139,3.493,2201,3.972,2345,3.291,2555,2.513,2785,3.972,2800,4.687,3121,4.451,3122,4.687,3171,4.263,3266,5.482,3267,5.482,3268,5.482,3269,5.482,3270,4.687,3271,5.126,3272,5.482,3273,3.232,3274,3.353,3275,4.687]],["t/504",[4,2.399,71,4.145,83,0.505,100,0.795,254,1.557,291,4.052,303,2.504,339,4.244,379,5.045,461,3.496,553,3.026,601,2.982,649,2.384,672,3.071,752,3.166,1144,4.8,1312,3.842,1367,5.451,1428,5.807,1517,5.339,1531,4.527,1834,4.244,1960,7.316,2297,5.451,3197,6.016]],["t/506",[1,1.047,14,0.929,45,3.53,51,1.518,62,0.518,67,1.603,74,2.179,78,0.59,83,0.346,84,3.139,85,2.689,100,0.883,103,0.583,106,2.427,107,1.797,117,2.147,128,2.434,138,1.672,145,1.224,153,1.272,157,2.131,162,1.841,165,2.147,169,0.952,180,1.123,183,2.155,196,1.216,209,1.465,210,1.98,212,0.74,231,1.36,237,2.807,239,3.884,249,0.931,253,3.082,254,0.752,258,1.558,261,0.759,283,1.239,287,1.323,288,2.147,294,2.178,301,0.961,311,2.417,315,1.209,322,1.766,329,2.628,339,3.1,340,0.886,342,1.59,345,1.505,378,2.434,399,1.817,411,2.832,419,1.137,427,2.048,430,1.041,431,1.977,436,1.288,440,2.526,451,1.955,452,1.341,468,1.35,475,4.578,477,2.127,479,2.349,480,0.796,521,2.145,546,4.395,556,1.9,559,1.934,565,2.073,578,1.263,585,2.903,591,1.717,601,1.439,629,1.934,646,1.752,649,3.081,650,1.913,672,3.809,676,2.51,682,1.836,717,1.658,736,2.434,820,2.896,838,2.903,887,1.913,894,1.493,898,5.527,924,2.024,943,1.63,990,4.183,991,4.37,1089,2.215,1125,1.783,1152,2.155,1280,2.824,1339,1.836,1365,3.622,1397,2.353,1447,2.434,1454,2.281,1515,2.576,1531,2.184,1586,2.51,1591,2.434,1623,4.105,1675,3.453,1850,3.093,1941,4.071,1945,5.344,2247,2.073,2252,4.166,2324,4.395,2340,3.211,2556,3.353,2629,3.768,2800,3.53,3086,3.211,3144,8.452,3167,3.53,3210,4.613,3273,2.434,3276,3.768,3277,4.129,3278,4.129,3279,4.129,3280,3.53,3281,4.129,3282,4.129,3283,4.129,3284,4.129,3285,4.129,3286,4.129,3287,4.129,3288,6.25,3289,6.25,3290,7.675,3291,4.129,3292,4.129,3293,3.768,3294,4.129]],["t/508",[4,1.568,6,1.467,14,0.976,51,1.126,52,0.779,55,0.602,56,1.427,59,1.055,62,0.702,63,1.145,65,1.012,66,0.737,74,1.982,78,0.623,83,0.507,91,0.863,98,1.113,100,0.52,106,1.694,107,0.626,109,1,111,1.447,117,2.237,128,1.549,134,2.138,135,3.461,139,1.069,145,2.448,153,1.344,158,1.398,162,1.648,163,1.037,169,2.28,170,1.217,175,1.781,180,1.187,182,0.896,209,1.022,210,2.047,217,1.103,219,1.303,231,2.72,234,1.52,237,3.501,239,1.783,241,1.004,249,1.629,250,1.028,253,4.17,254,1.419,256,0.943,258,1.088,261,0.483,268,1.39,282,1.911,283,0.789,284,0.853,287,0.842,288,1.922,294,1.52,296,1.156,301,0.86,315,0.769,317,0.555,321,1.607,322,1.124,328,1.134,329,0.915,332,1.273,333,0.728,337,1.847,340,0.937,342,2.154,346,2.285,347,2.538,349,1.126,363,3.546,368,1.799,371,2.812,399,1.156,428,1.752,430,1.643,438,4.004,461,2.286,468,1.427,469,1.353,472,1.012,473,2.066,477,1.578,480,0.507,481,0.877,497,0.902,511,0.909,518,1.371,521,2.565,531,1.258,536,2.439,540,0.896,543,2.021,545,2.783,553,0.929,556,3.553,559,1.231,580,1.134,589,2.19,601,1.52,622,1.231,643,1.124,646,2.424,649,2.973,665,1.204,672,0.943,717,2.616,728,4.421,817,2.669,821,1.371,828,1.244,838,1.847,872,2.415,888,2.087,907,1.353,909,1.336,914,2.375,921,1.674,939,1.474,944,0.98,949,1.847,974,1.074,979,1.708,993,1.18,995,4.002,997,1.192,1007,1.752,1035,1.093,1039,1.627,1066,4.605,1071,3.644,1079,1.204,1085,2.066,1117,1.244,1124,1.639,1150,4.345,1152,2.92,1174,2.164,1262,2.447,1272,2.398,1312,1.959,1338,1.607,1348,2.277,1365,1.523,1397,1.498,1516,1.336,1518,3.161,1525,3.393,1531,2.96,1566,2.247,1821,2.133,1941,1.711,1997,2.133,2059,1.498,2289,2.842,2319,3.985,2520,3.542,2576,4.351,3193,2.398,3195,2.398,3196,2.398,3295,2.627,3296,6.514,3297,2.246,3298,2.627,3299,2.627,3300,4.363,3301,2.627,3302,2.627,3303,2.627,3304,3.73,3305,2.627,3306,2.627,3307,2.627,3308,2.627,3309,1.968,3310,2.627]],["t/510",[1,0.892,14,1.218,48,1.167,51,0.708,62,0.693,66,1.913,67,1.366,74,0.807,78,0.502,83,0.483,100,0.718,103,0.781,104,1.597,111,1.167,128,2.075,139,0.862,158,2.477,165,3.202,170,1.631,197,1.783,209,1.295,210,0.924,211,1.767,212,0.99,214,1.084,215,1.209,234,1.926,237,3.772,239,4.301,241,1.344,249,0.793,252,2.432,253,3.809,254,1.242,261,0.647,265,0.595,276,1.974,282,0.862,283,1.056,289,2.039,294,2.377,297,1.837,298,1.477,301,0.541,303,1.03,311,1.128,340,2.001,349,1.112,352,1.916,359,2.377,371,1.519,395,1.862,403,4.045,409,1.549,411,2.764,427,2.741,430,0.887,432,1.79,442,1.888,451,1.667,452,1.143,461,1.438,477,2.921,479,2.077,481,2.581,497,1.209,536,1.534,540,1.2,548,3.1,556,2.074,578,1.691,591,1.464,595,1.888,601,1.226,646,2.993,649,0.981,665,2.533,667,1.813,672,1.983,676,1.413,682,1.565,684,1.944,689,3.599,691,2.113,697,2.474,720,1.143,805,2.242,843,1.974,912,2.884,924,1.725,944,1.313,979,1.598,1039,1.313,1174,1.746,1362,2.153,1367,3.52,1586,1.413,1638,3.827,1642,2.347,1648,2.113,1895,3.009,1994,5.305,2059,3.15,2139,5.939,2175,2.858,2297,2.242,2309,4.297,3187,3.78,3211,4.297,3262,4.139,3273,2.075,3274,4.173,3311,5.526,3312,3.519,3313,7.97,3314,5.526,3315,6.822,3316,5.526,3317,5.526,3318,5.526,3319,3.519,3320,3.519,3321,3.519,3322,3.519,3323,3.519,3324,3.519,3325,3.519,3326,3.519,3327,3.519,3328,3.519,3329,3.519,3330,3.519,3331,3.212,3332,3.519,3333,3.519,3334,4.487,3335,5.526,3336,5.526,3337,6.822,3338,7.729,3339,3.519,3340,3.519,3341,2.636,3342,6.822,3343,6.822,3344,6.822,3345,3.519,3346,5.526,3347,3.519,3348,3.212]],["t/512",[14,1.233,51,1.027,52,1.512,60,1.873,65,1.964,74,1.169,83,0.391,91,0.986,98,2.161,103,1.032,134,2.349,166,2.662,175,2.325,191,2.141,205,2.245,206,2.79,209,1.195,224,2.223,228,2.338,254,0.928,256,1.831,258,1.271,261,1.343,275,1.635,282,1.79,301,0.784,317,1.077,342,2.812,349,1.984,353,1.76,359,1.777,369,2.291,370,2.443,371,4.428,380,1.873,396,2.176,402,1.005,409,2.245,421,1.293,452,3.43,466,3.538,471,3.812,477,2.893,480,0.984,496,4.677,538,4.384,543,3.384,556,1.551,578,2.611,623,2.416,642,2.907,649,2.747,672,2.621,674,2.627,872,3.153,888,1.902,890,4.467,979,1.054,1014,3.348,1035,2.122,1039,1.902,1057,2.907,1125,2.202,1365,2.956,1397,2.907,1477,4.04,1496,2.737,1669,3.696,1690,4.653,1834,2.53,1912,3.322,1923,3.821,2059,4.163,2125,4.306,2126,2.861,2614,5.135,2757,3.821,2765,4.361,2779,5.679,2780,5.47,2781,3.966,2836,3.402,2837,3.696,3349,4.655,3350,4.655,3351,4.361,3352,4.361,3353,3.586,3354,4.142,3355,5.101,3356,4.361]],["t/514",[14,0.684,55,1.524,62,0.834,66,1.864,78,1.4,82,2.008,83,0.305,84,3.338,91,0.765,100,0.617,103,0.939,144,2.116,165,3.367,166,3.47,169,1.532,175,2.116,215,2.283,223,2.926,258,1.657,265,1.124,274,3.459,284,2.159,290,4.77,291,4.15,301,1.022,315,1.946,333,1.841,353,1.602,402,1.932,411,1.808,438,2.519,445,2.898,476,3.729,521,1.695,540,2.267,556,2.021,627,4.235,646,2.457,648,2.843,649,3.16,672,3.145,867,2.74,882,3.338,909,3.38,950,3.048,951,4.674,952,5.169,955,5.684,979,1.812,1151,4.434,1171,3.184,1174,4.347,1200,3.919,1312,2.985,1367,4.235,1428,4.456,1620,3.729,1625,4.979,1648,3.99,1713,4.066,2139,5.583,2206,4.066,2345,3.99,3191,6.814,3211,6.814,3357,5.684,3358,3.672,3359,5.398,3360,6.066,3361,6.648]],["t/516",[48,3.262]],["t/518",[6,2.299,14,1.022,51,1.376,52,3.24,61,3.434,63,2.981,82,2.697,91,0.996,100,0.635,106,2.655,109,1.567,158,2.191,182,2.332,197,2.206,239,2.794,250,1.611,252,3.01,253,2.794,255,2.531,258,1.705,273,3.102,282,1.676,283,2.053,301,1.052,340,1.468,342,2.633,371,2.952,403,2.723,406,2.267,422,2.221,436,2.134,480,1.319,521,2.535,556,2.079,646,1.917,681,4.266,717,2.746,821,3.569,890,5.461,943,3.524,963,3.157,979,2.178,989,4.561,1088,2.77,1155,4.859,1339,3.04,1812,4.808,2206,4.183,2345,4.105,3198,5.847,3362,8.147,3363,6.838]],["t/520",[14,1.103,83,0.329,100,0.855,106,3.948,175,2.283,197,2.314,212,1.285,221,2.594,277,2.113,282,2.256,340,2.183,346,2.268,349,1.444,353,2.219,396,1.829,430,1.809,479,2.696,556,2.799,646,2.011,649,3.092,672,2.574,681,3.953,890,4.388,943,4.236,979,2.102,1039,2.675,1153,6.956,1379,9.791,1843,3.744,2181,4.784,3364,7.173,3365,7.173,3366,7.173,3367,7.173,3368,9.207,3369,7.173,3370,7.173,3371,7.173,3372,7.173,3373,7.173,3374,7.173,3375,7.173,3376,7.173]],["t/522",[1,1.244,6,1.649,14,0.998,52,2.711,55,1.124,57,2.272,62,0.615,77,1.682,78,1.014,83,0.507,113,2.669,139,1.202,158,1.572,182,1.672,196,2.091,197,1.582,206,1.873,209,1.149,210,1.288,212,0.879,214,1.51,254,0.893,256,1.76,258,1.222,262,2.631,282,1.202,283,1.472,287,1.572,300,3.355,340,1.524,353,1.182,396,1.251,399,2.159,407,1.747,419,1.35,428,3.271,430,1.79,452,1.593,479,2.669,480,0.946,481,1.637,518,2.56,521,1.251,595,2.631,601,1.709,629,3.325,646,1.99,649,3.21,667,2.526,672,2.995,674,3.657,676,2.851,681,4.071,682,2.18,683,3.553,733,2.901,821,4.774,908,2.059,935,2.225,946,4.499,979,1.468,982,2.433,987,3.673,1014,2.248,1016,3.271,1118,3.06,1225,3.673,1233,2.463,1234,3.813,1376,2.842,1398,2.751,1438,3.553,1447,4.186,1463,7.113,1675,6.364,1782,2.795,1836,3.673,1837,4.186,2091,3.553,2184,3.553,2185,7.869,2531,4.193,3175,3.448,3176,4.193,3187,5.708,3188,4.475,3206,6.479,3377,8.345,3378,4.904,3379,9.147,3380,4.904,3381,4.475,3382,4.193,3383,4.904,3384,4.904,3385,4.904,3386,4.904,3387,4.904,3388,4.904,3389,4.904,3390,7.099]],["t/524",[14,1.116,51,1.227,62,0.764,83,0.43,100,0.566,103,1.486,109,1.397,116,2.406,117,3.726,119,3.061,134,1.452,137,1.519,158,3.007,212,1.092,241,2.328,261,1.121,288,3.223,295,3.419,296,2.683,297,3.181,301,0.937,332,2.953,345,2.221,353,1.993,359,2.124,395,4.375,403,3.736,436,1.902,477,1.719,480,1.595,497,2.093,521,2.109,545,2.347,601,2.124,643,2.607,646,2.318,649,3.023,672,2.968,674,4.259,681,3.865,682,3.676,689,3.97,700,4.065,828,3.916,946,4.677,963,2.155,979,1.94,1111,4.316,1129,4.639,1493,3.225,2187,4.949,3280,5.211,3313,9.652,3348,5.562,3391,6.095,3392,6.095,3393,6.095,3394,8.269,3395,6.095,3396,6.095,3397,6.095,3398,4.74,3399,5.562]],["t/526",[1,1.107,14,1.193,52,1.932,56,2.131,60,2.393,62,0.547,63,1.903,66,1.224,80,2.075,83,0.424,100,0.405,103,0.617,107,1.553,109,2.305,116,2.573,117,4.084,156,1.941,158,2.088,171,2.488,207,2.781,215,1.499,231,1.437,249,0.984,261,1.199,265,0.738,282,1.07,283,1.31,288,2.679,300,2.986,301,1.002,333,1.209,339,2.165,340,1.857,349,1.741,353,1.052,359,1.521,371,2.814,402,1.284,403,2.595,411,1.773,422,1.418,431,3.121,438,2.469,442,2.342,452,1.418,472,1.681,477,2.837,480,0.842,497,1.499,536,1.903,556,2.371,646,1.827,649,3.116,672,3.92,674,3.357,681,3.458,682,3.467,684,2.411,686,2.309,717,1.753,729,6.668,805,2.781,820,2.023,828,2.067,880,2.376,898,2.67,912,2.278,931,3.27,943,1.723,946,3.467,979,1.347,982,2.165,1026,3.966,1151,2.911,1171,2.091,1230,3.912,1262,3.656,1312,1.96,1515,4.066,1620,2.449,1675,2.411,1724,4.582,2252,3.6,2320,2.911,2325,2.911,2345,2.62,2503,3.27,2646,3.069,3086,5.068,3210,5.661,3274,3.987,3313,6.668,3399,3.984,3400,4.365,3401,4.365,3402,4.365,3403,3.27,3404,3.987,3405,3.984,3406,3.984,3407,4.365,3408,3.27,3409,3.984,3410,4.152,3411,3.984,3412,4.365,3413,4.365,3414,4.582,3415,4.365]],["t/528",[14,1.081,62,0.851,65,2.615,77,1.609,83,0.482,100,0.631,107,1.618,109,1.556,117,3.971,158,2.176,165,2.332,167,3.368,177,3.695,209,1.591,223,2.989,282,1.664,317,1.434,340,1.458,345,2.474,349,1.994,350,3.751,371,3.836,399,2.989,400,3.643,422,2.886,436,2.119,445,2.96,463,9.389,472,2.615,477,2.506,480,1.31,534,3.631,629,3.18,646,1.903,672,4.016,681,2.419,790,3.452,820,3.146,935,3.08,946,3.95,1057,3.87,1230,5.334,1493,3.592,1515,6.556,1865,7.597,1910,4.919,2023,5.086,3405,6.196,3406,6.196,3409,6.196,3416,6.79]],["t/530",[14,1.174,56,2.236,83,0.314,100,0.635,103,0.966,104,1.977,109,1.567,117,2.349,158,2.191,191,2.871,197,2.206,227,2.924,250,2.104,256,3.204,282,1.676,301,1.052,333,1.894,340,2.134,359,2.383,399,3.01,402,1.347,403,3.554,411,1.86,430,1.724,452,2.221,477,2.972,480,1.319,497,3.414,601,2.383,607,3.963,646,1.917,649,2.77,672,3.923,681,3.894,946,3.04,990,4.211,1200,4.032,1339,3.04,1397,3.898,1531,3.618,1671,4.605,1837,4.032,1857,4.561,1874,3.963,2603,5.122,3417,9.07,3418,6.838,3419,6.838,3420,6.838,3421,5.847,3422,6.838,3423,6.838,3424,6.838,3425,6.838]],["t/532",[14,1.119,56,2.013,62,0.772,74,1.41,77,1.972,78,0.879,82,1.859,91,0.823,100,0.938,103,0.869,114,2.736,124,3.82,139,1.508,140,2.537,142,2.429,146,1.616,148,2.736,209,1.442,210,2.476,212,1.103,231,2.741,250,1.961,255,2.277,258,1.534,282,2.726,296,2.709,297,3.212,336,3.738,367,3.921,379,3.628,426,3.17,430,2.378,431,2.947,481,2.055,497,3.886,545,2.37,553,2.943,589,2.409,590,2.851,646,2.644,649,1.715,651,2.632,681,3.876,867,2.537,946,2.736,964,3.09,975,4.105,983,3.84,1033,3.694,1071,4.009,1129,3.452,1339,2.736,1782,3.508,1874,5.855,1941,4.009,1942,4.459,1945,8.064,1948,4.786,2058,5.616,2324,6.631,2338,5.551,3191,4.786,3426,8.323,3427,6.154,3428,6.154,3429,6.154,3430,6.154]],["t/534",[14,1.101,52,1.356,55,1.547,58,0.696,62,0.574,74,1.547,78,0.653,83,0.309,91,0.934,102,2.12,106,2.62,115,2.388,134,1.09,140,2.782,145,2.623,153,1.409,157,1.56,158,2.836,209,1.072,212,0.82,214,2.469,221,2.44,225,1.102,237,2.054,241,1.747,248,4.051,249,1.031,250,1.078,256,1.642,275,1.466,277,1.347,282,2.313,283,1.373,284,1.486,296,2.97,301,1.038,305,2.356,317,0.966,349,1.358,353,2.379,368,1.886,387,2.798,396,1.167,402,0.901,421,2.658,428,3.051,430,2.643,436,1.428,439,2.297,458,2.42,469,2.356,473,2.166,480,0.882,497,3.508,518,2.388,521,1.167,553,1.618,607,3.91,622,3.16,646,2.939,649,1.275,667,2.356,671,2.192,681,3.877,691,2.746,720,1.486,752,1.693,768,4.744,790,2.326,946,2.034,964,2.297,979,1.395,990,3.749,1000,3.051,1036,1.956,1079,2.097,1125,2.913,1171,2.191,1183,2.98,1187,2.075,1246,4.889,1326,1.956,1407,2.454,1444,3.216,1446,3.216,1453,2.914,1489,2.527,1516,2.326,1560,2.914,1668,3.129,1671,3.126,1857,3.051,1874,4.646,1891,3.476,1910,3.314,1971,3.216,1976,3.557,1990,3.714,2001,4.889,2558,6.412,3431,4.575,3432,4.575,3433,5.247,3434,3.714,3435,3.714,3436,4.175,3437,4.575,3438,4.575,3439,8.017,3440,4.575,3441,3.911,3442,5.769,3443,4.575,3444,3.911,3445,4.575,3446,4.175,3447,6.158,3448,4.575,3449,4.575,3450,4.575,3451,2.98,3452,4.575]],["t/536",[1,1.394,14,0.991,56,1.798,62,0.689,65,4.413,77,1.303,78,0.785,80,1.75,83,0.465,91,0.977,100,0.942,145,2.283,158,2.468,169,1.267,197,1.774,211,3.867,227,2.351,234,1.916,249,1.239,255,2.034,282,1.887,287,1.762,288,1.888,317,1.161,340,2.256,380,2.828,402,1.083,405,2.992,430,1.386,452,2.887,469,3.967,479,2.067,480,1.061,497,3.308,553,1.944,607,3.186,623,2.604,646,2.492,649,2.146,655,3.983,672,3.19,681,3.612,702,3.867,717,2.208,733,3.147,935,2.494,959,2.444,979,1.137,990,4.803,1023,3.581,1174,2.727,1187,2.494,1531,2.909,1559,3.503,1671,4.119,1824,2.832,1874,3.186,1891,2.832,1971,3.866,2206,3.363,2307,5.892,2603,4.118,2852,5.69,3358,3.037,3410,3.503,3447,8.112,3451,5.017,3453,3.967,3454,5.498,3455,5.498,3456,5.498,3457,5.498]],["t/538",[1,0.827,14,1.078,51,1.629,52,0.966,58,1.312,62,0.409,66,0.914,71,1.58,77,0.773,78,1.383,82,0.985,83,0.371,91,0.956,100,0.873,101,1.787,108,1.749,109,0.747,111,1.081,112,1.45,137,0.813,139,1.59,150,3.719,153,1.004,170,2.41,175,1.656,180,1.765,182,1.112,196,0.96,197,1.052,204,1.637,210,0.856,211,1.637,212,0.932,214,1.004,225,1.253,227,1.394,234,1.136,237,1.464,248,1.957,254,1.473,255,1.207,256,1.17,261,0.957,262,1.749,274,1.287,277,0.96,282,0.799,283,0.979,287,1.045,303,0.954,315,2.751,317,1.822,333,0.903,340,2.017,347,2.36,349,0.656,353,2.444,367,2.077,368,2.675,379,1.922,387,3.97,392,1.922,402,0.642,419,1.432,421,2.618,439,3.259,458,1.725,469,2.679,480,1.252,481,1.737,496,1.637,511,1.128,518,1.702,520,3.245,536,1.421,541,2.071,545,2.003,553,1.153,568,2.41,601,1.136,622,4.835,646,0.914,663,2.975,667,1.679,669,2.944,671,2.801,673,2.975,702,1.637,704,1.679,733,3.026,752,1.925,876,2.124,908,1.369,959,2.312,974,1.332,979,2.222,990,1.381,993,1.464,998,2.124,1000,2.175,1021,2.204,1023,2.124,1028,1.995,1035,1.356,1042,2.874,1085,2.463,1096,2.442,1108,2.362,1118,2.034,1246,6.249,1268,2.976,1345,2.944,1368,5.077,1370,2.463,1376,1.89,1380,3.182,1397,1.859,1407,2.791,1489,3.585,1516,3.766,1578,2.831,1586,2.089,1663,2.442,1690,3.314,1782,1.859,1812,2.293,1820,2.442,1824,3.815,1891,2.679,2157,2.976,2307,1.995,2353,5.548,2436,2.965,2558,3.769,2694,4.224,2815,3.769,2852,1.658,3175,2.293,3271,2.175,3273,1.922,3353,2.293,3358,1.801,3433,4.045,3434,2.647,3435,2.647,3441,4.447,3442,6.332,3444,6.332,3458,3.261,3459,2.788,3460,2.788,3461,2.788,3462,5.548,3463,2.788,3464,2.788,3465,2.976,3466,5.922,3467,3.261,3468,5.202,3469,5.202,3470,3.261,3471,3.261,3472,2.976,3473,3.261,3474,2.976,3475,2.535,3476,4.447,3477,3.261,3478,2.976,3479,3.261]],["t/540",[14,0.708,51,1.386,52,2.658,55,1.579,62,0.864,66,1.931,80,2.192,83,0.457,100,0.64,103,0.973,182,2.349,191,3.765,214,2.121,217,2.891,234,2.4,241,2.631,311,2.207,331,6.975,364,5.668,394,3.644,407,2.454,431,3.299,469,3.548,479,2.589,521,2.288,589,2.696,598,4.594,622,4.201,632,4.62,646,1.931,681,4.177,733,3.665,790,4.561,821,5.206,890,4.213,924,3.376,943,2.719,967,6.286,971,6.286,979,2.184,996,5.889,1118,4.297,1278,4.843,1821,5.593,1835,4.843,1837,4.061,2018,5.842,2184,4.99,3175,4.843,3186,5.593,3187,7.495,3362,6.286,3480,6.286]],["t/542",[56,2.486,65,3.679,78,1.364,83,0.518,91,0.956,100,0.97,112,3.379,117,3.281,144,2.42,145,2.253,153,2.341,158,2.436,217,3.191,254,1.384,273,4.334,277,2.239,288,2.611,305,3.915,310,5.07,403,3.026,556,2.311,559,3.56,565,3.817,566,3.251,619,5.345,649,2.118,979,2.335,990,3.22,1111,3.967,1339,3.379,1824,6.094,2852,4.858,3191,5.911]],["t/544",[4,1.232,14,1.18,51,1.747,52,1.303,56,2.142,74,2.127,78,0.935,83,0.398,91,1.019,100,0.608,109,1.795,117,3.188,134,2.319,150,2.207,158,2.099,169,1.013,202,4.087,234,1.532,249,0.991,261,0.809,268,2.325,282,2.275,283,1.319,284,2.128,301,1.007,315,1.286,317,1.384,329,2.283,337,3.09,340,1.406,349,1.576,353,1.059,361,4.011,368,2.701,396,1.671,402,1.291,405,2.392,419,2.156,420,1.994,421,1.115,430,1.652,436,1.372,438,1.665,452,2.544,472,1.693,477,2.618,480,1.674,521,1.671,556,1.336,622,2.059,627,2.8,632,2.264,642,2.505,646,1.836,649,1.825,669,1.994,672,1.578,674,2.264,681,1.566,686,2.325,691,2.638,720,1.428,820,2.037,872,1.898,943,1.735,944,2.443,946,3.481,974,1.796,979,1.794,1014,3.59,1021,1.862,1231,2.742,1448,2.863,1475,3.292,1477,2.081,1533,1.954,1534,2.264,1572,3.007,1623,2.392,1638,2.466,1675,5.376,1824,3.374,1951,3.09,2208,3.758,2555,2.015,2774,3.569,2783,2.931,2789,5.601,2796,3.569,2836,4.369,2837,3.185,3274,2.689,3280,3.758,3382,3.758,3404,2.689,3474,4.011,3481,4.395,3482,4.011,3483,4.395,3484,4.011,3485,4.011,3486,4.011,3487,4.395,3488,4.011,3489,4.395,3490,4.395,3491,4.395,3492,4.395,3493,4.395,3494,4.011,3495,4.395,3496,4.011,3497,4.395,3498,4.395,3499,4.395,3500,4.395,3501,4.395,3502,6.551,3503,4.395,3504,4.395,3505,4.395,3506,4.011,3507,4.011,3508,4.011,3509,4.395,3510,6.551,3511,4.395,3512,4.395,3513,4.395,3514,4.395,3515,4.395,3516,4.395,3517,4.395,3518,4.395,3519,3.292]],["t/546",[56,2.824,65,3.325,83,0.507,84,4.336,100,0.959,103,1.459,117,2.965,172,3.352,225,2.08,282,2.116,284,2.804,301,1.589,402,2.035,541,3.437,672,3.099,681,3.076,902,4.044,979,2.135,990,3.658,1155,4.699,2852,4.39]],["t/548",[48,3.262]],["t/550",[52,1.553,56,1.714,58,0.798,62,1.086,64,2.339,65,2.018,66,1.469,77,2.052,78,1.52,83,0.432,90,2.895,91,1.038,100,0.691,106,2.035,137,1.306,139,1.825,148,2.33,158,1.679,165,1.8,166,2.735,187,3.414,196,1.544,210,1.376,212,0.939,214,1.614,227,2.241,255,2.755,258,1.306,265,1.259,271,3.206,273,2.378,274,2.069,296,2.307,334,2.161,340,1.125,353,2.272,403,2.087,411,1.426,421,2.391,430,1.321,480,1.011,497,2.557,553,1.853,569,2.104,601,1.826,607,3.037,622,2.455,646,1.469,671,1.702,672,2.672,673,2.403,702,2.632,733,3.042,935,3.378,963,1.853,979,1.539,990,3.669,1125,2.263,1155,2.853,1174,2.6,1275,4.481,1326,4.259,1407,5.711,1447,3.09,1462,5.106,1476,3.339,1939,6.832,2307,6.654,2436,2.987,2599,4.783,3133,4.783,3187,3.585,3358,6.114,3359,4.255,3451,5.641,3453,5.701,3520,5.241,3521,5.241,3522,4.783,3523,5.241,3524,4.481,3525,5.241,3526,4.783]],["t/552",[14,1.03,48,3.092,50,3.613,51,1.211,56,1.968,58,0.916,64,2.575,82,1.818,83,0.496,91,0.965,100,0.559,103,0.85,166,3.141,191,2.526,196,1.773,212,1.078,254,1.095,261,1.107,294,2.097,296,2.649,328,3.539,336,2.703,349,1.211,353,2.412,368,4.127,406,1.996,411,2.723,421,2.364,427,2.985,479,2.262,497,2.067,553,2.128,591,2.503,646,2.298,671,3.029,752,2.227,912,3.141,974,2.459,979,1.244,990,2.55,1036,2.574,1125,4.025,1147,4.508,1174,2.985,1326,4.936,1407,4.398,1462,6.176,1544,4.36,1586,4.34,1620,3.376,1638,4.599,2059,3.43,2125,3.548,2307,3.681,2321,5.764,2436,4.673,2742,4.887,3273,5.497,3358,4.529,3451,3.92,3453,3.1,3522,5.492,3527,7.481,3528,4.887]],["t/554",[1,1.551,14,1.036,62,0.767,66,1.714,67,2.374,78,0.873,83,0.38,91,0.82,97,2.394,100,0.936,102,2.833,103,1.423,107,2.24,134,2.692,137,1.524,162,1.801,163,2.414,169,1.409,182,2.085,210,1.605,214,1.883,236,2.477,241,2.336,249,1.379,254,1.113,256,2.195,261,1.524,276,3.43,282,2.031,294,2.131,303,1.79,311,1.96,328,2.64,352,4.511,368,3.416,383,3.881,394,3.235,468,2.71,481,2.042,545,3.191,601,2.131,607,4.803,613,3.544,654,3.378,704,3.15,733,3.386,907,3.15,912,4.325,979,1.713,997,2.774,1026,4.78,1129,3.43,1166,3.983,1312,4.221,1326,2.615,1407,4.446,1462,5.542,1686,8.19,1687,6.71,3175,4.299,3187,4.183,3453,5.72,3527,5.58,3528,4.965,3529,6.115,3530,6.115]],["t/556",[14,0.713,58,1.371,62,0.87,64,2.179,78,0.991,83,0.459,91,1,100,0.644,169,1.599,196,2.043,197,2.238,210,1.822,249,1.564,255,2.567,258,1.729,273,3.148,282,1.7,317,1.465,353,1.672,380,4.032,402,1.367,411,1.887,421,2.538,430,1.749,452,3.656,580,2.995,607,4.021,635,3.323,646,2.526,669,4.088,821,3.621,946,4.45,979,2.069,993,3.116,1035,2.886,1036,3.854,1039,2.588,1051,5.395,1125,3.89,1373,7.037,2021,4.42,2436,3.955,2837,6.529,3187,4.746,3358,3.833,3453,5.457,3531,6.938,3532,6.938]],["t/558",[14,1.086,51,1.385,58,1.239,62,1.124,63,2.048,64,2.816,78,0.983,82,2.079,83,0.373,91,1.023,100,0.436,106,2.672,109,1.577,119,3.456,137,1.171,139,1.151,140,2.837,144,1.496,150,3.456,204,4.791,209,1.101,225,1.132,241,2.629,254,0.855,255,4.053,258,1.171,265,0.795,296,3.029,301,0.723,340,1.009,353,1.658,380,3.504,396,1.198,411,1.872,421,2.065,431,2.25,436,2.147,452,3.648,480,0.906,534,3.327,540,2.347,548,3.861,569,1.887,646,2.795,669,3.694,821,2.452,842,2.723,843,2.636,872,2.028,876,4.483,888,2.567,946,3.62,959,2.089,979,1.853,986,3.519,990,1.991,1014,2.154,1036,3.482,1089,2.521,1101,3.519,1125,3.515,1326,2.943,1368,2.636,1638,2.636,1721,3.214,1812,3.304,1939,3.404,2167,4.017,2321,3.304,2436,3.923,2587,5.9,2832,3.654,2836,3.134,2837,7.223,3273,2.77,3354,6.612,3358,4.498,3453,5.641,3533,8.707,3534,6.882,3535,6.882,3536,6.882,3537,8.143]],["t/560",[1,1.651,14,0.889,78,0.93,83,0.299,162,1.918,209,1.526,340,1.398,371,2.811,411,2.64,422,2.115,452,2.115,477,3.119,591,2.709,632,4.452,649,1.814,702,3.27,720,2.115,898,5.288,1031,4.063,1085,3.084,1117,3.084,1196,6.263,1262,3.653,1326,3.697,1431,5.287,1586,2.615,1635,6.722,2101,7.071,2252,5.71,2307,5.288,2436,6.731,3144,7.019,3421,5.567,3538,10.337,3539,10.337,3540,10.849,3541,11.889,3542,6.511,3543,4.718,3544,5.567,3545,5.287]],["t/562",[7,2.678,58,1.804,62,0.8,64,2.004,78,0.911,82,1.928,83,0.44,91,0.557,100,0.593,138,2.584,145,1.891,150,3.204,182,2.176,204,3.204,255,2.361,261,1.569,265,1.442,294,2.223,303,1.868,356,4.962,368,2.63,369,2.865,411,2.789,421,1.618,477,1.8,578,1.953,591,2.654,606,3.245,669,2.895,671,2.073,720,2.073,799,4.623,836,3.762,872,2.755,884,5.181,917,2.956,979,1.319,1031,3.981,1102,4.962,1125,5.081,1129,3.58,1196,4.623,1326,3.647,1496,3.423,1665,5.181,1850,4.779,2247,3.204,2307,3.903,2436,6.708,3121,8.927,3270,9.4,3546,5.181,3547,6.381,3548,6.381,3549,6.381,3550,6.381,3551,6.381]],["t/564",[14,1.207,62,1.13,64,3.144,83,0.503,140,2.86,172,2.694,187,4.519,210,1.822,254,1.263,284,2.254,288,2.383,301,1.067,315,2.031,392,4.09,409,3.967,411,3.061,514,4.519,518,3.621,632,3.574,646,2.526,649,1.933,790,3.528,820,3.215,924,3.4,979,2.069,1036,2.967,1118,4.329,1151,4.627,1195,6.878,1196,5.027,1312,3.116,1326,2.967,1586,2.786,1832,5.395,2101,4.746,3358,5.529,3453,3.574,3543,6.529,3545,7.317,3552,9.134,3553,9.134,3554,8.224,3555,6.331]],["t/566",[2,2.545,6,1.807,14,1.126,64,1.688,83,0.532,91,0.469,100,0.704,103,1.474,104,1.553,137,1.34,138,2.177,140,3.62,144,2.413,165,1.846,196,2.232,209,1.259,212,0.963,225,1.295,232,3.778,256,1.929,261,1.615,265,0.909,284,1.746,289,3.114,290,2.925,291,2.545,297,2.805,301,0.826,311,3.343,315,1.573,322,2.298,359,1.873,383,4.112,392,3.168,399,2.365,411,2.388,458,2.843,476,3.015,477,2.476,531,2.574,578,1.644,589,2.104,591,2.235,646,1.506,676,3.043,765,3.424,843,3.015,847,4.363,848,4.904,916,6.154,943,2.121,959,2.389,979,1.111,986,5.677,987,5.677,1036,3.755,1148,4.636,1195,5.271,1312,3.403,1326,3.241,1367,6.075,1376,4.392,1398,3.015,1586,4.47,1638,5.853,1642,3.584,1713,3.287,1832,4.179,1835,3.778,1850,4.025,2008,3.226,2018,4.937,2247,2.698,2321,5.329,2436,3.063,3180,4.904,3273,3.168,3358,5.764,3543,3.894,3545,4.363,3552,4.904,3553,4.904,3554,4.904,3556,4.595,3557,4.904]],["t/568",[6,1.887,14,1.116,51,1.129,62,1.127,64,2.823,65,2.161,66,2.519,67,2.178,71,2.718,74,1.79,78,0.801,83,0.446,100,1.086,109,1.286,137,1.947,153,1.728,176,3.655,198,4.606,212,1.4,231,1.848,249,1.761,258,1.399,265,1.321,277,1.653,288,1.927,294,1.955,297,2.929,301,1.201,311,2.503,342,2.161,349,1.129,402,1.539,410,2.234,411,1.526,430,1.97,438,2.126,468,1.835,497,2.683,521,1.431,556,1.706,635,2.687,646,2.519,649,2.504,672,2.804,704,2.89,746,4.779,752,2.076,912,2.929,979,1.16,1036,2.4,1085,4.256,1113,3.1,1152,2.929,1245,2.89,1326,2.4,1365,4.527,1367,4.977,1566,4.024,1586,4.358,1613,2.494,1638,3.148,2184,4.065,2247,4.88,2248,3.945,2321,3.945,2338,5.21,2414,3.945,2587,6.511,3210,5.944,3274,3.432,3358,3.1,3404,3.432,3408,4.203,3478,5.12,3558,5.611,3559,5.611,3560,5.611,3561,4.797,3562,5.611,3563,5.12,3564,5.611,3565,5.611,3566,5.611]],["t/570",[7,3.929,14,1.15,51,1.727,62,0.923,65,2.834,77,1.222,78,1.547,82,2.223,83,0.429,91,1.047,100,0.984,109,2.359,112,2.292,116,4.182,130,1.686,174,3.527,191,2.164,196,2.168,197,1.663,210,1.354,212,0.924,214,1.588,237,2.315,249,1.659,254,1.34,258,1.285,265,1.244,276,2.892,282,1.804,287,1.652,301,1.132,328,3.177,359,1.797,368,2.126,411,2.002,436,1.609,441,6.034,442,3.948,480,1.42,497,2.948,622,4.636,642,2.939,663,2.364,720,1.675,776,6.716,912,2.691,964,2.589,1005,2.848,1054,6.243,1325,7.832,1407,3.948,1462,5.519,1499,7.6,1503,8.003,1588,3.154,3273,6.246,3451,3.358,3524,9.261,3526,4.705,3567,4.705,3568,5.156,3569,7.36,3570,5.156,3571,5.156,3572,4.705]],["t/572",[14,1.224,56,1.788,60,2.007,62,0.962,64,2.409,65,2.105,74,1.758,82,1.651,83,0.441,91,0.773,102,2.533,103,1.252,109,1.253,134,1.302,139,1.34,162,1.61,170,3.554,196,2.98,197,1.764,209,1.281,212,1.588,231,2.526,237,3.445,241,2.088,256,1.962,258,1.912,271,3.344,282,1.88,298,2.295,301,0.841,303,1.6,329,1.905,345,2.795,349,1.1,371,3.311,387,5.877,431,2.618,436,1.706,452,2.492,464,2.816,474,3.632,477,1.542,556,2.332,643,2.338,646,2.941,649,1.523,672,1.962,909,2.78,963,1.933,1036,2.338,1097,4.692,1129,3.067,1155,2.975,1271,3.96,1313,8.551,1355,3.739,1586,2.195,1613,2.43,1689,7.601,2023,4.094,2206,3.344,2253,4.996,2307,3.344,2360,4.988,2750,4.674,3453,5.549,3573,7.67,3574,5.466,3575,5.466,3576,5.466,3577,8.087,3578,7.67,3579,7,3580,7,3581,5.466,3582,5.466,3583,5.466]],["t/574",[14,1.27,51,0.647,52,0.953,58,1.305,62,0.403,64,2.525,74,0.737,78,0.918,80,1.024,82,0.972,83,0.429,91,0.945,100,0.97,103,0.454,109,0.737,134,1.532,137,1.283,157,1.097,162,1.894,169,1.186,175,1.638,182,1.755,191,1.35,202,3.21,206,2.456,209,1.206,210,1.351,214,0.991,218,1.146,221,1.861,225,0.775,228,2.359,243,2.145,249,0.725,254,0.585,256,1.154,258,1.283,261,1.183,265,1.243,275,2.061,282,0.788,283,2.206,286,1.416,301,0.989,311,2.576,317,1.087,340,0.691,342,1.239,343,2.612,346,1.017,349,1.726,353,1.24,368,1.326,369,2.311,370,2.464,371,4.163,380,1.181,402,1.014,421,0.816,431,1.54,433,1.444,452,2.924,471,1.679,473,1.523,477,2.073,480,0.62,481,1.074,496,4.305,521,1.874,534,1.314,538,3.86,543,2.98,557,1.615,559,1.507,578,2.46,580,1.389,623,1.523,642,1.833,643,1.376,646,0.902,649,2.048,705,1.896,752,1.904,828,1.523,842,1.864,872,2.776,888,1.2,890,1.968,908,1.35,921,2.049,943,1.27,979,1.33,1014,2.948,1035,1.338,1036,1.376,1039,1.919,1079,1.475,1085,3.045,1113,1.777,1122,2.409,1125,1.389,1212,2.049,1365,2.982,1380,1.968,1447,1.896,1454,2.843,1477,5.014,1496,1.726,1566,3.312,1669,2.331,1686,2.501,1687,2.049,1690,3.278,1901,5.027,1912,4.189,1923,2.409,2059,2.933,2125,1.896,2587,5.325,2614,2.262,2710,2.262,2765,2.75,2766,2.75,2774,2.612,2779,2.501,2780,2.409,2836,2.145,2837,2.331,3349,2.935,3350,4.696,3351,5.498,3352,2.75,3353,2.262,3354,2.612,3356,2.75,3453,3.785,3533,2.935,3563,2.935,3584,3.217,3585,3.217,3586,3.217,3587,3.217,3588,3.217,3589,7.349,3590,3.217,3591,3.217,3592,3.217,3593,3.217,3594,5.146,3595,3.217,3596,3.217,3597,3.217,3598,3.217,3599,2.75,3600,3.217,3601,2.262,3602,2.612,3603,2.935]],["t/576",[78,1.227,100,0.798,140,3.543,165,2.952,209,2.014,284,2.792,287,2.754,301,1.584,311,2.754,402,2.03,427,4.263,462,5.16,481,2.87,574,6.684,578,2.63,591,3.575,676,3.451,1245,4.427,2206,6.748,2555,3.94,3358,5.691,3453,5.306,3604,7.844]],["t/578",[48,3.262]],["t/580",[52,1.579,62,1.305,67,2.069,77,1.263,78,0.761,83,0.49,91,0.908,100,0.882,103,0.753,121,3.471,124,2.158,127,3.554,129,2.523,137,1.328,153,1.641,158,1.708,182,1.817,210,1.978,212,1.35,224,2.323,225,1.284,227,2.279,229,2.612,239,2.177,249,1.201,250,1.256,254,0.97,255,1.972,257,2.71,258,1.328,265,0.901,273,3.965,275,1.708,294,3.045,301,0.82,307,3.088,339,2.643,340,1.617,345,1.942,349,1.073,402,1.05,403,2.122,419,2.074,421,1.911,422,2.839,430,1.344,440,3.26,461,2.177,462,3.199,468,1.743,480,1.028,521,2.229,553,1.884,557,2.676,603,3.26,607,3.088,629,2.496,646,1.494,649,2.435,650,3.491,651,2.279,671,1.731,672,1.913,673,2.443,717,2.14,720,1.731,912,2.781,935,2.418,963,1.884,982,3.737,1144,4.227,1152,4.561,1155,2.9,1200,3.142,1399,3.645,1476,3.395,1586,2.14,1628,5.808,1643,4.327,1693,4.556,1938,8.436,2555,5.164,3187,3.645,3358,4.828,3453,3.881,3605,5.329,3606,4.327,3607,5.329,3608,5.329,3609,5.329,3610,5.329,3611,5.329,3612,5.329,3613,5.329,3614,5.329,3615,5.329,3616,5.329,3617,5.329,3618,5.329,3619,5.329,3620,4.863]],["t/582",[55,1.62,59,2.837,62,0.886,78,1.009,83,0.418,91,0.617,134,1.684,137,1.761,140,2.913,197,2.28,210,2.394,212,1.266,225,1.703,254,1.286,258,1.761,275,2.922,282,1.732,353,1.703,371,3.937,402,1.392,430,2.299,438,2.678,466,3.423,553,2.499,556,2.148,568,5.24,646,2.556,649,1.969,888,2.635,912,4.76,979,1.461,1171,3.384,1404,7.405,1447,4.166,1628,5.285,1926,7.091,1928,7.091,1929,7.091,2319,4.322,2555,4.629,2594,6.449,3210,6.526,3212,6.042,3213,7.405,3214,7.405,3215,6.042,3221,6.042,3274,4.322,3403,5.293,3404,4.322,3621,7.066,3622,7.066,3623,7.066]],["t/584",[4,1.215,6,2.18,14,0.949,51,1.563,52,1.285,55,0.994,58,1.182,60,1.592,62,1.157,74,2.219,77,2.294,78,1.109,83,0.395,91,0.876,100,1.047,101,1.489,103,0.916,104,1.253,126,2.762,137,1.616,138,1.756,139,1.062,144,1.38,176,2.824,179,1.683,180,2.73,187,2.824,210,1.138,211,2.177,212,1.392,214,1.335,215,1.489,225,1.045,227,2.773,232,5.461,240,3.048,249,0.977,252,1.908,254,0.789,255,1.604,258,1.081,265,1.313,282,1.062,305,2.233,311,1.389,340,1.667,379,2.556,402,1.278,411,1.764,419,1.785,422,1.408,428,2.891,430,2.531,438,2.943,454,2.652,461,4.311,462,2.602,468,2.12,478,1.458,480,1.251,521,1.654,536,1.89,568,3.004,589,1.697,601,1.511,646,1.818,648,1.854,649,1.208,650,3.004,651,3.321,720,1.408,820,2.009,908,1.82,912,2.263,928,3.757,935,2.942,964,2.177,979,0.896,982,2.151,992,2.125,1026,2.205,1033,3.892,1085,3.678,1114,2.705,1140,3.247,1184,3.52,1339,1.927,1428,2.205,1455,3.52,1535,3.247,1592,3.141,1628,2.512,1648,2.602,1659,2.471,1660,3.707,1782,2.471,1834,5.119,2280,2.966,2368,3.52,2511,3.707,2555,4.23,2785,3.141,3210,2.652,3403,3.247,3453,2.233,3601,3.048,3624,4.335,3625,4.335,3626,4.335,3627,4.335,3628,4.335,3629,4.335,3630,3.956,3631,4.335,3632,5.818,3633,4.335,3634,5.265,3635,4.335,3636,3.956,3637,4.335,3638,4.335,3639,4.335,3640,4.335,3641,4.335,3642,4.335,3643,4.335,3644,4.335,3645,4.335,3646,4.335,3647,4.335,3648,4.335,3649,4.335,3650,3.956,3651,4.335,3652,4.335,3653,3.707,3654,4.335]],["t/586",[14,0.974,58,1.442,62,1.115,64,1.451,66,1.295,67,1.793,78,0.659,80,1.47,82,1.395,83,0.434,91,0.704,100,0.976,103,0.96,104,1.335,109,1.059,116,1.823,157,1.575,171,3.874,197,1.49,209,1.592,211,2.319,212,0.828,217,1.939,239,1.887,241,2.596,250,2.095,258,1.151,261,1.25,275,1.48,277,1.36,284,1.5,301,1.367,305,2.379,315,2.773,328,2.934,329,2.368,332,2.238,336,2.074,339,2.291,340,1.731,347,3.658,349,0.93,350,3.754,359,1.61,393,2.633,396,1.178,398,1.658,407,1.645,411,1.256,419,1.272,420,2.096,421,2.045,430,1.713,436,1.441,452,1.5,472,1.779,474,3.557,480,0.891,497,2.334,511,1.598,518,3.547,526,2.773,607,2.677,646,2.492,650,4.591,669,3.083,686,2.444,720,1.5,872,1.994,963,2.403,972,3.009,979,2.337,990,1.957,1036,1.975,1077,3.009,1125,3.838,1129,2.591,1144,4.988,1299,3.009,1326,2.906,1339,2.053,1376,2.677,1531,2.444,1613,3.021,1627,5.81,1628,2.677,1867,7.693,1938,7.422,2001,4.924,2206,2.825,2234,3.75,2555,4.076,2826,3.949,3453,4.153,3655,8.891,3656,8.891,3657,4.619,3658,4.619,3659,4.619,3660,4.619,3661,4.619,3662,4.619,3663,4.619,3664,6.202,3665,4.619,3666,5.669,3667,4.619,3668,3.248,3669,4.619]],["t/588",[7,1.711,14,0.859,55,0.934,58,1.439,60,2.273,62,0.938,64,1.944,66,1.143,74,0.934,77,0.966,78,1.193,83,0.451,87,3.943,91,0.784,100,0.998,103,0.576,104,1.178,128,2.403,134,0.971,137,1.865,139,0.999,144,1.297,145,1.834,146,1.625,150,5.086,162,1.201,165,1.4,170,1.889,176,2.655,180,1.684,196,2.461,206,1.557,209,2.373,214,1.255,215,1.4,223,1.794,225,0.982,227,1.743,231,1.342,236,1.651,239,3.057,241,1.557,242,1.83,249,0.919,250,0.96,258,1.016,261,0.75,265,1.047,275,1.983,277,1.823,301,0.627,317,0.861,328,1.76,340,1.606,345,2.727,346,1.289,348,2.493,353,1.491,359,1.42,375,2.953,396,1.04,399,1.794,402,1.474,407,2.205,419,2.06,430,1.561,452,1.324,461,2.529,468,1.333,469,2.099,474,1.463,477,1.746,480,0.786,497,2.126,518,3.23,520,2.543,521,1.04,536,3.642,545,1.57,570,3.17,578,1.247,622,1.909,645,2.323,646,1.735,649,1.136,650,3.871,671,1.324,672,2.999,673,1.869,704,2.099,752,1.508,790,3.147,834,2.287,842,2.362,872,1.76,907,2.099,912,2.127,934,3.23,935,1.849,944,1.52,963,1.441,979,2.032,1026,2.073,1042,2.252,1043,2.493,1079,1.869,1113,2.252,1146,2.718,1169,2.447,1189,2.597,1200,2.403,1299,4.874,1428,2.073,1535,3.053,1613,1.812,1614,3.17,1628,2.362,1651,3.053,1690,5.724,1724,2.866,1741,3.053,1820,3.053,1834,4.456,1846,3.053,1857,2.718,1938,5.605,2008,2.447,2126,2.287,2163,3.053,2181,2.718,2319,3.786,2333,2.866,2382,3.053,2555,3.83,2646,2.866,3250,2.953,3414,6.317,3670,4.076,3671,6.189,3672,6.189,3673,6.189,3674,6.189,3675,4.076,3676,6.189,3677,3.72,3678,4.636,3679,4.076,3680,4.076,3681,3.485,3682,2.655,3683,4.076,3684,4.076,3685,3.72]],["t/590",[14,1.154,64,2.156,66,1.924,77,1.626,78,0.98,83,0.502,91,0.921,109,1.573,116,2.709,158,2.867,197,2.214,206,4.03,221,2.482,236,2.78,271,4.198,275,2.199,384,4.046,403,2.732,430,1.73,477,1.936,646,1.924,665,4.102,686,4.734,797,4.825,880,4.87,935,3.114,944,2.56,979,1.419,1024,3.18,1036,2.935,1080,4.695,1155,4.87,1225,5.141,1233,3.446,1398,3.85,1511,6.121,1586,4.237,1915,4.825,2139,4.372,2298,4.825,2555,5.372,3273,4.046,3358,3.791,3606,5.572,3686,6.863,3687,6.863,3688,6.863,3689,4.577,3690,6.263,3691,6.863,3692,6.863,3693,6.863,3694,6.863]],["t/592",[4,2.429,14,0.891,58,1.318,66,1.832,83,0.526,137,1.629,158,2.094,191,2.743,212,1.552,229,5.279,250,2.041,256,2.345,261,1.202,275,3.115,336,2.934,353,2.342,376,2.848,430,1.647,452,2.122,477,2.919,481,2.182,601,2.277,646,2.429,651,2.794,672,3.11,717,2.624,720,2.122,867,2.694,1024,4.014,1231,4.077,1511,4.47,1623,6.025,1704,5.586,1891,3.365,1915,4.594,1994,5.081,2320,4.358,2345,6.645,2555,5.373,3690,7.906,3695,6.534,3696,8.664,3697,6.534,3698,6.534,3699,6.534,3700,6.534,3701,6.534,3702,6.534,3703,6.534,3704,6.534,3705,6.534,3706,6.534]],["t/594",[1,1.293,14,1.111,78,1.218,83,0.335,91,0.943,97,1.997,100,0.474,107,1.215,137,1.271,145,1.512,158,1.635,162,1.502,165,1.752,166,2.662,169,1.683,197,1.646,210,1.339,212,0.914,218,1.817,224,3.183,237,2.291,249,1.15,254,0.928,255,1.888,258,1.271,271,3.12,274,2.014,347,3.313,349,1.027,353,1.229,387,5.219,395,2.699,403,2.031,405,2.776,422,3.668,468,1.668,470,2.066,477,2.979,540,3.361,556,1.551,601,1.777,622,2.389,632,2.627,649,2.035,686,2.699,707,2.907,770,8.575,898,3.12,956,2.471,979,1.054,990,3.094,1007,4.871,1036,2.181,1117,2.416,1155,2.776,1326,3.648,1348,2.662,1511,4.996,1539,3.062,1628,2.956,1915,5.135,2553,4.142,2555,5.115,3153,4.142,3210,5.698,3273,4.306,3304,9.235,3358,5.834,3359,4.142,3451,3.322,3543,3.696,3606,4.142,3707,4.655,3708,5.101,3709,5.101,3710,7.785,3711,5.101,3712,5.101,3713,5.101,3714,7.303,3715,9.314,3716,5.101,3717,7.303,3718,7.303,3719,7.303,3720,7.303,3721,5.101,3722,7.303,3723,5.101]],["t/596",[14,1.066,58,1.616,62,0.876,77,1.654,83,0.46,89,5.646,90,2.648,91,0.719,100,0.445,103,0.677,130,1.568,134,2.712,137,1.741,139,1.175,163,1.892,195,3.59,196,2.057,197,1.546,209,1.123,212,1.251,218,1.707,224,2.089,227,2.05,229,3.422,234,1.67,250,1.129,254,0.872,256,2.506,258,1.195,262,4.419,274,1.892,275,2.238,289,4.773,296,2.11,301,0.737,315,1.403,328,2.069,333,1.328,345,1.747,353,1.155,369,3.136,371,3.014,399,3.625,400,2.571,402,0.944,419,2.268,428,3.197,430,1.761,438,1.816,446,3.37,451,2.27,461,1.958,468,1.568,473,2.27,497,1.646,518,2.502,534,1.958,541,1.908,555,3.197,556,3.536,589,1.876,646,1.957,704,2.469,720,1.557,733,1.958,790,2.437,898,2.932,907,2.469,908,2.012,921,3.054,924,2.349,935,3.168,941,2.349,963,1.695,972,3.122,979,1.871,993,3.136,1021,2.031,1062,3.37,1108,3.473,1118,5.139,1155,2.609,1187,3.168,1280,3.279,1299,3.122,1326,2.05,1348,2.502,1398,2.689,1444,4.909,1511,5.634,1526,3.892,1775,3.892,1834,2.378,2040,4.098,2163,3.59,2281,4.098,2307,2.932,2319,4.271,2333,4.909,2455,3.892,2457,3.59,2458,3.892,2519,2.826,2555,4.603,2773,5.968,3414,4.909,3724,6.982,3725,4.793,3726,4.793,3727,4.793,3728,4.793,3729,6.372]],["t/598",[4,1.959,14,1.132,52,2.071,55,1.602,60,3.325,83,0.32,91,0.61,107,1.665,158,2.24,169,2.087,197,2.255,212,1.8,237,3.138,244,5.235,254,1.272,274,2.759,282,1.713,290,3.804,291,3.31,315,2.65,349,1.407,403,2.782,452,2.27,474,2.508,511,3.132,520,5.649,556,2.753,607,4.05,646,3.161,682,3.107,684,3.861,686,3.697,690,6.378,720,2.27,894,2.527,959,3.107,979,1.445,982,3.467,990,2.961,1007,6.039,1046,4.781,1119,3.984,1148,4.275,1154,4.36,1348,3.648,1375,5.975,1476,4.453,1498,6.378,1671,4.195,1824,3.6,2206,4.275,2280,4.781,2436,3.984,2555,4.605,3421,5.975,3451,4.552,3730,8.263,3731,6.989]],["t/600",[1,1.307,7,3.089,14,0.883,55,1.687,62,0.646,64,1.62,77,1.222,78,1.413,83,0.337,91,0.962,103,1.04,107,1.229,117,1.771,128,4.339,158,1.652,170,2.389,180,1.403,196,1.519,197,1.663,212,1.319,224,3.208,230,1.908,231,2.423,234,1.797,236,2.981,274,2.035,282,1.264,333,2.038,342,1.985,346,1.63,353,1.773,396,1.315,402,1.016,403,2.053,405,2.806,411,1.403,438,1.954,466,2.498,470,2.088,472,2.834,477,2.903,520,3.217,556,1.568,601,1.797,632,2.656,649,2.051,681,1.837,720,1.675,770,8.357,898,4.502,924,2.527,935,2.339,943,2.035,979,1.521,990,3.118,997,2.339,1039,1.923,1117,2.442,1125,2.226,1155,2.806,1196,3.736,1326,4.233,1339,2.292,1348,5.754,1539,3.095,1623,2.806,2101,3.527,2252,4.066,2279,3.527,2318,3.527,2503,7.709,2555,5.39,2742,4.186,3273,3.04,3274,3.154,3358,5.686,3404,3.154,3451,4.794,3543,5.332,3545,5.976,3606,4.186,3707,4.705,3710,8.542,3732,5.156,3733,5.156,3734,7.36,3735,5.156,3736,5.156,3737,4.408,3738,5.156,3739,7.36]],["t/602",[1,1.44,14,1.235,58,1.616,62,0.712,78,0.811,83,0.499,100,0.527,111,1.883,137,1.415,169,2.365,171,5.155,197,1.832,204,2.851,206,3.454,212,1.017,218,3.48,237,2.55,254,1.434,269,6.125,277,1.672,282,1.391,284,2.558,347,4.817,349,1.966,368,2.341,387,4.818,411,1.545,421,1.44,422,1.844,445,2.475,446,3.992,480,1.095,511,1.964,512,3.543,530,5.285,556,1.726,578,1.737,607,4.564,643,2.428,646,1.592,651,2.428,671,3.173,707,4.49,894,2.053,956,2.751,979,1.174,1007,6.514,1117,2.689,1170,3.992,1171,2.719,1397,3.236,1511,6.187,1516,4.599,1628,3.29,1824,2.925,2023,4.253,2553,4.61,2555,5.089,3153,4.61,3304,4.854,3453,5.469,3740,5.181,3741,5.678,3742,5.678]],["t/604",[14,0.582,51,1.139,57,2.623,58,0.861,62,0.71,78,0.808,80,2.502,83,0.447,100,0.985,101,1.944,103,0.8,112,2.517,113,3.081,116,2.235,117,2.7,128,3.338,134,2.643,137,2.251,142,2.235,145,3.038,157,2.68,158,2.519,162,1.667,171,3.227,182,1.931,212,1.014,224,2.468,236,2.293,243,3.776,253,3.212,254,1.03,261,1.446,276,3.176,277,1.667,282,2.391,284,2.553,288,2.7,301,0.871,376,2.468,402,1.549,403,2.254,409,2.492,421,1.436,452,1.839,461,3.689,468,2.571,474,2.032,477,1.597,646,1.587,649,1.577,682,2.517,791,4.84,838,3.98,912,4.102,924,3.853,959,2.517,979,2.119,982,3.899,993,2.542,995,3.808,997,3.566,1014,2.595,1187,2.568,1312,2.542,1339,2.517,1348,4.102,1362,3.463,1450,3.776,1531,2.995,1578,4.915,1648,3.398,1686,4.402,1687,3.607,1755,5.166,1834,2.808,1891,2.916,1907,4.24,2555,4.699,3129,3.687,3187,3.873,3453,5.464,3528,6.382,3572,5.166,3743,5.661,3744,5.661,3745,5.661]],["t/606",[83,0.374,99,5.14,100,0.927,103,1.152,104,2.357,157,2.781,171,4.648,172,3.874,211,4.095,241,3.115,265,1.379,273,3.699,283,2.448,346,2.578,402,1.966,431,3.905,452,2.649,461,3.332,477,2.3,480,1.573,556,2.479,649,2.272,675,5.311,908,3.423,926,5.733,974,3.332,979,2.229,1014,3.738,1044,5.195,1648,4.895,1834,4.045,1891,4.2,2247,4.095,2555,3.738,3414,5.733]],["t/608",[8,1.549,14,1.005,55,1.514,58,1.616,62,0.829,66,1.247,74,1.514,78,0.635,80,1.415,83,0.204,84,2.233,91,0.388,100,0.908,104,1.285,109,1.019,116,1.755,117,2.269,137,1.108,139,2.139,145,1.318,153,1.37,165,1.527,166,2.321,170,2.06,172,2.565,173,3.042,180,1.21,191,2.774,209,1.042,211,2.233,212,1.671,221,1.608,227,2.826,230,1.645,231,1.464,243,2.966,254,1.203,258,1.965,274,1.755,288,1.527,309,3.222,311,1.425,322,2.826,339,3.278,340,0.955,346,1.406,402,1.554,422,2.561,427,2.206,430,1.121,438,1.685,461,4.697,497,1.527,520,2.774,541,1.77,548,2.495,555,4.407,556,1.352,560,2.179,565,4.383,576,3.042,578,1.361,589,1.741,646,2.447,649,2.96,650,3.061,667,4.496,676,1.786,714,2.577,867,1.833,894,1.608,902,2.083,974,1.817,1039,2.464,1112,3.042,1144,4.423,1171,3.164,1326,2.826,1397,2.535,1428,5.287,1511,4.52,1516,2.261,1591,2.622,1620,2.495,1623,6.257,1625,3.331,1834,5.441,1891,3.403,2139,2.833,2201,5.713,2284,3.331,2296,4.646,2297,2.833,2345,2.669,2555,3.615,3271,4.407,3274,2.72,3404,4.042,3557,4.058,3632,6.986,3746,6.607,3747,6.607,3748,4.447,3749,4.447,3750,4.447,3751,6.607,3752,6.607]],["t/610",[62,0.825,67,2.554,74,2.235,77,1.559,83,0.476,91,0.851,100,0.611,109,1.508,180,1.79,182,2.244,197,2.122,205,2.896,212,1.179,231,3.418,234,2.292,250,2.051,277,1.938,294,2.292,305,3.389,329,2.292,340,1.412,396,2.753,411,1.79,430,1.659,461,4.411,469,3.389,470,2.665,536,2.868,569,2.642,601,2.292,646,1.844,649,3.152,672,3.124,736,3.879,790,3.345,935,2.985,968,4.5,979,1.799,982,3.263,983,4.105,1026,3.345,1096,4.928,1376,3.813,1450,4.388,1536,4.285,1834,5.148,2280,4.5,2319,4.024,2775,4.626,2878,7.942,3175,4.626,3408,6.519,3414,7.59,3630,6.004,3753,6.579,3754,6.579,3755,6.579,3756,6.579,3757,6.579,3758,6.579,3759,6.579]],["t/612",[14,1.195,51,1.692,55,1.139,58,1.091,62,0.623,78,1.2,83,0.329,91,1.026,100,0.855,109,1.642,116,1.961,134,1.184,137,2.534,150,2.495,160,1.853,165,1.706,169,1.145,172,1.929,177,2.704,206,1.898,239,2.928,249,1.12,275,1.592,284,1.614,286,2.187,311,1.592,317,1.776,322,2.124,349,1.964,353,1.197,359,1.731,369,2.231,370,2.379,371,3.629,396,2.144,402,0.979,419,2.314,420,2.254,421,1.26,422,1.614,444,3.493,452,2.989,468,1.625,472,1.913,477,2.021,480,0.958,496,3.598,497,1.706,538,2.982,540,1.694,543,2.302,548,5.162,627,3.165,632,2.559,642,2.832,646,1.393,649,1.997,685,4.247,689,3.236,704,2.559,872,3.094,943,1.961,944,1.853,974,2.03,979,1.738,1014,3.285,1026,2.526,1079,3.854,1085,2.353,1100,2.879,1448,3.236,1477,2.353,1538,3.493,1591,2.929,1704,4.247,2163,3.721,2319,3.039,2555,3.854,2614,3.493,2766,4.247,2783,3.313,2789,4.247,2833,4.247,2836,4.779,2837,3.599,3197,3.493,3275,4.247,3351,4.247,3352,4.247,3382,4.247,3411,4.534,3414,3.493,3482,6.54,3484,4.534,3486,4.534,3488,4.534,3507,4.534,3508,4.534,3603,4.534,3760,4.968,3761,4.968,3762,4.534,3763,4.968,3764,4.968,3765,4.968,3766,4.968,3767,4.968,3768,4.968,3769,4.968,3770,4.968,3771,4.968,3772,4.968,3773,4.968,3774,4.968]],["t/614",[58,1.291,83,0.389,91,0.74,100,0.788,104,2.451,117,2.913,144,2.699,145,2.514,221,3.067,225,2.043,256,3.044,265,1.434,284,2.755,301,1.687,322,3.627,402,2.014,421,2.151,461,4.175,497,2.913,649,2.363,672,3.044,681,3.021,1511,5.801,1834,4.207,2555,3.888,3210,5.187,3414,5.963,3632,6.352]],["t/616",[14,0.665,62,0.811,64,2.031,66,1.813,74,1.482,78,0.923,83,0.297,100,0.601,103,1.366,104,1.87,117,2.956,165,2.221,172,3.341,206,2.47,209,1.515,223,2.847,227,2.766,249,1.458,254,1.177,261,1.19,277,1.905,283,1.941,328,2.792,329,2.254,339,3.208,340,1.388,349,1.732,359,2.999,383,3.029,393,4.905,399,2.847,402,1.696,422,2.101,438,3.261,461,4.212,497,2.221,545,2.49,556,2.616,578,1.979,591,2.69,646,1.813,649,2.695,651,3.68,671,2.101,681,2.304,720,2.795,820,2.997,909,3.289,951,4.547,952,5.029,1006,5.902,1007,4.313,1024,3.987,1033,3.882,1085,3.063,1125,2.792,1144,3.628,1200,3.813,1376,4.987,1380,3.956,1428,5.242,1531,3.422,1566,3.331,1592,4.686,1834,3.208,2486,5.902,2783,4.313,3250,4.686,3274,5.264,3360,5.902,3404,5.264,3632,4.844,3664,5.902,3775,6.467,3776,6.467,3777,8.605,3778,6.467]],["t/618",[1,2.429,4,2.14,7,4.02,8,3.337,55,1.749,78,1.09,84,3.832,100,1.072,103,1.078,144,2.429,157,3.569,162,2.248,249,1.721,250,2.257,265,1.291,301,1.174,317,1.612,322,3.264,333,2.114,377,6.939,398,3.757,408,3.697,410,3.038,419,2.101,625,5.717,676,3.065,681,2.719,686,4.038,736,4.5,867,3.146,902,3.575,979,1.578,997,3.463,1015,4.862,1077,4.971,1088,3.879,1282,4.668,1294,5.221,1308,5.717,1527,4.281,1732,6.197,3779,7.632]],["t/620",[48,3.262]],["t/622",[1,1.415,2,2.642,48,1.85,55,1.278,58,1.789,62,0.699,77,2.123,78,1.455,83,0.497,91,0.782,99,2.873,100,1.056,101,2.672,107,2.31,112,3.983,124,2.259,139,1.367,144,2.852,146,1.465,165,1.916,169,2.349,180,1.517,182,1.902,188,3.129,200,2.902,209,1.307,212,0.999,221,2.814,230,2.064,259,4.365,261,1.648,274,2.202,303,1.633,311,2.493,325,3.816,398,3.967,402,1.099,410,2.221,411,1.517,427,2.767,433,2.505,452,1.812,468,1.824,477,1.573,478,1.876,521,1.423,568,2.585,569,2.24,590,4.151,619,3.922,654,3.082,671,2.527,673,3.567,752,3.315,941,3.813,981,3.289,992,2.734,1050,2.993,1085,2.642,1105,3.233,1110,4.042,1113,3.082,1278,3.922,1522,2.642,1566,2.873,1896,3.082,2603,5.827,3129,3.633,3780,5.48,3781,5.09,3782,5.09,3783,6.652,3784,5.09,3785,4.769,3786,5.578,3787,4.769,3788,5.578]],["t/624",[14,1.067,51,1.064,58,1.579,59,2.122,62,1.091,64,2.353,66,1.481,78,0.754,82,1.596,83,0.399,91,0.654,98,2.239,100,0.928,103,1.058,107,1.259,108,2.835,112,2.349,134,1.785,140,2.179,141,2.621,153,1.628,157,1.802,162,1.556,169,1.726,172,2.052,180,2.367,198,3.116,200,1.971,210,2.284,212,0.947,215,1.815,216,4.416,217,2.218,225,1.273,242,2.373,249,1.191,254,1.584,273,2.398,284,1.716,285,3.116,301,1.152,315,2.192,317,1.116,340,2.291,364,2.835,380,1.94,383,2.475,398,3.123,402,1.041,407,1.883,410,2.982,432,2.687,436,1.649,452,1.716,477,1.49,480,1.445,496,2.654,510,3.233,536,2.303,545,2.035,596,3.144,635,2.531,645,3.012,649,1.472,651,3.203,676,3.007,821,2.758,838,3.716,872,2.281,888,1.971,894,1.911,898,3.233,979,1.092,990,2.239,997,2.398,1025,3.172,1042,2.919,1047,3.062,1051,4.109,1079,2.423,1089,2.835,1147,3.958,1165,3.233,1282,3.233,1289,3.716,1311,4.291,1450,3.524,1477,4.732,1516,2.687,1758,2.796,1824,2.722,1837,3.116,1925,3.367,2519,3.116,2785,3.829,3077,4.822,3410,4.772,3666,3.716,3780,5.322,3789,4.822,3790,4.291,3791,4.822,3792,4.822,3793,4.822,3794,4.291,3795,6.404,3796,4.518,3797,4.822,3798,4.822,3799,4.822,3800,4.822,3801,4.822,3802,4.822,3803,4.822,3804,4.822]],["t/626",[1,0.903,4,1.763,5,1.093,14,1.059,48,0.685,51,1.266,52,1.055,55,0.816,56,0.676,58,1.594,62,0.588,64,0.649,65,0.796,71,1.001,74,0.474,77,0.49,78,1.337,81,0.91,83,0.437,90,1.967,91,0.94,98,0.875,100,1.026,103,0.788,107,1.754,112,0.919,117,0.71,121,1.346,130,1.824,134,1.329,135,3.012,141,1.025,142,0.816,144,0.658,145,0.612,146,0.935,150,1.788,153,1.937,157,1.214,158,1.141,163,0.816,165,0.71,169,0.821,175,1.133,177,1.125,179,0.802,180,1.517,182,0.705,189,1.198,191,0.867,202,2.221,209,1.099,210,0.935,211,2.356,212,1.319,214,1.445,215,1.223,216,2.099,218,0.736,221,0.747,224,0.901,234,0.72,236,0.837,237,2.504,239,1.455,242,1.599,248,2.137,249,0.803,250,0.839,253,2.569,254,1.145,258,0.888,261,0.38,262,1.109,265,0.793,266,1.317,274,1.852,277,1.382,282,0.873,283,0.62,284,0.671,285,1.218,287,1.141,290,1.125,296,0.91,298,0.867,301,0.318,303,1.042,305,1.064,311,1.141,315,2.684,317,0.436,329,1.635,332,1.001,333,0.986,340,1.746,345,1.709,346,1.126,349,0.944,352,1.125,353,1.515,357,1.767,364,1.109,368,1.468,376,0.901,380,0.759,383,0.968,387,1.264,396,1.196,398,3.129,399,1.567,402,0.924,406,0.685,408,1.725,409,2.065,410,0.823,411,0.969,419,0.569,420,1.615,421,2.063,422,0.671,429,1.125,430,1.183,433,0.928,436,0.645,440,1.264,443,1.378,452,1.524,456,1.886,458,1.884,464,1.834,465,1.632,468,0.676,469,1.834,470,0.837,474,0.742,478,0.695,479,0.777,480,0.687,481,1.189,511,0.715,521,1.878,524,1.198,530,1.064,531,1.705,540,2.344,553,0.731,556,0.628,557,1.038,560,1.013,568,0.957,578,0.632,580,1.537,591,0.86,596,2.64,606,1.051,622,1.668,627,2.268,637,2.268,646,0.998,649,2.267,663,3.375,665,0.947,671,2.642,674,1.064,681,2.24,687,1.453,704,1.064,716,2.58,722,1.159,733,1.917,752,0.765,821,1.858,828,0.979,834,1.159,867,0.852,872,1.537,888,1.328,894,0.747,928,1.198,940,1.218,941,2.733,944,0.771,956,1.001,959,1.583,963,0.731,964,1.038,979,0.427,981,4.796,995,1.001,997,0.938,1000,1.378,1014,0.947,1021,0.875,1030,2.891,1036,0.884,1042,1.142,1043,1.264,1057,1.178,1069,1.548,1088,0.837,1096,1.548,1097,1.264,1125,2.407,1153,1.414,1155,1.125,1161,1.414,1165,1.264,1174,1.025,1189,1.317,1246,2.58,1282,1.264,1312,0.928,1326,0.884,1332,1.317,1338,1.264,1339,0.919,1345,0.938,1368,1.997,1376,2.063,1381,1.453,1407,1.109,1467,1.24,1477,1.686,1489,1.967,1492,2.891,1496,1.91,1514,1.453,1516,1.051,1517,1.289,1522,0.979,1614,1.607,1627,1.767,1663,1.548,1672,1.548,1690,1.317,1756,1.548,1757,1.548,1758,2.95,1785,1.24,1820,1.548,1824,1.064,1836,1.548,1837,1.218,1859,1.886,1867,1.678,1874,1.198,1901,2.436,1925,1.317,1947,1.346,1971,1.453,1976,1.607,2008,1.24,2021,1.317,2152,1.767,2206,1.264,2211,1.886,2212,1.678,2320,1.378,2353,1.767,2443,1.767,2531,1.767,2558,3.399,2743,1.607,2832,1.607,2852,1.051,3129,1.346,3353,1.453,3410,1.317,3433,3.648,3434,1.678,3435,1.678,3444,3.044,3459,1.767,3460,1.767,3461,1.767,3462,3.044,3463,1.767,3464,1.767,3485,1.886,3506,1.886,3519,2.667,3634,1.678,3666,3.921,3668,3.921,3780,4.976,3794,1.678,3805,2.066,3806,2.066,3807,2.066,3808,2.066,3809,2.066,3810,1.317,3811,2.066,3812,2.066,3813,2.066,3814,2.066,3815,2.066,3816,2.066,3817,2.066,3818,2.066,3819,2.066,3820,3.56,3821,2.066,3822,1.767,3823,2.066,3824,4.011,3825,1.886,3826,2.066,3827,1.886,3828,3.249,3829,2.066,3830,1.886,3831,1.886,3832,1.886,3833,1.678,3834,1.886,3835,2.066,3836,1.886,3837,2.066,3838,3.044,3839,2.066,3840,1.886,3841,1.453,3842,1.24,3843,1.767,3844,3.56,3845,2.066,3846,3.044,3847,1.886,3848,2.066,3849,2.066,3850,1.497,3851,1.453,3852,2.066,3853,2.066]],["t/628",[52,2.376,55,2.263,58,1.777,60,2.944,80,2.552,83,0.512,100,1.037,103,1.132,104,2.317,141,3.977,144,3.406,153,2.469,196,2.361,219,3.977,230,2.967,249,1.807,265,1.356,398,3.544,402,1.58,433,3.6,477,2.261,842,4.646,867,3.305,981,4.727,1046,5.484,1531,4.241,3780,4.904,3854,6.854,3855,7.316,3856,7.316]],["t/630",[2,3.829,77,1.916,78,1.154,83,0.371,98,4.551,100,1.101,112,4.413,130,2.644,137,2.015,139,1.981,144,3.566,145,2.942,153,2.49,210,2.123,217,3.394,221,2.924,249,1.823,252,3.559,261,1.487,311,2.591,398,3.563,480,1.56,572,4.22,702,4.06,1531,4.277,1566,4.164,3857,5.858,3858,5.531,3859,8.085]],["t/632",[83,0.396,91,0.753,100,1.087,101,2.965,112,5.092,139,2.116,142,3.408,145,2.559,188,4.844,230,3.195,261,1.588,279,6.071,430,2.177,438,3.272,573,5.906,589,3.38,733,3.528,734,5.387,1894,5.281,1896,4.77,2097,6.714]],["t/634",[1,0.997,14,1.125,51,0.792,52,1.166,56,1.286,58,0.917,62,0.755,63,1.714,64,2.299,77,1.427,78,1.579,81,1.731,83,0.405,91,0.874,100,1.091,101,1.351,103,0.556,104,1.137,107,1.744,108,2.11,109,1.38,112,4.806,116,1.553,117,2.514,134,1.435,138,1.593,139,0.964,140,1.621,142,1.553,144,1.252,146,1.033,160,1.467,165,1.351,169,1.687,172,2.338,179,1.527,182,1.341,196,1.158,197,1.269,206,2.301,210,1.581,212,1.079,214,1.211,224,1.714,227,3.13,249,1.358,250,0.927,256,1.412,258,0.98,261,0.723,262,2.11,265,0.665,277,1.774,288,1.351,291,1.863,301,0.926,311,1.93,328,2.6,329,1.371,332,1.905,333,1.089,336,1.766,349,1.78,359,1.371,364,3.231,398,2.162,402,0.775,408,3.546,419,2.015,420,1.784,430,1.846,433,1.766,438,1.49,445,1.714,452,1.278,480,0.759,481,2.011,541,1.566,553,1.391,556,1.196,557,1.975,573,2.691,581,2.765,589,1.54,601,1.371,606,2,613,2.279,626,3.758,646,1.103,649,1.096,686,2.081,702,5.155,715,2.406,717,1.579,733,1.607,734,2.454,780,2.242,821,2.053,842,2.279,867,1.621,872,3.16,888,1.467,894,3.511,941,4.02,976,2.562,979,2.287,990,1.666,1024,1.822,1036,1.682,1079,1.803,1264,3.363,1311,3.194,1339,1.748,1384,3.82,1516,2,1559,2.506,1568,2.506,1578,4.464,1758,2.081,1842,2.406,2107,5.943,2206,2.406,2783,2.623,2825,5.497,2829,5.497,3358,2.173,3410,2.506,3453,2.026,3780,3.684,3789,3.589,3790,3.194,3860,3.933,3861,3.933,3862,7.32,3863,6.023,3864,6.023,3865,3.933]],["t/636",[4,1.241,14,0.455,56,2.153,59,1.777,60,1.625,62,0.986,63,1.929,78,1.393,83,0.399,91,0.76,93,3.392,97,2.578,100,1.109,103,0.625,104,1.279,107,2.408,112,2.927,114,3.496,117,2.701,134,1.055,138,1.793,146,2.064,148,2.927,153,2.028,157,1.509,160,1.651,165,1.52,169,2.145,179,1.718,180,2.369,182,1.509,185,4.28,196,1.304,200,2.456,210,1.162,212,1.18,216,3.882,253,1.808,255,1.638,258,1.103,259,2.483,265,1.113,294,2.294,303,1.927,311,2.11,325,3.028,340,1.998,352,2.409,353,1.066,384,2.609,385,3.442,394,2.342,398,3.627,410,3.467,419,1.218,451,2.096,480,0.854,512,2.761,553,1.565,568,2.051,569,3.497,590,2.051,591,1.841,634,3.784,635,2.12,646,1.241,648,2.816,649,2.426,698,3.315,699,2.761,702,2.223,720,1.438,731,3.315,752,2.437,881,3.112,894,2.381,941,2.169,991,2.565,992,4.268,994,2.761,1025,4.72,1036,1.893,1038,4.771,1066,2.82,1073,3.112,1080,3.028,1086,2.707,1088,2.667,1174,2.195,1185,3.784,1278,3.112,1520,3.112,1582,3.442,1624,3.442,1671,3.051,1716,4.039,1844,2.31,2386,2.707,2785,3.207,3182,6.009,3186,3.594,3408,3.315,3543,3.207,3689,2.952,3857,4.771,3858,7.952,3866,3.784,3867,4.039,3868,4.426,3869,4.426,3870,4.426,3871,4.426,3872,4.426,3873,4.426,3874,4.426,3875,4.426,3876,4.426,3877,4.426]],["t/638",[1,1.722,2,3.215,58,1.779,60,2.493,78,1.414,80,2.161,91,0.775,98,3.765,100,1.104,104,1.963,107,2.504,112,3.95,144,2.828,145,2.012,175,2.161,224,2.96,258,2.215,261,1.249,277,2,299,5.28,301,1.044,328,2.931,340,1.458,398,2.437,417,4.919,436,2.119,469,3.497,480,1.31,556,2.064,569,2.726,596,3.73,621,6.078,649,1.892,702,3.409,715,4.153,717,2.726,894,3.582,941,5.484,979,1.837,981,4.003,1054,5.926,1125,2.931,1531,3.592,1535,6.656,3858,4.645,3878,7.597,3879,6.196]],["t/640",[14,0.792,58,1.76,62,0.689,67,2.134,78,1.269,82,1.661,83,0.353,91,0.917,100,1.039,101,1.888,107,1.31,135,2.633,137,1.37,138,2.227,139,1.347,144,1.75,146,2.022,153,1.693,156,2.444,160,2.05,165,1.888,169,2.049,180,1.496,209,1.288,210,1.443,215,1.888,225,1.325,231,1.81,241,2.1,243,3.667,249,2.004,253,3.147,255,2.85,258,1.92,261,1.011,273,2.494,284,1.786,287,1.762,301,0.846,303,2.254,306,3.3,340,2.068,349,1.107,394,2.909,396,1.964,398,3.19,411,2.095,421,2.254,438,2.083,443,3.667,478,1.849,553,1.944,556,2.703,569,3.093,576,3.761,596,2.308,601,1.916,621,5.268,635,3.688,648,2.351,649,2.146,654,3.037,665,2.52,672,1.973,673,3.531,676,2.208,681,1.959,752,2.85,780,5.067,842,3.186,894,1.988,907,2.832,941,4.357,944,2.05,959,2.444,964,2.761,981,3.241,995,2.663,1021,2.329,1129,3.084,1398,3.084,1536,3.581,1824,2.832,3833,4.464,3854,4.7,3855,5.017,3856,5.017,3858,6.081,3878,8.235,3880,5.498,3881,5.498]],["t/642",[58,1.75,83,0.371,91,0.866,100,0.998,104,2.337,107,1.926,109,1.853,112,3.594,138,3.275,169,2.288,196,2.924,254,1.472,255,2.992,259,4.536,261,1.826,301,1.243,359,2.817,398,2.902,402,1.593,545,3.113,636,7.378,702,4.06,941,3.963,1125,3.49,1282,4.946,1934,7.378,3780,6.571,3783,6.912,3785,8.487,3858,5.531]],["t/644",[58,1.679,82,2.597,91,0.75,100,1.025,112,3.821,163,3.393,219,4.263,249,1.938,254,1.564,296,3.783,305,4.427,340,1.845,411,2.338,601,2.995,649,2.395,702,4.316,736,5.068,752,3.181,941,5.407,979,1.777,981,6.074,3879,7.844]],["t/646",[57,4.175,58,1.371,71,3.361,77,2.135,78,0.991,80,2.208,83,0.486,100,1.02,101,3.438,107,1.653,114,3.084,139,2.208,144,2.869,146,2.782,153,2.137,169,1.599,185,3.776,201,4.165,210,1.822,212,1.243,250,1.635,256,2.49,257,3.528,274,2.739,325,6.165,346,2.194,406,2.3,411,1.887,520,4.329,569,3.619,648,2.967,780,3.955,894,3.62,941,5.382,1021,3.818,1038,7.252,1088,3.65,1198,5.932,1582,5.395,1624,5.395,1667,4.519,2855,6.331,3782,6.331,3783,5.932,3854,5.932,3858,7.248,3866,5.932,3878,8.557]],["t/648",[1,1.12,14,1.003,51,0.889,56,1.444,58,1.415,62,0.824,64,1.387,78,1.121,83,0.476,91,0.685,97,1.729,100,1.126,107,1.566,117,1.517,134,1.052,146,2.062,169,2.606,175,1.406,193,4.03,197,2.121,212,1.178,214,1.36,215,1.517,224,1.925,249,1.77,250,1.04,254,1.429,265,0.747,277,2.313,282,1.082,283,1.973,287,1.415,305,2.274,311,1.415,329,1.539,340,1.867,346,2.078,376,1.925,380,3.194,383,2.068,386,3.434,396,2.793,398,1.585,422,3.28,426,2.274,443,4.384,464,2.274,468,2.15,479,1.66,481,2.195,521,1.126,540,2.242,541,1.758,560,2.164,569,1.773,623,2.091,646,1.843,648,1.888,649,1.23,672,1.585,688,7.166,717,1.773,752,1.634,872,3.39,902,2.068,946,1.963,959,1.963,964,2.217,990,1.871,992,3.849,997,2.003,1014,3.013,1021,1.871,1023,2.876,1050,2.369,1099,6.713,1108,3.199,1113,2.439,1326,1.888,1339,1.963,1370,2.091,1527,2.477,1566,4.044,1578,2.403,1628,2.559,1843,2.305,2116,4.03,2173,4.803,2213,3.585,2231,3.434,2253,2.876,2349,4.03,3276,4.03,3293,4.03,3561,5.62,3601,4.622,3858,4.497,3867,4.03,3882,4.416,3883,4.416,3884,10.375,3885,4.416,3886,4.416,3887,3.434,3888,4.416,3889,4.416,3890,4.416,3891,4.416,3892,4.416,3893,4.416,3894,4.416]],["t/650",[48,2.443,58,1.425,82,2.225,91,0.643,100,1.102,104,2.707,107,1.755,112,4.817,135,3.528,144,2.981,180,2.548,209,1.726,252,3.243,284,2.393,301,1.44,311,2.361,329,2.567,340,1.581,383,3.45,398,2.644,402,1.845,408,3.569,519,3.952,560,3.611,578,2.254,591,3.064,702,5.171,720,3.345,909,3.746,941,3.611,981,4.343,1024,3.413,1171,3.528,1200,5.522,1282,4.506,3780,4.506,3858,7.653,3895,6.723,3896,7.367,3897,5.981]],["t/652",[48,3.262]],["t/654",[1,0.794,2,1.483,14,0.916,51,0.63,52,1.493,55,0.718,56,2.066,57,1.451,58,0.961,62,0.994,64,0.984,69,4.799,74,0.718,78,0.719,81,2.217,83,0.408,85,2.04,91,0.777,93,4.085,97,1.972,100,0.468,103,0.892,104,0.905,106,1.216,107,0.746,109,1.154,111,1.039,134,0.746,137,1.256,139,0.768,140,1.291,160,2.699,172,1.216,175,1.603,180,2.157,190,1.995,191,2.652,196,0.923,205,2.217,210,1.322,212,0.561,225,1.214,249,1.136,250,0.738,253,1.28,254,0.917,258,0.781,261,0.576,265,1.068,277,1.484,287,1.004,298,1.315,301,0.775,307,1.815,308,1.88,315,2.118,317,0.662,324,2.346,333,0.867,336,2.837,340,1.081,346,0.99,349,1.014,351,1.916,353,2.303,384,2.97,393,1.785,396,2.362,398,1.124,399,1.379,402,0.617,410,2.515,418,1.483,419,1.739,421,1.602,430,0.79,451,1.483,470,1.269,477,0.883,480,0.604,481,1.046,519,1.68,521,1.611,522,1.68,540,2.154,541,1.247,564,1.657,571,2.202,577,2.089,637,4.025,680,2.543,691,1.88,706,2.202,707,2.871,718,1.573,820,1.451,867,1.291,881,2.202,888,1.168,924,1.535,927,1.635,934,1.635,944,1.879,946,3.526,956,2.44,963,1.108,966,2.594,974,1.28,982,1.554,991,3.661,992,1.535,997,1.421,1000,2.089,1024,1.451,1028,1.916,1035,1.303,1047,1.815,1053,3.281,1066,4.611,1079,2.309,1080,3.446,1082,2.436,1117,1.483,1125,1.352,1148,1.916,1166,2.04,1169,1.88,1245,1.613,1316,3.143,1335,3.446,1346,2.089,1380,1.916,1448,2.04,1471,6.374,1477,1.483,1489,2.782,1527,2.826,1533,4.677,1534,1.613,1671,2.334,1816,1.995,1844,5.491,1907,5.421,2061,3.209,2126,4.996,2151,3.883,2298,2.202,2430,2.143,2783,2.089,3309,3.773,3403,2.346,3601,2.202,3810,5.673,3851,3.541,3898,5.94,3899,3.132,3900,3.541,3901,3.649,3902,3.132,3903,3.132,3904,5.401,3905,5.765,3906,4.472,3907,3.132,3908,3.132,3909,3.132,3910,3.132,3911,3.132,3912,3.132,3913,6.605,3914,5.037,3915,3.132,3916,5.037,3917,3.132,3918,2.678,3919,3.132,3920,3.132,3921,3.132,3922,3.132,3923,3.132,3924,3.132,3925,3.132,3926,3.132,3927,5.765,3928,4.596,3929,2.858,3930,3.132,3931,2.858,3932,3.132,3933,3.132,3934,3.132,3935,3.541,3936,3.132,3937,3.132,3938,2.436]],["t/656",[14,0.841,52,2.759,56,1.962,69,5.02,78,0.857,81,2.641,83,0.48,91,0.942,97,2.349,111,1.989,117,2.061,137,2.039,139,1.47,162,1.767,180,1.632,191,2.518,208,3.477,210,1.575,212,1.075,221,2.17,225,1.446,249,1.353,254,1.489,259,3.366,265,1.014,284,1.949,301,0.923,315,1.756,333,1.662,340,1.288,349,1.208,353,1.446,396,2.55,409,2.641,419,2.252,452,1.949,481,2.003,521,2.086,522,4.389,527,4.347,540,2.046,637,5.212,717,2.409,944,2.237,946,3.637,1088,2.43,1212,3.822,1335,5.596,1471,7.501,1533,5,1816,3.822,1844,6.125,2038,5.129,2126,5.222,2704,6.127,3408,4.494,3681,5.129,3898,4.27,3904,6.994,3905,7.465,3906,4.674,3938,4.665,3939,6.642,3940,5.999,3941,5.999]],["t/658",[14,0.944,48,2.132,51,0.863,52,1.271,56,1.402,58,1.467,62,1.209,64,2.02,65,1.651,67,2.995,69,3.874,74,1.768,78,1.377,80,2.047,81,1.887,83,0.295,91,0.748,97,2.517,106,2.995,129,2.03,139,1.576,141,2.126,162,1.263,175,2.728,209,1.004,218,2.29,232,4.52,249,0.966,256,1.539,259,2.405,265,1.087,271,2.622,277,1.263,283,1.93,301,1.186,308,2.573,317,1.358,324,4.816,346,1.355,349,0.863,353,1.033,370,3.079,396,2.81,406,1.421,418,4.566,419,1.18,420,1.945,430,1.081,447,3.211,470,1.736,480,1.24,521,1.093,531,2.053,540,2.631,557,2.153,578,1.312,580,3.33,619,3.014,673,1.965,682,1.906,684,2.368,707,2.443,718,2.153,729,3.665,781,3.481,828,2.03,880,2.333,912,2.237,928,2.484,935,1.945,944,2.398,946,1.906,992,2.101,995,3.115,1070,3.014,1114,2.675,1212,2.731,1384,2.237,1404,3.481,1527,2.405,1533,4.836,1534,5.297,1535,3.211,1588,3.933,1671,3.574,1675,2.368,1844,5.032,1907,5.778,1951,3.014,2126,2.405,2470,4.999,2482,3.481,2484,3.014,2704,4.816,2850,6.264,3810,4.096,3851,3.014,3898,4.793,3904,3.665,3906,3.228,3913,7.822,3918,3.665,3927,5.867,3928,5.867,3929,3.912,3938,8.179,3942,5.497,3943,4.287,3944,3.665,3945,4.287,3946,4.287,3947,7.715,3948,4.287,3949,6.429,3950,4.287,3951,4.287,3952,4.287,3953,4.287,3954,4.287,3955,3.333,3956,6.429,3957,7.04,3958,3.481,3959,3.912]],["t/660",[14,1.139,56,2.234,58,1.563,62,1.015,69,4.772,71,2.254,78,0.975,83,0.213,91,0.707,100,0.432,111,1.543,117,1.598,158,1.491,174,3.183,191,1.953,210,1.222,256,1.67,277,2.385,281,2.743,286,2.048,298,1.953,301,0.716,311,1.491,317,1.443,333,1.289,335,3.371,353,2.29,364,5.099,370,3.272,380,1.708,384,2.743,396,2.424,418,2.203,421,1.18,511,2.364,521,2.065,540,3.727,541,1.852,564,2.462,626,2.903,883,2.743,937,3.271,946,3.037,963,1.645,979,0.962,992,3.349,1022,3.371,1055,2.228,1079,3.132,1119,2.652,1471,3.103,1517,4.263,1533,5.032,1534,4.17,1566,2.397,1758,4.284,1844,4.657,2126,2.61,2414,4.804,2484,3.271,2704,5.118,2842,2.846,2850,6.574,2852,2.366,3851,7.557,3898,2.428,3906,5.684,3942,5.841,3957,4.246,3958,3.778,3959,4.246,3960,4.653,3961,4.653,3962,3.485,3963,4.653,3964,3.485,3965,4.674,3966,3.371,3967,5.118,3968,5.118,3969,4.653,3970,6.33,3971,5.841,3972,3.978,3973,5.841,3974,3.978,3975,3.778,3976,5.313,3977,3.778,3978,3.778,3979,3.778,3980,3.618,3981,3.618,3982,3.618,3983,3.618,3984,4.653,3985,3.618,3986,3.618,3987,3.618,3988,4.246,3989,3.485,3990,3.778,3991,4.653,3992,4.246]],["t/662",[1,1.747,2,3.262,51,1.805,57,3.191,58,1.048,65,2.652,78,0.983,82,2.081,91,0.87,100,0.64,134,1.641,169,1.587,205,3.032,209,1.614,212,1.234,250,1.623,254,1.254,258,1.717,261,1.267,275,2.207,277,2.029,287,2.207,301,1.059,317,1.455,322,2.945,384,4.061,396,1.757,410,3.571,418,5.003,421,2.274,479,2.589,481,2.3,512,4.297,578,2.108,718,4.504,927,3.595,979,1.424,981,4.061,1053,4.486,1165,4.213,1169,4.135,1312,3.093,1383,4.135,1465,4.99,1533,3.987,2061,4.388,2151,6.222,3601,4.843,3810,5.714,3898,5.97,3901,6.499,3935,4.843,3964,5.159,3993,5.889,3994,8.528,3995,6.888,3996,6.888]],["t/664",[2,2.22,3,4.277,4,1.926,51,1.383,62,0.588,65,3.131,66,1.314,78,0.669,80,1.492,81,2.063,83,0.373,103,0.662,107,1.117,109,1.074,114,2.084,124,1.898,139,1.149,142,1.85,169,1.08,173,4.699,180,1.275,196,1.38,205,3.579,213,3.126,254,1.25,261,1.264,265,1.514,271,4.202,282,1.149,291,2.22,315,2.011,317,0.99,340,1.006,353,1.959,396,2.625,398,1.682,406,3.303,436,1.463,439,2.354,442,2.515,464,2.414,465,2.149,470,1.898,480,0.904,481,1.565,513,3.053,514,3.053,540,1.598,588,3.253,594,3.206,596,1.967,625,3.511,629,2.195,676,2.758,681,1.67,707,2.672,718,2.354,907,2.414,908,1.967,921,5.18,927,2.446,963,1.657,966,4.188,973,2.672,974,1.915,979,0.969,1014,2.149,1053,3.053,1088,1.898,1148,4.202,1166,3.053,1200,4.05,1230,4.123,1253,3.645,1326,2.004,1335,4.699,1527,3.854,1533,4.238,1588,5.477,1673,3.511,1803,6.974,1844,2.446,1845,4.277,1965,3.053,2125,2.763,2126,4.561,2151,5.114,2293,4.277,2764,4.007,3136,4.007,3555,4.277,3601,3.296,3810,6.557,3851,7.572,3898,5.916,3900,3.296,3906,3.449,3939,3.806,3955,3.645,3970,4.376,3993,4.007,3994,5.873,3997,9.487,3998,4.007,3999,4.277,4000,4.687,4001,4.687,4002,4.687,4003,4.277,4004,4.007,4005,4.687,4006,4.687,4007,4.687]],["t/666",[1,1.212,4,1.954,14,1.159,56,1.564,59,1.92,60,1.756,65,2.684,66,1.34,78,1.174,80,1.522,83,0.414,91,0.875,97,1.872,100,0.839,106,1.856,107,1.139,109,1.885,124,1.937,129,2.264,135,2.29,141,2.372,150,5.037,162,1.408,169,1.102,209,1.12,230,1.769,265,1.179,277,2.053,282,1.708,283,1.435,294,1.666,332,2.316,353,1.152,370,4.602,392,2.819,398,2.502,406,3.326,417,3.464,430,2.423,436,1.492,454,2.925,470,1.937,472,1.841,477,1.348,521,2.098,522,2.565,534,1.954,543,3.229,556,1.454,557,2.401,568,2.215,577,4.648,578,1.463,607,2.771,637,4.44,640,3.581,646,1.34,649,1.332,650,2.215,665,2.192,669,2.169,681,3.22,882,2.401,909,2.431,940,2.819,944,1.783,954,3.718,1089,2.565,1233,3.5,1335,3.271,1359,3.271,1370,3.301,1454,2.641,1471,7.079,1533,4.018,1762,5.22,1803,5.758,1844,2.495,1910,3.464,1952,3.581,2126,5.07,2151,2.565,2210,3.046,2796,6.678,2895,3.189,3129,3.114,3540,4.363,3810,4.44,3850,5.959,3851,6.355,3898,2.495,3906,4.539,3939,3.882,3955,3.718,3998,4.088,4008,6.97,4009,4.781,4010,4.781,4011,4.781,4012,4.781,4013,4.781,4014,4.781,4015,4.363,4016,4.781,4017,4.781,4018,4.781,4019,4.781,4020,4.781,4021,4.781,4022,4.781,4023,4.781,4024,4.781,4025,4.781,4026,4.781,4027,4.781,4028,4.781,4029,4.781]],["t/668",[14,1.186,52,1.122,58,0.576,62,1.008,64,2.245,66,1.061,69,2.936,78,0.54,83,0.328,91,0.886,100,0.543,103,0.826,104,1.094,106,1.469,107,1.393,117,1.3,130,1.238,134,1.703,135,5.055,139,0.928,145,1.122,157,1.291,158,1.874,163,1.494,180,1.03,209,0.887,210,1.876,212,0.678,213,2.524,214,1.166,218,1.348,225,0.912,230,1.401,254,0.689,258,1.458,262,2.031,265,1.359,274,1.494,277,2.559,282,1.433,283,1.136,301,1.236,303,1.108,305,1.949,317,0.799,321,2.315,322,1.619,332,3.462,339,1.877,340,0.812,342,2.252,349,1.991,359,2.038,372,2.361,396,2.216,402,1.152,408,3.462,410,1.507,421,2.038,422,1.899,436,2.23,458,2.002,470,1.533,477,1.649,521,1.823,526,2.272,545,1.457,580,3.47,588,1.792,596,1.589,607,4.658,642,2.157,645,3.333,649,1.055,676,1.52,681,1.348,733,1.546,828,1.792,880,2.06,902,1.773,944,2.181,946,3.574,974,1.546,979,1.662,990,1.603,1036,1.619,1039,1.412,1046,2.589,1085,1.792,1089,2.031,1124,4.459,1129,2.123,1158,2.589,1165,2.315,1171,1.813,1174,1.877,1187,1.717,1262,2.123,1312,1.7,1384,3.73,1428,1.925,1447,2.231,1527,2.123,1533,5.008,1544,5.178,1671,2.709,1675,2.091,1844,3.052,1901,5.499,1918,3.454,1973,5.557,2112,2.361,2173,2.315,2210,2.411,2279,4,2436,2.157,2852,3.634,2891,7.775,3544,3.236,3842,2.272,3850,2.742,3898,3.052,3906,4.808,4030,2.943,4031,5.848,4032,5.848,4033,3.236,4034,3.454,4035,3.236,4036,3.785,4037,3.785,4038,5,4039,3.785,4040,3.073,4041,3.454,4042,3.454,4043,3.454,4044,5.336,4045,3.454,4046,3.236,4047,3.454,4048,3.454,4049,5.336,4050,3.454,4051,3.785,4052,3.785,4053,3.454,4054,5.336,4055,3.785,4056,3.454,4057,3.785,4058,3.454,4059,3.073]],["t/670",[1,0.964,2,2.778,4,2.258,6,2.409,8,2.044,51,1.442,52,1.126,55,1.344,58,1.507,62,0.476,82,1.772,83,0.509,91,0.837,98,1.61,100,0.545,103,1.012,111,1.26,112,2.608,130,1.243,134,1.398,141,1.885,142,2.316,157,1.296,158,1.218,160,1.417,162,1.119,166,1.983,169,1.651,170,1.761,173,2.599,185,2.068,209,0.89,219,1.885,228,1.742,236,2.902,237,1.706,242,2.634,252,1.673,254,0.692,256,1.364,265,1.787,268,2.01,277,1.119,284,1.234,286,1.673,288,2.015,291,1.799,297,1.983,310,3.912,334,2.418,340,0.816,345,2.61,350,2.099,379,2.24,382,2.599,383,1.78,396,2.695,398,1.364,402,1.156,407,1.354,409,1.673,417,2.753,419,1.046,436,1.186,440,2.324,452,1.234,464,1.957,478,2.708,519,2.039,524,3.399,534,1.553,540,2,560,3.511,562,4.763,563,3.249,564,4.262,566,3.064,568,1.761,572,1.983,581,2.672,587,2.955,588,1.799,648,1.625,654,2.099,665,1.742,681,1.354,718,1.908,720,1.234,867,1.566,882,2.945,883,3.458,909,1.932,917,1.761,924,3.948,937,4.124,939,2.132,943,1.5,950,1.742,959,1.689,963,1.344,972,2.475,1003,2.955,1015,3.737,1021,1.61,1042,3.24,1043,2.324,1079,2.689,1088,2.376,1166,2.475,1177,4.394,1183,2.475,1208,2.132,1247,2.672,1316,3.66,1383,2.281,1384,3.739,1484,3.249,1527,3.291,1533,5.046,1534,4.484,1570,2.599,1588,3.588,1651,2.846,1713,2.324,1816,3.737,2041,2.672,2091,2.753,2125,2.24,2151,2.039,2220,3.249,2282,3.468,2459,2.753,2519,2.24,2592,3.249,2747,3.249,2798,3.468,2842,3.588,2891,4.763,3480,3.468,3678,2.846,3810,3.737,3898,3.061,3906,3.597,3970,4.564,3990,3.085,4060,3.8,4061,3.468,4062,3.8,4063,3.468,4064,3.8,4065,3.8,4066,3.8,4067,3.8,4068,3.249,4069,3.468,4070,5.866,4071,3.8,4072,3.8,4073,3.8,4074,5.353,4075,3.8,4076,6.125,4077,3.468,4078,7.164,4079,3.468,4080,3.468,4081,3.468,4082,3.468,4083,3.468]],["t/672",[7,2.583,66,1.725,67,2.389,91,0.537,99,4.287,160,2.295,173,4.21,175,3.002,198,3.628,205,2.709,261,1.132,265,1.709,277,2.451,340,1.321,346,1.946,353,2.273,370,2.947,396,2.775,402,1.213,418,3.942,421,1.561,433,2.764,480,1.187,481,2.055,511,2.129,540,2.099,564,4.403,580,2.657,704,3.17,721,4.997,728,3.765,828,2.914,883,5.561,887,2.851,946,2.736,1015,3.921,1262,3.452,1289,4.327,1384,4.344,1471,5.551,1477,2.914,1495,3.302,1527,3.452,1533,5.239,1534,5.437,1803,3.921,1844,4.922,2220,5.262,2687,4.61,2704,6.234,2842,5.091,3216,5.616,3682,4.009,3851,5.852,3898,5.273,3906,5.758,3938,4.786,3970,6.437,3989,4.61,3993,5.262,3994,5.262,3997,7.116]],["t/674",[6,2.587,8,2.681,83,0.441,84,3.863,91,0.671,100,0.714,103,1.36,134,1.833,157,2.624,169,1.773,218,2.741,254,1.4,265,1.628,284,2.499,301,1.48,317,1.625,333,2.131,349,1.549,396,2.455,402,1.516,478,2.587,560,3.771,566,3.29,580,3.321,902,4.508,927,4.015,1053,5.011,1088,3.116,1154,4.8,1398,4.316,1471,5.131,1533,4.67,1668,5.263,1729,5.409,1730,5.131,1803,4.902,1844,5.024,2151,4.128,3810,4.902,3851,6.768,3898,5.024,3900,5.409,3938,5.983,3997,6.578]],["t/676",[48,3.262]],["t/678",[1,2.048,4,1.651,51,1.185,58,1.704,69,2.957,81,4.058,83,0.37,109,1.35,113,3.205,121,3.835,139,1.443,146,2.887,153,2.839,163,3.188,175,1.874,200,3.012,223,3.555,228,4.548,265,1.366,277,1.734,287,1.887,296,2.592,303,1.723,317,1.244,322,2.518,340,1.264,346,1.862,376,3.521,396,2.651,398,2.113,409,2.592,421,2.338,431,2.82,477,1.661,511,2.037,521,1.502,588,2.788,648,2.518,654,3.253,676,2.364,841,3.751,843,3.303,921,5.146,943,4.103,959,2.618,966,4.16,998,3.835,1052,4.028,1100,3.412,1105,4.68,1195,3.535,1230,3.535,1279,5.373,1284,5.373,1299,3.835,1345,2.671,1477,2.788,1527,3.303,1533,4.888,1588,3.602,1641,5.034,2125,3.472,2151,3.159,2603,4.41,3810,3.751,3900,4.14,3906,4.056,3939,4.781,3998,5.034,4003,5.373,4084,8.077,4085,5.888,4086,5.888,4087,5.888,4088,5.888,4089,5.888,4090,5.888,4091,8.077,4092,5.888,4093,5.888,4094,5.888,4095,5.888]],["t/680",[14,0.965,58,0.789,62,1.079,63,2.26,69,4.323,78,1.055,83,0.395,91,0.926,100,0.799,104,1.499,106,2.013,107,1.235,139,1.27,162,1.527,175,1.65,205,3.252,225,1.249,258,1.292,317,1.561,340,1.586,353,2.39,369,2.328,380,1.904,396,1.884,398,1.861,402,1.021,407,1.847,418,4.077,419,2.37,420,4.5,421,2.751,440,3.171,480,1,511,1.793,521,2.53,556,1.576,580,2.238,603,3.171,663,2.377,671,2.4,673,3.387,707,2.955,733,3.019,888,1.933,938,3.457,943,2.917,963,1.833,975,3.457,997,2.352,1028,3.171,1079,2.377,1080,6.785,1086,3.171,1088,2.1,1117,2.455,1312,2.328,1495,5.034,1527,4.145,1533,5.321,1538,3.645,1577,3.756,1578,2.822,2151,5.693,2687,6.448,2842,5.266,3309,3.883,3689,3.457,3898,5.844,3906,4.981,3955,5.745,4096,5.184,4097,5.184,4098,7.388,4099,5.184]],["t/682",[7,1.417,14,0.847,55,0.774,58,1.798,62,0.671,64,1.06,67,2.076,78,0.482,80,1.075,81,1.486,83,0.478,91,0.855,103,0.477,104,0.976,109,0.774,111,2.903,175,2.787,180,0.918,196,0.994,197,1.089,210,1.404,218,1.905,221,1.221,225,1.289,241,2.043,242,1.516,249,0.761,254,1.209,261,1.221,265,1.551,274,1.333,277,1.575,284,1.097,288,1.16,295,1.894,301,1.162,303,1.944,317,1.595,333,1.481,336,1.516,340,1.621,346,1.068,349,1.076,359,1.176,376,1.472,393,1.924,396,2.836,402,0.665,407,1.905,421,2.087,422,1.097,436,1.669,446,2.374,472,2.059,480,1.281,521,0.861,524,1.956,534,3.364,540,3.605,541,1.344,553,1.891,564,4.633,580,1.458,598,2.252,626,2.106,671,1.097,704,1.739,706,2.374,752,1.249,883,6.313,888,1.259,982,2.653,1024,3.5,1079,1.548,1117,2.532,1212,2.151,1289,4.669,1312,1.516,1368,4.237,1384,4.296,1467,2.027,1473,3.874,1527,4.618,1533,5.253,1534,5.632,1951,2.374,2151,2.869,2484,2.374,2706,6.133,2852,4.186,2895,3.567,3682,5.704,3689,2.252,3898,5.345,3906,4.134,3964,5.657,3965,4.542,3966,3.874,3989,4.974,4100,5.163,4101,6.64,4102,3.376,4103,3.376,4104,4.342,4105,5.348,4106,4.88,4107,3.081,4108,3.376,4109,3.376]],["t/684",[2,1.664,4,0.985,14,1.044,51,0.707,56,1.805,58,1.704,60,1.29,62,0.692,66,2.164,67,1.364,69,3.422,74,0.805,78,1.102,83,0.442,91,0.866,97,1.375,99,1.809,100,0.717,102,1.628,103,0.779,106,3.745,107,1.315,108,1.885,109,1.265,111,2.259,116,2.178,119,1.764,130,1.149,134,1.624,144,1.118,145,1.041,153,1.082,158,1.126,175,2.458,180,0.956,209,0.823,210,0.922,227,1.502,230,1.3,236,1.423,242,2.478,249,0.792,253,2.784,261,1.015,265,1.152,268,1.858,273,3.091,274,1.387,275,1.126,281,3.253,294,1.923,298,1.475,301,0.54,317,1.631,321,2.149,332,1.702,340,1.184,346,1.111,353,2.146,359,1.923,372,2.192,374,2.732,376,3.658,379,2.071,380,2.026,396,2.684,403,1.399,406,1.83,421,0.891,422,1.141,436,1.722,465,4.083,480,1.064,540,3.037,559,1.645,564,2.919,580,1.517,588,1.664,626,2.192,699,2.192,820,2.557,828,1.664,882,1.764,883,4.017,894,1.27,944,1.31,974,1.435,979,1.141,993,2.478,995,1.702,1039,1.31,1042,1.941,1055,3.263,1057,2.002,1071,3.594,1079,1.61,1097,2.149,1117,2.613,1165,2.149,1187,1.594,1312,2.478,1384,3.556,1404,2.852,1453,3.515,1454,1.941,1473,2.545,1524,2.732,1527,4.331,1533,5.098,1534,6.006,1565,3.206,1655,2.545,1790,3.88,2112,3.443,2852,3.926,2895,2.343,3689,3.68,3898,4.03,3906,4.214,3962,4.133,3964,5.104,3965,4.661,3966,4.937,3986,2.732,3987,2.732,4100,4.291,4104,5.533,4106,3.206,4110,5.518,4111,3.513,4112,5.518,4113,5.518,4114,3.513,4115,3.513,4116,3.513,4117,3.513,4118,3.513,4119,3.513,4120,3.206,4121,3.513]],["t/686",[1,1.607,4,2.38,51,2.058,58,0.965,61,3.183,62,0.795,67,2.461,74,2.194,78,0.905,81,3.737,83,0.291,100,0.589,103,1.199,104,1.832,106,3.97,108,3.4,109,1.453,119,3.183,130,2.073,134,2.437,137,1.58,139,1.553,199,2.846,214,1.952,224,2.763,231,2.796,236,2.567,242,2.846,254,1.154,258,1.58,259,5.371,265,1.436,271,3.877,275,2.031,282,2.081,301,1.306,303,1.855,317,1.339,337,4.457,342,3.269,396,2.608,407,2.258,436,1.978,445,2.763,452,2.059,479,2.383,480,1.223,526,3.805,540,2.161,628,4.592,632,3.265,718,3.183,820,2.937,907,3.265,909,3.223,924,3.107,992,3.107,997,3.852,1016,4.227,1021,2.685,1043,3.877,1067,4.336,1082,4.929,1111,3.308,1145,5.419,1154,3.955,1383,3.805,1398,3.556,1533,3.774,1534,5.267,1625,4.748,1997,5.146,3494,5.784,3898,3.308,3906,3.183,4122,6.338,4123,6.894,4124,5.784]],["t/688",[1,0.859,4,0.95,14,1.088,51,0.682,52,1.004,55,1.229,56,1.108,58,1.61,62,0.949,63,1.477,67,2.082,69,4.784,78,1.08,81,1.491,82,1.024,83,0.402,91,0.802,106,2.082,111,1.778,117,1.842,137,0.845,139,0.83,158,2.425,162,0.998,175,3.122,180,0.922,189,1.963,191,1.422,210,1.408,212,0.607,225,0.816,231,1.116,254,1.377,265,1.28,274,1.337,277,2.229,281,3.162,287,1.086,301,1.024,315,0.992,317,1.406,333,0.938,336,1.521,349,1.079,353,2.47,364,5.111,370,1.623,376,2.338,396,1.93,406,1.123,407,1.207,422,1.1,465,1.553,470,3.065,472,2.065,480,1.46,481,2.222,511,1.172,519,1.818,521,1.697,540,3.426,545,1.305,553,1.198,557,1.701,561,2.897,564,1.792,626,3.346,643,1.449,646,1.503,663,1.553,671,1.1,676,1.36,959,1.506,1042,1.872,1055,1.623,1086,5.62,1111,1.768,1153,2.318,1187,3.02,1345,1.537,1384,4.577,1489,3.677,1495,3.571,1515,2.114,1517,2.114,1527,3.008,1533,5.212,1534,5.581,1758,4.363,1965,2.207,2021,3.417,2842,6.472,2852,4.459,3181,2.897,3678,2.538,3857,2.455,3906,5.951,3962,2.538,3964,4.017,3965,3.668,3966,3.885,3981,2.635,3982,4.17,3983,2.635,3985,4.17,4040,4.354,4100,4.17,4104,2.751,4125,5.363,4126,3.388,4127,3.092,4128,5.404,4129,3.388,4130,3.388,4131,3.388,4132,3.388,4133,3.388,4134,6.074,4135,3.092,4136,3.388,4137,3.388]],["t/690",[14,0.989,58,0.974,62,0.803,64,2.011,69,5.156,78,1.22,83,0.294,91,0.746,103,1.207,109,1.467,111,2.123,137,1.596,145,2.533,156,2.846,158,2.052,165,2.199,182,2.183,196,1.886,214,1.972,254,1.165,261,1.572,281,3.775,283,1.922,301,1.315,340,1.374,353,1.543,359,2.978,396,2.454,403,3.403,436,1.998,452,2.08,480,1.235,481,2.138,565,3.215,665,2.935,963,2.264,972,4.17,1055,3.066,1119,4.872,1436,4.639,1457,3.916,1533,4.995,1534,4.402,1758,5.09,1808,4.639,2016,5.474,2842,3.916,3898,3.341,3906,5.954,3962,4.795,3970,7.6,4128,6.94,4138,6.402,4139,6.402,4140,5.198,4141,5.842]],["t/692",[14,1.082,52,1.906,55,1.964,56,1.402,58,1.601,64,1.347,66,1.202,69,4.611,81,3.396,91,0.561,100,0.398,106,3.882,107,1.021,111,1.421,134,1.021,175,3.183,180,1.166,196,1.263,225,1.033,242,1.925,249,0.966,253,2.627,281,2.527,283,1.287,301,0.989,311,1.374,317,1.629,329,1.494,340,1.38,349,0.863,353,2.478,359,2.688,364,4.139,376,2.802,396,2.55,403,1.707,406,1.421,430,1.081,442,2.3,465,1.965,470,2.604,480,1.24,481,2.147,521,1.093,526,2.573,540,3.653,560,2.101,564,2.268,883,2.527,888,1.599,944,1.599,956,2.077,974,1.752,1005,4.262,1014,1.965,1026,2.18,1055,3.079,1071,2.792,1079,2.947,1089,2.3,1117,3.045,1262,2.405,1312,1.925,1339,1.906,1345,1.945,1381,4.52,1436,3.106,1489,4.262,1495,2.3,1519,3.859,1533,4.762,1534,5.885,1566,2.208,1671,3.971,1758,3.401,1790,3.014,1803,2.731,1808,3.106,2002,3.912,2112,2.675,2704,3.211,2842,3.933,3846,3.665,3906,5.84,3962,4.816,3970,6.93,3976,7.775,3980,3.333,4076,3.665,4128,3.481,4141,3.912,4142,4.287,4143,4.287,4144,4.287,4145,4.287,4146,4.287,4147,3.912,4148,3.912,4149,3.912,4150,3.912,4151,4.287,4152,6.429,4153,6.429]],["t/694",[6,1.413,14,1.051,51,0.846,58,1.385,62,1.142,69,2.11,78,0.6,81,1.849,82,1.269,83,0.439,91,0.553,98,1.78,100,0.588,103,0.594,106,1.631,134,1.001,137,2.115,144,1.337,229,2.059,248,2.522,254,0.765,256,1.508,261,0.773,294,1.464,303,1.23,311,2.029,328,1.814,333,1.754,342,2.439,349,1.833,353,1.012,364,5.328,370,3.033,380,2.326,396,1.072,421,1.065,430,1.059,464,2.164,470,2.565,472,1.618,477,2.15,521,1.072,540,3.884,556,1.926,623,1.99,667,2.164,888,2.362,944,2.362,979,1.309,992,5.01,1079,4.174,1111,2.193,1448,2.737,1453,4.035,1456,2.357,1533,4.982,1534,5.49,1566,3.926,1758,4.817,1804,2.802,1878,2.737,1989,3.147,2210,2.677,2289,4.965,2296,2.954,2310,5.709,2503,5.709,2809,5.709,2842,3.874,2852,2.137,3129,2.737,3528,3.412,3898,2.193,3906,5.133,3965,4.332,3967,6.356,3968,4.744,3970,6.097,3975,3.412,3976,6.598,3977,3.412,3978,3.412,3979,5.142,3980,5.927,3981,3.267,3982,4.925,3983,3.267,3985,4.925,3986,3.267,3987,3.267,4154,6.189,4155,4.202,4156,4.202,4157,4.202,4158,4.202,4159,7.622,4160,6.189,4161,6.517,4162,4.202,4163,7.622,4164,4.202,4165,4.202,4166,4.202,4167,4.202,4168,4.202,4169,4.202,4170,4.202,4171,4.202,4172,4.202,4173,4.202,4174,4.202,4175,4.202,4176,4.202,4177,4.202,4178,4.202]],["t/696",[1,1.024,2,1.135,4,0.672,14,1.103,50,1.438,51,1.381,55,1.2,58,1.632,62,0.657,69,4.347,78,0.748,81,1.778,83,0.363,100,0.223,103,0.338,106,2.665,107,0.962,108,1.286,109,0.926,111,1.339,130,0.784,134,2.063,137,0.597,140,0.988,144,1.667,146,1.061,153,0.738,157,0.817,162,0.706,163,0.946,175,2.369,182,1.786,183,1.251,210,1.061,212,0.429,214,1.613,215,0.823,218,2.19,224,1.044,225,0.577,228,1.852,229,1.174,242,1.076,249,0.911,250,0.952,253,0.979,256,0.86,257,1.218,261,1.592,265,0.405,273,1.087,274,0.946,277,1.19,294,0.835,301,0.805,311,0.768,317,1.298,328,1.034,333,0.664,340,0.514,349,1.237,350,1.324,353,1.262,359,0.835,364,5.342,368,0.988,370,1.934,376,1.044,380,0.88,396,2.755,399,1.055,406,1.339,421,0.608,427,2.004,452,0.778,465,3.146,468,0.784,470,1.636,471,1.251,472,0.923,480,0.779,481,2.292,497,1.798,521,0.611,534,1.65,540,3.629,559,1.122,564,1.268,579,1.946,580,1.744,622,1.122,643,1.025,665,1.852,676,0.962,679,1.736,707,2.302,718,1.203,720,1.312,722,1.344,752,1.495,883,1.413,894,1.461,908,1.006,943,2.427,956,1.161,972,1.561,979,0.495,992,3.881,997,2.789,1005,2.231,1012,1.598,1014,2.401,1021,2.218,1026,1.218,1050,3.298,1071,2.631,1079,1.852,1088,1.636,1100,1.389,1117,1.135,1118,1.495,1124,2.52,1151,1.598,1161,4.695,1174,2.597,1187,1.087,1245,1.234,1312,1.076,1345,2.376,1450,1.598,1454,1.324,1461,1.795,1489,2.231,1495,1.286,1533,4.835,1534,5.384,1566,1.234,1570,1.639,1659,1.366,1729,1.685,1730,1.598,1758,3.252,1790,4.322,1947,1.561,2008,3.143,2097,1.863,2112,1.495,2280,1.639,2842,4.552,2852,2.054,3271,1.598,3309,1.795,3634,1.946,3653,2.049,3906,5.495,3931,3.686,3958,1.946,3965,1.639,3967,5.141,3968,5.141,3970,6.929,3971,7.111,3972,2.049,3973,6.362,3974,2.049,3975,1.946,3976,3.141,3977,1.946,3978,1.946,3979,1.946,3980,1.863,3981,1.863,3982,1.863,3983,1.863,3985,1.863,3986,1.863,3987,1.863,3990,8.305,4015,2.187,4100,1.863,4127,4.778,4128,4.252,4134,2.187,4135,4.778,4147,2.187,4148,2.187,4149,4.778,4150,3.686,4179,2.187,4180,2.396,4181,2.396,4182,7.441,4183,2.396,4184,2.396,4185,2.396,4186,2.396,4187,2.396,4188,2.396,4189,2.396,4190,2.396,4191,2.396,4192,2.396,4193,2.187,4194,5.236,4195,5.236,4196,2.396,4197,2.396,4198,2.396,4199,4.039,4200,2.396,4201,2.396,4202,2.396]],["t/698",[1,1.875,8,2.577,55,2.152,83,0.43,103,1.326,104,2.138,130,2.419,162,2.178,180,2.012,214,2.278,228,3.39,236,2.995,252,3.255,254,1.346,261,1.36,265,1.25,276,4.149,287,2.37,315,2.165,334,3.049,353,2.262,359,2.577,396,1.886,472,2.848,477,2.086,479,2.78,481,2.469,560,3.625,578,2.263,588,3.502,681,2.634,718,3.714,907,3.809,1154,4.614,1312,3.321,1383,4.439,1477,3.502,1533,4.586,1534,4.836,1570,5.059,1808,5.358,1847,6.005,2185,5.751,2305,5.2,3906,5.449,3970,7.136,4076,8.82,4077,6.749,4203,7.395]],["t/700",[14,1.135,51,0.627,55,0.714,58,1.807,60,1.144,62,0.79,63,1.359,67,1.948,69,1.565,78,1.207,81,1.372,83,0.488,91,0.63,103,0.44,109,1.15,111,2.088,139,0.764,146,0.818,157,1.063,158,0.999,162,1.478,175,0.992,217,1.308,221,1.127,225,0.751,241,1.19,249,0.703,250,1.484,254,1.314,261,0.923,265,1.563,277,1.478,305,1.605,333,0.863,353,0.751,370,3.456,376,3.146,380,1.144,383,1.46,396,2.68,421,1.272,422,1.012,440,1.906,452,1.012,464,1.605,470,2.032,480,1.392,481,1.675,511,2.178,534,3.777,540,3.254,564,5.483,671,1.63,752,1.153,817,1.906,883,4.983,910,3.758,956,1.51,963,1.102,974,2.05,1005,1.722,1014,2.887,1042,2.772,1070,2.191,1097,1.906,1100,1.806,1151,2.079,1339,1.386,1345,4.627,1346,6.364,1362,1.906,1368,4.438,1384,4.129,1399,5.782,1533,5.358,1534,5.726,1643,2.531,1857,3.346,1935,2.258,1972,4.289,2101,2.132,2484,2.191,2546,2.844,2842,5.436,2852,3.202,3678,7.498,3682,4.701,3898,1.627,3906,3.972,3964,7.871,3989,5.406,4100,6.152,4104,2.531,4120,4.578,4193,2.844,4204,3.117,4205,6.297,4206,5.017,4207,3.117,4208,3.117,4209,5.017,4210,3.117,4211,3.117,4212,7.217]],["t/702",[4,1.04,14,1.08,50,2.228,51,1.159,55,0.851,56,2.601,58,1.31,60,1.363,62,0.885,66,1.04,69,4.324,74,0.851,78,0.822,81,2.535,82,1.121,83,0.436,91,0.616,100,0.345,107,0.884,109,1.32,111,1.231,131,2.689,137,0.925,139,0.909,163,2.274,166,1.937,175,1.833,191,1.558,210,0.974,223,1.634,224,1.618,231,1.222,249,1.298,254,1.448,261,0.683,265,1.193,277,1.696,298,1.558,301,1.223,311,1.189,329,1.293,333,1.028,336,1.667,349,0.747,353,0.894,364,4.62,370,5.175,380,1.363,421,0.941,433,1.667,436,1.158,440,2.27,470,2.859,477,1.047,480,0.716,511,3.491,521,0.947,540,3.441,553,1.312,569,2.313,598,2.475,629,1.738,642,2.115,704,1.911,706,2.609,717,1.49,797,6.054,867,1.53,887,1.719,909,1.887,912,1.937,944,1.384,973,2.115,974,2.884,979,0.767,992,3.899,1079,2.64,1086,4.317,1089,1.991,1096,2.78,1100,4.09,1102,2.886,1112,2.539,1124,2.315,1165,2.27,1194,3.173,1245,1.911,1378,2.539,1380,2.27,1384,3.684,1456,2.082,1457,2.27,1473,2.689,1476,5.068,1477,2.727,1515,2.315,1517,4.963,1533,5.026,1534,5.783,1566,1.911,1758,3.734,1804,2.475,1915,2.609,1951,2.609,1972,3.173,1989,2.78,2158,2.886,2298,4.049,2325,3.841,2842,6.308,2852,1.887,3678,5.287,3898,1.937,3906,5.489,3958,3.013,3965,2.539,3967,2.78,3968,2.78,3970,4.497,3971,4.924,3972,3.173,3973,4.924,3974,3.173,3975,3.013,3976,4.479,3977,3.013,3978,3.013,3979,3.013,3980,2.886,3981,2.886,3982,2.886,3983,2.886,3985,2.886,3986,2.886,3987,2.886,3992,5.256,4154,6.459,4213,3.013,4214,3.711,4215,3.711,4216,3.711,4217,4.479,4218,3.711,4219,3.711,4220,3.173]],["t/704",[52,2.373,58,1.628,64,1.434,83,0.432,91,0.588,99,2.351,100,0.424,106,3.432,130,1.492,134,1.087,169,1.052,175,2.144,180,1.241,261,1.239,265,1.594,277,2.604,283,1.37,284,1.482,287,1.462,301,1.036,333,1.264,349,1.611,350,2.521,353,2.272,370,2.186,396,2.603,402,1.578,406,1.513,410,1.817,418,2.161,433,2.049,465,2.092,470,2.728,474,3.173,511,2.33,521,2.254,534,2.752,540,1.556,564,4.988,572,2.382,596,2.828,617,5.238,805,2.908,828,2.161,883,4.72,887,2.115,927,2.382,992,4.333,1015,2.908,1024,2.115,1034,3.209,1035,1.898,1080,3.122,1144,4.959,1171,2.186,1288,3.209,1346,3.044,1368,3.779,1384,4.178,1399,3.122,1471,3.044,1489,3.721,1495,2.448,1497,3.549,1517,2.847,1533,5.278,1534,5.86,1844,2.382,2687,3.418,2706,3.706,2842,6.542,2852,3.425,3129,2.973,3682,2.973,3898,4.178,3906,5.891,3967,3.418,3968,3.418,3970,5.101,3989,5.997,3990,3.706,4154,3.706,4220,3.902,4221,6.147,4222,4.165,4223,4.165,4224,4.165,4225,4.165]],["t/706",[48,3.262]],["t/708",[14,1.114,51,1.038,58,0.785,62,0.923,66,1.445,67,2.002,74,1.182,78,0.736,80,1.641,83,0.394,91,0.864,100,1.039,109,1.182,135,5.188,137,1.835,140,2.126,145,1.528,146,1.932,169,2.372,180,2.546,183,2.691,198,3.04,200,1.923,216,5.836,231,1.698,252,2.269,254,0.938,258,1.835,261,0.948,265,0.872,268,2.728,277,2.168,283,1.548,284,2.788,290,2.806,294,3.449,301,1.132,303,2.154,305,2.656,321,3.154,329,1.797,340,1.842,349,1.727,385,4.009,396,2.525,398,1.851,408,2.498,410,3.726,419,2.026,431,2.469,436,2.297,443,3.439,464,2.656,466,2.498,477,1.454,480,1.42,521,1.315,530,2.656,541,2.053,569,2.07,580,2.226,663,2.364,671,2.391,699,5.84,780,2.939,881,3.625,908,2.164,944,3.201,991,4.265,1014,2.364,1025,5.152,1026,3.742,1047,2.988,1064,3.358,1152,2.691,1453,3.285,1525,4.009,1687,3.285,2173,3.154,2430,3.527,2775,6.581,2785,3.736,3410,4.689,3841,5.175,4226,6.034,4227,5.156,4228,5.976,4229,7.36,4230,5.156,4231,5.156]],["t/710",[4,2.098,62,1.186,83,0.476,91,0.825,98,3.17,100,1.012,107,2.254,124,3.031,135,5.383,139,1.834,158,2.398,210,1.965,216,4.411,218,2.666,231,2.464,250,1.763,265,1.265,282,2.318,283,2.246,288,3.249,298,3.141,301,1.151,333,2.072,372,4.668,402,1.474,408,3.625,480,1.443,646,2.652,649,2.89,717,3.004,728,4.577,1029,5.261,1110,5.421,1155,4.072,1245,3.854,1830,6.397,2097,5.818,2252,5.225,3842,4.491,3850,5.421,4226,5.261,4228,6.075,4232,6.828,4233,6.828]],["t/712",[14,1.128,74,1.944,83,0.469,100,1.019,103,1.198,129,4.016,130,2.773,135,5.253,216,5,218,3.021,340,2.194,408,4.108,643,3.627,649,2.847,944,3.163,1322,6.886,1894,5.187,2252,4.685,3842,6.584,4226,5.963,4228,6.886]],["t/714",[4,2.335,6,1.015,14,1.184,61,1.516,62,0.773,64,1.536,66,0.846,78,0.431,83,0.495,84,2.455,91,0.727,97,1.182,98,1.279,100,1.002,101,1.037,103,0.426,107,1.468,109,0.692,111,1.001,116,1.191,118,2.786,135,4.993,144,0.961,150,1.516,158,0.967,166,1.575,169,1.127,175,0.961,180,0.821,182,1.029,196,0.889,198,1.779,208,2.833,210,0.792,212,1.492,214,1.506,216,1.779,227,1.291,228,1.384,229,3.814,231,3.477,239,2.517,241,1.153,248,4.671,249,1.597,254,0.549,258,1.219,265,1.198,275,1.566,277,2.921,279,2.122,283,0.906,287,0.967,288,2.861,289,1.749,294,2.712,301,0.464,311,2.27,317,0.637,333,1.354,340,1.67,345,1.1,349,1.24,368,2.015,369,1.355,371,1.303,396,1.807,402,1.533,403,1.202,407,1.075,408,2.368,422,0.98,438,1.144,443,2.013,448,1.966,458,4.993,462,1.812,468,1.599,472,1.162,477,2.662,480,0.943,521,1.247,527,2.187,536,1.316,556,1.486,559,1.414,565,1.516,566,1.291,572,1.575,583,2.261,605,3.114,606,1.535,629,2.29,637,1.923,638,3.969,649,1.974,669,1.369,671,1.588,672,1.755,673,1.384,674,3.173,681,1.741,682,1.342,684,1.667,686,1.597,689,1.966,691,1.812,700,2.013,702,2.455,704,1.555,715,1.846,717,2.474,720,0.98,752,1.117,768,2.122,836,3.632,847,2.451,872,1.303,907,1.555,943,1.191,944,1.126,976,1.966,990,1.279,991,1.749,993,1.355,1012,2.013,1031,1.883,1039,1.126,1085,3.685,1088,1.222,1112,2.065,1152,2.551,1312,2.195,1350,1.966,1428,3.133,1453,1.923,1467,1.812,1477,1.429,1520,2.122,1525,2.347,1566,1.555,1620,2.742,1638,1.693,1642,2.013,1671,2.265,1782,1.72,1785,5.265,1947,1.966,2210,3.114,2252,1.667,2300,2.58,2319,1.846,2321,2.122,2339,2.58,2783,2.013,2833,4.179,2994,4.179,3261,4.179,3274,1.846,3341,2.261,3404,1.846,3410,1.923,3841,3.437,3842,4.252,4030,2.347,4069,2.754,4074,2.754,4160,2.451,4161,4.179,4226,2.122,4234,4.888,4235,3.018,4236,3.018,4237,3.018,4238,3.018,4239,2.754,4240,4.461,4241,3.018,4242,6.464,4243,4.888,4244,4.888,4245,3.018,4246,3.018,4247,3.018,4248,2.754,4249,4.888,4250,3.018,4251,3.018,4252,3.018,4253,4.179,4254,3.018,4255,2.754,4256,3.018,4257,3.018,4258,6.056,4259,3.018,4260,3.018,4261,3.018,4262,2.451]],["t/716",[1,0.637,5,1.33,7,1.055,14,0.784,45,2.149,50,1.509,51,0.506,55,0.576,62,0.527,65,2.715,66,1.776,74,2.194,80,0.8,81,1.106,83,0.462,91,0.473,100,0.819,101,2.915,107,1.291,109,0.964,111,2.101,116,0.992,117,0.863,130,1.375,134,0.599,135,4.065,137,1.579,139,0.616,144,0.8,145,0.745,146,1.104,148,1.117,153,0.774,156,1.117,157,0.857,158,1.347,160,2.363,162,0.74,175,1.338,180,1.144,182,0.857,210,0.66,212,0.45,214,0.774,216,3.735,225,0.606,227,1.075,228,2.485,229,5.179,231,2.999,239,2.589,249,1.428,250,0.991,253,1.027,254,0.457,257,2.138,258,1.579,261,0.997,265,1.071,275,0.805,277,0.74,282,1.328,283,0.754,288,2.176,289,5.112,294,0.876,295,1.41,296,2.386,306,1.509,307,3.672,317,0.531,329,0.876,336,1.128,340,2.445,342,0.968,345,0.916,346,0.795,349,1.091,350,2.322,353,0.606,380,0.923,383,1.177,396,1.616,402,0.828,407,0.895,409,1.106,419,0.692,430,1.597,433,1.128,452,1.366,458,4.949,468,0.822,477,0.709,521,0.641,531,1.203,568,1.164,569,2.176,589,1.646,590,1.164,595,1.348,605,1.601,621,1.719,629,1.177,637,1.601,643,1.075,646,1.519,649,1.51,669,2.459,671,0.816,672,1.509,681,0.895,702,2.111,705,1.482,717,1.009,720,0.816,778,1.954,805,1.601,821,2.194,842,2.436,847,3.414,874,3.836,880,1.368,882,1.262,894,0.909,943,0.992,944,2.021,964,2.111,973,2.396,979,0.869,991,2.436,995,1.217,1014,1.152,1023,1.637,1042,1.388,1053,1.637,1110,1.821,1151,1.676,1161,1.719,1171,1.203,1190,1.537,1231,1.568,1278,1.767,1294,1.719,1309,2.738,1457,1.537,1475,1.882,1480,2.149,1620,1.41,1671,1.164,1782,5.456,1794,7.724,1877,4.946,1974,5.782,2039,2.293,2147,5.725,2173,1.537,2210,6.22,2302,6.778,2320,2.804,2567,2.293,3250,5.109,3410,1.601,3682,4.127,3781,5.782,3842,3.253,3850,3.927,4035,5.417,4226,2.956,4228,3.414,4232,2.293,4233,2.293,4239,3.836,4262,5.725,4263,6.859,4264,2.513,4265,2.513,4266,2.513,4267,2.293,4268,2.293,4269,2.293,4270,6.191,4271,2.293,4272,5.42,4273,2.293,4274,2.293,4275,2.513,4276,2.293,4277,6.336,4278,4.634,4279,3.836,4280,2.513,4281,8.31,4282,2.513,4283,2.513,4284,2.513,4285,2.513,4286,2.513,4287,5.42,4288,2.513,4289,2.513,4290,2.513,4291,2.513,4292,2.513,4293,2.513,4294,2.513,4295,2.513,4296,2.513,4297,2.513,4298,3.594,4299,2.513,4300,2.513,4301,2.513,4302,2.513,4303,2.513,4304,2.513,4305,2.513,4306,2.513,4307,2.513,4308,2.513,4309,2.513,4310,2.513,4311,2.513,4312,2.513,4313,2.513,4314,2.513,4315,2.513,4316,2.513,4317,2.513]],["t/718",[4,2.111,7,1.729,14,1.206,51,0.829,58,1.278,62,0.782,63,1.796,78,1.199,83,0.466,91,0.829,97,1.613,100,1.001,103,0.582,111,1.366,129,1.951,134,1.795,135,4.866,137,1.555,142,1.626,175,1.311,209,0.965,210,1.082,212,1.118,214,1.269,216,2.429,218,2.223,223,1.814,227,1.762,229,2.019,231,2.48,234,1.436,249,1.698,268,2.18,277,2.218,282,1.01,283,1.237,288,2.143,289,2.388,294,1.436,301,0.634,328,1.779,332,1.996,333,1.141,336,2.802,340,1.803,342,1.587,346,1.973,349,1.516,353,1.504,363,2.242,367,4.798,368,3.105,396,1.921,398,1.479,402,0.812,408,1.996,419,1.718,421,1.582,422,2.027,429,3.396,430,1.039,454,2.52,461,1.683,477,2.124,480,1.204,497,1.415,524,3.616,534,1.683,536,3.283,541,1.64,545,1.587,555,2.748,606,3.83,643,1.762,649,2.647,651,1.762,652,4.269,663,1.889,667,2.122,671,3.085,867,1.698,908,1.729,912,2.15,937,2.897,944,2.327,950,1.889,979,0.852,993,2.802,1122,3.086,1152,2.15,1187,1.869,1189,2.625,1231,2.571,1339,1.832,1350,2.684,1367,3.975,1397,2.348,1428,3.173,1642,5.023,1675,5.45,1676,6.533,1741,3.086,1831,2.897,1832,3.204,1843,2.15,1891,3.879,2175,7.712,2234,3.345,2252,4.161,2297,2.625,2333,2.897,2832,4.852,3096,3.345,3114,3.523,3197,5.907,3273,2.429,3381,3.76,3410,2.625,3842,5.701,4226,2.897,4318,4.12,4319,4.12,4320,7.532,4321,4.12,4322,4.12,4323,4.12,4324,4.12]],["t/720",[4,1.207,7,1.357,14,1.238,48,0.613,51,1.04,52,0.958,55,0.424,56,1.408,58,0.786,60,0.679,61,0.928,62,0.54,64,1.622,65,1.244,66,0.518,69,1.623,71,0.895,74,0.741,78,0.838,80,0.588,81,0.813,83,0.436,91,0.675,100,0.964,101,1.11,102,1.497,103,0.456,104,0.534,109,1.184,116,0.73,117,1.11,134,0.77,135,4.406,137,0.806,140,0.762,144,1.371,153,0.995,158,1.655,162,0.544,169,0.426,172,0.718,183,0.965,196,1.268,200,0.689,209,0.433,210,0.485,212,0.579,216,3.045,224,1.409,229,3.791,231,0.609,236,0.749,242,0.83,248,1.94,249,0.729,250,0.761,252,0.813,254,0.784,258,0.806,259,1.037,261,0.34,265,0.313,275,0.592,277,1.521,282,0.453,284,1.399,287,0.592,288,0.635,289,2.993,291,0.875,301,0.497,303,0.541,305,0.952,311,1.036,315,0.541,317,0.683,329,0.644,336,0.83,337,1.299,339,0.917,340,1.661,342,1.989,346,1.022,349,1.679,359,0.644,368,1.332,371,1.395,372,1.153,375,1.339,395,0.978,396,1.645,400,0.992,402,1.018,403,1.287,407,0.658,408,2.502,410,0.736,418,1.53,419,1.776,420,2.927,422,0.6,427,0.917,430,0.466,431,0.885,433,1.451,445,1.877,458,3.674,466,1.565,470,1.309,474,0.663,477,0.911,480,0.357,513,1.204,521,1.098,537,1.58,540,1.761,541,0.736,542,1.437,545,0.712,553,0.654,556,0.562,578,0.566,580,1.859,589,0.724,591,0.769,596,2.708,607,2.495,629,0.866,637,1.177,643,1.382,649,2.456,665,1.482,671,0.6,672,2.315,681,2.756,682,1.914,684,1.785,686,1.71,689,2.105,691,1.94,699,2.016,717,0.742,720,0.6,733,0.755,738,1.384,778,1.437,780,1.053,821,1.687,836,1.09,842,1.071,880,3.193,927,0.965,938,1.233,943,0.73,944,1.606,946,4.649,959,0.822,964,0.928,968,1.264,974,0.755,979,1.599,981,1.09,991,1.071,993,0.83,997,0.838,1012,1.233,1021,0.783,1036,0.79,1039,1.205,1079,1.482,1085,3.815,1088,0.749,1089,0.992,1122,1.384,1124,1.153,1129,1.037,1141,1.153,1157,1.384,1171,0.885,1174,1.603,1187,0.838,1309,1.204,1312,0.83,1339,0.822,1397,1.842,1398,1.037,1428,0.94,1454,1.021,1457,1.977,1489,1.785,1527,1.037,1533,2.868,1534,0.952,1544,3.12,1566,1.665,1577,1.339,1578,1.006,1613,1.437,1625,1.384,1628,1.873,1659,1.053,1667,1.204,1671,2.393,1674,1.501,1675,1.021,1782,1.842,1794,2.341,1843,0.965,1901,1.264,1973,2.513,2023,1.384,2112,1.153,2126,2.897,2210,4.425,2231,1.437,2252,3.564,2302,3.742,2519,1.09,2852,0.94,2891,3.496,3250,1.339,3262,1.384,3341,3.225,3410,2.743,3453,1.665,3544,1.58,3577,1.687,3678,1.384,3682,2.805,3780,1.977,3842,5.623,3850,1.339,3887,5.016,3906,2.162,4030,1.437,4033,1.58,4035,3.682,4038,5.938,4040,2.624,4041,1.687,4042,1.687,4043,1.687,4044,1.687,4045,1.687,4046,1.58,4047,1.687,4048,1.687,4049,2.949,4050,1.687,4053,1.687,4054,2.949,4056,1.687,4058,1.687,4059,1.501,4226,1.299,4253,2.763,4255,1.687,4262,1.501,4263,1.437,4267,1.687,4268,1.687,4269,1.687,4270,2.624,4271,1.687,4273,1.687,4274,1.687,4276,1.687,4278,1.58,4279,1.687,4281,2.949,4298,1.58,4325,3.93,4326,1.848,4327,1.848,4328,6.3,4329,1.848,4330,1.848,4331,1.848,4332,1.848,4333,1.848,4334,1.848,4335,1.848,4336,1.848,4337,7.369,4338,1.848,4339,1.848,4340,1.848,4341,1.848,4342,1.848,4343,1.848,4344,1.848,4345,1.848,4346,1.848,4347,1.848,4348,1.848,4349,1.848,4350,1.848,4351,1.848,4352,1.848,4353,1.848,4354,2.763,4355,1.687,4356,1.687,4357,1.848,4358,1.848,4359,1.848,4360,1.848,4361,1.848,4362,1.848,4363,1.848,4364,1.848,4365,1.848,4366,1.848,4367,1.848,4368,1.848,4369,1.687,4370,1.687,4371,1.687,4372,5.016,4373,1.848,4374,1.848,4375,1.848,4376,1.848,4377,1.848,4378,3.232,4379,1.848,4380,3.232,4381,1.848,4382,1.848,4383,1.848,4384,1.848,4385,1.848,4386,3.232,4387,1.848,4388,1.848,4389,1.58,4390,1.848,4391,1.848,4392,1.58,4393,1.848,4394,1.848,4395,1.848,4396,1.848,4397,1.848,4398,1.848,4399,1.848,4400,1.848,4401,1.848,4402,1.848,4403,1.848,4404,1.848,4405,1.848,4406,1.848]],["t/722",[1,0.962,4,1.063,14,1.14,58,0.577,74,1.64,78,0.836,83,0.503,91,0.935,100,0.919,101,1.303,107,1.395,109,0.869,134,2.19,135,4.74,137,2.006,139,0.929,142,3.177,157,1.293,165,1.303,175,1.864,198,2.236,204,3.593,206,2.237,212,1.049,216,2.236,217,1.592,218,1.351,224,3.791,229,1.859,242,1.703,248,5.221,253,3.288,254,0.69,255,1.403,256,1.361,261,0.698,265,0.641,273,1.721,277,1.725,287,1.215,288,1.303,294,3.203,296,2.578,301,0.901,312,4.587,315,1.714,329,2.041,340,2.38,346,1.852,347,3.246,349,0.763,359,1.321,363,3.894,369,2.63,396,1.825,398,1.361,403,1.51,419,1.044,421,1.485,422,1.232,436,1.183,452,1.232,475,2.064,477,2.018,480,0.732,569,2.873,646,2.256,649,2.561,671,2.614,672,2.102,704,1.953,705,2.236,717,2.352,752,1.403,828,1.796,836,5.128,875,3.461,944,3.869,992,2.871,993,1.703,995,1.837,1007,3.906,1014,1.739,1024,2.714,1039,1.414,1046,2.594,1077,2.47,1187,1.721,1467,2.276,1477,4.81,1642,2.529,1802,5.345,1843,3.057,1894,2.32,1925,2.416,2809,4.387,3112,5.031,3230,4.755,3234,2.949,3235,3.461,3261,5.007,3453,1.953,3729,3.461,3887,2.949,4161,5.007,4226,2.666,4253,5.007,4407,7.155,4408,8.047,4409,6.529,4410,8.047,4411,7.937,4412,3.792,4413,3.792,4414,3.792,4415,3.792,4416,3.792,4417,5.857,4418,3.792,4419,3.792,4420,3.792,4421,5.857,4422,3.792,4423,3.792,4424,3.792]],["t/724",[1,1.546,6,2.049,67,3.21,80,1.94,82,1.841,83,0.379,85,3.97,86,4.286,90,3.367,98,2.582,100,1.048,103,1.168,130,1.993,134,1.452,135,5.038,148,2.71,169,1.405,180,1.658,182,2.079,210,1.6,212,1.092,215,2.093,223,2.683,229,2.987,249,1.374,254,1.109,261,1.121,263,3.803,265,1.702,301,0.937,303,2.42,311,2.65,315,1.784,346,1.927,349,1.227,380,2.238,384,3.594,394,3.225,402,1.201,408,4.006,419,1.678,420,2.765,458,4.375,540,2.079,566,2.607,580,2.631,598,4.065,649,2.304,671,1.98,676,2.448,681,2.171,694,3.659,716,4.416,820,2.824,834,3.419,843,3.419,880,4.501,924,2.987,931,6.194,960,5.211,962,3.659,966,4.259,979,1.26,1024,2.824,1082,4.74,1085,4.766,1350,3.97,1533,2.71,1566,5.184,1588,3.728,1758,3.225,1947,3.97,2098,8.604,2130,5.211,2252,4.568,3341,4.565,3842,3.659,4040,4.949,4372,7.298,4425,4.949,4426,6.095,4427,6.095,4428,6.095]],["t/726",[51,1.483,58,1.121,60,2.705,78,1.337,83,0.429,91,0.899,100,0.956,135,5.476,169,1.698,209,1.726,215,2.53,225,1.775,228,3.377,229,4.59,250,1.736,277,2.17,284,2.393,288,2.53,301,1.584,311,2.361,317,1.556,333,2.04,342,2.837,398,3.889,402,1.845,408,3.569,427,3.654,458,3.897,472,2.837,578,2.866,580,4.043,591,3.896,637,4.693,649,2.053,867,3.037,973,4.199,979,1.523,1085,3.489,1088,2.984,1309,4.798,1338,4.506,1520,5.18,1659,4.199,3842,4.422,4392,6.298]],["t/728",[6,2.506,8,2.597,83,0.342,84,4.738,98,3.158,100,0.961,103,1.333,137,1.858,142,2.942,146,1.957,157,2.542,172,3.663,180,2.027,183,3.89,191,3.129,209,2.426,212,1.335,221,2.695,250,1.756,261,1.371,265,1.26,402,2.04,419,2.052,548,4.181,556,2.266,560,3.653,564,3.943,572,3.89,645,5.378,689,4.855,842,4.319,902,4.419,934,3.89,962,4.474,1047,4.319,1052,5.098,1190,4.559,1282,4.559,1332,4.748,1398,4.181,1483,6.801,1539,5.664,1560,4.748,1570,5.098,1840,4.65,1844,3.89,2519,4.394,2797,6.801,4389,6.372,4429,7.453,4430,7.453]],["t/730",[48,3.262]],["t/732",[1,1.351,4,1.494,14,0.548,56,2.464,58,1.777,62,0.945,78,0.761,83,0.345,91,0.465,93,2.745,100,0.7,102,2.469,107,1.27,108,2.859,116,2.104,139,1.846,146,2.494,148,2.369,153,2.321,169,2.189,180,1.45,201,3.199,204,2.676,212,0.955,218,1.898,249,1.699,250,1.256,251,3.747,271,3.26,294,1.857,301,0.82,303,1.56,311,2.414,322,2.279,325,3.645,329,3.31,340,1.876,349,1.516,372,3.325,383,2.496,396,1.359,398,4.161,421,2.542,436,1.663,466,3.65,480,1.028,565,2.676,569,3.025,648,3.222,651,2.279,671,2.839,673,4.007,705,3.142,715,3.26,888,2.81,1038,5.459,1040,4.327,1079,3.454,1088,2.158,1105,5.808,1108,5.459,1113,4.162,1124,3.325,1155,2.9,1282,3.26,1309,3.471,1317,3.991,1463,4.144,1467,5.703,1497,4.144,1601,7.387,1659,3.037,1831,3.747,2151,5.961,2400,3.645,2603,3.991,3174,4.863,3780,4.609,3785,4.556,3866,4.556,4372,4.144,4431,5.329,4432,5.329,4433,4.863,4434,4.863]],["t/734",[1,1.387,2,1.645,5,4.69,14,1.213,51,0.699,58,1.571,59,1.395,62,0.962,65,1.338,78,0.496,83,0.352,91,0.81,100,0.628,101,2.324,103,1.084,107,0.828,115,1.813,131,2.517,134,1.303,135,3.24,144,1.106,147,2.602,153,2.084,156,1.545,158,1.113,162,1.023,169,1.261,179,1.349,180,0.945,189,2.013,196,1.023,199,1.56,204,2.747,210,1.436,212,0.623,215,1.193,216,2.048,217,1.458,218,1.238,224,1.514,225,0.837,240,2.443,251,2.443,252,1.529,254,0.996,282,0.851,283,2.031,287,1.113,294,1.211,297,1.813,301,1.18,306,2.086,307,2.013,311,1.753,315,1.017,322,1.486,329,2.358,340,1.792,345,1.266,349,0.699,352,1.891,364,5.156,369,1.56,371,1.5,376,2.384,396,1.395,398,2.428,408,2.65,430,1.379,436,1.707,444,2.443,452,1.129,474,1.247,477,1.908,480,1.055,510,2.125,520,2.168,534,1.42,536,1.514,540,2.307,545,2.106,596,1.458,625,2.602,646,1.534,651,2.893,665,1.593,669,1.576,671,1.129,674,1.79,676,1.395,702,1.745,715,2.125,720,1.777,733,1.42,780,3.118,790,1.767,843,1.949,872,2.921,898,2.125,908,1.458,997,1.576,1014,1.593,1026,1.767,1036,1.486,1077,3.563,1089,1.864,1105,2.013,1125,1.5,1245,1.79,1311,2.821,1326,1.486,1328,3.171,1454,3.022,1477,3.953,1495,2.935,1496,1.864,1530,2.214,1613,1.545,1623,2.977,1665,2.821,1667,2.263,1671,1.61,1689,2.517,1758,2.894,1837,2.048,1925,3.485,1971,2.443,2201,3.963,2210,4.31,2244,3.171,2307,2.125,2430,3.742,2519,2.048,3410,2.214,3496,3.171,3636,3.171,3666,2.443,3737,2.971,3780,4.139,3791,3.171,3792,3.171,3793,3.171,3794,2.821,3795,2.971,3796,2.971,3797,3.171,3798,3.171,3799,3.171,3800,3.171,3801,3.171,3802,3.171,3803,3.171,3804,3.171,3833,2.821,3834,4.992,3836,3.171,3846,5.785,3847,3.171,4258,2.971,4263,6.491,4328,4.677,4354,2.971,4435,3.474,4436,3.474,4437,3.171,4438,3.474,4439,3.474,4440,3.474,4441,3.474,4442,2.971,4443,3.171,4444,3.474,4445,5.47,4446,3.474,4447,3.171,4448,3.474,4449,3.474,4450,3.474,4451,3.474,4452,3.474,4453,3.474,4454,6.766,4455,3.474,4456,3.474,4457,3.474,4458,3.474,4459,3.171,4460,3.474,4461,3.474,4462,3.474,4463,7.674,4464,5.47,4465,7.617,4466,3.474,4467,3.474,4468,3.474,4469,3.474,4470,3.474,4471,3.474,4472,3.474,4473,3.474,4474,3.474,4475,3.474,4476,3.474,4477,3.474,4478,3.474,4479,3.474,4480,3.474,4481,3.474]],["t/736",[1,2.345,2,2.806,4,1.661,7,2.487,51,1.193,52,1.756,55,1.358,56,1.938,58,1.676,60,2.176,62,1.16,64,1.861,83,0.456,84,4.644,100,0.999,107,1.412,109,1.358,137,1.477,138,2.4,144,2.582,145,1.756,146,1.556,153,1.825,169,1.365,180,2.206,205,2.608,249,1.336,254,1.476,275,1.899,303,2.374,322,2.534,340,2.135,342,3.561,349,1.193,351,3.624,353,1.428,383,2.775,396,1.511,398,3.739,407,2.111,408,3.929,410,3.229,421,2.057,431,2.837,445,2.583,451,2.806,478,1.992,480,1.143,588,2.806,596,2.487,650,2.745,681,2.111,694,3.557,704,3.052,894,2.143,917,2.745,927,4.233,931,4.438,941,2.904,997,4.726,1014,2.716,1026,3.013,1035,2.465,1047,3.433,1088,2.4,1105,4.7,1282,3.624,1309,3.859,1312,2.661,1407,3.179,1518,4.293,1527,3.324,1560,3.775,1659,3.377,1735,5.066,1758,3.135,2036,4.811,2151,5.771,2181,3.951,3129,3.859,3780,3.624,4046,5.066]],["t/738",[1,0.506,4,0.559,5,1.056,14,1.113,51,1.096,52,1.024,55,0.792,56,0.652,58,1.594,59,1.387,62,0.683,64,1.435,65,0.768,66,1.28,74,0.792,77,1.29,78,1.032,80,1.099,83,0.484,89,2.155,91,0.837,100,0.993,101,1.87,102,1.6,103,0.282,107,1.606,109,0.792,111,0.662,112,2.736,113,1.086,115,1.041,117,1.87,118,1.137,121,1.3,124,2.205,128,1.176,134,1.723,135,1.654,137,1.357,139,0.489,142,1.803,145,0.591,146,0.907,156,0.887,158,0.639,165,1.569,166,1.041,176,1.3,180,1.674,197,1.114,200,0.744,208,2.002,209,0.467,210,1.616,212,0.818,214,0.614,215,0.685,217,0.837,221,0.721,225,0.481,228,1.583,233,1.551,239,0.815,242,0.896,248,1.198,249,1.228,250,0.47,251,1.403,253,1.411,254,1.316,256,0.716,257,1.014,261,1.24,265,1.14,266,1.271,270,1.331,275,0.639,277,1.345,282,0.846,284,1.484,287,1.745,288,0.685,290,1.086,298,0.837,301,0.702,303,1.011,305,1.028,310,2.304,311,1.972,315,2.239,317,0.965,322,1.477,328,1.491,334,0.822,338,1.331,340,2.145,342,1.33,346,1.722,347,0.905,349,0.402,353,1.101,359,0.695,380,0.733,382,1.365,383,2.55,396,0.881,398,2.595,406,0.662,408,0.966,419,0.549,421,2.031,422,0.648,426,1.028,429,1.086,430,1.373,436,0.623,451,0.945,452,1.122,455,1.331,465,4.632,468,0.652,469,1.028,472,1.759,477,0.974,478,0.671,479,1.717,480,1.3,481,0.666,510,2.113,511,0.69,518,1.041,521,1.719,526,1.198,530,1.028,531,0.955,534,0.815,536,1.506,540,1.178,541,0.794,545,1.33,564,1.827,568,0.924,569,0.801,570,1.551,578,1.057,589,0.781,590,0.924,595,1.07,596,2.286,601,0.695,622,2.139,626,1.245,629,0.934,644,1.62,648,0.853,649,1.878,663,3.507,665,0.915,671,1.484,681,1.23,683,1.445,702,1.002,705,1.176,714,1.156,716,3.309,717,1.387,720,0.648,722,1.119,752,1.278,780,1.137,820,0.924,828,2.163,834,1.938,872,1.972,887,0.924,894,0.721,907,1.028,908,0.837,914,1.88,917,0.924,941,2.239,973,3.508,974,0.815,979,1.393,981,1.176,990,0.845,993,0.896,995,0.966,996,4.655,997,0.905,1005,1.102,1012,1.331,1014,0.915,1035,0.83,1036,0.853,1039,1.703,1042,1.102,1050,1.853,1055,1.654,1062,1.403,1075,2.587,1079,1.583,1085,2.163,1086,1.22,1105,2.002,1119,1.969,1161,3.725,1230,2.073,1253,1.551,1262,1.938,1326,0.853,1339,0.887,1376,1.156,1397,1.969,1454,1.908,1463,1.551,1465,1.445,1467,1.198,1477,3.942,1516,1.756,1519,1.198,1523,1.494,1524,1.551,1566,1.028,1572,2.363,1578,1.88,1601,1.551,1628,2.002,1655,2.503,1659,1.969,1758,1.056,1831,1.403,1837,2.036,1843,1.803,1850,2.587,1857,1.331,1871,1.551,1891,1.028,1925,1.271,1952,2.587,1971,3.828,2043,1.821,2112,2.85,2126,1.938,2151,3.302,2152,2.953,2182,2.25,2219,1.706,2436,1.137,2532,1.62,2895,3.046,2993,1.706,3129,2.25,3144,1.62,3175,1.403,3210,2.113,3666,3.828,3668,1.403,3780,1.22,3790,1.62,3794,2.805,3795,1.706,3796,1.706,3824,2.953,3825,3.152,3827,3.152,3828,3.152,3830,3.152,3831,4.168,3832,3.152,3842,1.198,4068,1.706,4411,1.821,4442,5.262,4482,2.953,4483,1.821,4484,1.995,4485,1.995,4486,1.995,4487,3.454,4488,1.995,4489,1.995,4490,1.995,4491,1.995,4492,1.995,4493,1.995,4494,1.821,4495,1.995,4496,1.995,4497,1.995,4498,3.454,4499,4.567,4500,1.995,4501,1.995,4502,1.995,4503,1.995,4504,1.995,4505,3.454,4506,1.995,4507,3.454,4508,1.995,4509,3.454,4510,1.995,4511,1.821,4512,1.995,4513,1.995,4514,1.995,4515,1.995,4516,1.995,4517,1.995,4518,1.995,4519,1.995,4520,1.995,4521,1.995,4522,1.995,4523,1.995,4524,1.995,4525,1.995,4526,1.995,4527,1.995,4528,1.995]],["t/740",[14,0.891,51,1.315,61,3.281,62,1.086,66,1.832,77,2.053,78,1.58,80,2.08,91,0.756,100,1.088,107,1.557,112,3.852,129,3.094,141,4.297,142,2.579,153,2.012,157,2.228,163,2.579,165,2.244,180,1.777,182,2.228,196,2.552,204,3.281,211,3.281,219,3.241,249,1.473,250,2.29,258,1.629,265,1.105,311,2.094,340,1.403,346,2.739,430,1.647,469,3.365,596,3.637,622,3.06,669,2.964,733,3.972,872,2.821,888,2.437,917,3.027,974,2.67,979,2.009,981,3.852,982,3.241,1030,5.305,1111,3.41,1326,4.157,1516,3.322,1534,3.365,1566,3.365,1578,4.715,1824,3.365,2151,3.505,3112,6.091,3114,5.586,3149,5.586,3271,4.358,3666,4.594,3824,7.407,4482,5.586,4511,5.963,4529,6.534,4530,6.534,4531,5.586]],["t/742",[14,1.027,51,1.204,55,2.13,58,1.642,64,1.879,83,0.374,91,0.712,100,1.002,103,0.845,134,1.425,150,4.667,157,2.039,162,1.761,165,2.054,191,2.51,212,1.072,225,1.441,230,2.213,249,1.348,258,1.491,261,1.1,262,3.209,265,1.011,301,0.92,317,1.963,337,4.205,340,1.284,353,2.518,369,3.666,396,1.525,398,2.93,400,3.209,421,1.517,440,3.658,442,3.209,540,2.039,541,3.25,569,2.401,596,4.191,643,2.558,671,2.651,673,3.742,679,4.333,843,3.355,872,2.582,888,3.044,909,3.041,941,2.931,974,2.444,979,1.688,981,4.813,1036,2.558,1039,2.23,1047,3.466,1054,3.989,1057,3.409,1085,2.832,1125,4.311,1129,3.355,1326,2.558,1496,3.209,1516,3.041,1560,3.81,1570,4.091,1669,4.333,1690,6.361,1808,4.333,1837,3.526,2151,4.986,2836,5.444,3353,5.739,3356,5.113,3780,3.658,3787,5.113,3966,6.733,4482,5.113,4532,5.981,4533,5.458]],["t/744",[14,1.188,51,1.25,52,1.213,58,1.725,62,0.941,64,1.286,65,1.576,78,0.887,83,0.344,91,0.542,100,0.914,103,0.877,107,0.975,109,0.938,112,2.76,135,1.96,137,1.02,141,2.031,146,1.075,150,2.056,156,1.82,167,2.031,169,0.943,175,1.977,182,1.396,200,1.527,204,4.205,205,1.802,212,1.113,251,2.878,252,1.802,254,1.366,258,1.02,259,2.296,275,1.312,287,1.312,311,1.312,317,1.769,322,2.655,328,1.767,333,1.134,340,1.61,345,1.492,347,1.857,353,1.808,368,1.688,369,2.788,373,2.608,398,4.098,402,0.807,408,1.983,409,2.733,411,1.114,419,1.709,421,2.403,430,1.032,432,2.081,443,2.73,444,2.878,452,2.017,468,2.031,474,2.693,480,1.736,541,1.63,545,1.576,556,1.245,564,2.166,578,1.253,590,1.897,596,3.515,603,2.504,622,1.917,649,1.141,651,2.655,652,2.8,665,1.877,671,3.077,673,2.847,681,2.673,720,1.33,822,3.736,941,3.677,966,2.108,974,1.673,982,2.031,994,2.554,1014,2.847,1036,2.655,1039,1.527,1079,2.847,1129,2.296,1166,2.666,1187,1.857,1195,2.457,1312,1.838,1326,3.208,1370,1.938,1407,2.196,1456,2.296,1539,2.457,1654,5.309,1690,3.956,1701,5.042,1939,2.966,2151,3.331,2173,3.798,2436,2.333,2519,2.413,2555,1.877,2836,2.73,3353,2.878,3668,2.878,3780,2.504,3787,3.5,3790,3.324,3833,3.324,3842,2.457,3887,3.183,3965,6.157,3966,5.435,4033,3.5,4038,5.309,4248,3.736,4325,5.667,4372,4.829,4483,3.736,4534,4.093,4535,4.093,4536,4.093,4537,4.093,4538,4.093,4539,4.093,4540,6.209,4541,4.093,4542,7.502,4543,4.093,4544,4.093,4545,4.093,4546,3.736,4547,4.093,4548,4.093,4549,4.093]],["t/746",[14,1.147,58,1.5,62,0.64,66,1.43,78,1.218,83,0.391,91,0.637,100,1.021,134,2.033,139,1.25,144,1.624,150,4.677,158,1.635,165,1.752,171,2.907,179,1.98,209,1.195,210,1.918,243,3.402,275,2.34,315,1.493,317,1.802,328,2.202,340,1.831,347,2.314,349,1.027,353,2.056,369,3.28,376,3.719,380,2.682,384,4.306,398,3.79,419,1.404,420,2.314,421,2.5,427,2.53,466,4.133,479,1.917,497,1.752,540,1.739,578,1.561,596,4.534,640,5.47,643,2.181,646,2.047,663,2.338,944,1.902,946,3.247,979,2.12,990,2.161,1023,3.322,1039,2.724,1054,3.402,1125,3.683,1370,4.04,1397,2.907,1578,2.776,1659,2.907,1671,2.363,1690,3.25,2151,6.059,2389,5.998,2553,4.142,2555,2.338,2815,3.696,3353,5.135,3453,2.627,3653,4.361,3955,3.966,3965,3.489,3966,6.749,4546,4.655,4550,5.101,4551,5.101,4552,5.101,4553,5.101,4554,5.101,4555,5.101,4556,10.256,4557,8.531,4558,5.101,4559,5.101]],["t/748",[1,1.586,4,1.754,6,2.103,8,2.18,51,1.259,52,1.854,56,2.046,58,1.548,62,0.784,64,2.643,77,1.994,78,1.452,83,0.466,91,0.888,100,0.883,146,2.209,156,2.781,169,1.939,198,3.688,225,1.507,228,2.868,283,1.878,284,2.032,287,2.004,301,1.294,322,2.675,340,2.041,353,2.028,380,3.09,396,2.425,398,3.413,402,1.874,410,2.49,421,2.134,466,3.03,472,2.409,474,2.245,478,2.829,479,2.351,541,2.49,560,3.066,572,3.265,648,2.675,649,2.345,654,3.456,699,5.25,705,3.688,902,2.93,927,3.265,939,3.509,943,2.469,946,2.781,963,2.212,979,1.74,997,2.838,1026,3.181,1036,2.675,1088,3.408,1171,2.996,1384,3.265,1533,2.781,1659,3.565,2150,5.708,2151,5.457,2185,4.864,3181,5.348,3262,4.685,3842,3.755,4560,8.415,4561,9.51,4562,6.255]],["t/750",[48,3.262]],["t/752",[74,1.885,78,1.174,83,0.46,100,0.764,101,3.87,114,3.656,124,3.331,139,2.016,146,2.159,162,2.422,163,3.247,169,1.895,185,4.476,199,3.693,254,1.497,301,1.265,323,5.485,436,2.566,465,5.166,480,1.587,648,3.517,966,4.236,1033,6.021,1038,5.959,1447,4.849,1522,3.895,1523,6.16,1524,6.395,1568,5.24,1911,6.16,2171,7.505]],["t/754",[1,2.218,4,1.252,51,0.899,57,2.07,58,0.68,61,2.243,65,2.553,67,1.734,74,2.438,78,0.638,82,2.003,83,0.477,91,0.579,92,3.82,93,2.301,97,1.749,100,0.812,101,3.21,103,0.631,114,1.986,153,3.016,155,3.627,156,1.986,163,2.617,167,2.216,169,1.03,179,3.07,181,3.141,182,1.523,185,5.329,188,2.506,191,1.875,199,2.006,209,1.554,212,0.8,214,1.376,219,3.289,227,2.835,236,2.686,249,1.007,256,2.838,261,0.822,265,1.58,275,1.432,287,2.534,312,2.546,315,1.941,322,1.91,323,2.98,334,1.842,380,1.64,400,2.397,402,1.306,408,3.83,410,1.779,445,1.947,451,3.14,464,2.301,465,4.773,471,2.332,475,2.432,478,2.941,479,2.972,526,2.682,550,3.474,566,2.835,577,2.98,587,5.156,633,2.682,648,4.334,673,2.048,707,2.546,820,3.072,880,3.609,921,4.224,927,2.332,935,2.027,937,3.141,950,3.04,959,1.986,973,3.779,977,5.984,979,0.924,997,2.027,1033,2.682,1038,3.237,1043,2.733,1050,5.706,1052,3.056,1088,2.686,1141,2.787,1190,2.733,1208,2.506,1225,3.346,1245,2.301,1251,5.384,1253,5.156,1256,3.212,1258,3.82,1269,3.474,1282,2.733,1288,3.141,1316,2.787,1332,2.846,1522,4.425,1523,4.966,1713,2.733,1844,3.461,1891,2.301,2007,3.82,2011,3.474,2125,2.634,2151,3.557,2415,4.077,2696,3.474,3519,3.346,3685,4.077,3689,2.98,3843,3.82,4030,3.474,4425,5.384,4494,4.077,4563,4.467,4564,4.467,4565,4.467,4566,4.467,4567,4.467,4568,4.467]],["t/756",[4,2.21,52,2.337,60,2.895,74,1.807,80,2.51,83,0.362,101,2.708,102,3.653,103,1.114,109,1.807,167,3.911,200,2.941,209,1.847,219,3.911,265,1.333,283,2.367,298,4.459,345,2.873,359,2.747,380,2.895,417,5.713,419,2.171,465,4.481,472,3.036,566,3.372,588,3.734,780,4.494,880,4.291,963,2.788,966,4.061,977,5.394,1050,5.244,1245,4.061,1294,5.394,1336,6.741,1338,4.823,1522,4.628,1531,4.171,2430,5.394,3309,5.906,4061,7.195,4242,7.195,4569,7.884,4570,6.402]],["t/758",[74,2.598,78,1.195,83,0.465,91,0.73,124,3.39,137,2.086,139,2.051,144,3.227,145,2.48,188,6.118,212,1.499,229,4.102,253,3.42,281,4.934,321,5.119,465,4.647,475,4.555,511,2.895,569,3.36,681,2.981,1050,4.49,1055,5.428,1522,3.963,4570,6.795]],["t/760",[2,1.604,4,0.95,6,2.545,7,2.251,14,0.902,52,1.589,55,0.777,56,1.108,58,1.152,59,1.36,60,1.244,65,1.305,66,0.95,74,2.183,77,0.803,78,0.484,83,0.437,91,0.978,102,1.57,103,0.94,106,1.315,107,1.803,109,1.229,111,2.207,119,1.701,124,1.372,134,1.586,139,1.314,145,2.243,153,1.044,158,1.086,160,2.482,163,2.117,164,2.635,165,1.164,167,2.66,182,1.829,188,1.901,206,3.15,223,1.491,225,0.816,228,2.458,229,2.628,231,1.116,236,1.372,242,2.989,251,2.382,253,4.323,254,0.617,255,1.984,261,1.224,273,2.433,277,1.579,281,6.318,282,0.83,285,5.617,289,1.963,291,1.604,294,1.181,296,1.491,301,0.521,317,1.133,321,3.28,329,1.181,334,1.397,340,0.727,346,1.696,349,1.918,352,1.844,401,2.538,406,2.207,430,1.908,435,4.985,462,3.219,465,1.553,470,1.372,475,1.844,480,1.46,481,1.131,511,1.172,580,1.463,596,1.422,629,2.512,640,4.985,643,2.846,652,2.318,698,2.538,702,1.701,715,2.072,738,2.538,780,1.931,805,2.159,812,6.82,820,1.57,867,1.397,880,1.844,943,2.117,944,3.271,947,2.455,956,1.641,974,1.384,983,2.114,985,2.455,990,1.435,995,3.995,1014,1.553,1050,1.818,1055,5.578,1150,5.5,1262,1.901,1397,1.931,1450,2.26,1515,2.114,1522,5.496,1572,4.553,1582,2.635,1613,1.506,1638,1.901,1687,2.159,1801,4.585,1812,3.77,1824,3.428,1826,3.092,1894,2.072,1935,2.455,1942,2.455,2125,1.998,2343,3.092,2774,2.751,2852,4.193,2897,2.751,2900,2.635,4570,4.354,4571,3.388,4572,3.388,4573,3.388,4574,3.388,4575,3.388,4576,3.388,4577,3.388,4578,3.388,4579,3.092,4580,3.388,4581,3.388,4582,3.092,4583,2.897,4584,3.388,4585,3.388,4586,6.656,4587,3.388,4588,3.388,4589,3.388]],["t/762",[4,2.538,7,2.934,14,0.719,55,2.075,64,2.195,69,4.547,83,0.32,91,0.61,107,1.665,109,1.602,175,2.225,182,2.383,198,4.12,209,1.638,225,1.684,228,3.204,230,2.586,249,1.576,250,1.647,265,1.182,298,2.934,322,2.989,349,1.407,353,2.182,396,2.309,402,1.377,421,2.547,462,4.195,480,1.348,511,2.418,521,1.782,527,5.064,566,2.989,663,3.204,917,3.238,932,7.352,933,7.352,947,7.698,997,3.171,1080,4.781,1105,4.05,1117,4.756,1407,3.749,1495,3.749,1522,5.031,1527,3.921,1533,5.013,1538,4.914,2416,4.914,2687,5.235,3689,4.661,3898,5.242,3906,3.509,4590,6.989,4591,6.989,4592,6.989]],["t/764",[6,1.278,14,1.067,52,1.739,55,0.871,56,2.343,58,1.608,59,1.526,60,1.395,69,1.908,74,2.198,82,1.148,83,0.454,91,0.703,100,0.665,103,0.537,106,3.572,107,1.398,111,2.375,119,1.908,128,2.24,134,1.707,145,1.126,153,1.17,158,2.581,163,2.316,175,3.235,179,1.475,206,2.24,219,1.885,221,1.374,230,1.406,231,1.251,232,2.672,236,1.539,242,3.217,250,0.895,253,3.759,254,1.068,265,1.211,281,5.132,282,1.756,285,3.458,288,1.305,296,2.582,301,0.584,303,1.717,317,1.513,333,1.052,340,0.816,349,1.181,353,1.941,370,1.82,396,2.055,402,0.749,406,2.375,430,1.479,458,3.103,462,2.281,465,2.689,468,1.243,480,1.775,521,1.496,524,3.399,526,3.521,540,2.969,569,1.526,580,1.64,812,2.955,836,2.24,890,2.324,917,1.761,941,1.862,947,7.176,956,1.841,977,2.599,994,2.371,1050,2.039,1055,4.743,1058,3.085,1071,2.475,1117,1.799,1141,2.371,1245,1.957,1448,2.475,1453,2.421,1522,5.339,1530,2.421,1533,4.403,1534,5.442,1560,2.421,1572,2.599,1790,2.672,1824,3.021,1995,3.468,2106,3.249,2112,2.371,2173,2.324,2470,5.571,2482,3.085,2484,2.672,2532,3.085,2593,6.964,2852,2.983,3149,3.249,3689,3.912,3810,2.421,3857,2.753,3898,3.739,3906,1.908,3962,2.846,4579,3.468,4582,3.468,4583,5.015,4593,8.751,4594,3.8,4595,2.672,4596,5.866,4597,4.763,4598,3.8,4599,3.8,4600,3.468,4601,3.8,4602,3.8,4603,3.8,4604,3.8,4605,3.8,4606,3.8,4607,3.8,4608,3.8,4609,3.8,4610,3.8]],["t/766",[14,1.174,51,0.824,52,1.213,55,2.578,58,0.623,62,0.779,63,2.707,66,2.103,74,1.719,83,0.434,91,0.731,97,1.602,98,1.734,109,0.938,134,2.257,137,1.02,160,2.316,163,2.451,179,1.589,180,2.041,206,2.372,215,1.406,217,1.718,219,2.031,223,2.733,241,1.564,253,2.537,261,1.811,265,0.692,281,2.413,284,2.017,285,4.937,286,1.802,290,4.083,291,4.263,298,1.718,301,0.63,317,1.312,321,3.798,346,1.294,349,0.824,406,1.357,419,1.127,430,1.892,436,1.277,461,3.678,468,2.739,470,1.658,472,1.576,480,1.447,511,3.406,531,3.593,550,4.829,588,1.938,605,2.608,606,2.081,624,2.608,633,3.727,635,2.974,638,3.324,639,3.066,673,2.847,769,2.608,805,2.608,836,3.661,882,2.056,919,2.608,939,2.296,943,1.616,947,4.499,948,2.666,986,4.651,1014,1.877,1021,1.734,1031,3.874,1055,2.974,1119,4.773,1350,2.666,1476,4.78,1493,5.208,1495,3.331,1522,5.547,1572,2.8,1668,2.8,1671,2.877,1713,2.504,1824,2.108,2158,3.183,2593,6.329,3632,4.651,4593,3.736,4611,8.629,4612,3.736,4613,5.667,4614,3.324,4615,4.093,4616,4.093,4617,4.093,4618,4.093,4619,5.667,4620,6.209,4621,4.093,4622,4.093]],["t/768",[1,2.411,4,2.114,14,1.07,51,1.518,56,2.466,62,0.946,78,1.077,91,0.908,103,1.065,134,2.265,217,3.166,285,4.446,321,4.613,336,3.387,430,1.901,461,3.081,480,1.455,511,3.288,531,4.552,540,2.572,995,4.605,1024,3.494,1119,4.299,1174,3.741,1493,3.99,1522,5.53,1662,6.448,1675,5.251,1915,5.302,2158,5.864,2593,5.302,4611,8.559,4614,6.123,4623,7.541,4624,7.541,4625,7.541,4626,7.541]],["t/770",[14,1.1,51,0.99,55,1.63,62,0.616,63,2.143,66,1.994,74,2.536,83,0.42,91,0.998,107,1.171,130,1.608,134,2.671,138,1.991,160,1.834,162,1.448,175,2.264,179,2.761,206,3.192,223,3.131,242,2.208,253,4.522,254,0.895,277,2.862,281,6.525,285,2.899,301,0.756,317,1.502,321,5.602,340,1.055,344,3.068,392,4.193,406,2.358,411,1.337,430,1.24,445,2.143,468,2.326,470,2.881,480,1.372,511,3.168,567,4.633,588,2.328,624,3.132,635,3.406,643,3.042,828,2.328,867,2.027,947,5.153,1055,5.3,1150,6.108,1260,3.823,1288,3.457,1493,4.421,1515,4.438,1522,5.415,1801,4.204,1925,4.531,2593,3.457,2900,5.531,3137,4.204,4597,6.785,4611,5.153,4614,3.992,4627,4.917,4628,4.917,4629,4.917,4630,7.113,4631,4.917,4632,8.357,4633,4.917,4634,4.917,4635,4.917,4636,4.917,4637,4.917,4638,4.917]],["t/772",[14,0.523,51,1.873,55,2.257,62,0.914,63,2.217,66,1.426,74,2.257,77,1.205,83,0.334,87,3.241,91,0.636,101,2.926,107,1.212,113,3.967,115,2.655,134,2.657,138,2.06,145,1.508,157,1.735,175,1.619,179,1.975,196,2.147,205,3.209,206,1.943,207,4.644,212,0.911,215,1.747,225,1.226,231,1.675,261,0.936,275,1.63,276,2.854,277,3.108,281,4.298,285,4.298,292,3.956,334,2.097,345,1.854,359,1.773,368,2.097,378,2.999,387,3.112,396,2.173,407,2.597,411,2.936,422,1.652,430,1.283,461,2.978,475,2.769,480,0.981,511,2.522,524,4.224,565,3.66,805,3.241,828,3.452,919,4.644,950,2.332,975,4.862,1050,4.57,1055,4.08,1119,2.9,1288,3.577,1345,4.221,1475,3.81,1493,4.922,1522,4.034,1575,3.686,1586,2.043,1675,2.81,1941,5.549,1948,5.668,2053,4.349,2320,7.038,2324,5.125,2452,4.642,2593,5.989,2696,3.956,2900,7.657,3436,4.642,3580,4.642,4570,4.131,4597,4.131,4611,5.281,4614,4.131,4639,6.652,4640,6.652,4641,6.232,4642,7.289,4643,4.349,4644,5.087,4645,5.087,4646,5.087,4647,5.087,4648,7.289]],["t/774",[1,1.398,4,1.546,14,0.992,55,1.264,62,0.968,63,2.403,65,2.123,74,1.264,78,0.787,83,0.442,102,2.555,109,2.04,116,2.177,118,3.143,134,1.314,138,2.233,165,1.894,179,2.996,209,2.086,215,1.894,223,2.427,231,3.81,254,1.004,258,1.374,261,1.014,284,3.297,285,5.249,288,1.894,301,0.848,312,3.143,317,1.165,359,1.921,368,2.273,396,1.406,411,2.099,430,1.946,472,2.123,480,1.717,497,3.058,511,3.08,536,2.403,540,1.88,542,4.288,578,3.219,635,2.641,642,5.996,704,2.84,717,3.873,828,3.654,908,2.314,950,3.538,1174,2.735,1233,2.769,1345,2.502,1638,4.994,1675,3.046,1941,3.591,2320,3.677,2324,3.877,4611,8.448,4649,7.717,4650,7.717,4651,5.032,4652,8.569,4653,9.234,4654,5.514,4655,5.514,4656,5.514,4657,5.514,4658,5.514,4659,5.514,4660,5.032]],["t/776",[14,1.119,74,2.402,83,0.499,87,4.295,91,0.772,109,1.545,212,1.208,218,2.402,234,2.349,254,1.227,273,3.059,281,5.215,285,3.975,329,2.349,346,2.132,376,2.939,396,2.518,406,2.933,411,1.834,420,3.059,430,1.7,461,2.755,534,3.614,536,3.855,545,2.596,619,4.74,990,3.747,997,3.059,1055,4.727,1070,4.74,1428,3.428,1447,3.975,1450,4.496,1515,4.206,1638,6.477,2345,6.703,3211,5.242,4597,7.181,4651,6.152,4652,8.461,4653,8.959,4660,6.152,4661,6.742,4662,6.742,4663,6.742,4664,6.742,4665,6.742,4666,6.742,4667,6.742]],["t/778",[1,1.058,4,1.17,14,1.127,51,1.268,55,0.957,60,1.533,65,1.607,66,1.17,74,1.74,83,0.416,91,0.929,100,0.843,103,1.282,104,2.445,109,0.957,134,2.361,153,1.941,161,2.659,175,2.006,179,1.621,180,1.135,197,1.347,204,2.096,206,1.594,210,1.096,215,2.164,219,2.071,223,1.837,225,1.006,228,2.889,230,1.545,231,2.5,241,1.594,250,1.485,253,3.456,254,0.76,255,1.545,261,1.67,265,0.706,273,1.894,277,2.236,281,2.461,285,2.461,301,1.168,321,3.855,338,2.784,346,1.32,347,1.894,349,0.84,370,1.999,376,4.161,396,1.607,406,2.089,422,1.356,430,1.052,436,1.303,458,2.208,461,3.102,465,2.889,468,2.061,470,2.552,480,1.631,481,1.394,511,2.926,519,2.239,540,2.884,564,2.208,568,2.92,635,1.999,665,1.914,669,1.894,676,2.53,720,1.356,836,2.461,912,2.179,919,2.659,947,5.5,954,3.246,963,1.476,995,3.053,1014,1.914,1024,2.92,1039,2.831,1050,3.381,1055,3.018,1119,4.327,1161,5.193,1174,4.195,1343,3.569,1378,4.311,1448,2.719,1489,4.672,1493,3.334,1495,2.239,1522,4.945,1533,4.535,1534,4.917,1575,3.024,1620,2.342,1628,2.419,1671,1.934,1675,2.306,1935,3.024,1951,2.935,2470,3.246,2482,3.389,2593,2.935,3197,2.935,3247,3.569,3256,3.569,3632,4.72,3810,4.015,3857,3.024,4583,3.569,4595,2.935,4600,3.809,4611,6.916,4612,3.809,4613,3.809,4619,3.809,4668,4.174,4669,4.174,4670,4.174,4671,4.174,4672,4.174,4673,4.174,4674,4.174,4675,4.174,4676,4.174,4677,4.174,4678,4.174]],["t/780",[8,2.805,58,1.225,83,0.454,91,0.703,103,1.137,109,1.845,178,6.883,207,5.129,215,2.765,258,2.007,284,2.615,333,2.23,338,5.369,408,3.9,471,4.202,519,4.319,548,4.516,560,3.946,566,3.443,578,2.464,634,6.883,676,3.976,693,7.347,718,4.043,924,4.853,925,5.643,973,4.589,1012,5.369,1055,3.856,1455,6.537,1467,4.833,1522,5.077,1729,5.661,1730,5.369,3784,7.347,4639,7.347,4640,7.347,4679,7.347]],["t/782",[58,1.32,62,1.088,77,2.624,78,1.238,82,2.62,83,0.475,160,3.235,161,5.526,236,3.513,288,2.979,380,3.185,406,2.876,566,3.709,681,3.09,722,4.866,820,4.019,882,4.355,908,3.641,1522,5.432,2783,5.785,4679,7.915]],["t/784",[51,1.754,61,4.375,83,0.4,142,3.44,161,5.551,169,2.008,258,2.172,334,3.592,345,3.175,519,4.674,681,3.104,902,4.081,928,5.049,1245,4.488,1466,6.313,1522,4.918,1785,5.23,2371,8.433,4123,7.075,4680,7.449,4681,8.433,4682,10.386]],["t/786",[8,3.093,51,1.786,83,0.407,103,1.254,169,2.045,257,4.513,274,3.504,345,3.234,445,3.869,519,4.761,1141,5.537,1338,5.429,1522,5.296,2130,7.588,4123,7.206,4680,7.588,4681,7.206,4683,8.875,4684,8.099,4685,8.099]],["t/788",[58,1.296,66,2.388,83,0.47,189,4.936,200,3.177,209,1.996,234,2.968,261,1.567,277,2.509,452,2.767,477,2.403,479,3.202,521,2.173,566,3.643,578,2.607,650,4.747,932,6.917,933,6.917,940,5.022,1522,4.852,2126,4.779,4434,9.35,4686,8.518,4687,7.774,4688,8.518,4689,8.518,4690,8.518]],["t/790",[6,2.696,10,7.316,51,1.614,83,0.368,91,0.934,99,4.129,101,3.392,103,1.395,119,4.026,225,1.932,234,2.794,281,4.727,284,2.604,301,1.519,342,3.087,402,2.109,404,6.005,411,2.181,465,3.675,478,2.696,481,3.297,566,4.223,572,5.154,635,4.729,925,4.57,934,4.184,1055,4.729,1467,4.812,1522,4.676,1835,5.637,1840,5.002,4611,5.809]],["t/792",[48,3.262]],["t/794",[48,2.116,51,1.284,60,2.343,65,2.457,74,1.462,82,1.928,83,0.47,90,3.525,100,1.043,103,1.205,146,2.522,153,2.627,162,1.879,165,2.192,169,1.471,182,2.176,212,1.143,227,3.647,240,4.486,241,2.437,250,1.503,254,1.552,256,2.29,276,3.58,282,1.564,315,1.868,345,3.736,349,1.284,363,3.473,395,3.376,399,2.809,475,3.473,478,3.593,481,2.13,518,4.451,588,4.039,595,3.423,601,2.223,654,3.525,676,2.562,834,3.58,882,3.204,887,2.956,902,2.989,909,3.245,950,4.404,963,2.256,969,4.623,974,2.607,979,2.45,1028,3.903,1043,3.903,1057,4.861,1088,2.584,1233,4.282,1398,3.58,1520,4.486,1659,3.637,1808,4.623,1998,5.823,2018,4.156,2382,6.388,2568,4.962,2935,5.823,4691,6.381,4692,6.381]],["t/796",[1,1.736,5,2.468,6,1.568,55,1.069,59,1.873,60,1.713,61,2.342,71,2.259,76,5.558,77,2.117,78,0.666,82,2.068,83,0.471,98,1.976,100,0.954,127,3.111,139,1.143,142,1.841,144,2.179,146,2.698,163,3.201,169,1.075,183,2.434,191,1.958,206,3.097,221,3.23,230,1.726,238,5.558,254,1.246,265,1.51,274,1.841,282,2.331,286,2.053,287,1.495,301,0.717,306,2.8,307,3.967,315,1.365,317,0.985,329,2.385,338,3.111,345,2.494,380,3.651,394,2.468,396,1.19,400,3.672,402,0.919,410,1.857,418,2.209,430,1.726,438,2.593,445,2.033,451,2.209,469,2.402,478,3.199,479,1.753,517,3.787,518,4.232,519,2.502,556,1.418,582,4.682,585,3.279,589,1.826,595,3.672,623,2.209,632,4.176,676,1.873,681,1.662,694,2.8,841,5.166,843,2.617,880,2.539,894,1.687,909,2.372,917,2.161,922,3.627,927,2.434,935,4.961,943,1.841,949,3.279,950,2.138,956,2.259,963,3.159,964,2.342,966,4.9,976,3.038,979,2.056,983,2.91,1007,3.111,1016,4.565,1033,2.8,1043,2.853,1093,3.787,1125,2.014,1141,2.91,1145,3.988,1173,3.627,1208,3.84,1233,4.071,1234,3.627,1280,3.191,1282,2.853,1319,4.256,1398,4.549,1465,3.379,1629,4.256,2016,3.988,2036,3.787,2123,3.627,2367,4.256,2382,6.073,3186,3.787,3546,3.787,3677,4.256,4179,4.256,4389,3.988,4433,4.256,4693,4.664,4694,3.988,4695,4.664,4696,4.664,4697,4.664,4698,3.988]],["t/798",[1,1.914,2,2.531,6,1.797,51,2.02,62,0.67,65,2.907,71,3.657,77,1.266,78,0.763,82,2.281,83,0.517,100,0.967,103,0.755,108,2.867,117,1.835,134,1.799,139,2.145,144,1.701,146,1.982,153,2.325,162,1.574,169,2.017,182,1.822,196,1.574,202,3.334,212,0.957,219,4.977,221,1.932,236,2.164,241,2.883,250,1.779,254,0.973,258,1.332,259,4.91,265,0.904,276,2.998,279,3.757,282,1.85,287,1.712,296,2.352,307,3.097,315,1.564,340,1.147,342,2.058,349,1.076,376,2.329,399,2.352,400,2.867,411,1.454,429,2.908,430,2.53,431,2.559,438,3.317,439,2.683,446,3.757,450,4.339,452,2.452,462,3.208,474,1.918,477,1.507,478,3.9,543,3.498,556,2.661,576,3.655,589,2.955,605,3.404,717,2.146,733,3.085,821,3.94,834,2.998,888,3.265,890,3.269,894,3.165,943,2.11,962,3.208,1021,2.264,1043,3.269,1125,2.307,1128,3.564,1141,4.71,1174,2.651,1370,2.531,1650,4.155,1659,3.046,1671,2.476,1901,3.655,1967,4.339,2007,4.569,2210,3.404,2318,3.655,2696,4.155,2852,2.717,3451,3.481,4699,5.344,4700,5.344,4701,5.344]],["t/800",[6,2.451,14,0.523,48,1.687,51,1.467,55,1.671,56,2.786,58,1.109,60,2.677,61,2.555,62,0.638,71,2.464,74,1.166,77,1.205,78,0.726,83,0.334,84,2.555,91,0.444,100,0.914,107,1.212,137,1.268,139,1.247,160,1.897,162,2.147,163,2.878,168,6.232,169,2.269,172,2.83,180,1.384,210,2.237,212,1.306,225,1.226,230,1.883,236,2.06,249,1.147,258,1.817,265,0.86,275,1.63,282,2.28,287,1.63,298,2.135,301,1.31,307,2.948,315,2.134,335,3.686,340,1.092,345,1.854,348,3.112,353,1.756,363,2.769,372,3.174,380,1.868,394,3.856,402,1.436,418,4.405,419,1.4,420,3.307,421,1.29,422,1.652,430,2.346,436,2.275,438,2.762,445,2.217,458,3.856,461,3.801,478,3.445,479,1.912,480,0.981,528,4.131,530,2.62,556,1.547,566,3.117,579,4.131,581,3.577,582,3.48,589,3.335,632,2.62,694,3.054,718,2.555,841,3.241,843,2.854,894,2.636,902,2.383,909,2.587,914,2.769,925,2.9,930,3.956,935,2.308,938,3.393,941,2.493,963,2.578,979,1.507,1034,3.577,1045,4.642,1047,4.224,1057,2.9,1141,3.174,1148,3.112,1189,3.241,1233,4.671,1271,3.686,1332,3.241,1355,4.986,1519,3.054,1577,3.686,1578,2.769,1762,3.81,1803,4.644,2009,4.349,2119,4.642,2279,4.986,2519,4.298,3096,4.131,4702,4.642,4703,5.087,4704,5.087,4705,5.087,4706,4.642]],["t/802",[1,1.393,2,1.654,5,1.848,14,1.172,51,0.703,58,1.033,62,0.689,64,2.133,65,3.422,77,1.983,78,0.969,80,1.112,83,0.407,91,0.305,100,0.324,103,0.493,106,1.356,107,0.832,109,1.918,112,1.553,130,1.143,134,1.834,137,0.871,139,1.346,140,1.44,145,1.035,146,0.917,153,1.692,165,1.2,169,1.266,171,3.132,172,2.133,198,2.06,202,3.428,203,3.239,206,1.334,210,0.917,225,1.324,239,2.245,241,1.334,249,1.239,265,0.591,270,3.664,273,1.585,277,1.029,282,1.664,283,2.311,286,1.538,290,1.901,296,1.538,301,0.845,317,1.626,333,0.968,340,0.75,347,3.08,348,2.137,353,1.324,359,1.217,368,1.44,380,2.827,383,1.636,393,1.991,403,3.065,418,1.654,419,2.119,421,0.886,422,1.135,426,1.799,429,1.901,430,1.385,436,2.119,438,3.981,439,1.754,451,1.654,461,4.293,467,3.758,468,2.906,478,2.283,480,1.614,497,1.887,530,3.497,531,1.673,540,1.874,556,2.826,564,1.848,589,2.658,601,1.217,613,2.025,650,2.546,665,1.602,672,1.254,676,1.403,682,3.422,705,2.06,778,4.272,817,2.137,894,1.987,924,1.712,927,1.823,943,2.169,963,1.235,974,2.245,989,2.33,990,1.48,993,1.569,995,3.289,1022,2.531,1039,1.303,1083,3.188,1089,1.874,1104,2.617,1117,3.645,1125,1.508,1155,1.901,1228,2.717,1233,5.352,1240,3.188,1326,2.349,1348,1.823,1359,2.39,1362,2.137,1493,1.848,1516,1.776,1536,2.276,1569,3.188,1620,1.96,1870,4.697,1871,5.986,1939,2.531,1994,2.717,2059,1.991,2252,3.035,2295,2.837,2319,2.137,2333,5.412,2436,3.132,2519,5.677,2555,1.602,2574,2.987,2814,7.229,2895,2.33,3095,3.188,3297,2.987,4706,3.188,4707,3.494,4708,3.494,4709,3.494,4710,3.494,4711,5.086,4712,3.494,4713,3.494,4714,3.494,4715,5.494,4716,5.494,4717,5.014,4718,5.014,4719,4.697,4720,3.494,4721,3.494,4722,3.494,4723,2.987,4724,3.494,4725,3.188,4726,3.494,4727,3.494,4728,3.188,4729,3.188,4730,3.188]],["t/804",[14,1.205,51,1.281,55,1.459,58,0.644,59,1.698,62,0.53,67,1.642,69,2.124,77,1.813,83,0.351,137,1.054,145,1.254,149,3.616,175,2.436,180,1.151,209,1.491,214,2.955,227,1.809,235,3.86,239,1.728,250,0.997,258,1.054,261,0.778,275,1.355,282,1.037,291,2.003,301,1.177,309,3.065,317,1.616,333,1.171,340,0.908,351,2.587,418,4.032,419,2.107,422,1.374,426,2.179,430,1.605,461,4.54,467,2.893,477,1.193,480,0.816,497,2.924,519,2.269,522,2.269,524,2.451,540,1.442,578,2.342,594,5.235,650,3.945,665,1.939,681,2.726,687,2.974,697,2.974,718,3.196,774,3.891,925,2.411,928,2.451,959,1.88,979,1.316,990,4.064,1014,1.939,1016,2.821,1021,1.792,1042,2.337,1111,2.208,1117,3.014,1139,4.949,1153,2.893,1280,2.893,1289,2.974,1313,4.949,1326,2.722,1397,2.411,1419,3.616,1639,3.434,1770,5.808,2126,2.373,2129,3.616,2237,3.86,2279,2.893,2318,2.893,2333,2.974,2519,6.295,2555,1.939,2569,3.616,2724,3.86,2767,3.168,2814,3.289,3546,3.434,4652,3.289,4653,3.616,4702,3.86,4711,4.767,4717,5.808,4718,3.86,4719,5.442,4723,3.616,4728,3.86,4729,3.86,4731,4.23,4732,4.23,4733,7.653,4734,6.365,4735,4.23,4736,4.23,4737,4.23,4738,4.23,4739,6.365,4740,4.23,4741,4.23,4742,4.23,4743,4.23,4744,4.23,4745,4.23,4746,4.23,4747,4.23,4748,5.808,4749,4.23,4750,6.365,4751,4.23,4752,4.23,4753,4.23,4754,4.23,4755,4.23,4756,4.23,4757,3.86,4758,3.86,4759,3.86,4760,6.365,4761,4.23,4762,4.23,4763,4.23,4764,4.23,4765,3.86,4766,3.86,4767,3.86,4768,3.86,4769,3.86,4770,3.86,4771,3.86,4772,3.86,4773,4.23,4774,3.86,4775,3.86,4776,3.86,4777,4.23,4778,3.86,4779,3.86,4780,4.23,4781,4.23,4782,4.23,4783,4.23,4784,3.86,4785,3.86,4786,3.86,4787,4.23,4788,4.23]],["t/806",[6,2.481,14,1.23,51,1.485,52,1.533,55,0.742,56,2.413,58,0.983,59,1.3,60,1.189,62,0.81,63,1.412,64,1.625,65,3.106,77,1.911,82,0.978,83,0.43,100,0.301,101,2.534,103,0.731,106,1.257,109,0.742,124,2.095,134,1.758,137,0.807,140,1.335,142,1.278,144,1.031,145,0.96,153,0.997,162,0.954,163,2.042,169,1.192,171,1.846,172,1.257,177,1.763,206,3.284,209,0.759,221,1.171,225,1.778,228,1.485,230,1.198,231,1.066,234,1.128,239,1.323,249,1.166,250,0.763,254,0.589,256,1.162,265,0.548,282,1.268,283,0.972,284,1.68,288,1.112,291,1.534,301,0.796,303,0.948,308,1.944,309,2.346,315,1.514,317,1.093,322,1.385,329,1.803,333,0.897,340,1.111,342,1.247,349,1.3,363,2.816,376,1.412,419,2.031,426,1.668,430,2.499,440,1.981,461,3.688,468,1.059,472,1.247,474,1.162,476,1.817,477,1.822,478,1.089,497,1.112,521,0.826,526,1.944,536,2.255,540,1.104,554,2.518,556,1.964,605,4.116,628,3.748,630,2.63,665,1.485,672,2.648,679,2.346,681,1.843,682,1.44,687,2.277,706,2.277,717,2.077,718,1.626,720,1.052,821,1.69,882,1.626,894,1.171,898,4.513,943,1.278,979,1.07,982,1.606,1026,4.767,1036,1.385,1174,1.606,1233,1.626,1289,2.277,1397,1.846,1402,5.365,1403,2.955,1419,2.769,1456,1.817,1612,2.63,1687,3.296,1729,2.277,1730,2.16,1824,1.668,1874,2.998,1947,2.109,1960,2.769,1965,2.109,2125,1.909,2126,1.817,2129,2.769,2131,5.895,2139,2.063,2210,5.75,2279,3.539,2519,6.169,2814,4.023,3250,2.346,4262,2.63,4270,2.63,4278,2.769,4298,4.423,4652,2.518,4711,3.875,4719,5.523,4723,2.769,4725,2.955,4730,2.955,4748,2.955,4757,2.955,4758,2.955,4759,2.955,4765,2.955,4766,2.955,4767,2.955,4768,2.955,4769,2.955,4770,2.955,4771,2.955,4772,2.955,4774,2.955,4775,2.955,4776,2.955,4778,2.955,4779,2.955,4784,2.955,4785,2.955,4786,2.955,4789,3.238,4790,10.304,4791,3.238,4792,5.895,4793,8.065,4794,3.238,4795,3.238,4796,3.238,4797,3.238,4798,3.238,4799,3.238,4800,3.238,4801,3.238,4802,3.238,4803,3.238,4804,5.173,4805,3.238,4806,6.46,4807,6.46,4808,3.238,4809,3.238,4810,5.173,4811,3.238,4812,3.238,4813,3.238,4814,5.173,4815,3.238,4816,3.238,4817,3.238,4818,3.238,4819,3.238,4820,3.238,4821,3.238,4822,3.238]],["t/808",[1,1.293,5,2.699,14,0.525,51,1.027,52,1.512,56,2.388,58,1.111,59,2.048,64,2.294,65,1.964,77,2.207,82,1.541,83,0.47,85,4.757,91,0.637,100,0.792,103,1.032,109,1.169,111,1.691,117,1.752,140,2.103,144,2.325,146,1.918,150,2.561,172,1.98,198,3.007,200,2.724,204,2.561,206,2.79,209,1.195,241,1.948,249,1.923,255,2.703,258,1.271,261,1.343,283,1.531,303,1.493,311,1.635,315,1.493,317,1.543,380,3.42,395,2.699,402,1.681,419,2.564,421,1.852,422,2.771,448,3.322,461,2.984,468,2.388,472,1.964,474,1.831,478,3.448,534,2.084,541,2.031,545,1.964,556,1.551,589,1.997,591,2.122,597,3.586,628,3.696,635,3.498,643,2.181,665,2.338,672,1.831,676,2.933,720,1.657,766,3.821,820,2.363,838,3.586,843,2.861,872,2.202,876,5.557,888,2.724,917,2.363,934,2.662,943,2.014,944,1.902,946,2.268,956,2.471,979,2.233,1026,2.594,1035,3.038,1039,2.724,1046,3.489,1057,2.907,1105,2.956,1169,3.062,1174,2.53,1230,3.062,1233,2.561,1373,3.586,1402,3.183,1515,4.557,1539,3.062,2182,6.421,2554,5.47,2587,3.696,2814,7.243,2815,3.696,2836,3.402,3650,4.655,4643,4.361,4711,3.821,4823,5.101,4824,6.665,4825,5.101,4826,5.101,4827,4.655,4828,5.101,4829,4.655,4830,5.101,4831,5.101,4832,5.101,4833,5.101]],["t/810",[1,1.415,6,2.616,7,2.342,60,2.048,62,0.976,65,3.733,71,3.769,74,1.278,77,1.843,78,0.796,82,1.685,83,0.411,90,3.082,91,0.487,101,1.916,103,1.369,104,2.249,106,3.021,112,3.459,134,1.329,135,2.671,139,1.907,160,2.08,163,2.202,169,1.286,177,3.036,209,1.823,212,0.999,215,1.916,221,2.017,241,2.131,260,4.769,261,1.026,262,2.993,265,0.943,273,2.531,282,1.367,303,1.633,308,3.348,315,2.277,326,3.922,340,1.197,345,2.835,349,1.566,350,3.082,380,2.048,383,2.613,406,1.85,419,1.536,421,1.415,464,2.873,477,1.573,478,2.616,496,2.801,519,2.993,540,2.653,541,2.221,566,2.385,577,3.72,591,2.32,595,2.993,597,3.922,622,2.613,631,5.322,676,4.094,694,6.12,718,2.801,720,1.812,733,3.661,828,2.642,834,3.129,843,4.365,888,3.341,926,3.922,943,2.202,960,4.769,966,2.873,979,1.153,1055,2.671,1059,5.09,1113,3.082,1154,5.59,1190,4.759,1233,4.499,1262,3.129,1588,3.412,1776,5.637,1860,3.48,1895,4.769,2318,3.816,2481,5.09,2519,3.289,3681,6.652,4698,4.769,4711,4.178,4792,5.09,4827,5.09,4834,5.578,4835,5.09,4836,5.578,4837,5.578,4838,5.578,4839,5.578,4840,5.578,4841,5.578,4842,5.578]],["t/812",[51,1.707,83,0.469,90,4.685,100,1.019,104,2.451,282,2.078,301,1.687,315,2.482,402,1.671,478,3.688,595,4.55,717,3.405,805,5.403,828,4.016,841,5.403,935,3.848,963,2.999,966,4.368,979,1.753,1141,6.376,1233,4.258,1568,5.403,4843,8.48,4844,8.48]],["t/814",[48,3.262]],["t/816",[1,2.001,7,2.391,8,1.984,63,2.482,64,1.789,66,1.596,74,1.305,77,2.434,78,1.396,83,0.261,97,2.229,100,0.841,101,3.528,107,1.357,124,2.307,148,2.532,156,4.027,167,2.825,169,2.088,180,1.549,182,1.942,199,4.612,210,1.495,242,2.557,249,1.284,252,3.987,254,1.037,256,2.044,261,1.799,265,1.532,273,3.581,277,1.677,282,1.934,287,3.134,290,4.296,291,4.29,349,1.146,351,4.828,359,2.75,402,1.555,405,3.1,406,1.888,419,1.568,481,1.901,516,5.197,530,2.933,569,2.287,625,4.266,733,2.327,834,3.195,928,4.574,946,2.532,973,3.246,975,3.798,979,1.177,985,4.126,993,2.557,1024,2.639,1026,2.896,1069,5.912,1086,5.541,1088,3.197,1132,4.004,1148,3.483,1256,2.759,1398,3.195,1436,4.126,1516,2.896,1532,6.69,1536,5.141,1834,2.825,1840,6.799,1841,4.36,1843,5.361,1911,4.266,1948,4.428,2072,4.624,3740,7.203,3900,4.004,4845,4.869,4846,5.197]],["t/818",[1,1.885,5,2.765,6,2.499,77,2.45,78,1.519,83,0.488,97,2.046,100,0.69,101,2.552,103,0.738,114,3.304,130,1.709,137,1.303,139,2.308,141,2.593,142,2.063,144,2.366,160,1.949,162,2.189,163,2.063,169,2.17,179,2.029,180,2.022,199,4.468,215,1.795,221,1.89,225,1.259,242,2.347,249,1.178,252,2.301,254,0.951,257,2.658,261,1.367,265,1.462,287,2.771,290,2.845,298,3.119,301,0.804,315,1.53,340,1.122,377,3.787,398,3.38,402,1.03,405,2.845,407,2.647,408,2.532,409,2.301,410,3.443,419,2.046,440,3.197,445,2.278,466,2.532,525,5.566,540,1.782,560,2.562,588,2.475,635,2.503,648,2.235,676,2.099,683,3.787,769,3.33,927,3.879,948,3.404,974,2.136,993,2.347,1005,2.887,1012,3.486,1036,2.235,1043,3.197,1086,3.197,1088,3.503,1103,4.244,1148,3.197,1256,3.6,1282,3.197,1309,3.404,1516,2.658,1532,3.575,1536,4.841,1588,3.197,1651,3.915,1713,3.197,1729,3.675,1730,3.486,1835,3.675,1840,6.783,1842,3.197,1843,4.915,1844,3.879,1846,3.915,1896,5.203,2072,4.244,2280,3.575,2305,3.675,2752,4.77,3682,3.404,3900,5.225,4845,4.469,4847,5.227,4848,5.227,4849,4.77,4850,5.227,4851,5.227]],["t/820",[1,1.854,2,3.462,51,1.876,55,1.675,82,2.816,83,0.427,97,2.862,103,1.316,104,2.113,124,4.521,146,1.919,169,2.148,170,3.387,175,2.327,200,2.726,236,3.775,241,2.792,247,5.685,257,3.717,301,1.124,353,2.246,359,2.547,379,4.31,380,2.684,402,1.44,406,2.424,432,3.717,472,3.589,540,2.493,596,3.069,676,3.742,790,3.717,966,3.765,1046,5.001,1086,4.472,1141,5.815,1568,6.884,1840,6.402,1841,6.166,2280,5.001,3309,5.476,3999,6.671,4124,6.671,4852,6.25,4853,7.31]],["t/822",[14,0.831,64,2.54,91,0.706,146,2.606,191,3.394,208,4.685,212,1.449,214,2.49,277,2.381,349,1.627,380,4.297,386,6.287,387,4.946,521,2.062,682,3.594,694,5.959,780,5.658,943,4.24,964,4.06,1022,5.858,1088,3.275,1841,6.613,3887,6.287,4854,8.085,4855,8.085,4856,8.085]],["t/824",[62,1.208,317,2.036,380,3.539,480,1.859,1841,5.324]],["t/826",[14,0.978,83,0.346,91,0.908,111,2.501,124,4.216,160,4.202,203,5.604,210,1.98,253,3.081,261,1.915,271,4.613,276,5.333,298,3.166,303,2.207,329,2.628,331,5.864,466,3.653,553,3.361,556,2.293,629,3.532,660,6.448,1125,3.256,1456,5.333,1495,4.046,1568,6.632,1840,6.495,1841,5.751,1849,6.882,1869,6.123,2300,6.448,2305,5.302,3334,6.123,4857,10.497,4858,7.541]],["t/828",[14,0.962,51,0.901,55,1.026,62,0.561,63,1.952,66,1.255,77,1.876,78,1.13,82,1.353,83,0.429,91,0.855,103,0.633,119,2.249,130,2.172,137,1.656,145,1.327,160,1.67,172,1.739,175,1.425,179,1.739,180,1.218,197,1.445,199,5.338,206,1.71,215,2.281,225,1.079,237,2.011,252,4.313,253,1.83,254,1.441,256,2.384,258,1.974,261,0.824,265,0.757,273,2.032,274,1.768,277,2.332,284,2.157,286,1.971,294,2.314,301,1.218,312,2.552,317,0.946,340,0.961,342,1.724,349,1.337,353,2.361,371,1.933,405,2.437,407,1.595,422,1.455,432,2.277,433,2.011,434,3.482,436,2.073,477,1.873,479,1.683,480,1.281,481,1.495,496,2.249,511,3.604,521,1.694,541,1.783,553,2.349,623,2.121,624,2.853,629,2.097,633,3.987,642,2.552,704,3.421,705,2.64,707,2.552,797,3.148,828,3.145,836,4.668,887,2.075,995,4.747,1005,2.474,1026,2.277,1085,3.145,1086,4.063,1088,2.69,1245,2.306,1339,1.991,1346,4.43,1428,2.277,1466,3.244,1495,5.589,1532,7.515,1575,3.244,1601,3.482,1671,3.077,1834,5.279,1841,4.838,1842,2.739,1843,2.337,2181,2.987,2852,2.277,4217,3.482,4859,8.236,4860,4.478,4861,3.829,4862,7.111,4863,4.478,4864,4.086]],["t/830",[14,0.934,62,0.718,66,2.222,78,1.132,83,0.417,91,0.952,97,2.243,100,0.844,107,1.365,113,3.118,116,3.129,124,2.32,130,1.874,137,1.976,139,1.404,160,2.956,175,3.277,199,4.081,203,3.378,206,2.188,210,1.504,225,1.38,228,2.626,243,3.821,249,1.292,252,2.522,253,2.341,261,1.054,265,0.969,286,2.522,287,1.836,301,0.881,312,5.179,317,1.21,322,2.45,333,1.587,337,4.028,353,2.63,406,2.628,419,2.699,422,2.574,480,1.529,511,1.982,521,2.318,545,2.206,553,2.026,624,3.65,736,3.378,872,4.444,917,2.654,931,4.291,939,3.214,944,2.137,973,3.266,982,2.842,985,4.151,994,3.574,995,2.775,1086,3.504,1088,2.32,1493,3.031,1495,4.875,1532,5.422,1588,5.998,1841,6.239,1843,5.894,3944,4.898,4845,4.898,4859,6.435,4865,5.729,4866,5.729,4867,5.729,4868,5.729,4869,5.729]],["t/832",[14,1.026,66,1.477,77,1.249,78,1.24,83,0.458,91,0.93,100,0.489,101,2.984,107,1.781,130,2.445,139,1.291,175,2.765,180,2.033,182,1.797,189,3.054,199,2.367,204,3.754,209,1.235,253,3.054,254,1.361,265,1.469,273,4.289,277,1.552,284,1.712,301,0.81,303,1.542,312,3.004,317,1.113,340,1.131,346,2.364,349,1.903,405,5.145,406,2.88,407,1.877,422,2.428,447,3.947,468,1.723,481,2.496,511,2.586,521,2.644,642,3.004,828,2.496,836,3.107,872,4.081,917,2.442,950,2.416,956,2.553,974,2.153,993,2.367,995,3.621,997,2.391,1055,3.58,1097,3.224,1187,3.391,1245,2.714,1256,2.553,1309,3.433,1345,3.391,1346,3.515,1466,3.818,1495,5.845,1655,3.818,1841,6.207,1842,6.522,1843,6.297,1846,3.947,4217,7.351,4859,4.279]],["t/834",[1,0.642,4,0.71,5,1.34,6,1.423,14,1.006,51,1.282,55,1.623,58,0.829,62,0.531,64,1.329,66,0.71,74,0.581,77,1.509,78,0.909,82,0.765,83,0.494,91,0.743,93,2.18,100,0.848,103,0.77,104,1.223,107,1.008,109,0.581,111,0.84,113,1.379,114,1.126,115,3.696,118,1.444,130,0.828,137,1.359,139,1.876,142,1,158,1.356,160,2.033,163,1,169,0.975,172,0.983,175,2.027,180,0.689,182,1.443,183,1.322,196,0.746,199,5.015,206,2.924,208,2.453,209,1.492,210,1.111,212,0.758,214,0.78,221,1.53,223,1.115,225,0.61,228,1.161,236,1.026,241,0.968,242,1.137,250,0.597,254,0.77,258,0.631,262,1.359,265,1.294,275,0.812,277,1.605,279,1.781,283,0.76,284,0.823,286,1.115,287,1.356,291,1.2,298,1.063,300,1.733,301,0.39,303,1.239,310,2.823,311,1.356,312,3.63,315,2.493,317,0.535,320,2.312,323,5.106,329,2.668,340,1.828,341,2.312,342,0.975,348,3.895,349,0.51,353,1.534,383,2.553,393,1.444,396,1.39,398,3.444,402,0.834,405,1.379,406,3.028,410,2.535,432,1.288,434,3.291,435,3.17,445,1.104,446,1.781,464,2.18,465,1.161,466,1.227,472,0.975,474,0.909,477,2.159,479,1.591,480,0.489,511,0.876,519,1.359,521,2.072,527,1.835,540,0.864,553,0.896,566,1.083,568,1.174,569,1.699,578,0.775,587,1.97,589,1.657,590,1.174,591,1.054,596,1.776,601,1.475,605,1.614,606,1.288,624,1.614,631,1.733,638,2.057,645,2.412,650,1.174,660,3.618,671,2.3,673,1.94,700,1.689,718,1.272,722,1.421,828,1.2,880,2.303,881,1.781,883,2.495,902,1.186,919,5.176,927,2.209,943,1,946,1.126,959,1.126,964,2.125,993,1.9,1023,1.65,1025,1.521,1028,1.549,1055,2.027,1062,2.976,1077,2.757,1085,2.004,1088,1.026,1101,1.897,1126,2.057,1154,1.58,1270,3.17,1282,1.549,1345,1.149,1346,3.635,1359,2.895,1367,1.614,1484,2.166,1495,5.563,1519,1.521,1522,3.625,1532,6.053,1539,1.521,1560,1.614,1575,1.835,1588,1.549,1643,2.057,1659,1.444,1669,1.835,1671,1.961,1741,1.897,1834,4.389,1841,5.899,1843,4.618,1891,1.305,1916,2.166,1947,1.65,2008,1.521,2151,3.8,2153,2.312,2325,1.689,2430,1.733,2569,2.166,3239,2.312,3334,3.436,3602,6.915,3689,1.689,3842,2.54,4030,1.97,4068,2.166,4140,4.426,4443,2.312,4857,3.862,4859,2.057,4861,2.166,4870,2.312,4871,2.533,4872,4.232,4873,2.533,4874,2.533,4875,2.533,4876,2.533,4877,2.533,4878,4.232,4879,2.533,4880,2.533,4881,2.533,4882,2.533,4883,4.232,4884,2.533,4885,2.533,4886,2.312,4887,2.533,4888,3.862,4889,2.533,4890,2.533,4891,2.533,4892,2.533,4893,4.232]],["t/836",[51,1.655,56,2.69,97,3.22,103,1.417,142,3.247,236,3.331,250,1.938,261,1.513,315,2.407,334,3.391,340,1.766,342,3.167,380,3.02,396,2.098,398,3.6,408,3.984,435,6.16,560,4.031,580,3.551,880,4.476,902,4.698,907,4.236,944,3.067,973,4.688,1088,3.331,1154,5.131,1841,5.979,1891,4.236,1917,6.16,2210,5.24,2852,4.182,4425,8.145,4894,8.224]],["t/838",[1,1.061,4,1.173,14,0.782,51,0.842,58,0.961,60,1.536,63,1.823,66,1.173,77,0.991,78,1.458,80,1.332,83,0.417,91,0.865,93,5.571,96,2.941,100,0.97,101,2.908,107,1.811,118,4.333,139,1.025,165,1.437,169,1.455,175,2.42,199,1.879,200,3.389,210,1.098,212,0.749,230,1.548,249,1.423,254,0.761,256,1.501,258,1.043,261,0.769,262,2.244,265,1.285,286,1.841,287,1.341,317,1.606,322,1.789,329,1.458,336,1.879,342,1.611,349,0.842,353,1.832,359,1.458,384,3.722,398,1.501,402,1.244,406,1.387,410,4.159,419,2.502,438,2.88,445,1.823,451,1.981,454,2.559,481,2.538,512,3.938,524,2.424,531,2.003,536,1.823,560,2.05,654,2.311,665,1.918,676,1.68,716,3.031,769,2.665,888,1.56,902,1.959,917,1.938,946,3.764,950,1.918,956,2.026,979,1.572,1025,2.511,1026,2.127,1050,2.244,1053,2.725,1233,2.101,1256,4.804,1309,2.725,1316,4.743,1466,4.573,1495,2.244,1522,1.981,1689,3.031,1730,2.79,1803,2.665,1834,3.131,1841,6.185,1842,6.705,1843,5.557,1846,3.133,1891,2.155,1896,5.64,1947,2.725,2181,2.79,2386,5.558,2389,2.941,2400,2.862,2414,2.941,2430,5.2,2696,3.253,3519,4.728,3841,2.941,3900,2.941,4864,5.76,4870,3.818,4895,4.183,4896,3.818,4897,6.312,4898,4.908,4899,3.397,4900,3.397,4901,3.397,4902,3.397,4903,3.397,4904,3.577,4905,3.397,4906,4.183,4907,4.183]],["t/840",[8,2.308,55,1.518,74,1.518,83,0.449,91,0.763,97,2.593,100,0.615,103,1.382,107,2.332,115,3.457,142,2.615,157,2.259,163,2.615,170,3.069,199,2.975,209,1.552,236,3.541,254,1.206,261,1.608,283,1.989,284,2.152,303,1.939,312,3.776,323,6.527,333,1.835,334,2.731,363,3.606,402,1.305,405,3.606,409,2.916,410,2.637,422,2.152,479,2.49,530,3.412,540,2.259,548,3.716,588,3.137,589,2.593,676,3.93,841,4.221,902,4.095,917,3.069,923,4.8,924,3.247,937,4.658,939,3.716,944,2.471,1055,3.173,1077,4.315,1088,2.683,1154,4.133,1522,3.137,1532,7.401,1539,3.977,1575,4.8,1713,4.052,1730,4.418,1735,5.664,1736,5.664,1834,4.337,1840,4.133,1841,5.977,1843,3.457,1891,4.503,2747,5.664,4425,5.379,4641,5.664,4685,6.045,4888,7.979,4908,6.625]],["t/842",[6,1.331,14,1.22,62,0.496,66,1.109,78,0.565,83,0.277,91,0.898,107,1.442,115,2.066,118,4.188,135,1.895,158,1.939,160,2.257,175,2.619,179,1.537,199,5.061,204,1.987,206,3.143,207,2.521,234,1.379,237,1.777,253,1.617,273,2.746,275,1.268,277,3.143,283,1.188,284,2.673,301,0.609,312,5.054,315,1.158,349,1.785,353,1.458,368,1.631,371,1.709,392,2.333,422,2.387,430,0.998,433,1.777,434,3.077,477,1.707,481,2.021,511,3.235,521,2.623,530,2.038,536,1.725,624,3.856,633,2.376,635,1.895,642,4.188,651,1.692,820,1.834,828,1.874,836,3.568,872,3.172,883,2.333,887,1.834,1055,1.895,1085,2.866,1086,3.702,1187,1.796,1270,2.964,1346,5.488,1495,6.529,1532,6.396,1557,6.574,1671,3.405,1793,3.077,1834,4.398,1841,5.543,1842,3.702,1843,5.964,2318,2.707,2852,2.012,3136,3.384,3334,3.213,3341,2.964,4217,3.077,4861,3.384,4862,3.213,4909,3.958,4910,3.958,4911,3.958,4912,7.348,4913,3.958,4914,8.229,4915,3.958,4916,3.958,4917,3.958,4918,3.958,4919,3.958,4920,3.958,4921,3.958]],["t/844",[8,2.968,55,1.952,103,1.447,161,5.427,236,3.45,284,2.767,323,5.681,334,3.512,403,3.391,436,2.658,470,3.45,519,4.57,566,4.382,588,4.034,722,4.779,924,4.175,976,5.549,1027,7.283,1047,4.936,1169,5.113,1190,5.211,1254,7.283,1841,6.071,2133,6.624,3247,7.283,4922,8.518,4923,7.774]],["t/846",[4,2.286,62,1.022,77,1.932,83,0.374,99,4.2,101,2.801,124,3.303,169,1.879,199,3.662,209,1.911,256,2.927,265,1.379,301,1.254,340,1.75,398,3.581,402,1.607,410,3.246,454,4.988,566,3.487,572,4.256,665,3.738,880,4.438,1088,4.042,1105,4.725,1171,3.905,1282,4.988,1332,5.195,1532,5.578,1568,5.195,1840,5.088,1841,5.956,1843,5.208,1844,4.256,2151,4.375,3900,5.733,4924,8.154]],["t/848",[48,3.262]],["t/850",[14,0.964,51,0.906,55,1.528,58,1.014,62,0.564,64,1.413,67,1.747,71,2.179,78,1.253,83,0.403,91,0.971,100,1.045,108,2.414,109,1.031,112,2,116,2.631,117,1.545,157,2.273,196,1.325,197,1.451,206,2.546,210,1.75,211,3.347,212,1.194,214,1.386,227,1.924,237,3.942,249,1.014,254,1.213,255,2.466,256,3.364,262,4.259,265,0.761,273,2.041,305,2.317,315,2.873,329,1.568,340,2.287,347,4.252,349,0.906,352,2.449,353,1.606,359,1.568,385,3.499,392,4.681,402,1.564,411,1.813,418,3.76,419,2.417,420,2.041,421,2.013,429,2.449,439,3.347,469,2.317,497,2.289,522,2.414,534,1.838,541,1.791,621,3.078,622,5.268,669,3.983,681,1.603,717,1.807,719,4.106,733,2.723,820,2.085,940,2.653,979,2.203,993,2.02,1035,1.871,1089,2.414,1118,2.807,1125,2.877,1230,2.701,1352,3.499,1356,6.788,1365,2.607,1368,2.524,1376,3.863,1407,4.259,1559,2.866,1668,3.078,1721,3.078,1790,4.686,1824,3.433,1939,3.26,2206,2.752,2213,3.653,2436,3.799,2783,3.001,3175,3.163,3271,3.001,3358,3.682,3408,3.37,3410,2.866,3453,2.317,3476,5.699,3524,3.847,3567,4.106,3822,3.847,4533,4.106,4925,4.106,4926,4.499,4927,6.665,4928,4.499,4929,3.847,4930,4.499,4931,6.665,4932,4.499,4933,4.499]],["t/852",[1,1.052,14,1.072,51,1.697,55,0.95,62,0.948,66,1.163,78,0.592,80,1.32,83,0.386,89,2.587,91,0.889,100,0.841,103,0.586,107,1.494,109,0.95,117,1.424,134,2.008,137,2.373,204,4.546,212,1.123,225,0.999,237,2.816,248,3.764,251,2.916,254,0.755,255,2.32,258,1.034,276,2.326,282,1.853,283,1.882,294,2.185,296,2.76,315,2.65,317,1.325,333,1.737,336,2.816,339,2.057,340,2.237,342,2.415,345,1.511,346,1.983,353,1.822,359,1.445,368,2.585,387,3.836,392,2.445,400,2.225,419,1.142,421,2.582,430,2.282,458,2.194,468,1.356,474,1.488,477,1.17,480,0.8,521,1.599,541,1.651,548,2.326,568,1.921,569,1.665,622,2.937,629,2.937,643,1.773,646,2.363,671,1.347,673,3.864,733,2.562,752,2.32,843,2.326,867,1.71,882,2.082,944,2.339,976,2.701,979,1.968,982,2.057,1021,1.757,1062,4.409,1077,4.925,1089,2.225,1174,2.057,1246,3.005,1278,2.916,1407,2.225,1489,3.464,1542,3.784,1677,7.35,1690,3.995,1713,2.537,1831,2.916,1874,2.403,2173,2.537,2309,3.225,2556,3.367,2558,3.005,2743,4.876,2836,2.766,3112,7.692,3171,4.876,3191,4.876,3197,2.916,3273,3.697,3353,4.409,3433,4.876,3434,3.367,3435,3.367,3441,5.361,3442,7.74,3453,2.136,3459,3.545,3460,3.545,3461,3.545,3462,6.465,3463,3.545,3464,3.545,3465,3.784,3466,3.784,3472,3.784,3476,3.545,3602,5.092,4240,5.722,4442,3.545,4925,3.784,4934,4.147,4935,4.147,4936,4.147,4937,4.147,4938,4.147,4939,4.147,4940,4.147,4941,4.147,4942,4.147,4943,4.147,4944,4.147,4945,4.147]],["t/854",[14,0.871,64,2.661,83,0.522,84,3.172,87,4.025,91,0.929,106,2.453,145,1.872,166,3.297,191,2.652,197,2.038,212,1.518,237,4.292,239,2.581,254,1.542,261,1.162,262,3.389,315,2.479,328,2.727,340,2.052,346,1.998,349,1.924,353,2.303,394,3.342,402,1.245,430,1.593,438,2.394,473,2.992,477,1.782,556,1.921,646,2.375,649,3.212,654,3.49,672,3.43,681,3.638,715,3.864,733,2.581,817,3.864,1036,2.702,1125,4.126,1171,3.025,1212,5.397,1723,6.878,1894,3.864,1952,4.732,1971,4.442,1976,4.912,2184,8.114,2253,4.115,3210,5.182,3274,3.864,3404,3.864,4946,6.317,4947,6.317,4948,6.317,4949,9.557,4950,6.317,4951,6.317]],["t/856",[14,0.872,83,0.469,89,5.291,91,0.74,100,0.788,169,1.954,180,2.307,254,1.86,268,4.487,282,2.078,353,2.043,407,3.021,469,4.368,526,5.091,534,3.465,649,2.363,672,3.044,681,3.64,979,2.113,1042,4.685,1496,4.55,1497,6.595,1723,6.886,3417,9.325,3668,7.185,4952,8.48]],["t/858",[5,1.845,6,1.172,14,1.132,52,2.01,56,2.218,62,0.437,65,1.343,66,0.978,71,1.689,78,0.968,82,2.049,83,0.407,89,2.176,91,0.73,100,0.777,101,1.198,103,0.493,115,2.863,116,3.505,118,1.988,131,3.975,139,1.344,153,1.69,158,1.758,166,1.82,169,1.927,180,1.492,196,1.027,197,1.125,206,1.332,209,0.817,212,0.983,219,1.73,224,2.956,250,0.822,254,1.4,255,2.51,256,1.252,259,1.956,262,1.871,269,2.712,270,6.758,274,2.166,288,1.198,292,4.266,303,1.606,315,1.606,340,1.178,353,1.634,368,2.796,392,2.056,400,1.871,403,1.388,411,2.81,418,2.598,419,2.117,420,1.582,421,1.72,422,1.782,436,1.712,452,1.133,468,1.14,469,2.826,477,0.984,479,1.311,517,2.831,521,1.399,534,1.425,553,1.233,556,1.06,580,1.505,586,6.502,596,1.464,606,2.79,607,3.179,622,1.633,640,2.612,646,2.156,669,1.582,681,2.979,682,2.439,689,2.271,700,2.326,717,1.4,730,3.182,733,1.425,749,8.781,817,2.133,821,2.863,872,2.368,894,1.261,974,1.425,979,2.23,990,2.324,993,1.566,995,3.725,1000,2.326,1021,2.324,1029,2.452,1034,2.452,1042,1.926,1062,2.452,1157,2.612,1165,2.133,1326,1.491,1352,2.712,1365,2.021,1368,3.078,1493,1.845,1516,1.773,1557,3.857,1669,2.527,1671,1.616,1776,6.058,1777,5.006,1790,5.879,1824,4.956,1896,1.926,1973,2.712,2018,2.271,2295,4.454,2436,1.988,2558,4.914,2694,2.831,2852,1.773,2862,4.69,2994,2.981,3183,3.182,3358,1.926,3543,2.527,3682,2.271,3918,2.981,4258,2.981,4835,3.182,4953,10.902,4954,9.289,4955,3.487,4956,3.487,4957,3.487,4958,3.487,4959,3.182,4960,3.487,4961,7.69,4962,6.782,4963,3.487,4964,3.487,4965,3.487,4966,3.487,4967,3.487,4968,3.487,4969,3.487,4970,5.486,4971,3.487,4972,3.487,4973,3.487,4974,3.487,4975,3.487,4976,3.487,4977,3.487,4978,3.487,4979,6.782,4980,5.486,4981,3.487,4982,3.487,4983,3.487]],["t/860",[5,2.156,6,2.809,14,1.209,56,2.024,62,0.511,71,4.048,83,0.434,91,0.905,100,0.379,103,0.576,104,1.178,107,1.475,109,0.934,116,1.609,117,3.086,134,0.971,137,1.016,139,0.999,158,1.983,163,1.609,182,1.39,196,2.461,210,1.07,211,3.108,212,0.73,214,1.255,215,1.4,254,0.742,255,2.769,258,2.083,261,0.75,271,3.786,274,1.609,277,1.201,283,1.224,287,1.306,295,2.287,303,1.193,315,3.484,340,1.929,345,1.485,349,1.246,359,1.42,394,4.42,409,1.794,419,1.122,422,2.01,427,2.022,433,1.83,436,1.931,452,2.43,463,5.025,468,1.333,477,1.15,480,0.786,521,1.579,534,1.665,541,1.623,580,1.76,591,1.695,605,4.767,622,1.909,646,2.342,649,2.503,669,3.395,672,3.528,681,2.205,717,2.485,883,2.403,894,3.941,910,3.053,974,1.665,979,1.857,990,1.727,998,2.655,1014,1.869,1031,2.543,1071,2.655,1079,2.837,1187,1.849,1348,2.127,1380,3.786,1402,6.133,1456,5.04,1671,1.889,1675,2.252,1687,5.323,1824,3.854,1917,3.053,2252,4.616,2253,2.655,2279,2.788,2284,4.636,2555,2.837,2676,3.72,3210,2.493,3451,2.655,3453,3.854,3475,3.17,4984,8.97,4985,3.72,4986,7.482,4987,4.076,4988,4.076,4989,4.076,4990,4.076,4991,4.076,4992,4.076,4993,6.189,4994,4.076]],["t/862",[1,1.602,6,1.053,14,1.107,48,1.039,51,1.014,55,1.154,56,2.066,58,1.409,60,1.15,62,1.117,64,2.273,65,3.68,66,1.412,67,1.956,74,1.659,78,0.902,80,0.997,83,0.45,89,1.954,90,1.73,91,0.834,100,0.468,101,3.181,103,1.12,104,0.905,106,1.956,111,1.039,112,1.392,117,1.076,118,4.829,139,1.234,140,2.076,144,1.603,146,0.822,153,0.965,156,1.392,158,2.319,169,0.722,179,1.216,182,1.068,196,0.923,197,1.01,200,1.168,208,2.919,209,0.734,210,1.322,212,1.132,218,1.116,225,0.755,228,1.436,229,3.887,231,1.659,239,2.058,241,1.196,250,1.187,254,1.15,256,1.808,258,0.781,263,1.954,282,2.182,283,1.512,288,1.076,289,5.161,296,1.379,305,2.594,315,1.474,340,0.672,345,1.141,349,0.63,364,1.68,399,1.379,402,0.617,403,2.005,406,1.039,411,0.852,419,0.862,426,2.594,427,1.554,430,2.473,433,1.407,439,1.573,452,1.017,458,4.196,461,1.28,465,1.436,474,1.124,477,2.041,480,0.604,497,1.076,518,1.635,529,2.436,540,1.068,541,1.247,557,1.573,576,3.446,578,1.541,589,1.226,590,1.451,591,1.303,607,1.815,646,1.771,650,1.451,651,3.095,669,2.866,681,2.825,691,1.88,704,1.613,718,2.529,734,1.954,768,4.442,790,1.593,821,1.635,867,1.291,888,1.168,894,1.133,902,1.467,943,1.988,944,1.879,946,3.218,964,1.573,979,1.306,982,2.498,983,1.954,990,1.327,1036,2.702,1039,1.168,1057,1.785,1129,1.757,1150,2.089,1212,3.209,1326,3.808,1402,1.954,1517,1.954,1586,1.258,1782,1.785,1794,3.649,1874,1.815,1879,5.877,2018,3.281,2210,6.985,2231,2.436,2302,2.269,2483,2.858,3234,3.917,3250,5.244,3451,2.04,3475,2.436,3556,2.678,3682,2.04,3850,5.244,4059,2.543,4213,2.543,4263,2.436,4270,4.09,4328,2.678,4354,4.306,4355,2.858,4356,2.858,4369,2.858,4371,2.858,4465,2.858,4595,2.202,4995,3.132,4996,5.037,4997,9.558,4998,5.037,4999,5.037,5000,5.037,5001,5.037,5002,5.037,5003,5.037,5004,3.132,5005,3.132,5006,3.132,5007,3.132,5008,3.132,5009,3.132,5010,3.132,5011,5.037,5012,3.132,5013,3.132,5014,3.132,5015,3.132,5016,3.132,5017,3.132,5018,3.132,5019,3.132,5020,3.132,5021,3.132,5022,3.132,5023,3.132,5024,3.132,5025,3.132,5026,3.132,5027,3.132,5028,3.132]],["t/864",[4,1.832,14,1.108,60,2.399,62,1.219,63,2.848,64,3.053,74,1.497,83,0.475,91,0.848,100,0.607,117,2.244,138,3.509,144,3.094,146,2.906,153,2.012,158,2.094,196,1.924,200,3.625,212,1.171,221,2.363,249,1.473,282,1.601,349,1.315,350,3.609,394,3.457,411,2.93,419,1.799,436,2.039,451,3.094,452,2.122,521,1.666,543,3.027,590,3.027,600,4.594,646,1.832,943,2.579,959,2.905,964,3.281,979,2.14,983,4.077,1033,3.922,1036,3.705,1157,4.894,1568,4.163,1667,4.256,1758,3.457,1824,3.365,2018,4.256,2068,5.305,2111,7.407,2163,4.894,3475,5.081,3897,8.404,5029,8.664,5030,6.534,5031,6.534,5032,6.534,5033,6.534,5034,6.534,5035,6.534,5036,6.534,5037,6.534,5038,6.534]],["t/866",[1,1.759,14,0.713,62,1.329,64,2.831,66,1.945,78,0.991,83,0.413,91,1.041,98,2.939,104,2.006,106,2.694,124,2.81,144,2.208,166,3.621,208,5.222,212,1.243,214,2.137,230,2.567,250,2.123,296,3.054,328,2.995,380,3.675,394,3.67,402,1.367,419,1.91,430,2.272,433,3.116,478,2.333,479,3.388,589,2.716,646,1.945,674,3.574,733,3.682,938,6.01,943,2.739,944,2.588,979,1.863,1036,4.28,1039,2.588,1071,4.519,1125,2.995,1171,3.323,1326,2.967,1397,3.955,1568,4.42,1824,3.574,1952,5.197,1971,4.878,1976,5.395,2008,4.165,2018,4.519,3475,5.395,3546,5.633,3897,5.633,4459,6.331,5039,6.938]],["t/868",[51,1.32,60,2.407,62,1.089,66,1.838,78,1.39,83,0.398,91,0.758,100,0.609,157,2.236,158,2.783,169,2.244,196,1.931,197,2.115,206,2.504,231,2.159,234,2.285,239,2.679,244,4.911,274,2.588,287,2.101,311,2.783,312,6.62,368,3.58,422,2.13,430,1.653,468,2.144,477,2.923,479,2.465,569,2.633,589,2.567,629,3.071,646,2.434,682,2.915,684,3.622,894,2.371,908,2.752,956,3.176,979,1.355,995,3.176,1000,4.373,1148,4.01,1518,4.75,1592,4.75,1628,6.006,1751,5.983,1871,5.098,2555,3.006,3153,8.416,3230,7.906,3234,5.098,3453,4.473,3730,5.983,5040,6.556,5041,6.556,5042,6.556,5043,6.556,5044,6.556,5045,6.556,5046,6.556,5047,6.556,5048,6.556,5049,6.556,5050,6.556,5051,6.556,5052,6.556,5053,6.556,5054,6.556]],["t/870",[52,2.134,58,1.096,62,0.903,78,1.318,83,0.33,91,0.969,100,1.032,138,2.916,139,2.262,163,2.842,175,3.42,197,2.323,210,1.89,223,3.169,229,3.529,237,4.145,249,1.623,277,3,287,2.307,301,1.107,353,2.224,396,2.74,421,2.583,470,2.916,472,2.772,473,4.371,622,3.372,642,4.104,663,3.301,665,3.301,671,3.49,998,4.69,1125,3.108,1407,4.952,1588,5.646,1638,4.039,1891,3.708,1965,4.69,2850,7.494,3197,7.163,3453,3.708]],["t/872",[62,1.354,84,4.674,162,2.742,215,3.197,399,4.097,474,3.341,720,3.023,1039,3.472,1825,7.558,5055,7.958,5056,9.308]],["t/874",[1,1.657,62,1.086,66,1.832,67,2.537,71,3.165,77,2.453,78,0.933,82,1.974,83,0.397,100,1,109,1.497,116,2.579,117,3.555,128,3.852,139,1.601,146,2.275,156,2.905,162,2.552,196,1.924,212,1.171,218,2.328,227,3.705,255,2.418,258,1.629,261,1.202,263,4.077,311,3.115,315,2.536,352,3.556,359,2.277,394,3.457,402,1.287,409,2.876,411,1.777,451,4.103,474,3.11,578,1.999,590,4.014,635,3.129,643,2.794,654,3.609,669,4.41,717,2.624,733,2.67,752,3.206,780,3.724,812,6.737,888,2.437,979,2.227,1035,2.718,1051,5.081,1071,4.256,1077,4.256,1111,4.522,1171,3.129,1332,4.163,1377,5.586,1402,4.077,1531,3.457,1821,5.305,1824,3.365,2112,4.077,2182,6.331,2815,6.277,3112,4.594,5057,6.534]],["t/876",[1,2.583,6,2.836,48,2.081,51,1.263,55,1.438,65,2.417,77,1.998,83,0.288,90,3.467,100,0.987,103,1.191,104,1.814,107,1.495,146,2.501,165,2.155,166,3.275,167,3.113,176,4.088,183,3.275,215,2.897,223,2.762,224,2.736,227,2.684,250,1.479,261,1.154,265,1.426,282,1.538,283,1.884,315,1.837,317,1.326,345,2.287,353,2.032,396,1.601,399,2.762,401,4.701,402,1.237,409,2.762,436,1.958,445,2.736,472,2.417,474,2.252,479,3.17,518,3.275,560,3.076,590,2.908,601,2.939,614,4.701,676,2.52,790,3.191,834,3.521,908,3.54,912,3.275,917,2.908,921,3.998,935,4.322,963,2.219,974,3.446,979,2.197,980,4.547,1007,4.186,1028,3.839,1033,3.767,1183,5.494,1187,2.847,1188,4.88,1200,3.7,1208,3.521,1233,3.151,1299,4.088,1339,2.79,1531,3.32,1651,4.701,1724,4.413,1991,5.727,2018,4.088,2206,3.839,2767,4.701,3297,5.366,3843,5.366,4004,5.366,4698,5.366,5055,5.366,5058,6.276,5059,6.276,5060,6.276]],["t/878",[4,1.95,8,1.662,14,1.179,62,1.204,66,1.337,67,1.852,78,1.544,82,1.441,83,0.474,100,1.085,110,3.455,113,2.596,138,1.932,144,1.518,146,2.156,153,2.143,157,1.626,163,2.746,175,1.518,201,2.863,209,1.117,211,3.493,212,1.246,221,1.725,234,1.662,237,2.142,250,1.124,254,0.868,255,2.574,264,3.572,282,1.169,284,1.549,286,2.099,296,2.099,301,0.733,311,3.212,349,1.4,353,1.149,394,2.523,399,2.099,411,1.297,419,1.313,430,1.202,451,2.258,474,1.712,481,1.592,541,1.899,596,2.92,682,2.12,699,4.341,752,1.765,766,5.211,817,2.917,838,3.353,842,4.032,894,1.725,943,1.883,944,1.779,979,2.44,1085,4.275,1114,2.976,1230,2.863,1362,2.917,1376,2.764,1384,2.489,1566,2.456,1578,5.225,1587,3.872,1592,3.455,1648,2.863,1668,3.262,1690,3.038,1841,2.635,1843,2.489,2103,3.872,2107,3.455,2154,4.077,2173,5.522,2182,7.159,2815,5.95,3358,2.635,3561,4.077,3634,5.649,3857,3.455,4063,4.352,4447,4.352,5055,4.077,5061,4.769,5062,4.769,5063,4.769,5064,4.769,5065,4.769,5066,4.769,5067,4.769,5068,4.769,5069,6.957,5070,4.769,5071,4.769,5072,4.769,5073,4.769,5074,4.769]],["t/880",[7,4.313,8,2.982,55,1.961,83,0.392,100,0.954,134,2.039,137,2.133,157,2.918,230,3.166,284,2.779,301,1.316,340,1.837,346,2.706,377,6.199,398,3.071,402,1.686,408,4.145,452,2.779,474,3.071,648,3.659,649,2.384,672,3.071,844,7.808,944,3.191,979,1.769,1171,4.098,3112,6.016,4409,7.808]],["t/882",[48,3.262]],["t/884",[6,2.251,52,1.984,77,2.086,83,0.511,93,5.067,100,0.914,107,1.595,153,2.062,160,4.156,162,1.972,175,2.131,250,1.577,260,7.527,265,1.132,271,4.095,288,2.299,301,1.03,303,1.959,353,1.613,406,2.22,407,2.385,410,3.916,451,3.17,480,1.291,521,1.707,774,3.404,917,3.102,963,2.367,964,3.362,972,4.36,991,5.102,1034,4.707,1066,5.609,1073,4.707,1088,3.566,1188,5.206,1245,3.448,1256,3.243,1316,7.088,1844,5.134,1896,5.773,2026,4.707,2061,5.609,2710,4.707,3901,6.379,3935,7.633,5075,7.987,5076,7.527,5077,6.594,5078,6.109]],["t/886",[6,2.877,84,4.297,99,4.407,104,2.473,225,2.062,301,1.58,345,3.118,346,2.706,383,4.008,402,1.686,436,2.67,480,1.651,635,4.098,887,3.965,934,4.466,1183,5.574,1519,5.136,1847,6.948,2185,6.654,5076,9.414,5077,8.554,5079,8.784,5080,8.557]],["t/888",[1,1.71,60,2.476,80,2.146,83,0.499,105,5.764,106,4.068,115,4.616,139,1.652,162,1.986,169,2.508,182,2.299,210,1.77,234,2.349,249,1.52,250,1.589,254,1.61,261,1.24,317,1.424,338,4.496,349,1.357,410,3.93,473,3.193,481,2.251,525,5.05,569,2.707,635,3.229,643,2.883,648,2.883,774,3.428,927,5.152,991,3.907,993,3.027,1003,5.242,1005,3.724,1066,4.295,1113,3.724,1247,4.74,1256,3.266,1389,5.242,1448,4.391,1825,5.474,1844,3.519,1878,4.391,1896,3.724,2023,5.05,2041,4.74,2050,5.474,2246,5.474,2362,6.152,2520,9.374,3935,4.74,5077,6.625,5079,7.562,5081,9.008,5082,9.008,5083,8.071,5084,6.742,5085,6.742,5086,6.742]],["t/890",[1,1.152,52,1.346,57,2.104,58,0.691,62,0.57,80,1.446,81,1.999,82,1.372,83,0.431,84,2.281,97,1.778,100,0.422,106,4.218,109,1.041,115,4.603,139,2.161,153,1.399,160,4.5,162,1.338,169,1.047,210,1.193,217,2.818,250,1.582,252,2.954,254,0.827,256,1.63,257,3.413,261,0.835,286,1.999,301,0.699,303,1.329,317,1.863,353,1.924,359,1.583,395,2.403,406,1.506,410,3.178,470,1.84,480,0.876,514,2.958,568,3.11,601,1.583,623,2.151,635,2.175,774,2.31,909,2.31,920,5.028,927,2.371,928,2.632,930,3.532,964,2.281,990,1.924,997,3.622,1005,2.509,1044,2.894,1066,2.894,1105,2.632,1108,3.291,1169,4.029,1212,2.894,1294,3.107,1316,4.981,1635,3.532,1816,4.276,1844,4.167,1857,3.029,1896,2.509,2041,4.719,2061,7.179,2091,4.863,2246,6.482,2520,8.494,2710,5.613,2862,3.883,3901,7.985,4531,3.883,5075,3.688,5076,3.883,5077,8.354,5079,3.883,5081,10.178,5082,8.048,5083,9.299,5087,6.712,5088,6.712,5089,4.542,5090,4.542,5091,4.542,5092,4.145,5093,3.532,5094,4.542,5095,4.542]],["t/892",[1,1.014,4,1.121,14,1.241,55,1.695,56,2.419,57,1.853,58,1.259,59,1.606,62,0.765,78,0.571,83,0.448,87,2.548,91,0.349,93,4.262,97,1.566,100,0.371,103,0.565,109,0.917,137,1.521,139,0.98,148,1.778,151,3.11,160,4.355,162,1.178,169,1.406,171,2.279,175,1.942,180,1.088,225,0.964,230,1.48,241,1.527,242,4.001,250,0.942,253,3.022,254,0.728,261,0.736,292,3.11,298,2.561,303,1.17,307,2.317,315,1.17,317,1.748,329,1.394,340,0.858,353,2.147,359,1.394,392,6.543,394,3.227,406,1.326,410,2.429,419,2.278,421,1.547,430,1.008,458,3.227,468,1.308,480,1.177,511,1.383,514,2.605,521,1.02,526,2.401,540,2.522,541,1.592,553,1.414,601,1.394,614,2.995,635,1.915,651,1.71,663,1.833,669,1.814,774,3.102,872,1.726,907,2.06,920,2.995,940,3.597,941,1.96,944,1.491,956,2.955,966,2.06,974,1.634,995,1.937,1016,2.667,1025,3.662,1033,2.401,1035,1.663,1044,2.548,1047,2.317,1079,1.833,1097,2.446,1117,1.894,1132,2.812,1141,2.495,1169,2.401,1256,2.955,1286,4.744,1316,2.495,1332,2.548,1335,4.173,1370,1.894,1489,2.209,1804,2.667,1997,3.247,2041,4.289,2061,3.886,2072,3.247,2112,2.495,2386,3.731,2473,4.569,2519,2.358,2710,5.817,3359,3.247,3901,5.995,3935,2.812,4829,3.649,5077,2.995,5078,3.649,5096,6.1,5097,3.649,5098,6.1,5099,3.999,5100,6.1,5101,3.999,5102,6.1,5103,3.999,5104,10.067,5105,3.999,5106,3.999,5107,3.999,5108,3.999,5109,3.649,5110,3.999,5111,3.999,5112,3.999,5113,8.274,5114,3.999,5115,3.999,5116,3.999,5117,3.999,5118,3.649,5119,3.999,5120,3.419,5121,3.649,5122,3.999]],["t/894",[5,2.977,51,1.133,55,2.063,56,2.56,57,2.607,60,2.066,62,0.706,78,1.118,82,2.365,83,0.446,93,2.899,97,2.203,104,2.602,106,2.185,157,1.919,170,2.607,171,3.208,177,3.063,180,2.647,191,2.362,196,1.658,210,1.478,215,1.933,221,2.035,225,1.886,230,2.083,262,3.019,264,4.215,274,2.222,283,1.689,301,0.865,305,2.899,312,3.208,315,1.647,317,2.055,324,4.215,334,2.32,353,2.345,384,3.318,394,2.977,399,2.477,402,1.542,406,1.866,410,3.116,480,1.086,514,3.666,594,3.85,601,1.961,707,3.208,925,3.208,940,3.318,956,3.792,966,2.899,995,2.726,997,2.553,1021,2.384,1025,4.699,1031,3.511,1044,3.585,1047,5.216,1066,3.585,1073,3.957,1088,3.171,1105,3.261,1212,3.585,1286,6.999,1322,4.569,1326,2.407,1335,7.43,1350,3.666,1816,5.735,1896,4.324,1939,4.077,2037,7.309,2083,7.144,2123,6.087,2386,6.256,2473,5.863,2598,7.696,3176,4.811,3601,7.444,3988,7.144,4694,4.811,5120,4.811,5121,8.214,5123,5.628,5124,5.136,5125,5.628,5126,5.628]],["t/896",[14,0.744,48,1.28,51,0.777,54,2.641,55,1.862,56,1.263,57,1.789,58,0.904,63,1.683,64,1.865,83,0.332,93,1.989,97,2.325,101,1.326,103,0.545,104,1.717,106,1.499,109,1.361,110,8.246,111,1.28,116,1.524,124,2.931,140,2.983,145,2.408,156,1.717,158,1.237,160,4.066,162,1.749,173,4.063,175,1.229,180,1.615,199,1.734,200,1.44,205,1.7,210,1.559,214,1.189,215,1.326,218,1.376,244,2.892,249,1.631,258,0.963,264,4.448,275,1.237,284,1.929,286,1.7,290,3.232,291,2.812,303,1.13,312,5.675,315,2.567,317,1.528,324,6.085,329,1.346,332,1.871,340,0.829,353,1.744,357,3.301,394,2.043,398,1.386,403,2.364,406,2.694,410,4.065,435,2.892,436,1.205,440,2.362,455,2.575,470,1.564,481,1.289,518,2.015,521,1.515,522,2.072,525,2.892,531,2.844,545,2.287,577,2.575,578,1.817,595,2.072,665,1.77,682,1.717,707,5.943,717,1.551,728,3.633,736,2.277,746,2.362,774,1.963,790,1.963,797,4.176,902,1.809,946,1.717,950,2.723,956,1.871,982,2.946,985,2.798,995,4.248,997,1.752,1021,1.636,1025,5.264,1044,3.784,1047,4.194,1054,5.418,1070,2.715,1088,1.564,1132,2.715,1169,2.318,1212,2.46,1316,2.409,1335,2.641,1380,3.633,1448,3.868,1472,4.822,1516,1.963,1659,2.201,1896,3.998,2041,4.176,2061,3.784,2112,2.409,2123,4.618,2147,3.135,2386,5.664,2430,2.641,2473,4.448,2592,3.301,2710,7,3599,3.301,3901,5.243,4896,3.524,4898,5.627,4905,3.135,5118,3.524,5120,6.187,5124,3.524,5127,7.236,5128,5.419,5129,9.26,5130,5.077,5131,3.861,5132,3.861,5133,3.861,5134,3.861,5135,5.939,5136,3.861,5137,3.861,5138,3.861,5139,5.939]],["t/898",[14,0.998,48,1.304,51,0.792,55,1.38,56,2.394,57,2.791,58,1.114,59,1.579,62,0.918,64,1.235,77,1.734,78,1.466,83,0.405,91,0.847,93,5.397,97,1.54,100,0.866,104,1.741,106,1.527,110,4.364,111,1.304,138,1.593,139,0.964,145,1.166,157,1.341,160,2.73,169,1.687,175,1.252,200,2.73,215,1.351,219,1.951,221,1.422,249,0.887,253,2.991,254,1.493,256,2.162,258,1.501,261,1.108,273,1.784,277,1.158,286,1.731,294,1.371,301,0.605,303,1.151,311,1.26,317,1.733,336,1.766,342,1.515,346,1.905,347,3.321,349,0.792,353,1.451,376,1.714,378,2.319,380,1.444,396,1.003,397,3.363,403,1.566,406,1.997,410,4.482,422,1.278,475,2.141,480,0.759,481,1.313,512,2.454,514,2.562,522,4.744,524,2.279,525,5.483,542,3.058,571,2.765,573,2.691,580,1.698,646,1.103,649,1.096,676,1.579,720,1.278,888,1.467,908,1.651,944,2.246,946,1.748,974,1.607,991,3.49,993,1.766,995,2.918,1016,4.017,1021,2.552,1024,1.822,1025,5.599,1053,3.923,1073,4.235,1151,2.623,1316,3.758,1350,2.562,1380,4.477,1564,3.058,1635,3.058,1689,4.364,1824,2.026,1896,6.111,1935,2.85,2034,3.194,2050,3.194,2061,2.506,2386,5.018,2414,5.147,2473,2.946,2750,3.363,2895,2.623,2900,3.058,3262,4.511,3519,4.511,3838,5.15,3840,5.497,3841,5.147,3935,2.765,4641,3.363,4898,6.876,4899,5.943,4900,5.943,4901,5.943,4902,5.943,4903,5.943,4905,3.194,5109,3.589,5128,3.589,5130,3.363,5140,3.933,5141,3.933,5142,3.933,5143,3.933,5144,3.933,5145,3.933,5146,3.194,5147,3.933,5148,3.589,5149,3.589,5150,3.589,5151,3.589,5152,5.497,5153,5.497,5154,3.933,5155,3.589]],["t/900",[14,0.993,57,2.562,58,1.602,62,1.118,64,1.737,66,1.55,78,0.789,81,2.434,83,0.355,91,0.483,93,2.848,97,2.165,106,2.147,109,1.267,139,1.355,146,2.03,158,2.478,160,4.031,163,2.183,169,2.342,182,1.886,196,1.629,214,1.703,277,1.629,282,2.185,287,2.478,298,2.321,303,1.618,340,1.66,369,4.728,396,1.972,406,2.564,410,2.201,451,2.619,556,3.201,651,3.813,652,5.29,720,1.796,733,2.259,745,7.624,746,5.454,876,5.037,887,2.562,924,2.71,944,2.062,1025,3.319,1043,3.382,1161,3.783,1256,3.746,1380,4.73,1721,7.544,1896,3.055,1942,4.006,2061,6.474,2112,3.45,2144,4.728,2172,8.812,2173,6.441,3403,4.142,3404,4.73,3579,5.046,4370,5.046,4531,4.728,5156,5.53,5157,5.53,5158,9.656,5159,5.53,5160,5.53,5161,5.53,5162,5.53,5163,5.53,5164,7.733,5165,7.057,5166,7.733,5167,7.733,5168,5.53]],["t/902",[1,1.374,5,2.867,14,0.557,51,1.091,58,1.457,62,0.956,74,1.242,77,1.284,78,0.774,83,0.249,91,0.473,93,5.389,100,0.972,103,0.766,104,1.567,109,1.242,119,2.721,137,1.351,139,1.328,145,1.606,153,1.669,157,1.848,160,3.29,169,2.206,179,2.104,200,3.29,210,1.423,215,1.861,225,1.306,249,1.719,254,1.388,256,1.945,258,1.901,261,0.997,286,2.386,303,1.586,315,2.232,317,1.145,336,2.434,339,2.688,340,2.055,342,2.087,353,1.837,359,1.889,364,2.908,398,1.945,402,1.068,410,3.512,419,2.428,433,2.434,445,2.362,479,2.037,521,2.441,522,2.908,536,2.362,540,1.848,553,2.696,557,2.721,580,2.34,591,2.254,665,2.485,716,5.524,752,2.006,828,2.567,907,2.792,991,3.141,995,2.625,1025,5.295,1035,2.254,1053,7.001,1079,2.485,1165,3.315,1262,3.04,1456,3.04,1536,5.746,1559,3.453,1820,4.059,1840,3.381,1896,5.779,2061,3.453,2386,5.396,2389,6.202,2400,3.707,2414,3.811,3398,4.214,3841,5.361,4034,4.946,4898,5.929,4899,4.401,4900,4.401,4901,4.401,4902,4.401,4903,4.401,4904,6.519,4905,4.401,5169,5.42,5170,5.42,5171,5.42,5172,5.42]],["t/904",[14,0.984,56,2.885,77,1.807,78,1.089,80,2.427,83,0.439,91,0.665,93,5.389,96,3.811,100,1.087,101,3.03,111,1.797,112,2.409,117,1.861,118,4.346,139,1.868,169,1.249,188,3.04,191,2.275,197,1.749,200,2.844,210,1.423,212,1.58,249,1.222,254,1.388,255,2.821,315,1.586,317,1.145,322,3.261,368,2.234,380,3.947,410,3.035,418,3.611,419,2.777,420,4.746,421,1.374,438,3.342,534,2.215,556,1.648,569,2.176,573,3.707,718,2.721,938,5.085,943,3.01,956,2.625,979,1.12,997,2.459,1007,3.615,1036,2.318,1114,3.381,1125,4.132,1256,4.637,1339,2.409,1345,2.459,1351,5.361,1365,3.141,1577,5.524,1803,4.858,1837,4.495,1842,4.664,1844,4.604,1860,3.381,2386,4.664,4107,4.946,5173,7.624,5174,7.624,5175,5.42,5176,7.624,5177,7.624,5178,5.42,5179,5.42,5180,5.42,5181,5.42,5182,5.42,5183,5.42,5184,7.624,5185,5.42,5186,5.42,5187,5.42]],["t/906",[14,0.903,77,1.581,78,1.254,80,2.796,83,0.306,91,0.582,93,5.058,100,0.816,101,3.017,103,0.942,115,3.482,180,1.815,206,2.548,209,1.563,223,2.936,225,2.116,229,4.305,234,2.325,250,1.572,253,2.726,254,1.214,256,3.152,261,1.227,265,1.128,270,4.449,272,5.703,273,3.027,274,2.633,289,5.09,301,1.351,303,2.571,317,1.409,368,2.75,402,1.731,410,2.656,438,2.528,452,2.167,468,2.182,588,3.159,629,3.124,667,3.436,722,3.742,917,3.091,995,3.232,997,3.027,1025,4.004,1028,4.081,1029,4.69,1047,3.866,1067,4.563,1256,3.232,1339,2.966,1376,3.866,1396,5.417,1526,5.417,1559,4.25,1641,5.703,1653,6.088,1822,6.088,1840,4.162,1843,5.125,1896,3.685,1916,5.703,2026,4.69,2040,5.703,2125,3.933,2764,5.703,3271,5.858,3398,6.83,5155,8.961,5188,6.671,5189,6.671,5190,6.671]],["t/908",[1,1.906,14,1.029,52,0.992,56,1.737,57,1.551,58,1.523,61,1.68,62,0.666,63,1.459,77,1.564,78,0.943,83,0.459,89,3.313,91,0.656,93,4.493,97,1.31,100,0.698,103,0.473,106,3.387,109,0.767,117,1.824,147,3.978,148,2.361,153,2.033,160,1.981,162,0.986,165,1.149,169,0.771,182,1.141,200,2.462,204,1.68,205,4.497,209,0.784,212,0.6,214,1.031,221,1.92,225,1.28,237,2.385,242,2.385,252,1.473,254,0.609,255,1.238,261,0.977,262,1.795,265,0.566,273,2.409,277,0.986,301,0.515,317,0.707,329,1.166,334,1.38,346,1.058,353,1.28,356,4.129,359,1.166,364,2.849,396,0.854,399,1.473,402,0.659,403,1.332,410,3.64,411,0.91,419,2.402,421,1.906,426,1.724,458,1.77,461,1.367,470,2.151,477,0.944,478,1.785,480,1.274,521,1.354,542,2.602,545,1.289,556,1.017,569,1.344,591,1.392,596,2.771,617,5.134,663,1.534,671,1.087,673,2.434,686,1.77,702,2.667,717,3.018,752,1.238,876,3.459,888,1.981,914,1.821,920,3.978,937,3.734,941,1.64,946,3.878,979,1.554,991,1.939,1033,2.009,1035,1.392,1039,1.248,1072,4.846,1073,2.353,1113,2.934,1114,2.088,1256,4.429,1316,4.119,1462,3.131,1635,2.602,1721,2.289,1724,2.353,1764,4.642,1844,4.278,1896,3.647,1907,3.978,2173,2.047,2386,4.597,2389,2.353,2414,2.353,2473,2.507,2503,2.507,2519,4.431,2561,4.846,2710,2.353,2728,2.861,3262,5.629,3519,5.629,3822,2.861,3838,6.426,3841,2.353,3858,2.289,3935,4.642,4004,2.861,4160,2.717,4711,4.945,4824,4.846,4898,4.129,4899,2.717,4900,2.717,4901,2.717,4902,2.717,4903,2.717,5092,4.846,5093,4.129,5097,3.054,5130,2.861,5148,3.054,5149,3.054,5150,3.054,5151,3.054,5152,6.859,5153,6.859,5165,3.054,5191,10.548,5192,3.346,5193,3.346,5194,3.346,5195,3.346,5196,3.346,5197,3.346,5198,5.31,5199,3.346,5200,3.346,5201,3.346,5202,3.346,5203,3.346,5204,3.346,5205,3.346,5206,5.31,5207,3.346,5208,3.346,5209,3.346,5210,3.346,5211,3.346,5212,3.346,5213,3.346,5214,5.31,5215,5.31,5216,5.31,5217,5.31,5218,5.31,5219,5.31,5220,3.346,5221,3.346,5222,3.346,5223,3.346,5224,3.346,5225,3.346,5226,3.346,5227,3.346]],["t/910",[1,1.363,5,2.843,51,1.082,57,4.659,58,1.153,62,0.95,66,1.506,77,1.273,78,0.767,82,1.623,83,0.478,84,3.806,90,2.969,91,0.469,93,5.812,97,2.104,103,1.071,111,1.782,157,2.585,160,3.275,163,2.121,167,2.666,169,1.747,179,2.086,180,1.462,181,3.778,228,2.464,236,2.177,250,1.266,254,0.978,275,1.722,301,0.826,308,3.226,315,2.57,317,1.135,340,1.627,342,2.069,345,1.958,359,2.641,398,3.745,402,1.059,406,1.782,452,1.746,481,1.794,512,4.729,521,1.371,572,2.805,585,3.778,717,2.158,894,1.943,902,3.55,907,2.768,972,4.937,1015,3.424,1034,3.778,1053,7.502,1088,2.177,1119,4.32,1151,3.584,1165,3.287,1339,2.389,1613,2.389,1764,3.778,1878,3.5,1896,5.555,2026,6.705,2061,6.406,2251,4.904,2400,3.676,2430,5.185,2728,4.595,3841,3.778,3935,3.778,4643,4.595,4904,4.595,5228,11.132,5229,5.374,5230,5.374,5231,5.374,5232,5.374,5233,5.374,5234,5.374,5235,5.374,5236,5.374,5237,5.374,5238,7.579,5239,7.579,5240,7.579,5241,5.374]],["t/912",[1,1.753,8,2.409,51,1.391,52,2.049,55,1.584,58,1.052,62,0.867,65,2.662,71,3.349,77,1.638,82,2.088,83,0.412,84,3.471,93,5.146,103,0.976,104,2.599,160,3.726,162,2.036,172,2.684,205,3.043,206,2.64,225,1.666,254,1.258,301,1.063,353,1.666,364,3.709,384,4.076,398,2.481,402,1.969,410,3.579,468,2.261,541,2.752,556,2.102,560,3.388,565,3.471,567,4.503,596,2.902,623,3.274,718,3.471,894,2.5,917,3.203,924,3.388,1025,4.15,1171,4.306,1183,4.503,1200,4.076,1256,3.349,1383,4.15,1518,5.008,1519,4.15,1570,4.729,1844,4.692,1896,5.845,2026,4.86,2050,5.613,2246,5.613,2386,4.228,3398,5.375,3601,4.86,3901,5.008,5075,7.3,5093,5.375,5146,5.613,5242,6.308,5243,6.308,5244,6.308,5245,6.308,5246,6.913]],["t/914",[8,2.841,62,1.022,64,2.561,66,2.286,83,0.374,104,2.357,137,2.033,150,4.095,172,3.166,175,2.596,180,2.218,210,2.62,214,2.511,215,2.801,265,1.379,301,1.254,326,5.733,332,3.95,351,4.988,360,8.531,402,2.125,470,3.303,613,4.725,665,4.574,720,2.649,722,4.574,927,4.256,1014,3.738,1018,6.621,1786,7.441,1816,5.195,1847,6.621,2060,6.972,3599,6.972,5247,8.154,5248,8.154]],["t/916",[48,3.262]],["t/918",[14,0.631,58,1.264,62,0.769,64,2.609,79,5.598,83,0.281,91,0.535,100,0.874,104,2.722,135,4.832,179,2.382,209,2.206,229,3.007,239,2.507,250,1.445,265,1.037,270,4.091,311,3.623,315,2.431,334,2.529,340,2.166,349,1.235,411,2.259,422,2.698,433,2.755,439,3.08,443,4.091,452,1.993,474,2.202,519,3.291,556,1.865,578,3.401,591,4.931,631,7.213,635,3.977,812,4.77,828,4.459,979,1.268,981,4.897,1085,2.905,1125,2.648,1230,3.682,1231,3.827,1317,6.221,1326,3.552,1623,4.52,2139,5.291,2248,4.313,2334,5.245,2416,4.313,2555,2.812,2643,5.598,2785,4.445,3273,3.617,3358,3.389,3453,3.16,3604,5.598,3666,4.313,3842,4.985,3895,5.598,4392,5.245,4984,5.598,5249,6.134]],["t/921",[48,3.262]],["t/923",[58,1.363,144,2.852,380,3.29,383,4.196,418,5.316,420,4.064,427,4.444,1035,3.726,1208,5.026,1349,6.299,1352,8.21,1354,6.491,1358,9.635,4959,8.175,5250,8.958,5251,8.958]],["t/925",[91,0.812,100,0.864,258,2.32,329,3.244,380,3.418,419,2.563,420,4.223,1035,3.872,1044,5.93,1345,4.223,1368,5.222,2694,7.558]],["t/927",[14,0.438,51,0.857,52,2.277,58,1.562,64,1.337,83,0.392,91,0.558,100,0.85,103,0.601,106,3.317,134,1.015,169,2.11,175,2.036,237,2.873,261,1.177,265,1.082,277,2.697,283,1.278,315,1.246,340,1.373,349,1.843,353,2.206,370,3.064,385,3.311,396,1.96,398,1.528,406,1.412,426,2.193,465,1.952,470,2.591,474,3.066,511,2.213,521,2.889,534,2.614,540,2.181,564,4.519,596,2.685,617,4.974,632,2.193,646,1.194,651,1.821,663,3.522,671,1.383,673,1.952,825,3.886,828,2.016,883,4.53,894,2.313,992,4.187,1024,1.973,1066,2.713,1080,2.913,1088,1.725,1099,3.64,1109,4.167,1144,4.792,1204,3.64,1288,2.994,1348,2.222,1368,4.792,1370,2.016,1384,4.01,1399,2.913,1407,3.432,1425,4.791,1489,4.719,1495,2.284,1517,2.657,1533,5.234,1534,5.731,1690,2.713,1891,2.193,1912,2.774,2107,3.085,2687,3.189,2706,3.457,2754,3.64,2842,6.422,2852,3.253,3129,2.774,3666,2.994,3668,2.994,3682,2.774,3898,2.222,3906,5.769,3967,3.189,3968,3.189,3970,4.075,3989,4.791,4154,3.457,4220,3.64,4221,5.838,4222,3.886,4223,3.886,4224,3.886,4225,3.886,5252,4.258,5253,4.258,5254,4.258,5255,4.258,5256,4.258,5257,4.258]],["t/929",[48,3.262]],["t/931",[14,0.921,58,1.363,83,0.411,142,3.537,150,4.499,252,3.943,257,4.555,294,3.122,311,2.871,556,2.724,605,5.707,720,2.91,909,4.555,1039,3.341,1578,4.876,3857,7.65,5258,8.958,5259,8.958,5260,8.958]],["t/933",[48,3.262]],["t/935",[7,3.691,51,1.77,91,0.972,101,3.587,134,2.654,307,5.096,1588,5.379,2767,6.586,3810,6.653,3942,7.518,5146,8.479,5261,10.165,5262,8.793,5263,8.793,5264,8.793]],["t/937",[136,8.282,200,3.613,977,6.626,5261,8.84]],["t/939",[1,1.799,4,1.988,8,3.853,51,1.428,55,2.095,62,0.889,65,3.52,99,4.708,128,4.182,137,1.768,142,2.8,169,1.635,175,2.258,176,4.62,206,3.492,212,1.271,223,3.122,250,1.671,265,1.199,287,2.273,315,2.076,382,4.852,409,3.122,433,3.185,470,2.873,514,4.62,519,3.805,556,2.779,560,4.958,565,3.562,566,3.033,596,2.977,635,3.397,645,4.043,843,3.979,902,3.322,917,4.235,993,3.185,1027,6.064,1338,4.339,1343,6.064,1533,4.064,1539,4.258,1670,6.064,1728,6.473,2011,5.515,2371,5.759,4079,6.473,4080,6.473,4081,6.473,4082,6.473,4083,6.473,4684,8.342,5075,7.422,5093,7.866,5243,6.473,5244,6.473,5245,6.473,5265,7.093]],["t/941",[8,3.308,100,0.882,119,4.767,382,6.494,909,4.827,979,1.963,4923,8.664,5266,9.494]],["t/943",[1,2.024,7,3.351,8,3.432,77,2.334,78,1.14,83,0.366,87,5.086,100,0.741,107,1.902,130,2.611,144,2.541,146,2.805,156,3.549,169,1.84,200,2.977,209,1.871,252,3.514,254,1.453,307,4.626,410,3.178,464,4.112,556,2.427,582,5.461,590,3.699,698,5.98,722,4.479,894,2.887,917,3.699,950,3.66,979,2.036,980,5.784,1043,4.883,1169,4.792,1183,5.2,1298,6.826,1376,4.626,1517,4.981,1835,5.613,3620,7.285,5242,7.285,5267,7.983]],["t/945",[8,3.181,169,2.104,176,5.947,296,4.019,375,6.615,382,6.246,432,4.643,556,2.776,976,5.947,1522,4.324,2021,5.817,2767,6.839,4680,7.806,4681,7.413,4687,8.332,5268,8.332]],["t/947",[6,3.026,48,3.511,83,0.413,91,0.785,134,2.145,169,2.074,250,2.121,261,1.656,433,4.042,450,7.308,902,4.216,925,5.13,948,5.863,1262,5.049,1559,5.734,2371,7.308,3898,4.698,5269,9.001]],["t/949",[83,0.442,87,6.14,479,3.623,718,4.84,925,5.493]],["t/951",[6,2.742,7,3.423,51,1.641,77,2.555,83,0.374,91,0.712,108,4.375,114,3.625,134,1.943,136,8.531,183,4.256,221,2.949,257,4.146,282,1.998,406,2.704,411,2.218,436,2.545,474,2.927,519,4.375,556,2.479,568,3.778,590,3.778,596,3.423,718,4.095,790,4.146,820,3.778,881,5.733,917,3.778,925,4.648,1014,3.738,1033,4.895,1078,6.621,1128,5.438,1338,4.988,1839,7.441,5270,9.978,5271,8.154]],["t/953",[0,7.165,74,1.8,83,0.447,91,0.685,103,1.109,129,3.718,134,1.871,146,2.56,217,3.296,228,3.6,261,1.444,283,2.357,290,4.274,369,3.526,410,3.126,411,2.136,445,3.423,453,6.106,465,3.6,478,2.64,560,3.849,566,3.358,594,5.371,676,3.153,683,5.689,711,6.106,790,3.993,902,3.678,914,4.274,927,4.098,934,4.098,943,3.1,950,3.6,1207,7.165,1559,5.002,2114,8.896,2908,7.165,3737,6.713,4123,6.375,4437,7.165,5272,7.852,5273,7.852,5274,7.852,5275,7.852,5276,7.852,5277,7.852,5278,7.852]],["t/955",[82,2.656,103,1.242,104,3.22,130,2.876,209,2.06,219,4.362,257,4.471,277,2.59,303,2.574,311,2.818,519,4.718,645,5.012,917,4.074,925,5.952,943,3.471,976,5.728,1539,5.278,2194,7.14,4694,7.518,5279,8.793,5280,8.793]],["t/959",[58,1.37,62,1.129,100,0.836,103,1.271,146,2.363,148,4.001,265,1.522,439,4.52,445,3.923,720,2.924,962,5.403,993,4.042,1246,6.521,1466,6.521,2336,8.214,2372,7.308,2767,6.742,4852,7.695,5281,8.214]],["t/961",[62,1.173,103,1.321,323,6.239,461,3.822,481,3.123,894,3.383,1466,6.777,2372,7.595,4852,7.997,5281,8.536,5282,9.354]],["t/963",[143,8.707,478,3.208,1370,4.518,1519,5.727,2133,7.419,2612,8.707,5283,9.541]],["t/965",[14,0.934,71,4.402,104,2.627,144,2.892,299,7.066,307,5.266,519,4.875,590,4.933,968,6.216,1383,5.455,1953,8.292,2068,7.378,2091,6.584,2182,5.919,2802,8.292,5284,9.087]],["t/967",[8,3.136,83,0.485,87,5.734,91,0.924,100,0.836,150,4.52,200,3.949,258,2.244,282,2.206,473,4.262,596,3.778,917,4.17,1383,5.403,1517,5.616,5285,9.001,5286,9.001]],["t/969",[8,3.337,46,6.965,91,0.666,100,0.709,134,1.818,146,2.004,157,2.603,162,2.248,200,3.904,204,3.832,257,4.87,265,1.291,351,4.668,556,2.32,590,3.536,648,3.264,665,3.499,669,3.463,697,5.366,711,7.448,979,1.98,1050,5.138,1153,5.221,1190,4.668,1338,4.668,1522,4.535,1563,6.965,2111,6.525,2133,5.935,2134,6.965,2289,4.971,2560,6.965,2751,6.965,4681,6.197,5077,5.717,5093,5.935,5268,6.965,5287,9.577,5288,7.632,5289,7.632,5290,7.632,5291,7.632,5292,7.632,5293,7.632,5294,7.632,5295,7.632,5296,7.632,5297,7.632]],["t/971",[48,3.262]],["t/973",[150,4.84,249,2.173,402,1.899,1014,4.418,3404,5.895]],["t/975",[1,1.928,82,2.296,83,0.349,100,1.049,101,3.281,129,3.6,175,2.42,185,4.137,199,3.414,200,2.835,201,4.563,257,3.865,274,3.001,301,1.169,311,2.436,336,3.414,481,2.538,583,5.694,645,5.445,676,3.052,720,2.469,728,4.65,917,4.426,943,3.001,966,3.915,994,4.743,1017,5.911,1022,5.508,1039,2.835,1085,3.6,1086,4.65,1195,4.563,1198,6.499,1332,4.843,1383,4.563,1588,5.844,2103,6.172,2754,6.499,5146,8.483,5298,7.602,5299,7.602,5300,7.602,5301,7.602,5302,9.553,5303,7.602,5304,7.602,5305,7.602]],["t/977",[48,3.262]],["t/979",[14,1.152,66,2.267,83,0.493,140,3.333,277,2.924,287,2.591,331,6.287,941,4.865,944,3.702,1019,6.912,1043,6.072,1062,5.685,1077,5.266,1132,8.335,1177,6.056,1380,4.946,2340,7.719,3357,6.912,4595,5.685,4862,6.565,5306,6.912,5307,7.378,5308,7.378,5309,9.058,5310,9.058]],["t/981",[14,0.948,66,2.584,140,3.8,528,7.485,1878,6.005,4595,7.549,5311,8.412,5312,8.412,5313,8.412,5314,8.412,5315,8.412,5316,8.412,5317,8.412]],["t/983",[48,3.262]],["t/985",[1,1.355,4,2.116,14,0.776,48,1.772,58,1.149,66,2.116,83,0.245,101,3.446,103,0.755,104,1.545,107,2.086,109,1.225,116,2.11,139,1.31,140,3.112,156,2.376,181,5.308,228,2.45,258,1.332,297,2.789,315,2.562,316,7.723,340,2.042,363,2.908,403,2.127,405,2.908,421,1.355,422,1.736,465,2.45,477,2.937,479,2.838,497,1.835,550,5.87,565,2.683,567,3.481,569,2.146,577,5.035,623,4.145,628,5.47,666,4.339,672,1.918,682,2.376,687,3.757,769,4.81,799,5.47,828,2.531,843,2.998,880,4.109,941,2.619,1031,3.334,1035,2.223,1039,1.993,1064,6.196,1348,2.789,1350,3.481,1376,3.097,1438,3.872,1447,3.151,1516,2.717,1647,4.339,1754,3.655,1760,4.877,1795,4.877,1894,3.269,1947,3.481,2045,5.87,2436,4.989,2555,3.461,2895,6.344,2897,4.339,3689,3.564,3944,4.569,4263,4.155,4372,5.87,4595,3.757,5318,9.512,5319,6.454,5320,5.344,5321,5.344,5322,7.549,5323,7.549,5324,7.549,5325,7.549,5326,7.549,5327,7.549,5328,5.344,5329,5.344,5330,5.344,5331,5.344,5332,9.512,5333,5.344,5334,10.034,5335,7.549,5336,5.344,5337,5.344,5338,5.344,5339,7.549,5340,5.344,5341,5.344,5342,5.344]],["t/987",[66,2.081,74,2.369,83,0.474,91,0.821,102,3.44,109,1.702,140,3.88,212,1.33,258,1.851,265,1.591,285,4.377,316,7.642,340,1.594,344,4.632,349,1.894,353,1.789,359,3.28,396,2.401,461,3.846,465,3.404,497,2.55,769,4.73,805,5.996,941,3.639,1031,4.632,1064,6.731,1493,4.979,1522,4.457,2045,5.773,2470,5.773,2593,6.618,4886,6.775,5343,10.867,5344,9.412,5345,9.412,5346,7.424,5347,7.424,5348,9.412,5349,9.412]],["t/989",[65,4.167,66,2.057,83,0.471,101,3.209,109,1.682,134,2.226,140,3.852,249,1.654,258,1.829,281,5.508,303,2.148,316,7.586,321,5.715,329,2.557,349,1.881,420,3.329,465,3.364,497,2.52,583,5.497,589,2.873,769,4.675,880,3.994,941,3.597,1031,4.579,1055,4.474,1064,6.695,1174,4.634,1233,4.691,1515,4.579,1522,4.424,1812,6.569,1833,6.274,2045,5.706,2443,9.554,4652,7.265,5350,7.338,5351,9.343,5352,9.343,5353,9.343,5354,7.338]],["t/991",[64,2.868,65,4.111,83,0.419,112,4.059,258,2.276,465,4.186,880,4.969,925,5.204,1064,5.947,1190,5.585,1517,5.697,1812,6.419,4849,8.332,5306,9.128]],["t/993",[48,3.262]],["t/995",[14,0.814,48,2.625,62,0.993,63,3.451,66,2.219,83,0.449,91,0.855,101,2.719,107,1.886,118,4.513,249,1.785,250,1.865,265,1.339,402,1.56,430,1.996,478,2.662,497,2.719,521,2.019,540,2.7,790,4.026,986,5.93,1339,3.52,1493,4.189,1517,4.94,1541,7.225,1559,5.044,1840,4.94,1841,6.143,1842,6.51,1843,5.554,2895,7.098,3398,6.157,4217,6.157,5319,9.099,5355,7.225]],["t/997",[4,2.14,6,2.566,14,1.076,56,2.496,58,1.457,78,1.367,83,0.35,91,0.836,97,2.988,100,0.709,107,1.818,118,4.35,145,2.839,180,2.076,181,5.366,234,2.659,250,1.798,261,1.404,263,4.762,275,2.446,285,4.5,301,1.174,400,4.095,478,2.566,521,1.947,573,5.221,728,4.668,790,3.881,979,1.98,1492,6.197,1575,5.53,1841,4.216,1842,4.668,1843,4.999,2895,7.321,3446,6.965,5319,8.188,5355,6.965,5356,7.632,5357,9.577,5358,7.632,5359,7.632,5360,7.632,5361,7.632]],["t/999",[48,3.262]],["t/1001",[125,7.147,150,4.791,687,6.708,1362,5.836,5362,9.541,5363,10.952]],["t/1003",[83,0.492,125,6.905,537,7.881,1312,4.821,1367,5.873,2302,6.679,2595,8.412,3032,9.798,4059,7.485,5364,9.218,5365,9.218]],["t/1005",[125,7.368]],["t/1007",[8,3.064,69,4.416,90,4.858,125,7.822,153,2.708,157,3.561,218,3.72,477,2.945,711,6.838,1124,5.486,1174,4.362,2459,7.566,4846,8.024,5366,8.024,5367,8.793,5368,8.793,5369,8.793,5370,8.793,5371,8.793]],["t/1009",[5366,8.976]],["t/1011",[125,7.255,521,2.471,1132,6.811,1989,7.255]],["t/1013",[14,1.152,66,2.267,83,0.493,140,3.333,277,2.924,287,2.591,331,6.287,941,4.865,944,3.702,1019,6.912,1043,6.072,1062,5.685,1077,5.266,1132,8.335,1177,6.056,1380,4.946,2340,7.719,3357,6.912,4595,5.685,4862,6.565,5306,6.912,5307,7.378,5308,7.378,5309,9.058,5310,9.058]],["t/1015",[14,0.948,66,2.584,140,3.8,528,7.485,1878,6.005,4595,7.549,5311,8.412,5312,8.412,5313,8.412,5314,8.412,5315,8.412,5316,8.412,5317,8.412]],["t/1017",[48,3.262]],["t/1019",[8,1.938,14,0.798,58,0.846,62,0.974,74,1.779,78,1.382,83,0.41,91,0.921,97,2.177,100,1.055,103,0.786,104,1.608,107,1.325,117,2.667,131,4.03,134,1.325,137,1.935,145,1.649,153,2.982,165,3.072,169,1.282,170,2.577,180,2.112,188,3.12,218,2.766,229,4.384,231,1.831,236,2.253,252,2.448,254,1.012,265,0.94,288,1.91,294,2.706,311,1.782,317,1.175,322,2.379,334,2.293,339,4.802,340,1.194,396,1.419,411,2.945,419,1.531,427,2.759,461,3.956,480,1.073,497,1.91,521,1.419,530,2.865,578,1.702,591,2.314,642,3.17,649,1.55,667,2.865,671,2.522,672,3.21,673,4.438,752,2.058,931,4.166,979,1.15,990,2.356,1016,3.709,1112,5.311,1144,3.12,1586,2.233,1591,3.279,1613,2.473,1623,5.892,1775,4.516,1827,4.755,1834,3.851,1938,4.166,2139,3.544,2201,5.625,2295,4.516,2345,4.661,2555,2.55,3121,4.516,3122,4.755,3270,4.755,3273,4.578,3274,3.402,4213,7.263,5372,4.755,5373,5.562,5374,5.562]],["t/1021",[83,0.431,100,0.873,137,2.343,275,3.012,301,1.446,436,2.933,479,3.534,480,1.813,613,5.447,1623,5.116]],["t/1023",[58,1.015,62,0.836,66,1.87,97,2.611,100,0.62,107,1.589,109,2.251,116,2.633,134,2.584,165,2.291,204,5.24,212,1.574,221,2.412,229,4.813,231,3.234,239,3.589,250,1.572,265,1.128,295,3.742,339,5.523,340,1.886,348,4.081,349,1.343,353,1.607,403,2.656,409,2.936,411,2.389,421,1.692,461,2.726,468,2.182,477,1.882,643,4.199,674,3.436,684,3.685,689,4.345,697,4.69,717,3.527,917,3.091,963,2.359,982,4.357,1407,3.579,1586,3.527,1623,5.345,1671,3.091,1891,3.436,1994,5.187,2320,4.449,2345,4.004,2555,3.058,3092,5.703,3093,5.703,3134,6.088,3271,4.449,5375,6.671,5376,6.671,5377,6.671,5378,6.671,5379,6.671,5380,6.671,5381,6.671,5382,6.671,5383,6.671,5384,6.671,5385,6.671]],["t/1025",[14,0.831,82,2.442,83,0.371,100,1.068,107,1.926,134,2.365,231,2.662,277,2.381,339,5.328,419,2.733,461,3.304,643,4.245,649,2.766,663,3.707,671,2.626,672,2.902,673,4.925,963,2.859,1112,7.348,1113,4.466,1623,5.403,1938,6.056,3354,6.565,3668,6.979,5386,9.926,5387,8.085]],["t/1027",[1,1.582,4,0.49,14,0.838,51,0.999,55,0.705,56,1.006,60,0.641,62,0.517,63,0.761,64,0.549,66,0.49,67,1.194,74,1.544,80,0.556,82,0.929,83,0.509,84,1.544,91,0.268,97,0.684,100,0.916,103,0.434,104,0.889,106,2.197,107,0.416,109,1.138,111,2.069,112,0.776,117,3.95,128,1.813,130,0.571,134,2.002,137,1.027,139,0.428,140,0.72,141,0.866,145,0.518,158,0.56,160,1.147,162,0.514,163,0.689,165,2.143,166,1.605,171,0.996,177,0.951,180,2.049,182,0.596,196,1.666,197,0.563,205,0.769,209,0.965,210,0.459,211,0.877,212,1.283,214,0.538,217,1.729,227,0.747,228,0.801,231,2.218,241,0.667,250,0.725,254,0.903,261,0.321,262,0.937,265,1.211,273,0.792,275,0.56,276,0.98,282,1.01,283,0.524,287,0.56,294,1.972,301,0.473,303,0.511,306,1.048,315,0.511,317,0.65,328,0.754,329,2.347,333,0.484,339,5.16,340,1.804,345,1.121,349,0.619,353,0.741,367,1.113,369,0.784,376,1.34,383,0.818,395,0.924,402,1.115,403,0.695,411,0.475,419,1.366,421,2.189,428,1.165,430,0.775,433,0.784,438,1.165,451,2.35,467,2.104,468,1.006,470,1.246,471,3.515,473,0.827,474,2.923,475,1.674,477,1.4,479,1.549,480,0.337,497,1.056,521,2.592,522,1.65,534,3.078,540,0.596,541,0.695,543,1.425,545,0.673,555,1.165,556,0.935,560,0.856,565,0.877,568,2.3,589,0.684,595,2.21,601,0.609,613,1.012,629,1.93,643,3.933,646,1.749,649,2.27,650,1.909,671,0.999,672,3.807,673,1.889,674,1.584,676,0.701,680,1.418,681,0.622,682,0.776,684,0.965,686,0.924,689,1.138,691,1.048,704,0.9,705,1.03,715,1.068,717,1.235,720,0.567,733,2.028,843,0.98,898,4.12,902,1.44,907,0.9,924,0.856,934,1.605,944,0.651,963,1.087,968,1.195,974,1.683,979,1.684,990,1.746,1039,0.651,1042,2.276,1070,1.228,1109,1.138,1117,0.827,1119,1.753,1120,2.228,1144,0.98,1150,1.165,1152,0.912,1158,2.104,1165,3.036,1174,2.462,1187,0.792,1262,0.98,1271,1.265,1312,1.381,1332,1.113,1348,1.605,1351,1.228,1352,1.358,1362,1.068,1364,3.345,1462,3.971,1493,1.627,1516,0.888,1586,4.147,1591,2.429,1613,1.832,1620,0.98,1623,4.913,1628,1.012,1671,0.809,1673,2.303,1790,2.162,1824,0.9,1831,2.162,1894,1.068,1926,3.859,1928,3.859,1929,3.859,1941,2.003,1965,1.138,2001,2.985,2139,1.113,2173,1.068,2252,2.742,2279,1.195,2289,1.138,2296,2.162,2324,2.162,2345,2.473,2503,1.308,2507,1.493,2555,0.801,2556,1.418,2769,1.594,2784,1.493,2809,1.308,3086,1.358,3167,1.493,3171,1.358,3210,5.81,3212,3.523,3213,3.345,3214,3.345,3230,1.418,3234,1.358,3273,1.03,3274,1.881,3290,2.806,3331,1.594,3341,3.086,3403,3.086,3404,3.036,3433,2.391,3668,3.49,4140,1.418,4929,1.493,4985,1.594,5388,1.747,5389,7.667,5390,1.747,5391,1.747,5392,1.747,5393,3.075,5394,1.747,5395,1.747,5396,1.747,5397,1.747,5398,4.963,5399,1.747,5400,1.747,5401,5.658,5402,1.747,5403,1.747,5404,1.747,5405,1.747,5406,1.747,5407,4.12,5408,1.747,5409,1.747,5410,1.747,5411,1.747,5412,1.747,5413,1.747,5414,4.12,5415,1.747,5416,1.747,5417,1.747,5418,1.747,5419,1.747,5420,1.747,5421,1.747,5422,1.747,5423,1.747,5424,1.747,5425,1.747,5426,1.747,5427,1.747,5428,1.747,5429,3.075,5430,1.747,5431,3.075,5432,1.747,5433,1.747,5434,1.747,5435,1.747,5436,1.747,5437,4.12,5438,1.747]],["t/1029",[4,2.328,8,2.138,14,0.631,62,1.041,66,1.72,78,1.186,83,0.381,91,1.035,100,0.57,107,1.462,116,2.422,117,2.107,134,1.462,138,2.485,145,2.462,153,1.889,165,2.107,209,1.946,212,1.099,217,2.575,227,2.623,239,2.507,248,6.329,254,1.117,268,3.245,277,1.807,294,2.894,296,2.7,308,3.682,339,4.12,342,2.362,383,2.873,399,2.7,411,1.669,418,3.933,451,2.905,497,2.853,521,2.402,595,4.456,613,5.457,643,3.552,646,1.72,649,1.709,867,2.529,907,3.16,912,5.673,979,1.268,1079,3.807,1088,2.485,1152,3.202,1339,2.727,1364,4.981,1496,4.456,1503,5.245,1591,5.949,1620,3.441,1623,4.52,2176,5.598,2339,5.245,2809,4.595,3256,7.101,3264,5.598,3404,3.752,3602,4.981,3762,5.598,4160,4.981,5372,5.245,5389,5.598,5439,6.134,5440,6.134,5441,7.579,5442,6.134,5443,6.134]],["t/1031",[14,0.868,83,0.387,100,0.784,104,2.441,107,2.012,165,2.9,294,2.942,402,1.664,438,3.199,497,3.5,541,3.361,556,2.567,613,4.893,646,2.367,649,2.353,934,4.407,963,2.985,1152,4.407,1586,3.39,1623,6.188,2139,6.975,3274,5.165,3404,5.165,5444,10.192,5445,8.443,5446,8.443]],["t/1033",[4,1.441,14,0.528,51,0.646,52,0.952,58,0.489,62,1.128,63,1.4,67,1.247,77,1.903,78,0.734,80,1.022,83,0.412,89,2.004,90,1.774,91,0.701,100,0.955,103,0.454,104,0.928,107,0.765,109,1.178,110,5.32,111,1.065,117,3.089,137,1.281,139,1.968,144,1.636,146,0.843,153,0.989,156,1.428,165,1.765,180,0.874,182,1.095,196,1.513,209,0.752,211,1.612,212,0.921,218,1.144,225,0.774,231,3.255,234,1.119,242,1.442,250,0.757,252,1.413,256,1.844,258,0.8,261,0.591,275,2.059,288,1.103,294,1.119,301,1.129,305,1.654,306,1.928,317,0.678,329,1.791,339,1.593,340,1.724,345,1.17,359,1.119,396,2.294,399,1.413,409,2.262,411,1.398,422,1.043,430,2.025,436,1.604,437,2.405,438,2.782,461,4.376,466,1.556,469,3.309,470,2.081,471,2.682,472,1.237,477,0.906,480,0.991,497,2.206,521,1.311,534,1.312,536,4.401,541,2.046,553,1.136,557,3.226,560,1.574,565,3.226,568,4.466,578,1.572,621,2.197,643,1.373,646,2.401,649,1.79,654,2.839,672,3.228,683,2.327,717,1.289,752,2.717,842,1.861,894,1.161,979,1.86,990,1.36,1021,1.36,1026,4.574,1029,2.258,1035,1.336,1039,1.198,1114,2.004,1146,2.142,1152,1.676,1187,1.457,1312,2.308,1332,2.046,1367,3.274,1382,2.497,1428,3.733,1475,2.405,1531,3.399,1536,3.347,1539,1.928,1586,1.289,1591,3.03,1620,2.883,1623,4.896,1686,2.497,1687,4.678,1825,2.607,1834,5.368,1915,2.258,1926,4.995,1928,5.709,1929,4.995,2126,4.119,2139,2.046,2163,2.405,2201,2.327,2319,3.929,2555,2.356,2775,3.613,3213,5.216,3214,5.216,3215,2.745,3221,2.745,3271,4.897,3414,6.777,3556,2.745,4140,2.607,5447,5.139,5448,5.139,5449,3.211,5450,5.139,5451,4.689,5452,3.211,5453,3.211,5454,3.211,5455,3.211,5456,3.211,5457,3.211,5458,3.211,5459,5.139,5460,3.211,5461,3.211,5462,3.211,5463,3.211,5464,3.211,5465,3.211,5466,3.211,5467,3.211,5468,3.211,5469,3.211,5470,3.211,5471,3.211]],["t/1035",[2,1.878,4,1.112,8,1.382,14,1.114,58,1.668,62,1.113,63,1.729,64,2.311,66,1.699,74,0.909,78,0.566,80,1.262,83,0.407,91,0.346,100,1.039,103,0.856,104,1.146,109,0.909,139,1.803,140,1.635,145,1.175,153,1.221,165,2.527,171,3.455,187,2.583,196,1.168,209,1.42,212,1.086,221,1.434,231,3.206,249,0.894,250,0.934,254,0.722,258,0.989,261,0.729,265,0.671,275,1.942,288,1.362,290,3.299,311,1.271,328,2.617,339,3.007,340,1.301,346,1.917,347,1.799,349,0.798,352,2.158,363,2.158,402,1.194,406,1.315,418,2.87,419,2.025,420,3.738,421,2.469,422,1.288,461,3.006,474,3.186,476,2.225,477,1.119,497,3.048,521,1.011,536,1.729,548,2.225,553,1.402,565,1.991,578,1.214,591,1.65,597,2.788,607,2.298,619,2.788,646,1.699,650,2.808,651,1.696,654,2.191,667,3.122,669,2.75,676,1.592,752,3.284,820,1.837,842,2.298,867,1.635,949,4.262,959,1.763,979,1.835,1036,1.696,1039,1.479,1124,4.59,1125,3.176,1144,3.4,1189,2.527,1274,7.043,1312,1.781,1326,1.696,1440,2.97,1457,2.426,1493,2.098,1586,2.434,1613,3.662,1623,5.812,1638,2.225,1834,3.007,1891,3.122,2201,7.054,2296,4.262,2320,2.645,2321,2.788,2555,3.373,2646,2.788,2660,3.391,3271,2.645,3275,3.391,3897,7.598,4929,5.182,5441,3.619,5451,3.619,5472,3.966,5473,8.238,5474,6.061,5475,6.061,5476,6.061,5477,6.061,5478,6.061,5479,6.061,5480,6.061,5481,6.061,5482,6.061,5483,3.966,5484,3.966,5485,3.966,5486,3.966,5487,6.061,5488,3.966]],["t/1037",[1,1.861,2,3.475,8,2.557,51,1.477,56,3.055,62,0.92,83,0.428,91,0.64,98,3.109,100,1.061,104,2.121,142,2.897,145,2.175,165,2.52,177,3.994,191,3.08,223,3.23,236,2.972,241,2.803,261,1.35,265,1.241,284,2.384,301,1.129,303,2.148,363,3.994,378,4.327,402,1.841,438,3.54,442,3.937,461,2.998,469,3.78,497,2.52,555,4.894,588,3.475,595,3.937,632,3.78,843,4.117,867,3.025,975,4.894,976,4.78,979,1.931,1117,3.475,1146,4.894,1185,6.274,1275,6.274,1294,5.02,1378,5.02,1531,3.882,1623,6.082,1834,3.64,2574,6.274,4213,5.958,5372,6.274]]],"invertedIndex":[["",{"_index":14,"t":{"4":{"position":[[160,2],[191,2],[211,2],[228,2],[245,2],[264,2],[279,2],[291,2],[305,2],[318,2],[338,2],[353,2],[356,2],[367,2],[382,2],[385,2],[401,2],[404,2],[455,2],[458,2],[485,2]]},"8":{"position":[[213,1]]},"21":{"position":[[286,1],[295,1],[299,1],[301,1],[315,1],[324,1],[328,1],[330,1],[345,1],[354,1],[358,1],[360,1],[496,1],[1052,1],[1977,1],[2143,1],[2207,1],[2438,1],[2482,1],[2542,1],[2905,1],[2918,1],[3767,1],[3769,1],[3786,1],[3788,1],[3859,2]]},"23":{"position":[[1087,1],[1100,1]]},"25":{"position":[[733,1],[858,1]]},"27":{"position":[[302,1],[310,1],[465,1]]},"29":{"position":[[270,1],[573,1],[626,1],[633,1]]},"31":{"position":[[861,1],[871,1],[1082,1],[1092,1],[1099,1],[1285,1],[1295,1],[1302,1],[1309,1],[1314,1],[1316,1],[1509,1],[1519,1],[1526,1],[1533,1],[1538,1],[1540,1],[1546,1],[1938,1],[1948,1],[1955,1],[1962,1],[1967,1],[1969,1],[1975,1],[2002,1],[2140,1],[2142,1]]},"33":{"position":[[417,1],[520,1]]},"41":{"position":[[76,1],[156,1],[160,1],[164,1],[169,1],[176,1],[181,1],[187,1],[222,1],[226,1],[230,1],[235,1],[242,1],[247,1],[253,1],[273,3]]},"43":{"position":[[517,1],[904,1],[1143,1]]},"45":{"position":[[91,1],[162,2],[165,2],[168,2],[171,2],[174,2],[177,2],[180,2],[546,1],[562,1],[570,1],[576,1],[578,3]]},"47":{"position":[[1186,1],[2187,1],[4165,1],[4682,1],[6283,1],[6288,1],[6293,1],[7173,1],[7178,1],[7183,1],[8026,1]]},"51":{"position":[[352,1],[1115,1]]},"77":{"position":[[1274,1],[1373,1],[1493,4],[4555,1]]},"81":{"position":[[494,2],[564,2],[1316,1],[1411,3],[1475,2],[1512,1]]},"83":{"position":[[867,2],[913,1],[1059,1],[1084,3]]},"99":{"position":[[1989,2]]},"105":{"position":[[219,1]]},"117":{"position":[[551,1],[626,3],[640,3],[654,3],[678,3],[696,3],[731,3],[744,3],[825,2]]},"121":{"position":[[933,1],[1360,1],[1417,1]]},"125":{"position":[[759,1],[1402,1]]},"133":{"position":[[139,1],[246,1]]},"135":{"position":[[484,1]]},"137":{"position":[[126,1],[150,3],[305,1],[311,3],[402,2],[648,1],[777,1],[843,2],[949,1],[987,2],[1168,2]]},"139":{"position":[[262,1]]},"145":{"position":[[567,1],[608,2],[656,1]]},"151":{"position":[[220,2]]},"153":{"position":[[55,1],[82,1]]},"155":{"position":[[100,1],[127,1]]},"157":{"position":[[132,1],[157,1]]},"159":{"position":[[106,1],[199,1]]},"161":{"position":[[85,1],[168,1]]},"163":{"position":[[64,1]]},"165":{"position":[[136,1],[277,1],[290,1],[895,1]]},"167":{"position":[[81,1],[126,1]]},"173":{"position":[[89,1]]},"175":{"position":[[263,1],[267,1],[274,1],[657,1]]},"177":{"position":[[45,1]]},"181":{"position":[[73,1]]},"183":{"position":[[118,1],[175,1]]},"189":{"position":[[295,1],[396,1],[944,1],[962,1],[966,1],[971,1],[977,1],[986,1],[1001,3],[1576,1],[1583,1],[1705,1],[2263,1]]},"193":{"position":[[148,1],[155,1],[365,1],[372,1]]},"195":{"position":[[160,1],[167,1],[410,1],[847,1],[854,1],[1069,1],[1076,1],[1212,1],[1219,1],[1421,1],[1428,1],[1805,1],[1812,1],[1872,1],[1879,1],[2385,1]]},"197":{"position":[[98,1],[105,1]]},"199":{"position":[[125,1],[132,1],[618,1],[625,1]]},"201":{"position":[[186,1],[193,1],[263,1],[270,1]]},"203":{"position":[[133,1],[140,1],[142,2],[177,2],[540,1]]},"209":{"position":[[329,1],[336,1]]},"211":{"position":[[71,1],[78,1],[635,1],[659,1]]},"213":{"position":[[89,2],[216,1],[267,2],[588,2],[662,2],[895,1]]},"215":{"position":[[217,1],[263,2],[266,3],[273,3],[338,1],[340,1],[348,3],[406,1],[408,1],[416,3],[478,1],[480,1],[488,3],[549,1],[551,1]]},"217":{"position":[[221,1],[861,1],[869,1],[876,1],[928,1],[980,1],[1313,1]]},"219":{"position":[[503,1],[558,1],[680,1],[693,7],[783,1],[1133,1],[1161,2],[1164,1],[1374,1],[1388,2],[1524,1],[1738,1],[1777,2],[1780,2]]},"225":{"position":[[319,2],[334,1],[896,1],[953,1],[1274,1],[1386,1]]},"229":{"position":[[186,1],[265,3],[299,1],[536,1]]},"231":{"position":[[153,1],[307,1],[383,1],[622,1],[734,1],[757,1],[1338,1],[1408,1],[1419,1],[1669,1],[1679,1],[1871,1],[1903,1],[1975,1],[1985,1],[2073,1],[2131,1]]},"233":{"position":[[157,1],[167,1],[334,1],[381,1],[430,1]]},"235":{"position":[[308,1],[318,1],[640,1],[653,1]]},"243":{"position":[[158,1],[1634,1]]},"247":{"position":[[680,1]]},"249":{"position":[[996,1],[1064,1],[1119,1],[1175,1]]},"251":{"position":[[110,1],[534,1],[587,1],[594,1],[798,2]]},"253":{"position":[[588,1],[621,1],[1256,1],[1309,1],[1347,1],[1689,1],[1788,1],[1794,1],[2048,1],[2172,1],[2426,1],[2436,1],[2442,1],[2777,1],[2805,1],[3004,1],[3235,1],[3276,1],[3506,1]]},"255":{"position":[[908,1],[1115,1],[1121,1],[1393,1],[1420,1],[1446,1],[1632,2],[1667,1],[1693,2],[1736,2],[1774,2]]},"257":{"position":[[71,1],[538,1],[576,1],[682,1],[719,1],[1233,1],[2185,1],[2266,3],[3688,1],[3730,1],[4026,1],[4259,1],[5759,2],[5968,1],[6395,1],[6563,1]]},"259":{"position":[[171,1],[178,1],[195,1],[202,1]]},"260":{"position":[[299,1],[306,1],[313,1],[323,4],[328,1],[804,4],[865,1]]},"262":{"position":[[1431,1],[1507,1],[1606,1],[1705,2],[2026,2]]},"288":{"position":[[905,1],[1707,1],[1716,1],[1796,2]]},"290":{"position":[[425,1],[432,1],[696,1],[812,1]]},"292":{"position":[[2004,2]]},"296":{"position":[[230,1],[277,1],[279,1],[317,1]]},"300":{"position":[[193,1],[424,1]]},"302":{"position":[[97,3],[106,3],[371,1],[886,1],[908,2],[958,1],[1162,1],[1177,2],[1287,1],[1323,2],[1421,2]]},"304":{"position":[[885,1],[1168,1],[2159,1],[2172,1],[2256,2],[2259,2],[2262,1],[2291,1],[2308,4],[2492,1]]},"306":{"position":[[119,1],[135,2],[138,1],[155,1],[399,1],[470,1],[531,3]]},"308":{"position":[[245,1],[469,1]]},"310":{"position":[[677,1]]},"314":{"position":[[128,1]]},"316":{"position":[[98,1]]},"318":{"position":[[116,1]]},"320":{"position":[[181,1]]},"322":{"position":[[83,1]]},"324":{"position":[[114,1]]},"326":{"position":[[294,1]]},"328":{"position":[[181,1],[287,1]]},"340":{"position":[[101,1],[180,5],[201,2],[244,1],[304,1],[365,1]]},"342":{"position":[[48,6],[806,1],[883,1],[960,5],[1367,1],[1565,1],[1593,1],[1619,1]]},"344":{"position":[[528,1],[1894,2],[1994,3],[2977,3],[3239,1],[3777,2],[4382,7],[4447,6],[4836,1]]},"346":{"position":[[669,1],[709,1],[1164,2]]},"348":{"position":[[249,9],[342,2]]},"350":{"position":[[505,4],[569,4],[877,1],[1036,5],[1202,5],[1420,7]]},"352":{"position":[[1166,2],[2142,1],[2229,5]]},"362":{"position":[[452,1]]},"364":{"position":[[121,1],[611,1],[838,1],[1326,1],[1804,1],[1947,1],[2037,1]]},"366":{"position":[[418,1],[937,1],[1456,1],[1537,1],[1753,1],[2274,1],[3111,1],[3429,1],[3478,2],[3481,3],[3847,1],[4080,3],[4084,2],[4110,1],[4269,2],[4370,2],[4587,1],[4623,1]]},"368":{"position":[[87,1],[698,1],[832,1],[858,3],[1385,1],[1387,3],[1723,1]]},"370":{"position":[[40,1],[158,1],[410,1],[433,2],[651,2],[698,2],[750,2],[801,2],[869,2],[916,2],[963,1],[979,2],[1030,2],[1126,2]]},"372":{"position":[[34,1],[127,1],[189,1],[271,1],[314,3],[838,1],[987,1]]},"374":{"position":[[149,1],[476,1],[504,1],[809,1]]},"376":{"position":[[485,1],[495,8],[1111,7],[1172,1],[1197,2],[1364,1],[1374,5],[1380,4],[1580,1],[1648,3],[1652,1],[1852,1],[1862,5],[1868,5],[2721,1],[2733,6],[3389,1],[3401,6],[3408,4],[3535,3],[3539,5],[3670,7],[4095,1],[4116,6],[4454,1],[4475,6],[4552,1],[4590,1],[4856,1],[4877,6],[5028,2],[5068,2],[5071,1],[5231,1],[5252,6],[5545,1],[5563,6],[5628,1],[5712,1],[5822,1],[6479,1],[6491,7],[6499,6],[6638,4]]},"378":{"position":[[875,1],[881,2],[888,1],[1300,1],[1334,1],[1364,1],[1395,1],[1585,1],[1927,1],[2059,1],[2098,1],[2137,1],[2502,1],[2676,1],[2678,1],[2802,1],[2883,1],[2900,1],[3397,1],[3442,3],[3718,1]]},"380":{"position":[[168,1],[354,1],[363,2]]},"386":{"position":[[1793,1],[1798,1]]},"390":{"position":[[61,1],[199,1],[233,1],[242,1],[251,1],[261,1],[274,1],[284,1],[307,1],[324,1],[342,1],[360,1],[370,1],[388,1],[985,1]]},"394":{"position":[[1068,3],[1962,1],[1973,3]]},"396":{"position":[[355,1],[376,3],[950,1],[969,1],[1595,1],[1635,1]]},"398":{"position":[[436,3]]},"400":{"position":[[156,1],[232,2],[271,2],[329,2],[505,1],[874,1],[928,2],[1051,1],[1063,1],[1128,1],[1477,1],[1487,1],[1538,2]]},"402":{"position":[[467,1],[507,3],[1571,1]]},"404":{"position":[[304,1],[554,1],[580,1],[600,1]]},"406":{"position":[[449,1],[571,1],[829,1],[933,1],[945,1]]},"420":{"position":[[360,1],[410,1]]},"422":{"position":[[153,1],[226,1],[463,1],[632,1],[725,1],[1050,1],[1565,1],[1656,1],[1662,1],[1678,1],[1792,1],[1880,1],[1886,1],[1902,1]]},"424":{"position":[[53,1],[177,1],[184,1],[194,1],[205,4],[210,1],[223,1]]},"426":{"position":[[16,1],[246,1],[314,1],[316,1],[323,1],[325,1],[335,1],[337,1],[348,4],[353,1],[355,1],[368,1],[370,1]]},"430":{"position":[[348,1],[355,1],[365,1],[376,4],[381,1],[394,1],[443,3]]},"432":{"position":[[1017,1]]},"434":{"position":[[763,1],[1213,1],[1539,1],[2093,1]]},"436":{"position":[[110,1],[165,1],[167,1]]},"438":{"position":[[371,1],[378,1],[388,1],[399,4],[404,1],[417,1],[577,1],[1111,1]]},"442":{"position":[[207,1],[498,1],[1627,2]]},"446":{"position":[[423,1],[1293,1],[1757,1],[2618,1],[2966,1],[3306,1],[3471,1],[3512,1]]},"448":{"position":[[269,1],[315,1],[320,3],[405,1],[451,1],[456,3],[465,1],[619,3],[875,3],[913,3],[1038,1],[1068,1],[1244,1],[1250,1],[1256,1],[1262,1],[1268,1],[1592,1],[1626,1]]},"450":{"position":[[155,1],[206,1],[867,1],[1524,1],[1543,1],[1553,1],[1990,1],[2009,1],[2019,1],[2026,1],[2497,1],[2516,1],[2526,1],[2533,1],[3088,1],[4190,1],[4245,1],[4374,1],[4429,1],[4692,1],[4699,1],[4825,1],[4871,1],[5040,1],[5086,1]]},"452":{"position":[[87,1],[120,1],[243,1],[249,1],[255,1],[272,1]]},"454":{"position":[[88,1],[139,1],[146,1],[543,1],[594,1],[604,1],[1424,1],[1480,1],[1493,1],[1500,1]]},"456":{"position":[[569,1],[620,1],[627,1]]},"458":{"position":[[617,3]]},"462":{"position":[[442,1],[955,1],[962,1],[1441,1],[1448,1],[1457,1],[1898,1],[1905,1],[1914,1],[1972,3],[2190,1],[2197,1],[2206,1],[2275,3],[2582,1],[2589,1],[2598,1]]},"464":{"position":[[131,1],[166,1],[283,1],[290,1],[300,1],[607,1],[614,1],[624,1],[1215,1],[1222,1],[1240,1]]},"466":{"position":[[124,1],[149,1],[159,1],[559,1],[566,1],[576,1],[1082,1],[1089,1],[1099,1]]},"468":{"position":[[314,1],[321,1],[345,1],[1042,1],[1049,1],[1073,1],[1090,2],[1096,2],[1265,3],[1296,2],[1913,1],[1920,1],[1944,1],[1961,2],[1967,2],[2457,2],[2546,1],[2552,1],[2563,1],[2589,1],[2733,1],[2786,2],[2942,1],[2949,1],[2977,2],[2980,2],[2987,2]]},"470":{"position":[[430,1],[615,1],[642,1]]},"472":{"position":[[108,1],[121,1],[132,3],[366,1]]},"480":{"position":[[346,1]]},"482":{"position":[[106,1],[1031,1]]},"486":{"position":[[442,1],[570,20],[653,3],[803,1]]},"488":{"position":[[299,1],[305,1],[325,3]]},"490":{"position":[[942,1],[1008,1],[1213,1],[1273,1],[1401,1],[1502,1]]},"492":{"position":[[316,1]]},"496":{"position":[[163,2],[171,3],[183,2],[198,2],[201,1],[203,2],[246,2],[254,3],[266,2],[281,2],[284,1],[286,2],[337,1],[389,1],[457,1]]},"500":{"position":[[121,1],[159,1],[173,4],[395,1],[531,1],[790,1],[1018,1]]},"502":{"position":[[347,1],[394,1],[654,1],[982,1],[1004,1]]},"506":{"position":[[98,1],[194,1],[816,1],[1836,1],[1946,1]]},"508":{"position":[[2307,1],[2905,1],[2950,1],[3114,8],[3431,2],[3813,1],[4574,1],[4576,1],[4624,1],[4699,1],[4701,1]]},"510":{"position":[[187,1],[374,1],[414,1],[454,1],[752,1],[976,1],[1016,1],[1041,1],[1408,1],[1746,1],[1771,1],[1810,1],[1821,1],[1852,1],[1863,1],[1888,1],[1899,2],[1926,1],[1938,1],[1962,1],[2013,1],[2080,1],[2137,3],[2146,1],[2210,2],[2213,1],[2274,1],[2335,1],[2799,1],[2832,1],[2839,1]]},"512":{"position":[[242,1],[366,1],[373,1],[383,1],[394,4],[399,1],[409,1],[694,1],[824,1],[891,1],[1011,1],[1013,1],[1020,1],[1022,1],[1032,1],[1034,1],[1045,4],[1050,1],[1052,1],[1065,1],[1067,1]]},"514":{"position":[[522,1]]},"518":{"position":[[56,1],[356,1],[648,1]]},"520":{"position":[[96,1],[236,1],[238,1],[345,1]]},"522":{"position":[[857,1],[894,1],[931,1],[1017,1],[1421,1]]},"524":{"position":[[200,1],[257,1],[379,1],[785,1],[850,1],[888,1]]},"526":{"position":[[102,1],[155,1],[184,3],[206,1],[221,1],[258,2],[352,1],[455,2],[530,2],[912,1],[958,1],[1167,1],[1258,2],[1781,2],[1809,2],[1851,2],[2032,2],[2039,2]]},"528":{"position":[[293,1],[339,1],[341,1],[407,1]]},"530":{"position":[[129,1],[147,1],[215,1],[280,1],[292,1],[304,2],[449,1]]},"532":{"position":[[119,1],[139,1],[216,1],[245,1],[430,1],[1102,1]]},"534":{"position":[[207,1],[294,1],[296,1],[373,1],[728,1],[1421,1],[1546,1],[2143,1],[2158,2]]},"536":{"position":[[702,1],[717,2],[1493,3],[1526,1]]},"538":{"position":[[805,1],[970,1],[1243,1],[1343,1],[1426,1],[2332,3],[2503,1],[2628,1],[3029,1],[3604,1],[3606,1],[3626,2],[3748,2]]},"540":{"position":[[103,1]]},"544":{"position":[[500,1],[688,1],[716,1],[757,3],[1289,1],[1291,1],[1355,1],[1373,1],[1469,1],[1479,1],[1517,1],[1527,1],[1533,1],[1725,1],[2177,1],[2241,1]]},"552":{"position":[[325,1],[759,1],[952,1],[954,1]]},"554":{"position":[[76,1],[78,1],[93,2],[963,1]]},"556":{"position":[[256,1]]},"558":{"position":[[301,1],[328,2],[406,1],[433,2],[1766,1],[1793,2],[1871,1],[1898,2]]},"560":{"position":[[332,1],[385,2]]},"564":{"position":[[66,2],[92,2],[146,1],[163,1],[165,2],[168,1],[185,2],[497,1],[532,2]]},"566":{"position":[[209,2],[227,2],[245,3],[414,2],[421,2],[475,2],[963,2],[1136,4]]},"568":{"position":[[86,2],[403,2],[413,2],[427,3],[973,2],[983,2],[999,3]]},"570":{"position":[[185,2],[262,2],[789,2],[802,1],[817,1],[819,2],[1239,1],[1292,2],[1364,2],[1601,2]]},"572":{"position":[[245,2],[257,1],[293,2],[296,2],[542,1],[576,1],[580,1],[586,1],[618,2],[623,1],[627,1],[656,2],[659,2],[819,1],[895,2],[939,2],[942,2]]},"574":{"position":[[357,1],[487,1],[554,1],[673,1],[675,1],[682,1],[684,1],[694,1],[696,1],[707,4],[712,1],[714,1],[724,1],[726,1],[906,1],[977,1],[1039,1],[1100,1],[1167,1],[1287,2],[1297,2],[1313,3],[1322,1],[1429,2],[1446,2],[1456,3],[1499,2],[1516,2],[1525,3],[1682,2],[1699,2],[1709,3],[1718,1],[1801,1],[1803,1],[1810,1],[1812,1],[1822,1],[1824,1],[1835,4],[1840,1],[1842,1],[1855,1],[1857,1],[1888,2],[1905,2],[1914,3],[1923,1],[1963,1],[1965,1],[1989,1],[2045,1],[2051,1],[2064,1],[2182,1],[2184,1],[2190,1],[2192,1],[2208,1],[2210,1],[2216,1],[2218,1],[2225,1],[2227,1],[2237,1],[2239,1],[2250,4],[2255,1],[2257,1],[2270,1],[2272,1],[2303,1],[2377,1]]},"584":{"position":[[104,1],[138,3],[142,1],[144,1],[1673,1]]},"586":{"position":[[2107,1],[2121,1],[2157,1],[2159,1],[2174,2]]},"588":{"position":[[475,1],[634,1],[1725,1],[1878,1]]},"590":{"position":[[161,2],[176,1],[190,1],[694,2],[699,1],[706,2]]},"592":{"position":[[305,1],[313,1]]},"594":{"position":[[522,1],[660,1],[694,1],[837,1],[846,1],[909,1],[921,1],[927,2]]},"596":{"position":[[225,1],[968,2],[971,1],[984,2],[1013,1],[1124,2],[1127,1]]},"598":{"position":[[424,1],[436,1],[452,2],[468,1],[512,1]]},"600":{"position":[[586,1],[761,1],[1567,1]]},"602":{"position":[[747,1],[819,1],[821,1],[834,1],[836,2],[839,1],[841,1],[854,2],[875,1],[955,1],[964,1],[1010,1],[1022,1],[1028,2],[1046,1],[1103,1],[1115,1],[1121,2]]},"604":{"position":[[1418,2]]},"608":{"position":[[180,1],[375,1],[1404,1],[1418,4],[1458,1],[1708,1]]},"612":{"position":[[647,1],[766,1],[768,1],[775,1],[777,1],[787,1],[789,1],[800,4],[805,1],[807,1],[820,1],[822,1],[1124,1],[1389,1],[1733,1]]},"616":{"position":[[194,1]]},"624":{"position":[[319,1],[373,1],[384,2],[387,2],[694,4],[771,3]]},"626":{"position":[[323,1],[856,1],[1250,1],[1286,1],[1390,1],[1409,2],[1456,1],[1489,1],[1510,1],[1532,1],[1538,1],[1544,4],[1549,1],[1758,1],[3708,1],[3770,1],[4270,1],[4476,1],[4497,2],[4505,1],[5280,1]]},"634":{"position":[[447,1],[479,1],[506,1],[551,1],[569,2],[615,2],[1575,1],[1629,1],[1640,2],[1643,2],[1760,1],[2284,1],[2303,1]]},"636":{"position":[[1734,1]]},"640":{"position":[[1357,2],[1395,2]]},"648":{"position":[[183,1],[534,1],[806,1],[1209,1],[2003,1],[2167,2]]},"654":{"position":[[1977,1],[2425,1],[2599,1],[2620,1],[2891,3],[2908,3],[3404,3]]},"656":{"position":[[719,1],[919,1]]},"658":{"position":[[309,1],[1402,1],[1640,1],[1741,1],[2400,1]]},"660":{"position":[[248,1],[306,1],[311,13],[632,1],[844,1],[888,1],[974,1],[1019,1],[1092,1],[1152,1],[1198,1]]},"666":{"position":[[187,1],[865,1],[867,1],[1021,1],[1137,1],[1275,1],[1350,1],[1441,1],[1460,1],[1471,1],[1504,1],[1514,2]]},"668":{"position":[[379,1],[852,1],[1466,1],[1468,1],[1535,1],[1537,1],[1592,2],[1602,1],[1619,1],[1859,1],[1889,1],[1941,1],[1964,1],[1973,2],[2108,1],[2110,1],[2142,2],[2150,1],[2212,1],[2331,1],[2363,2]]},"680":{"position":[[676,1],[874,1],[1156,1],[1584,1]]},"682":{"position":[[260,1],[1021,1],[1031,1],[1199,1],[2629,1]]},"684":{"position":[[158,1],[244,1],[316,3],[780,3],[1048,1],[1521,1],[2180,1],[2619,1],[2629,2],[2940,1]]},"688":{"position":[[449,1],[941,1],[972,2],[1328,1],[1338,2],[1895,1],[1973,3],[2457,1],[2541,3],[2561,3],[2891,1],[2930,2],[2967,2]]},"690":{"position":[[299,1],[398,1],[402,2]]},"692":{"position":[[129,1],[618,1],[723,2],[1100,1],[1390,3],[1868,1],[1956,1],[1983,1],[2034,1]]},"694":{"position":[[352,1],[1075,1],[1762,1],[1835,2],[1838,1],[1898,1],[1944,1],[2201,1]]},"696":{"position":[[1329,1],[1450,2],[1577,1],[1695,2],[1851,1],[2309,1],[3232,6],[3298,7],[3335,6],[3394,7],[4084,4],[4089,3],[4097,4],[4611,1],[4963,3],[5290,1],[5334,1],[5371,1],[5416,1],[5489,1],[5549,1],[5595,1]]},"700":{"position":[[303,1],[492,1],[800,1],[1067,1],[1539,1],[1718,1],[1887,1],[2091,2],[2168,2],[2448,1],[3031,1],[3172,1],[3182,2],[3305,1],[3411,1],[3456,2],[3638,1],[3773,1],[3844,1]]},"702":{"position":[[376,1],[420,1],[457,1],[502,1],[575,1],[635,1],[681,1],[1660,1],[2513,1],[2701,1],[2788,1]]},"708":{"position":[[636,1],[817,1],[872,1],[919,1],[962,1],[1271,1],[1300,1],[1441,1]]},"712":{"position":[[125,1],[127,1],[131,1]]},"714":{"position":[[583,1],[927,2],[1231,1],[1334,2],[1373,2],[1411,2],[1418,1],[1481,1],[1595,2],[1600,1],[1623,2],[1675,2],[2523,1],[2528,1],[2590,2],[2593,2],[2617,1],[2669,2],[2707,2],[2938,1],[2950,4],[2955,2],[2967,1],[2996,1],[3025,1],[3054,1],[3167,1],[3223,2]]},"716":{"position":[[3133,1],[3143,1],[4695,1],[4931,1],[5469,2],[5476,3]]},"718":{"position":[[164,1],[166,1],[177,1],[548,1],[556,1],[577,3],[581,1],[652,1],[671,3],[924,3],[1147,2],[1343,1],[1367,1],[1393,1],[1920,1],[1958,1],[2008,1],[2016,1],[2037,3],[2041,1],[2166,1],[2192,1]]},"720":{"position":[[562,1],[639,1],[710,1],[712,1],[765,1],[805,1],[847,1],[890,1],[931,1],[975,1],[1016,1],[1058,1],[1069,1],[1122,1],[1162,1],[1204,1],[1247,1],[1288,1],[1332,1],[1373,1],[1415,1],[2145,1],[2218,1],[2295,1],[2330,1],[2362,1],[2407,1],[2451,1],[2487,1],[2756,1],[2838,1],[2861,1],[2898,1],[2935,1],[2937,2],[2949,2],[2971,2],[3067,1],[3069,2],[3082,1],[3122,1],[3170,1],[3172,2],[3197,1],[3199,2],[3211,2],[3233,2],[3338,1],[3340,2],[3343,1],[3371,2],[3374,1],[3434,2],[3442,1],[3504,1],[3524,1],[3526,2],[4166,1],[4347,2],[4353,2],[5065,2],[5072,2],[5376,1],[5531,1],[5768,1],[5811,1],[5840,1],[5887,1],[5954,1],[6023,1],[6025,1],[6225,1],[6255,1],[6307,1],[6330,1],[6339,2],[6488,1],[6490,1],[6522,2],[6530,1],[6592,1],[6718,1],[6750,2],[6860,1],[6862,1],[7019,3],[7354,1],[8187,1]]},"722":{"position":[[177,2],[270,2],[348,2],[726,1],[758,2],[766,2],[838,1],[895,1],[1161,1],[1181,1],[1183,2],[1268,1],[1359,2],[1465,1],[1475,1]]},"732":{"position":[[53,1]]},"734":{"position":[[349,1],[821,1],[843,4],[1132,1],[1489,1],[1516,2],[1527,1],[1546,1],[1548,2],[1582,1],[1642,1],[1710,1],[1809,1],[1827,1],[1833,2],[2175,1],[2233,1],[2257,2],[2286,2],[2325,2],[2657,1],[2850,4],[2855,2],[2858,1],[2862,1],[2864,2],[2881,2],[3180,1],[3201,2],[3209,1]]},"738":{"position":[[1086,1],[1333,1],[1344,2],[1347,2],[1653,1],[2099,1],[2980,1],[3173,1],[3242,1],[3306,1],[4227,1],[4631,1],[4698,1],[4730,2],[4739,2],[4963,1],[5016,1],[5072,1],[5134,1],[6421,1],[6514,1],[6574,1],[6643,1],[6695,1],[6756,1],[6834,1],[6910,1],[6971,1],[6973,1]]},"740":{"position":[[197,1],[233,1]]},"742":{"position":[[357,1],[751,1],[814,1],[816,2]]},"744":{"position":[[1187,1],[1281,1],[1430,1],[1507,1],[1518,2],[1521,2],[1583,1],[1599,2],[1641,1],[1659,1],[1661,2],[2071,1],[2147,2],[2244,1],[2673,1],[2682,1],[2684,1],[2725,1],[2787,1]]},"746":{"position":[[654,1],[692,1],[714,1],[716,2],[728,1],[832,1],[834,2],[860,1],[1009,2],[1533,1]]},"760":{"position":[[49,1],[881,1],[2667,1],[2799,1],[3020,1],[3022,1]]},"762":{"position":[[633,1]]},"764":{"position":[[138,1],[159,1],[831,1],[1107,1],[1134,1],[1175,1],[1343,1],[1495,1],[1830,1],[1934,3]]},"766":{"position":[[186,1],[308,1],[673,1],[730,1],[783,1],[1006,2],[1045,2],[1083,2],[1121,2],[1136,2],[1158,2],[1684,1],[1779,2],[1813,2],[1932,2],[1968,2],[2004,2]]},"768":{"position":[[336,2],[386,2],[441,2]]},"770":{"position":[[741,2],[786,1],[844,1],[895,1],[965,1],[1022,1],[1096,1],[1190,1]]},"772":{"position":[[574,1]]},"774":{"position":[[612,3],[1116,3],[1469,1],[1518,1]]},"776":{"position":[[707,1],[783,1],[841,1],[843,2],[919,1]]},"778":{"position":[[181,1],[375,3],[400,3],[454,3],[745,2],[793,1],[897,1],[1487,1],[2048,1],[2050,2],[2095,1],[2097,2]]},"800":{"position":[[1888,1]]},"802":{"position":[[440,1],[1349,1],[1378,1],[1401,1],[1470,1],[1529,1],[1613,1],[1686,1],[1755,1],[1781,1],[1783,2],[1786,1],[2139,1],[2376,1],[2415,1],[2758,1],[2774,1],[2803,1],[3273,1],[3299,1],[3301,2]]},"804":{"position":[[479,1],[520,1],[562,1],[647,1],[732,1],[781,2],[880,1],[914,1],[1034,2],[1049,2],[1058,1],[1093,1],[1138,1],[1195,1],[1742,1],[1805,1],[1834,1],[1860,1],[1893,1],[2027,1],[2066,1]]},"806":{"position":[[881,1],[929,1],[935,1],[1012,1],[1060,1],[1102,1],[1531,1],[1611,1],[1633,1],[1714,1],[1716,2],[1719,1],[1721,1],[1790,1],[1802,1],[1819,1],[1854,1],[1867,1],[1933,1],[1951,5],[2003,1],[2066,1],[2092,1],[2094,5],[2100,1],[2192,1],[2205,1],[2212,1],[2234,1],[2247,1],[2259,1],[2271,1],[2330,1],[2342,1],[3078,1],[3117,1],[3530,3],[3684,1],[3755,1],[3760,1]]},"808":{"position":[[1239,1]]},"822":{"position":[[124,1]]},"826":{"position":[[450,1],[481,1]]},"828":{"position":[[368,1],[454,1],[1457,2],[1773,1],[1803,1]]},"830":{"position":[[407,2],[964,1],[1106,2]]},"832":{"position":[[458,1],[1249,1],[1327,1],[1421,1],[1447,1]]},"834":{"position":[[1461,1],[1897,1],[2328,1],[2732,1],[2858,1],[3105,1],[3114,1],[3230,1],[3394,1],[3414,1],[3426,1],[4030,1],[4643,1]]},"838":{"position":[[559,1],[2203,1],[2272,1]]},"842":{"position":[[59,2],[245,1],[277,1],[306,1],[334,1],[362,1],[393,1],[418,1],[457,1],[488,1],[515,1],[542,1],[571,1],[759,1],[831,1],[922,1],[1064,1],[1142,1],[1175,1],[1269,1],[1295,1],[1549,1],[1579,1],[1641,1],[1687,1],[1718,1],[1838,1]]},"850":{"position":[[761,1],[999,1],[2004,2],[2017,1],[2071,2]]},"852":{"position":[[273,1],[1002,1],[1386,1],[1585,1],[1694,1],[1925,2],[1928,2],[1962,3],[2295,1]]},"854":{"position":[[566,1],[741,1]]},"856":{"position":[[270,1]]},"858":{"position":[[495,1],[665,2],[839,2],[842,1],[847,1],[865,2],[896,1],[914,2],[1957,1],[1962,1],[1987,2],[2141,1],[2152,1],[2397,1],[2502,1],[3394,2]]},"860":{"position":[[1081,1],[1153,1],[1174,1],[1183,1],[1188,1],[1289,1],[1319,1],[1338,2],[1341,1],[1409,1],[1429,1],[1457,2],[1460,1],[1509,2],[1512,1],[1572,2],[1575,1],[1631,1],[1633,1],[1686,2],[1937,2],[2093,1],[2486,2]]},"862":{"position":[[1173,2],[1176,1],[1478,1],[1935,1],[1951,2],[2145,1],[2806,1],[2813,2],[3306,2],[3309,1],[3427,1],[3434,2],[3680,1],[3695,1],[4006,1],[4061,1]]},"864":{"position":[[453,2],[474,2],[521,2],[544,2],[547,2]]},"866":{"position":[[256,1]]},"878":{"position":[[532,2],[547,2],[1480,1],[1637,2],[1701,2],[1729,1],[1731,2],[1753,1],[1755,2],[1781,1],[1783,2],[1830,1],[1832,1],[1865,1]]},"892":{"position":[[1687,2],[1693,2],[1696,1],[1698,2],[1707,1],[1709,1],[1711,2],[1717,1],[1719,1],[1721,1],[1723,2],[1726,1],[1728,1],[1730,1],[1732,1],[1736,2],[1739,1],[1743,1],[1751,2],[1754,1],[1756,1],[1762,1],[1764,1],[1768,3],[1779,1],[1787,1],[1789,1],[1791,1],[1800,1],[1802,1],[1804,2],[1823,1],[2188,1],[2193,1]]},"896":{"position":[[378,1],[2951,1],[3051,1]]},"898":{"position":[[1100,1],[1154,1],[2282,1],[2287,1],[2289,1],[2388,3],[2615,3]]},"900":{"position":[[133,60],[693,1],[864,1],[1302,1]]},"902":{"position":[[1352,1]]},"904":{"position":[[224,1],[275,1],[1059,1],[1113,1]]},"906":{"position":[[549,1],[556,2]]},"908":{"position":[[1488,1],[1968,1],[2081,1],[2086,1],[2088,1],[2187,3],[2772,3],[2823,3],[2852,1],[2895,3]]},"918":{"position":[[704,2]]},"927":{"position":[[79,2]]},"931":{"position":[[155,1]]},"965":{"position":[[4,2]]},"979":{"position":[[311,2],[356,3],[436,2],[439,3]]},"981":{"position":[[193,2]]},"985":{"position":[[491,2],[612,2]]},"995":{"position":[[461,1]]},"997":{"position":[[35,1],[247,1],[265,2]]},"1013":{"position":[[311,2],[356,3],[436,2],[439,3]]},"1015":{"position":[[193,2]]},"1019":{"position":[[575,1],[767,1]]},"1025":{"position":[[24,1]]},"1027":{"position":[[23,1],[1180,1],[4297,3],[6070,3],[6460,3],[7415,1],[7564,1],[8014,1],[8142,1],[8487,1],[8561,1]]},"1029":{"position":[[55,1]]},"1031":{"position":[[67,1]]},"1033":{"position":[[418,1],[4088,1]]},"1035":{"position":[[39,2],[56,1],[82,1],[369,1],[1999,1],[2109,1],[2498,1],[2945,1],[2959,1],[2995,1],[2997,1],[3010,2]]}}}],["0",{"_index":1671,"t":{"211":{"position":[[671,1]]},"249":{"position":[[1038,1],[1094,1],[1149,1]]},"257":{"position":[[2424,1],[3464,2]]},"262":{"position":[[811,1]]},"344":{"position":[[1136,1],[5054,2]]},"376":{"position":[[6506,5],[6671,3]]},"396":{"position":[[362,3],[707,2],[1605,3],[1643,3]]},"450":{"position":[[932,1],[946,1],[995,1],[1015,1],[1029,1],[1070,1],[1263,1]]},"464":{"position":[[1131,1],[1249,1],[1746,1],[2202,1]]},"468":{"position":[[354,1],[672,1],[1082,1],[1953,1]]},"530":{"position":[[264,1],[302,1],[515,1]]},"534":{"position":[[1461,3],[2196,3]]},"536":{"position":[[743,1],[1115,1],[1589,1]]},"598":{"position":[[387,3],[450,1]]},"636":{"position":[[1332,2],[1941,4]]},"654":{"position":[[2471,1],[2480,3]]},"658":{"position":[[1562,3],[1573,1],[1582,3]]},"668":{"position":[[2353,3],[2361,1]]},"692":{"position":[[1958,1],[1985,1],[2004,1],[2021,1]]},"714":{"position":[[807,3],[2958,4]]},"716":{"position":[[3355,3]]},"720":{"position":[[762,2],[1119,2],[6740,3],[6748,1]]},"734":{"position":[[2860,1]]},"746":{"position":[[1386,1]]},"766":{"position":[[637,1],[1678,1]]},"778":{"position":[[429,3]]},"798":{"position":[[1493,1]]},"828":{"position":[[1685,1],[1714,3]]},"834":{"position":[[2421,2],[2718,1]]},"842":{"position":[[597,1],[1461,1],[1490,3]]},"858":{"position":[[2563,1]]},"860":{"position":[[1336,1]]},"1023":{"position":[[899,1]]},"1027":{"position":[[5750,2]]}}}],["0,0",{"_index":4582,"t":{"760":{"position":[[394,3]]},"764":{"position":[[183,3]]}}}],["0..10..2",{"_index":5384,"t":{"1023":{"position":[[829,11]]}}}],["0..25..5}do",{"_index":3701,"t":{"592":{"position":[[839,12]]}}}],["0.0.0.0",{"_index":1763,"t":{"225":{"position":[[1323,7]]},"229":{"position":[[247,7]]},"231":{"position":[[356,7],[671,7]]}}}],["0.25.1",{"_index":5175,"t":{"904":{"position":[[354,6]]}}}],["0.25.1effect",{"_index":5180,"t":{"904":{"position":[[1166,15]]}}}],["00",{"_index":5362,"t":{"1001":{"position":[[36,2]]}}}],["000",{"_index":851,"t":{"47":{"position":[[6819,3]]}}}],["00001",{"_index":5065,"t":{"878":{"position":[[1704,5]]}}}],["001",{"_index":853,"t":{"47":{"position":[[6827,3]]}}}],["002",{"_index":855,"t":{"47":{"position":[[6835,3]]}}}],["003",{"_index":857,"t":{"47":{"position":[[6843,3]]}}}],["004",{"_index":859,"t":{"47":{"position":[[6851,3]]}}}],["005",{"_index":861,"t":{"47":{"position":[[6859,3]]}}}],["006",{"_index":863,"t":{"47":{"position":[[6867,3]]}}}],["007",{"_index":865,"t":{"47":{"position":[[6875,3]]}}}],["00:00",{"_index":5206,"t":{"908":{"position":[[1608,5],[3066,6]]}}}],["01e7a10",{"_index":3987,"t":{"660":{"position":[[1395,7]]},"684":{"position":[[1553,8]]},"694":{"position":[[2126,7]]},"696":{"position":[[5777,7]]},"702":{"position":[[863,7]]}}}],["03/jun/2019",{"_index":1798,"t":{"231":{"position":[[706,12]]}}}],["033",{"_index":4239,"t":{"714":{"position":[[502,6]]},"716":{"position":[[550,4],[1347,4]]}}}],["033[0;30m\\033[42mblack",{"_index":4307,"t":{"716":{"position":[[4053,24]]}}}],["033[0;40m",{"_index":4290,"t":{"716":{"position":[[2692,10]]}}}],["033[0;41m",{"_index":4291,"t":{"716":{"position":[[2730,10]]}}}],["033[0;42m",{"_index":4292,"t":{"716":{"position":[[2766,10]]}}}],["033[0;43m",{"_index":4293,"t":{"716":{"position":[[2804,10]]}}}],["033[0;44m",{"_index":4294,"t":{"716":{"position":[[2842,10]]}}}],["033[0;45m",{"_index":4295,"t":{"716":{"position":[[2879,10]]}}}],["033[0;46m",{"_index":4296,"t":{"716":{"position":[[2918,10]]}}}],["033[0;47m",{"_index":4297,"t":{"716":{"position":[[2955,10]]}}}],["033[0m",{"_index":4298,"t":{"716":{"position":[[3028,7],[5660,12]]},"720":{"position":[[2489,7]]},"806":{"position":[[2214,9],[2261,9]]}}}],["033[1;30m",{"_index":4280,"t":{"716":{"position":[[2186,10]]}}}],["033[1;31m",{"_index":4282,"t":{"716":{"position":[[2252,10]]}}}],["033[1;31mlight",{"_index":4305,"t":{"716":{"position":[[4016,16]]}}}],["033[1;32m",{"_index":4283,"t":{"716":{"position":[[2309,10]]}}}],["033[1;33m",{"_index":4284,"t":{"716":{"position":[[2370,10]]}}}],["033[1;34m",{"_index":4285,"t":{"716":{"position":[[2433,10]]}}}],["033[1;35m",{"_index":4286,"t":{"716":{"position":[[2492,10]]}}}],["033[1;36m",{"_index":4288,"t":{"716":{"position":[[2555,10]]}}}],["033[1;37m",{"_index":4289,"t":{"716":{"position":[[2614,10]]}}}],["033[1m",{"_index":4358,"t":{"720":{"position":[[2332,7]]}}}],["033[24m",{"_index":4367,"t":{"720":{"position":[[2453,8]]}}}],["033[2m",{"_index":4361,"t":{"720":{"position":[[2364,7]]}}}],["033[30m",{"_index":4267,"t":{"716":{"position":[[1854,8]]},"720":{"position":[[767,8]]}}}],["033[31m",{"_index":4268,"t":{"716":{"position":[[1890,8]]},"720":{"position":[[807,8]]}}}],["033[31mred\\033[0m\\n\"printf",{"_index":4304,"t":{"716":{"position":[[3987,28]]}}}],["033[32m",{"_index":4269,"t":{"716":{"position":[[1924,8]]},"720":{"position":[[849,8]]}}}],["033[32m\\]\\w",{"_index":4316,"t":{"716":{"position":[[5630,14]]}}}],["033[32m\\w",{"_index":4312,"t":{"716":{"position":[[4884,10]]}}}],["033[33m",{"_index":4271,"t":{"716":{"position":[[1960,8]]},"720":{"position":[[892,8]]}}}],["033[34m",{"_index":4273,"t":{"716":{"position":[[1997,8]]},"720":{"position":[[933,8]]}}}],["033[35m",{"_index":4274,"t":{"716":{"position":[[2032,8]]},"720":{"position":[[977,8]]}}}],["033[36m",{"_index":4276,"t":{"716":{"position":[[2070,8]]},"720":{"position":[[1018,8]]}}}],["033[37m",{"_index":4278,"t":{"716":{"position":[[2105,8],[4895,10],[5645,14]]},"720":{"position":[[1060,8]]},"806":{"position":[[2236,10]]}}}],["033[40m",{"_index":4338,"t":{"720":{"position":[[1124,8]]}}}],["033[41m",{"_index":4340,"t":{"720":{"position":[[1164,8]]}}}],["033[42m",{"_index":4342,"t":{"720":{"position":[[1206,8]]}}}],["033[43m",{"_index":4344,"t":{"720":{"position":[[1249,8]]}}}],["033[44m",{"_index":4346,"t":{"720":{"position":[[1290,8]]}}}],["033[45m",{"_index":4348,"t":{"720":{"position":[[1334,8]]}}}],["033[46m",{"_index":4350,"t":{"720":{"position":[[1375,8]]}}}],["033[47m",{"_index":4352,"t":{"720":{"position":[[1417,8]]}}}],["033[4m",{"_index":4364,"t":{"720":{"position":[[2409,7]]}}}],["033[92m",{"_index":4814,"t":{"806":{"position":[[2194,10],[2746,8]]}}}],["04:10",{"_index":4395,"t":{"720":{"position":[[5522,5]]}}}],["04:43",{"_index":4256,"t":{"714":{"position":[[2929,5]]}}}],["05",{"_index":3442,"t":{"534":{"position":[[861,2],[883,2]]},"538":{"position":[[1013,2],[1455,2],[1487,2],[2691,2]]},"852":{"position":[[1068,2],[1095,2],[1121,2],[1145,2],[1188,2]]}}}],["06",{"_index":4253,"t":{"714":{"position":[[2605,2],[2926,2]]},"720":{"position":[[5516,2],[5519,2]]},"722":{"position":[[1542,2],[1586,2]]}}}],["0644",{"_index":5157,"t":{"900":{"position":[[305,4]]}}}],["07995",{"_index":5067,"t":{"878":{"position":[[1734,5]]}}}],["08",{"_index":3264,"t":{"500":{"position":[[857,3],[1086,3]]},"1029":{"position":[[420,3]]}}}],["08/jan/2021",{"_index":1766,"t":{"225":{"position":[[1358,12],[1417,12],[1486,12],[1552,12],[1621,12]]}}}],["0800",{"_index":4163,"t":{"694":{"position":[[505,5],[653,5],[823,5]]}}}],["08:49:07",{"_index":4417,"t":{"722":{"position":[[1554,8],[1598,8]]}}}],["0;31mhello^[[0m32",{"_index":5013,"t":{"862":{"position":[[1984,20]]}}}],["0;32mhello^[[0m33",{"_index":5014,"t":{"862":{"position":[[2007,20]]}}}],["0;33mhello^[[0m34",{"_index":5015,"t":{"862":{"position":[[2030,20]]}}}],["0;34mhello^[[0m35",{"_index":5016,"t":{"862":{"position":[[2053,20]]}}}],["0;35mhello^[[0m36",{"_index":5017,"t":{"862":{"position":[[2076,20]]}}}],["0;36mhello^[[0m37",{"_index":5018,"t":{"862":{"position":[[2099,20]]}}}],["0;37mhello^[[0m",{"_index":5019,"t":{"862":{"position":[[2122,18]]}}}],["0c",{"_index":4921,"t":{"842":{"position":[[613,2]]}}}],["0elk",{"_index":2956,"t":{"450":{"position":[[1580,4],[1627,4],[2055,4],[2102,4],[2564,4],[2611,4]]}}}],["0go",{"_index":4620,"t":{"766":{"position":[[1754,3],[1907,3]]}}}],["0loop",{"_index":3702,"t":{"592":{"position":[[912,5]]}}}],["0m",{"_index":4303,"t":{"716":{"position":[[3635,2]]}}}],["0mi",{"_index":3373,"t":{"520":{"position":[[601,3]]}}}],["0remot",{"_index":3930,"t":{"654":{"position":[[2496,8]]}}}],["0unpack",{"_index":3950,"t":{"658":{"position":[[1598,10]]}}}],["1",{"_index":158,"t":{"17":{"position":[[5,1]]},"23":{"position":[[1111,2]]},"47":{"position":[[342,1],[1192,1],[1950,2],[2081,1]]},"69":{"position":[[749,2]]},"81":{"position":[[1312,3]]},"99":{"position":[[784,1]]},"135":{"position":[[789,1]]},"217":{"position":[[897,1],[948,1]]},"229":{"position":[[220,4],[570,4],[697,1]]},"231":{"position":[[1977,2],[1980,4]]},"233":{"position":[[331,2],[357,1]]},"235":{"position":[[601,3]]},"243":{"position":[[288,2],[318,1],[344,2],[378,2],[386,2],[410,2],[418,2],[444,1]]},"249":{"position":[[1025,1],[1044,1],[1081,1],[1100,1],[1136,1],[1155,1]]},"257":{"position":[[2270,1],[2485,1],[3467,1],[4214,2],[6358,1],[6440,1]]},"262":{"position":[[971,1]]},"284":{"position":[[1245,1]]},"288":{"position":[[1895,2]]},"302":{"position":[[393,1],[463,1]]},"368":{"position":[[1741,1],[1765,1]]},"374":{"position":[[686,2]]},"376":{"position":[[4345,5],[4355,2],[4899,5]]},"442":{"position":[[458,1],[500,1],[569,1],[607,2],[770,2],[983,1]]},"448":{"position":[[279,1],[415,1]]},"450":{"position":[[1116,1],[2024,1],[2531,1]]},"452":{"position":[[270,1],[513,1]]},"454":{"position":[[1587,1],[1980,1],[2227,1]]},"466":{"position":[[587,1],[1392,1]]},"468":{"position":[[1501,1]]},"470":{"position":[[653,1],[873,1]]},"472":{"position":[[142,1],[583,1]]},"498":{"position":[[324,2],[419,2]]},"508":{"position":[[3790,1],[3963,1]]},"510":{"position":[[1971,1],[2022,2],[2344,2],[2359,1]]},"518":{"position":[[76,2]]},"522":{"position":[[933,3]]},"524":{"position":[[489,2],[869,2],[882,5]]},"526":{"position":[[358,1],[1634,2]]},"528":{"position":[[473,1]]},"530":{"position":[[233,1]]},"534":{"position":[[1431,5],[1447,3],[2153,4],[2182,3]]},"536":{"position":[[712,4],[757,1]]},"542":{"position":[[158,3]]},"544":{"position":[[300,2],[783,2]]},"550":{"position":[[195,2]]},"580":{"position":[[161,2]]},"590":{"position":[[218,2],[701,2]]},"592":{"position":[[311,1]]},"594":{"position":[[171,2]]},"598":{"position":[[279,3]]},"600":{"position":[[216,2]]},"604":{"position":[[364,1],[366,1]]},"626":{"position":[[1404,4],[1421,7]]},"660":{"position":[[325,1]]},"668":{"position":[[381,1],[917,1]]},"670":{"position":[[902,2]]},"684":{"position":[[2682,1]]},"688":{"position":[[1438,1],[1454,1],[3083,1],[3099,1]]},"690":{"position":[[405,1]]},"700":{"position":[[1965,1]]},"710":{"position":[[107,3]]},"714":{"position":[[2978,4]]},"716":{"position":[[3138,4],[3175,3]]},"720":{"position":[[802,2],[1159,2],[2824,2],[3610,3]]},"734":{"position":[[1562,1]]},"738":{"position":[[4709,4]]},"746":{"position":[[1413,1]]},"760":{"position":[[398,1]]},"764":{"position":[[187,1],[2157,1],[2636,1],[2652,1]]},"834":{"position":[[2407,1],[2964,2]]},"842":{"position":[[618,1],[634,4]]},"858":{"position":[[1985,1],[2143,3]]},"860":{"position":[[1570,1],[2334,3]]},"862":{"position":[[2811,1],[2919,1],[3130,3],[3432,1]]},"864":{"position":[[591,1]]},"868":{"position":[[418,2],[494,3]]},"896":{"position":[[865,2]]},"900":{"position":[[712,1],[1343,1]]},"1027":{"position":[[5753,1]]}}}],["1\",\"97\",\"black",{"_index":3726,"t":{"596":{"position":[[338,15]]}}}],["1\"/p",{"_index":2487,"t":{"376":{"position":[[4139,7]]}}}],["1))done",{"_index":3509,"t":{"544":{"position":[[1727,7]]}}}],["1))doneifs=$old_if",{"_index":3763,"t":{"612":{"position":[[1391,19]]}}}],["1).tar.gz/home/dwmkerr/downloads/aspnetcor",{"_index":5475,"t":{"1035":{"position":[[452,44],[1465,44]]}}}],["1).tar.gz/home/dwmkerr/downloads/effect",{"_index":5481,"t":{"1035":{"position":[[688,43],[1701,43]]}}}],["1,0",{"_index":4609,"t":{"764":{"position":[[2153,3]]}}}],["1,1",{"_index":4676,"t":{"778":{"position":[[1900,3]]}}}],["1,11",{"_index":4585,"t":{"760":{"position":[[1529,4]]}}}],["1,16",{"_index":4602,"t":{"764":{"position":[[1400,4]]}}}],["1,2,3}.md",{"_index":3105,"t":{"464":{"position":[[148,12]]}}}],["1..10",{"_index":3381,"t":{"522":{"position":[[882,8]]},"718":{"position":[[1566,7]]}}}],["1..10}32",{"_index":4321,"t":{"718":{"position":[[1383,9]]}}}],["1..10}42",{"_index":4324,"t":{"718":{"position":[[2182,9]]}}}],["1..10}do",{"_index":3700,"t":{"592":{"position":[[569,9]]}}}],["1.0",{"_index":3362,"t":{"518":{"position":[[382,5],[673,4]]},"540":{"position":[[129,5]]}}}],["1.0kb/",{"_index":5226,"t":{"908":{"position":[[3058,7]]}}}],["1.2",{"_index":3376,"t":{"520":{"position":[[682,3]]}}}],["1.2succeed",{"_index":3374,"t":{"520":{"position":[[627,13]]}}}],["1.83",{"_index":3924,"t":{"654":{"position":[[2427,4]]}}}],["1.compute.amazonaws.com",{"_index":4903,"t":{"838":{"position":[[2114,23]]},"898":{"position":[[842,23],[1257,23],[2210,24]]},"902":{"position":[[473,23]]},"908":{"position":[[2954,23]]}}}],["1.md",{"_index":3111,"t":{"464":{"position":[[384,7],[650,4],[1270,5]]}}}],["1.md./chapter",{"_index":3107,"t":{"464":{"position":[[185,13]]}}}],["1.set",{"_index":4880,"t":{"834":{"position":[[2920,5]]}}}],["1/1",{"_index":2925,"t":{"450":{"position":[[934,3],[983,3],[1017,3],[1058,3],[1104,3],[1150,3],[1196,3],[1251,3],[1301,3],[1354,3],[1407,3]]}}}],["10",{"_index":674,"t":{"45":{"position":[[842,2]]},"288":{"position":[[487,2]]},"290":{"position":[[1220,2],[1787,2],[2108,2],[2412,2]]},"342":{"position":[[1653,4],[1700,5]]},"368":{"position":[[1743,2],[1767,3]]},"376":{"position":[[5722,2]]},"400":{"position":[[1144,4]]},"406":{"position":[[955,2]]},"424":{"position":[[233,2]]},"426":{"position":[[380,2]]},"430":{"position":[[404,2]]},"438":{"position":[[427,2]]},"458":{"position":[[1098,2]]},"472":{"position":[[426,2]]},"512":{"position":[[419,2]]},"522":{"position":[[1037,3],[1971,2]]},"524":{"position":[[417,2],[433,2]]},"526":{"position":[[1180,2],[1208,2]]},"544":{"position":[[712,3]]},"626":{"position":[[648,2]]},"714":{"position":[[2975,2],[3004,2],[3033,2]]},"734":{"position":[[2799,3]]},"866":{"position":[[21,2]]},"1023":{"position":[[909,3]]},"1027":{"position":[[7235,2],[7355,2]]}}}],["10,20",{"_index":2314,"t":{"342":{"position":[[1748,7]]}}}],["10.5kb/",{"_index":5205,"t":{"908":{"position":[[1599,8]]}}}],["100",{"_index":1907,"t":{"253":{"position":[[2787,3],[2906,3]]},"446":{"position":[[1028,3]]},"604":{"position":[[806,4]]},"654":{"position":[[2285,4],[2365,4],[2401,4],[2523,4]]},"658":{"position":[[1476,4],[1522,4],[1618,4]]},"908":{"position":[[1589,4],[3049,4]]}}}],["1000",{"_index":2781,"t":{"424":{"position":[[172,4],[349,4]]},"426":{"position":[[309,4]]},"430":{"position":[[343,4]]},"438":{"position":[[366,4]]},"512":{"position":[[361,4]]}}}],["10000",{"_index":4524,"t":{"738":{"position":[[7433,5]]}}}],["100000",{"_index":4525,"t":{"738":{"position":[[7510,6]]}}}],["10002",{"_index":2640,"t":{"394":{"position":[[2280,5]]},"396":{"position":[[409,5]]}}}],["100644",{"_index":3962,"t":{"660":{"position":[[370,6]]},"684":{"position":[[1676,6],[2728,6]]},"688":{"position":[[1481,6]]},"690":{"position":[[449,6]]},"692":{"position":[[2048,6],[2091,6]]},"764":{"position":[[2679,6]]}}}],["100755",{"_index":4114,"t":{"684":{"position":[[1646,6]]}}}],["100gbfile.csv",{"_index":1908,"t":{"253":{"position":[[2791,13]]}}}],["100linefile.csv",{"_index":1909,"t":{"253":{"position":[[2807,15]]}}}],["101",{"_index":5290,"t":{"969":{"position":[[173,3]]}}}],["1045",{"_index":3873,"t":{"636":{"position":[[1812,4]]}}}],["10loop",{"_index":3704,"t":{"592":{"position":[[924,6]]}}}],["10the",{"_index":3390,"t":{"522":{"position":[[1630,5],[1944,5]]}}}],["11",{"_index":2646,"t":{"398":{"position":[[445,2],[603,2],[735,2],[867,2],[999,2],[1127,2]]},"402":{"position":[[549,2],[909,2]]},"450":{"position":[[3142,2],[3270,2],[3426,2],[3485,2],[3572,2],[3635,2],[3726,2],[3785,2],[3872,2],[3957,2]]},"462":{"position":[[1168,2]]},"464":{"position":[[1321,2]]},"526":{"position":[[1681,5]]},"588":{"position":[[238,2]]},"1035":{"position":[[3425,2]]}}}],["1103",{"_index":5164,"t":{"900":{"position":[[730,4],[1361,4]]}}}],["11898",{"_index":3718,"t":{"594":{"position":[[1290,5],[1462,5]]}}}],["11:36:54",{"_index":3263,"t":{"500":{"position":[[845,8],[1074,8]]}}}],["11th",{"_index":3413,"t":{"526":{"position":[[1691,4]]}}}],["12",{"_index":2994,"t":{"450":{"position":[[4254,2]]},"714":{"position":[[902,2],[950,2]]},"858":{"position":[[2792,2]]}}}],["12.83",{"_index":3922,"t":{"654":{"position":[[2415,5]]}}}],["1250034",{"_index":4690,"t":{"788":{"position":[[369,7]]}}}],["127.0.0.1",{"_index":1765,"t":{"225":{"position":[[1341,12],[1403,9],[1472,9],[1538,9]]},"231":{"position":[[689,12]]},"402":{"position":[[770,12],[1130,12]]}}}],["12:02",{"_index":5166,"t":{"900":{"position":[[742,5],[1373,5]]}}}],["12:43",{"_index":4254,"t":{"714":{"position":[[2608,5]]}}}],["13",{"_index":2414,"t":{"364":{"position":[[2154,2]]},"442":{"position":[[1105,2]]},"568":{"position":[[231,2]]},"660":{"position":[[308,2],[341,2]]},"838":{"position":[[2087,2]]},"898":{"position":[[815,2],[1230,2],[2183,2]]},"902":{"position":[[446,2]]},"908":{"position":[[2927,2]]}}}],["13.213.71.135",{"_index":5141,"t":{"898":{"position":[[1281,16]]}}}],["130",{"_index":4970,"t":{"858":{"position":[[2191,3],[2256,3]]}}}],["135",{"_index":3765,"t":{"612":{"position":[[1744,3]]}}}],["135.ap",{"_index":4901,"t":{"838":{"position":[[2097,6]]},"898":{"position":[[825,6],[1240,6],[2193,6]]},"902":{"position":[[456,6]]},"908":{"position":[[2937,6]]}}}],["138b404",{"_index":3975,"t":{"660":{"position":[[1053,7]]},"694":{"position":[[1791,7]]},"696":{"position":[[5450,7]]},"702":{"position":[[536,7]]}}}],["138b40418d5658bc64421e7bcf2680c8339f8350",{"_index":4156,"t":{"694":{"position":[[368,40]]}}}],["139)2",{"_index":3514,"t":{"544":{"position":[[2210,7]]}}}],["13:38:45",{"_index":1799,"t":{"231":{"position":[[719,9]]}}}],["13delk",{"_index":2929,"t":{"450":{"position":[[997,6]]}}}],["13dfilebeat",{"_index":2938,"t":{"450":{"position":[[1118,11],[1164,11]]}}}],["13dkube",{"_index":2942,"t":{"450":{"position":[[1212,7]]}}}],["13l",{"_index":4607,"t":{"764":{"position":[[2143,4]]}}}],["14",{"_index":1657,"t":{"207":{"position":[[481,2]]},"209":{"position":[[596,4]]},"211":{"position":[[583,2],[820,3]]},"253":{"position":[[1317,2],[1402,2]]},"290":{"position":[[950,3]]},"376":{"position":[[223,2],[1984,2],[2858,2],[3499,2],[4592,3],[5052,5],[5359,4]]}}}],["14\"2\",\"94\",\"aveng",{"_index":2477,"t":{"376":{"position":[[1495,21]]}}}],["144",{"_index":3485,"t":{"544":{"position":[[313,3]]},"626":{"position":[[3632,3]]}}}],["144:~/effect",{"_index":3839,"t":{"626":{"position":[[3487,15]]}}}],["14718",{"_index":3714,"t":{"594":{"position":[[1229,5],[1456,5]]}}}],["14](../../part",{"_index":2505,"t":{"376":{"position":[[5875,14]]}}}],["14let'",{"_index":2472,"t":{"376":{"position":[[614,7]]}}}],["15",{"_index":2503,"t":{"376":{"position":[[5815,4],[5832,2]]},"526":{"position":[[399,2]]},"600":{"position":[[566,2],[736,2],[787,2],[1593,2],[1715,2],[1760,2]]},"694":{"position":[[488,2],[636,2],[806,2]]},"908":{"position":[[2046,2]]},"1027":{"position":[[9395,2]]}}}],["15:41:15)[gcc",{"_index":2810,"t":{"432":{"position":[[1066,13]]}}}],["15loop",{"_index":3705,"t":{"592":{"position":[[931,6]]}}}],["16",{"_index":680,"t":{"47":{"position":[[330,3]]},"390":{"position":[[1780,2]]},"654":{"position":[[2334,2]]},"1027":{"position":[[9431,2]]}}}],["1606818280:0;l",{"_index":2884,"t":{"446":{"position":[[1775,16]]}}}],["1606818300:0;ln",{"_index":2885,"t":{"446":{"position":[[1792,15]]}}}],["1606818308:0;cat",{"_index":2886,"t":{"446":{"position":[[1837,16]]}}}],["1606818342:0;head",{"_index":2887,"t":{"446":{"position":[[1889,17]]}}}],["1606819062:0;head",{"_index":2888,"t":{"446":{"position":[[1947,17]]}}}],["1606819647:0;gcd",{"_index":2889,"t":{"446":{"position":[[2000,17]]}}}],["1606819649:0;git",{"_index":2890,"t":{"446":{"position":[[2018,16]]}}}],["1606819650:0;gcd",{"_index":2892,"t":{"446":{"position":[[2042,17],[2641,17]]}}}],["1606819662:0;git",{"_index":2893,"t":{"446":{"position":[[2060,16],[2659,16]]}}}],["1606819803:0;tail",{"_index":2894,"t":{"446":{"position":[[2088,17],[2687,17]]}}}],["1621135004:0;unam",{"_index":3596,"t":{"574":{"position":[[1967,18]]}}}],["1675",{"_index":3863,"t":{"634":{"position":[[753,4],[905,4]]}}}],["16:33:40",{"_index":1767,"t":{"225":{"position":[[1371,9],[1430,9],[1499,9],[1565,9],[1634,9]]}}}],["16:42",{"_index":1708,"t":{"217":{"position":[[1071,5]]}}}],["16:49:07",{"_index":5439,"t":{"1029":{"position":[[411,8]]}}}],["17",{"_index":3774,"t":{"612":{"position":[[1824,2]]}}}],["172",{"_index":3838,"t":{"626":{"position":[[3477,3],[3622,3]]},"898":{"position":[[2374,3],[2601,3]]},"908":{"position":[[2173,3],[2758,3],[2809,3],[2881,3]]}}}],["17:17",{"_index":1705,"t":{"217":{"position":[[1026,5]]}}}],["17b",{"_index":4601,"t":{"764":{"position":[[1388,3]]}}}],["18",{"_index":1051,"t":{"69":{"position":[[3187,4]]},"175":{"position":[[2900,2]]},"556":{"position":[[642,2]]},"624":{"position":[[1049,2]]},"874":{"position":[[598,2]]}}}],["18:06",{"_index":1697,"t":{"217":{"position":[[917,5],[968,5]]}}}],["19",{"_index":3404,"t":{"526":{"position":[[770,2],[2128,2]]},"544":{"position":[[868,2]]},"568":{"position":[[1395,2]]},"582":{"position":[[11,2]]},"600":{"position":[[1114,2]]},"608":{"position":[[524,2],[1023,2]]},"616":{"position":[[597,2],[1065,2]]},"714":{"position":[[3711,2]]},"854":{"position":[[142,2]]},"900":{"position":[[739,2],[1370,2]]},"973":{"position":[[23,2]]},"1027":{"position":[[358,2],[5525,3],[7358,3],[10468,2]]},"1029":{"position":[[408,2]]},"1031":{"position":[[288,2]]}}}],["19,000",{"_index":4006,"t":{"664":{"position":[[2407,6]]}}}],["1912:50:5212:50:5212:50:52",{"_index":2995,"t":{"450":{"position":[[4257,26]]}}}],["1933",{"_index":3076,"t":{"456":{"position":[[659,7]]}}}],["1935)\"\"a",{"_index":3030,"t":{"454":{"position":[[226,9]]}}}],["19372",{"_index":1787,"t":{"229":{"position":[[575,5],[759,6]]}}}],["1938",{"_index":3079,"t":{"456":{"position":[[704,7]]}}}],["1939)\"\"the",{"_index":2919,"t":{"450":{"position":[[393,11]]}}}],["1939)\",\"120\"\"9\",\"96\",\"the",{"_index":2875,"t":{"446":{"position":[[784,26],[3812,26]]}}}],["1940)\"\"99\",\"chinatown",{"_index":3005,"t":{"450":{"position":[[5121,22]]}}}],["1940)\",\"55\"\"chinatown",{"_index":3000,"t":{"450":{"position":[[4899,22]]}}}],["1941)\"\"mission",{"_index":2917,"t":{"450":{"position":[[330,16]]}}}],["1941)\",\"94\"\"7\",\"97\",\"mission",{"_index":2870,"t":{"446":{"position":[[692,30],[3720,30]]}}}],["1944",{"_index":3084,"t":{"456":{"position":[[758,7]]}}}],["1950)\"\"argo",{"_index":3037,"t":{"454":{"position":[[307,12]]}}}],["196",{"_index":5153,"t":{"898":{"position":[[2384,3],[2611,3]]},"908":{"position":[[2183,3],[2768,3],[2819,3],[2891,3]]}}}],["1964)\"\"a",{"_index":3028,"t":{"454":{"position":[[197,9]]}}}],["1970",{"_index":1883,"t":{"249":{"position":[[1046,4],[1102,4],[1157,4]]}}}],["1974)\"\"94\",\"the",{"_index":3006,"t":{"450":{"position":[[5144,16]]}}}],["1974)\",\"75\"\"the",{"_index":3001,"t":{"450":{"position":[[4922,16]]}}}],["1975",{"_index":3073,"t":{"456":{"position":[[640,7]]}}}],["1979)\"\"all",{"_index":3035,"t":{"454":{"position":[[285,11]]}}}],["1980",{"_index":3552,"t":{"564":{"position":[[158,4],[306,4],[509,4]]},"566":{"position":[[222,4]]}}}],["1980s\"fi",{"_index":3554,"t":{"564":{"position":[[215,8],[562,8]]},"566":{"position":[[276,8]]}}}],["1988",{"_index":3757,"t":{"610":{"position":[[709,4]]}}}],["1989",{"_index":2224,"t":{"314":{"position":[[164,5]]}}}],["1990",{"_index":3553,"t":{"564":{"position":[[180,4],[325,5],[527,4]]},"566":{"position":[[240,4]]}}}],["1993",{"_index":2225,"t":{"314":{"position":[[170,5]]}}}],["1994",{"_index":2226,"t":{"314":{"position":[[176,4]]}}}],["1999",{"_index":3053,"t":{"454":{"position":[[811,7]]}}}],["19:03:00",{"_index":5208,"t":{"908":{"position":[[2021,8]]}}}],["19echo",{"_index":5421,"t":{"1027":{"position":[[7238,6]]}}}],["1[1",{"_index":1805,"t":{"231":{"position":[[1413,5]]},"235":{"position":[[647,5]]}}}],["1\\2\"\\3",{"_index":2499,"t":{"376":{"position":[[5570,21]]}}}],["1\\2\"\\3\"/p",{"_index":2497,"t":{"376":{"position":[[5259,22]]}}}],["1d",{"_index":2465,"t":{"374":{"position":[[821,4],[910,2]]}}}],["1g",{"_index":1718,"t":{"219":{"position":[[566,3]]}}}],["1l",{"_index":4600,"t":{"764":{"position":[[1384,3]]},"778":{"position":[[1892,3]]}}}],["1loop",{"_index":3691,"t":{"590":{"position":[[777,5]]}}}],["1nameelast",{"_index":2955,"t":{"450":{"position":[[1558,12]]}}}],["1set",{"_index":4881,"t":{"834":{"position":[[2940,4]]}}}],["1titl",{"_index":3375,"t":{"520":{"position":[[641,6]]}}}],["1you",{"_index":5049,"t":{"868":{"position":[[456,4]]}}}],["2",{"_index":403,"t":{"23":{"position":[[2097,2]]},"47":{"position":[[392,1],[2468,2],[2661,1]]},"69":{"position":[[2522,2]]},"99":{"position":[[1001,1]]},"111":{"position":[[84,1]]},"147":{"position":[[5,1]]},"217":{"position":[[1001,1],[1046,1]]},"219":{"position":[[709,1]]},"221":{"position":[[546,1]]},"249":{"position":[[1271,1]]},"257":{"position":[[2120,1],[2545,1],[3473,1],[4469,3],[4508,2],[4685,1],[5467,3],[6331,1]]},"262":{"position":[[1130,1]]},"290":{"position":[[423,1]]},"356":{"position":[[611,1]]},"398":{"position":[[521,1]]},"442":{"position":[[491,1],[516,1],[1037,2],[1143,2]]},"446":{"position":[[3253,3],[3990,2]]},"450":{"position":[[1162,1],[1930,2]]},"454":{"position":[[809,1]]},"458":{"position":[[949,1]]},"498":{"position":[[351,2],[442,2]]},"510":{"position":[[1806,3],[1819,1],[1897,1],[2037,1],[2089,2],[2104,1],[2170,1],[2222,2],[2237,1],[2330,4]]},"518":{"position":[[97,2]]},"524":{"position":[[496,2],[876,2],[890,6]]},"526":{"position":[[360,1],[1657,2]]},"530":{"position":[[190,1],[294,3]]},"542":{"position":[[162,2]]},"550":{"position":[[219,2]]},"580":{"position":[[185,2]]},"590":{"position":[[230,2]]},"594":{"position":[[195,2]]},"598":{"position":[[303,3]]},"600":{"position":[[240,2]]},"604":{"position":[[368,1]]},"684":{"position":[[1600,1]]},"690":{"position":[[400,1],[421,1]]},"692":{"position":[[1987,1]]},"714":{"position":[[3007,4]]},"720":{"position":[[844,2],[1201,2]]},"722":{"position":[[2122,2]]},"802":{"position":[[197,1],[277,2],[306,1],[352,1]]},"844":{"position":[[61,2]]},"858":{"position":[[3080,1]]},"862":{"position":[[4003,2],[4031,3]]},"896":{"position":[[945,1],[1131,2]]},"898":{"position":[[2304,1]]},"908":{"position":[[2103,1]]},"985":{"position":[[1497,1]]},"1023":{"position":[[901,1]]},"1027":{"position":[[5755,1]]}}}],["2\",\"94\",\"aveng",{"_index":3728,"t":{"596":{"position":[[381,19]]}}}],["2\"/p",{"_index":2491,"t":{"376":{"position":[[4500,7],[4927,7]]}}}],["2\"1\",\"97\",\"black",{"_index":2896,"t":{"446":{"position":[[3522,17]]}}}],["2).tar.gz/home/dwmkerr/downloads/effect",{"_index":5482,"t":{"1035":{"position":[[746,43],[1759,43]]}}}],["2,3",{"_index":3087,"t":{"458":{"position":[[1247,3]]}}}],["2,3\"100\",\"pinocchio",{"_index":3004,"t":{"450":{"position":[[5101,19]]}}}],["2.0",{"_index":2080,"t":{"282":{"position":[[837,3],[854,3]]}}}],["2.1",{"_index":4890,"t":{"834":{"position":[[4669,3]]}}}],["2.2",{"_index":4098,"t":{"680":{"position":[[1491,3],[1794,5]]}}}],["2.md",{"_index":3113,"t":{"464":{"position":[[665,4],[1287,5]]}}}],["2.md./chapter",{"_index":3108,"t":{"464":{"position":[[199,13]]}}}],["2/[ec2",{"_index":5151,"t":{"898":{"position":[[2359,6]]},"908":{"position":[[2158,6]]}}}],["20",{"_index":3410,"t":{"526":{"position":[[1183,2],[1211,2]]},"536":{"position":[[421,2]]},"624":{"position":[[86,2],[151,2]]},"626":{"position":[[4443,3]]},"634":{"position":[[2110,2]]},"708":{"position":[[296,5],[1108,5]]},"714":{"position":[[2576,5]]},"716":{"position":[[198,2]]},"718":{"position":[[143,5]]},"720":{"position":[[5409,5],[5489,5],[5578,5]]},"734":{"position":[[164,2]]},"850":{"position":[[2152,2]]}}}],["20.04.2",{"_index":3870,"t":{"636":{"position":[[1783,7]]}}}],["200",{"_index":1769,"t":{"225":{"position":[[1398,3],[1467,3],[1533,3]]},"231":{"position":[[746,3]]}}}],["2006",{"_index":4087,"t":{"678":{"position":[[711,5]]}}}],["2007",{"_index":4853,"t":{"820":{"position":[[233,5]]}}}],["2008",{"_index":3007,"t":{"450":{"position":[[5173,7]]}}}],["2008)\",\"342",{"_index":3003,"t":{"450":{"position":[[4951,13]]}}}],["2009)\"\"toy",{"_index":3051,"t":{"454":{"position":[[751,11]]}}}],["2010)\"\"toy",{"_index":3052,"t":{"454":{"position":[[791,11]]}}}],["2012)\"\"arriv",{"_index":3038,"t":{"454":{"position":[[320,15]]}}}],["2013)\"\"a",{"_index":3025,"t":{"454":{"position":[[170,9]]}}}],["2016",{"_index":3080,"t":{"456":{"position":[[724,7]]}}}],["2016)\"\"aveng",{"_index":3039,"t":{"454":{"position":[[336,17]]}}}],["2016)\"\"wonder",{"_index":3041,"t":{"454":{"position":[[620,14]]}}}],["2017",{"_index":3082,"t":{"456":{"position":[[743,7]]}}}],["2017)\"\"citizen",{"_index":2916,"t":{"450":{"position":[[309,15]]}}}],["2017)\"\"u",{"_index":3049,"t":{"454":{"position":[[729,10]]}}}],["2017)\"\"won't",{"_index":3043,"t":{"454":{"position":[[641,13]]}}}],["2017)\",\"393\"\"6\",\"100\",\"citizen",{"_index":2868,"t":{"446":{"position":[[655,31],[3683,31]]}}}],["2018)\"\"a",{"_index":3031,"t":{"454":{"position":[[248,9]]}}}],["2018)\"\"alien",{"_index":3034,"t":{"454":{"position":[[271,13]]}}}],["2018)\"\"aveng",{"_index":2912,"t":{"450":{"position":[[234,17]]}}}],["2018)\"\"the",{"_index":2918,"t":{"450":{"position":[[368,11]]}}}],["2018)\"\"war",{"_index":3046,"t":{"454":{"position":[[690,11]]}}}],["2018)\"\"widow",{"_index":3045,"t":{"454":{"position":[[675,14]]}}}],["2018)\",\"430\"\"8\",\"98\",\"the",{"_index":2872,"t":{"446":{"position":[[744,26],[3772,26]]}}}],["2018)\",\"515\"\"2\",\"94\",\"aveng",{"_index":2456,"t":{"374":{"position":[[255,32]]},"446":{"position":[[520,32],[1395,32],[3548,32]]}}}],["2018)\",\"515\"read",{"_index":3727,"t":{"596":{"position":[[362,18]]}}}],["20182019",{"_index":2466,"t":{"374":{"position":[[870,11]]}}}],["2019",{"_index":1417,"t":{"117":{"position":[[620,5]]},"123":{"position":[[196,4],[222,4],[482,4],[540,4],[672,5]]},"356":{"position":[[516,4],[613,6]]},"450":{"position":[[414,7]]},"454":{"position":[[362,7]]}}}],["2019)\"\"ladi",{"_index":2915,"t":{"450":{"position":[[291,12]]}}}],["2019)\"\"toy",{"_index":2914,"t":{"450":{"position":[[271,11]]},"454":{"position":[[771,11]]}}}],["2019)\"\"u",{"_index":2913,"t":{"450":{"position":[[260,10]]}}}],["2019)\"\"up",{"_index":3050,"t":{"454":{"position":[[740,10]]}}}],["2019)\",\"441",{"_index":2877,"t":{"446":{"position":[[820,13],[3848,13]]}}}],["2019)\",\"445\"\"5\",\"99\",\"ladi",{"_index":2866,"t":{"446":{"position":[[622,27],[3650,27]]}}}],["2019)\",\"531",{"_index":2458,"t":{"374":{"position":[[296,13]]},"376":{"position":[[1525,13]]},"446":{"position":[[1436,13]]},"596":{"position":[[409,16]]}}}],["2019)\",\"531\"\"3\",\"93\",\"u",{"_index":2864,"t":{"446":{"position":[[561,25],[3589,25]]}}}],["2019)\",\"536\"\"4\",\"97\",\"toy",{"_index":2865,"t":{"446":{"position":[[587,26],[3615,26]]}}}],["2019/outdoor/photo",{"_index":1414,"t":{"117":{"position":[[457,19]]}}}],["2019/outdoors/photo",{"_index":1424,"t":{"119":{"position":[[35,20]]}}}],["2019/outdoors/pictur",{"_index":1409,"t":{"115":{"position":[[531,23]]},"123":{"position":[[40,22]]}}}],["2019/outdoors/picturestre",{"_index":1410,"t":{"115":{"position":[[667,26]]}}}],["2020",{"_index":1228,"t":{"91":{"position":[[1555,4]]},"117":{"position":[[726,4]]},"135":{"position":[[1027,4],[1188,4]]},"398":{"position":[[440,4],[598,4],[730,4],[862,4],[994,4],[1122,4]]},"802":{"position":[[394,5]]}}}],["2021",{"_index":2809,"t":{"432":{"position":[[1060,5]]},"500":{"position":[[840,4],[1069,4]]},"694":{"position":[[500,4],[648,4],[818,4]]},"722":{"position":[[1549,4],[1593,4]]},"1027":{"position":[[7350,4]]},"1029":{"position":[[424,5]]}}}],["2022",{"_index":5209,"t":{"908":{"position":[[2030,4]]}}}],["20480",{"_index":1707,"t":{"217":{"position":[[1058,5]]}}}],["20:/home/dwmkerr/effect",{"_index":4231,"t":{"708":{"position":[[1403,26]]}}}],["20:53:01",{"_index":4170,"t":{"694":{"position":[[809,8]]}}}],["20:~/.../logs/apm",{"_index":4423,"t":{"722":{"position":[[2211,17]]}}}],["20:~/effect",{"_index":4228,"t":{"708":{"position":[[1163,14],[1346,14]]},"710":{"position":[[296,14]]},"712":{"position":[[95,14]]},"716":{"position":[[247,14],[4846,14]]}}}],["20:~/repos/github/dwmkerr/effect",{"_index":4404,"t":{"720":{"position":[[7397,35]]}}}],["20loop",{"_index":3706,"t":{"592":{"position":[[938,6]]}}}],["20welcom",{"_index":3869,"t":{"636":{"position":[[1763,9]]}}}],["21",{"_index":3275,"t":{"502":{"position":[[1140,2]]},"612":{"position":[[1814,2]]},"1035":{"position":[[3260,2]]}}}],["212",{"_index":2790,"t":{"424":{"position":[[1515,4]]}}}],["21268",{"_index":1800,"t":{"231":{"position":[[759,5],[1421,5],[1987,5]]}}}],["213",{"_index":4899,"t":{"838":{"position":[[2090,3]]},"898":{"position":[[818,3],[1233,3],[2186,3]]},"902":{"position":[[449,3]]},"908":{"position":[[2930,3]]}}}],["2169",{"_index":2831,"t":{"434":{"position":[[2163,4]]},"436":{"position":[[230,4]]}}}],["218.singnet.com.sg",{"_index":5212,"t":{"908":{"position":[[2053,18]]}}}],["21:00:22",{"_index":4166,"t":{"694":{"position":[[639,8]]}}}],["21:00:28",{"_index":4162,"t":{"694":{"position":[[491,8]]}}}],["22",{"_index":3822,"t":{"626":{"position":[[1605,2]]},"850":{"position":[[2348,2]]},"908":{"position":[[1215,2]]}}}],["2240",{"_index":2830,"t":{"434":{"position":[[1658,4]]}}}],["228",{"_index":4118,"t":{"684":{"position":[[2698,3]]}}}],["22843",{"_index":1814,"t":{"235":{"position":[[655,5]]}}}],["22:44",{"_index":2177,"t":{"302":{"position":[[418,5],[488,5]]}}}],["23",{"_index":3262,"t":{"500":{"position":[[833,2],[1062,2]]},"510":{"position":[[640,2],[1214,2]]},"720":{"position":[[2580,2]]},"748":{"position":[[1177,2]]},"898":{"position":[[2381,2],[2608,2]]},"908":{"position":[[2180,2],[2765,2],[2816,2],[2888,2]]}}}],["23*4",{"_index":5444,"t":{"1031":{"position":[[183,4],[222,4]]}}}],["23*4))\"echo",{"_index":5445,"t":{"1031":{"position":[[192,14]]}}}],["23+34",{"_index":3319,"t":{"510":{"position":[[653,5]]}}}],["23enter",{"_index":3316,"t":{"510":{"position":[[603,7],[1177,7]]}}}],["24",{"_index":2833,"t":{"436":{"position":[[352,2]]},"612":{"position":[[1805,2]]},"714":{"position":[[854,2],[995,2]]}}}],["240.00",{"_index":3952,"t":{"658":{"position":[[1642,6]]}}}],["249",{"_index":5211,"t":{"908":{"position":[[2049,3]]}}}],["25",{"_index":1704,"t":{"217":{"position":[[1023,2],[1068,2]]},"592":{"position":[[945,2]]},"612":{"position":[[1797,2]]}}}],["2532277",{"_index":3958,"t":{"658":{"position":[[2444,7]]},"660":{"position":[[937,7]]},"696":{"position":[[5319,7]]},"702":{"position":[[405,7]]}}}],["2532277..4a28994",{"_index":3956,"t":{"658":{"position":[[1718,16],[2377,16]]}}}],["2532277..4a28994fast",{"_index":3961,"t":{"660":{"position":[[267,20]]}}}],["255",{"_index":3456,"t":{"536":{"position":[[1117,3]]}}}],["256color",{"_index":4440,"t":{"734":{"position":[[1226,9]]}}}],["25811",{"_index":3722,"t":{"594":{"position":[[1413,5],[1480,5]]}}}],["26",{"_index":4030,"t":{"668":{"position":[[11,2]]},"714":{"position":[[405,4]]},"720":{"position":[[7321,2]]},"754":{"position":[[1935,2]]},"834":{"position":[[421,2]]}}}],["2646",{"_index":3716,"t":{"594":{"position":[[1260,4]]}}}],["27",{"_index":1538,"t":{"175":{"position":[[2642,2]]},"402":{"position":[[748,3],[1108,3]]},"432":{"position":[[1057,2]]},"450":{"position":[[4438,2],[4696,2]]},"454":{"position":[[1433,2],[2342,2]]},"612":{"position":[[1788,2]]},"680":{"position":[[453,2]]},"762":{"position":[[473,2]]}}}],["272.220.1",{"_index":2229,"t":{"314":{"position":[[210,9]]}}}],["27598[1",{"_index":4259,"t":{"714":{"position":[[2983,8]]}}}],["27600[2",{"_index":4260,"t":{"714":{"position":[[3012,8]]}}}],["27601[3",{"_index":4261,"t":{"714":{"position":[[3041,8]]}}}],["277",{"_index":5225,"t":{"908":{"position":[[3054,3]]}}}],["27t12:24:37.429z",{"_index":2716,"t":{"402":{"position":[[552,16]]}}}],["27t12:25:11.415z",{"_index":2735,"t":{"402":{"position":[[912,16]]}}}],["27th",{"_index":2998,"t":{"450":{"position":[[4669,4]]}}}],["28",{"_index":3444,"t":{"534":{"position":[[886,4]]},"538":{"position":[[1016,3],[1458,3],[1490,3],[2694,3]]},"626":{"position":[[3484,2],[3629,2]]}}}],["280",{"_index":3483,"t":{"544":{"position":[[303,3]]}}}],["28\\'...\\n/tmp/2021",{"_index":3443,"t":{"534":{"position":[[864,18]]}}}],["29",{"_index":1677,"t":{"213":{"position":[[270,4]]},"249":{"position":[[601,2]]},"502":{"position":[[1426,2]]},"852":{"position":[[1071,3],[1098,3],[1124,3],[1148,2],[1191,2]]}}}],["29t12:50:30.594z",{"_index":2647,"t":{"398":{"position":[[448,17]]}}}],["29t12:50:31.827z",{"_index":2652,"t":{"398":{"position":[[606,17],[738,17],[870,17]]}}}],["29t12:50:31.848z",{"_index":2657,"t":{"398":{"position":[[1002,17]]}}}],["29t12:50:31.849z",{"_index":2659,"t":{"398":{"position":[[1130,17]]}}}],["29t12:50:52.721z",{"_index":2970,"t":{"450":{"position":[[3145,17]]}}}],["29t12:50:52.722z",{"_index":2972,"t":{"450":{"position":[[3273,17]]}}}],["29t12:50:52.762z",{"_index":2974,"t":{"450":{"position":[[3429,17]]}}}],["29t12:50:52.763z",{"_index":2976,"t":{"450":{"position":[[3488,17],[3575,17]]}}}],["29t12:50:52.764z",{"_index":2979,"t":{"450":{"position":[[3638,17]]}}}],["29t12:50:52.765z",{"_index":2981,"t":{"450":{"position":[[3729,17]]}}}],["29t12:50:52.766z",{"_index":2983,"t":{"450":{"position":[[3788,17]]}}}],["29t12:50:52.784z",{"_index":2985,"t":{"450":{"position":[[3875,17]]}}}],["29t12:50:52.785z",{"_index":2990,"t":{"450":{"position":[[3960,17]]}}}],["2>&1",{"_index":1952,"t":{"257":{"position":[[1711,4],[2042,4],[3725,4],[5963,4],[6298,4],[6580,4]]},"262":{"position":[[1793,4]]},"666":{"position":[[1243,5]]},"738":{"position":[[3162,5],[4725,4]]},"854":{"position":[[753,5]]},"866":{"position":[[287,5]]}}}],["2>./errors.txt",{"_index":1968,"t":{"257":{"position":[[4613,14]]}}}],["2>/dev/nul",{"_index":1973,"t":{"257":{"position":[[5119,11]]},"262":{"position":[[1893,11]]},"668":{"position":[[1578,13],[1784,13],[1875,13]]},"720":{"position":[[6150,13],[6241,13]]},"858":{"position":[[653,11]]}}}],["2>1",{"_index":1966,"t":{"257":{"position":[[4127,3]]}}}],["2>>./all",{"_index":1978,"t":{"257":{"position":[[5714,8]]}}}],["2elast",{"_index":2965,"t":{"450":{"position":[[2036,9]]}}}],["2fdgo",{"_index":4654,"t":{"774":{"position":[[653,5]]}}}],["2loop",{"_index":3692,"t":{"590":{"position":[[783,5]]}}}],["2nd",{"_index":5287,"t":{"969":{"position":[[74,3],[160,3]]}}}],["2x",{"_index":5337,"t":{"985":{"position":[[1505,2]]}}}],["2xm7t",{"_index":2948,"t":{"450":{"position":[[1295,5]]}}}],["2xm7tmetricbeat",{"_index":2963,"t":{"450":{"position":[[1797,15],[2272,15],[2781,15]]}}}],["3",{"_index":682,"t":{"47":{"position":[[452,1],[2977,2],[3073,1]]},"69":{"position":[[5775,2]]},"71":{"position":[[229,2]]},"117":{"position":[[771,1]]},"219":{"position":[[1758,1]]},"257":{"position":[[3430,1]]},"332":{"position":[[5,1]]},"374":{"position":[[186,1],[486,1]]},"376":{"position":[[4911,5],[5890,1]]},"400":{"position":[[171,1]]},"406":{"position":[[541,1]]},"442":{"position":[[478,1],[737,1]]},"446":{"position":[[1303,1],[1910,1],[2628,1]]},"450":{"position":[[170,1],[4200,1],[4384,1],[4835,1],[4886,1],[5050,1]]},"454":{"position":[[103,1],[558,1],[789,1]]},"456":{"position":[[584,1]]},"458":{"position":[[288,1],[764,1]]},"466":{"position":[[1110,1]]},"490":{"position":[[1222,1]]},"506":{"position":[[2582,1]]},"510":{"position":[[2155,2]]},"522":{"position":[[1657,1]]},"524":{"position":[[385,1],[399,1]]},"526":{"position":[[362,1],[1178,1],[1198,1]]},"598":{"position":[[328,3]]},"604":{"position":[[370,1]]},"658":{"position":[[1553,1]]},"714":{"position":[[3036,4]]},"720":{"position":[[887,2],[1244,2],[7356,1]]},"802":{"position":[[107,1],[342,2],[413,1],[557,1]]},"806":{"position":[[2023,1]]},"822":{"position":[[194,1]]},"858":{"position":[[2370,1],[2596,1]]},"868":{"position":[[537,3]]},"878":{"position":[[2028,1]]},"896":{"position":[[1367,2]]},"985":{"position":[[1336,2]]},"1027":{"position":[[5757,1]]}}}],["3))done",{"_index":4976,"t":{"858":{"position":[[2399,7]]}}}],["3+4",{"_index":3324,"t":{"510":{"position":[[1762,8]]}}}],["3.0",{"_index":2087,"t":{"282":{"position":[[1300,4]]}}}],["3.1.18",{"_index":5473,"t":{"1035":{"position":[[437,6],[505,6],[1450,6],[1518,6]]}}}],["3.1.412",{"_index":5478,"t":{"1035":{"position":[[561,7],[1574,7]]}}}],["3.2a",{"_index":4855,"t":{"822":{"position":[[138,4]]}}}],["3.8.5",{"_index":2808,"t":{"432":{"position":[[1037,5]]}}}],["3.9.10",{"_index":4710,"t":{"802":{"position":[[466,6]]}}}],["3.md",{"_index":3109,"t":{"464":{"position":[[213,4],[680,7],[1304,5]]}}}],["3/3",{"_index":3949,"t":{"658":{"position":[[1527,6],[1623,6]]}}}],["30",{"_index":1724,"t":{"219":{"position":[[1022,2]]},"458":{"position":[[137,2]]},"474":{"position":[[824,3]]},"492":{"position":[[846,2]]},"526":{"position":[[1186,2],[1214,2]]},"588":{"position":[[2906,2]]},"876":{"position":[[1045,2]]},"908":{"position":[[1292,2]]}}}],["3000",{"_index":1757,"t":{"225":{"position":[[602,4],[1020,4],[1336,4]]},"229":{"position":[[215,4],[260,4],[565,4]]},"231":{"position":[[369,4],[427,4],[684,4],[802,4],[1464,4],[1716,4],[2030,4]]},"233":{"position":[[204,4]]},"235":{"position":[[357,4],[699,4]]},"626":{"position":[[384,6]]}}}],["3000serv",{"_index":1761,"t":{"225":{"position":[[1303,11]]},"231":{"position":[[336,11],[651,11]]}}}],["300italic.woff2",{"_index":2992,"t":{"450":{"position":[[4046,19],[4629,19]]}}}],["300italic.woff22020",{"_index":2989,"t":{"450":{"position":[[3937,19]]}}}],["300italic.woff2info",{"_index":2997,"t":{"450":{"position":[[4546,19]]}}}],["3072",{"_index":5114,"t":{"892":{"position":[[1678,5]]}}}],["30m",{"_index":4299,"t":{"716":{"position":[[3546,3]]}}}],["31",{"_index":3519,"t":{"544":{"position":[[2253,4]]},"626":{"position":[[3481,2],[3626,2]]},"754":{"position":[[682,2]]},"838":{"position":[[621,2],[2020,2]]},"898":{"position":[[2378,2],[2605,2]]},"908":{"position":[[2177,2],[2762,2],[2813,2],[2885,2]]}}}],["31195",{"_index":5068,"t":{"878":{"position":[[1758,5]]}}}],["31506",{"_index":3719,"t":{"594":{"position":[[1321,5],[1468,5]]}}}],["31548e4",{"_index":3974,"t":{"660":{"position":[[1021,7]]},"696":{"position":[[5418,7]]},"702":{"position":[[504,7]]}}}],["32",{"_index":2175,"t":{"302":{"position":[[409,2],[479,2]]},"420":{"position":[[1282,2]]},"510":{"position":[[2841,2]]},"718":{"position":[[649,2],[1340,2],[1494,3],[1596,3],[1628,2],[2163,2]]}}}],["32436",{"_index":3720,"t":{"594":{"position":[[1352,5],[1474,5]]}}}],["325b",{"_index":4608,"t":{"764":{"position":[[2148,4]]}}}],["329",{"_index":2941,"t":{"450":{"position":[[1208,3]]}}}],["33",{"_index":3398,"t":{"524":{"position":[[440,2]]},"902":{"position":[[1574,2]]},"906":{"position":[[570,2],[1075,3]]},"912":{"position":[[423,3]]},"995":{"position":[[119,2]]}}}],["33the",{"_index":3397,"t":{"524":{"position":[[420,5]]}}}],["34",{"_index":3318,"t":{"510":{"position":[[647,2],[1221,2]]}}}],["345",{"_index":1959,"t":{"257":{"position":[[2999,3]]}}}],["3486",{"_index":2085,"t":{"282":{"position":[[1259,5]]}}}],["34the",{"_index":3317,"t":{"510":{"position":[[627,5],[1201,5]]}}}],["35d",{"_index":2954,"t":{"450":{"position":[[1424,3]]}}}],["35delk",{"_index":2926,"t":{"450":{"position":[[948,6],[1031,6]]}}}],["35dfilebeat",{"_index":2934,"t":{"450":{"position":[[1072,11]]}}}],["35dmetricbeat",{"_index":2946,"t":{"450":{"position":[[1265,13],[1318,13],[1371,13]]}}}],["36)5",{"_index":3518,"t":{"544":{"position":[[2243,6]]}}}],["36/36",{"_index":3920,"t":{"654":{"position":[[2370,8]]}}}],["364272371462227929",{"_index":3735,"t":{"600":{"position":[[933,18]]}}}],["37",{"_index":3769,"t":{"612":{"position":[[1778,2]]}}}],["37m",{"_index":4300,"t":{"716":{"position":[[3553,4]]}}}],["384",{"_index":1137,"t":{"77":{"position":[[4071,3]]}}}],["39",{"_index":3914,"t":{"654":{"position":[[2258,3],[2450,2]]}}}],["39/39",{"_index":3916,"t":{"654":{"position":[[2290,8],[2406,8]]}}}],["3loop",{"_index":3693,"t":{"590":{"position":[[789,5]]}}}],["3w",{"_index":4670,"t":{"778":{"position":[[608,4]]}}}],["3wgo",{"_index":4626,"t":{"768":{"position":[[415,4]]}}}],["3you",{"_index":5051,"t":{"868":{"position":[[498,4]]}}}],["4",{"_index":684,"t":{"47":{"position":[[515,1],[3975,2],[4089,1]]},"227":{"position":[[662,1]]},"251":{"position":[[357,1]]},"253":{"position":[[2264,1]]},"262":{"position":[[3275,1]]},"446":{"position":[[620,1],[3648,1]]},"450":{"position":[[289,1]]},"454":{"position":[[769,1]]},"458":{"position":[[951,1],[1289,1]]},"476":{"position":[[5,1]]},"502":{"position":[[984,3],[1002,1],[1006,1]]},"510":{"position":[[1801,4]]},"526":{"position":[[364,1]]},"598":{"position":[[348,3]]},"658":{"position":[[1442,2]]},"714":{"position":[[2963,3]]},"720":{"position":[[928,2],[1285,2]]},"868":{"position":[[580,3]]},"1023":{"position":[[903,1]]},"1027":{"position":[[5759,1]]}}}],["4**3",{"_index":3330,"t":{"510":{"position":[[1916,9]]}}}],["4*2",{"_index":3326,"t":{"510":{"position":[[1843,8]]}}}],["4.0",{"_index":2055,"t":{"276":{"position":[[993,4]]}}}],["4/2",{"_index":3328,"t":{"510":{"position":[[1879,8]]}}}],["4/4",{"_index":3948,"t":{"658":{"position":[[1481,6]]}}}],["40",{"_index":3411,"t":{"526":{"position":[[1189,2]]},"612":{"position":[[1770,2]]}}}],["400",{"_index":5167,"t":{"900":{"position":[[1310,3],[1473,3]]}}}],["401",{"_index":2732,"t":{"402":{"position":[[841,4],[1201,4]]}}}],["404",{"_index":1770,"t":{"225":{"position":[[1580,4],[1672,3]]},"804":{"position":[[784,4],[1037,4]]}}}],["40960",{"_index":1702,"t":{"217":{"position":[[1013,5]]}}}],["40m",{"_index":4301,"t":{"716":{"position":[[3585,3]]}}}],["42)4",{"_index":3517,"t":{"544":{"position":[[2231,6]]}}}],["43",{"_index":3280,"t":{"506":{"position":[[658,2]]},"524":{"position":[[446,2]]},"544":{"position":[[1357,3]]}}}],["44",{"_index":3500,"t":{"544":{"position":[[389,2]]}}}],["4485",{"_index":5204,"t":{"908":{"position":[[1594,4]]}}}],["45",{"_index":3498,"t":{"544":{"position":[[374,2]]}}}],["454e",{"_index":2721,"t":{"402":{"position":[[659,4]]}}}],["45b",{"_index":4675,"t":{"778":{"position":[[1896,3]]}}}],["47",{"_index":3767,"t":{"612":{"position":[[1762,2]]}}}],["47m",{"_index":4302,"t":{"716":{"position":[[3592,3]]}}}],["48",{"_index":3496,"t":{"544":{"position":[[365,2]]},"734":{"position":[[1639,2]]}}}],["49833",{"_index":5069,"t":{"878":{"position":[[1786,5],[1859,5]]}}}],["49834",{"_index":5071,"t":{"878":{"position":[[1835,5]]}}}],["49835",{"_index":5072,"t":{"878":{"position":[[1870,5]]}}}],["4][0",{"_index":2267,"t":{"336":{"position":[[873,4],[929,4]]}}}],["4a28994",{"_index":3959,"t":{"658":{"position":[[2455,7]]},"660":{"position":[[873,7]]}}}],["4e50",{"_index":2738,"t":{"402":{"position":[[1019,4]]}}}],["4jgo",{"_index":4625,"t":{"768":{"position":[[365,4]]}}}],["4loop",{"_index":3694,"t":{"590":{"position":[[795,5]]}}}],["4you",{"_index":5053,"t":{"868":{"position":[[541,4]]}}}],["5",{"_index":686,"t":{"47":{"position":[[565,1],[4914,2],[4998,1],[5646,1]]},"65":{"position":[[282,2]]},"243":{"position":[[782,1]]},"253":{"position":[[3245,1],[3510,3]]},"255":{"position":[[1253,1]]},"304":{"position":[[2110,1]]},"390":{"position":[[1215,1]]},"400":{"position":[[890,1],[1497,1]]},"456":{"position":[[11,1]]},"480":{"position":[[174,1]]},"490":{"position":[[1224,1]]},"526":{"position":[[374,1]]},"544":{"position":[[336,3]]},"586":{"position":[[1753,2]]},"590":{"position":[[709,2],[801,1]]},"594":{"position":[[688,1]]},"598":{"position":[[369,3]]},"618":{"position":[[5,1]]},"634":{"position":[[2298,4]]},"714":{"position":[[2992,3]]},"720":{"position":[[972,2],[1329,2]]},"908":{"position":[[2019,1]]},"1027":{"position":[[5761,1]]}}}],["5))\"10",{"_index":3865,"t":{"634":{"position":[[2305,6]]}}}],["5)for",{"_index":3744,"t":{"604":{"position":[[372,5]]}}}],["5.0",{"_index":4246,"t":{"714":{"position":[[1091,6]]}}}],["5.0.17",{"_index":4247,"t":{"714":{"position":[[1157,9]]}}}],["5.10",{"_index":5132,"t":{"896":{"position":[[966,5]]}}}],["5.4.0",{"_index":3872,"t":{"636":{"position":[[1806,5]]}}}],["50",{"_index":3494,"t":{"544":{"position":[[356,2]]},"686":{"position":[[150,2]]}}}],["500",{"_index":2656,"t":{"398":{"position":[[726,3],[858,3],[990,3],[1118,3],[1246,3]]}}}],["500error",{"_index":3058,"t":{"454":{"position":[[1656,8],[1761,8]]}}}],["500g",{"_index":1719,"t":{"219":{"position":[[571,4]]}}}],["500info",{"_index":3059,"t":{"454":{"position":[[1862,7]]}}}],["5094983766",{"_index":4674,"t":{"778":{"position":[[1880,11]]}}}],["50top",{"_index":3412,"t":{"526":{"position":[[1192,5]]}}}],["53a41a98",{"_index":2719,"t":{"402":{"position":[[644,9]]}}}],["54",{"_index":3492,"t":{"544":{"position":[[348,2]]}}}],["57",{"_index":3322,"t":{"510":{"position":[[1227,2]]}}}],["5802",{"_index":2688,"t":{"400":{"position":[[1510,4]]}}}],["5803",{"_index":2689,"t":{"400":{"position":[[1557,4]]}}}],["5804",{"_index":2691,"t":{"400":{"position":[[1603,4]]}}}],["5805",{"_index":2693,"t":{"400":{"position":[[1620,4]]}}}],["5806",{"_index":2695,"t":{"400":{"position":[[1636,4]]}}}],["5807",{"_index":2697,"t":{"400":{"position":[[1654,4]]}}}],["5808",{"_index":2698,"t":{"400":{"position":[[1671,4]]}}}],["5809",{"_index":2701,"t":{"400":{"position":[[1760,4]]}}}],["5810",{"_index":2702,"t":{"400":{"position":[[1784,4]]}}}],["5811",{"_index":2703,"t":{"400":{"position":[[1808,4]]}}}],["5812",{"_index":2705,"t":{"400":{"position":[[1851,4]]}}}],["5]|[0",{"_index":2266,"t":{"336":{"position":[[867,5],[923,5]]}}}],["5cb57bdc45",{"_index":2944,"t":{"450":{"position":[[1234,10],[1754,10],[2229,10],[2738,10]]}}}],["5common",{"_index":3511,"t":{"544":{"position":[[2186,7]]}}}],["5j",{"_index":4624,"t":{"768":{"position":[[216,3]]}}}],["5loop",{"_index":3703,"t":{"592":{"position":[[918,5]]}}}],["5sum",{"_index":3402,"t":{"526":{"position":[[366,7]]}}}],["6",{"_index":689,"t":{"47":{"position":[[625,1],[5802,2],[5844,1],[5953,1],[6162,1]]},"368":{"position":[[1710,1]]},"510":{"position":[[2283,2],[2298,1]]},"524":{"position":[[405,1]]},"612":{"position":[[1784,3]]},"714":{"position":[[3021,3]]},"720":{"position":[[1013,2],[1370,2]]},"728":{"position":[[5,1]]},"858":{"position":[[2629,1]]},"1023":{"position":[[905,1]]},"1027":{"position":[[5763,2]]}}}],["60",{"_index":5281,"t":{"959":{"position":[[42,3]]},"961":{"position":[[40,3]]}}}],["6097",{"_index":2951,"t":{"450":{"position":[[1366,4]]}}}],["60louch",{"_index":4817,"t":{"806":{"position":[[3177,9]]}}}],["6103",{"_index":2949,"t":{"450":{"position":[[1313,4]]}}}],["6109",{"_index":2953,"t":{"450":{"position":[[1419,4]]}}}],["62",{"_index":3490,"t":{"544":{"position":[[340,2]]}}}],["63ea74f",{"_index":3978,"t":{"660":{"position":[[1154,7]]},"694":{"position":[[1900,7]]},"696":{"position":[[5551,7]]},"702":{"position":[[637,7]]}}}],["64",{"_index":3331,"t":{"510":{"position":[[1935,2]]},"1027":{"position":[[3905,2]]}}}],["6429",{"_index":4450,"t":{"734":{"position":[[1653,6]]}}}],["65b698fb8c",{"_index":2927,"t":{"450":{"position":[[966,10],[1596,10],[2071,10],[2580,10]]}}}],["6803",{"_index":3721,"t":{"594":{"position":[[1383,4]]}}}],["6cbf419a365",{"_index":2740,"t":{"402":{"position":[[1029,14]]}}}],["6f8bb6457b",{"_index":2932,"t":{"450":{"position":[[1041,10],[1635,10],[2110,10],[2619,10]]}}}],["6the",{"_index":3396,"t":{"524":{"position":[[387,4]]}}}],["7",{"_index":691,"t":{"47":{"position":[[643,1],[6562,2],[6658,1]]},"217":{"position":[[909,1],[915,1],[966,1]]},"284":{"position":[[1503,1]]},"394":{"position":[[1837,1]]},"448":{"position":[[1880,1]]},"462":{"position":[[1371,1]]},"490":{"position":[[1282,1]]},"510":{"position":[[1780,1]]},"534":{"position":[[1923,1]]},"544":{"position":[[575,1]]},"654":{"position":[[2460,3]]},"714":{"position":[[3050,3]]},"720":{"position":[[1055,2],[1412,2]]},"862":{"position":[[3212,1]]},"1027":{"position":[[6277,1]]}}}],["7%3",{"_index":3333,"t":{"510":{"position":[[1953,8]]}}}],["7/7",{"_index":3932,"t":{"654":{"position":[[2528,6]]}}}],["7025",{"_index":1784,"t":{"229":{"position":[[225,5]]}}}],["71",{"_index":4900,"t":{"838":{"position":[[2094,2]]},"898":{"position":[[822,2],[1237,2],[2190,2]]},"902":{"position":[[453,2]]},"908":{"position":[[2934,2]]}}}],["72",{"_index":3489,"t":{"544":{"position":[[330,2]]}}}],["72c97dc40e96",{"_index":2723,"t":{"402":{"position":[[669,14]]}}}],["73",{"_index":3766,"t":{"612":{"position":[[1754,2]]}}}],["74)3",{"_index":3515,"t":{"544":{"position":[[2221,6]]}}}],["7657",{"_index":1792,"t":{"231":{"position":[[385,4]]}}}],["8",{"_index":697,"t":{"47":{"position":[[735,1],[6894,2],[7046,1]]},"217":{"position":[[960,1]]},"284":{"position":[[1140,1]]},"302":{"position":[[416,1],[486,1]]},"510":{"position":[[1861,1]]},"804":{"position":[[910,3]]},"969":{"position":[[361,1]]},"1023":{"position":[[907,1]]}}}],["8.2.4314",{"_index":4571,"t":{"760":{"position":[[88,9]]}}}],["80",{"_index":4124,"t":{"686":{"position":[[481,2]]},"820":{"position":[[103,3]]}}}],["8000",{"_index":1864,"t":{"243":{"position":[[1641,4]]}}}],["800px",{"_index":5323,"t":{"985":{"position":[[407,9],[528,9]]}}}],["8022",{"_index":5198,"t":{"908":{"position":[[833,4],[1171,5]]}}}],["8080",{"_index":2666,"t":{"400":{"position":[[299,6]]}}}],["8342bec",{"_index":4223,"t":{"704":{"position":[[1826,7]]},"927":{"position":[[1779,7]]}}}],["834bec",{"_index":4224,"t":{"704":{"position":[[1861,7]]},"927":{"position":[[1814,7]]}}}],["84",{"_index":3487,"t":{"544":{"position":[[322,2]]}}}],["9",{"_index":700,"t":{"47":{"position":[[802,1]]},"336":{"position":[[802,1],[837,1],[969,1]]},"344":{"position":[[1035,2],[1138,2],[1204,3],[1892,1],[1977,1],[1992,1],[5057,3]]},"376":{"position":[[6675,2]]},"394":{"position":[[402,1]]},"396":{"position":[[366,4],[710,4],[1609,3],[1647,3]]},"524":{"position":[[410,2]]},"714":{"position":[[3444,1]]},"834":{"position":[[2728,3]]},"858":{"position":[[2759,1]]}}}],["9!#$%&'*+/=?^_",{"_index":2255,"t":{"336":{"position":[[631,19],[664,19]]}}}],["9.3.0",{"_index":2811,"t":{"432":{"position":[[1080,6]]}}}],["9/5",{"_index":3347,"t":{"510":{"position":[[2834,4]]}}}],["90",{"_index":4113,"t":{"684":{"position":[[1617,2],[1746,2]]}}}],["911012",{"_index":1143,"t":{"77":{"position":[[4383,6]]}}}],["911k",{"_index":1142,"t":{"77":{"position":[[4328,4]]}}}],["9125",{"_index":2632,"t":{"394":{"position":[[1977,4]]},"396":{"position":[[380,4]]}}}],["9188",{"_index":2633,"t":{"394":{"position":[[1993,4]]}}}],["92",{"_index":5446,"t":{"1031":{"position":[[231,3]]}}}],["9211",{"_index":2634,"t":{"394":{"position":[[2047,4]]},"396":{"position":[[396,4]]}}}],["9341",{"_index":2635,"t":{"394":{"position":[[2060,4]]}}}],["9344",{"_index":2636,"t":{"394":{"position":[[2113,4]]}}}],["9347",{"_index":2637,"t":{"394":{"position":[[2165,4]]}}}],["9352",{"_index":2638,"t":{"394":{"position":[[2207,4]]}}}],["9355",{"_index":2639,"t":{"394":{"position":[[2255,4],[2489,5],[2513,4]]}}}],["962",{"_index":3951,"t":{"658":{"position":[[1630,3]]}}}],["96dkt",{"_index":2950,"t":{"450":{"position":[[1348,5]]}}}],["96dktmetricbeat",{"_index":2964,"t":{"450":{"position":[[1829,15],[2304,15],[2813,15]]}}}],["99",{"_index":4064,"t":{"670":{"position":[[811,3]]}}}],["9](?:[a",{"_index":2263,"t":{"336":{"position":[[791,7],[826,7]]}}}],["9]))\\.){3}(?:(2(5[0",{"_index":2272,"t":{"336":{"position":[[903,19]]}}}],["9])?\\.)+[a",{"_index":2264,"t":{"336":{"position":[[812,10]]}}}],["9])?|\\[(?:(?:(2(5[0",{"_index":2265,"t":{"336":{"position":[[847,19]]}}}],["9])|1[0",{"_index":2268,"t":{"336":{"position":[[878,7],[934,7]]}}}],["9])|[a",{"_index":2273,"t":{"336":{"position":[[959,6]]}}}],["9]+$)/p",{"_index":2509,"t":{"376":{"position":[[6512,8]]}}}],["9]+)\\).*/\\1",{"_index":2462,"t":{"374":{"position":[[524,13],[840,13]]}}}],["9]+@[a",{"_index":2326,"t":{"344":{"position":[[1191,6]]}}}],["9]:(?:[\\x01",{"_index":2274,"t":{"336":{"position":[[979,11]]}}}],["9]?[0",{"_index":2271,"t":{"336":{"position":[[897,5],[953,5]]}}}],["9][0",{"_index":2269,"t":{"336":{"position":[[886,4],[942,4]]}}}],["9]\\+\\s*//;s/[;&|]\\s*alert",{"_index":4478,"t":{"734":{"position":[[2937,36]]}}}],["9]|[1",{"_index":2270,"t":{"336":{"position":[[891,5],[947,5]]}}}],["9_",{"_index":2328,"t":{"344":{"position":[[2744,4],[4911,4]]}}}],["9dd0",{"_index":2739,"t":{"402":{"position":[[1024,4]]}}}],["_",{"_index":542,"t":{"31":{"position":[[1542,3],[1971,3],[2144,3]]},"720":{"position":[[1624,1]]},"774":{"position":[[733,3]]},"898":{"position":[[2284,2]]},"908":{"position":[[2083,2]]}}}],["_\"agnes_skinnerartie_ziffcletus_spucklerhank_scorpiohelen_lovejoyjohn_frinkkirk_van_houtennick_rivieraruth_powersseymore_skinnertimothy_lovejoy",{"_index":539,"t":{"31":{"position":[[1318,144]]}}}],["__",{"_index":5148,"t":{"898":{"position":[[2273,3]]},"908":{"position":[[2072,3]]}}}],["___|\\___|___|https://aws.amazon.com/amazon",{"_index":5150,"t":{"898":{"position":[[2310,42]]},"908":{"position":[[2109,42]]}}}],["__|_",{"_index":5149,"t":{"898":{"position":[[2277,4]]},"908":{"position":[[2076,4]]}}}],["_after_",{"_index":3597,"t":{"574":{"position":[[2088,7]]}}}],["_backup",{"_index":3203,"t":{"488":{"position":[[430,9]]}}}],["_copi",{"_index":1391,"t":{"109":{"position":[[109,7]]}}}],["_es_*}do",{"_index":5412,"t":{"1027":{"position":[[5286,11]]}}}],["_es_download_folder=~/downloads_es_backup_folder=~/backups_es_download_address=https://effect",{"_index":5410,"t":{"1027":{"position":[[5012,96]]}}}],["_git_info",{"_index":4041,"t":{"668":{"position":[[1454,11]]},"720":{"position":[[6011,11]]}}}],["_one",{"_index":2434,"t":{"368":{"position":[[1786,4]]}}}],["_original_ps1",{"_index":4353,"t":{"720":{"position":[[1487,14]]}}}],["_to_zsh",{"_index":4388,"t":{"720":{"position":[[4070,7]]}}}],["a'alia",{"_index":3802,"t":{"624":{"position":[[751,7]]},"734":{"position":[[2711,7]]}}}],["a49d5546",{"_index":2736,"t":{"402":{"position":[[1004,9]]}}}],["a51ae1a",{"_index":3977,"t":{"660":{"position":[[1094,7]]},"694":{"position":[[1840,7]]},"696":{"position":[[5491,7]]},"702":{"position":[[577,7]]}}}],["a51ae1aa42432c2f391ca782c1c20b3793c232ab",{"_index":4168,"t":{"694":{"position":[[700,40]]}}}],["a51ae1aauthor",{"_index":4158,"t":{"694":{"position":[[430,14]]}}}],["a8cbb15",{"_index":3972,"t":{"660":{"position":[[976,7]]},"696":{"position":[[5373,7]]},"702":{"position":[[459,7]]}}}],["a95bd90",{"_index":3979,"t":{"660":{"position":[[1200,7]]},"694":{"position":[[422,7],[1946,7]]},"696":{"position":[[5597,7]]},"702":{"position":[[683,7]]}}}],["a95bd90e3656b2e55b8708193d387c80c282a6adauthor",{"_index":4165,"t":{"694":{"position":[[545,47]]}}}],["a=x",{"_index":1726,"t":{"219":{"position":[[1141,4]]}}}],["aadf",{"_index":2722,"t":{"402":{"position":[[664,4]]}}}],["aappend",{"_index":4633,"t":{"770":{"position":[[999,7]]}}}],["abbrevi",{"_index":4248,"t":{"714":{"position":[[1212,11]]},"744":{"position":[[2623,11]]}}}],["abc",{"_index":2660,"t":{"400":{"position":[[129,4]]},"410":{"position":[[369,3]]},"1035":{"position":[[2194,5]]}}}],["abcddefghhiijllmnoopqrssuvvwxz",{"_index":2585,"t":{"390":{"position":[[201,31]]}}}],["abil",{"_index":3653,"t":{"584":{"position":[[2535,7]]},"696":{"position":[[72,7]]},"746":{"position":[[1755,7]]}}}],["abnorm",{"_index":4779,"t":{"804":{"position":[[2513,8]]},"806":{"position":[[3453,8]]}}}],["abort",{"_index":1790,"t":{"231":{"position":[[240,6]]},"684":{"position":[[358,6],[822,6]]},"692":{"position":[[1437,6]]},"696":{"position":[[2119,6],[2415,6],[2425,5],[5005,6]]},"764":{"position":[[1976,6]]},"850":{"position":[[456,5],[1780,5]]},"858":{"position":[[314,6],[1565,8],[2000,9],[2225,9],[2481,5]]},"1027":{"position":[[2654,6],[3000,5]]}}}],["aborted.cleanup",{"_index":4960,"t":{"858":{"position":[[823,15]]}}}],["abov",{"_index":643,"t":{"43":{"position":[[764,5]]},"47":{"position":[[3529,6]]},"69":{"position":[[2301,5]]},"77":{"position":[[3498,5]]},"175":{"position":[[1542,6]]},"227":{"position":[[384,6]]},"280":{"position":[[974,6],[1123,6]]},"288":{"position":[[1748,5]]},"336":{"position":[[1374,5],[1612,6]]},"338":{"position":[[64,6]]},"352":{"position":[[2402,5],[2623,6]]},"368":{"position":[[25,6]]},"378":{"position":[[4767,6]]},"394":{"position":[[2435,6]]},"396":{"position":[[35,6]]},"448":{"position":[[891,5]]},"468":{"position":[[1605,6],[2317,5]]},"490":{"position":[[1621,5]]},"508":{"position":[[2218,6]]},"524":{"position":[[994,6]]},"572":{"position":[[726,5]]},"574":{"position":[[2021,6]]},"602":{"position":[[1250,6]]},"688":{"position":[[1526,5]]},"696":{"position":[[4304,5]]},"712":{"position":[[238,6]]},"716":{"position":[[4944,5]]},"718":{"position":[[1410,5]]},"720":{"position":[[6913,5],[7700,5]]},"742":{"position":[[1112,5]]},"746":{"position":[[1953,5]]},"760":{"position":[[544,6],[848,5],[1379,6]]},"770":{"position":[[552,5],[1151,5]]},"808":{"position":[[1650,5]]},"852":{"position":[[1563,5]]},"874":{"position":[[244,6]]},"888":{"position":[[270,5]]},"1023":{"position":[[353,5],[610,5],[869,5]]},"1025":{"position":[[149,5],[340,5]]},"1027":{"position":[[634,5],[934,5],[1132,5],[2227,6],[3599,5],[3877,6],[4133,5],[5222,9],[5724,5],[6240,5],[6916,5],[7297,5],[7671,6],[7811,5],[8335,5],[8805,5]]},"1029":{"position":[[360,5],[862,5]]},"1033":{"position":[[2397,6]]}}}],["above)set",{"_index":4891,"t":{"834":{"position":[[4677,9]]}}}],["absinth",{"_index":4759,"t":{"804":{"position":[[2168,8]]},"806":{"position":[[3224,8]]}}}],["absolut",{"_index":382,"t":{"23":{"position":[[1514,10]]},"135":{"position":[[142,8],[180,8],[276,8],[386,8],[465,8],[590,8],[1095,8],[1309,8]]},"139":{"position":[[1148,8]]},"145":{"position":[[410,8]]},"670":{"position":[[22,8]]},"738":{"position":[[3751,10]]},"939":{"position":[[272,8]]},"941":{"position":[[41,8]]},"945":{"position":[[0,10]]}}}],["abstract",{"_index":152,"t":{"15":{"position":[[59,12],[138,12]]},"274":{"position":[[397,9],[471,11]]},"292":{"position":[[570,11]]}}}],["accept",{"_index":1555,"t":{"181":{"position":[[268,6]]},"253":{"position":[[1081,6]]}}}],["access",{"_index":57,"t":{"8":{"position":[[90,6]]},"21":{"position":[[53,6]]},"23":{"position":[[361,8]]},"25":{"position":[[58,6]]},"33":{"position":[[196,6],[872,8]]},"37":{"position":[[444,6]]},"91":{"position":[[3152,6],[3475,10]]},"129":{"position":[[740,6]]},"139":{"position":[[241,8],[751,10]]},"249":{"position":[[18,6],[925,6]]},"257":{"position":[[2356,6],[6084,6]]},"262":{"position":[[560,6]]},"276":{"position":[[294,6],[573,6]]},"278":{"position":[[185,6],[227,6],[305,6],[900,6]]},"280":{"position":[[94,6],[250,9]]},"282":{"position":[[1785,6]]},"290":{"position":[[1471,6]]},"292":{"position":[[1236,6],[1499,6]]},"378":{"position":[[5194,9]]},"484":{"position":[[74,10]]},"522":{"position":[[1213,6]]},"604":{"position":[[622,6]]},"646":{"position":[[149,6],[496,6]]},"654":{"position":[[662,8]]},"662":{"position":[[739,10]]},"754":{"position":[[1004,6]]},"890":{"position":[[2319,6]]},"892":{"position":[[2394,6]]},"894":{"position":[[1101,6]]},"896":{"position":[[2129,6]]},"898":{"position":[[2940,6],[3050,9]]},"900":{"position":[[394,10]]},"908":{"position":[[112,6]]},"910":{"position":[[464,6],[575,10],[626,6],[1365,10],[1652,12]]}}}],["accident",{"_index":3176,"t":{"482":{"position":[[815,12]]},"522":{"position":[[535,12]]},"894":{"position":[[1004,12]]}}}],["accompani",{"_index":4772,"t":{"804":{"position":[[2373,11]]},"806":{"position":[[3326,11]]}}}],["accomplish",{"_index":343,"t":{"23":{"position":[[232,11]]},"422":{"position":[[1212,10]]},"450":{"position":[[2923,10],[5326,10]]},"574":{"position":[[3217,11]]}}}],["account",{"_index":3601,"t":{"574":{"position":[[2846,7]]},"584":{"position":[[1347,8]]},"648":{"position":[[319,8],[2395,8]]},"654":{"position":[[939,8]]},"662":{"position":[[123,8]]},"664":{"position":[[874,8]]},"894":{"position":[[209,7],[294,7],[329,8],[419,8],[1158,8],[1427,7]]},"912":{"position":[[138,8]]}}}],["accur",{"_index":2074,"t":{"280":{"position":[[1201,10]]}}}],["achiev",{"_index":1333,"t":{"95":{"position":[[1493,8]]},"133":{"position":[[618,7]]},"414":{"position":[[497,7]]}}}],["ack",{"_index":864,"t":{"47":{"position":[[6871,3]]},"408":{"position":[[1123,3]]}}}],["acknowledg",{"_index":5138,"t":{"896":{"position":[[2105,11]]}}}],["acquir",{"_index":4782,"t":{"804":{"position":[[2567,9]]}}}],["acronym",{"_index":1855,"t":{"243":{"position":[[1083,8]]},"292":{"position":[[1812,7]]}}}],["action",{"_index":63,"t":{"8":{"position":[[139,7]]},"21":{"position":[[973,8]]},"165":{"position":[[975,6]]},"189":{"position":[[134,6]]},"193":{"position":[[89,7]]},"209":{"position":[[11,6],[33,6],[218,8],[625,7]]},"211":{"position":[[63,7]]},"213":{"position":[[22,6],[928,6]]},"215":{"position":[[20,6],[51,7]]},"221":{"position":[[184,7]]},"247":{"position":[[310,6]]},"249":{"position":[[2285,6]]},"257":{"position":[[35,6]]},"464":{"position":[[1095,6],[1364,6],[1460,6],[2185,6]]},"468":{"position":[[2923,6]]},"508":{"position":[[215,7]]},"518":{"position":[[340,7]]},"526":{"position":[[94,7]]},"558":{"position":[[176,6]]},"634":{"position":[[2191,6]]},"636":{"position":[[1558,6]]},"680":{"position":[[234,6]]},"688":{"position":[[374,7]]},"700":{"position":[[212,7]]},"718":{"position":[[2416,6]]},"766":{"position":[[457,7],[848,7]]},"770":{"position":[[687,7]]},"772":{"position":[[269,6]]},"774":{"position":[[519,7]]},"806":{"position":[[1395,6]]},"816":{"position":[[528,6]]},"828":{"position":[[1359,6]]},"838":{"position":[[426,7]]},"864":{"position":[[307,7]]},"896":{"position":[[369,8]]},"908":{"position":[[1236,6]]},"995":{"position":[[90,6]]},"1027":{"position":[[10087,6]]},"1033":{"position":[[709,7]]},"1035":{"position":[[2484,6]]}}}],["activ",{"_index":2369,"t":{"354":{"position":[[1169,10]]}}}],["actual",{"_index":80,"t":{"8":{"position":[[345,6]]},"21":{"position":[[1707,8],[1987,8]]},"41":{"position":[[886,8]]},"43":{"position":[[848,8]]},"45":{"position":[[399,8]]},"47":{"position":[[3613,8],[6920,8],[8183,6]]},"59":{"position":[[10,8]]},"67":{"position":[[177,8],[819,8]]},"69":{"position":[[309,8]]},"71":{"position":[[1168,8],[2088,8]]},"77":{"position":[[1002,8],[2416,8]]},"119":{"position":[[516,8]]},"141":{"position":[[542,8]]},"165":{"position":[[460,8]]},"175":{"position":[[1343,8],[1559,8]]},"179":{"position":[[303,8]]},"185":{"position":[[377,8]]},"195":{"position":[[479,8]]},"205":{"position":[[333,8],[886,8],[930,6],[1025,8]]},"207":{"position":[[375,8]]},"217":{"position":[[1125,8]]},"221":{"position":[[292,8]]},"255":{"position":[[403,8]]},"257":{"position":[[11,8],[1262,8]]},"262":{"position":[[2768,8]]},"276":{"position":[[1187,8]]},"282":{"position":[[71,8]]},"288":{"position":[[90,8]]},"292":{"position":[[1888,8]]},"302":{"position":[[308,8]]},"304":{"position":[[1314,8],[1773,8]]},"308":{"position":[[160,8]]},"344":{"position":[[4096,8]]},"348":{"position":[[420,8],[704,8]]},"350":{"position":[[1102,8]]},"354":{"position":[[1190,8],[1224,8]]},"366":{"position":[[4187,8]]},"368":{"position":[[1574,8]]},"372":{"position":[[1095,8]]},"376":{"position":[[3254,8],[5389,8]]},"378":{"position":[[3058,8],[4846,6]]},"446":{"position":[[1001,8]]},"454":{"position":[[829,8]]},"462":{"position":[[1596,8]]},"464":{"position":[[585,8],[2336,8]]},"468":{"position":[[1732,8]]},"474":{"position":[[793,8],[844,6]]},"488":{"position":[[455,8]]},"526":{"position":[[826,8],[2273,10]]},"536":{"position":[[385,8]]},"540":{"position":[[703,8]]},"574":{"position":[[2689,8]]},"586":{"position":[[1988,8]]},"604":{"position":[[1004,8],[1315,8]]},"608":{"position":[[2433,8]]},"628":{"position":[[109,8]]},"638":{"position":[[388,8]]},"646":{"position":[[750,8]]},"658":{"position":[[164,6],[2518,8]]},"664":{"position":[[127,8]]},"666":{"position":[[719,8]]},"682":{"position":[[1942,8]]},"708":{"position":[[1722,8]]},"716":{"position":[[5335,8]]},"720":{"position":[[2272,8]]},"724":{"position":[[812,8]]},"738":{"position":[[424,8],[8009,8]]},"740":{"position":[[923,8]]},"756":{"position":[[117,8]]},"802":{"position":[[3410,8]]},"838":{"position":[[1715,8]]},"852":{"position":[[1618,6]]},"862":{"position":[[1707,8]]},"888":{"position":[[858,8]]},"890":{"position":[[1797,8]]},"904":{"position":[[18,8],[781,8]]},"906":{"position":[[455,8],[956,8]]},"1027":{"position":[[8856,8]]},"1033":{"position":[[4058,8]]},"1035":{"position":[[193,8]]}}}],["ad",{"_index":376,"t":{"23":{"position":[[1265,6]]},"69":{"position":[[2401,6]]},"95":{"position":[[740,5]]},"105":{"position":[[991,6]]},"107":{"position":[[143,5]]},"125":{"position":[[541,6]]},"179":{"position":[[170,5]]},"183":{"position":[[58,6]]},"197":{"position":[[343,5]]},"260":{"position":[[466,5]]},"302":{"position":[[939,6]]},"320":{"position":[[11,5]]},"338":{"position":[[305,6]]},"374":{"position":[[900,5]]},"376":{"position":[[2901,5]]},"402":{"position":[[1601,6]]},"428":{"position":[[477,6]]},"438":{"position":[[198,5]]},"442":{"position":[[1830,6]]},"446":{"position":[[2892,5]]},"466":{"position":[[281,5]]},"592":{"position":[[807,6]]},"604":{"position":[[1210,6]]},"626":{"position":[[4776,6]]},"648":{"position":[[1094,5]]},"678":{"position":[[150,6],[1320,6]]},"682":{"position":[[416,5]]},"684":{"position":[[1765,6],[1804,6],[2145,6],[2302,5],[2518,6]]},"688":{"position":[[1138,5],[2359,5]]},"692":{"position":[[811,5],[868,6]]},"696":{"position":[[2550,5]]},"700":{"position":[[1663,6],[2775,5],[3134,5],[3389,6]]},"734":{"position":[[3088,6],[3246,5]]},"746":{"position":[[964,6],[1139,6],[1614,6]]},"776":{"position":[[484,5]]},"778":{"position":[[174,6],[1503,6],[1588,5],[1833,6],[2009,6],[2114,6]]},"798":{"position":[[1713,6]]},"806":{"position":[[2931,6]]},"898":{"position":[[2172,5]]},"1027":{"position":[[1458,5],[6449,6]]}}}],["adapt",{"_index":2036,"t":{"274":{"position":[[159,8]]},"292":{"position":[[1562,7]]},"736":{"position":[[1189,5]]},"796":{"position":[[641,5]]}}}],["adapter3",{"_index":2027,"t":{"272":{"position":[[179,8]]}}}],["adapter4",{"_index":2028,"t":{"272":{"position":[[200,9]]}}}],["add",{"_index":540,"t":{"31":{"position":[[1468,3],[2205,3]]},"69":{"position":[[4538,4]]},"115":{"position":[[739,3]]},"119":{"position":[[705,3]]},"255":{"position":[[1612,3]]},"330":{"position":[[409,4]]},"338":{"position":[[175,3],[757,3]]},"344":{"position":[[1768,3],[1853,3]]},"366":{"position":[[1161,3]]},"370":{"position":[[105,3],[1112,4],[1316,3]]},"372":{"position":[[139,3]]},"376":{"position":[[2195,3],[3364,3],[5196,3]]},"400":{"position":[[1687,3]]},"408":{"position":[[1039,3]]},"414":{"position":[[447,3]]},"422":{"position":[[36,3],[977,3]]},"424":{"position":[[0,3]]},"426":{"position":[[587,3]]},"430":{"position":[[146,3]]},"440":{"position":[[419,3]]},"446":{"position":[[2781,3]]},"508":{"position":[[4099,3]]},"510":{"position":[[340,3]]},"514":{"position":[[533,4]]},"558":{"position":[[819,3],[1208,3]]},"594":{"position":[[746,3],[797,4],[870,3],[1096,3],[1225,3]]},"612":{"position":[[254,3]]},"626":{"position":[[8,3],[153,3],[278,3],[1208,3],[5230,3],[5591,3]]},"648":{"position":[[1020,3],[2120,3]]},"654":{"position":[[1990,3],[3457,3],[3564,3]]},"656":{"position":[[382,3]]},"658":{"position":[[523,3],[554,4],[580,3]]},"660":{"position":[[909,3],[997,3],[1029,3],[1117,3],[1162,3],[1208,3],[1264,3],[1328,3],[1369,3],[1403,3]]},"664":{"position":[[1743,4]]},"670":{"position":[[1366,3],[2329,3]]},"672":{"position":[[644,3]]},"682":{"position":[[327,3],[470,4],[923,3],[979,3],[983,4],[1027,3],[1041,3],[1649,3],[1904,3],[2213,3],[2254,3],[2432,3],[2835,3]]},"684":{"position":[[671,3],[1562,3],[2233,3],[2356,4],[2625,3],[2657,3]]},"686":{"position":[[338,3]]},"688":{"position":[[427,3],[1055,3],[1192,4],[1334,3],[1355,4],[1406,3],[2834,3],[2963,3],[2984,4],[3043,3]]},"692":{"position":[[170,3],[263,3],[282,4],[341,3],[395,3],[414,4],[511,4],[650,3],[767,4],[941,3]]},"694":{"position":[[659,3],[829,3],[1144,3],[1201,3],[1242,3],[1294,3],[1341,3],[1380,3],[1412,3],[1863,3],[1908,3],[1954,3],[2010,3],[2059,3],[2100,3],[2134,3]]},"696":{"position":[[652,3],[710,3],[1284,4],[1494,4],[1558,3],[1739,4],[2467,3],[2576,4],[2778,3],[4196,3],[4557,3],[4617,3],[5394,3],[5426,3],[5514,3],[5559,3],[5605,3],[5661,3],[5710,3],[5751,3],[5785,3]]},"700":{"position":[[1581,3],[1714,3],[1723,3],[2692,3],[2801,4],[3027,3],[3036,3],[3178,3],[3452,3]]},"702":{"position":[[480,3],[512,3],[600,3],[645,3],[691,3],[747,3],[796,3],[837,3],[871,3]]},"704":{"position":[[733,3]]},"720":{"position":[[3345,3],[5647,3],[7171,3],[8009,3]]},"724":{"position":[[373,3]]},"734":{"position":[[2213,3],[2731,3],[3501,3]]},"738":{"position":[[1027,3],[1953,3]]},"742":{"position":[[677,3]]},"746":{"position":[[791,3]]},"764":{"position":[[1447,3],[1501,3],[2454,3],[2483,3],[2526,3]]},"768":{"position":[[259,3]]},"774":{"position":[[863,3]]},"778":{"position":[[78,3],[1341,3],[2044,3],[2091,3]]},"802":{"position":[[1078,3],[1615,3]]},"804":{"position":[[228,3]]},"806":{"position":[[1422,3]]},"810":{"position":[[101,4],[704,3]]},"818":{"position":[[1966,3]]},"820":{"position":[[307,4]]},"834":{"position":[[1067,3]]},"840":{"position":[[349,3]]},"862":{"position":[[2353,4]]},"892":{"position":[[1101,3],[1126,3],[1205,4]]},"902":{"position":[[352,3]]},"927":{"position":[[256,3],[686,3]]},"995":{"position":[[331,4]]},"1027":{"position":[[6062,3]]}}}],["add/remov",{"_index":4882,"t":{"834":{"position":[[2975,10]]}}}],["add/rm",{"_index":4206,"t":{"700":{"position":[[1149,6],[2524,6]]}}}],["addit",{"_index":843,"t":{"47":{"position":[[6606,10]]},"257":{"position":[[4391,10]]},"408":{"position":[[1203,10]]},"510":{"position":[[1748,8]]},"558":{"position":[[72,10]]},"566":{"position":[[1431,10]]},"678":{"position":[[1327,10]]},"724":{"position":[[899,10]]},"734":{"position":[[3023,9]]},"742":{"position":[[1252,10]]},"796":{"position":[[2215,10]]},"800":{"position":[[1431,10]]},"808":{"position":[[1431,10]]},"810":{"position":[[73,10],[219,10]]},"852":{"position":[[2130,10]]},"939":{"position":[[39,8]]},"985":{"position":[[926,10]]},"1027":{"position":[[1520,10]]},"1037":{"position":[[422,10]]}}}],["address",{"_index":522,"t":{"31":{"position":[[197,7],[266,9],[1500,8]]},"81":{"position":[[199,8]]},"99":{"position":[[1254,7]]},"135":{"position":[[78,11]]},"195":{"position":[[1622,7]]},"336":{"position":[[537,8]]},"338":{"position":[[582,7],[669,7],[749,7],[894,7],[949,9],[1036,10],[1433,7],[1586,7],[1629,7]]},"340":{"position":[[55,8],[836,8],[883,10]]},"342":{"position":[[299,10],[1219,10]]},"344":{"position":[[311,7],[471,7],[1450,7],[3948,7]]},"346":{"position":[[316,8],[374,10],[645,8]]},"354":{"position":[[1037,7],[1216,7]]},"390":{"position":[[1866,9]]},"654":{"position":[[3616,8]]},"656":{"position":[[604,7],[654,7]]},"666":{"position":[[522,8]]},"804":{"position":[[1453,7]]},"850":{"position":[[1720,7]]},"896":{"position":[[1624,7]]},"898":{"position":[[196,7],[355,7],[1129,7],[1741,7],[1866,7]]},"902":{"position":[[860,7]]},"1027":{"position":[[6875,9],[6942,9]]}}}],["address#http",{"_index":5418,"t":{"1027":{"position":[[6885,21]]}}}],["address=https://effect",{"_index":5416,"t":{"1027":{"position":[[6835,25]]}}}],["adeclar",{"_index":4946,"t":{"854":{"position":[[580,8]]}}}],["adher",{"_index":1876,"t":{"249":{"position":[[429,7]]}}}],["adjac",{"_index":2851,"t":{"442":{"position":[[402,8]]}}}],["admin",{"_index":2517,"t":{"378":{"position":[[296,5],[1548,5],[1982,6],[2298,5]]}}}],["administ",{"_index":4430,"t":{"728":{"position":[[571,10]]}}}],["administr",{"_index":698,"t":{"47":{"position":[[746,14],[5778,15],[7236,14]]},"266":{"position":[[102,14]]},"288":{"position":[[1125,14]]},"636":{"position":[[1851,13]]},"760":{"position":[[2508,14]]},"943":{"position":[[18,14]]}}}],["adopt",{"_index":1291,"t":{"93":{"position":[[1021,8]]}}}],["advanc",{"_index":902,"t":{"47":{"position":[[8523,8]]},"253":{"position":[[3185,8]]},"266":{"position":[[492,9]]},"352":{"position":[[410,8],[2545,8]]},"354":{"position":[[538,8]]},"376":{"position":[[155,9],[189,8],[546,9],[580,8],[1427,9],[1461,8],[1916,9],[1950,8],[2790,9],[2824,8],[3465,8],[4554,8],[4988,9],[5323,9],[5745,9],[5779,9],[5837,8],[6562,8]]},"386":{"position":[[795,8]]},"390":{"position":[[1309,8]]},"424":{"position":[[959,8]]},"474":{"position":[[510,8]]},"476":{"position":[[489,8]]},"546":{"position":[[233,8]]},"608":{"position":[[2533,8]]},"618":{"position":[[93,8]]},"648":{"position":[[1717,8]]},"668":{"position":[[3015,8]]},"674":{"position":[[243,8],[567,8]]},"728":{"position":[[9,8],[149,8]]},"748":{"position":[[871,8]]},"784":{"position":[[118,8]]},"794":{"position":[[221,8]]},"800":{"position":[[1368,9]]},"834":{"position":[[5129,8]]},"836":{"position":[[327,8],[367,8]]},"838":{"position":[[218,8]]},"840":{"position":[[353,8],[952,8]]},"862":{"position":[[4154,9]]},"896":{"position":[[1344,8]]},"910":{"position":[[198,8],[1845,8]]},"939":{"position":[[356,8]]},"947":{"position":[[196,9]]},"953":{"position":[[86,8]]},"1027":{"position":[[5373,8],[9436,8]]}}}],["advent",{"_index":1198,"t":{"91":{"position":[[186,6]]},"646":{"position":[[11,6]]},"975":{"position":[[132,6]]}}}],["advic",{"_index":2281,"t":{"338":{"position":[[124,6]]},"382":{"position":[[300,6]]},"596":{"position":[[1674,6]]}}}],["advis",{"_index":2366,"t":{"352":{"position":[[2982,6]]},"354":{"position":[[677,6]]},"408":{"position":[[371,6]]}}}],["ae",{"_index":2905,"t":{"448":{"position":[[1252,2]]}}}],["affect",{"_index":998,"t":{"65":{"position":[[141,6]]},"77":{"position":[[968,6]]},"89":{"position":[[115,6]]},"296":{"position":[[510,6]]},"350":{"position":[[83,7]]},"354":{"position":[[521,7]]},"434":{"position":[[364,6]]},"538":{"position":[[2875,7]]},"678":{"position":[[608,9]]},"860":{"position":[[737,7]]},"870":{"position":[[119,9]]}}}],["afterward",{"_index":1475,"t":{"141":{"position":[[948,11]]},"257":{"position":[[4298,11]]},"544":{"position":[[1412,11]]},"716":{"position":[[4770,10]]},"772":{"position":[[276,11]]},"1033":{"position":[[3774,10]]}}}],["ag",{"_index":2754,"t":{"408":{"position":[[1116,2]]},"927":{"position":[[417,2]]},"975":{"position":[[415,3]]}}}],["again",{"_index":274,"t":{"21":{"position":[[2091,5],[2101,5]]},"31":{"position":[[552,5],[562,5],[2910,5]]},"45":{"position":[[825,5]]},"47":{"position":[[4346,6],[5558,6]]},"51":{"position":[[427,5],[437,6]]},"71":{"position":[[1801,6]]},"95":{"position":[[2200,6]]},"103":{"position":[[101,6]]},"121":{"position":[[944,6]]},"131":{"position":[[769,6]]},"141":{"position":[[89,6]]},"197":{"position":[[297,6]]},"205":{"position":[[296,5]]},"227":{"position":[[823,6]]},"231":{"position":[[138,5],[1547,6]]},"233":{"position":[[565,5]]},"249":{"position":[[1541,5],[1551,5]]},"262":{"position":[[20,5],[30,6]]},"288":{"position":[[914,5],[1631,6]]},"304":{"position":[[2890,5]]},"316":{"position":[[132,5]]},"324":{"position":[[134,6]]},"344":{"position":[[2775,5]]},"378":{"position":[[49,5],[59,6]]},"420":{"position":[[126,5],[136,6]]},"452":{"position":[[386,6]]},"514":{"position":[[244,5],[254,6]]},"538":{"position":[[2496,6]]},"550":{"position":[[1038,6]]},"594":{"position":[[427,6]]},"596":{"position":[[1645,5]]},"598":{"position":[[159,6]]},"600":{"position":[[479,6]]},"608":{"position":[[2385,6]]},"622":{"position":[[1167,5]]},"626":{"position":[[243,5],[253,6],[7363,6]]},"646":{"position":[[782,6]]},"668":{"position":[[1192,5]]},"682":{"position":[[1192,6]]},"684":{"position":[[2173,6]]},"688":{"position":[[2450,6]]},"696":{"position":[[4884,7]]},"786":{"position":[[140,6]]},"796":{"position":[[916,6]]},"828":{"position":[[857,6]]},"858":{"position":[[2063,5],[2702,5]]},"860":{"position":[[2565,6]]},"868":{"position":[[718,6]]},"894":{"position":[[1382,6]]},"906":{"position":[[918,6]]},"975":{"position":[[47,5]]}}}],["again\"fi",{"_index":5039,"t":{"866":{"position":[[352,8]]}}}],["against",{"_index":2750,"t":{"408":{"position":[[378,7]]},"572":{"position":[[404,7]]},"898":{"position":[[1956,7]]}}}],["ageelast",{"_index":2924,"t":{"450":{"position":[[912,10]]}}}],["agent",{"_index":2728,"t":{"402":{"position":[[789,7],[1149,7]]},"908":{"position":[[1079,5]]},"910":{"position":[[1804,5]]}}}],["aggreg",{"_index":945,"t":{"51":{"position":[[712,10]]}}}],["agn",{"_index":498,"t":{"29":{"position":[[690,5]]}}}],["ago",{"_index":1305,"t":{"95":{"position":[[174,3]]},"179":{"position":[[16,3]]},"422":{"position":[[2136,3]]}}}],["ah",{"_index":1949,"t":{"257":{"position":[[1613,2]]}}}],["aid",{"_index":2069,"t":{"280":{"position":[[226,3]]}}}],["aim",{"_index":450,"t":{"25":{"position":[[1440,6]]},"67":{"position":[[1230,3]]},"798":{"position":[[35,5]]},"947":{"position":[[38,4]]}}}],["al",{"_index":652,"t":{"43":{"position":[[1131,2]]},"133":{"position":[[248,3],[297,2]]},"137":{"position":[[162,2],[211,2]]},"249":{"position":[[982,2],[1002,2]]},"406":{"position":[[797,2]]},"718":{"position":[[545,2],[2005,2]]},"744":{"position":[[2679,2]]},"760":{"position":[[119,4]]},"900":{"position":[[699,2],[1330,2]]}}}],["alcohol",{"_index":4757,"t":{"804":{"position":[[2143,9]]},"806":{"position":[[3199,9]]}}}],["alert",{"_index":1328,"t":{"95":{"position":[[1200,8]]},"734":{"position":[[2738,7]]}}}],["alert='notifi",{"_index":4473,"t":{"734":{"position":[[2814,13]]}}}],["alertalia",{"_index":4472,"t":{"734":{"position":[[2803,10]]}}}],["alex",{"_index":22,"t":{"4":{"position":[[231,4]]}}}],["alf'alia",{"_index":3800,"t":{"624":{"position":[[733,9]]},"734":{"position":[[2693,9]]}}}],["algorithm",{"_index":5243,"t":{"912":{"position":[[591,11]]},"939":{"position":[[210,11]]}}}],["alia",{"_index":364,"t":{"23":{"position":[[1009,5],[1885,5],[1958,5]]},"33":{"position":[[279,5]]},"45":{"position":[[183,6]]},"225":{"position":[[650,5]]},"308":{"position":[[3,5],[128,6],[238,6],[266,5],[471,5],[525,5]]},"326":{"position":[[349,5]]},"328":{"position":[[202,5]]},"540":{"position":[[340,5],[658,5],[838,5],[925,5]]},"624":{"position":[[1432,5]]},"626":{"position":[[4786,5]]},"634":{"position":[[2644,5],[2752,6]]},"660":{"position":[[1012,6],[1044,8],[1221,5],[1268,5],[1332,5]]},"688":{"position":[[829,5],[948,6],[1360,5],[1410,5],[2846,5],[2898,6],[2989,5],[3047,5]]},"692":{"position":[[700,6],[781,5],[823,5]]},"694":{"position":[[672,5],[1157,5],[1298,5],[1345,5],[1967,5],[2014,5],[2063,5]]},"696":{"position":[[1295,5],[1409,6],[1510,6],[1570,6],[1672,6],[1755,6],[2935,5],[3740,5],[3888,5],[4407,5],[5409,6],[5441,8],[5618,5],[5665,5],[5714,5]]},"702":{"position":[[495,6],[527,8],[704,5],[751,5],[800,5]]},"734":{"position":[[2351,5],[2378,6],[2408,6],[2440,5],[2471,5],[2504,5],[2746,5],[2974,5]]},"862":{"position":[[1719,5]]},"902":{"position":[[602,7]]},"908":{"position":[[1819,5],[1886,5]]},"912":{"position":[[259,5]]}}}],["alias",{"_index":1758,"t":{"225":{"position":[[711,7]]},"243":{"position":[[1714,7]]},"288":{"position":[[418,7]]},"298":{"position":[[128,7]]},"308":{"position":[[177,7],[425,7],[569,7]]},"310":{"position":[[510,7]]},"322":{"position":[[97,7]]},"424":{"position":[[1606,7]]},"624":{"position":[[1369,7]]},"626":{"position":[[176,8],[287,7],[625,8],[696,7]]},"634":{"position":[[1433,7]]},"660":{"position":[[1143,8],[1189,8],[1254,9]]},"688":{"position":[[415,7],[499,10],[1810,7],[2414,7],[2498,10]]},"690":{"position":[[161,7],[728,7],[867,7]]},"692":{"position":[[310,9],[441,8]]},"694":{"position":[[855,7],[1284,9],[1889,8],[1935,8],[2000,9]]},"696":{"position":[[4337,8],[5540,8],[5586,8],[5651,9]]},"702":{"position":[[626,8],[672,8],[737,9]]},"724":{"position":[[387,8]]},"734":{"position":[[2574,7],[3234,7]]},"736":{"position":[[859,7]]},"738":{"position":[[1937,7]]},"864":{"position":[[744,7]]}}}],["aliases63ea74f",{"_index":4174,"t":{"694":{"position":[[1227,14]]}}}],["aliasesalia",{"_index":3798,"t":{"624":{"position":[[712,12]]},"734":{"position":[[2672,12]]}}}],["aliasesb9ae0ad",{"_index":4175,"t":{"694":{"position":[[1269,14]]}}}],["aliasesif",{"_index":4461,"t":{"734":{"position":[[2223,9]]}}}],["aliasesnoth",{"_index":4126,"t":{"688":{"position":[[530,14]]}}}],["aliasesswitch",{"_index":4125,"t":{"688":{"position":[[467,15],[2472,15]]}}}],["aliasesuntrack",{"_index":4129,"t":{"688":{"position":[[1021,16]]}}}],["aliasesupd",{"_index":4138,"t":{"690":{"position":[[330,15]]}}}],["alic",{"_index":5081,"t":{"888":{"position":[[120,7],[245,5],[593,7]]},"890":{"position":[[35,5],[121,5],[329,6],[519,5],[538,5],[569,5],[901,5],[959,5],[1069,6],[1076,5],[1304,5],[1505,5]]}}}],["alice'",{"_index":5087,"t":{"890":{"position":[[280,7],[364,7]]}}}],["alicloud",{"_index":2385,"t":{"364":{"position":[[239,9],[919,9]]},"366":{"position":[[541,9],[1468,9],[1549,9],[1881,9],[3585,9]]},"370":{"position":[[543,9]]},"372":{"position":[[418,9]]}}}],["aliyun/config.json",{"_index":2391,"t":{"364":{"position":[[299,21],[979,21]]},"366":{"position":[[607,21],[1944,21]]},"368":{"position":[[140,21],[901,21]]},"370":{"position":[[603,21]]},"372":{"position":[[484,21]]}}}],["alloc",{"_index":658,"t":{"45":{"position":[[190,6]]},"278":{"position":[[125,8]]}}}],["allow",{"_index":287,"t":{"21":{"position":[[2335,6]]},"69":{"position":[[5911,6]]},"77":{"position":[[2425,5]]},"83":{"position":[[364,5]]},"179":{"position":[[425,5]]},"185":{"position":[[712,6]]},"191":{"position":[[144,5]]},"221":{"position":[[163,6]]},"239":{"position":[[515,5]]},"241":{"position":[[787,9]]},"278":{"position":[[637,6],[749,7]]},"280":{"position":[[87,6]]},"282":{"position":[[1835,6]]},"284":{"position":[[1560,8]]},"286":{"position":[[694,5]]},"290":{"position":[[2630,8]]},"300":{"position":[[884,6]]},"338":{"position":[[1542,5]]},"342":{"position":[[207,6],[443,6]]},"344":{"position":[[170,9],[3287,6]]},"346":{"position":[[969,6]]},"352":{"position":[[1580,5]]},"354":{"position":[[612,8]]},"362":{"position":[[171,5]]},"366":{"position":[[4566,6]]},"378":{"position":[[3786,6],[4530,8],[5169,5]]},"380":{"position":[[68,6],[125,6]]},"386":{"position":[[832,8]]},"390":{"position":[[1702,5]]},"392":{"position":[[615,5]]},"396":{"position":[[582,5],[1543,5]]},"426":{"position":[[118,6]]},"432":{"position":[[1614,5]]},"458":{"position":[[1170,6]]},"466":{"position":[[1166,7]]},"474":{"position":[[62,6],[567,5]]},"492":{"position":[[186,6]]},"498":{"position":[[70,6]]},"500":{"position":[[75,5]]},"506":{"position":[[2163,5]]},"508":{"position":[[149,8]]},"522":{"position":[[758,6]]},"536":{"position":[[1097,5]]},"538":{"position":[[2354,6]]},"570":{"position":[[53,6]]},"576":{"position":[[86,6]]},"626":{"position":[[1946,6],[2426,6]]},"640":{"position":[[1485,6]]},"648":{"position":[[1395,7]]},"654":{"position":[[541,5]]},"662":{"position":[[172,5]]},"678":{"position":[[271,6]]},"688":{"position":[[131,5]]},"698":{"position":[[327,5]]},"704":{"position":[[342,5]]},"714":{"position":[[42,6]]},"720":{"position":[[255,5]]},"722":{"position":[[1100,6]]},"734":{"position":[[3653,6]]},"738":{"position":[[4229,5],[5136,5],[5781,6],[6189,5]]},"744":{"position":[[51,5]]},"748":{"position":[[706,6]]},"754":{"position":[[1376,5],[1475,5],[2198,8]]},"796":{"position":[[2145,6]]},"798":{"position":[[348,6]]},"800":{"position":[[880,5]]},"816":{"position":[[46,6],[326,5],[414,8],[1508,8]]},"818":{"position":[[516,8],[1254,5],[1319,8]]},"830":{"position":[[513,6]]},"834":{"position":[[2875,5],[4410,8]]},"838":{"position":[[1390,6]]},"860":{"position":[[454,8]]},"868":{"position":[[46,6]]},"870":{"position":[[44,6]]},"900":{"position":[[1116,7],[1186,7]]},"939":{"position":[[407,5]]},"979":{"position":[[154,5]]},"1013":{"position":[[154,5]]},"1027":{"position":[[791,6]]}}}],["along",{"_index":527,"t":{"31":{"position":[[448,5]]},"73":{"position":[[130,5]]},"488":{"position":[[1099,5]]},"656":{"position":[[589,5]]},"714":{"position":[[3780,5]]},"762":{"position":[[144,6]]},"834":{"position":[[1126,6]]}}}],["alongsid",{"_index":1213,"t":{"91":{"position":[[1002,9]]}}}],["alphabet",{"_index":1869,"t":{"247":{"position":[[266,10]]},"448":{"position":[[2012,10]]},"458":{"position":[[1463,15]]},"826":{"position":[[625,9]]}}}],["alpin",{"_index":1737,"t":{"221":{"position":[[402,6]]}}}],["alreadi",{"_index":191,"t":{"21":{"position":[[78,7],[1853,7],[2874,7]]},"47":{"position":[[6929,7]]},"107":{"position":[[483,7],[1244,7]]},"111":{"position":[[113,7]]},"115":{"position":[[806,7]]},"123":{"position":[[77,7]]},"249":{"position":[[2060,7]]},"253":{"position":[[956,7]]},"255":{"position":[[1523,7],[1989,7]]},"257":{"position":[[3479,7],[6718,7]]},"272":{"position":[[65,7]]},"296":{"position":[[180,7]]},"302":{"position":[[1258,7]]},"308":{"position":[[169,7]]},"328":{"position":[[87,7]]},"376":{"position":[[3338,7]]},"378":{"position":[[3101,7]]},"416":{"position":[[175,8]]},"420":{"position":[[1030,7]]},"480":{"position":[[102,7]]},"500":{"position":[[1004,7]]},"512":{"position":[[1683,7]]},"530":{"position":[[628,7]]},"540":{"position":[[321,7],[673,7]]},"552":{"position":[[283,7]]},"570":{"position":[[752,7]]},"574":{"position":[[3565,7]]},"592":{"position":[[86,7]]},"608":{"position":[[2343,7],[2442,7]]},"626":{"position":[[3144,8]]},"654":{"position":[[1324,7],[1701,7],[3781,7]]},"656":{"position":[[6,7]]},"660":{"position":[[120,7]]},"688":{"position":[[1648,7]]},"702":{"position":[[6,7]]},"728":{"position":[[276,7]]},"742":{"position":[[636,7]]},"754":{"position":[[1233,7]]},"796":{"position":[[1861,7]]},"822":{"position":[[9,7]]},"854":{"position":[[1049,7]]},"894":{"position":[[193,7]]},"904":{"position":[[1307,7]]},"1037":{"position":[[222,7]]}}}],["alt",{"_index":304,"t":{"21":{"position":[[2800,3]]},"155":{"position":[[96,3],[123,3]]},"159":{"position":[[102,3]]},"183":{"position":[[114,3]]}}}],["alter",{"_index":2452,"t":{"372":{"position":[[1369,5]]},"772":{"position":[[645,5]]}}}],["altern",{"_index":595,"t":{"37":{"position":[[919,11]]},"43":{"position":[[1270,11]]},"77":{"position":[[4754,11]]},"237":{"position":[[1434,13]]},"241":{"position":[[334,11]]},"243":{"position":[[1152,11]]},"386":{"position":[[1098,11]]},"390":{"position":[[867,11]]},"408":{"position":[[139,12],[187,12],[392,12],[463,11],[894,13]]},"492":{"position":[[897,11]]},"510":{"position":[[1242,11]]},"522":{"position":[[274,11]]},"716":{"position":[[1592,11]]},"738":{"position":[[2945,11]]},"794":{"position":[[1187,11]]},"796":{"position":[[129,11],[1940,12]]},"810":{"position":[[1587,11]]},"812":{"position":[[29,12]]},"896":{"position":[[554,13]]},"1027":{"position":[[3100,9],[3266,9],[9345,11]]},"1029":{"position":[[713,11],[785,11]]},"1037":{"position":[[716,12]]}}}],["although",{"_index":1668,"t":{"211":{"position":[[505,9]]},"260":{"position":[[745,8],[1052,9]]},"262":{"position":[[3359,8]]},"434":{"position":[[874,8]]},"534":{"position":[[0,8]]},"674":{"position":[[311,8]]},"766":{"position":[[1406,8]]},"850":{"position":[[1248,8]]},"878":{"position":[[1918,8]]}}}],["alway",{"_index":959,"t":{"53":{"position":[[466,6]]},"71":{"position":[[525,6]]},"91":{"position":[[3126,6]]},"105":{"position":[[577,6]]},"129":{"position":[[318,6],[424,6]]},"135":{"position":[[401,6]]},"139":{"position":[[231,6],[584,6],[1059,6]]},"209":{"position":[[154,6]]},"231":{"position":[[1317,6]]},"243":{"position":[[1051,6]]},"257":{"position":[[1779,6]]},"262":{"position":[[772,6],[932,6],[1091,6],[3368,6]]},"288":{"position":[[1456,6]]},"308":{"position":[[349,6]]},"348":{"position":[[512,6]]},"366":{"position":[[4661,6]]},"404":{"position":[[365,6]]},"406":{"position":[[1099,6]]},"464":{"position":[[2158,6]]},"484":{"position":[[644,6]]},"536":{"position":[[1452,6]]},"538":{"position":[[3320,6],[3772,6]]},"558":{"position":[[2392,6]]},"566":{"position":[[595,6]]},"598":{"position":[[237,6]]},"604":{"position":[[1562,6]]},"626":{"position":[[858,6],[5116,6]]},"640":{"position":[[980,6]]},"648":{"position":[[2474,6]]},"670":{"position":[[636,6]]},"678":{"position":[[777,6]]},"688":{"position":[[2618,6]]},"720":{"position":[[7952,6]]},"754":{"position":[[554,6]]},"804":{"position":[[2585,6]]},"834":{"position":[[3089,6]]},"864":{"position":[[179,6]]},"1035":{"position":[[2723,6]]}}}],["am/pm",{"_index":4244,"t":{"714":{"position":[[958,5],[2689,5]]}}}],["amaz",{"_index":2767,"t":{"422":{"position":[[471,7]]},"804":{"position":[[606,7]]},"876":{"position":[[13,8]]},"935":{"position":[[260,8]]},"945":{"position":[[123,7]]},"959":{"position":[[61,6]]}}}],["amazingli",{"_index":4060,"t":{"670":{"position":[[72,9]]}}}],["amazon",{"_index":2473,"t":{"376":{"position":[[690,6]]},"892":{"position":[[171,6],[2463,6]]},"894":{"position":[[105,6],[633,6]]},"896":{"position":[[878,6],[932,6]]},"898":{"position":[[2291,6]]},"908":{"position":[[2090,6]]}}}],["amber",{"_index":4037,"t":{"668":{"position":[[986,6]]}}}],["ambigu",{"_index":766,"t":{"47":{"position":[[3732,9]]},"175":{"position":[[2406,9]]},"428":{"position":[[1310,9]]},"488":{"position":[[832,9]]},"808":{"position":[[1958,10]]},"878":{"position":[[787,10],[1198,9]]}}}],["american",{"_index":4264,"t":{"716":{"position":[[593,8]]}}}],["ami",{"_index":5130,"t":{"896":{"position":[[899,5],[947,3]]},"898":{"position":[[2306,3]]},"908":{"position":[[2105,3]]}}}],["amongst",{"_index":1176,"t":{"87":{"position":[[206,7]]},"336":{"position":[[114,7]]},"392":{"position":[[84,7]]}}}],["amount",{"_index":1260,"t":{"91":{"position":[[2559,6]]},"113":{"position":[[273,6]]},"376":{"position":[[6343,6]]},"450":{"position":[[5202,6]]},"770":{"position":[[1398,7]]}}}],["ampersand",{"_index":1783,"t":{"229":{"position":[[127,10],[301,9],[770,9]]},"257":{"position":[[4016,9],[4238,9],[4402,9]]}}}],["anchor",{"_index":2337,"t":{"346":{"position":[[487,8],[496,7],[698,6],[732,7],[1050,8],[1420,7]]}}}],["and/or",{"_index":4193,"t":{"696":{"position":[[2581,6]]},"700":{"position":[[2806,6]]}}}],["anethol",{"_index":4763,"t":{"804":{"position":[[2241,9]]}}}],["angl",{"_index":2343,"t":{"350":{"position":[[585,6],[645,6],[751,6]]},"760":{"position":[[1940,6]]}}}],["anim",{"_index":4832,"t":{"808":{"position":[[1340,7]]}}}],["annot",{"_index":5344,"t":{"987":{"position":[[109,10],[685,10]]}}}],["annotatedcommand",{"_index":5343,"t":{"987":{"position":[[4,16],[288,16],[430,17],[531,17]]}}}],["annotation=\"go",{"_index":5348,"t":{"987":{"position":[[448,14],[549,14]]}}}],["annoy",{"_index":1641,"t":{"203":{"position":[[459,8]]},"678":{"position":[[739,8]]},"906":{"position":[[331,8]]}}}],["anoth",{"_index":481,"t":{"29":{"position":[[169,7]]},"47":{"position":[[1619,7],[5907,7]]},"67":{"position":[[1352,7]]},"77":{"position":[[1843,7]]},"81":{"position":[[522,7]]},"87":{"position":[[316,8]]},"91":{"position":[[2940,8],[2980,8]]},"95":{"position":[[405,7]]},"141":{"position":[[67,8]]},"175":{"position":[[1745,7],[1987,7]]},"243":{"position":[[1590,7],[1694,7]]},"251":{"position":[[1212,8]]},"255":{"position":[[2254,7]]},"257":{"position":[[1937,7],[1965,7]]},"259":{"position":[[851,7]]},"262":{"position":[[1409,7],[1849,7],[3105,7]]},"282":{"position":[[345,7],[1516,7]]},"288":{"position":[[274,7]]},"302":{"position":[[1180,7]]},"352":{"position":[[1666,7],[1768,7],[1860,7],[2004,7]]},"366":{"position":[[1165,7]]},"376":{"position":[[14,7]]},"378":{"position":[[7,7]]},"382":{"position":[[9,7]]},"384":{"position":[[176,7]]},"408":{"position":[[478,7]]},"428":{"position":[[180,7],[1265,7]]},"432":{"position":[[1468,7]]},"446":{"position":[[2989,7],[3204,7]]},"450":{"position":[[2958,7]]},"458":{"position":[[603,7]]},"468":{"position":[[246,7]]},"484":{"position":[[198,7],[238,7],[1399,7],[1665,7]]},"508":{"position":[[1744,7]]},"510":{"position":[[438,7],[611,7],[1000,7],[1185,7]]},"522":{"position":[[573,7]]},"532":{"position":[[366,7]]},"538":{"position":[[1789,7],[3674,7]]},"554":{"position":[[303,7]]},"574":{"position":[[3086,8]]},"576":{"position":[[162,7]]},"592":{"position":[[0,7]]},"626":{"position":[[4011,7],[4077,7]]},"634":{"position":[[2144,7],[2713,7]]},"648":{"position":[[1495,7],[1755,7]]},"654":{"position":[[246,7]]},"656":{"position":[[308,7]]},"662":{"position":[[276,7]]},"664":{"position":[[1209,8]]},"672":{"position":[[514,8]]},"688":{"position":[[628,7],[2838,7],[2875,7]]},"690":{"position":[[118,8]]},"692":{"position":[[327,7],[665,7]]},"696":{"position":[[362,7],[515,7],[692,7],[959,7],[1562,7]]},"698":{"position":[[511,7]]},"700":{"position":[[986,7],[1995,7]]},"738":{"position":[[4183,7]]},"760":{"position":[[2562,7]]},"778":{"position":[[1179,7]]},"790":{"position":[[269,7],[334,7]]},"794":{"position":[[902,7]]},"816":{"position":[[1239,7]]},"828":{"position":[[796,7]]},"832":{"position":[[1258,7],[1311,7]]},"838":{"position":[[131,7],[204,7],[988,7]]},"842":{"position":[[1073,7],[1126,7]]},"878":{"position":[[1134,7]]},"888":{"position":[[155,7]]},"896":{"position":[[1857,7]]},"898":{"position":[[2021,7]]},"910":{"position":[[1311,7]]},"961":{"position":[[0,7]]},"975":{"position":[[504,7]]}}}],["ansi",{"_index":3250,"t":{"498":{"position":[[48,5],[126,4]]},"588":{"position":[[2455,5]]},"616":{"position":[[1001,4]]},"716":{"position":[[559,4],[577,4],[1043,4],[1290,4],[4436,4]]},"720":{"position":[[5224,4]]},"806":{"position":[[2759,4]]},"862":{"position":[[686,4],[952,5],[3594,4],[4012,4]]}}}],["answer",{"_index":226,"t":{"21":{"position":[[744,6]]},"51":{"position":[[1034,7]]},"91":{"position":[[1979,6]]},"217":{"position":[[681,6]]},"260":{"position":[[1125,6]]}}}],["anti",{"_index":5055,"t":{"872":{"position":[[0,4]]},"876":{"position":[[623,4]]},"878":{"position":[[1511,4]]}}}],["anyon",{"_index":1169,"t":{"83":{"position":[[80,6]]},"249":{"position":[[453,6]]},"282":{"position":[[2302,6]]},"288":{"position":[[595,6]]},"396":{"position":[[853,6]]},"426":{"position":[[424,6]]},"588":{"position":[[1151,6]]},"654":{"position":[[601,7]]},"662":{"position":[[617,6]]},"808":{"position":[[95,6]]},"844":{"position":[[135,7]]},"890":{"position":[[1131,6],[1435,6]]},"892":{"position":[[2015,6]]},"896":{"position":[[1603,6]]},"943":{"position":[[419,6]]}}}],["anyth",{"_index":332,"t":{"21":{"position":[[3960,9]]},"69":{"position":[[15,8]]},"121":{"position":[[382,9]]},"195":{"position":[[117,8],[2133,8]]},"203":{"position":[[651,8]]},"219":{"position":[[754,8]]},"253":{"position":[[294,8],[316,8]]},"255":{"position":[[1514,8]]},"292":{"position":[[1337,9]]},"330":{"position":[[18,8]]},"342":{"position":[[355,8]]},"352":{"position":[[1176,8]]},"376":{"position":[[1661,9],[6643,9],[6678,9]]},"380":{"position":[[428,8]]},"402":{"position":[[1287,8]]},"404":{"position":[[805,8],[851,8]]},"406":{"position":[[329,9]]},"422":{"position":[[358,8]]},"464":{"position":[[2077,8]]},"508":{"position":[[5010,8]]},"524":{"position":[[1013,8]]},"586":{"position":[[808,8]]},"626":{"position":[[6451,8]]},"634":{"position":[[1359,8]]},"666":{"position":[[1288,8]]},"668":{"position":[[938,8],[1482,8],[2648,9]]},"684":{"position":[[1143,8]]},"718":{"position":[[89,9]]},"896":{"position":[[1534,8]]},"914":{"position":[[386,8]]}}}],["anythingcas",{"_index":3789,"t":{"624":{"position":[[360,12]]},"634":{"position":[[1616,12]]}}}],["anyway",{"_index":2479,"t":{"376":{"position":[[1844,7]]}}}],["anywher",{"_index":1465,"t":{"139":{"position":[[566,9]]},"195":{"position":[[1574,8]]},"257":{"position":[[5230,8]]},"304":{"position":[[126,8]]},"662":{"position":[[750,9]]},"738":{"position":[[5371,8]]},"796":{"position":[[306,8]]}}}],["ap",{"_index":3048,"t":{"454":{"position":[[724,4]]}}}],["apart",{"_index":2073,"t":{"280":{"position":[[1167,6]]}}}],["api",{"_index":2119,"t":{"290":{"position":[[1550,4]]},"800":{"position":[[196,4]]}}}],["apivers",{"_index":2512,"t":{"378":{"position":[[197,11]]}}}],["apm",{"_index":1617,"t":{"195":{"position":[[1202,3],[1227,4],[1270,3],[1372,3],[1436,5],[1519,3],[1551,3]]},"197":{"position":[[113,5],[385,4],[490,3]]},"450":{"position":[[955,3],[1585,3],[2060,3],[2569,3]]}}}],["apm00.logs\"./logs/apm",{"_index":1615,"t":{"195":{"position":[[1084,22]]}}}],["app",{"_index":1078,"t":{"69":{"position":[[6282,3],[6356,4],[6515,3]]},"71":{"position":[[738,3]]},"282":{"position":[[2451,3]]},"951":{"position":[[410,4]]}}}],["app?...nkubectl",{"_index":3157,"t":{"470":{"position":[[713,15]]}}}],["appear",{"_index":3357,"t":{"514":{"position":[[237,6]]},"979":{"position":[[54,7]]},"1013":{"position":[[54,7]]}}}],["append",{"_index":1925,"t":{"255":{"position":[[1544,6],[1638,6],[1916,9],[2005,9]]},"257":{"position":[[5576,6],[5588,6],[5658,9],[5774,6]]},"262":{"position":[[1679,6],[1966,6]]},"380":{"position":[[292,9],[421,6]]},"386":{"position":[[670,6]]},"490":{"position":[[1284,6]]},"624":{"position":[[528,6]]},"626":{"position":[[2899,6]]},"722":{"position":[[2729,6]]},"734":{"position":[[475,6],[752,9]]},"738":{"position":[[7203,6]]},"770":{"position":[[420,6],[452,6]]}}}],["append.safe_set",{"_index":4517,"t":{"738":{"position":[[6550,15]]}}}],["appendix",{"_index":2785,"t":{"424":{"position":[[1080,8]]},"502":{"position":[[1494,8]]},"584":{"position":[[755,8]]},"624":{"position":[[168,8]]},"636":{"position":[[1687,8]]},"708":{"position":[[448,8]]},"918":{"position":[[6,9]]}}}],["appl",{"_index":3234,"t":{"490":{"position":[[1410,7]]},"722":{"position":[[1202,6]]},"862":{"position":[[315,8],[355,7]]},"868":{"position":[[265,5]]},"1027":{"position":[[668,7]]}}}],["apple'",{"_index":1002,"t":{"67":{"position":[[143,7]]}}}],["apple2",{"_index":5045,"t":{"868":{"position":[[421,7]]}}}],["apples2",{"_index":4415,"t":{"722":{"position":[[1241,8]]}}}],["applethi",{"_index":5050,"t":{"868":{"position":[[468,9]]}}}],["appli",{"_index":565,"t":{"31":{"position":[[3125,5]]},"67":{"position":[[643,5]]},"129":{"position":[[407,7]]},"185":{"position":[[60,5]]},"332":{"position":[[886,5]]},"360":{"position":[[280,8]]},"364":{"position":[[1524,7],[1612,5]]},"366":{"position":[[2530,5],[3405,5]]},"368":{"position":[[1278,7]]},"370":{"position":[[312,5]]},"372":{"position":[[82,5],[1260,7]]},"378":{"position":[[1276,5],[3249,5]]},"386":{"position":[[394,5]]},"392":{"position":[[312,7]]},"506":{"position":[[2203,5]]},"542":{"position":[[44,7]]},"608":{"position":[[1699,8],[1946,7],[2356,7],[2377,7]]},"690":{"position":[[750,7]]},"714":{"position":[[2042,5]]},"732":{"position":[[1026,7]]},"772":{"position":[[42,7],[1270,7]]},"912":{"position":[[557,8]]},"939":{"position":[[177,7]]},"985":{"position":[[951,6]]},"1027":{"position":[[4519,8]]},"1033":{"position":[[337,7],[360,7],[1243,5]]},"1035":{"position":[[170,7]]}}}],["applic",{"_index":1074,"t":{"69":{"position":[[5388,12],[5469,12]]},"77":{"position":[[2532,13],[2556,12],[2618,12],[2681,12],[2787,12],[2847,12],[2913,14],[3006,12],[3101,12],[3181,12]]},"81":{"position":[[928,11]]},"93":{"position":[[356,11]]},"332":{"position":[[714,10]]},"454":{"position":[[1919,12],[2138,12]]}}}],["apppod/nginxpod/postgr",{"_index":3152,"t":{"470":{"position":[[463,24]]}}}],["approach",{"_index":2641,"t":{"396":{"position":[[1519,8]]},"428":{"position":[[904,8]]}}}],["appropri",{"_index":243,"t":{"21":{"position":[[1198,11]]},"43":{"position":[[126,11]]},"47":{"position":[[7700,11]]},"73":{"position":[[96,11]]},"574":{"position":[[251,14]]},"604":{"position":[[940,12]]},"608":{"position":[[2571,11]]},"640":{"position":[[1199,11]]},"746":{"position":[[195,11]]},"830":{"position":[[156,11]]}}}],["appropriately1",{"_index":4460,"t":{"734":{"position":[[2159,15]]}}}],["apr",{"_index":5165,"t":{"900":{"position":[[735,3],[1366,3]]},"908":{"position":[[2015,3]]}}}],["apropo",{"_index":904,"t":{"47":{"position":[[8624,7]]}}}],["apt",{"_index":386,"t":{"23":{"position":[[1678,3]]},"107":{"position":[[569,3]]},"438":{"position":[[1557,3]]},"648":{"position":[[2058,3]]},"822":{"position":[[436,3]]}}}],["apvx",{"_index":608,"t":{"41":{"position":[[189,5],[255,5]]}}}],["arbitrari",{"_index":1673,"t":{"213":{"position":[[43,9],[982,9]]},"257":{"position":[[4562,9]]},"336":{"position":[[481,9]]},"432":{"position":[[783,9]]},"664":{"position":[[2081,9]]},"1027":{"position":[[9910,9],[10265,9]]}}}],["archaic",{"_index":2015,"t":{"266":{"position":[[760,7]]}}}],["archiv",{"_index":1364,"t":{"99":{"position":[[2110,8]]},"103":{"position":[[434,8]]},"1027":{"position":[[8297,7],[8309,16],[8370,7]]},"1029":{"position":[[691,9]]}}}],["archive=effect",{"_index":5427,"t":{"1027":{"position":[[8253,17]]}}}],["archives=$(find",{"_index":5440,"t":{"1029":{"position":[[555,15]]}}}],["archives=`find",{"_index":5442,"t":{"1029":{"position":[[894,14]]}}}],["area",{"_index":1289,"t":{"93":{"position":[[997,4]]},"99":{"position":[[94,4]]},"276":{"position":[[1150,4]]},"624":{"position":[[312,6]]},"672":{"position":[[290,4]]},"682":{"position":[[1528,5],[1797,4],[2302,5]]},"804":{"position":[[2522,4]]},"806":{"position":[[3462,4]]}}}],["arg",{"_index":2199,"t":{"304":{"position":[[2302,5]]}}}],["argpars",{"_index":4792,"t":{"806":{"position":[[651,8],[1128,8],[3565,8]]},"810":{"position":[[634,8]]}}}],["argparse.argumentparser()parser.add_argu",{"_index":4795,"t":{"806":{"position":[[883,45]]}}}],["argparsepars",{"_index":4794,"t":{"806":{"position":[[866,14]]}}}],["args.crop",{"_index":4806,"t":{"806":{"position":[[1765,10],[1856,10],[2044,10]]}}}],["argument",{"_index":1402,"t":{"113":{"position":[[605,9]]},"253":{"position":[[1512,8]]},"304":{"position":[[2529,11],[2675,9]]},"326":{"position":[[110,10],[163,9],[244,9]]},"462":{"position":[[162,10],[2176,9],[2732,9]]},"466":{"position":[[66,9],[408,9],[779,9],[902,10],[971,9]]},"468":{"position":[[65,9],[145,9],[898,10],[1031,10],[1342,9],[1675,10],[2261,9]]},"472":{"position":[[515,8]]},"806":{"position":[[685,9],[757,9],[1301,8],[1737,8],[2650,8],[3780,10]]},"808":{"position":[[1586,9]]},"860":{"position":[[38,10],[72,9],[1484,9],[1606,9],[1676,9],[1849,8],[2023,8]]},"862":{"position":[[547,10]]},"874":{"position":[[156,9]]}}}],["arithmet",{"_index":2139,"t":{"292":{"position":[[601,10]]},"502":{"position":[[948,10]]},"510":{"position":[[825,10],[1342,10],[1625,10],[2401,10],[2460,10],[2528,11],[2955,10]]},"514":{"position":[[156,10],[638,10]]},"590":{"position":[[280,10]]},"608":{"position":[[2100,10]]},"806":{"position":[[2627,10]]},"918":{"position":[[361,11],[396,10]]},"1019":{"position":[[1010,10]]},"1027":{"position":[[216,10]]},"1031":{"position":[[72,10],[139,10],[235,10]]},"1033":{"position":[[199,10]]}}}],["around",{"_index":223,"t":{"21":{"position":[[703,6],[1509,6],[2954,6]]},"33":{"position":[[252,6]]},"81":{"position":[[61,7],[1704,7]]},"93":{"position":[[1122,6],[1749,6]]},"105":{"position":[[976,6]]},"133":{"position":[[1090,6]]},"139":{"position":[[470,6]]},"151":{"position":[[244,6],[365,6]]},"185":{"position":[[587,7],[1101,6]]},"237":{"position":[[722,7],[1241,6]]},"257":{"position":[[3877,6]]},"282":{"position":[[2406,7]]},"288":{"position":[[949,6]]},"376":{"position":[[5207,6]]},"408":{"position":[[47,6]]},"410":{"position":[[443,6]]},"464":{"position":[[842,6]]},"488":{"position":[[25,6],[567,6]]},"490":{"position":[[1564,6]]},"502":{"position":[[28,6],[446,6]]},"514":{"position":[[1056,6]]},"528":{"position":[[811,6]]},"588":{"position":[[1422,7]]},"616":{"position":[[503,6]]},"678":{"position":[[216,6],[879,6]]},"702":{"position":[[1758,7]]},"718":{"position":[[890,6]]},"724":{"position":[[1184,6]]},"760":{"position":[[2475,7]]},"766":{"position":[[132,6],[1498,6]]},"770":{"position":[[267,6],[1529,6]]},"774":{"position":[[545,6]]},"778":{"position":[[1637,6]]},"834":{"position":[[4857,7]]},"870":{"position":[[272,6]]},"876":{"position":[[55,7]]},"906":{"position":[[1042,6]]},"939":{"position":[[291,6]]},"1037":{"position":[[504,6]]}}}],["arr",{"_index":3224,"t":{"490":{"position":[[1158,7],[1202,10]]}}}],["arr[3]=\"appl",{"_index":3225,"t":{"490":{"position":[[1166,15]]}}}],["arr[5]=\"pear",{"_index":3226,"t":{"490":{"position":[[1182,14]]}}}],["arrang",{"_index":4847,"t":{"818":{"position":[[343,7]]}}}],["array",{"_index":3210,"t":{"490":{"position":[[0,6],[56,5],[116,5],[259,5],[430,6],[466,6],[724,7],[799,5],[805,8],[896,5],[980,11],[1080,5],[1131,5],[1145,12],[1230,5],[1243,12],[1294,5],[1576,5],[1642,5],[1864,6],[1939,6],[1946,6],[2025,5],[2069,6]]},"492":{"position":[[64,7],[82,6],[257,5],[340,5],[662,7]]},"496":{"position":[[489,6]]},"506":{"position":[[2040,5],[2064,12],[2101,6]]},"526":{"position":[[714,5],[740,5],[851,5],[1845,5],[2093,6]]},"568":{"position":[[1219,5],[1257,5],[1313,5],[1472,6]]},"582":{"position":[[79,7],[130,5],[514,6],[612,7]]},"584":{"position":[[1824,6]]},"594":{"position":[[540,5],[612,5],[760,6],[899,6]]},"614":{"position":[[108,6]]},"738":{"position":[[3044,5],[3477,5]]},"854":{"position":[[267,5],[535,6]]},"860":{"position":[[2506,6]]},"1027":{"position":[[4016,7],[4248,7],[4277,5],[4311,5],[4388,6],[4428,5],[5466,5],[5544,11],[5611,6],[5839,5],[6049,5],[6092,13],[6223,7],[6291,6],[6434,6],[6543,5],[6601,5],[9002,6]]}}}],["array+=(val1",{"_index":3227,"t":{"490":{"position":[[1300,12]]}}}],["array.book[title]=\"effect",{"_index":3242,"t":{"492":{"position":[[400,28]]}}}],["array.echo",{"_index":3713,"t":{"594":{"position":[[1002,10]]}}}],["array[@]:start:numb",{"_index":3236,"t":{"490":{"position":[[1457,24]]}}}],["array[index",{"_index":3219,"t":{"490":{"position":[[910,15]]}}}],["array[index]=valu",{"_index":3222,"t":{"490":{"position":[[1094,18]]}}}],["arrow",{"_index":624,"t":{"41":{"position":[[848,5]]},"43":{"position":[[88,5],[570,5]]},"151":{"position":[[319,5]]},"163":{"position":[[128,5]]},"165":{"position":[[828,5]]},"171":{"position":[[98,5]]},"766":{"position":[[1479,5]]},"770":{"position":[[1510,5]]},"828":{"position":[[532,5]]},"830":{"position":[[770,5]]},"834":{"position":[[4838,5]]},"842":{"position":[[1747,7],[1795,5]]}}}],["art",{"_index":4768,"t":{"804":{"position":[[2344,3]]},"806":{"position":[[3297,3]]}}}],["arti",{"_index":532,"t":{"31":{"position":[[615,5]]}}}],["articl",{"_index":1383,"t":{"107":{"position":[[173,8]]},"207":{"position":[[864,9]]},"282":{"position":[[2589,8]]},"286":{"position":[[373,8]]},"288":{"position":[[740,7],[1000,9]]},"290":{"position":[[316,8],[1945,7]]},"292":{"position":[[20,8],[2074,7]]},"662":{"position":[[970,8]]},"670":{"position":[[3122,8]]},"686":{"position":[[557,8]]},"698":{"position":[[715,8]]},"912":{"position":[[839,8]]},"965":{"position":[[97,7]]},"967":{"position":[[14,8]]},"975":{"position":[[427,7]]}}}],["artie_ziff@simpsons.com",{"_index":523,"t":{"31":{"position":[[210,25]]}}}],["ascend",{"_index":764,"t":{"47":{"position":[[3572,9]]}}}],["ascii",{"_index":583,"t":{"37":{"position":[[380,5]]},"47":{"position":[[6660,5],[6732,5],[6772,5]]},"474":{"position":[[613,5],[851,5],[872,7]]},"714":{"position":[[479,5]]},"975":{"position":[[441,5]]},"989":{"position":[[177,5]]}}}],["ascii(7",{"_index":845,"t":{"47":{"position":[[6673,8]]}}}],["ascii(7)nam",{"_index":846,"t":{"47":{"position":[[6719,12]]}}}],["asciinema",{"_index":5319,"t":{"985":{"position":[[44,9],[681,9]]},"995":{"position":[[4,9],[175,10],[424,9]]},"997":{"position":[[194,9],[210,9]]}}}],["asciinemaplay",{"_index":5318,"t":{"985":{"position":[[4,15],[234,15],[373,16],[494,16]]}}}],["asid",{"_index":1360,"t":{"99":{"position":[[1414,6]]},"249":{"position":[[1488,5]]},"292":{"position":[[1588,6]]}}}],["ask",{"_index":956,"t":{"53":{"position":[[403,3]]},"69":{"position":[[1491,3],[1809,3],[3459,5],[3988,5],[4234,3]]},"101":{"position":[[55,3]]},"107":{"position":[[1050,3]]},"125":{"position":[[233,3]]},"175":{"position":[[2147,6]]},"211":{"position":[[249,3],[712,3]]},"215":{"position":[[92,4],[632,5]]},"290":{"position":[[2492,5]]},"470":{"position":[[38,3],[997,3],[1108,4]]},"472":{"position":[[7,3],[650,3]]},"474":{"position":[[167,3]]},"594":{"position":[[717,3]]},"602":{"position":[[877,3]]},"626":{"position":[[809,3]]},"654":{"position":[[1085,5],[2771,5]]},"692":{"position":[[1617,4]]},"696":{"position":[[3668,6]]},"700":{"position":[[76,3]]},"760":{"position":[[469,5]]},"764":{"position":[[2185,3]]},"796":{"position":[[192,3]]},"808":{"position":[[249,5]]},"832":{"position":[[928,3]]},"838":{"position":[[1497,3]]},"868":{"position":[[193,3]]},"892":{"position":[[457,3],[864,5]]},"894":{"position":[[465,5],[1358,5]]},"896":{"position":[[1964,5]]},"904":{"position":[[623,3]]}}}],["aspect",{"_index":1334,"t":{"95":{"position":[[1528,7]]}}}],["assembl",{"_index":901,"t":{"47":{"position":[[8495,9]]},"91":{"position":[[915,8]]}}}],["assert",{"_index":5090,"t":{"890":{"position":[[1483,6]]}}}],["assign",{"_index":4985,"t":{"860":{"position":[[759,7]]},"1027":{"position":[[1918,6]]}}}],["associ",{"_index":1212,"t":{"91":{"position":[[797,10]]},"249":{"position":[[1453,10]]},"492":{"position":[[52,11],[130,10],[245,11],[328,11],[650,11]]},"574":{"position":[[2625,10]]},"656":{"position":[[1000,9]]},"658":{"position":[[1804,10]]},"682":{"position":[[2096,10]]},"854":{"position":[[255,11],[523,11]]},"862":{"position":[[2715,10],[3075,10]]},"890":{"position":[[402,10]]},"894":{"position":[[893,10]]},"896":{"position":[[2219,10]]}}}],["assort",{"_index":844,"t":{"47":{"position":[[6617,8]]},"880":{"position":[[26,10]]}}}],["assum",{"_index":352,"t":{"23":{"position":[[576,6],[796,8]]},"65":{"position":[[46,6],[237,8]]},"67":{"position":[[436,6]]},"73":{"position":[[43,6]]},"113":{"position":[[621,6]]},"151":{"position":[[6,6]]},"165":{"position":[[89,6]]},"189":{"position":[[1127,6]]},"233":{"position":[[465,6]]},"257":{"position":[[5519,7]]},"396":{"position":[[1446,7]]},"442":{"position":[[63,8]]},"510":{"position":[[150,6]]},"554":{"position":[[129,7],[489,7]]},"626":{"position":[[1801,6]]},"636":{"position":[[1488,6]]},"734":{"position":[[1607,6]]},"760":{"position":[[1730,6]]},"850":{"position":[[1454,6]]},"874":{"position":[[723,8]]},"1035":{"position":[[2772,8]]}}}],["assumpt",{"_index":3634,"t":{"584":{"position":[[1038,10],[2641,11]]},"626":{"position":[[7225,10]]},"696":{"position":[[3603,10]]},"878":{"position":[[275,10],[441,10]]}}}],["asterisk",{"_index":2296,"t":{"340":{"position":[[306,8]]},"342":{"position":[[808,8]]},"486":{"position":[[243,9],[355,9]]},"496":{"position":[[391,9]]},"608":{"position":[[1487,10],[2249,9]]},"694":{"position":[[2203,8]]},"1027":{"position":[[7406,8],[8005,8]]},"1035":{"position":[[30,8],[1990,8]]}}}],["asymmetr",{"_index":5076,"t":{"884":{"position":[[534,10],[589,10]]},"886":{"position":[[29,10],[185,10],[304,10]]},"890":{"position":[[12,10]]}}}],["asymmetricencrypt",{"_index":5314,"t":{"981":{"position":[[75,20]]},"1015":{"position":[[75,20]]}}}],["atom",{"_index":1258,"t":{"91":{"position":[[2529,4]]},"352":{"position":[[2520,6],[2646,7],[2674,6],[2951,6]]},"754":{"position":[[93,4]]}}}],["attach",{"_index":1842,"t":{"241":{"position":[[808,7]]},"249":{"position":[[1734,8],[1795,8]]},"251":{"position":[[847,8]]},"255":{"position":[[428,8]]},"292":{"position":[[1935,8]]},"634":{"position":[[74,8]]},"818":{"position":[[1013,6]]},"828":{"position":[[2065,6]]},"832":{"position":[[144,6],[298,6],[361,6],[632,6],[677,6],[1154,6],[1161,6]]},"838":{"position":[[850,6],[862,8],[1366,7],[1468,7],[1737,8],[1799,6],[1897,6],[2220,6],[2265,6],[2274,6],[2403,6],[2422,6]]},"842":{"position":[[824,6],[841,6]]},"904":{"position":[[790,9],[1535,8]]},"995":{"position":[[263,7],[314,7],[454,6]]},"997":{"position":[[240,6]]}}}],["attack",{"_index":2563,"t":{"378":{"position":[[5175,9]]}}}],["attempt",{"_index":621,"t":{"41":{"position":[[592,7]]},"95":{"position":[[214,10]]},"278":{"position":[[657,7]]},"336":{"position":[[1285,10]]},"638":{"position":[[514,8],[681,8]]},"640":{"position":[[354,7],[497,7]]},"716":{"position":[[667,7]]},"850":{"position":[[1151,7]]},"1033":{"position":[[229,8]]}}}],["attent",{"_index":3849,"t":{"626":{"position":[[5996,9]]}}}],["attribut",{"_index":1723,"t":{"219":{"position":[[960,10]]},"484":{"position":[[530,10]]},"854":{"position":[[37,11],[398,10]]},"856":{"position":[[113,11]]}}}],["aug",{"_index":1696,"t":{"217":{"position":[[911,3],[962,3]]}}}],["aumasson",{"_index":5297,"t":{"969":{"position":[[470,8]]}}}],["authent",{"_index":3935,"t":{"654":{"position":[[2969,13],[3099,12]]},"662":{"position":[[535,12]]},"884":{"position":[[412,13],[444,12],[559,12],[682,14],[771,14]]},"888":{"position":[[558,15]]},"892":{"position":[[35,14]]},"898":{"position":[[1204,12]]},"908":{"position":[[1109,12],[2346,15],[3222,15]]},"910":{"position":[[1465,14]]}}}],["author",{"_index":4687,"t":{"788":{"position":[[277,7]]},"945":{"position":[[109,6]]}}}],["auto",{"_index":3833,"t":{"626":{"position":[[2770,4]]},"640":{"position":[[1451,4]]},"734":{"position":[[3457,5]]},"744":{"position":[[659,4]]}}}],["autocd",{"_index":3824,"t":{"626":{"position":[[1841,6],[1919,6],[2342,6]]},"738":{"position":[[4956,6],[5774,6]]},"740":{"position":[[271,7],[288,9]]}}}],["autocomplet",{"_index":4426,"t":{"724":{"position":[[407,15]]}}}],["autom",{"_index":980,"t":{"57":{"position":[[1337,8]]},"95":{"position":[[1103,11],[1115,9],[1152,9],[1190,9],[1247,9],[1287,9],[1350,10],[1468,10],[1807,10],[1872,11],[2142,9]]},"266":{"position":[[1091,8]]},"284":{"position":[[534,9],[857,9]]},"476":{"position":[[266,8]]},"876":{"position":[[355,10]]},"943":{"position":[[70,10]]}}}],["automat",{"_index":1947,"t":{"257":{"position":[[969,13]]},"376":{"position":[[2981,9]]},"468":{"position":[[1460,13]]},"626":{"position":[[795,13]]},"696":{"position":[[2050,13]]},"714":{"position":[[2238,13]]},"724":{"position":[[229,14]]},"806":{"position":[[3601,13]]},"834":{"position":[[2231,9]]},"838":{"position":[[1883,13]]},"985":{"position":[[1119,14]]}}}],["autoplay",{"_index":5333,"t":{"985":{"position":[[1061,8]]}}}],["autoplay={tru",{"_index":5326,"t":{"985":{"position":[[460,15],[581,15]]}}}],["avail",{"_index":479,"t":{"29":{"position":[[123,9]]},"37":{"position":[[397,9],[689,9]]},"39":{"position":[[56,10],[169,9]]},"41":{"position":[[922,9]]},"43":{"position":[[1289,9]]},"47":{"position":[[2599,9],[6019,9],[6087,9],[7746,9]]},"49":{"position":[[859,9]]},"69":{"position":[[1124,9],[1770,9],[2014,9]]},"77":{"position":[[4902,9]]},"91":{"position":[[1171,12]]},"93":{"position":[[820,9]]},"95":{"position":[[2176,12]]},"99":{"position":[[354,9]]},"181":{"position":[[448,10]]},"229":{"position":[[891,9]]},"262":{"position":[[685,9],[843,9],[1002,9]]},"276":{"position":[[158,9]]},"292":{"position":[[1714,9]]},"324":{"position":[[11,9]]},"332":{"position":[[858,9]]},"356":{"position":[[521,9]]},"406":{"position":[[1106,9]]},"408":{"position":[[583,9]]},"442":{"position":[[1237,9]]},"470":{"position":[[381,9]]},"484":{"position":[[461,9]]},"506":{"position":[[350,9],[2450,9]]},"510":{"position":[[1646,10],[2422,9]]},"520":{"position":[[750,9]]},"522":{"position":[[680,9],[1159,9]]},"536":{"position":[[1459,9]]},"540":{"position":[[190,9]]},"552":{"position":[[1223,9]]},"626":{"position":[[2206,9]]},"648":{"position":[[747,10]]},"662":{"position":[[98,9]]},"686":{"position":[[566,9]]},"698":{"position":[[246,9]]},"738":{"position":[[1628,9],[2725,9],[3020,9]]},"746":{"position":[[1859,9]]},"748":{"position":[[1119,10]]},"754":{"position":[[415,10],[590,10],[799,9]]},"788":{"position":[[118,9]]},"796":{"position":[[241,9]]},"800":{"position":[[794,9]]},"828":{"position":[[1116,9]]},"834":{"position":[[190,9],[833,10]]},"840":{"position":[[677,10]]},"858":{"position":[[3194,9]]},"866":{"position":[[113,10],[194,9]]},"868":{"position":[[128,9]]},"876":{"position":[[914,9],[982,10]]},"902":{"position":[[1457,9]]},"949":{"position":[[20,9]]},"985":{"position":[[102,9],[888,9]]},"1021":{"position":[[72,9]]},"1027":{"position":[[437,9],[1540,9],[8885,9]]}}}],["averag",{"_index":1267,"t":{"91":{"position":[[2799,7]]}}}],["avoid",{"_index":399,"t":{"23":{"position":[[2012,5]]},"49":{"position":[[39,5]]},"175":{"position":[[1684,5],[1878,5]]},"237":{"position":[[0,5],[908,8],[1126,5],[1369,5]]},"276":{"position":[[1204,5]]},"312":{"position":[[255,8]]},"318":{"position":[[253,7]]},"320":{"position":[[144,7]]},"330":{"position":[[12,5]]},"336":{"position":[[182,7]]},"352":{"position":[[1449,5],[2585,5],[2760,5]]},"376":{"position":[[2291,5]]},"378":{"position":[[5020,5]]},"428":{"position":[[1300,5]]},"456":{"position":[[437,5]]},"488":{"position":[[1196,5]]},"492":{"position":[[858,5]]},"506":{"position":[[2302,5]]},"508":{"position":[[5211,5]]},"522":{"position":[[1344,5]]},"528":{"position":[[706,5]]},"530":{"position":[[587,8]]},"566":{"position":[[708,5]]},"588":{"position":[[2918,5]]},"596":{"position":[[872,5],[1102,5],[1695,5]]},"616":{"position":[[116,5]]},"626":{"position":[[6137,5],[6437,5]]},"654":{"position":[[2912,5]]},"696":{"position":[[802,5]]},"794":{"position":[[321,5]]},"798":{"position":[[827,8]]},"862":{"position":[[2598,5]]},"872":{"position":[[60,8]]},"876":{"position":[[1057,5]]},"878":{"position":[[102,6]]},"894":{"position":[[606,8]]},"908":{"position":[[1304,5]]},"1029":{"position":[[1023,5]]},"1033":{"position":[[3081,5]]}}}],["aw",{"_index":2386,"t":{"364":{"position":[[249,4],[929,4]]},"366":{"position":[[551,4],[1478,4],[1559,4],[1891,4],[3595,4]]},"370":{"position":[[553,4]]},"372":{"position":[[428,4]]},"636":{"position":[[1817,3]]},"838":{"position":[[835,3],[1351,3],[2064,3],[2368,3],[2521,3]]},"892":{"position":[[194,4],[199,3]]},"894":{"position":[[325,3],[850,3],[1059,3],[1282,3],[1423,3]]},"896":{"position":[[26,3],[86,3],[192,3],[836,3],[2166,3],[2908,3]]},"898":{"position":[[477,3],[492,3],[2124,3],[2422,3]]},"902":{"position":[[423,3],[1098,3],[1418,3]]},"904":{"position":[[246,3],[1084,3]]},"908":{"position":[[1568,3],[1841,3],[1990,3],[3007,3]]},"912":{"position":[[134,3]]}}}],["awar",{"_index":1000,"t":{"65":{"position":[[201,5]]},"81":{"position":[[1004,5]]},"253":{"position":[[1586,5]]},"380":{"position":[[13,5]]},"534":{"position":[[1778,5]]},"538":{"position":[[2835,5]]},"626":{"position":[[914,5]]},"654":{"position":[[4360,5]]},"858":{"position":[[2880,5]]},"868":{"position":[[959,5]]}}}],["away",{"_index":581,"t":{"37":{"position":[[264,5]]},"93":{"position":[[642,4]]},"175":{"position":[[1947,4]]},"239":{"position":[[32,5]]},"274":{"position":[[421,4]]},"634":{"position":[[2033,5]]},"670":{"position":[[1851,5]]},"800":{"position":[[1597,4]]}}}],["awesom",{"_index":2480,"t":{"376":{"position":[[1987,8],[5364,8]]}}}],["awk",{"_index":2568,"t":{"382":{"position":[[56,4],[155,3],[195,3],[357,3]]},"386":{"position":[[1079,3]]},"448":{"position":[[1378,3]]},"450":{"position":[[5297,4]]},"794":{"position":[[198,3]]}}}],["aws/config",{"_index":2393,"t":{"364":{"position":[[349,13],[1034,13]]},"366":{"position":[[660,13],[1997,13]]},"368":{"position":[[190,13],[951,13]]},"370":{"position":[[661,13]]},"372":{"position":[[541,13]]}}}],["aws/credenti",{"_index":2395,"t":{"364":{"position":[[388,18],[1078,18]]},"366":{"position":[[702,18],[2039,18]]},"368":{"position":[[229,18],[990,18]]},"370":{"position":[[708,18]]},"372":{"position":[[587,18]]}}}],["aws_secret_access_key",{"_index":2562,"t":{"378":{"position":[[5107,24]]}}}],["axwwo",{"_index":5073,"t":{"878":{"position":[[1885,5]]}}}],["azur",{"_index":2387,"t":{"364":{"position":[[254,6],[934,6]]},"366":{"position":[[556,6],[1483,6],[1564,6],[1896,6],[3600,6]]},"370":{"position":[[558,6]]},"372":{"position":[[433,6]]}}}],["azure/config",{"_index":2396,"t":{"364":{"position":[[432,15],[1127,15]]},"366":{"position":[[749,15],[2086,15]]},"368":{"position":[[273,15],[1034,15]]},"370":{"position":[[760,15]]},"372":{"position":[[638,15]]}}}],["b",{"_index":1495,"t":{"155":{"position":[[102,1]]},"390":{"position":[[244,1]]},"400":{"position":[[714,1],[888,1]]},"458":{"position":[[529,3]]},"672":{"position":[[788,1]]},"680":{"position":[[1246,1],[1596,1],[1654,3],[1734,1]]},"688":{"position":[[465,1],[649,1],[3244,2]]},"692":{"position":[[214,1]]},"696":{"position":[[1392,1]]},"704":{"position":[[1239,1]]},"734":{"position":[[2308,1],[2347,3]]},"762":{"position":[[691,1]]},"766":{"position":[[758,1],[1838,1]]},"778":{"position":[[354,3]]},"826":{"position":[[452,2]]},"828":{"position":[[1175,2],[1454,2],[1620,2],[1649,2],[1682,2],[1770,2],[1800,2],[1831,2]]},"830":{"position":[[904,2],[922,3],[959,4]]},"832":{"position":[[490,2],[828,2],[1246,2],[1324,2],[1356,2],[1418,2],[1444,2],[1478,2]]},"834":{"position":[[1741,2],[2325,2],[2682,2],[3102,2],[3111,2],[3193,2],[3227,2],[3763,2],[3908,2],[3911,1],[4036,2],[4039,1],[4221,2],[4224,1],[4245,1],[4882,2],[4912,2]]},"838":{"position":[[556,2]]},"842":{"position":[[56,2],[122,1],[126,1],[149,1],[181,1],[215,1],[243,1],[275,1],[304,1],[332,1],[360,1],[391,1],[416,1],[455,1],[486,1],[513,1],[540,1],[569,1],[595,1],[616,1],[1024,2],[1061,2],[1139,2],[1172,2],[1204,2],[1266,2],[1292,2],[1326,2],[1396,2],[1425,2],[1458,2],[1546,2],[1576,2],[1607,2],[1638,2],[1684,2],[1715,2],[1744,2],[1805,2],[1835,2]]},"927":{"position":[[1192,1]]}}}],["b8d2",{"_index":2737,"t":{"402":{"position":[[1014,4]]}}}],["b9ae0ad",{"_index":3981,"t":{"660":{"position":[[1246,7]]},"688":{"position":[[3034,8]]},"694":{"position":[[1992,7]]},"696":{"position":[[5643,7]]},"702":{"position":[[729,7]]}}}],["ba12",{"_index":2720,"t":{"402":{"position":[[654,4]]}}}],["back",{"_index":470,"t":{"27":{"position":[[638,4]]},"31":{"position":[[1913,4],[2597,4]]},"45":{"position":[[932,4]]},"71":{"position":[[539,4]]},"91":{"position":[[902,6]]},"141":{"position":[[84,4],[310,4],[918,4]]},"143":{"position":[[122,4],[230,4]]},"145":{"position":[[930,4]]},"155":{"position":[[109,4]]},"163":{"position":[[156,4]]},"171":{"position":[[137,4]]},"173":{"position":[[29,4]]},"175":{"position":[[1085,4],[1431,5]]},"233":{"position":[[54,4],[304,4]]},"239":{"position":[[543,4]]},"284":{"position":[[49,4]]},"442":{"position":[[1802,4]]},"594":{"position":[[375,5]]},"600":{"position":[[427,5]]},"610":{"position":[[913,5]]},"626":{"position":[[5877,4]]},"654":{"position":[[342,4]]},"658":{"position":[[1205,4]]},"664":{"position":[[1359,5]]},"666":{"position":[[1195,5]]},"668":{"position":[[1145,4]]},"688":{"position":[[1840,4],[2015,4],[2402,4],[2628,4]]},"692":{"position":[[575,4],[623,4]]},"694":{"position":[[1701,5],[2299,4]]},"696":{"position":[[1530,4],[1582,4]]},"700":{"position":[[785,4],[3365,4]]},"702":{"position":[[1906,4],[2414,4],[2547,4]]},"704":{"position":[[1911,4],[1974,4]]},"720":{"position":[[1581,4],[3894,4]]},"760":{"position":[[2346,4]]},"766":{"position":[[1990,4]]},"770":{"position":[[1266,4],[1423,4]]},"778":{"position":[[360,4],[381,4]]},"844":{"position":[[189,4]]},"870":{"position":[[483,4]]},"890":{"position":[[1811,4]]},"896":{"position":[[686,4]]},"908":{"position":[[1391,4],[2718,4]]},"914":{"position":[[209,4]]},"927":{"position":[[1864,4],[1927,4]]},"939":{"position":[[153,4]]},"1027":{"position":[[7596,4],[8075,4]]},"1033":{"position":[[3638,4],[3738,4]]}}}],["background",{"_index":1782,"t":{"229":{"position":[[78,11],[365,10],[493,10],[669,11],[863,10],[969,11]]},"231":{"position":[[49,11],[295,11],[590,11],[931,10],[1238,11],[1264,14],[1772,10],[2090,11],[2153,11]]},"233":{"position":[[25,11],[223,10]]},"235":{"position":[[180,10]]},"237":{"position":[[298,11],[484,11],[698,10],[982,10],[1085,11]]},"239":{"position":[[172,11],[271,11],[621,10]]},"243":{"position":[[181,10],[238,10],[296,10],[356,10],[1437,11]]},"255":{"position":[[322,10]]},"422":{"position":[[1945,11]]},"426":{"position":[[438,10]]},"522":{"position":[[31,10]]},"532":{"position":[[521,10]]},"538":{"position":[[194,11]]},"584":{"position":[[397,11]]},"714":{"position":[[3260,10]]},"716":{"position":[[2675,10],[2707,10],[2745,10],[2781,10],[2819,10],[2857,10],[2894,10],[2933,10],[2970,10],[3401,10],[3604,10],[4219,10]]},"720":{"position":[[1071,10],[1897,10]]},"862":{"position":[[847,10]]}}}],["background3",{"_index":1807,"t":{"231":{"position":[[2217,12]]}}}],["backlash",{"_index":3729,"t":{"596":{"position":[[746,10],[1339,10]]},"722":{"position":[[709,8]]}}}],["backmatt",{"_index":5371,"t":{"1007":{"position":[[244,10]]}}}],["backslash",{"_index":1642,"t":{"203":{"position":[[542,9]]},"213":{"position":[[897,9]]},"378":{"position":[[1587,9]]},"426":{"position":[[18,9]]},"500":{"position":[[533,9],[662,9]]},"510":{"position":[[2556,9]]},"566":{"position":[[1206,11]]},"714":{"position":[[1602,9]]},"718":{"position":[[1125,9],[1922,9],[2047,9]]},"722":{"position":[[897,9]]}}}],["backspac",{"_index":3310,"t":{"508":{"position":[[5172,10]]}}}],["backtick",{"_index":3256,"t":{"500":{"position":[[178,8],[694,8],[749,8],[893,8]]},"778":{"position":[[888,8]]},"1029":{"position":[[833,8],[1205,8]]}}}],["backtrack",{"_index":2348,"t":{"352":{"position":[[668,12],[995,13],[2591,12],[2702,12],[2779,13]]}}}],["backup",{"_index":1462,"t":{"137":{"position":[[613,6],[650,7]]},"255":{"position":[[357,6]]},"364":{"position":[[75,6],[139,6],[196,6],[219,9],[735,6],[872,6]]},"366":{"position":[[102,6],[441,6],[498,6],[521,9],[1781,6],[1838,6],[1861,9],[3485,6],[3542,6],[3565,9]]},"368":{"position":[[107,6],[868,6]]},"370":{"position":[[443,6],[500,6],[523,9]]},"372":{"position":[[318,6],[375,6],[398,9]]},"376":{"position":[[660,6]]},"386":{"position":[[1547,6],[1562,6]]},"488":{"position":[[175,6],[266,6],[657,6],[766,6],[1288,6],[1404,6]]},"550":{"position":[[731,9],[785,7],[994,9]]},"552":{"position":[[252,9],[364,7],[475,9],[959,9],[990,7]]},"554":{"position":[[83,9],[973,10],[995,10]]},"570":{"position":[[175,9],[191,9],[807,9],[828,9]]},"908":{"position":[[881,9],[945,9]]},"1027":{"position":[[891,6],[964,6],[1091,6],[1162,6],[1441,6],[7254,6],[7332,6]]}}}],["backup/configuration/aliyun/cp",{"_index":2407,"t":{"364":{"position":[[1001,32]]}}}],["backup/configuration/aws/cp",{"_index":2408,"t":{"364":{"position":[[1048,29],[1097,29]]}}}],["backup/configuration/azure/cp",{"_index":2409,"t":{"364":{"position":[[1143,31]]}}}],["backup/configuration/gcloud/cp",{"_index":2410,"t":{"364":{"position":[[1207,32]]}}}],["backup/configuration/ssh",{"_index":2412,"t":{"364":{"position":[[1298,27],[1362,27]]}}}],["backup/configuration/ssh/cp",{"_index":2411,"t":{"364":{"position":[[1254,29]]}}}],["backup/settings/aliyun",{"_index":2437,"t":{"370":{"position":[[625,25]]}}}],["backup/settings/aliyun/\"echo",{"_index":2446,"t":{"372":{"position":[[506,30]]}}}],["backup/settings/aliyun/cp",{"_index":2392,"t":{"364":{"position":[[321,27]]},"368":{"position":[[162,27],[923,27]]}}}],["backup/settings/aliyun/rmdir",{"_index":2421,"t":{"366":{"position":[[629,30],[1966,30],[3648,30]]}}}],["backup/settings/aw",{"_index":2439,"t":{"370":{"position":[[675,22],[727,22]]}}}],["backup/settings/aws/\"echo",{"_index":2447,"t":{"372":{"position":[[555,27],[606,27]]}}}],["backup/settings/aws/cp",{"_index":2394,"t":{"364":{"position":[[363,24],[407,24]]},"368":{"position":[[204,24],[248,24],[965,24],[1009,24]]}}}],["backup/settings/aws/rmdir",{"_index":2422,"t":{"366":{"position":[[674,27],[721,27],[2011,27],[2058,27],[3679,27],[3707,27]]}}}],["backup/settings/azur",{"_index":2440,"t":{"370":{"position":[[776,24]]}}}],["backup/settings/azure/\"echo",{"_index":2448,"t":{"372":{"position":[[654,29]]}}}],["backup/settings/azure/cp",{"_index":2397,"t":{"364":{"position":[[448,26]]},"368":{"position":[[289,26],[1050,26]]}}}],["backup/settings/azure/rmdir",{"_index":2423,"t":{"366":{"position":[[765,29],[2102,29],[3735,29]]}}}],["backup/settings/gcloud",{"_index":2441,"t":{"370":{"position":[[843,25]]}}}],["backup/settings/gcloud/\"echo",{"_index":2449,"t":{"372":{"position":[[720,30]]}}}],["backup/settings/gcloud/cp",{"_index":2399,"t":{"364":{"position":[[507,27]]},"368":{"position":[[348,27],[1109,27]]}}}],["backup/settings/gcloud/rmdir",{"_index":2424,"t":{"366":{"position":[[827,30],[2164,30],[3765,30]]}}}],["backup/settings/ssh",{"_index":2403,"t":{"364":{"position":[[588,22],[647,22]]},"366":{"position":[[914,22],[976,22],[2251,22],[2313,22],[3824,22],[3868,22]]},"368":{"position":[[472,22],[1233,22]]},"370":{"position":[[893,22],[940,22],[1007,22]]},"372":{"position":[[815,22],[881,23]]}}}],["backup/settings/ssh/\"echo",{"_index":2450,"t":{"372":{"position":[[769,27]]}}}],["backup/settings/ssh/cp",{"_index":2401,"t":{"364":{"position":[[549,24]]},"368":{"position":[[390,24],[429,24],[1151,24],[1190,24]]}}}],["backup/settings/ssh/rmdir",{"_index":2425,"t":{"366":{"position":[[872,27],[2209,27],[3796,27]]}}}],["backup_dir",{"_index":5398,"t":{"1027":{"position":[[2768,10],[2822,13],[2913,10],[3062,10]]}}}],["backup_location=${backup_dir:?pleas",{"_index":5397,"t":{"1027":{"position":[[2727,36]]}}}],["backupcp",{"_index":2433,"t":{"368":{"position":[[129,10],[890,10]]}}}],["backupdir",{"_index":3209,"t":{"488":{"position":[[1327,14]]}}}],["backupdir=\"${user}_backup\"echo",{"_index":3207,"t":{"488":{"position":[[1247,30]]}}}],["backupdir}'\"mkdir",{"_index":3208,"t":{"488":{"position":[[1306,20]]}}}],["backups/{}.bakcp",{"_index":3140,"t":{"468":{"position":[[1970,18]]}}}],["backupscp",{"_index":3132,"t":{"468":{"position":[[362,11],[1099,11]]}}}],["backupsfi",{"_index":3527,"t":{"552":{"position":[[386,11],[1012,11]]},"554":{"position":[[107,11]]}}}],["backupsthen",{"_index":3522,"t":{"550":{"position":[[686,13]]},"552":{"position":[[335,13]]}}}],["backward",{"_index":1476,"t":{"143":{"position":[[287,9]]},"155":{"position":[[54,9]]},"165":{"position":[[245,9],[682,9]]},"167":{"position":[[7,9],[94,9]]},"181":{"position":[[130,8],[220,8]]},"255":{"position":[[1150,10]]},"456":{"position":[[163,9]]},"550":{"position":[[485,10]]},"580":{"position":[[1912,9]]},"598":{"position":[[312,10]]},"702":{"position":[[2637,10],[2775,12],[2840,9],[2966,10]]},"766":{"position":[[763,9],[788,9],[1853,9]]}}}],["bad",{"_index":1867,"t":{"243":{"position":[[2050,3]]},"422":{"position":[[1552,3]]},"586":{"position":[[996,5],[1128,3],[1237,5],[1563,5],[2080,5]]},"626":{"position":[[6079,3]]}}}],["baffl",{"_index":2778,"t":{"422":{"position":[[2162,8]]}}}],["bak",{"_index":2564,"t":{"380":{"position":[[177,6],[287,4]]},"468":{"position":[[1865,4]]}}}],["balanc",{"_index":2042,"t":{"276":{"position":[[358,7]]}}}],["banana",{"_index":5041,"t":{"868":{"position":[[271,6]]}}}],["banana3",{"_index":5046,"t":{"868":{"position":[[429,8]]}}}],["bang",{"_index":2802,"t":{"430":{"position":[[709,6],[768,6]]},"965":{"position":[[45,4]]}}}],["bank",{"_index":1490,"t":{"151":{"position":[[213,6]]}}}],["banks.txt",{"_index":1688,"t":{"215":{"position":[[396,9]]}}}],["banks.txt16",{"_index":1681,"t":{"213":{"position":[[382,11]]}}}],["banner",{"_index":831,"t":{"47":{"position":[[6164,6],[6228,6],[6250,6],[6276,6],[6327,6],[6363,6]]}}}],["banner(6",{"_index":832,"t":{"47":{"position":[[6187,9]]}}}],["banner(6)nam",{"_index":833,"t":{"47":{"position":[[6214,13]]}}}],["bar",{"_index":435,"t":{"25":{"position":[[854,3]]},"81":{"position":[[208,5]]},"760":{"position":[[1466,3],[2306,3],[2975,3]]},"834":{"position":[[3280,4],[3315,3]]},"836":{"position":[[153,4]]},"896":{"position":[[15,3]]}}}],["bare",{"_index":634,"t":{"43":{"position":[[440,4]]},"636":{"position":[[218,4]]},"780":{"position":[[28,6]]}}}],["base",{"_index":964,"t":{"57":{"position":[[147,5],[516,5]]},"67":{"position":[[186,5]]},"175":{"position":[[2683,5]]},"189":{"position":[[1336,5]]},"197":{"position":[[54,5]]},"221":{"position":[[628,5]]},"266":{"position":[[314,5]]},"278":{"position":[[774,6]]},"352":{"position":[[900,5]]},"382":{"position":[[126,5]]},"410":{"position":[[22,5]]},"532":{"position":[[649,5]]},"534":{"position":[[602,5]]},"570":{"position":[[81,5]]},"584":{"position":[[1290,5]]},"626":{"position":[[4920,5]]},"640":{"position":[[652,5]]},"648":{"position":[[1989,5]]},"716":{"position":[[1531,4],[1555,4]]},"720":{"position":[[2679,5]]},"796":{"position":[[500,5]]},"822":{"position":[[389,5]]},"834":{"position":[[2929,4],[2953,4]]},"862":{"position":[[4453,5]]},"864":{"position":[[847,5]]},"884":{"position":[[850,5]]},"890":{"position":[[1743,5]]}}}],["basenam",{"_index":4240,"t":{"714":{"position":[[668,8],[795,8]]},"852":{"position":[[1907,10],[1987,8]]}}}],["bash",{"_index":311,"t":{"21":{"position":[[3244,5]]},"23":{"position":[[1345,4],[1932,4]]},"27":{"position":[[172,7]]},"45":{"position":[[519,4]]},"51":{"position":[[1156,6]]},"65":{"position":[[269,4]]},"67":{"position":[[96,6],[1288,6],[1329,4]]},"69":{"position":[[2096,6],[2207,4],[2323,4],[2445,4],[5506,6],[5596,4],[5633,4],[5719,4],[6031,6],[6579,4]]},"71":{"position":[[171,4],[488,4],[1472,4],[1537,4],[1615,4],[1692,4],[2020,5]]},"73":{"position":[[182,4]]},"77":{"position":[[383,4],[613,4],[672,4]]},"161":{"position":[[7,4]]},"185":{"position":[[70,4]]},"243":{"position":[[545,4],[646,4],[682,4]]},"257":{"position":[[6155,4],[6170,4]]},"288":{"position":[[882,5]]},"290":{"position":[[885,5],[973,4],[1071,4],[2548,4]]},"292":{"position":[[2012,4]]},"304":{"position":[[1903,5],[1966,4],[2469,4]]},"316":{"position":[[172,4]]},"354":{"position":[[126,5]]},"364":{"position":[[170,5],[903,5]]},"366":{"position":[[472,5],[1812,5],[3516,5]]},"370":{"position":[[474,5]]},"372":{"position":[[349,5]]},"378":{"position":[[3768,4]]},"394":{"position":[[536,4],[582,5]]},"428":{"position":[[239,4],[1035,5],[1091,4]]},"430":{"position":[[1083,4],[1177,5]]},"432":{"position":[[1459,5]]},"434":{"position":[[1897,5],[1958,4]]},"446":{"position":[[2140,4]]},"490":{"position":[[440,4],[1874,4]]},"492":{"position":[[24,4]]},"494":{"position":[[389,4]]},"506":{"position":[[401,4],[2368,4],[2481,5]]},"510":{"position":[[2499,4]]},"540":{"position":[[203,4]]},"554":{"position":[[617,5]]},"566":{"position":[[43,5],[53,4],[634,4],[1113,4],[1338,4],[1347,4]]},"568":{"position":[[1181,4],[1487,5]]},"574":{"position":[[184,4],[1449,6],[1600,4],[1702,6],[2762,5]]},"576":{"position":[[305,4]]},"584":{"position":[[841,4]]},"608":{"position":[[2598,4]]},"612":{"position":[[922,4]]},"622":{"position":[[1038,4],[1147,4]]},"626":{"position":[[2265,4],[3751,4]]},"630":{"position":[[178,5]]},"634":{"position":[[686,6],[1114,4]]},"636":{"position":[[1946,4],[2023,5]]},"648":{"position":[[1866,4]]},"650":{"position":[[325,4]]},"660":{"position":[[1182,6]]},"692":{"position":[[303,6]]},"694":{"position":[[1262,6],[1928,6]]},"696":{"position":[[5579,6]]},"702":{"position":[[665,6]]},"714":{"position":[[825,5],[1072,5],[1116,5],[2003,4]]},"720":{"position":[[4260,4],[4269,4]]},"724":{"position":[[167,4],[1140,4]]},"726":{"position":[[596,4]]},"732":{"position":[[949,4],[1007,4]]},"734":{"position":[[3160,4],[3486,5]]},"738":{"position":[[695,4],[1223,4],[1509,4],[7905,5],[7914,4]]},"740":{"position":[[835,4]]},"744":{"position":[[654,4]]},"808":{"position":[[2028,4]]},"834":{"position":[[1306,5],[1324,5]]},"868":{"position":[[141,4],[155,4]]},"874":{"position":[[283,4],[946,4],[989,4]]},"878":{"position":[[427,4],[481,4],[504,5],[886,4],[950,5],[1125,5]]},"898":{"position":[[2105,4]]},"918":{"position":[[345,4],[425,4],[434,4],[525,4],[593,4],[688,4],[763,4],[808,4]]},"931":{"position":[[96,4]]},"955":{"position":[[51,5]]},"975":{"position":[[247,4]]},"1019":{"position":[[1544,4]]},"1035":{"position":[[2314,5]]}}}],["bash(1",{"_index":3791,"t":{"624":{"position":[[480,7]]},"734":{"position":[[427,7]]}}}],["bash(1)histsize=1000histfilesize=2000",{"_index":3797,"t":{"624":{"position":[[655,38]]},"734":{"position":[[602,37]]}}}],["bash.ex",{"_index":2127,"t":{"290":{"position":[[2436,8]]}}}],["bash_alias",{"_index":3846,"t":{"626":{"position":[[4481,15],[4580,15]]},"692":{"position":[[1547,12]]},"734":{"position":[[3060,16],[3185,15],[3311,15]]}}}],["bash_aliases=()declar",{"_index":4947,"t":{"854":{"position":[[592,22]]}}}],["bash_aliasesfi",{"_index":3847,"t":{"626":{"position":[[4507,17]]},"734":{"position":[[3211,17]]}}}],["bash_cmd",{"_index":4948,"t":{"854":{"position":[[618,12]]}}}],["bash_env",{"_index":3879,"t":{"638":{"position":[[835,8]]},"644":{"position":[[80,8]]}}}],["bash_histori",{"_index":2614,"t":{"394":{"position":[[556,16]]},"424":{"position":[[153,15],[367,15]]},"426":{"position":[[290,15]]},"430":{"position":[[324,15]]},"438":{"position":[[347,15]]},"512":{"position":[[342,15],[975,15]]},"574":{"position":[[637,15]]},"612":{"position":[[730,15]]}}}],["bash_login",{"_index":3880,"t":{"640":{"position":[[541,13]]}}}],["bash_logout",{"_index":3856,"t":{"628":{"position":[[319,14]]},"640":{"position":[[849,14]]}}}],["bash_profil",{"_index":3855,"t":{"628":{"position":[[302,16]]},"640":{"position":[[525,15]]}}}],["bash_rematch",{"_index":3566,"t":{"568":{"position":[[1190,13]]}}}],["bash_rematch[1]}\"els",{"_index":3564,"t":{"568":{"position":[[1036,23]]}}}],["bash_vers",{"_index":1098,"t":{"71":{"position":[[2064,14]]}}}],["bashbash(1",{"_index":2232,"t":{"316":{"position":[[107,11]]}}}],["bashecho",{"_index":2816,"t":{"432":{"position":[[1438,8]]}}}],["bashrc",{"_index":3780,"t":{"622":{"position":[[177,9],[545,10],[959,9]]},"624":{"position":[[58,9],[1568,9],[1706,9]]},"626":{"position":[[41,9],[165,10],[1225,9],[1671,9],[4042,9],[4418,9],[4799,9],[5264,9],[5611,9]]},"628":{"position":[[50,9]]},"634":{"position":[[1382,9],[1564,10]]},"642":{"position":[[134,9],[325,11],[341,9]]},"650":{"position":[[69,9]]},"720":{"position":[[8035,10],[8421,9]]},"732":{"position":[[981,10],[1624,9]]},"734":{"position":[[30,9],[126,9],[253,9]]},"736":{"position":[[401,9]]},"738":{"position":[[41,9]]},"742":{"position":[[708,9]]},"744":{"position":[[2474,9]]}}}],["basi",{"_index":2571,"t":{"384":{"position":[[493,6]]}}}],["basic",{"_index":334,"t":{"21":{"position":[[3993,6]]},"23":{"position":[[113,5]]},"41":{"position":[[9,5]]},"49":{"position":[[597,5],[664,6]]},"61":{"position":[[78,5]]},"71":{"position":[[1338,5]]},"77":{"position":[[202,7]]},"81":{"position":[[1767,7]]},"135":{"position":[[519,9]]},"165":{"position":[[18,5]]},"189":{"position":[[2401,5]]},"219":{"position":[[235,6]]},"270":{"position":[[115,7]]},"282":{"position":[[1419,9]]},"284":{"position":[[1417,5]]},"292":{"position":[[194,5]]},"338":{"position":[[292,7]]},"340":{"position":[[798,5]]},"350":{"position":[[117,9]]},"352":{"position":[[29,6]]},"356":{"position":[[23,5]]},"360":{"position":[[388,6]]},"364":{"position":[[1405,6],[2274,5],[2367,5],[2455,5]]},"366":{"position":[[4892,5]]},"376":{"position":[[6307,5]]},"378":{"position":[[125,5]]},"384":{"position":[[82,5]]},"396":{"position":[[536,5],[1145,5],[1984,5],[2166,5]]},"424":{"position":[[1735,5]]},"458":{"position":[[42,5]]},"462":{"position":[[1545,9]]},"550":{"position":[[1265,6]]},"670":{"position":[[31,6],[758,5]]},"698":{"position":[[587,6]]},"738":{"position":[[7856,5]]},"754":{"position":[[2424,6]]},"760":{"position":[[3211,6]]},"772":{"position":[[690,5]]},"784":{"position":[[96,6]]},"836":{"position":[[37,5]]},"840":{"position":[[778,5]]},"844":{"position":[[237,6]]},"894":{"position":[[569,6]]},"908":{"position":[[3631,6]]},"918":{"position":[[85,5]]},"1019":{"position":[[1116,5]]}}}],["bastion",{"_index":5228,"t":{"910":{"position":[[128,7],[507,9],[722,8],[848,7],[964,7],[1347,8],[1433,7],[1618,7],[1821,8]]}}}],["bastion.cloudopshelp",{"_index":2617,"t":{"394":{"position":[[750,20]]}}}],["bastion.myproject.com",{"_index":5234,"t":{"910":{"position":[[865,21]]}}}],["bb116",{"_index":5210,"t":{"908":{"position":[[2040,5]]}}}],["bbbnn",{"_index":2933,"t":{"450":{"position":[[1052,5]]}}}],["bbbnnfilebeat",{"_index":2958,"t":{"450":{"position":[[1646,13],[2121,13],[2630,13]]}}}],["be",{"_index":219,"t":{"21":{"position":[[587,5]]},"25":{"position":[[1502,5]]},"37":{"position":[[430,5]]},"67":{"position":[[828,5]]},"183":{"position":[[309,5]]},"185":{"position":[[1074,5]]},"227":{"position":[[456,5],[489,5]]},"231":{"position":[[1503,5]]},"266":{"position":[[1028,5]]},"280":{"position":[[760,5]]},"286":{"position":[[648,5]]},"336":{"position":[[139,5]]},"342":{"position":[[394,5]]},"378":{"position":[[4819,5]]},"508":{"position":[[2792,5]]},"628":{"position":[[230,5]]},"644":{"position":[[301,5]]},"670":{"position":[[491,5]]},"740":{"position":[[667,5]]},"754":{"position":[[1135,5],[1264,5]]},"756":{"position":[[47,5]]},"764":{"position":[[1682,5]]},"766":{"position":[[1548,5]]},"778":{"position":[[2201,5]]},"798":{"position":[[302,5],[533,5],[947,5],[1245,5],[1310,5]]},"858":{"position":[[3368,5]]},"898":{"position":[[2545,5]]},"955":{"position":[[241,5]]}}}],["bear",{"_index":4122,"t":{"686":{"position":[[106,4]]}}}],["beat",{"_index":2935,"t":{"450":{"position":[[1084,4],[1130,4],[1176,4],[1279,4],[1332,4],[1385,4],[1660,4],[1688,4],[1716,4],[1781,4],[1813,4],[1845,4],[2135,4],[2163,4],[2191,4],[2256,4],[2288,4],[2320,4],[2644,4],[2672,4],[2700,4],[2765,4],[2797,4],[2829,4]]},"794":{"position":[[109,5]]}}}],["beauti",{"_index":936,"t":{"51":{"position":[[244,6]]}}}],["becam",{"_index":1654,"t":{"205":{"position":[[1574,6]]},"282":{"position":[[729,6]]},"744":{"position":[[271,6],[335,6]]}}}],["becom",{"_index":588,"t":{"37":{"position":[[594,6]]},"67":{"position":[[580,6]]},"91":{"position":[[300,6]]},"93":{"position":[[142,6]]},"95":{"position":[[190,8],[1749,8]]},"227":{"position":[[666,8]]},"251":{"position":[[361,8]]},"253":{"position":[[2268,8]]},"255":{"position":[[2311,7]]},"262":{"position":[[3279,8]]},"290":{"position":[[2716,6]]},"292":{"position":[[593,7]]},"304":{"position":[[2704,6]]},"392":{"position":[[70,6]]},"396":{"position":[[1773,6]]},"406":{"position":[[83,7]]},"664":{"position":[[159,6],[2173,6]]},"668":{"position":[[602,6]]},"670":{"position":[[1048,8]]},"678":{"position":[[1103,6]]},"684":{"position":[[3282,6]]},"698":{"position":[[557,6]]},"736":{"position":[[249,7]]},"756":{"position":[[326,6]]},"766":{"position":[[103,8]]},"770":{"position":[[1338,6]]},"794":{"position":[[456,6],[505,6]]},"818":{"position":[[104,6]]},"840":{"position":[[565,8]]},"844":{"position":[[207,6]]},"906":{"position":[[25,6]]},"1037":{"position":[[768,6]]}}}],["beep",{"_index":4234,"t":{"714":{"position":[[257,6],[306,4]]}}}],["befor",{"_index":346,"t":{"23":{"position":[[301,6]]},"27":{"position":[[708,7]]},"73":{"position":[[3,7]]},"77":{"position":[[33,7],[241,6]]},"91":{"position":[[169,7]]},"99":{"position":[[0,6]]},"111":{"position":[[52,6]]},"117":{"position":[[523,6]]},"123":{"position":[[284,6]]},"141":{"position":[[941,6]]},"147":{"position":[[835,6]]},"173":{"position":[[56,6]]},"195":{"position":[[421,6]]},"201":{"position":[[105,6]]},"203":{"position":[[552,6]]},"205":{"position":[[267,6],[659,6],[734,6]]},"207":{"position":[[276,6]]},"211":{"position":[[387,6],[736,6]]},"213":{"position":[[907,6]]},"215":{"position":[[655,6]]},"253":{"position":[[3534,6]]},"255":{"position":[[117,7]]},"257":{"position":[[1190,7],[1665,6],[4248,6],[4444,7],[5744,7]]},"259":{"position":[[585,6]]},"260":{"position":[[94,6]]},"302":{"position":[[152,7],[554,6]]},"342":{"position":[[364,6],[465,6],[574,6]]},"344":{"position":[[548,6]]},"348":{"position":[[589,6]]},"352":{"position":[[1650,6],[1761,6]]},"366":{"position":[[2376,6]]},"376":{"position":[[1257,6]]},"378":{"position":[[3386,6]]},"396":{"position":[[939,6]]},"400":{"position":[[727,6],[770,6],[1378,6]]},"402":{"position":[[343,6]]},"408":{"position":[[979,6]]},"410":{"position":[[388,6],[432,6]]},"420":{"position":[[1307,6]]},"434":{"position":[[745,6]]},"438":{"position":[[0,6]]},"448":{"position":[[231,6]]},"456":{"position":[[378,6]]},"462":{"position":[[363,6],[1810,6],[2430,6],[2507,6]]},"470":{"position":[[75,6]]},"482":{"position":[[286,6]]},"508":{"position":[[1810,6],[1991,6],[3151,6],[3453,6],[4119,6]]},"520":{"position":[[416,6]]},"574":{"position":[[2795,7]]},"588":{"position":[[359,6]]},"600":{"position":[[1529,6]]},"606":{"position":[[112,6]]},"608":{"position":[[2178,6]]},"626":{"position":[[830,6],[2605,6]]},"646":{"position":[[0,6]]},"648":{"position":[[647,6],[2511,6]]},"654":{"position":[[4274,6]]},"658":{"position":[[2204,6]]},"672":{"position":[[0,6]]},"678":{"position":[[886,6]]},"682":{"position":[[1932,6]]},"684":{"position":[[3142,6]]},"716":{"position":[[3412,6]]},"718":{"position":[[1135,6],[2057,6]]},"720":{"position":[[3922,6],[5196,6]]},"722":{"position":[[2408,6],[2520,6]]},"724":{"position":[[721,6]]},"738":{"position":[[2471,6],[6645,6],[7356,6],[7999,6]]},"740":{"position":[[0,6],[913,6]]},"760":{"position":[[784,6],[3372,6]]},"766":{"position":[[922,6]]},"776":{"position":[[490,6]]},"778":{"position":[[643,6]]},"832":{"position":[[664,7],[949,6]]},"852":{"position":[[198,6],[1252,6]]},"854":{"position":[[996,6]]},"880":{"position":[[248,6]]},"886":{"position":[[167,6]]},"898":{"position":[[1079,6],[1580,6]]},"908":{"position":[[3368,6]]},"1035":{"position":[[1145,6],[2765,6]]}}}],["begin",{"_index":1493,"t":{"153":{"position":[[20,9],[67,9]]},"161":{"position":[[53,9],[184,9]]},"181":{"position":[[108,9]]},"249":{"position":[[2076,10]]},"346":{"position":[[448,9],[562,9]]},"368":{"position":[[556,9]]},"372":{"position":[[996,9]]},"402":{"position":[[1413,9]]},"430":{"position":[[45,9]]},"524":{"position":[[578,9]]},"528":{"position":[[93,9]]},"766":{"position":[[571,9],[645,9],[986,9],[1222,9],[1707,9],[1761,9],[1914,9]]},"768":{"position":[[316,9]]},"770":{"position":[[391,9],[721,9],[877,9]]},"772":{"position":[[389,9],[737,9],[960,9],[1421,9]]},"778":{"position":[[435,9],[480,9]]},"802":{"position":[[1688,9]]},"830":{"position":[[596,10]]},"858":{"position":[[2930,9]]},"987":{"position":[[466,9],[567,9]]},"995":{"position":[[354,9]]},"1027":{"position":[[3537,10],[7504,9]]},"1035":{"position":[[109,9]]}}}],["beginn",{"_index":4923,"t":{"844":{"position":[[148,8]]},"941":{"position":[[66,10]]}}}],["behav",{"_index":983,"t":{"59":{"position":[[157,6]]},"61":{"position":[[519,6]]},"81":{"position":[[2095,7]]},"83":{"position":[[334,6]]},"288":{"position":[[209,6]]},"376":{"position":[[3167,6]]},"384":{"position":[[552,7]]},"532":{"position":[[1198,6]]},"610":{"position":[[796,6]]},"760":{"position":[[1309,7]]},"796":{"position":[[1194,6]]},"862":{"position":[[594,7]]},"864":{"position":[[41,6]]}}}],["behaviour",{"_index":1376,"t":{"105":{"position":[[539,9]]},"211":{"position":[[534,9]]},"217":{"position":[[764,9]]},"262":{"position":[[3570,9]]},"522":{"position":[[2155,9]]},"538":{"position":[[2098,10]]},"566":{"position":[[736,10],[1665,10]]},"586":{"position":[[1796,9]]},"610":{"position":[[373,9]]},"616":{"position":[[150,10],[177,9]]},"626":{"position":[[718,9],[1097,9]]},"738":{"position":[[2049,11]]},"850":{"position":[[76,9],[297,10]]},"878":{"position":[[770,9]]},"906":{"position":[[1054,9]]},"943":{"position":[[173,9]]},"985":{"position":[[1731,9]]}}}],["behaviour4",{"_index":2238,"t":{"318":{"position":[[284,11]]}}}],["behind",{"_index":1497,"t":{"159":{"position":[[40,6]]},"442":{"position":[[1779,6]]},"704":{"position":[[55,6]]},"732":{"position":[[384,7]]},"856":{"position":[[196,6]]}}}],["bel",{"_index":866,"t":{"47":{"position":[[6879,6]]}}}],["belief",{"_index":1342,"t":{"95":{"position":[[2429,7]]}}}],["believ",{"_index":3685,"t":{"588":{"position":[[2685,7]]},"754":{"position":[[2359,7]]}}}],["belong",{"_index":2144,"t":{"292":{"position":[[1273,6]]},"354":{"position":[[1233,7]]},"900":{"position":[[982,7]]}}}],["below",{"_index":66,"t":{"8":{"position":[[162,6]]},"21":{"position":[[465,5],[3079,5]]},"23":{"position":[[662,6],[1413,5]]},"25":{"position":[[778,5]]},"31":{"position":[[480,5]]},"43":{"position":[[897,6]]},"67":{"position":[[1532,5]]},"81":{"position":[[254,6]]},"123":{"position":[[182,6]]},"135":{"position":[[688,5]]},"175":{"position":[[973,5],[2052,6]]},"217":{"position":[[214,6],[854,6]]},"219":{"position":[[1121,6]]},"231":{"position":[[262,6]]},"253":{"position":[[3625,6]]},"272":{"position":[[12,5]]},"276":{"position":[[778,5]]},"286":{"position":[[860,5]]},"290":{"position":[[325,5],[821,6]]},"292":{"position":[[127,6]]},"346":{"position":[[40,6]]},"364":{"position":[[831,6]]},"376":{"position":[[895,6]]},"378":{"position":[[190,6]]},"380":{"position":[[161,6]]},"396":{"position":[[1580,6]]},"432":{"position":[[1010,6]]},"434":{"position":[[563,5]]},"448":{"position":[[1234,6]]},"462":{"position":[[948,6]]},"466":{"position":[[117,6]]},"472":{"position":[[101,6]]},"480":{"position":[[949,6]]},"490":{"position":[[353,6],[760,6]]},"494":{"position":[[341,6]]},"508":{"position":[[3068,6]]},"510":{"position":[[944,6],[1154,6],[2656,5]]},"514":{"position":[[282,5]]},"526":{"position":[[1521,5]]},"538":{"position":[[2967,6]]},"540":{"position":[[79,6]]},"550":{"position":[[962,6]]},"554":{"position":[[66,6]]},"568":{"position":[[269,5],[541,6],[941,6]]},"586":{"position":[[940,5]]},"588":{"position":[[845,6]]},"590":{"position":[[150,6]]},"592":{"position":[[486,5]]},"608":{"position":[[173,6]]},"616":{"position":[[187,6]]},"624":{"position":[[261,6]]},"664":{"position":[[1612,5]]},"666":{"position":[[771,5]]},"668":{"position":[[433,5]]},"672":{"position":[[130,6]]},"684":{"position":[[237,6],[984,5],[1491,5],[2572,6]]},"692":{"position":[[1847,5]]},"702":{"position":[[2165,6]]},"708":{"position":[[258,6]]},"714":{"position":[[218,6]]},"716":{"position":[[1760,5],[4143,6],[4568,5],[5603,6]]},"720":{"position":[[5756,6]]},"738":{"position":[[5191,5],[5318,5],[5747,6]]},"740":{"position":[[190,6]]},"746":{"position":[[117,5]]},"760":{"position":[[969,5]]},"766":{"position":[[259,6],[475,5],[953,6]]},"770":{"position":[[514,5],[767,5]]},"772":{"position":[[243,5]]},"778":{"position":[[1425,5]]},"788":{"position":[[202,6]]},"816":{"position":[[499,5]]},"828":{"position":[[45,6]]},"830":{"position":[[887,6],[1231,6]]},"832":{"position":[[713,6]]},"834":{"position":[[4905,6]]},"838":{"position":[[287,5]]},"842":{"position":[[677,6]]},"852":{"position":[[2377,5]]},"858":{"position":[[1841,5]]},"862":{"position":[[294,6],[1114,5]]},"866":{"position":[[246,6]]},"868":{"position":[[411,6]]},"874":{"position":[[117,8]]},"878":{"position":[[1684,5]]},"900":{"position":[[126,6]]},"910":{"position":[[316,6]]},"914":{"position":[[315,5]]},"979":{"position":[[349,6]]},"981":{"position":[[217,6]]},"985":{"position":[[336,6],[785,6]]},"987":{"position":[[393,6]]},"989":{"position":[[390,6]]},"995":{"position":[[417,6]]},"1013":{"position":[[349,6]]},"1015":{"position":[[217,6]]},"1023":{"position":[[166,5]]},"1027":{"position":[[9713,6]]},"1029":{"position":[[262,5]]},"1035":{"position":[[1315,6],[2491,6]]}}}],["below.preferred_editors=(nano",{"_index":4485,"t":{"738":{"position":[[3050,29]]}}}],["below:befor",{"_index":2427,"t":{"366":{"position":[[1434,13]]}}}],["ben",{"_index":4082,"t":{"670":{"position":[[3223,3]]},"939":{"position":[[643,3]]}}}],["beneath",{"_index":5345,"t":{"987":{"position":[[120,8],[713,7]]}}}],["benefit",{"_index":1835,"t":{"241":{"position":[[139,7]]},"414":{"position":[[277,9]]},"422":{"position":[[179,7]]},"540":{"position":[[267,7]]},"566":{"position":[[326,8]]},"790":{"position":[[342,7]]},"818":{"position":[[156,8]]},"943":{"position":[[57,7]]}}}],["benign.",{"_index":4783,"t":{"804":{"position":[[2592,11]]}}}],["ber",{"_index":792,"t":{"47":{"position":[[4563,3]]}}}],["best",{"_index":176,"t":{"17":{"position":[[278,4]]},"69":{"position":[[289,4],[418,4],[567,4]]},"243":{"position":[[518,4]]},"330":{"position":[[285,4]]},"568":{"position":[[601,4]]},"584":{"position":[[2242,4]]},"588":{"position":[[2726,4]]},"738":{"position":[[2657,4]]},"876":{"position":[[276,4]]},"939":{"position":[[281,4]]},"945":{"position":[[15,4]]}}}],["better",{"_index":790,"t":{"47":{"position":[[4544,6],[5828,6]]},"189":{"position":[[1587,6]]},"211":{"position":[[592,6]]},"255":{"position":[[1727,8]]},"260":{"position":[[944,6]]},"342":{"position":[[1180,6]]},"344":{"position":[[1717,7]]},"366":{"position":[[1663,7]]},"376":{"position":[[1544,7]]},"422":{"position":[[1767,6]]},"528":{"position":[[831,6]]},"534":{"position":[[1707,6]]},"540":{"position":[[729,6],[759,6]]},"564":{"position":[[666,6]]},"588":{"position":[[1410,6],[2851,6]]},"596":{"position":[[1727,6]]},"610":{"position":[[1057,6]]},"734":{"position":[[3590,6]]},"820":{"position":[[539,6]]},"862":{"position":[[494,6]]},"876":{"position":[[1291,6]]},"896":{"position":[[3139,6]]},"951":{"position":[[39,6]]},"953":{"position":[[37,6]]},"995":{"position":[[384,6]]},"997":{"position":[[521,7]]}}}],["better!thursday",{"_index":1933,"t":{"255":{"position":[[1831,15]]}}}],["between",{"_index":252,"t":{"21":{"position":[[1370,7]]},"47":{"position":[[3474,7]]},"67":{"position":[[27,7]]},"75":{"position":[[55,7],[109,7]]},"95":{"position":[[804,7]]},"189":{"position":[[1203,7]]},"276":{"position":[[376,7]]},"288":{"position":[[544,7]]},"292":{"position":[[347,7]]},"296":{"position":[[475,7]]},"342":{"position":[[611,8],[1621,7],[1756,7]]},"344":{"position":[[950,7],[4419,7]]},"350":{"position":[[1689,7]]},"354":{"position":[[92,7]]},"364":{"position":[[2447,7]]},"374":{"position":[[440,7]]},"396":{"position":[[1949,7],[2158,7]]},"482":{"position":[[511,7]]},"510":{"position":[[766,7],[2596,7]]},"518":{"position":[[170,7]]},"584":{"position":[[821,7]]},"630":{"position":[[225,7]]},"650":{"position":[[129,7]]},"670":{"position":[[1502,7]]},"698":{"position":[[382,7]]},"708":{"position":[[706,7]]},"720":{"position":[[7910,7]]},"734":{"position":[[3680,7]]},"744":{"position":[[194,7]]},"816":{"position":[[234,7],[968,7],[1070,7]]},"818":{"position":[[596,8]]},"828":{"position":[[286,7],[488,7],[603,7],[937,7],[1154,7],[1267,7]]},"830":{"position":[[673,7]]},"890":{"position":[[833,7],[2065,7]]},"908":{"position":[[428,7]]},"931":{"position":[[81,7]]},"943":{"position":[[488,7]]},"1019":{"position":[[485,7]]},"1033":{"position":[[839,7]]}}}],["beverag",{"_index":4758,"t":{"804":{"position":[[2153,9]]},"806":{"position":[[3209,9]]}}}],["beyond",{"_index":1847,"t":{"241":{"position":[[1026,6]]},"698":{"position":[[113,6]]},"886":{"position":[[63,6]]},"914":{"position":[[395,6]]}}}],["bg",{"_index":659,"t":{"45":{"position":[[197,3]]},"231":{"position":[[1261,2],[1410,2]]},"233":{"position":[[462,2]]},"235":{"position":[[834,2]]},"243":{"position":[[341,2],[1398,2]]}}}],["bg_black=$(tput",{"_index":4336,"t":{"720":{"position":[[1097,15]]}}}],["bg_blue=$(tput",{"_index":4345,"t":{"720":{"position":[[1264,14]]}}}],["bg_cyan=$(tput",{"_index":4349,"t":{"720":{"position":[[1349,14]]}}}],["bg_green=$(tput",{"_index":4341,"t":{"720":{"position":[[1179,15]]}}}],["bg_magenta=$(tput",{"_index":4347,"t":{"720":{"position":[[1305,17]]}}}],["bg_red=$(tput",{"_index":4339,"t":{"720":{"position":[[1139,13]]}}}],["bg_white=$(tput",{"_index":4351,"t":{"720":{"position":[[1390,15]]}}}],["bg_yellow=$(tput",{"_index":4343,"t":{"720":{"position":[[1221,16]]}}}],["bgo",{"_index":4622,"t":{"766":{"position":[[1983,3]]}}}],["big",{"_index":1140,"t":{"77":{"position":[[4283,3]]},"99":{"position":[[1396,3]]},"219":{"position":[[1582,3]]},"251":{"position":[[1488,3]]},"386":{"position":[[1394,3]]},"584":{"position":[[1034,3]]}}}],["biggest",{"_index":5060,"t":{"876":{"position":[[615,7]]}}}],["bill",{"_index":5124,"t":{"894":{"position":[[627,5]]},"896":{"position":[[3083,6]]}}}],["bin",{"_index":1373,"t":{"105":{"position":[[454,4]]},"119":{"position":[[311,4]]},"125":{"position":[[1149,4]]},"217":{"position":[[353,4],[419,4],[633,4],[664,4],[696,4],[923,3],[1100,3]]},"300":{"position":[[585,5]]},"406":{"position":[[809,4]]},"556":{"position":[[348,3],[428,3],[840,3]]},"808":{"position":[[856,3]]}}}],["bin/bash",{"_index":3293,"t":{"506":{"position":[[2001,9]]},"648":{"position":[[575,9]]}}}],["bin/bashyour",{"_index":3169,"t":{"480":{"position":[[971,13]]}}}],["bin/bin",{"_index":1692,"t":{"217":{"position":[[330,8]]}}}],["bin/bin/bin/fwupdtool/bin/gnom",{"_index":1710,"t":{"217":{"position":[[1323,32]]}}}],["bin/cat",{"_index":2215,"t":{"310":{"position":[[697,8]]}}}],["bin/echo",{"_index":2192,"t":{"304":{"position":[[1213,9]]}}}],["bin/l",{"_index":2209,"t":{"308":{"position":[[287,7]]},"328":{"position":[[223,7],[305,7]]}}}],["bin/ls/bin/l",{"_index":2222,"t":{"314":{"position":[[135,14]]}}}],["bin/sh",{"_index":3886,"t":{"648":{"position":[[1219,7]]}}}],["bin/sh/bin/sh",{"_index":5260,"t":{"931":{"position":[[165,15]]}}}],["bin/shecho",{"_index":2169,"t":{"302":{"position":[[83,13]]}}}],["bin/zsh",{"_index":3561,"t":{"568":{"position":[[573,10]]},"648":{"position":[[2156,10],[2265,10]]},"878":{"position":[[1772,8]]}}}],["binari",{"_index":2587,"t":{"390":{"position":[[287,6]]},"438":{"position":[[156,7]]},"472":{"position":[[321,8]]},"558":{"position":[[666,8],[1007,8],[1362,8]]},"568":{"position":[[920,6],[1025,6],[1170,6]]},"574":{"position":[[1359,7],[2540,6],[2610,6],[2745,7]]},"808":{"position":[[1198,8]]}}}],["binary\"fi",{"_index":3565,"t":{"568":{"position":[[1095,9]]}}}],["binary.shell_regex=\"([^/]+$)\"if",{"_index":3587,"t":{"574":{"position":[[1255,31]]}}}],["bind",{"_index":660,"t":{"45":{"position":[[201,5]]},"826":{"position":[[218,8]]},"834":{"position":[[3421,4],[3875,4]]}}}],["bindingc",{"_index":4920,"t":{"842":{"position":[[586,8]]}}}],["bindkey",{"_index":661,"t":{"45":{"position":[[207,8]]},"181":{"position":[[16,7],[75,11]]}}}],["bindrwxr",{"_index":1706,"t":{"217":{"position":[[1032,8]]}}}],["bingo",{"_index":2966,"t":{"450":{"position":[[2342,5]]}}}],["binlrwxrwxrwx",{"_index":1695,"t":{"217":{"position":[[883,13]]}}}],["bird",{"_index":2867,"t":{"446":{"position":[[650,4],[3678,4]]},"450":{"position":[[304,4]]},"456":{"position":[[738,4]]}}}],["bit",{"_index":102,"t":{"10":{"position":[[158,3]]},"33":{"position":[[753,3]]},"71":{"position":[[1266,3]]},"107":{"position":[[1268,3]]},"123":{"position":[[389,4]]},"129":{"position":[[715,3]]},"133":{"position":[[1166,3]]},"219":{"position":[[1271,3],[1340,4]]},"221":{"position":[[258,3]]},"253":{"position":[[257,3]]},"276":{"position":[[830,3]]},"280":{"position":[[1255,3],[1262,4]]},"298":{"position":[[209,3]]},"300":{"position":[[49,3]]},"302":{"position":[[293,3]]},"330":{"position":[[429,5]]},"332":{"position":[[433,3]]},"340":{"position":[[196,4],[341,3],[398,3]]},"376":{"position":[[2221,3],[2228,4]]},"386":{"position":[[1643,3]]},"408":{"position":[[85,3]]},"428":{"position":[[500,5]]},"484":{"position":[[1495,3]]},"534":{"position":[[25,3]]},"554":{"position":[[1156,3]]},"572":{"position":[[137,3]]},"684":{"position":[[3093,3]]},"720":{"position":[[627,3],[634,4]]},"732":{"position":[[1438,3]]},"738":{"position":[[2904,3],[6320,3]]},"756":{"position":[[519,3]]},"760":{"position":[[2715,3]]},"774":{"position":[[561,3]]},"987":{"position":[[227,3]]}}}],["bitbucket",{"_index":3998,"t":{"664":{"position":[[92,9]]},"666":{"position":[[54,9]]},"678":{"position":[[1258,9]]}}}],["black",{"_index":1974,"t":{"257":{"position":[[5169,5]]},"716":{"position":[[1881,8],[2223,6],[2245,6],[2721,8]]}}}],["blank",{"_index":1997,"t":{"260":{"position":[[666,5],[732,5],[774,5],[1007,5]]},"508":{"position":[[4105,5]]},"686":{"position":[[363,5]]},"892":{"position":[[924,5]]}}}],["blat",{"_index":1374,"t":{"105":{"position":[[468,7]]}}}],["bloat",{"_index":1249,"t":{"91":{"position":[[2069,8]]}}}],["block",{"_index":1812,"t":{"235":{"position":[[443,8]]},"243":{"position":[[421,5]]},"518":{"position":[[734,6]]},"538":{"position":[[3554,5]]},"558":{"position":[[2284,5]]},"760":{"position":[[842,5],[875,5]]},"989":{"position":[[135,5],[724,5]]},"991":{"position":[[122,6]]}}}],["blog",{"_index":1735,"t":{"221":{"position":[[362,4]]},"736":{"position":[[1116,5]]},"840":{"position":[[913,4]]}}}],["blow",{"_index":1139,"t":{"77":{"position":[[4124,4],[4132,5]]},"378":{"position":[[3138,4],[3146,5]]},"416":{"position":[[248,4],[256,4]]},"424":{"position":[[312,4],[320,5]]},"804":{"position":[[1273,4],[1281,5]]}}}],["blue",{"_index":2147,"t":{"292":{"position":[[1420,5]]},"340":{"position":[[527,4]]},"716":{"position":[[2024,7],[2470,5],[2485,6],[2871,7],[4614,4]]},"896":{"position":[[1046,4]]}}}],["blur",{"_index":1321,"t":{"95":{"position":[[839,4]]}}}],["board",{"_index":4121,"t":{"684":{"position":[[3186,6]]}}}],["bob",{"_index":5082,"t":{"888":{"position":[[169,6],[212,3],[631,6]]},"890":{"position":[[177,4],[191,3],[655,3],[1784,3]]}}}],["bob'",{"_index":5089,"t":{"890":{"position":[[579,5]]}}}],["bodi",{"_index":1751,"t":{"225":{"position":[[312,6]]},"868":{"position":[[679,4]]}}}],["body>~3",{"_index":4225,"t":{"704":{"position":[[1941,10]]},"927":{"position":[[1894,10]]}}}],["branch_nam",{"_index":3992,"t":{"660":{"position":[[1829,13]]},"702":{"position":[[2498,14],[3018,13]]}}}],["branch}...\\n",{"_index":4018,"t":{"666":{"position":[[1121,15]]}}}],["break",{"_index":530,"t":{"31":{"position":[[572,8]]},"45":{"position":[[216,6]]},"175":{"position":[[1419,6]]},"257":{"position":[[2075,5],[6278,8],[6601,8]]},"338":{"position":[[477,6]]},"344":{"position":[[4870,6]]},"348":{"position":[[115,5]]},"352":{"position":[[3046,5]]},"354":{"position":[[768,5],[791,5]]},"372":{"position":[[1399,6]]},"396":{"position":[[430,5]]},"416":{"position":[[234,5]]},"426":{"position":[[132,5]]},"448":{"position":[[766,5]]},"454":{"position":[[2318,5]]},"602":{"position":[[103,5],[626,5],[1000,6],[1036,6],[1154,5]]},"626":{"position":[[6635,5]]},"708":{"position":[[1586,5]]},"738":{"position":[[3405,5]]},"800":{"position":[[619,8]]},"802":{"position":[[1523,5],[2624,5],[2990,5]]},"816":{"position":[[430,5]]},"840":{"position":[[431,5]]},"842":{"position":[[247,5]]},"1019":{"position":[[107,6]]}}}],["break\"^h",{"_index":1552,"t":{"181":{"position":[[210,9]]}}}],["breakdown",{"_index":2303,"t":{"340":{"position":[[1133,9]]},"424":{"position":[[1039,9]]}}}],["breaksw",{"_index":662,"t":{"45":{"position":[[223,8]]}}}],["breviti",{"_index":4825,"t":{"808":{"position":[[458,8]]}}}],["brew",{"_index":1094,"t":{"71":{"position":[[1524,4],[1556,4]]},"107":{"position":[[428,4]]},"189":{"position":[[1707,4]]}}}],["brew/usr/local/bin/brew",{"_index":2240,"t":{"320":{"position":[[190,23]]}}}],["brian",{"_index":2133,"t":{"292":{"position":[[292,5]]},"392":{"position":[[818,5]]},"844":{"position":[[101,5]]},"963":{"position":[[10,5]]},"969":{"position":[[122,5]]}}}],["brief",{"_index":2346,"t":{"352":{"position":[[437,5],[637,5]]}}}],["briefli",{"_index":1170,"t":{"83":{"position":[[660,7]]},"217":{"position":[[12,7]]},"227":{"position":[[643,7]]},"259":{"position":[[65,7]]},"262":{"position":[[2046,7],[2524,7]]},"386":{"position":[[1061,7]]},"442":{"position":[[13,7]]},"602":{"position":[[3,7]]}}}],["bright",{"_index":4281,"t":{"716":{"position":[[2215,7],[2281,7],[2338,7],[2399,7],[2462,7],[2521,7],[2584,7],[2643,7],[3192,8],[4381,8]]},"720":{"position":[[2116,8],[2238,6]]}}}],["bring",{"_index":1808,"t":{"233":{"position":[[45,5],[295,5],[347,5]]},"239":{"position":[[330,5]]},"690":{"position":[[102,5]]},"692":{"position":[[1516,6]]},"698":{"position":[[701,5]]},"742":{"position":[[1030,5]]},"794":{"position":[[132,5]]}}}],["broad",{"_index":2355,"t":{"352":{"position":[[1129,5]]}}}],["broken",{"_index":2040,"t":{"274":{"position":[[820,6]]},"596":{"position":[[805,6]]},"906":{"position":[[509,6]]}}}],["brought",{"_index":1292,"t":{"93":{"position":[[1157,7]]}}}],["brown",{"_index":45,"t":{"4":{"position":[[477,7]]},"506":{"position":[[578,5]]},"716":{"position":[[2833,8]]}}}],["brows",{"_index":1059,"t":{"69":{"position":[[4341,8]]},"810":{"position":[[779,8]]}}}],["browser",{"_index":577,"t":{"37":{"position":[[173,7],[500,7]]},"99":{"position":[[493,8],[650,7]]},"225":{"position":[[373,7],[1092,8]]},"231":{"position":[[1116,8]]},"654":{"position":[[4041,7]]},"666":{"position":[[544,8],[1960,8]]},"754":{"position":[[2264,7]]},"810":{"position":[[822,7]]},"896":{"position":[[2654,7]]},"985":{"position":[[132,8],[907,8]]}}}],["bruce",{"_index":5244,"t":{"912":{"position":[[626,5]]},"939":{"position":[[245,5]]}}}],["bsd",{"_index":600,"t":{"41":{"position":[[90,3]]},"45":{"position":[[110,3]]},"47":{"position":[[1208,3],[2103,3],[2685,3],[3089,3],[4115,3],[4449,3],[5026,3],[6197,3],[6682,3],[7062,3]]},"67":{"position":[[195,3]]},"189":{"position":[[1349,3],[1914,3]]},"304":{"position":[[2204,3]]},"390":{"position":[[79,3]]},"864":{"position":[[856,3]]}}}],["bucket",{"_index":2475,"t":{"376":{"position":[[700,7],[792,7]]}}}],["buffer",{"_index":2593,"t":{"390":{"position":[[378,9]]},"764":{"position":[[236,7],[246,6],[340,6],[360,6],[441,6],[536,6],[588,6],[1327,6]]},"766":{"position":[[143,8],[584,7],[618,7],[999,6],[1239,7]]},"768":{"position":[[329,6]]},"770":{"position":[[734,6]]},"772":{"position":[[160,7],[754,6],[1199,6]]},"778":{"position":[[493,7]]},"987":{"position":[[479,7],[580,7]]}}}],["buffermot",{"_index":4669,"t":{"778":{"position":[[514,13]]}}}],["buffersc",{"_index":4916,"t":{"842":{"position":[[323,8]]}}}],["bug",{"_index":2234,"t":{"316":{"position":[[165,3]]},"366":{"position":[[1258,4],[1631,3]]},"586":{"position":[[765,4]]},"718":{"position":[[1258,3]]}}}],["bugzilla@redhat.com",{"_index":4575,"t":{"760":{"position":[[136,22]]}}}],["build",{"_index":7,"t":{"4":{"position":[[67,5]]},"69":{"position":[[651,5]]},"95":{"position":[[890,5],[1000,5],[1858,5],[1958,5]]},"195":{"position":[[1767,8],[2029,5]]},"203":{"position":[[7,5]]},"205":{"position":[[903,5]]},"207":{"position":[[486,5]]},"213":{"position":[[1099,5]]},"219":{"position":[[2143,5]]},"225":{"position":[[34,8]]},"255":{"position":[[2087,5]]},"260":{"position":[[274,5],[1211,5]]},"290":{"position":[[1651,8]]},"306":{"position":[[650,8]]},"338":{"position":[[218,5],[621,5],[835,5]]},"340":{"position":[[25,8]]},"344":{"position":[[3300,5]]},"376":{"position":[[1996,8],[2464,8],[3222,8]]},"384":{"position":[[656,5]]},"414":{"position":[[412,5]]},"416":{"position":[[127,5]]},"456":{"position":[[315,5]]},"462":{"position":[[10,6],[290,5]]},"470":{"position":[[538,5],[1301,5]]},"474":{"position":[[75,5]]},"562":{"position":[[188,8]]},"570":{"position":[[1595,5],[1641,6],[1674,7],[1793,7]]},"588":{"position":[[1171,5]]},"600":{"position":[[526,6],[642,5]]},"618":{"position":[[9,8],[391,5]]},"672":{"position":[[299,8]]},"682":{"position":[[1815,5]]},"716":{"position":[[5779,5]]},"718":{"position":[[308,5]]},"720":{"position":[[5813,5],[7868,5]]},"736":{"position":[[747,5]]},"760":{"position":[[2940,9],[3410,8]]},"762":{"position":[[15,5]]},"810":{"position":[[282,8]]},"816":{"position":[[811,6]]},"880":{"position":[[80,8],[208,5]]},"935":{"position":[[181,8]]},"943":{"position":[[294,5]]},"951":{"position":[[385,8]]}}}],["built",{"_index":395,"t":{"23":{"position":[[1829,5]]},"45":{"position":[[444,5],[896,5]]},"77":{"position":[[754,5]]},"79":{"position":[[506,5]]},"93":{"position":[[1585,5]]},"255":{"position":[[1292,5]]},"260":{"position":[[433,5]]},"288":{"position":[[341,5]]},"298":{"position":[[61,6]]},"304":{"position":[[187,5],[1818,5]]},"314":{"position":[[116,5]]},"356":{"position":[[333,5]]},"394":{"position":[[611,5]]},"474":{"position":[[538,5]]},"480":{"position":[[584,5]]},"510":{"position":[[19,5]]},"524":{"position":[[111,5],[499,5]]},"594":{"position":[[1512,5]]},"720":{"position":[[4377,5]]},"794":{"position":[[165,5]]},"808":{"position":[[59,5]]},"890":{"position":[[1963,5]]},"1027":{"position":[[9205,5]]}}}],["builtin",{"_index":655,"t":{"45":{"position":[[61,10],[153,8],[232,9]]},"298":{"position":[[96,8]]},"304":{"position":[[159,7],[226,8],[493,8],[827,8],[914,7],[958,9],[1053,8],[1330,7],[1379,7],[1457,7],[1714,7],[1843,8],[1971,9],[2149,9],[2247,8],[2283,7],[2371,9],[2788,8],[2914,9]]},"310":{"position":[[360,8]]},"322":{"position":[[16,7]]},"484":{"position":[[541,7]]},"536":{"position":[[1555,8]]}}}],["builtin(1)nam",{"_index":657,"t":{"45":{"position":[[138,14]]},"304":{"position":[[2232,14]]}}}],["builtinbuiltin(1",{"_index":2197,"t":{"304":{"position":[[2186,17]]}}}],["builtinecho",{"_index":2191,"t":{"304":{"position":[[1198,11]]}}}],["builtins.html",{"_index":2196,"t":{"304":{"position":[[2061,13]]}}}],["bulk",{"_index":666,"t":{"45":{"position":[[308,4],[737,4]]},"266":{"position":[[1374,4]]},"290":{"position":[[1530,4]]},"985":{"position":[[619,4]]}}}],["bullet",{"_index":2482,"t":{"376":{"position":[[2393,6]]},"658":{"position":[[896,8]]},"764":{"position":[[879,8]]},"778":{"position":[[835,6]]}}}],["bunch",{"_index":267,"t":{"21":{"position":[[1674,5]]},"272":{"position":[[299,5]]},"282":{"position":[[2530,5]]},"300":{"position":[[666,5]]},"402":{"position":[[25,5]]}}}],["bundl",{"_index":1266,"t":{"91":{"position":[[2777,6]]}}}],["button",{"_index":324,"t":{"21":{"position":[[3524,7]]},"61":{"position":[[208,6]]},"654":{"position":[[1867,6]]},"658":{"position":[[569,6],[1113,7]]},"894":{"position":[[1306,6]]},"896":{"position":[[1060,6],[1828,6],[2457,6],[2587,7]]}}}],["buy",{"_index":3259,"t":{"500":{"position":[[365,4]]}}}],["byte",{"_index":781,"t":{"47":{"position":[[4302,4]]},"77":{"position":[[4419,5]]},"253":{"position":[[1558,4]]},"658":{"position":[[1634,5]]}}}],["bΓΌschel",{"_index":16,"t":{"4":{"position":[[170,9]]}}}],["c",{"_index":206,"t":{"21":{"position":[[317,1],[326,1],[332,1],[1979,2],[2145,1],[2907,1],[3771,1]]},"23":{"position":[[1763,1]]},"37":{"position":[[323,1]]},"47":{"position":[[3026,1],[3170,1],[3691,1],[4627,1],[4803,1],[7530,1]]},"91":{"position":[[820,4],[836,5],[1447,2],[1470,2],[1681,3],[1689,2]]},"247":{"position":[[601,2]]},"253":{"position":[[1738,2],[3243,1],[3508,1]]},"262":{"position":[[2905,2]]},"278":{"position":[[612,1]]},"280":{"position":[[508,2]]},"288":{"position":[[1047,1]]},"314":{"position":[[160,3]]},"400":{"position":[[1289,2],[1321,1],[1495,1]]},"406":{"position":[[539,1]]},"424":{"position":[[192,1],[537,1]]},"426":{"position":[[333,1]]},"430":{"position":[[363,1]]},"438":{"position":[[386,1]]},"442":{"position":[[431,1]]},"450":{"position":[[4082,1],[4252,1],[4436,1],[4694,1]]},"454":{"position":[[1431,1],[2340,1]]},"458":{"position":[[947,1],[963,1],[1096,1]]},"464":{"position":[[1925,1]]},"484":{"position":[[983,1]]},"498":{"position":[[54,1],[114,1],[131,1]]},"512":{"position":[[381,1],[1030,1]]},"522":{"position":[[248,2]]},"574":{"position":[[692,1],[1820,1],[2235,1]]},"588":{"position":[[2461,1]]},"590":{"position":[[44,2],[47,4],[111,2],[640,1]]},"602":{"position":[[716,1],[984,3],[1024,3]]},"612":{"position":[[785,1]]},"616":{"position":[[1006,1]]},"634":{"position":[[2290,1],[2394,1]]},"722":{"position":[[2585,2],[2798,1]]},"760":{"position":[[1823,4],[2065,1],[2081,2],[2084,2],[2220,2]]},"764":{"position":[[1151,2],[2502,2]]},"766":{"position":[[873,2],[876,2]]},"770":{"position":[[929,2],[1240,2],[1243,2]]},"772":{"position":[[309,1]]},"778":{"position":[[2053,2]]},"796":{"position":[[1053,1],[1079,1],[1105,1]]},"802":{"position":[[2115,1]]},"806":{"position":[[931,3],[1032,1],[1080,1],[3175,1],[3762,1],[3835,1]]},"808":{"position":[[1249,1],[1930,1]]},"828":{"position":[[1025,1]]},"830":{"position":[[1076,2]]},"834":{"position":[[1744,2],[1960,1],[1974,1],[3445,1],[3493,1],[3766,1]]},"842":{"position":[[120,1],[124,1],[151,1],[183,1]]},"850":{"position":[[975,1],[1010,1]]},"858":{"position":[[1156,1]]},"868":{"position":[[793,2]]},"906":{"position":[[442,3]]},"912":{"position":[[622,1]]},"939":{"position":[[241,1],[400,1]]}}}],["c\"alia",{"_index":390,"t":{"23":{"position":[[1729,7]]}}}],["c2wchang",{"_index":4645,"t":{"772":{"position":[[817,9]]}}}],["c:\\program",{"_index":1121,"t":{"77":{"position":[[3208,11],[3435,10]]}}}],["c>add",{"_index":4677,"t":{"778":{"position":[[2056,5]]}}}],["c>command",{"_index":4587,"t":{"760":{"position":[[2223,9]]}}}],["c>enter",{"_index":4597,"t":{"764":{"position":[[1154,7],[2505,7]]},"770":{"position":[[811,7],[932,7],[1063,7]]},"772":{"position":[[871,7]]},"776":{"position":[[721,7],[857,7]]}}}],["c>open",{"_index":4636,"t":{"770":{"position":[[1139,6]]}}}],["c[num",{"_index":2586,"t":{"390":{"position":[[253,7]]}}}],["ca",{"_index":4661,"t":{"776":{"position":[[174,3]]}}}],["ca\"chang",{"_index":4666,"t":{"776":{"position":[[797,9]]}}}],["cabl",{"_index":2090,"t":{"282":{"position":[[1476,5]]},"292":{"position":[[1074,5]]}}}],["cach",{"_index":2706,"t":{"400":{"position":[[1865,6]]},"682":{"position":[[1279,6],[2739,6],[3197,6],[3332,6]]},"704":{"position":[[874,6]]},"927":{"position":[[827,6]]}}}],["cal",{"_index":2621,"t":{"394":{"position":[[1136,3],[2056,3]]},"396":{"position":[[405,3]]}}}],["calcul",{"_index":3348,"t":{"510":{"position":[[3022,12]]},"524":{"position":[[810,10]]}}}],["call",{"_index":52,"t":{"8":{"position":[[42,4],[352,5]]},"21":{"position":[[787,6]]},"23":{"position":[[1564,4]]},"37":{"position":[[807,6]]},"47":{"position":[[403,5],[464,5],[2478,6],[2629,6],[2696,5],[2911,4],[2988,6],[3701,5],[3874,6],[3888,6],[7763,6]]},"49":{"position":[[907,4]]},"61":{"position":[[123,6]]},"71":{"position":[[600,6]]},"79":{"position":[[408,6]]},"81":{"position":[[2121,6]]},"99":{"position":[[1207,4],[1463,6],[1792,5],[1978,4]]},"115":{"position":[[134,6]]},"119":{"position":[[540,4]]},"121":{"position":[[240,4],[1435,6]]},"123":{"position":[[409,4]]},"129":{"position":[[669,6]]},"141":{"position":[[622,6]]},"185":{"position":[[259,6]]},"213":{"position":[[598,5]]},"257":{"position":[[217,4],[282,5],[4708,6],[4916,6],[6474,6]]},"262":{"position":[[729,6],[888,6],[1047,6]]},"266":{"position":[[333,6]]},"278":{"position":[[65,6],[872,5],[954,4],[982,4]]},"280":{"position":[[525,5],[1101,5]]},"288":{"position":[[266,7],[875,6]]},"298":{"position":[[91,4]]},"302":{"position":[[30,6]]},"310":{"position":[[341,5]]},"352":{"position":[[974,6]]},"354":{"position":[[1431,4]]},"384":{"position":[[239,4]]},"418":{"position":[[37,6]]},"420":{"position":[[183,6],[284,6],[381,6],[609,6]]},"428":{"position":[[1419,6]]},"430":{"position":[[557,6],[698,6]]},"434":{"position":[[465,6]]},"446":{"position":[[2169,6],[2449,6]]},"452":{"position":[[31,6]]},"462":{"position":[[561,6]]},"464":{"position":[[969,6]]},"466":{"position":[[348,6],[639,6],[719,6]]},"484":{"position":[[41,6]]},"488":{"position":[[359,6],[517,6]]},"492":{"position":[[346,6]]},"496":{"position":[[585,4]]},"498":{"position":[[41,6]]},"502":{"position":[[107,6]]},"508":{"position":[[611,6]]},"512":{"position":[[650,4]]},"518":{"position":[[274,4],[431,6],[474,4],[517,4],[629,4]]},"522":{"position":[[296,6],[1365,7],[1479,4],[1987,7]]},"526":{"position":[[302,4],[1035,4]]},"534":{"position":[[1298,4]]},"538":{"position":[[368,6]]},"540":{"position":[[916,4],[935,5]]},"544":{"position":[[2139,6]]},"550":{"position":[[778,6]]},"574":{"position":[[795,6]]},"580":{"position":[[339,6]]},"584":{"position":[[241,6]]},"598":{"position":[[627,4]]},"626":{"position":[[4179,6],[6188,4]]},"628":{"position":[[176,6]]},"634":{"position":[[110,6]]},"654":{"position":[[3583,6],[3789,6]]},"656":{"position":[[141,4],[407,4],[1213,6]]},"658":{"position":[[860,6]]},"668":{"position":[[1041,7]]},"670":{"position":[[2115,7]]},"688":{"position":[[408,6]]},"692":{"position":[[147,6],[487,6]]},"704":{"position":[[1210,6],[1281,6],[1330,6]]},"720":{"position":[[1769,6],[5346,7]]},"736":{"position":[[1338,6]]},"738":{"position":[[198,4],[1763,4]]},"744":{"position":[[2027,4]]},"748":{"position":[[805,6]]},"756":{"position":[[484,6]]},"760":{"position":[[2554,7],[2594,6]]},"764":{"position":[[227,6],[2340,6]]},"766":{"position":[[38,6]]},"806":{"position":[[644,6],[2942,6]]},"808":{"position":[[1222,4]]},"858":{"position":[[783,4],[1213,6],[1278,4]]},"870":{"position":[[725,6]]},"884":{"position":[[632,6]]},"890":{"position":[[1902,6]]},"908":{"position":[[642,6]]},"912":{"position":[[782,6]]},"927":{"position":[[1163,6],[1234,6],[1283,6]]},"1033":{"position":[[3460,6]]}}}],["call.echo",{"_index":3370,"t":{"520":{"position":[[446,9]]}}}],["came",{"_index":143,"t":{"13":{"position":[[301,4]]},"963":{"position":[[56,4]]}}}],["can't",{"_index":1564,"t":{"183":{"position":[[287,5]]},"259":{"position":[[30,5]]},"282":{"position":[[2098,5]]},"316":{"position":[[183,5]]},"898":{"position":[[1298,5]]}}}],["cancel",{"_index":269,"t":{"21":{"position":[[1934,6]]},"165":{"position":[[902,6]]},"173":{"position":[[0,6]]},"602":{"position":[[592,6],[992,7]]},"858":{"position":[[2097,8]]}}}],["cancel.\"for",{"_index":3741,"t":{"602":{"position":[[721,11]]}}}],["cancel.long",{"_index":4982,"t":{"858":{"position":[[2736,11]]}}}],["capabl",{"_index":625,"t":{"41":{"position":[[875,10]]},"378":{"position":[[100,12]]},"618":{"position":[[102,12]]},"664":{"position":[[1957,12]]},"734":{"position":[[1316,11]]},"816":{"position":[[400,13]]}}}],["cape",{"_index":4785,"t":{"804":{"position":[[2622,4]]},"806":{"position":[[3495,4]]}}}],["capit",{"_index":3189,"t":{"484":{"position":[[1461,12]]}}}],["capitalis",{"_index":2708,"t":{"400":{"position":[[1917,12]]}}}],["captur",{"_index":2338,"t":{"348":{"position":[[74,7],[92,7],[362,7],[563,7],[651,7],[737,7]]},"350":{"position":[[358,7],[468,7],[715,8],[959,7],[1111,8]]},"352":{"position":[[90,7],[2505,7]]},"354":{"position":[[476,7],[570,7],[633,7]]},"356":{"position":[[150,7]]},"374":{"position":[[623,7]]},"376":{"position":[[3997,8],[4074,7],[4398,7],[4627,7],[4831,7]]},"386":{"position":[[728,7],[816,7],[847,7]]},"532":{"position":[[46,7],[333,7]]},"568":{"position":[[796,7],[1341,7]]}}}],["card",{"_index":2037,"t":{"274":{"position":[[179,5]]},"282":{"position":[[658,6]]},"390":{"position":[[1815,4]]},"894":{"position":[[530,4],[622,4],[661,4]]}}}],["care",{"_index":305,"t":{"21":{"position":[[2853,5]]},"47":{"position":[[1439,5]]},"105":{"position":[[347,7]]},"352":{"position":[[1239,7]]},"372":{"position":[[1326,7]]},"378":{"position":[[4439,7],[4955,7]]},"426":{"position":[[479,7]]},"484":{"position":[[1552,5]]},"534":{"position":[[154,8]]},"542":{"position":[[407,7]]},"584":{"position":[[508,4]]},"586":{"position":[[721,7]]},"610":{"position":[[717,7]]},"626":{"position":[[6620,7]]},"644":{"position":[[175,7]]},"648":{"position":[[2285,7]]},"668":{"position":[[2176,7]]},"700":{"position":[[2178,7]]},"708":{"position":[[1520,7]]},"720":{"position":[[6556,7]]},"738":{"position":[[6292,7]]},"850":{"position":[[1500,7]]},"862":{"position":[[994,7],[1570,7]]},"894":{"position":[[791,7]]},"1033":{"position":[[3680,7]]}}}],["carefulli",{"_index":2251,"t":{"336":{"position":[[308,10]]},"910":{"position":[[608,9]]}}}],["caret",{"_index":2443,"t":{"372":{"position":[[28,5]]},"626":{"position":[[3381,5]]},"989":{"position":[[4,5],[114,6],[141,6],[203,6],[318,5]]}}}],["caretstyl",{"_index":5354,"t":{"989":{"position":[[688,10]]}}}],["caretstyle='block'>(word",{"_index":5353,"t":{"989":{"position":[[488,34],[592,34]]}}}],["carriag",{"_index":4241,"t":{"714":{"position":[[738,8]]}}}],["cascad",{"_index":3473,"t":{"538":{"position":[[1976,9]]}}}],["case",{"_index":196,"t":{"21":{"position":[[155,5],[3258,4]]},"29":{"position":[[950,5]]},"45":{"position":[[242,5]]},"47":{"position":[[1839,5]]},"53":{"position":[[308,5]]},"71":{"position":[[836,6]]},"91":{"position":[[1062,6]]},"95":{"position":[[1174,4]]},"105":{"position":[[883,4]]},"119":{"position":[[504,5]]},"179":{"position":[[587,5]]},"195":{"position":[[1919,5],[2182,5],[2440,4],[2768,5]]},"199":{"position":[[755,4]]},"201":{"position":[[64,4],[343,4],[389,4]]},"215":{"position":[[690,4]]},"217":{"position":[[402,5]]},"229":{"position":[[180,5]]},"231":{"position":[[81,4]]},"235":{"position":[[613,6]]},"239":{"position":[[405,5]]},"247":{"position":[[525,4]]},"253":{"position":[[1473,5],[2880,4]]},"255":{"position":[[480,4],[685,4],[2279,5]]},"257":{"position":[[816,4]]},"262":{"position":[[3518,6]]},"282":{"position":[[1572,4]]},"332":{"position":[[690,5]]},"338":{"position":[[88,5],[967,5]]},"342":{"position":[[150,6]]},"344":{"position":[[3807,4]]},"350":{"position":[[1472,4]]},"352":{"position":[[957,5],[2220,4]]},"360":{"position":[[68,5]]},"368":{"position":[[1337,5]]},"376":{"position":[[2436,4]]},"378":{"position":[[4923,4]]},"380":{"position":[[407,4]]},"384":{"position":[[480,4],[488,4]]},"390":{"position":[[1651,4]]},"396":{"position":[[526,4]]},"398":{"position":[[88,4]]},"402":{"position":[[1249,4]]},"404":{"position":[[49,4]]},"410":{"position":[[322,4]]},"442":{"position":[[1509,4]]},"446":{"position":[[1621,4]]},"448":{"position":[[1691,4]]},"450":{"position":[[588,4]]},"458":{"position":[[1062,4]]},"470":{"position":[[1038,5]]},"488":{"position":[[1350,4]]},"502":{"position":[[362,4]]},"506":{"position":[[2245,4]]},"522":{"position":[[1982,4],[2078,5]]},"538":{"position":[[2722,4]]},"550":{"position":[[1082,4]]},"552":{"position":[[448,4]]},"556":{"position":[[451,4]]},"566":{"position":[[606,4],[1581,4]]},"570":{"position":[[209,4],[846,4]]},"572":{"position":[[77,4],[117,4],[194,4],[356,6],[1228,4]]},"588":{"position":[[1383,6],[2139,6],[2706,4],[2825,4]]},"596":{"position":[[1057,4],[2085,4]]},"600":{"position":[[1673,4]]},"628":{"position":[[8,5]]},"634":{"position":[[2074,4]]},"636":{"position":[[966,6]]},"642":{"position":[[256,5],[319,5]]},"654":{"position":[[3644,4]]},"664":{"position":[[668,4]]},"682":{"position":[[243,4]]},"690":{"position":[[571,4]]},"692":{"position":[[40,4]]},"714":{"position":[[1472,4]]},"720":{"position":[[2640,4],[2819,4],[5621,4]]},"734":{"position":[[1714,4]]},"740":{"position":[[228,4],[348,4]]},"772":{"position":[[504,5],[1552,4]]},"798":{"position":[[504,4]]},"834":{"position":[[4321,4]]},"850":{"position":[[2087,4]]},"858":{"position":[[1608,4]]},"860":{"position":[[1025,4],[1271,4],[1973,4],[2128,4]]},"862":{"position":[[2442,5]]},"864":{"position":[[417,4]]},"868":{"position":[[850,4]]},"874":{"position":[[239,4]]},"894":{"position":[[677,4]]},"900":{"position":[[526,4]]},"1027":{"position":[[1245,4],[2107,5],[2544,4],[3439,5],[8419,4]]},"1033":{"position":[[1790,4],[2613,5]]},"1035":{"position":[[2656,4]]}}}],["cast",{"_index":5330,"t":{"985":{"position":[[869,4]]}}}],["cat",{"_index":426,"t":{"25":{"position":[[506,3],[682,3],[813,3],[1268,3]]},"29":{"position":[[272,3],[575,3]]},"31":{"position":[[2694,3]]},"121":{"position":[[77,3],[167,3],[420,3],[568,3],[881,3],[903,3],[1000,3],[1166,3],[1347,3]]},"125":{"position":[[1158,3],[1288,3]]},"251":{"position":[[20,3],[112,3],[536,3]]},"253":{"position":[[993,3],[1063,3],[1258,3],[2157,3]]},"255":{"position":[[1788,3]]},"257":{"position":[[4092,3]]},"259":{"position":[[120,3]]},"260":{"position":[[286,3],[566,3]]},"288":{"position":[[44,3]]},"300":{"position":[[76,3],[135,3],[195,3],[434,3],[452,4]]},"364":{"position":[[135,3]]},"378":{"position":[[877,3]]},"394":{"position":[[713,3]]},"532":{"position":[[836,3]]},"648":{"position":[[808,3]]},"738":{"position":[[5976,3]]},"802":{"position":[[2378,3]]},"804":{"position":[[2029,3]]},"806":{"position":[[3080,3]]},"862":{"position":[[1964,3],[2166,3]]},"908":{"position":[[3073,3]]},"927":{"position":[[1986,3]]}}}],["catalina",{"_index":1084,"t":{"71":{"position":[[340,9]]}}}],["catastroph",{"_index":2351,"t":{"352":{"position":[[981,13],[2766,12]]}}}],["catcat",{"_index":2214,"t":{"310":{"position":[[687,6]]}}}],["catch",{"_index":1268,"t":{"91":{"position":[[3017,5]]},"538":{"position":[[3563,7]]}}}],["caught",{"_index":3118,"t":{"464":{"position":[[2236,6]]}}}],["caus",{"_index":469,"t":{"27":{"position":[[578,5]]},"219":{"position":[[1539,5]]},"237":{"position":[[556,5],[826,7]]},"288":{"position":[[1399,5]]},"292":{"position":[[1371,5]]},"352":{"position":[[1064,5]]},"482":{"position":[[750,7]]},"490":{"position":[[518,5]]},"508":{"position":[[5096,6]]},"534":{"position":[[1350,5]]},"536":{"position":[[48,6],[129,6]]},"538":{"position":[[1163,5],[2611,5]]},"540":{"position":[[854,5]]},"588":{"position":[[430,5]]},"610":{"position":[[767,5]]},"626":{"position":[[6690,5],[7400,5]]},"638":{"position":[[293,5]]},"738":{"position":[[1963,5]]},"740":{"position":[[520,6]]},"796":{"position":[[1481,5]]},"850":{"position":[[667,5]]},"856":{"position":[[240,5]]},"858":{"position":[[2029,5],[2676,5]]},"1033":{"position":[[49,5],[561,5],[2745,5]]},"1037":{"position":[[609,5]]}}}],["caution",{"_index":1382,"t":{"105":{"position":[[1102,8]]},"211":{"position":[[178,8]]},"237":{"position":[[626,7]]},"262":{"position":[[3401,8]]},"1033":{"position":[[3896,8]]}}}],["cc",{"_index":2053,"t":{"276":{"position":[[984,2]]},"282":{"position":[[828,2],[845,2],[1291,2]]},"772":{"position":[[1343,2]]}}}],["ccgl7",{"_index":2937,"t":{"450":{"position":[[1098,5]]}}}],["ccgl7filebeat",{"_index":2959,"t":{"450":{"position":[[1674,13],[2149,13],[2658,13]]}}}],["cd",{"_index":663,"t":{"45":{"position":[[248,3],[559,2]]},"77":{"position":[[1271,2],[1370,2],[1417,2],[1459,2]]},"81":{"position":[[491,2],[812,2],[2255,2]]},"83":{"position":[[450,2]]},"111":{"position":[[173,2],[213,2]]},"133":{"position":[[110,2],[732,2],[995,2],[1047,2]]},"135":{"position":[[1110,2],[1185,2]]},"137":{"position":[[941,2],[984,2],[1374,2]]},"139":{"position":[[408,2],[601,2],[1028,2],[1106,2]]},"143":{"position":[[61,2],[98,2]]},"145":{"position":[[350,2],[772,2],[911,2]]},"189":{"position":[[398,2]]},"225":{"position":[[955,2]]},"288":{"position":[[314,2]]},"304":{"position":[[1526,2],[1644,2]]},"364":{"position":[[123,2]]},"374":{"position":[[151,2]]},"400":{"position":[[1562,2]]},"462":{"position":[[476,2]]},"502":{"position":[[651,2],[671,2]]},"538":{"position":[[1020,2],[1494,2],[1510,3]]},"570":{"position":[[188,2]]},"626":{"position":[[1412,2],[1819,4],[2052,2],[2404,2],[2440,2],[2483,2],[2539,2]]},"680":{"position":[[921,2]]},"688":{"position":[[2712,2]]},"708":{"position":[[1114,2]]},"718":{"position":[[1345,2]]},"738":{"position":[[4988,4],[5142,3],[5223,4],[5350,3],[5851,2],[5896,2],[6088,2],[6202,2]]},"746":{"position":[[1535,2]]},"762":{"position":[[660,2]]},"870":{"position":[[224,2]]},"892":{"position":[[395,2]]},"908":{"position":[[1490,2]]},"927":{"position":[[38,2],[63,2],[66,2]]},"1025":{"position":[[118,2]]}}}],["cd'.safe_set",{"_index":4506,"t":{"738":{"position":[[5049,13]]}}}],["cdable_var",{"_index":3825,"t":{"626":{"position":[[2414,11]]},"738":{"position":[[5122,11],[6026,11]]}}}],["cdbuiltin(1",{"_index":656,"t":{"45":{"position":[[97,12]]}}}],["cdcd",{"_index":668,"t":{"45":{"position":[[553,5]]}}}],["cdexit",{"_index":2619,"t":{"394":{"position":[[780,6]]}}}],["cdpath",{"_index":4515,"t":{"738":{"position":[[6164,6]]}}}],["cdpath=\"~:~/repo",{"_index":4510,"t":{"738":{"position":[[5424,18]]}}}],["cdspell",{"_index":3827,"t":{"626":{"position":[[2492,7]]},"738":{"position":[[5008,7],[5861,7]]}}}],["cdwget",{"_index":1357,"t":{"99":{"position":[[1093,6],[1798,6]]}}}],["ceas",{"_index":2363,"t":{"352":{"position":[[2715,7]]}}}],["celciu",{"_index":3346,"t":{"510":{"position":[[2823,8],[2851,11]]}}}],["celciusfahrenheit",{"_index":3345,"t":{"510":{"position":[[2801,21]]}}}],["celsiu",{"_index":3343,"t":{"510":{"position":[[2741,7],[2790,8],[2871,7]]}}}],["centr",{"_index":2092,"t":{"282":{"position":[[1630,7]]}}}],["central",{"_index":2136,"t":{"292":{"position":[[457,7]]}}}],["certain",{"_index":166,"t":{"17":{"position":[[133,7]]},"21":{"position":[[1442,7]]},"79":{"position":[[259,7]]},"284":{"position":[[576,7]]},"308":{"position":[[39,7]]},"346":{"position":[[1033,7]]},"354":{"position":[[459,7]]},"370":{"position":[[253,7]]},"422":{"position":[[513,7]]},"512":{"position":[[126,7]]},"514":{"position":[[750,7]]},"550":{"position":[[77,7]]},"552":{"position":[[66,7]]},"594":{"position":[[56,7]]},"608":{"position":[[1978,7]]},"670":{"position":[[1225,7]]},"702":{"position":[[154,7]]},"714":{"position":[[49,7]]},"738":{"position":[[7693,7]]},"854":{"position":[[479,7]]},"858":{"position":[[130,7]]},"866":{"position":[[696,7]]},"876":{"position":[[730,7]]},"1027":{"position":[[4724,7],[7171,7]]}}}],["certainli",{"_index":1651,"t":{"205":{"position":[[1349,9]]},"272":{"position":[[159,9]]},"588":{"position":[[819,9]]},"670":{"position":[[1570,9]]},"818":{"position":[[85,9]]},"876":{"position":[[766,9]]}}}],["cf",{"_index":3804,"t":{"624":{"position":[[766,4]]},"734":{"position":[[2726,4]]}}}],["chacon",{"_index":4081,"t":{"670":{"position":[[3212,6]]},"939":{"position":[[632,6]]}}}],["chain",{"_index":441,"t":{"25":{"position":[[1129,7]]},"33":{"position":[[382,7]]},"260":{"position":[[1399,5]]},"284":{"position":[[1339,5]]},"288":{"position":[[1727,5],[2322,5]]},"404":{"position":[[664,7]]},"452":{"position":[[746,8]]},"570":{"position":[[8,7],[222,7],[1223,8]]}}}],["challeng",{"_index":1183,"t":{"87":{"position":[[533,10]]},"237":{"position":[[79,11],[1173,9],[1333,10]]},"290":{"position":[[2766,11]]},"304":{"position":[[2900,10]]},"464":{"position":[[11,9]]},"534":{"position":[[1762,9]]},"670":{"position":[[650,10]]},"876":{"position":[[435,11],[527,12]]},"886":{"position":[[239,10]]},"912":{"position":[[324,10]]},"943":{"position":[[436,10]]}}}],["chanc",{"_index":961,"t":{"53":{"position":[[499,7]]},"77":{"position":[[267,6]]},"492":{"position":[[700,6]]}}}],["chang",{"_index":396,"t":{"23":{"position":[[1852,7]]},"41":{"position":[[566,8]]},"45":{"position":[[588,6],[624,6]]},"47":{"position":[[2757,6],[4591,8]]},"59":{"position":[[128,7]]},"63":{"position":[[396,7]]},"69":{"position":[[1445,6],[1690,6],[2712,8]]},"71":{"position":[[138,8],[372,7],[532,6],[1059,7],[1826,7],[1937,7],[2125,6],[2209,7]]},"77":{"position":[[1386,6],[3634,7]]},"81":{"position":[[497,7],[600,6],[1916,6],[2072,6],[2258,7]]},"83":{"position":[[461,7]]},"87":{"position":[[600,7],[686,7]]},"89":{"position":[[33,7]]},"95":{"position":[[335,7]]},"105":{"position":[[527,6]]},"111":{"position":[[133,6],[219,6]]},"113":{"position":[[793,6]]},"129":{"position":[[793,6]]},"133":{"position":[[750,6],[1002,6]]},"137":{"position":[[993,6]]},"145":{"position":[[353,7],[372,7]]},"157":{"position":[[50,7],[109,7],[180,6]]},"159":{"position":[[177,7]]},"175":{"position":[[901,6],[2186,8]]},"211":{"position":[[288,7]]},"219":{"position":[[889,7],[993,8]]},"243":{"position":[[1860,6]]},"266":{"position":[[597,7]]},"288":{"position":[[317,7],[1286,6]]},"290":{"position":[[1086,7]]},"302":{"position":[[342,7]]},"304":{"position":[[1537,7],[1610,6]]},"330":{"position":[[372,7]]},"336":{"position":[[1299,6]]},"342":{"position":[[546,6]]},"350":{"position":[[1483,7]]},"364":{"position":[[702,6],[762,6]]},"366":{"position":[[161,7],[398,6],[1194,7],[1275,7],[1371,8],[1394,7],[1677,6]]},"376":{"position":[[778,6],[829,6],[5398,6],[6454,6],[6607,7]]},"380":{"position":[[82,6]]},"420":{"position":[[746,6]]},"428":{"position":[[429,6],[567,7]]},"434":{"position":[[295,6],[671,7],[828,7],[886,7],[945,6],[1285,7],[1354,8]]},"446":{"position":[[1578,7],[2749,7]]},"448":{"position":[[1205,8]]},"462":{"position":[[2140,6]]},"482":{"position":[[1487,8]]},"488":{"position":[[1108,6]]},"494":{"position":[[224,6]]},"512":{"position":[[188,8],[596,8],[1269,7]]},"520":{"position":[[209,7]]},"522":{"position":[[2008,7]]},"534":{"position":[[679,6]]},"544":{"position":[[419,8],[990,6]]},"558":{"position":[[848,6]]},"586":{"position":[[1778,7]]},"588":{"position":[[1478,6]]},"600":{"position":[[1684,7]]},"610":{"position":[[303,7],[489,6],[730,8],[890,6],[1001,8]]},"612":{"position":[[452,7],[892,6],[1237,6]]},"626":{"position":[[707,6],[770,6],[3281,8]]},"640":{"position":[[619,6],[1697,6]]},"648":{"position":[[619,6],[658,6],[1148,7],[1174,6],[1345,8],[1406,6],[1474,6],[1630,6],[1691,8],[2216,6],[2298,8]]},"654":{"position":[[18,7],[290,7],[1500,7],[2840,7],[3837,8],[3962,7],[4102,7],[4299,8]]},"656":{"position":[[70,7],[630,8],[683,7],[906,7]]},"658":{"position":[[26,8],[86,7],[171,7],[251,8],[366,8],[388,6],[1393,8],[1874,8],[1909,7],[1941,7],[2291,7],[2334,7]]},"660":{"position":[[59,7],[332,8],[499,8],[1520,7],[1678,7]]},"662":{"position":[[214,8]]},"664":{"position":[[923,7],[1081,7],[1180,7],[1351,7],[1490,8],[1846,8],[2068,8]]},"668":{"position":[[908,8],[1138,6],[1642,8],[2749,7],[3172,7]]},"670":{"position":[[673,7],[1213,7],[1494,7],[1644,8],[1810,8],[1843,7],[1904,7],[2245,7],[2355,7],[2433,7],[2881,8]]},"672":{"position":[[255,6],[656,6],[719,6],[937,7],[1005,7],[1054,7]]},"674":{"position":[[102,8],[508,8]]},"678":{"position":[[118,7],[190,8],[291,7],[378,7],[591,8]]},"680":{"position":[[81,7],[279,7]]},"682":{"position":[[223,7],[678,7],[888,7],[1548,7],[1595,7],[1689,8],[1713,7],[1756,8],[1833,7],[2538,8],[2933,6],[3128,7],[3167,7],[3212,7],[3514,7],[3562,7],[3651,7],[3786,7]]},"684":{"position":[[34,7],[90,7],[287,8],[413,7],[627,8],[751,8],[877,7],[1608,8],[1734,7],[1941,7],[1976,8],[2689,8]]},"686":{"position":[[277,7],[847,6],[916,7],[1140,7]]},"688":{"position":[[160,7],[1445,8],[2720,6],[3090,8]]},"690":{"position":[[74,7],[144,7],[412,8]]},"692":{"position":[[603,6],[887,8],[949,7],[1530,7],[1746,6],[1995,8],[2296,7]]},"694":{"position":[[2362,7]]},"696":{"position":[[131,7],[234,7],[389,7],[468,8],[749,7],[905,7],[1070,7],[1100,7],[1610,6],[1768,7],[2080,7],[2209,7],[2542,7],[2795,7],[3263,7],[3414,7],[3500,7],[3639,6],[4016,7],[4132,7],[4224,6],[4374,7],[4933,8]]},"698":{"position":[[69,7]]},"700":{"position":[[1241,7],[1634,6],[1672,6],[1737,7],[1817,7],[1909,7],[2055,7],[2234,7],[2616,7],[2767,7],[3044,7],[3402,8],[3619,7],[3819,7]]},"704":{"position":[[148,8],[164,8],[363,7],[420,7],[467,7],[1141,7],[1392,6]]},"708":{"position":[[1031,6],[1191,6],[1247,7],[1287,7],[1602,8]]},"714":{"position":[[2386,6],[2463,8],[2812,8],[3387,9]]},"716":{"position":[[3372,7],[4237,6],[4284,6],[4806,8]]},"718":{"position":[[1320,6],[1418,7],[1733,7]]},"720":{"position":[[1538,7],[1571,6],[5325,6],[5932,7],[7561,7],[7903,6]]},"722":{"position":[[1054,6],[1622,6],[2952,6]]},"732":{"position":[[1732,6]]},"734":{"position":[[3609,7],[3744,8]]},"736":{"position":[[1491,7]]},"738":{"position":[[12,8],[1860,6]]},"742":{"position":[[1013,7]]},"748":{"position":[[430,7],[664,7],[1038,8]]},"762":{"position":[[390,7],[490,7]]},"764":{"position":[[1904,8],[2052,7],[2275,7],[2643,8]]},"772":{"position":[[311,6],[1145,7],[1346,6]]},"774":{"position":[[1047,6]]},"776":{"position":[[84,6],[206,7],[343,7]]},"778":{"position":[[1351,7],[1563,7]]},"796":{"position":[[1755,7]]},"834":{"position":[[2072,6],[2167,6],[2796,6]]},"836":{"position":[[223,8]]},"870":{"position":[[58,6],[381,6],[476,6],[680,6]]},"876":{"position":[[83,7]]},"898":{"position":[[1874,7]]},"900":{"position":[[1400,7],[1538,6]]},"908":{"position":[[3426,7]]},"927":{"position":[[195,6],[1094,7],[1345,6]]},"987":{"position":[[487,6],[588,6]]},"1019":{"position":[[212,6]]},"1033":{"position":[[3113,8],[3475,7],[3627,6],[3693,8],[3728,6],[3883,7],[4328,6]]}}}],["changelog",{"_index":5261,"t":{"935":{"position":[[4,9],[54,9],[114,9]]},"937":{"position":[[4,9]]}}}],["chapter",{"_index":402,"t":{"23":{"position":[[2066,7],[2187,7]]},"31":{"position":[[604,10],[2376,7]]},"33":{"position":[[8,7]]},"45":{"position":[[834,7]]},"53":{"position":[[8,7]]},"59":{"position":[[255,8]]},"87":{"position":[[569,8]]},"99":{"position":[[776,7],[993,7]]},"105":{"position":[[496,7]]},"111":{"position":[[76,7]]},"125":{"position":[[8,7]]},"137":{"position":[[723,8]]},"141":{"position":[[142,8]]},"145":{"position":[[8,7]]},"147":{"position":[[506,8]]},"175":{"position":[[2303,9],[2634,7],[2892,7]]},"183":{"position":[[276,8]]},"185":{"position":[[52,7]]},"189":{"position":[[1853,7],[1945,7]]},"195":{"position":[[2802,8]]},"207":{"position":[[473,7]]},"209":{"position":[[588,7]]},"211":{"position":[[575,7],[812,7]]},"219":{"position":[[1979,7]]},"221":{"position":[[8,7],[227,7],[538,7]]},"225":{"position":[[730,8]]},"227":{"position":[[654,7]]},"241":{"position":[[1295,9]]},"243":{"position":[[774,7],[1102,8],[1722,9]]},"249":{"position":[[593,7],[1263,7]]},"251":{"position":[[349,7]]},"253":{"position":[[2256,7]]},"255":{"position":[[1245,7],[2173,7]]},"257":{"position":[[3123,7]]},"259":{"position":[[15,8]]},"260":{"position":[[114,8]]},"262":{"position":[[129,7],[344,8],[471,8],[517,7],[2430,8],[2927,8],[3013,8],[3267,7]]},"268":{"position":[[225,7]]},"284":{"position":[[1132,7],[1237,7],[1495,7]]},"288":{"position":[[479,7]]},"302":{"position":[[1595,8]]},"304":{"position":[[2102,7]]},"326":{"position":[[423,9]]},"330":{"position":[[220,10]]},"332":{"position":[[642,7]]},"348":{"position":[[786,7]]},"354":{"position":[[278,7]]},"356":{"position":[[310,7]]},"364":{"position":[[2146,7]]},"366":{"position":[[2743,8]]},"376":{"position":[[5824,7],[5866,8]]},"378":{"position":[[1174,8]]},"384":{"position":[[511,8]]},"386":{"position":[[8,7]]},"390":{"position":[[1207,7]]},"394":{"position":[[394,7],[1829,7]]},"406":{"position":[[49,8]]},"420":{"position":[[678,7],[785,8],[1274,7]]},"424":{"position":[[894,8],[995,8],[1177,8]]},"428":{"position":[[714,8]]},"436":{"position":[[344,7]]},"440":{"position":[[8,7],[390,7]]},"442":{"position":[[1893,8]]},"446":{"position":[[1166,7]]},"448":{"position":[[1438,8]]},"450":{"position":[[2421,8]]},"456":{"position":[[3,7]]},"458":{"position":[[8,7]]},"462":{"position":[[1056,7],[1160,7],[1363,7]]},"464":{"position":[[139,8],[328,12],[640,9],[655,9],[670,9],[1259,10],[1276,10],[1293,10],[1313,7]]},"472":{"position":[[418,7]]},"474":{"position":[[8,7],[395,7],[476,7]]},"480":{"position":[[166,7]]},"482":{"position":[[1542,7]]},"490":{"position":[[2098,7]]},"492":{"position":[[838,7]]},"502":{"position":[[1065,8],[1132,7],[1418,7]]},"512":{"position":[[104,7]]},"514":{"position":[[8,7],[219,7],[661,7]]},"526":{"position":[[762,7],[2120,7]]},"530":{"position":[[644,8]]},"534":{"position":[[1915,7]]},"536":{"position":[[413,7]]},"538":{"position":[[127,7]]},"544":{"position":[[16,7],[860,7]]},"546":{"position":[[8,7],[184,7]]},"556":{"position":[[634,7]]},"568":{"position":[[223,7],[1387,7]]},"574":{"position":[[122,8],[348,8]]},"576":{"position":[[8,7],[138,7]]},"580":{"position":[[442,8]]},"582":{"position":[[3,7]]},"584":{"position":[[782,7],[2723,8]]},"588":{"position":[[230,7],[2674,8],[2898,7]]},"596":{"position":[[1712,7]]},"600":{"position":[[1106,7]]},"604":{"position":[[24,7],[1481,8]]},"606":{"position":[[30,7],[136,7]]},"608":{"position":[[516,7],[595,8],[1015,7]]},"612":{"position":[[16,7]]},"614":{"position":[[8,7],[286,7]]},"616":{"position":[[589,7],[1057,7]]},"622":{"position":[[120,8]]},"624":{"position":[[1041,7]]},"626":{"position":[[640,7],[1597,7],[3941,7]]},"628":{"position":[[423,7]]},"634":{"position":[[2102,7]]},"642":{"position":[[498,7]]},"650":{"position":[[8,7],[377,7]]},"654":{"position":[[3378,7]]},"668":{"position":[[3,7],[278,8]]},"670":{"position":[[53,8],[1018,8]]},"672":{"position":[[21,8]]},"674":{"position":[[8,7]]},"680":{"position":[[445,7]]},"682":{"position":[[3468,8]]},"704":{"position":[[8,7],[267,7],[554,7]]},"710":{"position":[[530,7]]},"714":{"position":[[1969,7],[2137,8],[2163,7],[3436,7],[3703,7]]},"716":{"position":[[28,7],[5765,7]]},"718":{"position":[[263,7]]},"720":{"position":[[2572,7],[4716,7],[7101,9],[8321,7]]},"724":{"position":[[713,7]]},"726":{"position":[[8,7],[398,7]]},"728":{"position":[[98,8],[415,8],[513,7]]},"744":{"position":[[982,7]]},"748":{"position":[[8,7],[356,7],[1169,7]]},"754":{"position":[[674,7],[1927,7]]},"762":{"position":[[465,7]]},"764":{"position":[[1636,8]]},"790":{"position":[[8,7],[247,7],[451,7]]},"796":{"position":[[1915,7]]},"800":{"position":[[1344,8],[1561,8]]},"808":{"position":[[683,7],[1083,7],[1489,8]]},"812":{"position":[[8,7]]},"816":{"position":[[575,8],[746,8]]},"818":{"position":[[1803,7]]},"820":{"position":[[399,7]]},"834":{"position":[[413,7],[456,7]]},"838":{"position":[[613,7],[2012,7]]},"840":{"position":[[807,7]]},"846":{"position":[[8,7]]},"850":{"position":[[2144,7],[2332,7],[2340,7]]},"854":{"position":[[134,7]]},"862":{"position":[[3204,7]]},"866":{"position":[[13,7]]},"874":{"position":[[590,7]]},"876":{"position":[[1037,7]]},"880":{"position":[[8,7]]},"886":{"position":[[158,8]]},"894":{"position":[[247,8],[713,7]]},"902":{"position":[[1566,7]]},"906":{"position":[[562,7],[1067,7]]},"908":{"position":[[1284,7]]},"910":{"position":[[66,8]]},"912":{"position":[[8,7],[415,7],[684,7]]},"914":{"position":[[89,7],[173,8],[355,8]]},"973":{"position":[[15,7]]},"995":{"position":[[111,7]]},"1027":{"position":[[350,7],[5517,7],[9387,7],[9423,7],[10460,7]]},"1031":{"position":[[280,7]]},"1035":{"position":[[3252,7],[3417,7]]},"1037":{"position":[[8,7],[645,7]]}}}],["char",{"_index":751,"t":{"47":{"position":[[2834,4],[4677,4],[4709,4]]},"181":{"position":[[155,4]]}}}],["char\"^d",{"_index":1548,"t":{"181":{"position":[[139,8]]}}}],["char\"^g",{"_index":1551,"t":{"181":{"position":[[196,8]]}}}],["char\"^i",{"_index":1553,"t":{"181":{"position":[[236,8]]}}}],["charact",{"_index":231,"t":{"21":{"position":[[910,10],[2369,10],[3849,9]]},"25":{"position":[[396,10]]},"29":{"position":[[197,10]]},"47":{"position":[[6778,9]]},"77":{"position":[[1483,9],[1503,9]]},"139":{"position":[[264,10]]},"183":{"position":[[415,10]]},"195":{"position":[[536,10],[584,10],[622,10],[647,13]]},"213":{"position":[[78,10]]},"247":{"position":[[471,9]]},"253":{"position":[[1544,9]]},"257":{"position":[[482,11],[2031,10]]},"259":{"position":[[254,11],[544,10]]},"308":{"position":[[54,11]]},"340":{"position":[[75,10],[136,10],[215,10],[377,10],[433,10],[763,9]]},"342":{"position":[[454,10],[563,10],[757,10],[1456,9],[1581,11],[1608,10],[1642,10],[1689,10],[1737,10],[1798,10],[1831,9]]},"344":{"position":[[45,10],[86,10],[115,9],[222,9],[409,9],[613,10],[672,10],[783,9],[825,9],[851,9],[979,10],[998,9],[1043,9],[1545,9],[1569,9],[1661,9],[1678,9],[1725,9],[1750,10],[1777,10],[1795,9],[2300,9],[2372,9],[2420,11],[2871,9],[3187,9],[3213,10],[3271,10],[3308,9],[3643,9],[3697,10],[3753,10],[3854,10],[4010,9],[4037,9],[4063,10],[4144,9],[4239,9],[4286,10],[4330,10],[4569,9],[4616,10],[4672,9],[4728,9],[4842,9],[4891,10],[4934,10],[4965,9],[5018,10],[5044,9],[5080,10]]},"350":{"position":[[619,10],[974,10],[1509,9]]},"352":{"position":[[52,9],[1527,11]]},"356":{"position":[[122,11]]},"366":{"position":[[4137,11],[4174,9]]},"368":{"position":[[1436,10],[1469,11]]},"376":{"position":[[1145,9],[1174,10],[1623,10],[3578,10],[3722,10]]},"378":{"position":[[1597,9],[3914,10]]},"386":{"position":[[1732,9]]},"396":{"position":[[166,10],[741,9],[1064,12]]},"426":{"position":[[28,9],[530,9],[557,9]]},"430":{"position":[[430,10]]},"436":{"position":[[116,10]]},"448":{"position":[[45,12],[152,11],[595,11],[657,9],[691,9],[724,10],[847,10],[917,11],[969,11],[1171,9],[1214,10],[1529,9],[1716,10],[1757,11],[1799,12],[1853,9],[1922,9],[2023,10]]},"450":{"position":[[622,9],[1488,9],[3021,10],[4084,12],[4117,10],[4304,9],[4674,9]]},"452":{"position":[[625,10]]},"458":{"position":[[587,10],[665,10],[968,10],[1030,10],[1067,10],[1112,9]]},"464":{"position":[[1631,10],[1677,9]]},"468":{"position":[[2511,10],[2990,10],[3158,11]]},"472":{"position":[[374,9]]},"474":{"position":[[600,9]]},"486":{"position":[[211,10],[337,9]]},"496":{"position":[[128,10],[554,10],[636,11]]},"498":{"position":[[223,10]]},"500":{"position":[[703,9],[902,9]]},"506":{"position":[[1061,11]]},"508":{"position":[[3170,11],[3711,11],[3756,10],[3915,9],[4006,9],[4480,9],[5153,10]]},"526":{"position":[[2307,9]]},"532":{"position":[[165,10],[189,11]]},"568":{"position":[[746,10]]},"572":{"position":[[1074,9],[1109,11]]},"588":{"position":[[2367,9]]},"600":{"position":[[569,10],[739,10]]},"608":{"position":[[1539,9]]},"610":{"position":[[103,10],[203,10],[222,9],[248,10]]},"640":{"position":[[1277,10]]},"686":{"position":[[153,10],[484,10]]},"688":{"position":[[2771,9]]},"702":{"position":[[2709,10]]},"708":{"position":[[668,9]]},"710":{"position":[[593,10]]},"714":{"position":[[65,10],[99,10],[176,10],[225,10],[264,10],[492,9],[539,11],[722,10],[754,10],[1513,9],[1583,11],[1612,10],[1653,11],[1703,11],[4001,11]]},"716":{"position":[[115,11],[411,10],[510,10],[791,10],[871,10],[1336,10],[3110,10],[3517,10],[5302,10],[5458,10]]},"718":{"position":[[1046,10],[1150,9],[1932,9]]},"720":{"position":[[5049,10]]},"760":{"position":[[925,10]]},"764":{"position":[[807,10]]},"772":{"position":[[345,10]]},"774":{"position":[[89,10],[164,10],[316,10],[368,10],[416,10],[468,10],[616,9],[682,9],[737,9],[797,9],[1120,9],[1365,10]]},"778":{"position":[[748,11],[795,10],[899,10]]},"806":{"position":[[2727,10]]},"862":{"position":[[2185,10],[2267,10]]},"868":{"position":[[774,9]]},"1019":{"position":[[1371,10]]},"1023":{"position":[[138,11],[304,11],[567,11]]},"1025":{"position":[[32,10]]},"1027":{"position":[[3517,10],[3689,10],[3908,10],[7152,10],[7484,10],[8443,10],[8516,10]]},"1033":{"position":[[828,10],[1922,9],[1967,9],[2096,10],[2272,10],[2314,10],[2445,10],[2493,10],[2586,9],[4350,9]]},"1035":{"position":[[84,11],[2001,10],[2044,9],[2111,9],[2145,11],[2223,11],[2274,10]]}}}],["character1",{"_index":3116,"t":{"464":{"position":[[1553,11]]}}}],["characters.txt",{"_index":425,"t":{"25":{"position":[[469,15],[718,14]]},"29":{"position":[[249,14],[611,14]]},"215":{"position":[[323,14]]},"251":{"position":[[572,14]]},"253":{"position":[[1294,14],[1387,14],[1450,14]]},"259":{"position":[[156,14]]}}}],["characters.txt./scripts./scripts/show",{"_index":1580,"t":{"189":{"position":[[447,37]]}}}],["characters.txt./scripts/show",{"_index":1598,"t":{"193":{"position":[[180,28]]}}}],["characters.txt20",{"_index":1679,"t":{"213":{"position":[[321,16]]}}}],["characters.txtarti",{"_index":483,"t":{"29":{"position":[[308,19]]},"251":{"position":[[148,19]]}}}],["chart",{"_index":584,"t":{"37":{"position":[[386,6]]}}}],["chdir",{"_index":664,"t":{"45":{"position":[[252,6]]}}}],["cheat",{"_index":932,"t":{"51":{"position":[[120,6]]},"185":{"position":[[817,5]]},"762":{"position":[[301,5],[405,5]]},"788":{"position":[[340,5]]}}}],["cheat.sh",{"_index":942,"t":{"51":{"position":[[518,8],[698,8]]}}}],["cheatsheet",{"_index":947,"t":{"51":{"position":[[871,10]]},"760":{"position":[[3431,10]]},"762":{"position":[[27,10],[195,10],[647,12],[669,11]]},"764":{"position":[[47,11],[1035,11],[1140,10],[1181,10],[1460,10],[2491,10],[2534,10],[2737,10]]},"766":{"position":[[167,10],[314,12]]},"770":{"position":[[792,10],[1196,10]]},"778":{"position":[[26,10],[187,12],[1040,10]]}}}],["cheatsheet.md",{"_index":4593,"t":{"764":{"position":[[123,14],[143,15],[161,15],[549,13],[1505,13],[2347,13],[2686,13]]},"766":{"position":[[191,13]]}}}],["cheatsheet.md#~~\"~/vim",{"_index":4605,"t":{"764":{"position":[[2088,22]]}}}],["cheatsheet.png",{"_index":5310,"t":{"979":{"position":[[293,17],[418,17]]},"1013":{"position":[[293,17],[418,17]]}}}],["cheatsheet/.git",{"_index":4592,"t":{"762":{"position":[[751,16]]}}}],["cheatsheet/.git/commit_editmsg",{"_index":4606,"t":{"764":{"position":[[2111,31]]}}}],["cheatsheetent",{"_index":4596,"t":{"764":{"position":[[1113,15],[2462,15]]}}}],["cheatsheethello",{"_index":4630,"t":{"770":{"position":[[850,15],[901,15]]}}}],["cheatsheetwelcom",{"_index":4632,"t":{"770":{"position":[[971,17],[1028,17],[1102,17]]}}}],["cheatsheet~~~\"cheatsheet.md",{"_index":4599,"t":{"764":{"position":[[1349,28]]}}}],["check",{"_index":64,"t":{"8":{"position":[[147,5]]},"37":{"position":[[293,8]]},"47":{"position":[[271,5]]},"49":{"position":[[916,8]]},"71":{"position":[[1099,5]]},"99":{"position":[[770,5],[987,5]]},"141":{"position":[[165,5],[267,7]]},"189":{"position":[[1939,5]]},"211":{"position":[[569,5]]},"217":{"position":[[1465,5]]},"219":{"position":[[1200,8]]},"221":{"position":[[532,5]]},"231":{"position":[[1599,6]]},"243":{"position":[[1092,5]]},"249":{"position":[[1257,5]]},"253":{"position":[[341,5]]},"259":{"position":[[96,5]]},"262":{"position":[[3261,5]]},"302":{"position":[[1266,6]]},"354":{"position":[[981,5],[1125,5],[1300,5]]},"368":{"position":[[1798,5]]},"378":{"position":[[2583,9],[3584,5]]},"390":{"position":[[8,5],[1197,5]]},"394":{"position":[[388,5]]},"424":{"position":[[864,5],[949,5],[1074,5]]},"428":{"position":[[681,5]]},"442":{"position":[[1796,5]]},"462":{"position":[[1154,5],[1357,5],[2489,5]]},"550":{"position":[[1853,5],[1938,5]]},"552":{"position":[[50,5],[558,5]]},"556":{"position":[[507,5]]},"558":{"position":[[83,6],[209,5],[2052,5],[2183,5]]},"562":{"position":[[690,6]]},"564":{"position":[[23,5],[112,5],[236,6]]},"566":{"position":[[133,7]]},"568":{"position":[[217,5],[278,5],[1381,5]]},"572":{"position":[[398,5],[469,8]]},"574":{"position":[[152,5],[820,6],[1208,5],[2494,5],[2729,5]]},"586":{"position":[[2279,5]]},"588":{"position":[[224,5],[2892,5]]},"590":{"position":[[484,5]]},"600":{"position":[[1100,5]]},"616":{"position":[[555,5]]},"624":{"position":[[162,5],[1035,5]]},"626":{"position":[[634,5]]},"634":{"position":[[1691,6],[1882,5],[2096,5]]},"648":{"position":[[79,8]]},"654":{"position":[[1367,5]]},"658":{"position":[[1843,6],[2082,7]]},"668":{"position":[[2528,5],[2719,5],[2897,5]]},"682":{"position":[[2611,5]]},"690":{"position":[[224,8]]},"692":{"position":[[463,6]]},"704":{"position":[[1353,5]]},"714":{"position":[[3430,5],[3697,5]]},"720":{"position":[[3568,5],[3958,5],[4006,5],[4812,5]]},"736":{"position":[[570,5]]},"738":{"position":[[1561,5],[3516,5],[3783,5]]},"742":{"position":[[908,5]]},"744":{"position":[[1784,5]]},"748":{"position":[[1092,5],[1134,8]]},"762":{"position":[[459,5]]},"802":{"position":[[13,5],[400,5],[2001,6]]},"806":{"position":[[1533,5],[2372,6]]},"808":{"position":[[1073,5],[2018,5]]},"816":{"position":[[872,5]]},"822":{"position":[[54,5]]},"834":{"position":[[407,5],[852,5]]},"850":{"position":[[2326,5]]},"854":{"position":[[1068,5],[1225,5]]},"862":{"position":[[2662,5],[2956,5],[3198,5],[4400,6]]},"864":{"position":[[228,5],[389,5],[671,6]]},"866":{"position":[[674,6],[808,8]]},"896":{"position":[[2082,5],[2325,7]]},"898":{"position":[[2559,8]]},"900":{"position":[[665,5]]},"914":{"position":[[203,5]]},"918":{"position":[[159,5],[233,8]]},"927":{"position":[[1306,5]]},"991":{"position":[[0,5]]},"1027":{"position":[[9381,5]]},"1035":{"position":[[2730,5],[3071,5],[3411,5]]}}}],["checkbox",{"_index":5139,"t":{"896":{"position":[[2337,9],[2494,8]]}}}],["checkjob",{"_index":3829,"t":{"626":{"position":[[2551,9]]}}}],["checkout",{"_index":2842,"t":{"442":{"position":[[138,8],[272,8],[464,8],[575,8],[792,8],[989,8]]},"660":{"position":[[1289,8]]},"670":{"position":[[1862,8],[2296,9]]},"672":{"position":[[778,8],[865,8]]},"680":{"position":[[1138,8],[1236,8],[1271,8]]},"688":{"position":[[288,8],[455,8],[596,8],[1885,9],[1901,8],[2227,8],[2441,8],[2463,8],[2679,8],[2914,8],[3010,8],[3068,8],[3234,8]]},"690":{"position":[[305,8]]},"692":{"position":[[204,8],[682,8]]},"694":{"position":[[1319,8],[2035,8]]},"696":{"position":[[1382,8],[1654,8],[2971,8],[3776,8],[4443,8],[5686,8]]},"700":{"position":[[760,8],[806,8],[878,8],[1947,8],[2102,8],[2159,8],[2274,8]]},"702":{"position":[[31,8],[104,8],[772,8],[990,8],[1637,8],[1666,8],[2489,8],[2519,8],[2794,8],[3009,8]]},"704":{"position":[[1174,8],[1192,8],[1229,8],[1259,8],[1817,8],[1834,8],[1873,8],[1932,8],[1952,8]]},"927":{"position":[[1127,8],[1145,8],[1182,8],[1212,8],[1770,8],[1787,8],[1826,8],[1885,8],[1905,8]]}}}],["checkouts/gnu/bash/manual/bash.html#job",{"_index":1862,"t":{"243":{"position":[[1541,39]]}}}],["cherri",{"_index":5042,"t":{"868":{"position":[[278,6]]}}}],["cherry4",{"_index":5047,"t":{"868":{"position":[[438,8]]}}}],["cherrythi",{"_index":5052,"t":{"868":{"position":[[510,10]]}}}],["child",{"_index":1624,"t":{"195":{"position":[[2540,5]]},"434":{"position":[[236,5],[1803,5],[2007,5]]},"484":{"position":[[478,5],[664,5],[751,5],[1372,5],[1640,5]]},"636":{"position":[[462,5]]},"646":{"position":[[408,5]]}}}],["children",{"_index":1582,"t":{"189":{"position":[[734,8],[2210,9]]},"211":{"position":[[339,8]]},"636":{"position":[[1238,8]]},"646":{"position":[[87,8]]},"760":{"position":[[215,8]]}}}],["china.naevi",{"_index":4777,"t":{"804":{"position":[[2466,11]]}}}],["chip",{"_index":2137,"t":{"292":{"position":[[494,4]]}}}],["chmod",{"_index":876,"t":{"47":{"position":[[7330,5]]},"219":{"position":[[1152,5],[1298,5],[1419,5],[1488,5]]},"302":{"position":[[336,5],[429,5],[575,5]]},"330":{"position":[[366,5]]},"428":{"position":[[561,5],[606,5],[645,5]]},"438":{"position":[[477,5]]},"538":{"position":[[972,5]]},"558":{"position":[[1023,5],[1156,5]]},"808":{"position":[[809,5],[868,5],[1030,5]]},"900":{"position":[[1304,5],[1394,5]]},"908":{"position":[[2776,5],[3420,5]]}}}],["chmod(1",{"_index":877,"t":{"47":{"position":[[7346,9]]}}}],["choic",{"_index":1007,"t":{"67":{"position":[[565,7]]},"508":{"position":[[1069,6]]},"594":{"position":[[839,6],[911,9]]},"598":{"position":[[426,6],[438,7]]},"602":{"position":[[957,6],[1012,9],[1060,6],[1105,9]]},"616":{"position":[[88,6]]},"722":{"position":[[1173,7],[1260,7]]},"796":{"position":[[2260,6]]},"876":{"position":[[281,6]]},"904":{"position":[[1486,6]]}}}],["choos",{"_index":707,"t":{"47":{"position":[[992,6]]},"69":{"position":[[912,6],[1838,6],[3706,6],[3821,6],[4183,6],[4361,6],[4636,6],[4808,6],[4885,6],[6418,6]]},"147":{"position":[[786,7]]},"249":{"position":[[492,6]]},"266":{"position":[[738,6]]},"386":{"position":[[625,6]]},"450":{"position":[[482,6],[545,6]]},"594":{"position":[[1086,6]]},"602":{"position":[[581,7],[684,6]]},"654":{"position":[[1027,6],[1288,6]]},"658":{"position":[[1043,6]]},"664":{"position":[[789,6]]},"680":{"position":[[1102,6]]},"696":{"position":[[3681,7],[3943,6]]},"754":{"position":[[440,6]]},"828":{"position":[[1092,6]]},"894":{"position":[[395,6]]},"896":{"position":[[362,6],[716,6],[868,6],[1099,6],[1134,6],[1932,6],[2355,6],[2623,6],[2924,6],[3028,6]]}}}],["chop",{"_index":4811,"t":{"806":{"position":[[1880,4]]}}}],["chord",{"_index":1801,"t":{"231":{"position":[[865,5]]},"760":{"position":[[1866,8],[2156,6]]},"770":{"position":[[1246,5]]}}}],["chose",{"_index":3153,"t":{"470":{"position":[[572,5]]},"594":{"position":[[860,5]]},"602":{"position":[[978,5]]},"868":{"position":[[304,6],[461,6],[503,6],[546,6]]}}}],["chosen",{"_index":3309,"t":{"508":{"position":[[4985,7]]},"654":{"position":[[1194,6],[1526,6]]},"680":{"position":[[1383,6]]},"696":{"position":[[3857,6]]},"756":{"position":[[7,6]]},"820":{"position":[[452,6]]}}}],["chown",{"_index":739,"t":{"47":{"position":[[2663,5],[2722,6],[7364,5]]}}}],["chown(2",{"_index":740,"t":{"47":{"position":[[2676,8]]}}}],["chown(2)nam",{"_index":741,"t":{"47":{"position":[[2709,12]]}}}],["chown(8",{"_index":878,"t":{"47":{"position":[[7380,9]]}}}],["chown(const",{"_index":750,"t":{"47":{"position":[[2822,11]]}}}],["chroot",{"_index":4561,"t":{"748":{"position":[[657,6],[691,6],[858,6]]}}}],["chsh",{"_index":1099,"t":{"71":{"position":[[2185,4]]},"648":{"position":[[1143,4],[1211,4],[1567,4]]},"927":{"position":[[190,4]]}}}],["cht.sh",{"_index":593,"t":{"37":{"position":[[901,6]]},"51":{"position":[[359,6],[562,7]]},"53":{"position":[[323,6]]}}}],["cht.sh/\"how",{"_index":953,"t":{"51":{"position":[[1122,11]]}}}],["chui",{"_index":39,"t":{"4":{"position":[[415,6]]}}}],["chunk",{"_index":2368,"t":{"354":{"position":[[828,6]]},"362":{"position":[[225,7]]},"376":{"position":[[4016,6]]},"584":{"position":[[2316,9]]}}}],["ci",{"_index":4663,"t":{"776":{"position":[[339,3]]}}}],["ci)chang",{"_index":4664,"t":{"776":{"position":[[657,9]]}}}],["circumflex",{"_index":2330,"t":{"344":{"position":[[3241,10],[4121,10],[4471,10]]},"346":{"position":[[671,10]]}}}],["circumst",{"_index":576,"t":{"37":{"position":[[131,13]]},"255":{"position":[[2049,13]]},"314":{"position":[[246,12]]},"488":{"position":[[968,13]]},"492":{"position":[[804,12]]},"608":{"position":[[1137,13]]},"640":{"position":[[585,13]]},"798":{"position":[[1642,13]]},"862":{"position":[[1055,14],[1816,12]]}}}],["citizen",{"_index":1201,"t":{"91":{"position":[[369,8]]}}}],["cl",{"_index":2906,"t":{"448":{"position":[[1258,2]]}}}],["clariti",{"_index":3125,"t":{"466":{"position":[[294,8]]}}}],["clash",{"_index":331,"t":{"21":{"position":[[3949,5]]},"540":{"position":[[632,5],[801,5]]},"826":{"position":[[233,5]]},"979":{"position":[[65,5]]},"1013":{"position":[[65,5]]}}}],["class",{"_index":2908,"t":{"448":{"position":[[1539,8],[1740,5],[1782,5],[1863,7],[1932,7]]},"953":{"position":[[58,7]]}}}],["classname=\"languag",{"_index":5351,"t":{"989":{"position":[[433,19],[537,19]]}}}],["claus",{"_index":2364,"t":{"352":{"position":[[2750,6]]}}}],["clean",{"_index":1042,"t":{"69":{"position":[[2356,5]]},"235":{"position":[[254,5]]},"237":{"position":[[753,7]]},"251":{"position":[[1452,5]]},"284":{"position":[[564,8]]},"462":{"position":[[432,5]]},"538":{"position":[[1211,5],[2487,5]]},"588":{"position":[[2800,5]]},"624":{"position":[[73,5]]},"626":{"position":[[6883,5]]},"670":{"position":[[1942,6],[1957,5]]},"684":{"position":[[2998,5]]},"688":{"position":[[569,5]]},"700":{"position":[[3515,5],[3941,5]]},"716":{"position":[[185,5]]},"738":{"position":[[4079,5]]},"804":{"position":[[2814,8]]},"856":{"position":[[40,5]]},"858":{"position":[[1627,5]]},"1027":{"position":[[4997,5],[5236,5],[5303,9]]}}}],["cleaner",{"_index":361,"t":{"23":{"position":[[923,7]]},"544":{"position":[[1614,8]]}}}],["cleanli",{"_index":4837,"t":{"810":{"position":[[490,7]]}}}],["cleanup",{"_index":4954,"t":{"858":{"position":[[241,9],[429,7],[753,7],[948,7],[1007,9],[1220,10],[1727,8]]}}}],["clear",{"_index":705,"t":{"47":{"position":[[884,5]]},"177":{"position":[[56,6]]},"181":{"position":[[298,5]]},"225":{"position":[[1222,5]]},"227":{"position":[[755,5]]},"249":{"position":[[2117,5]]},"422":{"position":[[1440,5]]},"574":{"position":[[3174,5]]},"716":{"position":[[3362,5]]},"722":{"position":[[2803,5]]},"732":{"position":[[821,5]]},"738":{"position":[[897,5]]},"748":{"position":[[995,5]]},"802":{"position":[[530,5]]},"828":{"position":[[1437,5]]},"1027":{"position":[[1372,5]]}}}],["clearer",{"_index":1895,"t":{"249":{"position":[[2317,8]]},"510":{"position":[[3184,7]]},"810":{"position":[[951,7]]}}}],["clearli",{"_index":570,"t":{"33":{"position":[[588,7]]},"344":{"position":[[358,7]]},"434":{"position":[[1497,7]]},"588":{"position":[[935,7]]},"738":{"position":[[1404,7]]}}}],["clever",{"_index":1389,"t":{"107":{"position":[[1277,7]]},"195":{"position":[[2142,6]]},"356":{"position":[[399,6]]},"396":{"position":[[603,6]]},"888":{"position":[[736,6]]}}}],["cli",{"_index":136,"t":{"13":{"position":[[151,3]]},"937":{"position":[[39,3]]},"951":{"position":[[266,3],[406,3]]}}}],["click",{"_index":326,"t":{"21":{"position":[[3694,5]]},"57":{"position":[[950,5]]},"61":{"position":[[189,8]]},"103":{"position":[[193,5]]},"133":{"position":[[68,8]]},"205":{"position":[[1563,7]]},"810":{"position":[[1267,5]]},"914":{"position":[[69,7]]}}}],["clickrick",{"_index":2086,"t":{"282":{"position":[[1269,9]]}}}],["client",{"_index":525,"t":{"31":{"position":[[321,6]]},"241":{"position":[[922,6]]},"818":{"position":[[1422,7],[1478,6]]},"888":{"position":[[586,6]]},"896":{"position":[[2635,8]]},"898":{"position":[[1521,6],[1715,6],[1889,6]]}}}],["clientc",{"_index":4912,"t":{"842":{"position":[[207,7],[478,7],[505,7]]}}}],["climb",{"_index":1419,"t":{"117":{"position":[[644,9]]},"804":{"position":[[2294,8]]},"806":{"position":[[3248,8]]}}}],["climbing6",{"_index":1423,"t":{"117":{"position":[[748,9]]}}}],["clipboard",{"_index":190,"t":{"21":{"position":[[64,9],[577,9],[1759,9],[4119,10]]},"23":{"position":[[41,9],[348,9],[1076,10]]},"25":{"position":[[69,10]]},"27":{"position":[[274,9],[538,9],[652,10],[828,9]]},"29":{"position":[[40,9],[662,9]]},"31":{"position":[[282,9],[823,10],[1927,10],[2075,9],[2522,9],[2744,9],[2829,9],[3164,9]]},"33":{"position":[[207,9],[470,9],[741,9],[852,9]]},"227":{"position":[[677,9]]},"251":{"position":[[372,9]]},"253":{"position":[[2243,9],[2279,9],[2350,10],[2415,10],[2492,9]]},"262":{"position":[[3290,9]]},"654":{"position":[[1910,10]]}}}],["clipboard2",{"_index":1905,"t":{"253":{"position":[[2625,11]]}}}],["clone",{"_index":3994,"t":{"662":{"position":[[238,5],[339,5],[400,5]]},"664":{"position":[[895,5],[1654,5]]},"672":{"position":[[603,5]]}}}],["close",{"_index":273,"t":{"21":{"position":[[2064,8]]},"67":{"position":[[1210,5]]},"93":{"position":[[387,6]]},"175":{"position":[[1437,5]]},"241":{"position":[[771,5]]},"247":{"position":[[646,6]]},"262":{"position":[[1349,5]]},"290":{"position":[[306,5]]},"350":{"position":[[637,7],[809,7],[1166,7],[1248,7],[1534,7]]},"376":{"position":[[376,5]]},"446":{"position":[[3180,5]]},"518":{"position":[[199,7]]},"542":{"position":[[517,5],[644,5]]},"550":{"position":[[419,7]]},"556":{"position":[[903,5]]},"580":{"position":[[1766,6],[1897,6],[1948,6]]},"606":{"position":[[122,5]]},"624":{"position":[[954,6]]},"640":{"position":[[809,7]]},"684":{"position":[[1279,5],[1434,7],[1458,6]]},"696":{"position":[[5200,5]]},"722":{"position":[[2939,7]]},"760":{"position":[[3187,6],[3302,5]]},"776":{"position":[[541,5]]},"778":{"position":[[2168,6]]},"802":{"position":[[2120,5]]},"810":{"position":[[484,5]]},"816":{"position":[[1307,5],[1423,5]]},"828":{"position":[[1775,5]]},"832":{"position":[[185,5],[510,5],[959,6],[1384,5]]},"842":{"position":[[1232,5],[1551,5]]},"850":{"position":[[161,5]]},"898":{"position":[[2840,5]]},"906":{"position":[[738,5]]},"908":{"position":[[2657,5],[2978,8]]},"1027":{"position":[[2566,8]]}}}],["closer",{"_index":1660,"t":{"207":{"position":[[720,6]]},"366":{"position":[[4841,6]]},"584":{"position":[[876,6]]}}}],["cloth",{"_index":4775,"t":{"804":{"position":[[2433,5]]},"806":{"position":[[3379,5]]}}}],["cloud",{"_index":1286,"t":{"93":{"position":[[751,5]]},"95":{"position":[[1568,5]]},"378":{"position":[[5222,5]]},"892":{"position":[[2338,5],[2495,5]]},"894":{"position":[[47,5],[161,5],[1475,6]]}}}],["cloudflar",{"_index":2374,"t":{"356":{"position":[[495,10],[585,10]]}}}],["cloudi",{"_index":4761,"t":{"804":{"position":[[2186,6]]}}}],["clunki",{"_index":3431,"t":{"534":{"position":[[29,7]]}}}],["cluster",{"_index":2922,"t":{"450":{"position":[[809,8]]},"470":{"position":[[159,8],[216,7],[403,7]]}}}],["cmd.ex",{"_index":2121,"t":{"290":{"position":[[1907,7]]}}}],["cmdhist",{"_index":3830,"t":{"626":{"position":[[2631,7]]},"738":{"position":[[6566,7],[7258,7]]}}}],["cobol",{"_index":1221,"t":{"91":{"position":[[1216,5]]}}}],["code",{"_index":65,"t":{"8":{"position":[[157,4]]},"91":{"position":[[2523,5]]},"175":{"position":[[999,4],[1038,4],[1257,4]]},"209":{"position":[[126,4]]},"225":{"position":[[422,4],[483,5],[1121,4],[1575,4]]},"237":{"position":[[791,5],[884,5]]},"243":{"position":[[936,6]]},"253":{"position":[[547,4]]},"257":{"position":[[2818,4]]},"290":{"position":[[2793,4]]},"292":{"position":[[1362,4],[1459,4]]},"302":{"position":[[1372,5]]},"314":{"position":[[104,4]]},"352":{"position":[[3147,4]]},"378":{"position":[[1841,5]]},"422":{"position":[[530,4],[1045,4],[2054,5]]},"488":{"position":[[1119,5]]},"508":{"position":[[1549,6]]},"512":{"position":[[1121,6]]},"528":{"position":[[786,4]]},"536":{"position":[[94,5],[337,4],[376,5],[508,4],[564,4],[597,5],[679,5],[798,4],[1069,5],[1245,5],[1321,5]]},"542":{"position":[[284,5],[398,5]]},"546":{"position":[[155,6]]},"550":{"position":[[1785,5]]},"568":{"position":[[936,4]]},"570":{"position":[[575,5],[940,5]]},"572":{"position":[[109,5]]},"626":{"position":[[5600,4]]},"658":{"position":[[905,4]]},"662":{"position":[[198,4]]},"664":{"position":[[395,4],[2146,4],[2345,4]]},"666":{"position":[[756,4],[808,4]]},"716":{"position":[[570,6],[1085,5],[1452,4],[3668,5],[4563,4]]},"720":{"position":[[3560,4],[7689,4]]},"734":{"position":[[2070,4]]},"738":{"position":[[3856,5]]},"744":{"position":[[1715,4]]},"754":{"position":[[87,5],[831,4]]},"760":{"position":[[837,4]]},"774":{"position":[[575,5]]},"778":{"position":[[957,4]]},"794":{"position":[[1112,5]]},"798":{"position":[[1507,4],[1550,5]]},"802":{"position":[[251,4],[308,4],[1129,4],[1176,4],[1895,5],[2639,4]]},"806":{"position":[[1276,4],[2361,4],[2483,6],[2557,5],[2772,5]]},"808":{"position":[[420,4]]},"810":{"position":[[28,6],[568,4],[650,5],[713,6]]},"858":{"position":[[2186,4]]},"862":{"position":[[476,6],[676,5],[900,5],[964,7],[1020,5],[1862,6],[2525,5],[3951,4],[4429,5]]},"876":{"position":[[597,5]]},"912":{"position":[[614,4]]},"939":{"position":[[233,4],[383,4]]},"989":{"position":[[427,5],[523,7],[531,5],[627,7]]},"991":{"position":[[117,4],[143,4]]}}}],["code.j",{"_index":1744,"t":{"225":{"position":[[119,7],[1514,8]]}}}],["coder",{"_index":1202,"t":{"91":{"position":[[378,6]]}}}],["coffee,tea,milkshak",{"_index":3695,"t":{"592":{"position":[[243,22]]}}}],["coher",{"_index":4203,"t":{"698":{"position":[[365,8]]}}}],["col",{"_index":5331,"t":{"985":{"position":[[958,4]]}}}],["collabor",{"_index":3900,"t":{"654":{"position":[[428,11],[851,13]]},"664":{"position":[[2220,11]]},"674":{"position":[[464,11]]},"678":{"position":[[1209,13]]},"816":{"position":[[332,14]]},"818":{"position":[[1389,13],[1600,11]]},"838":{"position":[[1125,11]]},"846":{"position":[[308,11]]}}}],["colleagu",{"_index":2604,"t":{"392":{"position":[[416,9]]}}}],["collect",{"_index":931,"t":{"51":{"position":[[106,10]]},"526":{"position":[[2213,9]]},"724":{"position":[[335,10],[507,10]]},"736":{"position":[[758,10]]},"830":{"position":[[23,10]]},"1019":{"position":[[272,12]]}}}],["colon",{"_index":1687,"t":{"213":{"position":[[728,5],[806,5],[876,5]]},"300":{"position":[[704,7]]},"376":{"position":[[1268,6],[1686,5],[3610,5],[3651,5],[4706,6]]},"472":{"position":[[368,5],[557,6]]},"554":{"position":[[337,5],[377,5],[444,5],[588,5],[1041,5]]},"574":{"position":[[2105,6]]},"604":{"position":[[202,5]]},"708":{"position":[[823,5]]},"760":{"position":[[2661,5]]},"806":{"position":[[203,5],[1687,5]]},"860":{"position":[[534,5],[727,6],[1784,5],[1832,5]]},"1033":{"position":[[3501,5],[4080,7],[4295,6],[4344,5]]}}}],["color",{"_index":4263,"t":{"716":{"position":[[564,5],[1848,5],[2173,5],[2686,5],[3021,6],[3051,7],[3615,6],[3649,7],[4967,5]]},"720":{"position":[[4386,5]]},"734":{"position":[[1158,6],[1218,7],[1276,7],[1592,5],[2184,5]]},"862":{"position":[[958,5]]},"985":{"position":[[1601,5]]}}}],["color)cas",{"_index":4438,"t":{"734":{"position":[[1190,10]]}}}],["color=auto",{"_index":4465,"t":{"734":{"position":[[2366,11],[2396,11],[2428,11],[2459,11],[2492,11]]},"862":{"position":[[1734,10]]}}}],["color=auto'fi",{"_index":4471,"t":{"734":{"position":[[2525,13]]}}}],["color[=when",{"_index":2589,"t":{"390":{"position":[[310,13]]}}}],["color_prompt",{"_index":4454,"t":{"734":{"position":[[1788,13],[1811,15],[2010,12]]}}}],["color_prompt=y",{"_index":4453,"t":{"734":{"position":[[1766,16]]}}}],["color_prompt=yes;;esac",{"_index":4441,"t":{"734":{"position":[[1236,23]]}}}],["colorterm=truecolor",{"_index":3148,"t":{"468":{"position":[[2652,19]]}}}],["colour",{"_index":2210,"t":{"308":{"position":[[383,7]]},"666":{"position":[[816,6]]},"668":{"position":[[455,7]]},"694":{"position":[[1549,7]]},"714":{"position":[[3870,7],[3952,6]]},"716":{"position":[[3167,7],[3201,6],[3317,6],[3474,7],[3687,8],[4190,6],[4390,8],[4536,7],[4799,6],[5251,6],[5425,6],[5729,6],[5836,7]]},"720":{"position":[[119,6],[725,8],[1082,8],[1908,8],[2226,7],[2245,8],[4967,7]]},"734":{"position":[[2113,7],[2561,7],[2649,7]]},"798":{"position":[[780,6]]},"806":{"position":[[146,6],[1580,6],[2476,6],[2550,6],[2789,6],[2892,10],[3042,7]]},"836":{"position":[[162,8]]},"862":{"position":[[68,6],[469,6],[669,6],[858,8],[1013,6],[1156,8],[1246,10],[1298,10],[1436,6],[1469,8],[1778,7],[1855,6],[2362,6],[2518,6],[3379,10],[3449,10],[3510,10],[4422,6]]}}}],["colour<=37",{"_index":5001,"t":{"862":{"position":[[1234,11],[3367,11]]}}}],["colour=31",{"_index":5000,"t":{"862":{"position":[[1221,12],[3354,12]]}}}],["colour[=when",{"_index":2590,"t":{"390":{"position":[[327,14]]}}}],["colour_code=\"\\\\e[0;${colour}m",{"_index":5002,"t":{"862":{"position":[[1260,30],[3393,30]]}}}],["colour_code}${message}${reset}\\n",{"_index":5003,"t":{"862":{"position":[[1311,35],[3462,35]]}}}],["colouris",{"_index":4371,"t":{"720":{"position":[[2548,12]]},"862":{"position":[[4373,10]]}}}],["coloursecho",{"_index":5027,"t":{"862":{"position":[[4073,11]]}}}],["column",{"_index":2897,"t":{"446":{"position":[[4121,7]]},"448":{"position":[[199,7]]},"760":{"position":[[949,6]]},"985":{"position":[[977,7]]}}}],["comand",{"_index":3762,"t":{"612":{"position":[[1361,8]]},"1029":{"position":[[180,10]]}}}],["combin",{"_index":276,"t":{"21":{"position":[[2147,11],[2442,11]]},"95":{"position":[[705,9]]},"219":{"position":[[120,8]]},"290":{"position":[[1884,8]]},"344":{"position":[[2928,7]]},"366":{"position":[[3074,7]]},"458":{"position":[[1637,11]]},"470":{"position":[[1206,8]]},"510":{"position":[[2686,11]]},"554":{"position":[[824,7]]},"570":{"position":[[506,11]]},"604":{"position":[[149,7]]},"698":{"position":[[57,7]]},"772":{"position":[[77,7]]},"794":{"position":[[124,7]]},"798":{"position":[[215,9]]},"826":{"position":[[98,11],[178,11]]},"852":{"position":[[2447,8]]},"1027":{"position":[[6581,8]]}}}],["come",{"_index":908,"t":{"49":{"position":[[96,4]]},"61":{"position":[[599,4]]},"71":{"position":[[182,5],[1408,5]]},"113":{"position":[[222,5]]},"121":{"position":[[1175,5]]},"165":{"position":[[806,6]]},"175":{"position":[[1426,4]]},"209":{"position":[[493,4]]},"253":{"position":[[2104,4]]},"262":{"position":[[2460,4]]},"266":{"position":[[9,4]]},"276":{"position":[[1276,5]]},"278":{"position":[[1094,4]]},"280":{"position":[[164,4]]},"292":{"position":[[1843,5],[1974,4]]},"314":{"position":[[326,4]]},"342":{"position":[[102,4]]},"352":{"position":[[286,4],[1644,5],[1755,5],[1848,5]]},"378":{"position":[[3364,5]]},"392":{"position":[[176,5]]},"400":{"position":[[764,5]]},"402":{"position":[[241,4]]},"422":{"position":[[1921,4]]},"430":{"position":[[466,5]]},"442":{"position":[[1108,5]]},"480":{"position":[[607,5]]},"488":{"position":[[1093,5]]},"522":{"position":[[7,4]]},"538":{"position":[[170,4]]},"574":{"position":[[2681,4]]},"584":{"position":[[373,4]]},"596":{"position":[[1914,4]]},"606":{"position":[[376,4]]},"664":{"position":[[204,4]]},"696":{"position":[[764,4]]},"708":{"position":[[151,5]]},"718":{"position":[[2243,5]]},"734":{"position":[[146,5]]},"738":{"position":[[242,5]]},"774":{"position":[[1016,4]]},"782":{"position":[[4,5]]},"868":{"position":[[917,4]]},"876":{"position":[[420,4],[722,5]]},"898":{"position":[[1072,6]]}}}],["comfort",{"_index":1008,"t":{"67":{"position":[[587,11]]},"219":{"position":[[2098,11]]},"260":{"position":[[13,11]]},"396":{"position":[[1780,11]]}}}],["comma",{"_index":2878,"t":{"446":{"position":[[924,5]]},"610":{"position":[[533,7],[654,5]]}}}],["command",{"_index":91,"t":{"10":{"position":[[11,7],[47,7],[340,9]]},"13":{"position":[[106,7],[135,7]]},"21":{"position":[[222,7],[402,8],[1113,8],[1148,8],[1210,7],[1275,7],[1450,8],[2257,8],[2571,7],[2622,8],[2888,7],[3005,9],[3144,7],[3841,7]]},"23":{"position":[[119,8],[623,8],[689,8],[863,9],[1063,7],[1142,8],[1280,8],[1404,8],[1591,8],[1891,7],[2040,9],[2077,8]]},"25":{"position":[[29,9],[218,8],[510,7],[673,8],[929,7],[1137,8],[1624,8]]},"27":{"position":[[293,8],[449,7]]},"29":{"position":[[876,7]]},"31":{"position":[[428,8],[506,8],[2292,9],[2465,8]]},"33":{"position":[[79,8],[141,8],[285,7],[390,8],[570,8],[673,8]]},"37":{"position":[[313,9]]},"39":{"position":[[109,7]]},"41":{"position":[[36,7],[102,8],[657,7],[735,7]]},"43":{"position":[[108,7],[374,8],[742,9],[889,7],[1061,8]]},"45":{"position":[[122,8],[259,8],[365,7],[459,8],[508,8],[861,9],[905,9],[948,8]]},"47":{"position":[[375,8],[761,8],[1220,8],[1282,8],[1371,8],[1772,7],[1972,9],[2115,8],[2156,7],[5204,7],[5278,8],[5481,11],[6904,9],[6955,8],[7138,8],[7214,8],[7408,8],[8642,8]]},"49":{"position":[[263,8],[945,8]]},"51":{"position":[[343,8],[409,7]]},"61":{"position":[[134,8],[164,7],[268,7],[293,7]]},"63":{"position":[[107,7]]},"67":{"position":[[1037,8]]},"69":{"position":[[2144,8],[5559,7]]},"71":{"position":[[1217,7],[1502,7],[1561,8],[2190,7]]},"77":{"position":[[677,8],[688,7],[912,9],[979,8],[1262,8],[1311,8],[1361,8],[1620,8],[1634,7],[1810,7],[1861,8],[1913,7],[2407,8],[3290,7],[3648,7],[4660,8],[4929,8],[5021,7],[5130,8]]},"79":{"position":[[11,7],[124,7],[299,8]]},"81":{"position":[[283,8],[1017,7],[1164,7],[1583,7],[1856,8],[1900,8],[1927,8],[2003,7],[2087,7]]},"83":{"position":[[440,9]]},"87":{"position":[[104,7],[724,7]]},"91":{"position":[[3169,7]]},"99":{"position":[[860,7],[1221,8],[1479,8]]},"101":{"position":[[236,7]]},"103":{"position":[[257,8],[347,8]]},"105":{"position":[[135,7],[367,8],[766,7],[849,7]]},"107":{"position":[[292,7],[347,7],[617,7],[762,7],[847,7],[938,7],[1233,7]]},"109":{"position":[[117,8]]},"113":{"position":[[205,8],[318,7]]},"115":{"position":[[270,9],[336,8]]},"117":{"position":[[129,8],[507,7],[868,8]]},"121":{"position":[[95,7],[158,8],[424,7]]},"123":{"position":[[100,8],[138,7],[174,7],[276,7],[379,7]]},"125":{"position":[[64,7],[210,7],[380,7],[429,7],[463,7],[582,7],[694,7],[795,7],[840,7],[976,7],[1162,7],[1274,8]]},"129":{"position":[[209,8],[472,7],[511,8],[909,8]]},"131":{"position":[[219,8],[274,7],[313,8],[490,8]]},"133":{"position":[[113,8],[329,9],[635,9],[735,7],[767,8],[813,8],[953,7],[1050,7]]},"137":{"position":[[29,9],[547,8],[665,7],[685,8],[791,7]]},"139":{"position":[[348,7]]},"141":{"position":[[223,8],[341,7],[424,7],[822,8]]},"143":{"position":[[52,8]]},"145":{"position":[[79,7],[203,7],[286,7],[916,7]]},"151":{"position":[[35,7]]},"159":{"position":[[203,8]]},"165":{"position":[[35,8],[126,9],[634,8],[717,7],[752,7],[797,8],[851,8],[963,8]]},"171":{"position":[[24,7]]},"173":{"position":[[93,8]]},"175":{"position":[[88,8],[316,7],[715,7],[1581,8],[1625,7],[1939,7],[2121,7],[2229,7],[2285,8],[2428,7]]},"177":{"position":[[104,8],[243,8]]},"179":{"position":[[81,8],[133,8],[212,8],[280,8],[388,8],[469,10],[785,7],[817,7]]},"181":{"position":[[24,7],[342,7]]},"183":{"position":[[30,8],[224,8],[336,8]]},"185":{"position":[[20,8],[223,7],[733,8],[847,8],[1112,7],[1221,7]]},"189":{"position":[[28,7],[230,7],[387,8],[1035,7],[1110,7],[1507,7],[2042,7],[2129,7],[2501,8]]},"193":{"position":[[493,7],[585,9]]},"195":{"position":[[1796,8],[1863,8],[1971,7]]},"201":{"position":[[162,8]]},"203":{"position":[[710,8]]},"205":{"position":[[9,7],[500,7],[612,7],[1098,8],[1215,7],[1334,7],[1555,7],[1611,7]]},"207":{"position":[[211,7],[445,7],[492,8],[534,7],[599,7],[769,7]]},"211":{"position":[[192,7]]},"213":{"position":[[53,8],[561,8],[624,7],[773,7],[992,7],[1105,8]]},"215":{"position":[[762,8]]},"217":{"position":[[205,8],[846,7],[1206,7]]},"219":{"position":[[9,7],[368,8],[1307,7],[1361,7],[1459,7],[1594,7],[2040,8]]},"221":{"position":[[39,8],[781,8]]},"225":{"position":[[661,8],[831,7],[923,8],[1049,8],[1258,7]]},"227":{"position":[[610,7]]},"229":{"position":[[30,7],[283,7],[352,7],[469,7]]},"231":{"position":[[34,7],[1279,8],[1622,7],[2060,7]]},"233":{"position":[[104,8],[148,8],[281,8]]},"235":{"position":[[282,7],[771,7],[820,7],[867,8]]},"237":{"position":[[340,9],[372,7],[1068,7]]},"239":{"position":[[83,7],[500,7]]},"243":{"position":[[126,9],[136,7],[150,7],[168,7],[1874,7],[2000,8],[2132,7]]},"247":{"position":[[628,8],[858,7]]},"249":{"position":[[1243,8]]},"251":{"position":[[24,8],[428,7],[450,7],[493,7]]},"253":{"position":[[2035,8],[2755,7],[2864,7],[3306,8],[3405,7]]},"257":{"position":[[1122,7],[1175,7],[1945,8],[3572,8],[4633,7],[5296,9],[6001,8]]},"259":{"position":[[111,8],[219,7],[360,7],[415,7],[717,8]]},"262":{"position":[[2063,9],[2825,7]]},"266":{"position":[[900,8]]},"270":{"position":[[38,7]]},"280":{"position":[[385,9],[1077,8]]},"284":{"position":[[175,9],[304,9],[1056,9],[1085,8],[1118,8],[1155,7],[1359,8]]},"286":{"position":[[67,7],[267,9],[442,7],[499,7],[582,7],[744,7],[766,7]]},"288":{"position":[[13,10],[195,8],[296,9],[368,8],[435,8],[465,9],[506,10],[517,8],[1776,7],[2266,7]]},"290":{"position":[[381,7],[710,7],[739,7],[1106,7],[1733,7],[1811,7],[2000,7],[2188,8]]},"292":{"position":[[1868,8]]},"296":{"position":[[42,7],[152,7],[198,8],[431,8],[496,8]]},"298":{"position":[[24,8]]},"300":{"position":[[80,8],[415,8]]},"302":{"position":[[362,8],[589,8],[1677,9]]},"304":{"position":[[44,7],[179,7],[814,7],[876,8],[931,7],[1005,8],[1264,8],[1442,8],[1529,7],[2216,8],[2558,8]]},"306":{"position":[[390,8],[816,9]]},"308":{"position":[[147,8],[224,7],[330,8],[560,8]]},"310":{"position":[[73,9],[91,8],[132,8],[192,8],[623,7],[668,8]]},"312":{"position":[[75,8],[153,9],[202,7]]},"320":{"position":[[118,8]]},"322":{"position":[[56,9]]},"326":{"position":[[0,7],[142,8],[296,7],[327,7],[365,7]]},"328":{"position":[[130,7],[236,7]]},"330":{"position":[[66,9],[143,7]]},"360":{"position":[[18,7],[190,7],[300,8]]},"362":{"position":[[387,7]]},"364":{"position":[[691,7],[823,7],[1585,8],[2527,7]]},"366":{"position":[[176,7],[193,7],[1216,8],[3315,8],[3358,7]]},"368":{"position":[[1267,7]]},"370":{"position":[[374,7],[1052,7],[1239,8]]},"372":{"position":[[1143,8]]},"376":{"position":[[767,7],[887,7],[3081,7],[3235,8],[5957,8]]},"378":{"position":[[1623,7],[1657,7],[3073,8],[3259,7],[3429,8],[4383,9],[4460,8]]},"384":{"position":[[394,7],[560,7]]},"386":{"position":[[54,7],[214,8],[1448,7]]},"390":{"position":[[91,8],[833,8],[1187,9]]},"392":{"position":[[189,7],[249,8],[270,7]]},"394":{"position":[[78,8],[135,8],[220,8],[303,9],[417,7],[834,8],[993,7],[1370,8],[1622,8],[1636,7],[1708,7],[1917,8],[2456,8]]},"396":{"position":[[124,7]]},"398":{"position":[[15,7]]},"400":{"position":[[1457,8]]},"402":{"position":[[458,8]]},"404":{"position":[[394,8],[535,8],[652,8]]},"406":{"position":[[33,7],[984,8]]},"410":{"position":[[674,8]]},"414":{"position":[[59,9],[129,8],[181,8],[242,8],[330,8]]},"416":{"position":[[78,9],[101,7]]},"418":{"position":[[28,8],[94,9],[215,8],[250,9],[341,8]]},"420":{"position":[[51,8],[853,8],[892,7],[981,7],[1056,7]]},"422":{"position":[[1091,8],[1455,8]]},"424":{"position":[[18,8],[99,11],[429,9],[914,7],[1238,7],[1308,7],[1387,7],[1587,8],[1622,8],[1695,7]]},"426":{"position":[[143,8],[219,7],[639,7]]},"428":{"position":[[586,7],[651,7],[705,8],[843,8]]},"430":{"position":[[270,11]]},"432":{"position":[[612,8],[621,7],[638,7],[692,7],[793,7],[867,7],[943,7],[993,7],[1188,7],[1215,7],[1407,7],[1564,7],[1603,7]]},"434":{"position":[[81,8],[103,7],[1107,8],[1187,7],[1387,8],[1746,8],[1782,7]]},"438":{"position":[[293,11],[483,8],[680,7],[865,7],[1074,7],[1192,8],[1289,7],[1496,8],[1623,8]]},"440":{"position":[[289,7]]},"442":{"position":[[1583,9],[1609,7]]},"446":{"position":[[4,8],[201,8],[843,7],[1459,7],[1723,8],[2237,8],[2296,7],[2854,7],[2944,7],[3029,9],[3064,7]]},"448":{"position":[[254,7],[552,8]]},"450":{"position":[[9,7],[74,7],[614,7],[842,7],[1933,7],[2885,8]]},"452":{"position":[[23,7],[771,8]]},"454":{"position":[[10,8],[1145,7]]},"458":{"position":[[399,7],[555,8],[695,7],[1157,7],[1534,7],[1770,9]]},"462":{"position":[[29,9],[39,7],[85,9],[113,9],[305,8],[940,7],[1113,7],[1197,7],[1276,7],[1348,8],[1580,7],[1697,8],[2078,8],[2096,7],[2151,7],[2352,7],[2446,7],[2499,7],[2718,7]]},"464":{"position":[[730,7],[1383,7]]},"466":{"position":[[92,8],[443,8],[516,7],[655,7],[801,7],[847,7],[917,7],[1202,8]]},"468":{"position":[[48,7],[105,7],[630,8],[821,7],[1306,7],[1530,7],[2221,8],[2243,7],[2309,7],[2421,9],[3059,8]]},"470":{"position":[[67,7],[421,8],[527,7],[1054,7]]},"472":{"position":[[93,7],[445,9]]},"474":{"position":[[48,7],[87,8],[148,7],[215,8],[278,8]]},"480":{"position":[[437,7]]},"482":{"position":[[1110,7],[1377,8]]},"484":{"position":[[1124,7],[1799,8]]},"486":{"position":[[31,7],[311,7],[611,8],[767,8]]},"492":{"position":[[307,8],[551,7]]},"496":{"position":[[592,7]]},"502":{"position":[[823,7],[902,7]]},"508":{"position":[[36,7],[526,7],[1000,7],[1118,8],[2015,7],[2201,7],[2462,7],[2570,7],[2620,7],[3226,7],[3407,8],[4723,7],[5402,7]]},"512":{"position":[[69,7],[233,8],[288,11],[536,8],[740,11],[792,7],[914,8],[1202,8],[1300,7],[1440,7],[1607,7]]},"514":{"position":[[430,7],[731,8]]},"518":{"position":[[68,7],[89,7],[110,7],[233,8],[526,7],[582,7],[712,8]]},"532":{"position":[[407,7],[705,8],[1231,9]]},"534":{"position":[[62,7],[1205,7],[1286,7],[1829,7],[1870,7],[1989,7],[2088,8],[2223,7],[2272,7]]},"536":{"position":[[40,7],[462,7],[776,7],[909,7],[969,7],[1045,7],[1166,7],[1287,7],[1441,7]]},"538":{"position":[[34,7],[283,7],[1180,8],[1561,7],[1880,7],[1996,8],[2142,7],[2236,7],[2310,7],[2346,7],[2438,7],[2803,7],[2893,7],[3089,7],[3145,7],[3364,8],[3866,8]]},"542":{"position":[[232,7],[323,8],[341,7],[435,7]]},"544":{"position":[[52,8],[79,8],[266,7],[484,8],[514,7],[619,7],[739,8],[1052,8],[1108,7],[1301,7],[1390,7],[1540,11],[1627,7],[1693,12],[1890,8],[1987,7],[2035,8],[2122,7],[2156,7]]},"550":{"position":[[187,7],[211,7],[235,7],[284,10],[316,8],[394,8],[613,7],[825,7],[866,7],[1055,7],[1131,8],[1202,8],[1319,9],[1341,8],[1380,8],[1467,8],[1511,8],[1950,7]]},"552":{"position":[[31,7],[139,7],[313,8],[407,7],[895,8],[1122,8],[1192,7]]},"554":{"position":[[388,8],[466,7],[1080,8]]},"556":{"position":[[237,7],[308,7],[384,7],[597,7],[612,7],[730,7],[796,7]]},"558":{"position":[[236,7],[355,7],[460,7],[534,7],[644,7],[837,7],[986,7],[1122,8],[1252,8],[1339,7],[1466,9],[1482,8],[1513,9],[1528,9],[1546,8],[1820,7],[1925,7],[1999,7]]},"562":{"position":[[39,7]]},"566":{"position":[[351,9]]},"570":{"position":[[16,8],[73,7],[115,8],[234,8],[310,7],[331,7],[386,7],[446,8],[526,9],[622,7],[683,7],[740,7],[874,7],[900,7],[996,7],[1060,8],[1091,7],[1149,8],[1215,7],[1580,9],[1658,8]]},"572":{"position":[[235,9],[283,9],[308,9]]},"574":{"position":[[70,7],[242,8],[313,7],[403,11],[455,7],[577,8],[896,9],[948,7],[1190,8],[1748,9],[2010,7],[2125,7],[2784,7],[2833,8],[3322,7],[3489,7]]},"580":{"position":[[40,8],[153,7],[177,7],[201,7],[252,8],[1350,8]]},"582":{"position":[[678,7]]},"584":{"position":[[149,9],[172,8],[1399,8],[1437,8],[1520,8],[1641,7],[1939,8]]},"586":{"position":[[1655,7],[1958,8],[2268,7]]},"588":{"position":[[117,7],[215,8],[277,7],[1593,7],[2002,7]]},"590":{"position":[[209,8],[221,8],[233,8],[592,8]]},"594":{"position":[[39,8],[163,7],[187,7],[211,7],[246,8],[305,9],[337,8],[418,8]]},"596":{"position":[[539,7],[575,7],[1536,8]]},"598":{"position":[[222,8]]},"600":{"position":[[85,8],[208,7],[232,7],[256,7],[291,8],[357,9],[389,8],[470,8],[1455,8]]},"608":{"position":[[2079,7]]},"610":{"position":[[290,7],[397,8],[784,8]]},"612":{"position":[[52,8],[79,8],[309,7],[353,9],[523,7],[549,8],[570,7],[678,8],[694,10],[996,8],[1115,8],[1137,8],[1186,8],[1322,7],[1566,8],[1663,8],[1691,8]]},"614":{"position":[[177,9]]},"622":{"position":[[524,8],[640,9],[896,8]]},"624":{"position":[[33,8],[1237,8]]},"626":{"position":[[16,8],[63,8],[667,9],[740,9],[784,7],[956,8],[1114,8],[1900,7],[2005,8],[2377,7],[2542,8],[2655,8],[3032,8],[3294,7],[3314,7],[3416,9],[3515,7],[3828,7],[3981,7],[5016,8],[5248,8],[6151,8],[6193,8],[6569,7],[6666,8],[7097,7],[7157,8]]},"632":{"position":[[262,8]]},"634":{"position":[[493,7],[956,7],[1863,7],[1976,8],[2253,7],[2270,7],[2377,7],[2396,9]]},"636":{"position":[[873,7],[1000,8],[1840,7],[2285,8]]},"638":{"position":[[592,8],[706,8]]},"640":{"position":[[60,8],[469,9],[833,8],[1544,9],[1567,8],[1740,7]]},"642":{"position":[[81,8],[386,8]]},"644":{"position":[[209,8]]},"648":{"position":[[525,8],[1163,7],[1572,7]]},"650":{"position":[[425,7]]},"654":{"position":[[1587,8],[1822,7],[1846,8],[1890,7],[2715,7],[3427,7],[4336,8]]},"656":{"position":[[32,7],[51,7],[499,8],[522,7],[878,7],[1147,7]]},"658":{"position":[[35,7],[134,7],[300,8],[2601,8]]},"660":{"position":[[36,7],[210,7],[400,7]]},"662":{"position":[[326,8],[430,7],[774,7]]},"666":{"position":[[136,7],[468,8],[666,7],[1731,8],[1769,7],[1808,7]]},"668":{"position":[[33,7],[76,7],[290,7],[626,7],[1023,7],[1279,7],[2270,8],[2990,7],[3271,7]]},"670":{"position":[[170,8],[387,8],[406,7],[977,8],[1780,7],[1963,7],[2336,7]]},"672":{"position":[[65,8]]},"674":{"position":[[208,7]]},"680":{"position":[[207,8],[611,7],[864,9],[1089,8],[1147,8],[1280,7],[1575,8]]},"682":{"position":[[94,7],[1012,8],[1045,7],[2217,8],[2547,8],[2911,7],[3232,8],[3438,7],[3814,8]]},"684":{"position":[[117,8],[149,8],[567,7],[1323,8],[1402,8],[1512,8],[2386,7],[2564,7],[2851,7]]},"688":{"position":[[339,8],[605,7],[733,7],[854,8],[1592,8],[2236,7],[3460,7]]},"690":{"position":[[54,7],[505,7]]},"692":{"position":[[919,7],[1000,8]]},"694":{"position":[[302,7],[1468,7]]},"700":{"position":[[294,8],[349,7],[3572,7],[3953,8]]},"702":{"position":[[40,7],[113,7],[2815,7]]},"704":{"position":[[537,8],[609,7]]},"708":{"position":[[4,7],[124,8],[192,7],[1541,8],[1663,7]]},"710":{"position":[[21,7],[157,7]]},"714":{"position":[[1364,8],[1380,7],[1403,7],[2879,7],[3169,8],[3332,8]]},"716":{"position":[[3868,7],[4117,8],[5127,9]]},"718":{"position":[[288,8],[316,7],[430,8],[599,8],[2317,8],[2382,7]]},"720":{"position":[[221,8],[496,8],[1806,7],[1988,7],[2523,7],[5122,7],[6650,8],[7241,7],[7339,7]]},"722":{"position":[[214,7],[979,8],[1023,7],[1152,8],[2143,7],[2374,7],[2392,8],[2504,7],[2532,7],[2691,7],[2896,8],[3021,7]]},"726":{"position":[[55,7],[288,7],[558,7]]},"732":{"position":[[179,8]]},"734":{"position":[[996,7],[1436,9],[2144,7],[2606,8],[2769,9],[3276,9],[3399,8]]},"738":{"position":[[2233,7],[3128,7],[4391,7],[4495,9],[5882,8],[5962,8],[6587,8],[6715,8],[6775,8],[6916,8],[7002,9],[7290,8],[7593,8],[7648,9],[7701,8],[7788,7]]},"740":{"position":[[182,7],[550,7]]},"742":{"position":[[455,7],[843,7]]},"744":{"position":[[853,7],[2596,7]]},"746":{"position":[[1254,7],[1370,7]]},"748":{"position":[[678,8],[698,7],[943,7],[1108,7]]},"754":{"position":[[2239,9],[2336,9]]},"758":{"position":[[159,7]]},"760":{"position":[[40,8],[565,7],[672,9],[686,7],[737,8],[1104,7],[1647,8],[1679,7],[1791,7],[2354,7],[2399,7],[2607,8],[2753,9],[2791,7],[2925,7],[3064,7],[3084,7],[3164,7],[3251,7]]},"762":{"position":[[225,8]]},"764":{"position":[[1162,7],[1228,7],[1727,8],[2513,7]]},"766":{"position":[[4,8],[354,8],[445,8],[909,7]]},"768":{"position":[[9,8],[85,7],[280,9]]},"770":{"position":[[58,9],[126,7],[182,8],[328,8],[342,7],[582,8],[831,7],[952,7],[1083,7],[1177,7],[1276,7],[1431,7],[1599,9]]},"772":{"position":[[22,7],[895,7]]},"776":{"position":[[745,7],[881,7]]},"778":{"position":[[227,8],[1250,7],[1299,7],[1539,7],[1648,7],[1696,7],[1771,7],[1925,7],[2074,7],[2246,7]]},"780":{"position":[[227,7]]},"790":{"position":[[115,7],[359,8],[414,7]]},"800":{"position":[[1823,7]]},"802":{"position":[[741,7]]},"808":{"position":[[1603,8],[1636,7]]},"810":{"position":[[1200,7]]},"822":{"position":[[82,8]]},"826":{"position":[[128,8],[351,8],[404,8]]},"828":{"position":[[37,7],[219,8],[1027,7],[1180,7],[1564,8],[1600,7]]},"830":{"position":[[300,8],[415,7],[438,7],[500,7],[537,8],[862,8],[1090,7]]},"832":{"position":[[433,7],[481,8],[653,7],[1089,8],[1129,7],[1277,7],[1507,7]]},"834":{"position":[[1506,7],[1641,7],[3075,8],[3120,8],[3724,8],[3923,7],[4185,8],[4633,9]]},"838":{"position":[[1320,7],[1410,7],[1437,7],[1514,7],[1648,8],[1704,7],[2410,7]]},"840":{"position":[[285,8],[520,8]]},"842":{"position":[[36,8],[81,7],[660,7],[668,8],[684,7],[719,8],[1092,7],[1149,7],[1355,7]]},"850":{"position":[[56,7],[172,7],[467,7],[518,7],[640,7],[1090,7],[1436,8],[1576,7],[1839,9],[1904,7],[2216,7],[2451,9]]},"852":{"position":[[33,7],[1199,7],[1645,8],[1851,8],[1950,7],[2105,7],[2181,8],[2315,8]]},"854":{"position":[[49,7],[209,7],[334,8],[653,7],[883,7]]},"856":{"position":[[125,8]]},"858":{"position":[[47,7],[75,8],[1252,7],[2902,7],[3422,7]]},"860":{"position":[[49,7],[154,7],[223,7],[282,7],[341,7],[919,7],[1115,7],[1363,8],[2658,8]]},"862":{"position":[[426,7],[558,8],[1696,7],[1765,7],[2486,8],[2794,8],[3930,7],[4137,7],[4233,7]]},"864":{"position":[[373,7],[761,8],[834,8]]},"866":{"position":[[40,8],[102,7],[183,7],[218,7],[229,7],[258,7],[392,7],[400,8],[548,7],[601,7],[837,9]]},"868":{"position":[[20,7],[374,8]]},"870":{"position":[[102,8],[310,8],[524,8],[566,8],[651,7]]},"892":{"position":[[440,8]]},"898":{"position":[[579,7],[652,7],[763,8],[1063,8],[1172,7],[2668,8],[2829,7]]},"900":{"position":[[1426,7]]},"902":{"position":[[1069,8]]},"904":{"position":[[79,9],[116,8]]},"906":{"position":[[814,8]]},"908":{"position":[[145,7],[271,8],[480,7],[3452,7]]},"910":{"position":[[1066,7]]},"918":{"position":[[319,8]]},"925":{"position":[[105,8]]},"927":{"position":[[9,8],[18,7]]},"935":{"position":[[27,7],[208,7],[244,7]]},"947":{"position":[[67,7]]},"951":{"position":[[46,7]]},"953":{"position":[[337,7]]},"967":{"position":[[195,9],[251,9]]},"969":{"position":[[243,7]]},"987":{"position":[[179,8],[725,8]]},"995":{"position":[[409,7],[440,7]]},"997":{"position":[[89,12],[226,7]]},"1019":{"position":[[26,8],[855,7],[908,10],[952,8],[997,7],[1229,7]]},"1027":{"position":[[192,7],[9134,7]]},"1029":{"position":[[67,7],[150,8],[204,7],[287,7],[464,8],[524,7],[617,7],[736,7],[808,7],[854,7],[1068,8],[1117,7],[1146,7]]},"1033":{"position":[[174,7],[729,7],[3000,8],[3805,8],[4021,9]]},"1035":{"position":[[2856,7]]},"1037":{"position":[[392,9]]}}}],["command\"^a",{"_index":1546,"t":{"181":{"position":[[96,11]]}}}],["command.echo",{"_index":2779,"t":{"424":{"position":[[78,12]]},"430":{"position":[[249,12]]},"438":{"position":[[272,12]]},"512":{"position":[[267,12],[719,12]]},"574":{"position":[[382,12]]}}}],["command1",{"_index":1499,"t":{"165":{"position":[[138,8]]},"227":{"position":[[172,9]]},"424":{"position":[[403,8]]},"570":{"position":[[586,9],[951,9],[1245,9],[1309,9]]}}}],["command2",{"_index":1503,"t":{"165":{"position":[[169,8]]},"570":{"position":[[600,9],[964,9],[1295,9],[1367,8]]},"1029":{"position":[[1297,13]]}}}],["command2.command1",{"_index":3569,"t":{"570":{"position":[[1274,17],[1346,17]]}}}],["command3",{"_index":1507,"t":{"165":{"position":[[200,8]]}}}],["command=$(echo",{"_index":3505,"t":{"544":{"position":[[1492,14]]}}}],["command>\".se",{"_index":3875,"t":{"636":{"position":[[1890,14]]}}}],["command>.bind",{"_index":4887,"t":{"834":{"position":[[4226,14]]}}}],["command_count",{"_index":3352,"t":{"512":{"position":[[1077,16]]},"574":{"position":[[736,16]]},"612":{"position":[[832,17]]}}}],["command_count=${1",{"_index":3501,"t":{"544":{"position":[[690,21]]}}}],["command_count}elif",{"_index":3595,"t":{"574":{"position":[[1867,20]]}}}],["command_count}els",{"_index":3598,"t":{"574":{"position":[[2282,20]]}}}],["command_exist",{"_index":3447,"t":{"534":{"position":[[1404,16],[2126,16]]},"536":{"position":[[639,14],[685,16],[860,14]]}}}],["command_numb",{"_index":4988,"t":{"860":{"position":[[1192,17]]}}}],["commands.counter=1old_ifs=$ifsifs=$'\\n'for",{"_index":3761,"t":{"612":{"position":[[1279,42]]}}}],["commands.if",{"_index":3593,"t":{"574":{"position":[[1670,11]]}}}],["commands.safe_set",{"_index":4507,"t":{"738":{"position":[[5104,17],[6485,17]]}}}],["commands.tail",{"_index":2780,"t":{"424":{"position":[[139,13]]},"426":{"position":[[276,13]]},"430":{"position":[[310,13]]},"438":{"position":[[333,13]]},"512":{"position":[[328,13],[961,13]]},"574":{"position":[[623,13]]}}}],["commands9",{"_index":2115,"t":{"290":{"position":[[865,10]]}}}],["commands:1",{"_index":3512,"t":{"544":{"position":[[2194,11]]}}}],["commands:463",{"_index":2786,"t":{"424":{"position":[[1487,12]]}}}],["commands:97",{"_index":2835,"t":{"438":{"position":[[608,11],[1126,11]]}}}],["commands>do",{"_index":3707,"t":{"594":{"position":[[138,11]]},"600":{"position":[[183,11]]}}}],["commands>then",{"_index":3520,"t":{"550":{"position":[[160,13]]}}}],["commandsdo",{"_index":3507,"t":{"544":{"position":[[1638,11]]},"612":{"position":[[1333,11]]}}}],["comment",{"_index":1454,"t":{"133":{"position":[[387,8],[402,8],[551,8]]},"260":{"position":[[226,9]]},"292":{"position":[[118,8]]},"366":{"position":[[1305,7],[1354,7]]},"368":{"position":[[47,9],[526,8],[594,7]]},"370":{"position":[[1073,9]]},"386":{"position":[[495,8]]},"414":{"position":[[451,10]]},"420":{"position":[[1358,9]]},"422":{"position":[[0,8],[111,8],[256,8],[417,8],[622,9],[644,7],[727,8],[787,7],[805,8],[865,8],[900,7],[942,7],[1013,7],[1029,10],[1164,8],[1291,8],[1556,8],[1688,7],[1774,7],[1992,8]]},"426":{"position":[[621,8]]},"440":{"position":[[113,8]]},"506":{"position":[[2795,7]]},"574":{"position":[[3141,9],[3156,7]]},"666":{"position":[[795,8]]},"684":{"position":[[1188,7]]},"696":{"position":[[5108,8]]},"720":{"position":[[2001,8]]},"734":{"position":[[1013,9],[1065,7]]},"738":{"position":[[1391,7],[6133,7]]}}}],["commerci",{"_index":1284,"t":{"93":{"position":[[707,10],[725,12]]},"678":{"position":[[1057,10]]}}}],["commit",{"_index":1534,"t":{"175":{"position":[[2114,6]]},"462":{"position":[[2517,6]]},"544":{"position":[[1365,7]]},"654":{"position":[[4227,6]]},"658":{"position":[[1104,8],[1131,6],[1169,7],[1306,6],[2028,6],[2099,6],[2431,7],[2561,6]]},"660":{"position":[[240,7],[422,6],[1559,8]]},"670":{"position":[[1373,8],[1520,8],[1692,9],[2890,6],[2990,7]]},"672":{"position":[[308,8],[352,8],[746,6],[766,7],[915,7]]},"682":{"position":[[289,7],[369,10],[425,6],[648,7],[658,7],[1228,7],[1253,10],[1451,10],[1581,7],[1984,7],[2368,7],[2688,7],[2713,10],[2877,10],[3775,6],[3807,6]]},"684":{"position":[[75,6],[142,6],[164,6],[263,6],[369,9],[404,8],[427,11],[560,6],[727,6],[833,9],[868,8],[891,11],[1226,6],[1505,6],[1545,7],[1851,7],[1958,11],[2006,6],[2103,6],[2275,10],[2311,6],[2508,6],[2557,6],[2611,7],[2783,6],[2977,7]]},"686":{"position":[[36,6],[309,6],[630,6],[971,6]]},"688":{"position":[[4,7],[112,7],[548,7],[1097,10],[1147,6],[1317,6],[1345,6],[1546,7],[1585,6],[1667,6],[1707,7],[1793,9],[2347,6],[2883,7],[2974,6]]},"690":{"position":[[713,7],[912,8]]},"692":{"position":[[20,7],[120,8],[272,6],[404,6],[553,8],[658,6],[754,6],[912,6],[974,6],[1191,6],[1251,6],[1448,7],[1485,6],[1582,6],[1693,6],[2397,6]]},"694":{"position":[[45,6],[137,8],[251,7],[289,6],[328,7],[906,8],[930,6],[1061,7],[1742,6],[2177,6]]},"696":{"position":[[1364,6],[1481,6],[1601,6],[1726,6],[2015,6],[2108,7],[2152,7],[2388,8],[2559,6],[2593,6],[2807,6],[4603,7],[4684,6],[4789,10],[4909,6],[5016,9],[5084,6],[5233,7],[5282,7]]},"698":{"position":[[433,7],[482,7]]},"700":{"position":[[532,10],[682,6],[692,6],[860,6],[926,6],[969,6],[1131,7],[1190,10],[1460,6],[1855,7],[2143,6],[2506,7],[2565,10],[2734,10],[2784,6],[2818,6],[3223,10],[3494,7],[3691,10],[3920,7]]},"702":{"position":[[175,6],[975,7],[1003,6],[1191,7],[1270,6],[1445,6],[1519,8],[1614,7],[1795,6],[1832,7],[1965,6],[2115,7],[2351,6],[2751,7],[2854,7],[2926,6],[2943,7]]},"704":{"position":[[157,6],[945,6],[961,6],[1028,6],[1048,6],[1077,6],[1110,6],[1130,6],[1534,8],[1586,8],[1608,7],[1845,6],[1920,7],[1985,7]]},"720":{"position":[[5944,9]]},"740":{"position":[[932,6]]},"764":{"position":[[1854,8],[1880,6],[1987,9],[2043,8],[2066,11],[2205,6],[2290,10],[2368,7],[2401,6],[2628,7],[2787,6]]},"778":{"position":[[1493,6],[1552,6],[1823,6],[1999,6],[2104,6],[2183,6]]},"927":{"position":[[898,6],[914,6],[981,6],[1001,6],[1030,6],[1063,6],[1083,6],[1487,8],[1539,8],[1561,7],[1798,6],[1873,7],[1938,7]]}}}],["commit.git",{"_index":4142,"t":{"692":{"position":[[193,10]]}}}],["commit.touch",{"_index":4145,"t":{"692":{"position":[[354,12]]}}}],["commit[main",{"_index":4112,"t":{"684":{"position":[[1527,11],[2636,11]]}}}],["commit_sha",{"_index":4214,"t":{"702":{"position":[[1646,13]]}}}],["committ",{"_index":4604,"t":{"764":{"position":[[1997,10]]}}}],["common",{"_index":452,"t":{"25":{"position":[[1545,6]]},"53":{"position":[[297,6]]},"69":{"position":[[127,6]]},"79":{"position":[[204,6]]},"81":{"position":[[4,6]]},"91":{"position":[[3420,6]]},"93":{"position":[[1045,6],[1663,6]]},"95":{"position":[[2241,6]]},"121":{"position":[[632,6]]},"131":{"position":[[538,6]]},"191":{"position":[[17,6],[193,6]]},"207":{"position":[[961,6]]},"229":{"position":[[801,6]]},"241":{"position":[[1155,6]]},"243":{"position":[[119,6]]},"253":{"position":[[39,6]]},"257":{"position":[[1575,6],[4745,6],[5255,6]]},"266":{"position":[[367,6]]},"282":{"position":[[738,6],[819,8],[1001,6]]},"288":{"position":[[802,6]]},"302":{"position":[[1082,6]]},"308":{"position":[[140,6]]},"362":{"position":[[296,6]]},"386":{"position":[[1358,7]]},"418":{"position":[[44,9]]},"424":{"position":[[91,7],[1480,6],[1580,6]]},"430":{"position":[[262,7]]},"432":{"position":[[522,6]]},"438":{"position":[[285,7],[996,7],[1067,6]]},"448":{"position":[[95,6]]},"464":{"position":[[4,6]]},"480":{"position":[[771,6]]},"506":{"position":[[482,6]]},"510":{"position":[[1686,6]]},"512":{"position":[[60,8],[224,8],[280,7],[732,7],[907,6],[1195,6],[1433,6]]},"522":{"position":[[194,6]]},"526":{"position":[[1545,6]]},"530":{"position":[[749,6]]},"536":{"position":[[875,9],[900,8],[960,8]]},"544":{"position":[[72,6],[732,6],[2179,6]]},"556":{"position":[[228,8],[299,8],[375,8],[588,8],[721,8],[787,8]]},"558":{"position":[[227,8],[346,8],[451,8],[525,8],[635,8],[977,8],[1113,8],[1243,8],[1330,8],[1811,8],[1916,8],[1990,8]]},"560":{"position":[[130,6]]},"572":{"position":[[441,6],[1393,6]]},"574":{"position":[[61,8],[235,6],[395,7],[570,6],[1183,6],[1663,6],[3315,6]]},"586":{"position":[[16,6]]},"588":{"position":[[1323,6]]},"592":{"position":[[8,6]]},"598":{"position":[[543,6]]},"604":{"position":[[259,6]]},"606":{"position":[[166,6]]},"612":{"position":[[72,6],[272,6],[542,6],[1684,6]]},"622":{"position":[[1185,6]]},"624":{"position":[[1663,6]]},"626":{"position":[[4019,6],[4138,6],[4280,6]]},"634":{"position":[[151,6]]},"656":{"position":[[120,6]]},"670":{"position":[[3060,6]]},"686":{"position":[[440,6]]},"690":{"position":[[942,6]]},"696":{"position":[[270,6]]},"700":{"position":[[994,6]]},"716":{"position":[[682,6],[1071,6]]},"722":{"position":[[2438,6]]},"734":{"position":[[2599,6]]},"738":{"position":[[830,6],[6391,6]]},"744":{"position":[[2,6],[466,6]]},"788":{"position":[[81,6]]},"798":{"position":[[1023,6],[1226,6]]},"858":{"position":[[205,6]]},"860":{"position":[[145,8],[1176,6],[2259,6]]},"862":{"position":[[2629,6]]},"864":{"position":[[69,6]]},"880":{"position":[[40,6]]},"906":{"position":[[240,6]]},"910":{"position":[[1700,6]]},"918":{"position":[[165,6]]}}}],["common.sh",{"_index":2763,"t":{"420":{"position":[[652,9]]}}}],["common.v1.sh",{"_index":2757,"t":{"420":{"position":[[190,12],[335,12],[627,12]]},"424":{"position":[[34,12]]},"430":{"position":[[167,12]]},"438":{"position":[[168,12]]},"442":{"position":[[36,12]]},"512":{"position":[[626,12]]}}}],["common.v2.sh",{"_index":2765,"t":{"420":{"position":[[809,13]]},"512":{"position":[[658,12]]},"574":{"position":[[300,12]]}}}],["common.v3.sh",{"_index":2766,"t":{"420":{"position":[[823,12]]},"574":{"position":[[802,12]]},"612":{"position":[[39,12]]}}}],["common.v4.sh",{"_index":3481,"t":{"544":{"position":[[39,12]]}}}],["common1",{"_index":3764,"t":{"612":{"position":[[1735,8]]}}}],["commoncommon",{"_index":2838,"t":{"438":{"position":[[1113,12]]}}}],["commonli",{"_index":2059,"t":{"278":{"position":[[56,8]]},"344":{"position":[[4797,8]]},"366":{"position":[[4720,8]]},"392":{"position":[[33,8]]},"408":{"position":[[15,8]]},"416":{"position":[[58,8]]},"418":{"position":[[74,8],[328,8]]},"424":{"position":[[125,8]]},"426":{"position":[[262,8]]},"430":{"position":[[296,8]]},"438":{"position":[[84,8],[319,8]]},"468":{"position":[[2792,8]]},"508":{"position":[[5509,8]]},"510":{"position":[[92,8],[1585,8]]},"512":{"position":[[314,8],[947,8]]},"552":{"position":[[1059,8]]},"574":{"position":[[609,8],[1734,8]]},"802":{"position":[[85,8]]}}}],["commun",{"_index":920,"t":{"49":{"position":[[763,9]]},"87":{"position":[[228,9]]},"91":{"position":[[262,10]]},"890":{"position":[[2126,11],[2233,14]]},"892":{"position":[[122,11]]},"908":{"position":[[2259,13],[3135,13]]}}}],["compact",{"_index":3528,"t":{"552":{"position":[[940,8]]},"554":{"position":[[1227,7]]},"604":{"position":[[916,7],[1289,7]]},"694":{"position":[[1004,7]]}}}],["compani",{"_index":1304,"t":{"95":{"position":[[150,9]]}}}],["compar",{"_index":916,"t":{"49":{"position":[[394,7]]},"135":{"position":[[655,7]]},"217":{"position":[[175,7]]},"566":{"position":[[769,8],[838,7]]}}}],["comparison",{"_index":4662,"t":{"776":{"position":[[327,11]]}}}],["compat",{"_index":1587,"t":{"189":{"position":[[1315,10]]},"318":{"position":[[214,13]]},"324":{"position":[[170,14]]},"878":{"position":[[364,10]]}}}],["compel",{"_index":4566,"t":{"754":{"position":[[518,10]]}}}],["compil",{"_index":76,"t":{"8":{"position":[[293,7]]},"91":{"position":[[975,9],[2740,8]]},"302":{"position":[[1355,8]]},"796":{"position":[[1081,8],[1177,12]]}}}],["complain",{"_index":5168,"t":{"900":{"position":[[1604,11]]}}}],["complement",{"_index":4699,"t":{"798":{"position":[[198,10]]}}}],["complet",{"_index":665,"t":{"45":{"position":[[268,9]]},"67":{"position":[[292,10]]},"69":{"position":[[488,10],[2609,8],[3605,8],[5121,9],[6452,8]]},"83":{"position":[[972,8]]},"135":{"position":[[1368,10]]},"262":{"position":[[1219,9]]},"312":{"position":[[272,12]]},"332":{"position":[[785,9]]},"346":{"position":[[359,8],[630,8]]},"348":{"position":[[523,8]]},"508":{"position":[[463,8]]},"510":{"position":[[1458,13],[2385,8]]},"590":{"position":[[510,9],[605,10]]},"626":{"position":[[2775,10]]},"640":{"position":[[1456,10]]},"666":{"position":[[1820,10]]},"670":{"position":[[1743,10]]},"690":{"position":[[808,9]]},"696":{"position":[[4200,10],[5220,8]]},"720":{"position":[[3932,8],[4171,8]]},"734":{"position":[[3463,11]]},"738":{"position":[[3619,10]]},"744":{"position":[[664,9]]},"778":{"position":[[2190,10]]},"802":{"position":[[2842,10]]},"804":{"position":[[278,8]]},"806":{"position":[[783,8]]},"808":{"position":[[1762,9]]},"838":{"position":[[954,8]]},"846":{"position":[[364,8]]},"870":{"position":[[538,10]]},"896":{"position":[[2989,10]]},"902":{"position":[[1147,11]]},"914":{"position":[[120,10],[269,10]]},"969":{"position":[[259,8]]}}}],["complete\"^j",{"_index":1554,"t":{"181":{"position":[[255,12]]}}}],["complex",{"_index":345,"t":{"23":{"position":[[263,10],[325,10]]},"91":{"position":[[3287,11],[3518,7]]},"95":{"position":[[1839,7],[2312,7]]},"175":{"position":[[80,7]]},"203":{"position":[[18,7]]},"221":{"position":[[134,7]]},"231":{"position":[[2300,7]]},"237":{"position":[[448,8],[876,7]]},"241":{"position":[[872,7]]},"251":{"position":[[1552,7]]},"259":{"position":[[751,7]]},"260":{"position":[[1185,7]]},"282":{"position":[[961,8]]},"288":{"position":[[691,7],[1405,10]]},"336":{"position":[[145,7],[1177,8],[1502,8],[1589,7]]},"338":{"position":[[41,7],[179,10]]},"342":{"position":[[59,10]]},"352":{"position":[[889,10],[3023,8]]},"354":{"position":[[755,7]]},"360":{"position":[[354,8]]},"382":{"position":[[113,7],[257,7]]},"384":{"position":[[42,7],[329,7]]},"386":{"position":[[1206,7]]},"404":{"position":[[988,7]]},"406":{"position":[[418,7]]},"448":{"position":[[1337,7]]},"450":{"position":[[5260,7],[5480,7]]},"468":{"position":[[2413,7]]},"470":{"position":[[1312,7]]},"476":{"position":[[527,7]]},"492":{"position":[[751,7]]},"502":{"position":[[1105,7]]},"506":{"position":[[2274,7]]},"524":{"position":[[1027,7]]},"528":{"position":[[778,7]]},"572":{"position":[[36,7],[1258,8]]},"580":{"position":[[380,8]]},"588":{"position":[[57,7],[2439,7],[2621,7]]},"596":{"position":[[1084,7]]},"626":{"position":[[1151,7],[4822,8],[6874,8]]},"670":{"position":[[497,8],[581,7],[2873,7]]},"714":{"position":[[1805,8]]},"716":{"position":[[1011,8]]},"734":{"position":[[2054,7]]},"744":{"position":[[358,8]]},"756":{"position":[[62,7]]},"772":{"position":[[1137,7]]},"784":{"position":[[219,7]]},"786":{"position":[[178,7]]},"794":{"position":[[389,8],[512,7],[650,7],[1033,8]]},"796":{"position":[[1255,7],[1710,7]]},"800":{"position":[[255,7]]},"810":{"position":[[1192,7],[1541,7]]},"852":{"position":[[1843,7]]},"860":{"position":[[2147,7]]},"862":{"position":[[4307,9]]},"876":{"position":[[304,7]]},"886":{"position":[[51,7]]},"910":{"position":[[1890,7]]},"1027":{"position":[[9057,7],[9255,7]]},"1033":{"position":[[20,7]]}}}],["compliant",{"_index":4447,"t":{"734":{"position":[[1619,9]]},"878":{"position":[[333,9]]}}}],["complic",{"_index":2097,"t":{"284":{"position":[[1598,11]]},"336":{"position":[[233,11]]},"632":{"position":[[125,12]]},"696":{"position":[[5863,12]]},"710":{"position":[[467,11]]}}}],["compon",{"_index":1064,"t":{"69":{"position":[[4712,10]]},"91":{"position":[[3526,11]]},"278":{"position":[[1019,10]]},"366":{"position":[[3935,9],[3948,10]]},"386":{"position":[[266,10]]},"396":{"position":[[695,11]]},"708":{"position":[[528,10]]},"985":{"position":[[20,9],[216,10],[323,9],[698,9]]},"987":{"position":[[21,9],[270,10],[380,9]]},"989":{"position":[[10,9],[300,10],[377,9]]},"991":{"position":[[49,9]]}}}],["compos",{"_index":2559,"t":{"378":{"position":[[4347,9]]},"384":{"position":[[595,7]]},"404":{"position":[[973,7]]}}}],["compound",{"_index":5040,"t":{"868":{"position":[[11,8]]}}}],["compress",{"_index":3918,"t":{"654":{"position":[[2310,11]]},"658":{"position":[[1501,11]]},"858":{"position":[[1067,10]]}}}],["compris",{"_index":713,"t":{"47":{"position":[[1386,11]]}}}],["comput",{"_index":148,"t":{"15":{"position":[[2,8]]},"21":{"position":[[862,9]]},"57":{"position":[[190,8],[491,8],[670,8],[766,10],[1058,9],[1439,9]]},"59":{"position":[[55,9]]},"69":{"position":[[357,8],[523,8],[979,9],[4907,9],[6238,8]]},"71":{"position":[[690,9]]},"77":{"position":[[721,9],[1081,9],[2024,8],[3045,9]]},"83":{"position":[[134,9]]},"87":{"position":[[971,10]]},"93":{"position":[[188,10]]},"107":{"position":[[84,8]]},"135":{"position":[[262,9]]},"139":{"position":[[643,9]]},"141":{"position":[[524,10]]},"237":{"position":[[1223,8]]},"262":{"position":[[223,8]]},"266":{"position":[[48,8],[396,9],[478,9],[1215,9]]},"270":{"position":[[136,8]]},"272":{"position":[[55,9],[102,8],[215,9]]},"274":{"position":[[61,8],[250,9]]},"276":{"position":[[668,9],[944,9]]},"280":{"position":[[885,9]]},"282":{"position":[[591,9],[626,8],[777,10],[934,9],[1383,8],[1504,8],[1611,8],[1765,8],[2250,9]]},"284":{"position":[[599,8]]},"288":{"position":[[670,8]]},"290":{"position":[[1506,8]]},"292":{"position":[[506,8],[956,8],[1112,8]]},"464":{"position":[[1845,8]]},"532":{"position":[[672,8]]},"550":{"position":[[1667,8]]},"636":{"position":[[20,8],[1179,9]]},"716":{"position":[[703,9]]},"724":{"position":[[604,10]]},"732":{"position":[[1479,9]]},"816":{"position":[[1432,9]]},"892":{"position":[[2501,9]]},"908":{"position":[[2481,10],[3357,10]]},"959":{"position":[[147,9]]}}}],["computerphile1",{"_index":2613,"t":{"392":{"position":[[840,15]]}}}],["con",{"_index":615,"t":{"41":{"position":[[440,3]]}}}],["concaten",{"_index":1429,"t":{"121":{"position":[[81,13],[291,12],[353,13],[437,11],[1105,12],[1191,13],[1554,12]]},"125":{"position":[[1170,14]]}}}],["concept",{"_index":99,"t":{"10":{"position":[[121,7]]},"13":{"position":[[355,7]]},"31":{"position":[[2488,7]]},"81":{"position":[[1784,8]]},"147":{"position":[[489,8]]},"189":{"position":[[2002,8]]},"249":{"position":[[1640,7]]},"262":{"position":[[385,9],[2507,8]]},"268":{"position":[[95,8]]},"290":{"position":[[35,8]]},"342":{"position":[[1306,7]]},"344":{"position":[[243,7]]},"352":{"position":[[602,9],[658,9]]},"356":{"position":[[102,8]]},"378":{"position":[[3086,8],[4134,9]]},"492":{"position":[[41,7]]},"606":{"position":[[61,7],[204,8]]},"622":{"position":[[1575,8]]},"672":{"position":[[161,8],[175,7]]},"684":{"position":[[3008,8]]},"704":{"position":[[46,8]]},"790":{"position":[[34,7]]},"846":{"position":[[34,7]]},"886":{"position":[[126,8]]},"939":{"position":[[321,9],[441,8]]}}}],["concern",{"_index":2561,"t":{"378":{"position":[[4658,8]]},"908":{"position":[[2244,9],[3120,9]]}}}],["concis",{"_index":2596,"t":{"390":{"position":[[967,7],[1113,8]]},"436":{"position":[[25,7]]}}}],["concret",{"_index":1868,"t":{"247":{"position":[[189,8]]}}}],["concurr",{"_index":4092,"t":{"678":{"position":[[910,11]]}}}],["condit",{"_index":3358,"t":{"514":{"position":[[758,10]]},"536":{"position":[[436,11]]},"538":{"position":[[3707,11]]},"550":{"position":[[85,10],[174,12],[198,12],[222,12],[380,13],[899,11],[1119,11],[1368,11],[1499,11]]},"552":{"position":[[74,9],[107,9]]},"556":{"position":[[95,9]]},"558":{"position":[[137,10],[2119,9],[2357,9]]},"564":{"position":[[38,11],[131,11],[683,12]]},"566":{"position":[[0,12],[121,11],[141,11],[285,11],[1011,9],[1076,11]]},"568":{"position":[[40,11]]},"576":{"position":[[107,11],[259,11]]},"580":{"position":[[140,12],[164,12],[188,12]]},"590":{"position":[[442,13]]},"594":{"position":[[64,9],[150,12],[174,12],[198,12],[293,11],[325,11],[1190,9]]},"600":{"position":[[195,12],[219,12],[243,12],[345,11],[377,11],[1696,9]]},"634":{"position":[[2125,11]]},"850":{"position":[[2106,11],[2167,11]]},"858":{"position":[[3256,10]]},"878":{"position":[[535,11]]},"918":{"position":[[484,13]]}}}],["confid",{"_index":1576,"t":{"185":{"position":[[1174,9]]}}}],["confidenti",{"_index":5216,"t":{"908":{"position":[[2287,15],[3163,15]]}}}],["config",{"_index":2389,"t":{"364":{"position":[[273,6],[953,6]]},"366":{"position":[[578,6],[1502,6],[1586,6],[1915,6],[3619,6]]},"370":{"position":[[577,6]]},"372":{"position":[[452,6]]},"746":{"position":[[566,6],[661,6],[783,7]]},"838":{"position":[[1868,6]]},"902":{"position":[[214,6],[1277,7],[1479,6]]},"908":{"position":[[1862,7]]}}}],["config.host",{"_index":2671,"t":{"400":{"position":[[402,12]]}}}],["config.host}:${config.port",{"_index":2674,"t":{"400":{"position":[[472,32]]}}}],["config.port",{"_index":2672,"t":{"400":{"position":[[421,11]]}}}],["config.sh#!/usr/bin/env",{"_index":2383,"t":{"364":{"position":[[146,23],[879,23]]},"366":{"position":[[448,23],[1788,23],[3492,23]]},"370":{"position":[[450,23]]},"372":{"position":[[325,23]]}}}],["config.shmkdir",{"_index":2432,"t":{"368":{"position":[[114,14],[875,14]]}}}],["config/gcloud/credentials.db",{"_index":2398,"t":{"364":{"position":[[475,31],[1175,31]]},"366":{"position":[[795,31],[2132,31]]},"368":{"position":[[316,31],[1077,31]]},"370":{"position":[[811,31]]},"372":{"position":[[688,31]]}}}],["config/v1/ag",{"_index":2725,"t":{"402":{"position":[[709,20],[1069,20]]}}}],["config_fil",{"_index":4556,"t":{"746":{"position":[[616,11],[631,19],[697,16],[890,17],[947,16],[1012,16]]}}}],["configur",{"_index":398,"t":{"23":{"position":[[1985,9],[2338,13],[2426,13]]},"59":{"position":[[114,13]]},"61":{"position":[[480,13]]},"63":{"position":[[382,13]]},"67":{"position":[[128,14]]},"69":{"position":[[3468,9]]},"77":{"position":[[475,11]]},"83":{"position":[[291,9]]},"175":{"position":[[1814,10],[2964,11]]},"179":{"position":[[539,13],[657,13]]},"185":{"position":[[386,9],[479,13],[504,13],[538,9]]},"288":{"position":[[1144,9]]},"292":{"position":[[2130,9]]},"306":{"position":[[257,13]]},"332":{"position":[[181,10]]},"364":{"position":[[50,13],[792,13]]},"378":{"position":[[663,13]]},"436":{"position":[[298,13],[357,11]]},"480":{"position":[[735,9]]},"484":{"position":[[721,13]]},"586":{"position":[[1674,9]]},"618":{"position":[[196,11],[280,14],[477,13]]},"622":{"position":[[62,14],[700,16],[1043,13],[1137,9],[1252,13],[1319,13],[1484,13],[1528,14]]},"624":{"position":[[1128,13],[1522,13],[1689,9]]},"626":{"position":[[303,13],[1708,9],[3088,9],[3850,10],[4255,14],[4721,14],[4888,11],[4967,9],[5933,13],[7329,13],[7375,13]]},"628":{"position":[[68,9],[145,13]]},"630":{"position":[[47,11],[347,10]]},"634":{"position":[[2492,13],[2898,13]]},"636":{"position":[[231,13],[522,14],[671,10],[713,13],[2368,13],[2504,13],[2550,14]]},"638":{"position":[[777,13]]},"640":{"position":[[142,13],[727,13],[1022,13]]},"642":{"position":[[398,9]]},"648":{"position":[[2462,11]]},"650":{"position":[[46,13]]},"654":{"position":[[1616,9]]},"664":{"position":[[537,14]]},"666":{"position":[[294,14],[398,16]]},"670":{"position":[[1320,14]]},"678":{"position":[[92,13]]},"680":{"position":[[374,13]]},"708":{"position":[[170,13]]},"718":{"position":[[1274,14]]},"722":{"position":[[61,9]]},"726":{"position":[[187,9],[272,11],[365,9],[484,13]]},"732":{"position":[[407,13],[719,13],[896,13],[954,13],[1012,13],[1089,13],[1170,13],[1347,13],[1649,14],[1691,13],[1917,14],[1964,13],[2026,15]]},"734":{"position":[[3385,13],[3628,13],[3709,13]]},"736":{"position":[[115,13],[170,13],[464,13],[637,13],[1308,13]]},"738":{"position":[[121,13],[1036,13],[1167,13],[1830,13],[1873,13],[3909,13],[6423,9]]},"742":{"position":[[770,13],[1152,13]]},"744":{"position":[[66,13],[211,13],[236,13],[315,13],[415,13],[483,13],[502,13],[557,13],[636,13],[702,13],[786,14],[997,13],[1103,14],[1166,13],[2223,13],[2371,14],[2514,14]]},"746":{"position":[[228,14],[533,13],[1118,13],[1186,13],[1290,13],[1691,14],[1774,9]]},"748":{"position":[[43,13],[156,13],[206,13]]},"818":{"position":[[408,13],[628,10],[1191,13],[1229,13]]},"834":{"position":[[22,13],[168,13],[300,13],[345,9],[594,9],[811,13],[960,9],[1028,13],[1076,13],[1425,13],[4727,13],[4961,10]]},"836":{"position":[[46,13],[336,15]]},"838":{"position":[[2306,13]]},"846":{"position":[[171,9],[450,9]]},"880":{"position":[[278,10]]},"896":{"position":[[1323,9]]},"902":{"position":[[576,13]]},"910":{"position":[[110,13],[982,13],[1200,13],[1678,13],[1731,13],[1784,11]]},"912":{"position":[[237,9]]},"927":{"position":[[141,13]]}}}],["configuration...remote:remot",{"_index":4009,"t":{"666":{"position":[[222,30]]}}}],["configuration.ln",{"_index":4550,"t":{"746":{"position":[[422,16]]}}}],["configuration.rm",{"_index":4933,"t":{"850":{"position":[[2036,16]]}}}],["confirm",{"_index":1097,"t":{"71":{"position":[[2040,7]]},"211":{"position":[[257,13],[728,7]]},"215":{"position":[[112,12],[642,12]]},"219":{"position":[[1670,12]]},"354":{"position":[[1199,7]]},"462":{"position":[[2633,7]]},"470":{"position":[[54,7],[1117,12]]},"474":{"position":[[184,13]]},"572":{"position":[[607,10],[884,10]]},"626":{"position":[[817,12]]},"684":{"position":[[1478,12]]},"700":{"position":[[1487,7]]},"832":{"position":[[936,12]]},"892":{"position":[[1370,12]]}}}],["conflict",{"_index":3990,"t":{"660":{"position":[[1612,9]]},"670":{"position":[[715,10]]},"696":{"position":[[90,10],[101,9],[312,10],[808,9],[1187,10],[1238,9],[1841,9],[1934,8],[1996,9],[2181,10],[2365,9],[2685,9],[2841,9],[3198,9],[4727,11],[5169,12],[5836,9]]},"704":{"position":[[216,9]]}}}],["confus",{"_index":1531,"t":{"175":{"position":[[2015,10]]},"237":{"position":[[562,10],[651,7],[834,10]]},"253":{"position":[[3723,9]]},"378":{"position":[[3987,7]]},"464":{"position":[[463,9],[2020,8]]},"490":{"position":[[524,9]]},"494":{"position":[[24,9]]},"504":{"position":[[17,9]]},"506":{"position":[[2392,9]]},"508":{"position":[[1402,9],[4789,9],[5064,9]]},"530":{"position":[[714,9]]},"536":{"position":[[136,9]]},"586":{"position":[[877,9]]},"604":{"position":[[1507,9]]},"616":{"position":[[140,9]]},"628":{"position":[[391,10]]},"630":{"position":[[318,8]]},"638":{"position":[[299,10]]},"756":{"position":[[523,9]]},"874":{"position":[[1148,9]]},"876":{"position":[[465,10]]},"1033":{"position":[[55,10],[567,10],[2751,9]]},"1037":{"position":[[622,10]]}}}],["confusingli",{"_index":2595,"t":{"390":{"position":[[846,12]]},"1003":{"position":[[78,11]]}}}],["congenit",{"_index":4781,"t":{"804":{"position":[[2553,10]]}}}],["congratul",{"_index":3988,"t":{"660":{"position":[[1441,15]]},"894":{"position":[[1222,17],[1389,15]]}}}],["connect",{"_index":1896,"t":{"251":{"position":[[1161,8],[1249,9]]},"282":{"position":[[1031,9],[1461,9],[1958,10]]},"292":{"position":[[998,7]]},"622":{"position":[[396,9]]},"632":{"position":[[88,9]]},"818":{"position":[[930,10],[1489,7],[1556,7],[1653,10]]},"838":{"position":[[163,7],[750,7],[975,7],[1091,10],[1224,10],[1534,7],[1680,10],[2446,7]]},"858":{"position":[[1496,12]]},"884":{"position":[[113,10],[276,12],[356,7],[1051,12]]},"888":{"position":[[514,7]]},"890":{"position":[[2012,10]]},"894":{"position":[[56,7],[1529,7]]},"896":{"position":[[1660,7],[2286,7],[2577,9]]},"898":{"position":[[81,10],[284,7],[338,7],[415,8],[538,8],[663,7],[691,8],[1471,10],[1554,9],[1643,7],[1826,7],[2003,10],[2082,11],[2850,10]]},"900":{"position":[[57,7]]},"902":{"position":[[172,7],[274,7],[660,7],[909,7],[974,7],[999,7]]},"906":{"position":[[146,13]]},"908":{"position":[[741,10],[1922,7],[2667,10]]},"910":{"position":[[666,7],[1003,7],[1295,7],[1528,10],[1603,7]]},"912":{"position":[[80,11],[197,7],[280,11],[360,12]]}}}],["consid",{"_index":90,"t":{"10":{"position":[[0,8],[36,8]]},"87":{"position":[[666,8]]},"89":{"position":[[91,8]]},"95":{"position":[[560,8],[635,8]]},"219":{"position":[[831,10]]},"320":{"position":[[156,10]]},"382":{"position":[[339,8]]},"490":{"position":[[595,8]]},"550":{"position":[[1613,10]]},"596":{"position":[[2107,8]]},"626":{"position":[[6068,10],[7072,8]]},"724":{"position":[[278,8]]},"794":{"position":[[1169,8]]},"810":{"position":[[765,11]]},"812":{"position":[[77,8]]},"862":{"position":[[3880,8]]},"876":{"position":[[22,11]]},"910":{"position":[[295,8]]},"1007":{"position":[[39,10]]},"1033":{"position":[[2300,10]]}}}],["consider",{"_index":4389,"t":{"720":{"position":[[4242,12]]},"728":{"position":[[220,12]]},"796":{"position":[[1454,13]]}}}],["consist",{"_index":263,"t":{"21":{"position":[[1585,10]]},"59":{"position":[[182,10]]},"63":{"position":[[419,10]]},"65":{"position":[[179,13]]},"81":{"position":[[1130,10]]},"113":{"position":[[144,10]]},"221":{"position":[[790,10]]},"288":{"position":[[1545,13]]},"490":{"position":[[1705,10]]},"724":{"position":[[1124,12]]},"862":{"position":[[607,12]]},"874":{"position":[[842,12]]},"997":{"position":[[472,10]]}}}],["consol",{"_index":2123,"t":{"290":{"position":[[2033,7]]},"292":{"position":[[395,8],[434,7]]},"796":{"position":[[492,7]]},"894":{"position":[[1297,8],[1337,8]]},"896":{"position":[[41,7],[2912,7]]}}}],["consolid",{"_index":4187,"t":{"696":{"position":[[2064,11]]}}}],["const",{"_index":2679,"t":{"400":{"position":[[1053,5],[1115,5]]}}}],["const=80",{"_index":4799,"t":{"806":{"position":[[1002,9]]}}}],["constant",{"_index":1866,"t":{"243":{"position":[[1963,9]]}}}],["construct",{"_index":1648,"t":{"205":{"position":[[994,10]]},"213":{"position":[[961,9]]},"304":{"position":[[780,10]]},"306":{"position":[[596,10]]},"352":{"position":[[1563,10],[2554,9]]},"396":{"position":[[610,10]]},"428":{"position":[[1409,9]]},"462":{"position":[[2692,10]]},"468":{"position":[[2403,9]]},"510":{"position":[[1599,10]]},"514":{"position":[[200,10]]},"584":{"position":[[1532,10]]},"604":{"position":[[55,10]]},"606":{"position":[[421,11]]},"878":{"position":[[550,10]]}}}],["consum",{"_index":731,"t":{"47":{"position":[[2330,8]]},"57":{"position":[[1197,9]]},"237":{"position":[[661,8]]},"266":{"position":[[1105,9]]},"352":{"position":[[1438,10]]},"636":{"position":[[2308,10]]}}}],["contain",{"_index":156,"t":{"15":{"position":[[159,11]]},"29":{"position":[[681,8]]},"41":{"position":[[723,7]]},"47":{"position":[[1350,8],[2578,8],[5126,8],[7478,8]]},"81":{"position":[[1366,8]]},"83":{"position":[[832,8]]},"93":{"position":[[1052,9],[1074,11],[1089,9],[1165,10],[1601,10]]},"103":{"position":[[397,8]]},"119":{"position":[[786,9]]},"137":{"position":[[80,8]]},"217":{"position":[[366,7]]},"257":{"position":[[3336,8]]},"259":{"position":[[523,8]]},"278":{"position":[[1187,8]]},"280":{"position":[[363,7]]},"304":{"position":[[2647,10]]},"346":{"position":[[298,8],[620,7]]},"394":{"position":[[812,7],[1027,7]]},"414":{"position":[[41,8]]},"454":{"position":[[2492,7]]},"480":{"position":[[705,7]]},"482":{"position":[[916,7]]},"526":{"position":[[542,8]]},"640":{"position":[[114,8]]},"690":{"position":[[882,7]]},"716":{"position":[[90,8]]},"734":{"position":[[50,7]]},"738":{"position":[[59,7]]},"744":{"position":[[920,8]]},"748":{"position":[[847,10]]},"754":{"position":[[1090,10]]},"816":{"position":[[720,8],[910,8],[1172,8]]},"862":{"position":[[3736,7]]},"874":{"position":[[54,7]]},"896":{"position":[[2678,8]]},"943":{"position":[[124,8]]},"985":{"position":[[1752,9]]},"1033":{"position":[[4400,7]]}}}],["containeris",{"_index":1295,"t":{"93":{"position":[[1349,13]]}}}],["content",{"_index":218,"t":{"21":{"position":[[561,8]]},"25":{"position":[[530,8],[638,8]]},"27":{"position":[[258,8],[812,8]]},"29":{"position":[[24,8],[646,8]]},"31":{"position":[[2728,8]]},"41":{"position":[[359,8],[831,7],[1027,7]]},"43":{"position":[[1091,7],[1165,8]]},"51":{"position":[[481,7],[656,7]]},"73":{"position":[[145,7]]},"77":{"position":[[1949,8],[2290,8],[2494,8],[3419,8],[4176,8],[4475,8]]},"83":{"position":[[549,8]]},"107":{"position":[[312,8]]},"121":{"position":[[678,8]]},"125":{"position":[[1210,8]]},"129":{"position":[[337,8]]},"131":{"position":[[129,8],[158,8],[304,8],[339,8],[417,8],[572,8]]},"133":{"position":[[204,8]]},"137":{"position":[[1051,8]]},"139":{"position":[[398,9]]},"145":{"position":[[221,8],[304,8]]},"175":{"position":[[574,8],[1462,8],[2369,8]]},"217":{"position":[[594,8]]},"231":{"position":[[1558,7]]},"249":{"position":[[1234,8]]},"251":{"position":[[63,8]]},"253":{"position":[[2399,8],[2476,8]]},"262":{"position":[[2105,7]]},"288":{"position":[[65,8]]},"296":{"position":[[241,8],[330,8]]},"302":{"position":[[1477,8]]},"350":{"position":[[480,8],[1138,8]]},"354":{"position":[[1396,7]]},"370":{"position":[[109,7]]},"376":{"position":[[54,8],[251,7],[425,7]]},"378":{"position":[[4543,8]]},"424":{"position":[[1161,7]]},"434":{"position":[[123,8],[550,8]]},"446":{"position":[[1564,7],[2328,8],[2811,8],[3433,8]]},"450":{"position":[[2488,8],[5397,7]]},"472":{"position":[[687,8]]},"500":{"position":[[452,8]]},"574":{"position":[[2912,7]]},"594":{"position":[[986,8]]},"596":{"position":[[470,8]]},"602":{"position":[[455,8],[703,9],[914,9],[1079,9]]},"626":{"position":[[4852,8]]},"658":{"position":[[1026,7],[1238,8]]},"668":{"position":[[674,7]]},"674":{"position":[[436,7]]},"682":{"position":[[993,8],[3574,7]]},"696":{"position":[[656,7],[724,7],[1917,10],[3974,7]]},"710":{"position":[[188,8]]},"712":{"position":[[156,8]]},"718":{"position":[[389,9],[718,8]]},"722":{"position":[[1769,8]]},"732":{"position":[[544,9]]},"734":{"position":[[272,8]]},"776":{"position":[[95,8]]},"862":{"position":[[760,7]]},"874":{"position":[[108,8]]},"896":{"position":[[626,9]]},"1007":{"position":[[23,7],[175,7]]},"1019":{"position":[[896,8],[1051,8]]},"1033":{"position":[[3932,8]]}}}],["content={asymmetricencrypt",{"_index":5317,"t":{"981":{"position":[[162,30]]},"1015":{"position":[[162,30]]}}}],["content_length",{"_index":2726,"t":{"402":{"position":[[730,17],[1090,17]]}}}],["context",{"_index":1530,"t":{"175":{"position":[[1924,7]]},"253":{"position":[[3498,7]]},"310":{"position":[[572,7]]},"400":{"position":[[1334,8],[1986,7]]},"406":{"position":[[678,8]]},"410":{"position":[[399,8]]},"430":{"position":[[595,9]]},"434":{"position":[[1135,7]]},"470":{"position":[[682,7],[731,7],[798,7]]},"484":{"position":[[738,7]]},"734":{"position":[[877,7]]},"764":{"position":[[325,7]]}}}],["context[=num",{"_index":2591,"t":{"390":{"position":[[345,14]]}}}],["continu",{"_index":347,"t":{"23":{"position":[[311,9]]},"25":{"position":[[1073,9]]},"31":{"position":[[2349,10]]},"45":{"position":[[278,11]]},"57":{"position":[[1484,8]]},"69":{"position":[[2470,8]]},"79":{"position":[[657,8]]},"121":{"position":[[1697,8]]},"129":{"position":[[1159,8]]},"139":{"position":[[1235,8]]},"207":{"position":[[286,9]]},"227":{"position":[[25,8],[731,9]]},"229":{"position":[[946,8]]},"231":{"position":[[1213,8],[1390,8],[1427,9],[1509,10],[1949,8],[1993,9],[2133,9]]},"235":{"position":[[29,8]]},"239":{"position":[[574,8]]},"243":{"position":[[347,8],[1237,8]]},"260":{"position":[[1335,9]]},"350":{"position":[[193,9]]},"426":{"position":[[50,14],[517,12]]},"440":{"position":[[174,13]]},"502":{"position":[[1338,8]]},"508":{"position":[[3795,10],[4182,9],[4879,8]]},"538":{"position":[[79,8],[2005,8]]},"586":{"position":[[2182,9],[2343,8],[2367,8]]},"594":{"position":[[637,8],[1732,8]]},"602":{"position":[[24,8],[190,8],[352,8],[518,8],[862,9],[1164,8]]},"722":{"position":[[139,14],[678,14],[1000,10]]},"738":{"position":[[1301,8]]},"744":{"position":[[1475,8]]},"746":{"position":[[719,8]]},"778":{"position":[[653,8]]},"802":{"position":[[1604,8],[2701,13],[3015,8]]},"850":{"position":[[31,8],[215,10],[1121,8],[1801,8],[1950,8]]},"898":{"position":[[1462,8],[1662,9],[2073,8]]},"1035":{"position":[[3018,9]]}}}],["contrast",{"_index":3568,"t":{"570":{"position":[[768,8]]}}}],["contribut",{"_index":3,"t":{"4":{"position":[[24,11]]},"664":{"position":[[2378,13]]}}}],["contributor",{"_index":4007,"t":{"664":{"position":[[2425,13]]}}}],["control",{"_index":228,"t":{"21":{"position":[[798,7],[837,7],[893,7],[1516,7],[1683,7],[1807,7],[4069,7]]},"57":{"position":[[180,7]]},"69":{"position":[[6157,7]]},"175":{"position":[[1065,7]]},"185":{"position":[[207,7]]},"217":{"position":[[1430,7]]},"235":{"position":[[812,7],[932,7]]},"239":{"position":[[213,9]]},"243":{"position":[[563,7],[592,8],[718,8],[1344,7],[1581,8]]},"247":{"position":[[463,7]]},"292":{"position":[[1284,7]]},"310":{"position":[[405,7]]},"474":{"position":[[198,7]]},"512":{"position":[[776,7]]},"574":{"position":[[439,7],[932,7]]},"670":{"position":[[540,7]]},"678":{"position":[[251,7],[863,7],[1151,7],[1304,7]]},"696":{"position":[[50,7],[1161,7]]},"698":{"position":[[214,7]]},"714":{"position":[[3452,8]]},"716":{"position":[[824,7],[1130,7],[1231,7]]},"726":{"position":[[546,7]]},"738":{"position":[[4319,7],[4447,7]]},"748":{"position":[[396,7]]},"760":{"position":[[1811,7],[2053,7]]},"762":{"position":[[478,11]]},"778":{"position":[[1728,8],[1740,7]]},"806":{"position":[[2764,7]]},"830":{"position":[[913,8]]},"834":{"position":[[509,7]]},"862":{"position":[[734,7]]},"910":{"position":[[618,7]]},"953":{"position":[[409,7]]},"985":{"position":[[1692,8]]},"1027":{"position":[[8928,7]]}}}],["conveni",{"_index":1165,"t":{"81":{"position":[[1524,11]]},"175":{"position":[[2844,10]]},"211":{"position":[[116,10]]},"310":{"position":[[522,12]]},"624":{"position":[[1399,12]]},"626":{"position":[[5798,10]]},"662":{"position":[[700,10]]},"668":{"position":[[166,10]]},"684":{"position":[[1085,11]]},"702":{"position":[[2600,10]]},"858":{"position":[[1690,10]]},"902":{"position":[[685,10]]},"910":{"position":[[1771,12]]},"1027":{"position":[[1596,11],[1826,10],[2589,10],[5784,10]]}}}],["convent",{"_index":259,"t":{"21":{"position":[[1497,11]]},"47":{"position":[[586,11],[691,13]]},"249":{"position":[[407,11],[577,12],[656,12]]},"253":{"position":[[1624,10]]},"438":{"position":[[1445,11]]},"442":{"position":[[1767,11]]},"480":{"position":[[502,11]]},"482":{"position":[[417,11]]},"484":{"position":[[1407,10]]},"622":{"position":[[727,10],[1192,10]]},"636":{"position":[[1422,11]]},"642":{"position":[[177,10]]},"656":{"position":[[127,10]]},"658":{"position":[[618,11]]},"686":{"position":[[447,10],[596,11],[713,10]]},"720":{"position":[[1663,10]]},"744":{"position":[[114,10]]},"798":{"position":[[86,11],[1030,11],[1677,11]]},"858":{"position":[[2154,10]]}}}],["convention1",{"_index":4701,"t":{"798":{"position":[[1233,11]]}}}],["converg",{"_index":1191,"t":{"87":{"position":[[848,11]]}}}],["convers",{"_index":4391,"t":{"720":{"position":[[4651,10]]}}}],["convert",{"_index":3341,"t":{"510":{"position":[[2714,7]]},"714":{"position":[[2252,8]]},"720":{"position":[[3465,7],[4090,7],[4566,8]]},"724":{"position":[[155,7]]},"842":{"position":[[1840,7]]},"1027":{"position":[[4437,9],[9571,9],[9675,9]]}}}],["cool",{"_index":1153,"t":{"79":{"position":[[619,4]]},"99":{"position":[[1992,4]]},"253":{"position":[[1668,4],[3356,4]]},"394":{"position":[[2340,4]]},"520":{"position":[[127,4],[605,4],[660,4]]},"626":{"position":[[6326,4]]},"688":{"position":[[2137,4]]},"804":{"position":[[2692,4]]},"969":{"position":[[140,4]]}}}],["copi",{"_index":205,"t":{"21":{"position":[[305,4],[2986,6],[3411,4],[3457,4],[3642,4],[3777,4]]},"23":{"position":[[0,7],[1494,4]]},"25":{"position":[[98,4],[270,7],[590,4],[1363,4]]},"27":{"position":[[35,4],[144,4],[404,4]]},"29":{"position":[[555,4]]},"31":{"position":[[462,4],[802,4]]},"33":{"position":[[35,4],[348,4]]},"41":{"position":[[134,4],[348,6],[480,6],[603,4],[630,4]]},"51":{"position":[[1139,4]]},"99":{"position":[[16,8]]},"109":{"position":[[59,4],[246,4],[368,5]]},"113":{"position":[[350,4],[688,6]]},"117":{"position":[[6,4],[188,7],[301,4],[405,4],[533,4]]},"125":{"position":[[687,6],[706,4],[764,4]]},"137":{"position":[[584,4],[680,4],[802,4]]},"207":{"position":[[172,5]]},"253":{"position":[[2599,6]]},"364":{"position":[[38,6],[229,4],[909,4]]},"366":{"position":[[531,4],[1458,4],[1539,4],[1871,4],[3575,4]]},"370":{"position":[[533,4]]},"372":{"position":[[408,4],[1104,4]]},"414":{"position":[[619,4]]},"434":{"position":[[1060,4]]},"468":{"position":[[210,4],[533,4],[1904,4]]},"482":{"position":[[1466,4]]},"512":{"position":[[614,4]]},"610":{"position":[[849,4]]},"654":{"position":[[1837,4],[1879,6]]},"662":{"position":[[188,4]]},"664":{"position":[[287,4],[372,4],[839,4]]},"672":{"position":[[393,4]]},"680":{"position":[[538,4],[724,4]]},"736":{"position":[[336,4]]},"744":{"position":[[1224,4]]},"772":{"position":[[452,4],[1499,4]]},"896":{"position":[[617,4]]},"908":{"position":[[40,5],[57,4],[423,4],[896,6],[1246,7],[1381,4],[1441,4],[1695,4],[2536,6],[2698,4],[3525,7]]},"912":{"position":[[456,4]]},"1027":{"position":[[2854,4]]}}}],["copilot",{"_index":5264,"t":{"935":{"position":[[228,7]]}}}],["copy.jpeg",{"_index":1401,"t":{"113":{"position":[[436,9]]},"117":{"position":[[667,10]]}}}],["copy.jpg",{"_index":1395,"t":{"109":{"position":[[224,8]]},"113":{"position":[[409,8]]}}}],["copy/past",{"_index":314,"t":{"21":{"position":[[3334,10]]}}}],["copyright",{"_index":2223,"t":{"314":{"position":[[150,9]]},"432":{"position":[[1108,12]]}}}],["core",{"_index":1015,"t":{"67":{"position":[[920,4]]},"147":{"position":[[9,4],[342,4],[641,4]]},"249":{"position":[[1635,4]]},"276":{"position":[[1098,4]]},"398":{"position":[[523,4]]},"408":{"position":[[948,4]]},"476":{"position":[[54,4]]},"618":{"position":[[58,4]]},"670":{"position":[[378,4],[968,4]]},"672":{"position":[[156,4]]},"704":{"position":[[41,4]]},"910":{"position":[[31,4]]}}}],["correct",{"_index":2008,"t":{"262":{"position":[[3672,7]]},"292":{"position":[[99,11]]},"338":{"position":[[880,7]]},"344":{"position":[[1959,8]]},"354":{"position":[[1062,7]]},"376":{"position":[[1007,8],[1087,8]]},"432":{"position":[[1247,7]]},"456":{"position":[[1006,7]]},"488":{"position":[[741,7]]},"566":{"position":[[1655,9]]},"588":{"position":[[2031,7]]},"626":{"position":[[2751,7]]},"696":{"position":[[246,8],[3571,8],[4825,8]]},"834":{"position":[[1580,7]]},"866":{"position":[[133,7]]}}}],["correctli",{"_index":3556,"t":{"566":{"position":[[759,9]]},"862":{"position":[[445,9]]},"1033":{"position":[[3543,9]]}}}],["correspond",{"_index":2300,"t":{"340":{"position":[[610,11]]},"714":{"position":[[1523,13]]},"826":{"position":[[386,10]]}}}],["cost",{"_index":5121,"t":{"892":{"position":[[2570,4]]},"894":{"position":[[772,6],[888,4],[926,5]]}}}],["cotton",{"_index":4774,"t":{"804":{"position":[[2426,6]]},"806":{"position":[[3372,6]]}}}],["count",{"_index":1675,"t":{"213":{"position":[[177,5],[617,6]]},"253":{"position":[[1209,5],[1563,6]]},"260":{"position":[[883,5]]},"262":{"position":[[2157,5]]},"418":{"position":[[265,5]]},"424":{"position":[[545,6],[560,5],[736,5]]},"442":{"position":[[451,6]]},"506":{"position":[[1055,5],[1076,5]]},"522":{"position":[[947,6],[958,10],[1046,7],[1106,5],[1429,5],[1559,7],[1580,9],[1620,5],[1636,7],[1708,5],[1883,5],[1934,5],[1950,7],[2020,7]]},"526":{"position":[[1972,7]]},"544":{"position":[[627,6],[1026,5],[1080,5],[1313,6],[1406,5],[1552,12]]},"658":{"position":[[1458,8]]},"668":{"position":[[1853,5]]},"718":{"position":[[406,6],[442,5],[788,6],[906,6],[1468,5],[1578,5],[1810,5]]},"720":{"position":[[6219,5]]},"768":{"position":[[41,8],[263,6]]},"772":{"position":[[85,7]]},"774":{"position":[[869,7]]},"778":{"position":[[539,9]]},"860":{"position":[[1210,8]]}}}],["count'.echo",{"_index":3389,"t":{"522":{"position":[[1541,12]]}}}],["count=$((count",{"_index":3383,"t":{"522":{"position":[[916,14]]}}}],["count=$(echo",{"_index":3504,"t":{"544":{"position":[[1446,12]]}}}],["count=0",{"_index":3380,"t":{"522":{"position":[[865,7]]}}}],["counter",{"_index":3382,"t":{"522":{"position":[[907,8]]},"544":{"position":[[1655,10]]},"612":{"position":[[1350,10]]}}}],["counter=$((count",{"_index":3508,"t":{"544":{"position":[[1706,18]]},"612":{"position":[[1370,18]]}}}],["count}\"count",{"_index":3385,"t":{"522":{"position":[[1054,15]]}}}],["coupl",{"_index":337,"t":{"23":{"position":[[103,6]]},"121":{"position":[[845,6]]},"508":{"position":[[3634,6]]},"544":{"position":[[409,6]]},"686":{"position":[[73,6]]},"720":{"position":[[7091,6]]},"742":{"position":[[1001,6]]},"830":{"position":[[346,6]]}}}],["cours",{"_index":246,"t":{"21":{"position":[[1252,6]]},"51":{"position":[[962,7]]},"77":{"position":[[188,6]]},"81":{"position":[[1752,6]]},"91":{"position":[[1340,6]]},"241":{"position":[[254,6]]},"260":{"position":[[1158,7]]},"420":{"position":[[1299,7]]}}}],["cover",{"_index":934,"t":{"51":{"position":[[149,5]]},"53":{"position":[[281,6]]},"77":{"position":[[332,7]]},"189":{"position":[[1989,6]]},"195":{"position":[[2783,7]]},"207":{"position":[[462,7]]},"286":{"position":[[47,7]]},"292":{"position":[[2055,7]]},"330":{"position":[[183,5]]},"344":{"position":[[3931,6]]},"366":{"position":[[2707,7]]},"376":{"position":[[2410,5]]},"410":{"position":[[159,8]]},"440":{"position":[[22,7]]},"442":{"position":[[21,6],[1958,5]]},"588":{"position":[[1372,5],[2121,5]]},"654":{"position":[[4314,5]]},"728":{"position":[[284,7]]},"790":{"position":[[435,7]]},"808":{"position":[[1325,8]]},"886":{"position":[[101,5]]},"953":{"position":[[204,8]]},"1027":{"position":[[329,7],[5496,7]]},"1031":{"position":[[259,7]]}}}],["cp",{"_index":603,"t":{"41":{"position":[[128,2],[153,2],[219,2],[337,2],[578,2]]},"109":{"position":[[106,2],[126,2]]},"117":{"position":[[120,2],[323,2],[547,3],[553,2]]},"125":{"position":[[684,2],[723,2]]},"137":{"position":[[645,2],[662,2]]},"366":{"position":[[173,2],[279,2],[405,2],[1213,2],[1295,2],[1749,3],[2580,4],[2653,4],[2767,3],[2793,3],[2821,3],[2900,2],[2922,2],[3003,3],[3048,2],[3348,2]]},"368":{"position":[[612,2]]},"370":{"position":[[371,2],[1236,2]]},"372":{"position":[[480,3],[537,3],[583,3],[634,3],[684,3],[751,3],[797,3],[859,3],[969,3]]},"386":{"position":[[1445,2]]},"468":{"position":[[359,2],[1093,2],[1964,2]]},"580":{"position":[[1373,2]]},"680":{"position":[[876,2]]},"744":{"position":[[1283,2]]}}}],["cp(1)name",{"_index":602,"t":{"41":{"position":[[118,9]]}}}],["cp/",{"_index":2435,"t":{"370":{"position":[[422,10]]},"372":{"position":[[283,13]]}}}],["cp/s/cp/rmdir",{"_index":2429,"t":{"366":{"position":[[1762,18],[3441,18]]}}}],["cpcp(1",{"_index":599,"t":{"41":{"position":[[82,7]]}}}],["cpu",{"_index":891,"t":{"47":{"position":[[8319,3],[8360,4],[8439,4]]},"274":{"position":[[136,4]]},"292":{"position":[[452,4]]}}}],["cpu1",{"_index":2024,"t":{"272":{"position":[[130,4]]}}}],["cpuwalk.d(1m",{"_index":892,"t":{"47":{"position":[[8330,13]]}}}],["craft",{"_index":2751,"t":{"408":{"position":[[813,5]]},"969":{"position":[[317,5]]}}}],["crash",{"_index":1103,"t":{"77":{"position":[[182,5]]},"81":{"position":[[1746,5]]},"420":{"position":[[1293,5]]},"818":{"position":[[756,8]]}}}],["creat",{"_index":353,"t":{"23":{"position":[[592,7],[674,8],[1474,6],[1902,6],[2331,6]]},"25":{"position":[[15,7]]},"33":{"position":[[267,8]]},"69":{"position":[[2661,6],[3722,6],[4104,7]]},"99":{"position":[[72,6],[239,7]]},"115":{"position":[[212,6],[395,6],[481,6],[599,6],[832,6]]},"119":{"position":[[99,8],[170,7]]},"123":{"position":[[346,7],[601,7]]},"125":{"position":[[852,6],[882,6],[932,7]]},"129":{"position":[[772,6]]},"199":{"position":[[365,7]]},"207":{"position":[[588,6]]},"219":{"position":[[132,6]]},"243":{"position":[[2067,6]]},"251":{"position":[[1545,6]]},"255":{"position":[[1963,8]]},"257":{"position":[[246,7],[5807,8]]},"262":{"position":[[1570,6],[1669,6],[1956,6]]},"266":{"position":[[454,8],[1071,8]]},"288":{"position":[[579,7],[635,8]]},"296":{"position":[[444,7]]},"302":{"position":[[11,6]]},"306":{"position":[[170,7]]},"338":{"position":[[714,6],[1613,6]]},"346":{"position":[[982,6]]},"350":{"position":[[839,6]]},"374":{"position":[[331,6]]},"376":{"position":[[5678,7]]},"378":{"position":[[116,6],[831,6],[3175,8],[4312,6]]},"416":{"position":[[6,6]]},"418":{"position":[[15,6]]},"420":{"position":[[85,8],[169,6],[268,6],[324,6],[362,6],[489,6],[802,6],[900,7],[928,7],[1064,7]]},"426":{"position":[[41,6]]},"428":{"position":[[230,8]]},"434":{"position":[[223,7],[449,6],[2073,6]]},"438":{"position":[[720,6],[851,7],[873,7],[1642,6]]},"440":{"position":[[87,6]]},"448":{"position":[[170,6],[752,6]]},"462":{"position":[[78,6],[390,6],[881,6]]},"464":{"position":[[86,6]]},"476":{"position":[[292,6]]},"482":{"position":[[8,6]]},"484":{"position":[[17,6]]},"488":{"position":[[165,9],[257,8],[341,8],[647,9],[757,8],[1278,9],[1355,8],[1391,8]]},"490":{"position":[[65,7],[792,6]]},"492":{"position":[[200,6],[266,7],[318,6]]},"512":{"position":[[80,7],[605,6]]},"514":{"position":[[836,6]]},"520":{"position":[[165,6],[815,6]]},"522":{"position":[[77,6]]},"524":{"position":[[787,6],[1104,6]]},"526":{"position":[[104,6]]},"534":{"position":[[223,7],[375,6],[463,9],[567,7],[832,9],[1065,8]]},"538":{"position":[[352,6],[570,6],[711,7],[886,6],[1050,7],[1096,7],[1234,8],[1288,6],[1638,6],[1706,7],[2583,8]]},"544":{"position":[[27,7]]},"550":{"position":[[574,6],[655,7],[719,7],[982,7]]},"552":{"position":[[239,8],[354,9],[612,6],[980,9]]},"556":{"position":[[623,7]]},"558":{"position":[[65,6],[956,6]]},"574":{"position":[[324,7],[759,6]]},"582":{"position":[[72,6]]},"588":{"position":[[388,6],[477,6]]},"592":{"position":[[195,6],[285,6],[446,6]]},"594":{"position":[[524,6]]},"596":{"position":[[2160,6]]},"600":{"position":[[588,6],[1521,7]]},"612":{"position":[[27,7]]},"626":{"position":[[4122,6],[5351,6],[5492,7],[5678,7],[5747,7]]},"636":{"position":[[445,6]]},"654":{"position":[[714,6],[878,6],[1006,6],[1038,7],[1443,6],[1537,7],[1671,7],[3670,7],[4167,7]]},"656":{"position":[[339,6]]},"658":{"position":[[1296,7]]},"660":{"position":[[358,6],[553,7],[1472,7],[1551,7],[1568,7]]},"664":{"position":[[432,7],[830,6],[2127,6]]},"666":{"position":[[253,6]]},"672":{"position":[[571,7],[753,6],[790,6]]},"680":{"position":[[131,6],[244,8],[416,6],[814,6],[943,7]]},"684":{"position":[[1634,6],[1664,6],[1833,7],[2492,6],[2716,6],[2766,7]]},"688":{"position":[[82,6],[210,6],[254,6],[388,6],[697,6],[793,6],[1469,6],[2868,6],[3160,6],[3364,6],[3473,6]]},"690":{"position":[[437,6]]},"692":{"position":[[6,6],[131,6],[320,6],[1472,6],[1604,8],[2036,6],[2079,6],[2383,7]]},"694":{"position":[[2245,7]]},"696":{"position":[[1227,8],[1263,6],[1331,6]]},"698":{"position":[[348,6],[416,8]]},"700":{"position":[[1474,9]]},"702":{"position":[[2338,6]]},"704":{"position":[[173,6],[952,6],[1068,6],[1248,6],[1314,6]]},"716":{"position":[[941,6]]},"718":{"position":[[1524,8],[2426,8]]},"736":{"position":[[1246,8]]},"738":{"position":[[100,6],[5548,7],[6045,6]]},"742":{"position":[[173,6],[251,6],[466,6],[507,6],[1057,8]]},"744":{"position":[[2170,6],[2332,7],[2569,7]]},"746":{"position":[[395,6],[1048,7],[1847,7]]},"748":{"position":[[274,7],[720,6]]},"762":{"position":[[271,8],[520,6]]},"764":{"position":[[399,6],[1012,6],[2667,6],[2718,6]]},"800":{"position":[[68,6],[574,6]]},"802":{"position":[[894,6],[2673,6]]},"820":{"position":[[83,7],[222,7]]},"828":{"position":[[77,6],[259,8],[309,6],[393,6],[909,8],[1038,6]]},"830":{"position":[[87,8],[337,6],[370,6],[613,6],[966,6],[1053,6],[1109,6]]},"834":{"position":[[610,6],[745,7],[1716,6],[2445,7]]},"838":{"position":[[602,7],[714,8],[2001,7]]},"842":{"position":[[1689,6],[1720,6]]},"850":{"position":[[763,6],[1399,6]]},"852":{"position":[[286,7],[487,6],[628,7]]},"854":{"position":[[103,8],[245,6],[557,8]]},"856":{"position":[[168,6]]},"858":{"position":[[232,6],[497,6],[1660,8]]},"870":{"position":[[581,6],[707,6]]},"876":{"position":[[380,8],[776,6]]},"878":{"position":[[1387,6]]},"884":{"position":[[989,6]]},"890":{"position":[[47,7],[218,7],[2451,6]]},"892":{"position":[[66,6],[161,6],[267,6],[1032,8],[2302,6]]},"894":{"position":[[15,6],[406,7],[1017,6],[1492,6]]},"896":{"position":[[227,6],[845,6],[1730,8]]},"898":{"position":[[17,7],[386,7]]},"902":{"position":[[186,6],[304,6]]},"908":{"position":[[1273,7],[1807,7]]},"912":{"position":[[147,6]]},"927":{"position":[[288,7],[905,6],[1021,6],[1201,6],[1267,6]]},"987":{"position":[[42,6]]},"1023":{"position":[[536,6]]},"1027":{"position":[[4860,6],[4985,7]]}}}],["created.echo",{"_index":3465,"t":{"538":{"position":[[829,12]]},"852":{"position":[[804,12]]}}}],["created.for",{"_index":5411,"t":{"1027":{"position":[[5262,11]]}}}],["creation",{"_index":3356,"t":{"512":{"position":[[1647,8]]},"574":{"position":[[3529,8]]},"742":{"position":[[586,8]]}}}],["creator",{"_index":4086,"t":{"678":{"position":[[690,7]]}}}],["credenti",{"_index":1073,"t":{"69":{"position":[[5318,11]]},"366":{"position":[[1513,12],[1597,12]]},"378":{"position":[[248,11],[961,11],[1496,11],[2238,11]]},"636":{"position":[[52,11]]},"884":{"position":[[296,12]]},"894":{"position":[[1063,11]]},"898":{"position":[[297,11],[933,12]]},"908":{"position":[[977,11]]}}}],["credentials.cp",{"_index":2390,"t":{"364":{"position":[[284,14],[964,14]]},"370":{"position":[[588,14]]}}}],["credentials.echo",{"_index":2445,"t":{"372":{"position":[[463,16]]}}}],["credentials.rmdir",{"_index":2420,"t":{"366":{"position":[[589,17],[1926,17],[3630,17]]}}}],["credit",{"_index":2598,"t":{"390":{"position":[[1808,6]]},"432":{"position":[[1121,9]]},"894":{"position":[[523,6],[615,6],[654,6]]}}}],["criteria",{"_index":1629,"t":{"199":{"position":[[456,9]]},"796":{"position":[[1741,8]]}}}],["critic",{"_index":453,"t":{"25":{"position":[[1561,9]]},"91":{"position":[[1281,8]]},"93":{"position":[[1817,9]]},"376":{"position":[[2496,8],[2879,8]]},"953":{"position":[[173,8]]}}}],["cron",{"_index":819,"t":{"47":{"position":[[5476,4],[7048,5],[7102,4],[7168,4]]}}}],["cron(8",{"_index":808,"t":{"47":{"position":[[5155,7],[7017,8],[7054,7]]}}}],["cron(8)nam",{"_index":869,"t":{"47":{"position":[[7090,11]]}}}],["cron)synopsi",{"_index":871,"t":{"47":{"position":[[7154,13]]}}}],["crondescript",{"_index":807,"t":{"47":{"position":[[5095,15]]}}}],["crontab",{"_index":802,"t":{"47":{"position":[[5000,7],[5065,7],[5113,7],[5265,8],[5300,7],[5352,8],[5403,9],[5513,7],[5590,7]]}}}],["crontab(1",{"_index":823,"t":{"47":{"position":[[5619,11]]}}}],["crontab(5",{"_index":803,"t":{"47":{"position":[[5015,10],[6994,10]]}}}],["crontab(5)nam",{"_index":804,"t":{"47":{"position":[[5050,14]]}}}],["crop",{"_index":4790,"t":{"806":{"position":[[391,6],[750,6],[938,6],[1316,5],[1730,6],[1983,8],[2645,4],[3064,6],[3837,7],[3847,4],[3852,6],[3859,4]]}}}],["crop]]opt",{"_index":4822,"t":{"806":{"position":[[3764,15]]}}}],["crucial",{"_index":574,"t":{"37":{"position":[[67,7],[565,7]]},"93":{"position":[[1401,8]]},"266":{"position":[[1428,7]]},"446":{"position":[[4212,7]]},"576":{"position":[[170,7]]}}}],["cryptographi",{"_index":5093,"t":{"890":{"position":[[1716,12]]},"908":{"position":[[2609,15],[2839,12]]},"912":{"position":[[566,13]]},"939":{"position":[[185,13],[301,12],[531,12]]},"969":{"position":[[393,13]]}}}],["cryptographycryptographi",{"_index":5213,"t":{"908":{"position":[[2203,25]]}}}],["cs",{"_index":5272,"t":{"953":{"position":[[109,3]]}}}],["csh",{"_index":2236,"t":{"318":{"position":[[178,4]]}}}],["css",{"_index":1760,"t":{"225":{"position":[[1203,3]]},"985":{"position":[[937,3]]}}}],["csv",{"_index":2771,"t":{"422":{"position":[[1577,3],[1833,3]]}}}],["ctime",{"_index":1722,"t":{"219":{"position":[[902,5],[1015,5]]}}}],["ctrl",{"_index":203,"t":{"21":{"position":[[281,4],[290,4],[310,4],[319,4],[340,4],[349,4],[491,4],[775,4],[1047,4],[1972,4],[2138,4],[2202,4],[2433,4],[2477,4],[2555,5],[2900,4],[2913,4]]},"153":{"position":[[50,4],[77,4]]},"157":{"position":[[127,4],[152,4]]},"159":{"position":[[194,4]]},"161":{"position":[[80,4],[163,4]]},"163":{"position":[[59,4]]},"165":{"position":[[272,4],[285,4],[890,4]]},"167":{"position":[[76,4],[121,4]]},"173":{"position":[[84,4]]},"175":{"position":[[258,4],[269,4]]},"177":{"position":[[40,4]]},"183":{"position":[[170,4]]},"394":{"position":[[261,4]]},"802":{"position":[[1964,4],[2110,4]]},"826":{"position":[[445,4],[476,4]]},"830":{"position":[[940,4]]}}}],["ctrl+",{"_index":4499,"t":{"738":{"position":[[4245,6],[4488,6],[4532,6]]}}}],["ctrl+b",{"_index":4859,"t":{"828":{"position":[[345,6],[431,6],[510,6],[745,6],[848,6],[1018,6],[1080,6]]},"830":{"position":[[400,6],[695,6]]},"832":{"position":[[451,6]]},"834":{"position":[[2851,6]]}}}],["ctrl+c",{"_index":1776,"t":{"227":{"position":[[558,7]]},"231":{"position":[[113,6]]},"239":{"position":[[369,7]]},"262":{"position":[[1272,6]]},"446":{"position":[[3170,6]]},"810":{"position":[[467,6],[501,6]]},"858":{"position":[[343,6],[1874,6],[2056,6],[2465,6],[2695,6]]}}}],["ctrl+d",{"_index":1870,"t":{"247":{"position":[[402,7],[410,6]]},"262":{"position":[[1136,6]]},"802":{"position":[[1420,8],[2969,7]]}}}],["ctrl+i",{"_index":1858,"t":{"243":{"position":[[1181,7]]}}}],["ctrl+q",{"_index":4501,"t":{"738":{"position":[[4477,6]]}}}],["ctrl+shift+c",{"_index":318,"t":{"21":{"position":[[3394,12],[3625,12]]}}}],["ctrl+shift+c/v",{"_index":313,"t":{"21":{"position":[[3316,14]]}}}],["ctrl+shift+v",{"_index":319,"t":{"21":{"position":[[3420,12],[3651,12]]}}}],["ctrl+v",{"_index":5249,"t":{"918":{"position":[[328,7]]}}}],["ctrl+w",{"_index":1565,"t":{"183":{"position":[[396,7]]},"684":{"position":[[1268,6]]}}}],["ctrl+x",{"_index":4111,"t":{"684":{"position":[[1301,6]]}}}],["ctrl+z",{"_index":1796,"t":{"231":{"position":[[509,6],[858,6]]},"239":{"position":[[188,7]]},"243":{"position":[[197,8]]}}}],["cultur",{"_index":1310,"t":{"95":{"position":[[326,8]]}}}],["curiou",{"_index":2150,"t":{"292":{"position":[[1606,7]]},"748":{"position":[[566,8]]}}}],["curl",{"_index":938,"t":{"51":{"position":[[354,4],[404,4],[1117,4]]},"71":{"position":[[952,7]]},"189":{"position":[[277,4]]},"225":{"position":[[878,4]]},"362":{"position":[[434,4]]},"680":{"position":[[658,4]]},"720":{"position":[[544,4]]},"800":{"position":[[1870,4]]},"866":{"position":[[269,6],[303,7]]},"904":{"position":[[256,5],[1094,5]]}}}],["curli",{"_index":3198,"t":{"488":{"position":[[12,5],[620,5],[890,5],[948,5]]},"490":{"position":[[1551,5]]},"518":{"position":[[207,5]]}}}],["current",{"_index":277,"t":{"21":{"position":[[2174,7]]},"45":{"position":[[635,7]]},"69":{"position":[[5579,7]]},"77":{"position":[[1564,7],[1718,9],[1999,7],[2717,7],[3073,7]]},"79":{"position":[[556,7]]},"81":{"position":[[111,7],[345,7],[383,9],[442,7],[1337,7],[1603,7],[2188,7],[2270,7],[2303,7]]},"83":{"position":[[502,7],[936,7]]},"99":{"position":[[1292,7],[1530,9]]},"125":{"position":[[650,7]]},"129":{"position":[[927,7]]},"131":{"position":[[78,7],[174,7],[433,7]]},"135":{"position":[[846,7],[1287,7]]},"137":{"position":[[593,7],[1067,7],[1148,7],[1256,7]]},"145":{"position":[[97,7],[163,7],[237,7],[320,7],[384,7],[545,7]]},"165":{"position":[[319,7],[482,7],[576,7],[626,7]]},"167":{"position":[[41,7]]},"175":{"position":[[308,7]]},"177":{"position":[[96,7]]},"189":{"position":[[314,7],[685,7],[1138,7],[1542,7],[2102,7]]},"229":{"position":[[720,9]]},"231":{"position":[[447,9]]},"243":{"position":[[215,7]]},"257":{"position":[[6371,9]]},"296":{"position":[[257,7]]},"302":{"position":[[867,7]]},"304":{"position":[[1549,7]]},"306":{"position":[[329,7]]},"434":{"position":[[151,7],[257,7],[977,7],[1150,7],[1428,7],[1685,7]]},"440":{"position":[[365,7]]},"452":{"position":[[310,7]]},"484":{"position":[[92,7],[171,7],[1081,7],[1716,7]]},"520":{"position":[[54,7]]},"534":{"position":[[615,7]]},"538":{"position":[[918,7]]},"542":{"position":[[359,7]]},"568":{"position":[[906,7]]},"586":{"position":[[2398,7]]},"588":{"position":[[1736,7],[2248,7]]},"602":{"position":[[271,7]]},"604":{"position":[[681,7]]},"610":{"position":[[854,7]]},"616":{"position":[[841,7]]},"626":{"position":[[3606,7],[3690,7],[7309,7]]},"634":{"position":[[335,7],[1702,7]]},"638":{"position":[[161,7]]},"648":{"position":[[30,9],[1964,7],[2227,7]]},"654":{"position":[[3726,7],[3763,7]]},"658":{"position":[[1824,7]]},"660":{"position":[[90,7],[1729,7],[1996,7]]},"662":{"position":[[472,7]]},"666":{"position":[[877,7],[1641,7]]},"668":{"position":[[728,7],[803,7],[1717,9],[2666,7],[2709,9]]},"670":{"position":[[1666,9]]},"672":{"position":[[845,7],[883,7]]},"678":{"position":[[622,7]]},"682":{"position":[[1141,7],[3702,7]]},"688":{"position":[[1267,9],[3267,7],[3300,7],[3499,7]]},"696":{"position":[[2635,9],[3278,7]]},"700":{"position":[[918,7],[2135,7]]},"702":{"position":[[1332,9],[2251,7]]},"704":{"position":[[977,7],[1403,7],[1494,7],[1898,7]]},"708":{"position":[[623,7],[893,7]]},"714":{"position":[[629,9],[838,7],[886,7],[934,7],[979,7],[1040,7],[1174,7],[1254,7],[2653,8],[2672,8],[3090,8]]},"716":{"position":[[4639,7]]},"718":{"position":[[487,7],[734,7],[1674,7]]},"720":{"position":[[1446,7],[3817,7],[5300,7],[6083,9]]},"722":{"position":[[1015,7],[2755,7]]},"726":{"position":[[162,7]]},"738":{"position":[[5280,7],[6243,7],[7468,7]]},"760":{"position":[[1265,7],[3056,7]]},"770":{"position":[[404,7],[482,7],[562,7],[773,7],[1157,7]]},"772":{"position":[[1285,7],[1353,7],[1458,7],[1484,7],[1565,7],[1619,7],[1673,7]]},"778":{"position":[[1682,7],[1791,7],[2232,7]]},"788":{"position":[[185,9]]},"802":{"position":[[116,7]]},"816":{"position":[[1213,9]]},"822":{"position":[[107,7]]},"828":{"position":[[121,7],[1785,7],[1816,7]]},"832":{"position":[[1340,7]]},"834":{"position":[[1198,7],[1870,7],[2124,9]]},"842":{"position":[[199,7],[343,7],[400,7],[524,7],[553,7],[1008,7],[1045,7],[1188,7],[1561,7],[1592,7],[1652,7]]},"860":{"position":[[664,9]]},"870":{"position":[[69,7],[133,7],[433,7]]},"898":{"position":[[2531,7]]},"900":{"position":[[1514,7]]},"908":{"position":[[1011,7]]},"927":{"position":[[112,7],[930,7],[1356,7],[1447,7],[1851,7]]},"955":{"position":[[247,9]]},"979":{"position":[[30,9],[365,9]]},"1013":{"position":[[30,9],[365,9]]},"1025":{"position":[[268,7]]},"1029":{"position":[[308,7]]}}}],["cursor",{"_index":321,"t":{"21":{"position":[[3488,6],[3717,6]]},"159":{"position":[[51,7]]},"171":{"position":[[50,6]]},"508":{"position":[[4394,6]]},"668":{"position":[[586,6]]},"684":{"position":[[994,6]]},"708":{"position":[[65,6]]},"758":{"position":[[245,7]]},"760":{"position":[[795,7],[823,6]]},"766":{"position":[[27,6],[380,7]]},"768":{"position":[[238,6]]},"770":{"position":[[33,6],[374,7],[442,7],[1463,7]]},"778":{"position":[[253,7],[303,6]]},"989":{"position":[[716,7],[771,7]]}}}],["curv",{"_index":4567,"t":{"754":{"position":[[1568,6]]}}}],["customis",{"_index":408,"t":{"23":{"position":[[2227,14]]},"69":{"position":[[2386,14]]},"93":{"position":[[521,15]]},"175":{"position":[[2935,14]]},"286":{"position":[[789,11]]},"288":{"position":[[1586,11]]},"290":{"position":[[728,10]]},"292":{"position":[[1652,14]]},"618":{"position":[[566,9]]},"626":{"position":[[938,9],[3965,11]]},"634":{"position":[[1450,14],[2609,15],[2915,15]]},"650":{"position":[[30,9]]},"668":{"position":[[16,11],[62,9],[3248,14]]},"708":{"position":[[1731,9]]},"710":{"position":[[721,9]]},"712":{"position":[[291,9]]},"714":{"position":[[125,9],[1859,9]]},"718":{"position":[[2452,11]]},"720":{"position":[[48,9],[4777,9],[7324,9],[8469,14]]},"724":{"position":[[533,9],[1207,14]]},"726":{"position":[[41,9]]},"734":{"position":[[70,14],[3513,14]]},"736":{"position":[[21,14],[1176,9]]},"738":{"position":[[78,15]]},"744":{"position":[[839,9]]},"754":{"position":[[1739,13],[1779,10],[1844,14]]},"780":{"position":[[295,11]]},"818":{"position":[[1267,9]]},"836":{"position":[[81,10]]},"880":{"position":[[168,9]]}}}],["cut",{"_index":202,"t":{"21":{"position":[[277,3]]},"376":{"position":[[6024,6]]},"422":{"position":[[1599,3],[1664,3],[1888,3]]},"450":{"position":[[70,3],[157,3],[451,4],[515,3],[1459,3],[1545,3],[2011,3],[2518,3],[2977,3],[2994,3],[4247,3],[4431,3],[4652,7],[4873,3],[5088,3],[5229,3],[5427,3]]},"452":{"position":[[257,3],[349,3]]},"454":{"position":[[90,3],[545,3],[1426,3],[2335,3]]},"456":{"position":[[571,3],[869,3]]},"458":{"position":[[691,3],[751,3],[837,3],[921,3],[942,3],[1091,3],[1102,4],[1153,3],[1218,7],[1351,7]]},"544":{"position":[[1471,3],[1519,3]]},"574":{"position":[[2047,3],[2194,3]]},"626":{"position":[[1429,4],[1540,3]]},"798":{"position":[[722,4]]},"802":{"position":[[909,4],[925,3]]}}}],["cute",{"_index":813,"t":{"47":{"position":[[5321,5]]}}}],["cv",{"_index":4091,"t":{"678":{"position":[[906,3],[980,3]]}}}],["cyan",{"_index":4277,"t":{"716":{"position":[[2097,7],[2592,5],[2607,6],[2947,7]]}}}],["cycl",{"_index":4314,"t":{"716":{"position":[[5094,5]]}}}],["cygwin",{"_index":1032,"t":{"69":{"position":[[1056,7],[1064,6],[1204,6],[1479,6],[1946,6],[2007,6]]}}}],["cygwin*|mingw32*|msys*|mingw",{"_index":5033,"t":{"864":{"position":[[477,30]]}}}],["d",{"_index":368,"t":{"23":{"position":[[1093,1]]},"43":{"position":[[461,1]]},"47":{"position":[[6285,2]]},"159":{"position":[[108,1]]},"193":{"position":[[330,1],[343,1]]},"219":{"position":[[1746,1]]},"247":{"position":[[447,2],[767,2]]},"253":{"position":[[1732,2]]},"255":{"position":[[1076,1]]},"262":{"position":[[2848,2],[2912,3]]},"344":{"position":[[5029,2],[5061,2]]},"352":{"position":[[2052,8],[2089,2],[2423,6]]},"368":{"position":[[810,1],[862,5]]},"422":{"position":[[1669,4],[1893,4]]},"448":{"position":[[471,1],[820,1],[873,1]]},"450":{"position":[[162,4],[472,1],[1550,2],[2016,2],[2523,2],[4878,4],[5093,4]]},"452":{"position":[[262,4],[505,4]]},"454":{"position":[[95,4],[550,4]]},"456":{"position":[[576,4]]},"458":{"position":[[615,1],[636,1],[756,4],[781,1]]},"472":{"position":[[130,1],[467,1]]},"508":{"position":[[4572,1],[4697,1]]},"534":{"position":[[368,4]]},"538":{"position":[[564,5],[1372,5],[2657,6]]},"544":{"position":[[1476,2],[1524,2]]},"552":{"position":[[333,1],[473,1],[511,1],[957,1]]},"554":{"position":[[81,1],[971,1]]},"562":{"position":[[267,1]]},"570":{"position":[[805,1]]},"574":{"position":[[2199,4]]},"602":{"position":[[824,1]]},"626":{"position":[[1499,4],[5345,5]]},"696":{"position":[[4862,1]]},"714":{"position":[[339,2],[2650,2]]},"718":{"position":[[562,1],[852,1],[2022,1]]},"720":{"position":[[3133,3],[3278,3]]},"744":{"position":[[162,2]]},"772":{"position":[[413,1]]},"774":{"position":[[678,3]]},"802":{"position":[[1969,1]]},"842":{"position":[[1027,1]]},"852":{"position":[[481,5],[1053,3]]},"858":{"position":[[651,1],[676,1],[899,1]]},"868":{"position":[[584,2],[771,2]]},"904":{"position":[[1502,1]]},"906":{"position":[[436,2]]}}}],["d../text./scripts./websites./websites/simpl",{"_index":1600,"t":{"193":{"position":[[380,47]]}}}],["d/o/q",{"_index":5187,"t":{"904":{"position":[[1493,8]]}}}],["d7e1bb9",{"_index":3986,"t":{"660":{"position":[[1361,7]]},"684":{"position":[[2648,8]]},"694":{"position":[[2092,7]]},"696":{"position":[[5743,7]]},"702":{"position":[[829,7]]}}}],["d7e1bb9..b9ae0adfast",{"_index":4139,"t":{"690":{"position":[[346,20]]}}}],["d]elet",{"_index":5183,"t":{"904":{"position":[[1358,8]]}}}],["daemon",{"_index":809,"t":{"47":{"position":[[5163,6],[7110,6]]}}}],["daili",{"_index":456,"t":{"25":{"position":[[1650,5]]},"626":{"position":[[5201,6]]}}}],["damag",{"_index":2063,"t":{"278":{"position":[[444,6]]}}}],["danzigword",{"_index":3756,"t":{"610":{"position":[[697,11]]}}}],["dard",{"_index":840,"t":{"47":{"position":[[6468,4]]}}}],["dark",{"_index":3002,"t":{"450":{"position":[[4939,4],[5161,4]]}}}],["dart",{"_index":1242,"t":{"91":{"position":[[1839,5]]}}}],["darwin",{"_index":5030,"t":{"864":{"position":[[436,7]]}}}],["dash",{"_index":3857,"t":{"630":{"position":[[191,5]]},"636":{"position":[[1467,4],[2050,4]]},"688":{"position":[[2766,4]]},"764":{"position":[[868,4]]},"778":{"position":[[850,4]]},"878":{"position":[[1116,5]]},"931":{"position":[[149,5],[198,4]]}}}],["dashboard",{"_index":5128,"t":{"896":{"position":[[318,10],[702,9]]},"898":{"position":[[481,10]]}}}],["data",{"_index":774,"t":{"47":{"position":[[4185,4]]},"51":{"position":[[728,4]]},"87":{"position":[[147,4],[287,4]]},"129":{"position":[[722,4]]},"249":{"position":[[2131,4]]},"253":{"position":[[2707,4]]},"255":{"position":[[1440,5]]},"257":{"position":[[3352,6]]},"259":{"position":[[476,4]]},"260":{"position":[[157,4],[179,4]]},"266":{"position":[[77,4]]},"282":{"position":[[1625,4]]},"292":{"position":[[1527,4]]},"374":{"position":[[122,4]]},"446":{"position":[[294,4]]},"448":{"position":[[214,4]]},"450":{"position":[[688,4]]},"454":{"position":[[412,4]]},"456":{"position":[[899,4],[984,4]]},"480":{"position":[[90,5]]},"804":{"position":[[1082,5],[1088,4],[2778,4]]},"884":{"position":[[708,5]]},"888":{"position":[[824,4]]},"890":{"position":[[2390,4]]},"892":{"position":[[1006,5],[1074,4]]},"896":{"position":[[1797,4]]}}}],["data.dat",{"_index":1992,"t":{"260":{"position":[[290,8],[570,8]]}}}],["data/top100.csv",{"_index":2460,"t":{"374":{"position":[[488,15],[854,15]]}}}],["data[0]['meanings'][0]['definitions'][0]['definit",{"_index":4751,"t":{"804":{"position":[[1140,54]]}}}],["databas",{"_index":2231,"t":{"316":{"position":[[29,8]]},"378":{"position":[[239,8],[952,8],[1487,8],[2229,8]]},"648":{"position":[[2062,9]]},"720":{"position":[[1830,9]]},"862":{"position":[[3920,9]]}}}],["datafound",{"_index":3641,"t":{"584":{"position":[[2044,10]]}}}],["date",{"_index":248,"t":{"21":{"position":[[1303,4],[1720,4]]},"47":{"position":[[5233,7]]},"71":{"position":[[257,5]]},"500":{"position":[[802,4],[821,4],[1030,4],[1050,4]]},"502":{"position":[[850,7],[897,4]]},"534":{"position":[[310,4],[623,5]]},"538":{"position":[[513,4]]},"626":{"position":[[5294,4],[5699,4]]},"694":{"position":[[962,4]]},"714":{"position":[[346,4],[369,5],[425,4],[2504,5],[2662,6]]},"720":{"position":[[3107,4],[3825,4]]},"722":{"position":[[1495,4],[1503,10],[1514,5],[1530,4],[1574,4]]},"738":{"position":[[7761,4]]},"852":{"position":[[430,4],[1041,4]]},"1029":{"position":[[282,4],[316,5],[332,4],[341,9],[391,4]]}}}],["date)\"th",{"_index":3265,"t":{"500":{"position":[[1038,11]]}}}],["date`\"th",{"_index":3260,"t":{"500":{"position":[[810,10]]}}}],["datetim",{"_index":4380,"t":{"720":{"position":[[3072,9],[3770,8]]}}}],["datetime2021",{"_index":4394,"t":{"720":{"position":[[5503,12]]}}}],["daunt",{"_index":1537,"t":{"175":{"position":[[2587,8]]},"396":{"position":[[1732,8]]}}}],["dave",{"_index":2289,"t":{"338":{"position":[[1144,5]]},"342":{"position":[[1066,5],[1117,5],[1274,6]]},"344":{"position":[[1308,5],[1359,5],[2098,5],[2149,5],[2561,5],[2612,5],[3081,5],[3132,5],[3522,5],[3573,5]]},"346":{"position":[[163,5],[214,5],[858,5],[909,5],[1267,5],[1318,5]]},"348":{"position":[[538,4]]},"482":{"position":[[396,4]]},"492":{"position":[[613,4]]},"508":{"position":[[512,4],[2404,4]]},"694":{"position":[[445,4],[593,4],[763,4]]},"969":{"position":[[221,4]]},"1027":{"position":[[8840,5]]}}}],["dave\"echo",{"_index":5432,"t":{"1027":{"position":[[8758,9]]}}}],["dave@effect",{"_index":2286,"t":{"338":{"position":[[1047,14],[1095,14]]},"342":{"position":[[966,14],[991,14],[1016,14],[1041,14]]},"344":{"position":[[1208,14],[1233,14],[1258,14],[1283,14],[1458,14],[1998,14],[2023,14],[2048,14],[2073,14],[2461,14],[2486,14],[2511,14],[2536,14],[2981,14],[3006,14],[3031,14],[3056,14],[3422,14],[3447,14],[3472,14],[3497,14]]},"346":{"position":[[63,14],[88,14],[113,14],[138,14],[758,14],[783,14],[808,14],[833,14],[1167,14],[1192,14],[1217,14],[1242,14]]},"348":{"position":[[259,14],[449,14]]}}}],["dave@kerr@effective.shell.com",{"_index":2313,"t":{"342":{"position":[[1142,29]]},"344":{"position":[[319,30],[1384,29],[2174,29],[2637,29],[3157,29],[3598,29]]},"346":{"position":[[239,29],[934,29],[1343,29]]}}}],["davehello",{"_index":3301,"t":{"508":{"position":[[2393,10]]}}}],["day",{"_index":568,"t":{"31":{"position":[[3272,3],[3279,3]]},"37":{"position":[[852,3],[859,3]]},"69":{"position":[[385,3],[392,4],[954,3],[961,3]]},"91":{"position":[[2920,4]]},"93":{"position":[[180,4]]},"99":{"position":[[607,3]]},"179":{"position":[[11,4]]},"205":{"position":[[1622,3],[1629,3]]},"229":{"position":[[824,3],[831,3]]},"260":{"position":[[90,3]]},"280":{"position":[[561,3],[568,3]]},"314":{"position":[[295,3],[302,3]]},"376":{"position":[[2574,3],[2581,3]]},"392":{"position":[[779,4]]},"422":{"position":[[2187,4]]},"474":{"position":[[448,3],[455,3]]},"490":{"position":[[376,3],[410,3],[997,10],[1261,11]]},"538":{"position":[[422,4],[926,3]]},"582":{"position":[[253,3],[281,6],[305,6],[436,5],[543,10],[742,3]]},"584":{"position":[[2357,3],[2364,3]]},"622":{"position":[[753,4]]},"626":{"position":[[5856,4]]},"636":{"position":[[2257,4]]},"666":{"position":[[2007,3]]},"670":{"position":[[2381,4]]},"716":{"position":[[737,4]]},"738":{"position":[[2447,4]]},"778":{"position":[[1213,3],[1220,3]]},"834":{"position":[[3157,3]]},"852":{"position":[[375,5]]},"890":{"position":[[1712,3],[1945,3]]},"951":{"position":[[137,4]]},"1027":{"position":[[5700,14],[6199,11],[6211,4],[6279,4]]},"1033":{"position":[[859,5],[932,3],[1076,3],[1083,6],[1143,4],[1178,4],[1409,5],[1532,5],[1668,3]]}}}],["day'",{"_index":3026,"t":{"454":{"position":[[185,5]]}}}],["daylibrari",{"_index":757,"t":{"47":{"position":[[3150,10]]}}}],["days\"do",{"_index":5449,"t":{"1033":{"position":[[939,9]]}}}],["days=\"monday",{"_index":5447,"t":{"1033":{"position":[[865,12],[1601,12]]}}}],["days=(\"monday",{"_index":3212,"t":{"490":{"position":[[154,14],[814,14]]},"582":{"position":[[172,14]]},"1027":{"position":[[4024,14],[5618,14],[6106,14]]}}}],["days[0]=\"mon",{"_index":3223,"t":{"490":{"position":[[1113,13]]}}}],["days[0]}\"echo",{"_index":3217,"t":{"490":{"position":[[384,15]]}}}],["days[2",{"_index":3220,"t":{"490":{"position":[[931,10]]}}}],["days[6",{"_index":3218,"t":{"490":{"position":[[418,11]]}}}],["days[@]:2:3",{"_index":5408,"t":{"1027":{"position":[[4106,17]]}}}],["days[@]:5:2",{"_index":3237,"t":{"490":{"position":[[1487,14]]}}}],["days[@]}do",{"_index":3622,"t":{"582":{"position":[[260,12]]}}}],["daysdo",{"_index":5452,"t":{"1033":{"position":[[1675,7]]}}}],["day}\"don",{"_index":5450,"t":{"1033":{"position":[[954,12],[1688,12]]}}}],["dazzl",{"_index":2017,"t":{"266":{"position":[[854,8]]}}}],["db_password%eof",{"_index":2524,"t":{"378":{"position":[[1041,16]]}}}],["db_password=dhhs22kfid9c",{"_index":2535,"t":{"378":{"position":[[1996,25]]}}}],["db_usernam",{"_index":2523,"t":{"378":{"position":[[1017,13],[4721,14]]}}}],["db_username=prod",{"_index":2534,"t":{"378":{"position":[[1965,16]]}}}],["dd",{"_index":3436,"t":{"534":{"position":[[337,3]]},"772":{"position":[[1444,2]]}}}],["dd.today=$(d",{"_index":3459,"t":{"538":{"position":[[540,15]]},"626":{"position":[[5321,15]]},"852":{"position":[[457,15]]}}}],["deal",{"_index":350,"t":{"23":{"position":[[444,4]]},"47":{"position":[[4000,5]]},"99":{"position":[[591,4]]},"195":{"position":[[1990,4],[2214,4],[2286,5],[2702,4]]},"237":{"position":[[1319,4]]},"257":{"position":[[1543,4]]},"432":{"position":[[539,7]]},"464":{"position":[[42,4],[1037,4]]},"470":{"position":[[901,4]]},"476":{"position":[[512,4]]},"496":{"position":[[192,5],[275,5]]},"500":{"position":[[292,7],[357,4],[413,5],[468,5]]},"528":{"position":[[662,4]]},"586":{"position":[[1424,4],[1913,4]]},"670":{"position":[[702,7]]},"696":{"position":[[5823,7]]},"704":{"position":[[206,4]]},"716":{"position":[[1023,4],[5385,4]]},"810":{"position":[[1523,7]]},"864":{"position":[[947,4]]}}}],["deal=\"buy",{"_index":3257,"t":{"500":{"position":[[248,9]]}}}],["death",{"_index":2148,"t":{"292":{"position":[[1436,8]]}}}],["debian",{"_index":3887,"t":{"648":{"position":[[1982,6]]},"720":{"position":[[2830,7],[3639,6],[3717,6],[6875,6],[7670,6],[8300,8]]},"722":{"position":[[2133,6]]},"744":{"position":[[2138,8]]},"822":{"position":[[382,6]]}}}],["debian.sourc",{"_index":4405,"t":{"720":{"position":[[8240,13]]}}}],["debian/ubuntu",{"_index":4373,"t":{"720":{"position":[[2840,13]]}}}],["debian_chroot",{"_index":4560,"t":{"748":{"position":[[579,13],[929,13]]}}}],["debiandwmkerr@effect",{"_index":4396,"t":{"720":{"position":[[5541,23]]}}}],["debug",{"_index":2743,"t":{"404":{"position":[[287,7],[314,5]]},"476":{"position":[[557,9]]},"484":{"position":[[991,6]]},"626":{"position":[[6841,6]]},"852":{"position":[[103,9],[2416,5]]}}}],["debug_mod",{"_index":3185,"t":{"484":{"position":[[1007,15],[1054,10],[1273,10]]}}}],["debug_mode=1sh",{"_index":3184,"t":{"484":{"position":[[967,14]]}}}],["debugflag",{"_index":873,"t":{"47":{"position":[[7187,16]]}}}],["decad",{"_index":1188,"t":{"87":{"position":[[654,7]]},"91":{"position":[[2292,8]]},"266":{"position":[[625,8]]},"876":{"position":[[125,8]]},"884":{"position":[[74,7]]}}}],["decid",{"_index":2280,"t":{"336":{"position":[[1384,6]]},"384":{"position":[[366,6]]},"408":{"position":[[1020,6]]},"584":{"position":[[1572,6]]},"598":{"position":[[654,6]]},"610":{"position":[[91,6]]},"696":{"position":[[214,6]]},"818":{"position":[[1894,6]]},"820":{"position":[[628,6]]}}}],["decim",{"_index":849,"t":{"47":{"position":[[6764,7]]}}}],["deciph",{"_index":5028,"t":{"862":{"position":[[4253,8]]}}}],["decis",{"_index":2064,"t":{"278":{"position":[[714,8]]},"384":{"position":[[438,8]]}}}],["declar",{"_index":2184,"t":{"304":{"position":[[341,7]]},"492":{"position":[[284,7]]},"496":{"position":[[23,9]]},"522":{"position":[[1093,8]]},"540":{"position":[[389,10]]},"568":{"position":[[609,7]]},"854":{"position":[[4,7],[83,7],[324,9],[568,7],[716,7],[875,7],[1187,9]]}}}],["decod",{"_index":4753,"t":{"804":{"position":[[1607,6]]}}}],["decrement",{"_index":3337,"t":{"510":{"position":[[2117,9],[2184,9],[2305,9]]}}}],["decrypt",{"_index":5083,"t":{"888":{"position":[[220,7],[493,7]]},"890":{"position":[[429,7],[502,7],[609,7],[630,8],[911,7],[1165,7],[1469,7]]}}}],["dedic",{"_index":3843,"t":{"626":{"position":[[3952,9]]},"754":{"position":[[263,9]]},"876":{"position":[[861,9]]}}}],["dedupl",{"_index":549,"t":{"31":{"position":[[2116,11]]}}}],["deep",{"_index":1728,"t":{"219":{"position":[[1895,5]]},"939":{"position":[[575,4]]}}}],["deeper",{"_index":1570,"t":{"185":{"position":[[310,6],[939,7]]},"386":{"position":[[423,6]]},"448":{"position":[[1910,6]]},"670":{"position":[[2621,6]]},"696":{"position":[[5981,7]]},"698":{"position":[[629,6]]},"728":{"position":[[233,6]]},"742":{"position":[[1223,6]]},"912":{"position":[[514,7]]}}}],["def",{"_index":4652,"t":{"774":{"position":[[626,3],[692,3],[747,3],[807,3],[1130,3],[1186,3],[1243,3],[1315,3]]},"776":{"position":[[630,3],[679,3],[758,3],[816,3],[894,3]]},"804":{"position":[[452,3]]},"806":{"position":[[1491,3]]},"989":{"position":[[462,3],[566,3]]}}}],["default",{"_index":329,"t":{"21":{"position":[[3878,7]]},"29":{"position":[[922,8]]},"45":{"position":[[665,7]]},"69":{"position":[[1362,7],[1408,7],[1625,7],[1849,7],[3508,8],[3920,8],[4046,7],[4780,8]]},"71":{"position":[[201,7],[309,7],[354,7],[1738,8]]},"77":{"position":[[2126,7]]},"81":{"position":[[968,7]]},"83":{"position":[[276,7]]},"107":{"position":[[375,7]]},"121":{"position":[[559,8]]},"125":{"position":[[671,8]]},"137":{"position":[[1132,8]]},"175":{"position":[[337,7],[408,7],[458,7],[543,7],[769,7],[913,7],[2992,7]]},"189":{"position":[[607,8],[1097,7]]},"209":{"position":[[25,7]]},"217":{"position":[[1261,7]]},"247":{"position":[[840,8]]},"257":{"position":[[5537,8]]},"288":{"position":[[848,7]]},"290":{"position":[[1098,7]]},"350":{"position":[[274,8],[703,8]]},"364":{"position":[[2252,8]]},"390":{"position":[[530,8]]},"396":{"position":[[1123,7]]},"400":{"position":[[996,8]]},"406":{"position":[[276,8]]},"432":{"position":[[1043,9]]},"442":{"position":[[884,7]]},"450":{"position":[[1007,7],[1619,7],[2094,7],[2603,7]]},"462":{"position":[[1979,7]]},"464":{"position":[[1356,7]]},"466":{"position":[[3,7],[261,8]]},"468":{"position":[[2866,7]]},"506":{"position":[[665,7],[690,8],[758,7]]},"508":{"position":[[1362,7]]},"544":{"position":[[672,7],[928,7]]},"572":{"position":[[299,8]]},"586":{"position":[[773,8],[1265,8]]},"610":{"position":[[161,8]]},"616":{"position":[[992,8]]},"626":{"position":[[948,7],[1089,7],[4410,7]]},"634":{"position":[[1556,7]]},"648":{"position":[[51,7]]},"650":{"position":[[726,9]]},"692":{"position":[[1651,7]]},"702":{"position":[[3079,8]]},"708":{"position":[[216,7]]},"716":{"position":[[52,7]]},"720":{"position":[[8115,7]]},"722":{"position":[[262,7],[1301,8]]},"732":{"position":[[121,8],[351,8],[475,7],[1956,7]]},"734":{"position":[[22,7],[743,8],[1343,7]]},"760":{"position":[[1631,7]]},"776":{"position":[[785,11]]},"796":{"position":[[780,7],[952,8]]},"806":{"position":[[1014,7],[1062,7]]},"826":{"position":[[427,7]]},"834":{"position":[[1137,7],[1471,7],[1498,7],[1604,7],[1633,7],[1699,7]]},"838":{"position":[[1483,7]]},"850":{"position":[[3,8]]},"892":{"position":[[635,7]]},"896":{"position":[[1169,7]]},"908":{"position":[[1193,7]]},"925":{"position":[[41,7]]},"989":{"position":[[733,8]]},"1027":{"position":[[1640,7],[1683,8],[1797,7],[1925,7],[2082,7],[2138,8],[2199,8]]},"1033":{"position":[[2169,8],[3848,7]]}}}],["default\"d",{"_index":4159,"t":{"694":{"position":[[455,24],[603,24],[773,24]]}}}],["dwmkerr@ip",{"_index":3837,"t":{"626":{"position":[[3466,10]]}}}],["dwmkerr@macbookth",{"_index":5111,"t":{"892":{"position":[[1625,18]]}}}],["dwmkerr@myserver.com",{"_index":5195,"t":{"908":{"position":[[559,22]]}}}],["dwmkerr_backup",{"_index":3202,"t":{"488":{"position":[[366,14],[784,16]]}}}],["dwmkerryour",{"_index":3170,"t":{"480":{"position":[[994,11]]}}}],["dynam",{"_index":2531,"t":{"378":{"position":[[1806,8]]},"522":{"position":[[172,7]]},"626":{"position":[[4908,11]]}}}],["d{%i",{"_index":4381,"t":{"720":{"position":[[3124,5]]}}}],["d{format",{"_index":4238,"t":{"714":{"position":[[410,10]]}}}],["e",{"_index":669,"t":{"45":{"position":[[572,3]]},"71":{"position":[[950,1]]},"153":{"position":[[84,1]]},"175":{"position":[[276,1]]},"364":{"position":[[2359,1]]},"366":{"position":[[16,1],[425,1],[1046,1],[1760,1],[3436,1],[3439,1],[3461,1],[4505,1]]},"368":{"position":[[94,1],[839,1],[842,1],[856,1]]},"370":{"position":[[417,1],[420,1]]},"372":{"position":[[278,1],[281,1],[298,1]]},"374":{"position":[[511,1],[816,1],[819,1],[827,1]]},"376":{"position":[[493,1],[1372,1],[1860,1],[2728,1],[3396,1],[4102,1],[4461,1],[4863,1],[5238,1],[5552,1],[6486,1]]},"378":{"position":[[1307,1],[1337,1],[1367,1],[2027,1],[2062,1],[2101,1],[2509,1],[2809,1],[2833,1],[4004,1]]},"386":{"position":[[389,1]]},"390":{"position":[[263,1]]},"396":{"position":[[1351,1],[1603,1]]},"410":{"position":[[226,1]]},"538":{"position":[[2329,2],[2408,3],[2861,1]]},"544":{"position":[[533,1]]},"556":{"position":[[259,1],[469,1]]},"558":{"position":[[409,1],[1769,1],[2130,1]]},"562":{"position":[[311,1]]},"586":{"position":[[2162,1],[2238,1]]},"666":{"position":[[1478,1]]},"714":{"position":[[473,2]]},"716":{"position":[[1355,3],[3885,1],[3958,1]]},"734":{"position":[[2922,1]]},"740":{"position":[[511,1]]},"778":{"position":[[1748,2]]},"850":{"position":[[608,1],[1059,1],[1678,2],[2430,1]]},"858":{"position":[[850,1]]},"860":{"position":[[237,1],[1190,1],[1427,1]]},"862":{"position":[[214,1],[405,1],[4086,1]]},"874":{"position":[[961,1],[995,1],[1089,1]]},"892":{"position":[[1772,2]]},"969":{"position":[[291,2]]},"1035":{"position":[[3000,1],[3132,1]]}}}],["e.g",{"_index":687,"t":{"47":{"position":[[598,5],[705,4]]},"107":{"position":[[563,5]]},"123":{"position":[[666,5]]},"626":{"position":[[1450,5]]},"804":{"position":[[2163,4]]},"806":{"position":[[3219,4]]},"985":{"position":[[1330,5]]},"1001":{"position":[[31,4]]}}}],["e/dotfiles.git",{"_index":3995,"t":{"662":{"position":[[345,46]]}}}],["git@github.com:dwmkerr/java",{"_index":2699,"t":{"400":{"position":[[1698,27]]}}}],["git_aliases.sh",{"_index":4134,"t":{"688":{"position":[[2088,14],[2314,14],[2545,15]]},"696":{"position":[[1308,14]]}}}],["git_any_local_chang",{"_index":4053,"t":{"668":{"position":[[2115,26]]},"720":{"position":[[6495,26]]}}}],["git_any_local_changes=\"$(git",{"_index":4043,"t":{"668":{"position":[[1733,28]]},"720":{"position":[[6099,28]]}}}],["git_branch_nam",{"_index":4050,"t":{"668":{"position":[[1943,20]]},"720":{"position":[[6309,20]]}}}],["git_branch_name=\"$(git",{"_index":4042,"t":{"668":{"position":[[1680,22]]},"720":{"position":[[6046,22]]}}}],["git_info",{"_index":4049,"t":{"668":{"position":[[1926,11],[2452,14]]},"720":{"position":[[6292,11],[6846,13]]}}}],["git_info=\"${bold}${fg_green}${git_branch_name}${reset",{"_index":4398,"t":{"720":{"position":[[6426,55]]}}}],["git_info=\"${bold}${fg_green}${start_underline}${git_branch_name}${reset",{"_index":4397,"t":{"720":{"position":[[6347,73]]}}}],["git_info=\"${fg_green}${git_branch_name}${reset",{"_index":4052,"t":{"668":{"position":[[2053,48]]}}}],["git_info=\"${fg_green}${start_underline}${git_branch_name}${reset",{"_index":4051,"t":{"668":{"position":[[1981,66]]}}}],["git_info=\"${git_info",{"_index":4054,"t":{"668":{"position":[[2279,21],[2371,21]]},"720":{"position":[[6659,21],[6758,21]]}}}],["git_stash_count",{"_index":4056,"t":{"668":{"position":[[2333,19]]},"720":{"position":[[6720,19]]}}}],["git_stash_count=\"$(git",{"_index":4045,"t":{"668":{"position":[[1804,22]]},"720":{"position":[[6170,22]]}}}],["gitconfig",{"_index":4066,"t":{"670":{"position":[[1244,10]]}}}],["github",{"_index":3810,"t":{"626":{"position":[[535,6]]},"654":{"position":[[752,6],[768,6],[932,6],[3117,7],[3184,6],[3317,6],[3681,7]]},"658":{"position":[[467,6],[940,7]]},"662":{"position":[[116,6],[683,6]]},"664":{"position":[[84,7],[472,6],[615,6],[818,6],[1007,7],[2030,6],[2162,6]]},"666":{"position":[[38,7],[312,6]]},"670":{"position":[[3045,6],[3086,6]]},"674":{"position":[[38,6]]},"678":{"position":[[1239,7]]},"764":{"position":[[975,7]]},"778":{"position":[[1087,6],[1143,6]]},"935":{"position":[[153,6],[221,6]]}}}],["github.com/dwmkerr/dofil",{"_index":4924,"t":{"846":{"position":[[392,26]]}}}],["github.com/dwmkerr/dotfil",{"_index":2153,"t":{"292":{"position":[[1755,29]]},"834":{"position":[[5278,27]]}}}],["github.com/dwmkerr/linux",{"_index":2132,"t":{"292":{"position":[[235,24]]}}}],["github.com/effect",{"_index":4012,"t":{"666":{"position":[[589,20]]}}}],["github/connect",{"_index":3937,"t":{"654":{"position":[[3296,17]]}}}],["github/dwmkerr/effect",{"_index":4031,"t":{"668":{"position":[[322,24],[694,24]]}}}],["github/gitlab/whatev",{"_index":4022,"t":{"666":{"position":[[1360,23]]}}}],["gitignor",{"_index":4065,"t":{"670":{"position":[[1142,9]]}}}],["gitlab",{"_index":3939,"t":{"656":{"position":[[300,7],[415,7]]},"664":{"position":[[106,7]]},"666":{"position":[[46,7]]},"678":{"position":[[1247,6]]}}}],["give",{"_index":286,"t":{"21":{"position":[[2321,4]]},"47":{"position":[[2669,6]]},"69":{"position":[[2599,4]]},"77":{"position":[[256,4],[3357,6]]},"99":{"position":[[1238,4]]},"107":{"position":[[858,4],[1067,4],[1343,6]]},"113":{"position":[[594,4]]},"131":{"position":[[376,4]]},"135":{"position":[[207,5]]},"137":{"position":[[957,5]]},"195":{"position":[[1833,4]]},"209":{"position":[[180,5]]},"257":{"position":[[3225,5]]},"278":{"position":[[883,4]]},"352":{"position":[[432,4]]},"354":{"position":[[628,4]]},"356":{"position":[[15,5]]},"364":{"position":[[1433,4]]},"390":{"position":[[42,5]]},"394":{"position":[[1746,4]]},"396":{"position":[[2082,5]]},"456":{"position":[[137,6]]},"466":{"position":[[985,4]]},"574":{"position":[[3167,6]]},"612":{"position":[[1091,5]]},"660":{"position":[[460,5]]},"670":{"position":[[864,5]]},"766":{"position":[[276,4]]},"796":{"position":[[373,4]]},"802":{"position":[[1028,4]]},"828":{"position":[[1938,4]]},"830":{"position":[[787,5]]},"834":{"position":[[2334,4]]},"838":{"position":[[510,6]]},"878":{"position":[[121,5]]},"890":{"position":[[2169,4]]},"896":{"position":[[398,4]]},"898":{"position":[[499,6]]},"902":{"position":[[699,4]]}}}],["given",{"_index":623,"t":{"41":{"position":[[690,5]]},"47":{"position":[[5294,5]]},"125":{"position":[[626,5],[738,5]]},"145":{"position":[[260,5]]},"189":{"position":[[2179,5]]},"199":{"position":[[450,5]]},"233":{"position":[[442,5]]},"237":{"position":[[1348,5]]},"243":{"position":[[1352,5],[1677,5]]},"253":{"position":[[2118,5]]},"266":{"position":[[463,5]]},"278":{"position":[[221,5]]},"288":{"position":[[1348,5]]},"374":{"position":[[726,5]]},"390":{"position":[[459,5]]},"420":{"position":[[1095,5]]},"446":{"position":[[3353,5]]},"450":{"position":[[113,5]]},"452":{"position":[[61,5]]},"466":{"position":[[430,5]]},"468":{"position":[[81,5]]},"490":{"position":[[298,5]]},"512":{"position":[[1705,5]]},"536":{"position":[[81,5]]},"574":{"position":[[3587,5]]},"648":{"position":[[1197,5]]},"694":{"position":[[34,5]]},"796":{"position":[[2023,5]]},"828":{"position":[[1421,5]]},"890":{"position":[[1288,5]]},"912":{"position":[[179,5]]},"985":{"position":[[1376,5],[1450,5],[1561,5]]}}}],["gl",{"_index":2208,"t":{"308":{"position":[[280,3]]},"328":{"position":[[216,3]]},"544":{"position":[[2250,2]]}}}],["gl6",{"_index":3491,"t":{"544":{"position":[[343,4]]}}}],["gl8",{"_index":3771,"t":{"612":{"position":[[1800,4]]}}}],["gl97",{"_index":2794,"t":{"424":{"position":[[1545,4]]}}}],["glamor",{"_index":5262,"t":{"935":{"position":[[40,9]]}}}],["glanc",{"_index":3015,"t":{"452":{"position":[[158,6]]}}}],["glob",{"_index":1627,"t":{"195":{"position":[[2745,8]]},"586":{"position":[[1285,6],[1517,6]]},"626":{"position":[[2830,8]]}}}],["global",{"_index":2603,"t":{"392":{"position":[[303,8]]},"530":{"position":[[682,6]]},"536":{"position":[[1392,6]]},"622":{"position":[[1243,8],[1521,6]]},"678":{"position":[[1166,9]]},"732":{"position":[[1000,6]]}}}],["globstar",{"_index":3834,"t":{"626":{"position":[[2803,8]]},"734":{"position":[[965,8],[987,8]]}}}],["glog",{"_index":3973,"t":{"660":{"position":[[1005,6],[1037,6]]},"696":{"position":[[1503,6],[1748,6],[3883,4],[4332,4],[5402,6],[5434,6]]},"702":{"position":[[488,6],[520,6]]}}}],["glog=\"git",{"_index":4182,"t":{"696":{"position":[[1416,9],[1679,9],[3023,9],[3050,9],[3816,9],[4483,9]]}}}],["glog_alia",{"_index":3971,"t":{"660":{"position":[[958,15],[984,12]]},"696":{"position":[[1340,12],[3090,10],[3342,10],[3429,10],[3910,10],[4713,13],[5355,15],[5381,12]]},"702":{"position":[[441,15],[467,12]]}}}],["glog_aliasauto",{"_index":4184,"t":{"696":{"position":[[1863,14]]}}}],["glog_aliasecho",{"_index":4181,"t":{"696":{"position":[[1394,14]]}}}],["gm",{"_index":3980,"t":{"660":{"position":[[1216,4]]},"692":{"position":[[776,4]]},"694":{"position":[[667,4],[1152,4],[1962,4]]},"696":{"position":[[5613,4]]},"702":{"position":[[699,4]]}}}],["gm=\"git",{"_index":4149,"t":{"692":{"position":[[707,7]]},"696":{"position":[[2991,7],[3796,7],[4463,7]]}}}],["gmail",{"_index":2294,"t":{"338":{"position":[[1496,6]]}}}],["gnome",{"_index":325,"t":{"21":{"position":[[3580,5]]},"241":{"position":[[76,5]]},"282":{"position":[[2472,5]]},"288":{"position":[[2177,5]]},"290":{"position":[[958,5]]},"622":{"position":[[295,5]]},"636":{"position":[[589,6]]},"646":{"position":[[226,5],[366,5]]},"732":{"position":[[300,6]]}}}],["gnu",{"_index":1568,"t":{"185":{"position":[[266,3],[355,3],[962,3]]},"189":{"position":[[1225,3],[1274,3],[1437,3],[1663,3],[1791,3],[1922,3],[1968,3]]},"316":{"position":[[121,3]]},"378":{"position":[[4279,3]]},"634":{"position":[[449,3]]},"752":{"position":[[342,3]]},"812":{"position":[[330,3]]},"820":{"position":[[47,3],[68,3],[270,3],[648,3]]},"826":{"position":[[52,3],[459,3],[559,3]]},"846":{"position":[[82,3]]},"864":{"position":[[791,3]]},"866":{"position":[[704,3]]}}}],["gnu/linux",{"_index":3871,"t":{"636":{"position":[[1795,10]]}}}],["go",{"_index":55,"t":{"8":{"position":[[73,5],[437,5]]},"10":{"position":[[306,5]]},"21":{"position":[[2113,2],[2932,5]]},"23":{"position":[[567,5]]},"25":{"position":[[1018,5]]},"31":{"position":[[2311,5],[2944,5],[3087,2]]},"37":{"position":[[537,5],[638,5]]},"45":{"position":[[929,2]]},"47":{"position":[[912,2],[2025,5],[5658,5],[6499,5]]},"49":{"position":[[617,5],[634,2]]},"69":{"position":[[713,2],[2442,2],[3256,2],[4529,5]]},"71":{"position":[[1198,5],[1874,2]]},"75":{"position":[[183,2]]},"77":{"position":[[83,5],[92,2]]},"79":{"position":[[593,5]]},"81":{"position":[[94,2],[637,3],[1080,5],[1625,5],[1634,2],[1982,2]]},"91":{"position":[[851,2],[1650,2]]},"103":{"position":[[216,5]]},"121":{"position":[[60,5]]},"123":{"position":[[681,5]]},"137":{"position":[[898,3]]},"139":{"position":[[534,2],[1087,2]]},"141":{"position":[[81,2],[307,2],[915,2]]},"143":{"position":[[119,2],[227,2],[284,2]]},"145":{"position":[[798,2],[927,2]]},"151":{"position":[[68,5]]},"153":{"position":[[61,2],[88,2]]},"155":{"position":[[106,2],[133,2]]},"171":{"position":[[134,2]]},"185":{"position":[[307,2]]},"205":{"position":[[656,2]]},"207":{"position":[[35,5],[319,5],[328,2]]},"219":{"position":[[44,2]]},"237":{"position":[[1117,5]]},"241":{"position":[[408,5]]},"251":{"position":[[992,5],[1039,5],[1744,2],[1799,5]]},"253":{"position":[[497,2],[922,2],[1828,5],[3321,2],[3665,2]]},"255":{"position":[[1092,2]]},"257":{"position":[[868,5],[3436,2],[3554,2],[3650,2],[6881,2]]},"259":{"position":[[481,2],[836,2]]},"262":{"position":[[2477,2]]},"272":{"position":[[87,5],[114,5]]},"280":{"position":[[1226,5]]},"282":{"position":[[1646,2]]},"284":{"position":[[46,2],[120,5]]},"288":{"position":[[1679,5]]},"302":{"position":[[200,2]]},"306":{"position":[[686,5]]},"308":{"position":[[686,2]]},"312":{"position":[[297,2]]},"348":{"position":[[713,5],[806,2]]},"352":{"position":[[562,5]]},"362":{"position":[[90,5]]},"364":{"position":[[1732,5]]},"376":{"position":[[3917,5],[6042,5]]},"378":{"position":[[3129,5]]},"396":{"position":[[2452,5],[2502,2]]},"406":{"position":[[1050,2]]},"410":{"position":[[116,2]]},"416":{"position":[[293,2],[336,5]]},"418":{"position":[[6,5]]},"420":{"position":[[5,5],[259,5],[709,5]]},"422":{"position":[[101,5],[740,2]]},"424":{"position":[[285,5]]},"428":{"position":[[950,5]]},"434":{"position":[[1943,5]]},"442":{"position":[[1818,5]]},"446":{"position":[[4135,5]]},"448":{"position":[[1898,5],[1907,2]]},"462":{"position":[[1525,5]]},"464":{"position":[[1070,5],[1190,5]]},"468":{"position":[[158,2],[304,2]]},"470":{"position":[[1092,5]]},"482":{"position":[[1080,2]]},"494":{"position":[[260,5]]},"502":{"position":[[1288,5]]},"508":{"position":[[2040,5]]},"514":{"position":[[676,5]]},"522":{"position":[[2096,5]]},"534":{"position":[[730,2],[924,5]]},"540":{"position":[[623,5]]},"582":{"position":[[485,2]]},"584":{"position":[[2667,2]]},"588":{"position":[[1298,5]]},"598":{"position":[[503,2]]},"600":{"position":[[633,5],[1250,5]]},"608":{"position":[[1218,5],[2478,5]]},"612":{"position":[[408,5]]},"618":{"position":[[162,5]]},"622":{"position":[[87,5]]},"626":{"position":[[526,2],[5874,2]]},"628":{"position":[[437,5],[446,2]]},"654":{"position":[[1134,5]]},"670":{"position":[[2257,5],[2618,2]]},"682":{"position":[[862,5]]},"688":{"position":[[2399,2],[2625,2]]},"692":{"position":[[572,2],[620,2],[1463,5],[1592,5]]},"696":{"position":[[1527,2],[1579,2],[5978,2]]},"698":{"position":[[94,5],[724,5]]},"700":{"position":[[3634,3]]},"702":{"position":[[2963,2]]},"716":{"position":[[5704,2]]},"720":{"position":[[613,2]]},"736":{"position":[[942,5]]},"738":{"position":[[2899,2],[3495,2]]},"742":{"position":[[353,3],[985,5],[1220,2]]},"760":{"position":[[702,5]]},"762":{"position":[[6,5],[141,2]]},"764":{"position":[[1577,5]]},"766":{"position":[[565,2],[594,2],[605,2],[628,2],[639,2],[666,2],[675,2],[696,2],[708,2],[732,2],[760,2],[785,2],[1212,2],[1697,2],[1794,3]]},"770":{"position":[[1263,2],[1420,2]]},"772":{"position":[[528,4],[560,4],[889,2],[1598,4],[1652,4]]},"774":{"position":[[776,2]]},"778":{"position":[[6,5]]},"796":{"position":[[229,5]]},"800":{"position":[[59,5],[1546,2]]},"804":{"position":[[1248,5],[1257,2]]},"806":{"position":[[359,5]]},"820":{"position":[[414,5]]},"828":{"position":[[68,5]]},"834":{"position":[[48,5],[1123,2],[4890,2],[4920,2],[5155,5]]},"840":{"position":[[939,2]]},"844":{"position":[[186,2]]},"850":{"position":[[269,5],[1863,5]]},"852":{"position":[[1348,5]]},"862":{"position":[[2460,5],[2984,5]]},"876":{"position":[[260,5]]},"880":{"position":[[139,5]]},"892":{"position":[[92,5],[944,5],[2423,5]]},"894":{"position":[[6,5],[1271,3],[1316,2]]},"896":{"position":[[152,5],[683,2],[753,5],[762,2]]},"898":{"position":[[275,5],[329,5]]},"912":{"position":[[511,2]]},"939":{"position":[[150,2],[572,2]]},"1027":{"position":[[67,5],[9190,5]]}}}],["goal",{"_index":1308,"t":{"95":{"position":[[238,5]]},"107":{"position":[[646,4]]},"219":{"position":[[1966,4]]},"290":{"position":[[207,4]]},"450":{"position":[[2943,5],[5346,6]]},"618":{"position":[[299,4]]}}}],["gocode='cd",{"_index":3812,"t":{"626":{"position":[[561,10]]}}}],["goe",{"_index":1539,"t":{"175":{"position":[[2645,4]]},"205":{"position":[[729,4]]},"249":{"position":[[1937,4]]},"290":{"position":[[170,4]]},"352":{"position":[[738,4]]},"594":{"position":[[370,4]]},"600":{"position":[[422,4]]},"728":{"position":[[123,4],[215,4]]},"744":{"position":[[1720,4]]},"808":{"position":[[425,4]]},"834":{"position":[[469,4]]},"840":{"position":[[381,4]]},"939":{"position":[[727,4]]},"955":{"position":[[60,4]]},"1033":{"position":[[111,4]]}}}],["golang",{"_index":1257,"t":{"91":{"position":[[2398,7],[2973,6]]},"354":{"position":[[158,6]]}}}],["gone",{"_index":374,"t":{"23":{"position":[[1177,4]]},"105":{"position":[[292,4]]},"125":{"position":[[1124,4]]},"292":{"position":[[890,4]]},"684":{"position":[[1896,4]]}}}],["good",{"_index":909,"t":{"49":{"position":[[157,4]]},"225":{"position":[[1691,4]]},"237":{"position":[[1147,4]]},"255":{"position":[[1687,5]]},"262":{"position":[[2552,4]]},"338":{"position":[[1428,4]]},"422":{"position":[[1270,4]]},"468":{"position":[[742,4]]},"482":{"position":[[573,4]]},"492":{"position":[[695,4]]},"508":{"position":[[1544,4]]},"514":{"position":[[897,4]]},"572":{"position":[[1424,4]]},"616":{"position":[[689,4]]},"650":{"position":[[595,4]]},"666":{"position":[[1328,4]]},"670":{"position":[[796,4]]},"686":{"position":[[966,4]]},"702":{"position":[[1082,5]]},"742":{"position":[[345,4]]},"794":{"position":[[1146,4]]},"796":{"position":[[720,4]]},"800":{"position":[[211,4]]},"890":{"position":[[1260,4]]},"931":{"position":[[7,4]]},"941":{"position":[[27,4]]}}}],["goodby",{"_index":2769,"t":{"422":{"position":[[833,8]]},"1027":{"position":[[8831,8]]}}}],["goodwednesday",{"_index":1932,"t":{"255":{"position":[[1813,13]]}}}],["goof",{"_index":2203,"t":{"306":{"position":[[314,6]]}}}],["goofi",{"_index":3117,"t":{"464":{"position":[[2106,5]]}}}],["googl",{"_index":1829,"t":{"239":{"position":[[201,6]]},"257":{"position":[[1794,6]]},"262":{"position":[[3773,8]]}}}],["gori",{"_index":1851,"t":{"243":{"position":[[492,4]]}}}],["gpo122",{"_index":2792,"t":{"424":{"position":[[1526,6]]}}}],["gpo7",{"_index":3770,"t":{"612":{"position":[[1791,5]]}}}],["gpo8",{"_index":3495,"t":{"544":{"position":[[359,5]]}}}],["gpr",{"_index":2796,"t":{"424":{"position":[[1556,3]]},"438":{"position":[[626,6],[1144,6]]},"544":{"position":[[392,3]]},"666":{"position":[[674,3],[859,5],[1979,3]]}}}],["gpr9",{"_index":3772,"t":{"612":{"position":[[1808,5]]}}}],["grab",{"_index":1910,"t":{"253":{"position":[[2890,5]]},"446":{"position":[[3996,4]]},"450":{"position":[[1502,4]]},"454":{"position":[[381,7]]},"528":{"position":[[343,4]]},"534":{"position":[[643,4]]},"666":{"position":[[1155,8]]}}}],["grace",{"_index":4836,"t":{"810":{"position":[[446,8]]}}}],["grain",{"_index":1494,"t":{"155":{"position":[[23,7]]}}}],["grammar",{"_index":3604,"t":{"576":{"position":[[325,8]]},"918":{"position":[[446,7]]}}}],["grammar::me::cpu::gasm(n",{"_index":900,"t":{"47":{"position":[[8464,25]]}}}],["grand",{"_index":278,"t":{"21":{"position":[[2224,5]]},"456":{"position":[[671,6],[687,6]]}}}],["grape",{"_index":3233,"t":{"490":{"position":[[1372,10],[1424,7]]}}}],["graph",{"_index":3967,"t":{"660":{"position":[[703,5],[856,5]]},"694":{"position":[[1610,7],[1730,5],[1749,6],[1784,6]]},"696":{"position":[[1432,5],[3066,5],[3832,5],[4521,5],[5302,5]]},"702":{"position":[[388,5]]},"704":{"position":[[1632,6]]},"927":{"position":[[1585,6]]}}}],["graphic",{"_index":185,"t":{"17":{"position":[[436,10]]},"21":{"position":[[1308,9],[1729,9]]},"57":{"position":[[894,9]]},"103":{"position":[[127,9]]},"105":{"position":[[388,9]]},"107":{"position":[[187,9],[727,9]]},"115":{"position":[[187,9],[560,9],[1069,9]]},"129":{"position":[[103,9],[285,9]]},"131":{"position":[[7,9],[636,9]]},"133":{"position":[[5,9]]},"147":{"position":[[128,9]]},"175":{"position":[[1221,9],[1692,9],[1886,9],[2469,9]]},"241":{"position":[[534,9]]},"274":{"position":[[170,8]]},"290":{"position":[[1290,9],[1678,9]]},"636":{"position":[[410,9],[541,9],[621,9]]},"646":{"position":[[25,9]]},"670":{"position":[[207,9]]},"752":{"position":[[210,9]]},"754":{"position":[[41,9],[568,9],[766,9],[1016,9],[1988,9],[2171,9]]},"975":{"position":[[405,9]]}}}],["great",{"_index":917,"t":{"49":{"position":[[679,6]]},"53":{"position":[[513,5]]},"69":{"position":[[5992,5]]},"123":{"position":[[333,6]]},"185":{"position":[[811,5]]},"332":{"position":[[825,5]]},"376":{"position":[[2161,5],[2240,5]]},"386":{"position":[[132,5]]},"392":{"position":[[797,5]]},"410":{"position":[[603,5]]},"562":{"position":[[11,5]]},"670":{"position":[[2914,5]]},"736":{"position":[[427,5]]},"738":{"position":[[3668,5]]},"740":{"position":[[858,5]]},"762":{"position":[[55,5]]},"764":{"position":[[725,5]]},"796":{"position":[[1110,5]]},"808":{"position":[[4,5]]},"830":{"position":[[77,5]]},"832":{"position":[[4,5]]},"838":{"position":[[69,5]]},"840":{"position":[[897,5]]},"876":{"position":[[345,5]]},"884":{"position":[[891,5]]},"906":{"position":[[617,5]]},"912":{"position":[[833,5]]},"939":{"position":[[33,5],[737,5]]},"943":{"position":[[364,5]]},"951":{"position":[[360,5]]},"955":{"position":[[125,5]]},"967":{"position":[[82,5]]},"975":{"position":[[421,5],[584,5]]},"1023":{"position":[[444,5]]}}}],["greater",{"_index":1196,"t":{"91":{"position":[[146,7]]},"302":{"position":[[1569,7]]},"310":{"position":[[27,7]]},"560":{"position":[[691,7],[743,7]]},"562":{"position":[[554,7]]},"564":{"position":[[281,7]]},"600":{"position":[[1718,8]]}}}],["greatest",{"_index":1287,"t":{"93":{"position":[[971,8]]}}}],["greedi",{"_index":2341,"t":{"350":{"position":[[35,7],[76,6],[264,6],[693,6],[1706,6]]},"352":{"position":[[773,6],[1139,6]]}}}],["green",{"_index":4270,"t":{"716":{"position":[[1951,8],[2346,6],[2362,7],[2795,8],[4171,5],[4668,6]]},"720":{"position":[[3190,6],[5881,5]]},"806":{"position":[[2140,5]]},"862":{"position":[[172,6],[336,6]]}}}],["green)reset=$(tput",{"_index":5026,"t":{"862":{"position":[[4035,19]]}}}],["green=$(tput",{"_index":5025,"t":{"862":{"position":[[3984,12]]}}}],["green='\\e[0;32m'reset='\\e[0m'echo",{"_index":4995,"t":{"862":{"position":[[179,33]]}}}],["green\\033[0m\\n",{"_index":4308,"t":{"716":{"position":[[4081,15]]}}}],["green}${link}${reset",{"_index":4027,"t":{"666":{"position":[[1541,27]]}}}],["green}apples${reset",{"_index":4996,"t":{"862":{"position":[[229,24],[4101,24]]}}}],["grep",{"_index":1370,"t":{"105":{"position":[[221,4]]},"217":{"position":[[878,4]]},"243":{"position":[[1636,4]]},"253":{"position":[[3237,4],[3376,4],[3650,4]]},"259":{"position":[[204,4]]},"260":{"position":[[315,4],[796,4]]},"262":{"position":[[2297,4]]},"364":{"position":[[2177,5]]},"376":{"position":[[487,4],[1366,4],[1854,4],[3179,5]]},"378":{"position":[[2622,4],[3574,4]]},"390":{"position":[[37,4],[119,5],[194,4],[433,4],[992,4],[997,4],[1263,4],[1410,4],[1946,4]]},"392":{"position":[[18,4],[137,4],[589,4]]},"394":{"position":[[956,5],[988,4],[1049,4],[1339,4],[1751,4],[1886,4],[1964,4],[2290,4]]},"396":{"position":[[357,4],[419,4],[1131,4],[1366,4],[1597,4]]},"398":{"position":[[47,4],[381,4]]},"400":{"position":[[45,5],[158,4],[876,4],[1489,4]]},"402":{"position":[[469,4],[1573,4],[1639,4]]},"404":{"position":[[132,4],[306,4],[389,4],[556,4],[582,4],[602,4]]},"406":{"position":[[78,4],[123,4],[165,8],[207,5],[303,4],[437,5],[451,4],[529,4],[831,4],[935,4],[940,4],[979,4],[1091,4]]},"408":{"position":[[0,4],[451,5],[953,4]]},"410":{"position":[[0,4],[96,4],[168,4],[592,4]]},"450":{"position":[[2398,4],[2535,4],[5410,4]]},"454":{"position":[[1482,4],[2452,4]]},"538":{"position":[[2974,4],[3084,4]]},"648":{"position":[[185,4]]},"666":{"position":[[1443,4],[1908,4]]},"744":{"position":[[2686,4]]},"746":{"position":[[862,4],[1246,4],[1365,4]]},"798":{"position":[[1339,5]]},"892":{"position":[[2195,4]]},"927":{"position":[[1960,4]]},"963":{"position":[[46,4]]}}}],["grep(1)nam",{"_index":2578,"t":{"390":{"position":[[107,11]]}}}],["grep='grep",{"_index":4468,"t":{"734":{"position":[[2446,10]]}}}],["grepgrep(1",{"_index":2577,"t":{"390":{"position":[[67,11]]}}}],["grey",{"_index":1877,"t":{"249":{"position":[[764,4]]},"716":{"position":[[2156,5],[2230,6],[3008,6]]}}}],["grip",{"_index":2415,"t":{"364":{"position":[[2166,5]]},"754":{"position":[[2400,5]]}}}],["grmdir",{"_index":2419,"t":{"366":{"position":[[563,6],[1571,6]]}}}],["groff(7",{"_index":696,"t":{"47":{"position":[[718,8]]}}}],["ground",{"_index":2370,"t":{"356":{"position":[[29,9]]}}}],["group",{"_index":746,"t":{"47":{"position":[[2774,5],[2865,10]]},"53":{"position":[[167,7]]},"95":{"position":[[366,5],[413,5],[467,5]]},"203":{"position":[[41,8],[280,6],[480,8]]},"219":{"position":[[1240,6]]},"348":{"position":[[82,7],[100,5],[222,7],[370,6],[571,6],[659,6],[745,6]]},"352":{"position":[[98,7],[2513,6],[2527,6],[2681,5],[2958,7]]},"354":{"position":[[484,7],[578,6],[641,6]]},"356":{"position":[[158,6]]},"376":{"position":[[4006,7],[4082,6],[4406,7],[4635,6]]},"386":{"position":[[736,6],[824,7]]},"568":{"position":[[804,6],[1349,5]]},"896":{"position":[[1444,5]]},"900":{"position":[[966,5],[1015,5],[1166,5]]}}}],["grow",{"_index":3779,"t":{"618":{"position":[[427,4]]}}}],["gs",{"_index":3985,"t":{"660":{"position":[[1338,4]]},"688":{"position":[[1366,4],[1416,4]]},"694":{"position":[[1351,4],[2069,4]]},"696":{"position":[[5720,4]]},"702":{"position":[[806,4]]}}}],["gs=\"git",{"_index":4127,"t":{"688":{"position":[[955,7]]},"696":{"position":[[2941,7],[3746,7],[4413,7]]}}}],["gst",{"_index":3513,"t":{"544":{"position":[[2206,3]]}}}],["gst2",{"_index":3484,"t":{"544":{"position":[[307,5]]},"612":{"position":[[1748,5]]}}}],["gt",{"_index":3544,"t":{"560":{"position":[[660,2]]},"668":{"position":[[2358,2]]},"720":{"position":[[6745,2]]}}}],["gu",{"_index":4642,"t":{"772":{"position":[[510,2],[542,2]]}}}],["gu0go",{"_index":4647,"t":{"772":{"position":[[937,5]]}}}],["guard",{"_index":5396,"t":{"1027":{"position":[[2613,7]]}}}],["guess",{"_index":4021,"t":{"666":{"position":[[1333,5]]}}}],["gui",{"_index":184,"t":{"17":{"position":[[432,3]]},"147":{"position":[[190,3],[306,4]]},"241":{"position":[[682,4]]}}}],["gui)go",{"_index":4657,"t":{"774":{"position":[[1157,6]]}}}],["guid",{"_index":881,"t":{"47":{"position":[[7538,5]]},"51":{"position":[[681,5],[918,6]]},"67":{"position":[[1525,6]]},"636":{"position":[[1678,5]]},"654":{"position":[[3209,5]]},"708":{"position":[[439,5]]},"834":{"position":[[943,6]]},"951":{"position":[[15,5]]}}}],["guin",{"_index":1432,"t":{"121":{"position":[[128,4]]}}}],["guin.txt",{"_index":1434,"t":{"121":{"position":[[188,8]]},"215":{"position":[[469,8]]}}}],["guin.txt10373",{"_index":1683,"t":{"213":{"position":[[443,13]]}}}],["guu",{"_index":4648,"t":{"772":{"position":[[1579,3],[1633,3]]}}}],["guumak",{"_index":4658,"t":{"774":{"position":[[1213,7]]}}}],["gymnast",{"_index":1778,"t":{"227":{"position":[[687,7]]},"251":{"position":[[382,7]]},"253":{"position":[[2289,7]]},"262":{"position":[[3300,7]]}}}],["h",{"_index":605,"t":{"41":{"position":[[162,1],[228,1]]},"189":{"position":[[964,1]]},"400":{"position":[[1669,1]]},"402":{"position":[[294,1]]},"714":{"position":[[551,2],[590,2]]},"716":{"position":[[489,2]]},"766":{"position":[[592,1]]},"798":{"position":[[1196,1]]},"806":{"position":[[3633,1],[3757,2],[3792,2]]},"834":{"position":[[3442,1]]},"860":{"position":[[197,1],[1185,2],[1317,1]]},"931":{"position":[[163,1]]}}}],["ha",{"_index":1950,"t":{"257":{"position":[[1616,3]]}}}],["habit",{"_index":3137,"t":{"468":{"position":[[747,5]]},"482":{"position":[[578,5]]},"770":{"position":[[1347,5]]}}}],["half",{"_index":636,"t":{"43":{"position":[[477,4],[503,4]]},"642":{"position":[[486,4]]}}}],["halfway",{"_index":3446,"t":{"534":{"position":[[990,7]]},"997":{"position":[[432,7]]}}}],["hand",{"_index":986,"t":{"61":{"position":[[234,4]]},"332":{"position":[[912,5]]},"558":{"position":[[1594,4]]},"566":{"position":[[502,4],[549,4]]},"766":{"position":[[1400,5],[1597,4]]},"995":{"position":[[290,5]]}}}],["handi",{"_index":1665,"t":{"209":{"position":[[501,5]]},"432":{"position":[[842,5]]},"562":{"position":[[169,5]]},"734":{"position":[[2217,5]]}}}],["handl",{"_index":71,"t":{"8":{"position":[[206,6]]},"29":{"position":[[1032,6]]},"31":{"position":[[352,6]]},"217":{"position":[[83,7]]},"219":{"position":[[1620,7]]},"229":{"position":[[730,8]]},"237":{"position":[[766,8],[856,9]]},"274":{"position":[[713,7]]},"338":{"position":[[1398,6]]},"342":{"position":[[137,6]]},"440":{"position":[[151,6]]},"466":{"position":[[953,6]]},"468":{"position":[[722,7]]},"504":{"position":[[145,6]]},"538":{"position":[[3413,8]]},"568":{"position":[[731,6]]},"626":{"position":[[1642,9]]},"646":{"position":[[651,6]]},"660":{"position":[[1586,7]]},"720":{"position":[[4296,7]]},"796":{"position":[[430,8]]},"798":{"position":[[1431,8],[1623,6]]},"800":{"position":[[304,6]]},"810":{"position":[[368,8],[455,8]]},"850":{"position":[[2385,8]]},"858":{"position":[[1708,6]]},"860":{"position":[[1291,6],[1343,6],[1954,6],[2109,6]]},"874":{"position":[[834,7]]},"912":{"position":[[405,6]]},"965":{"position":[[154,6]]}}}],["handler",{"_index":4835,"t":{"810":{"position":[[397,8]]},"858":{"position":[[3539,7]]}}}],["hang",{"_index":1822,"t":{"237":{"position":[[714,7]]},"906":{"position":[[900,5]]}}}],["happen",{"_index":667,"t":{"45":{"position":[[348,7]]},"77":{"position":[[1336,8],[3515,7]]},"113":{"position":[[473,9]]},"123":{"position":[[322,10]]},"211":{"position":[[756,8]]},"227":{"position":[[334,7],[423,10]]},"237":{"position":[[222,7]]},"243":{"position":[[965,7]]},"257":{"position":[[3832,9],[4142,6]]},"284":{"position":[[837,6]]},"338":{"position":[[540,10]]},"378":{"position":[[4699,6]]},"452":{"position":[[407,9]]},"456":{"position":[[837,10]]},"462":{"position":[[1858,7]]},"510":{"position":[[318,7]]},"522":{"position":[[984,7]]},"534":{"position":[[1474,7]]},"538":{"position":[[1149,7]]},"608":{"position":[[364,10],[1909,7],[2170,7],[2315,8]]},"694":{"position":[[2380,8]]},"718":{"position":[[1304,7]]},"906":{"position":[[350,7]]},"1019":{"position":[[143,8]]},"1035":{"position":[[1904,7],[2404,7]]}}}],["happi",{"_index":1404,"t":{"115":{"position":[[18,5]]},"582":{"position":[[298,6],[430,5]]},"658":{"position":[[1011,5]]},"684":{"position":[[12,5]]}}}],["hard",{"_index":1057,"t":{"69":{"position":[[4013,4]]},"87":{"position":[[20,4]]},"255":{"position":[[980,4]]},"272":{"position":[[248,4]]},"292":{"position":[[842,4]]},"336":{"position":[[249,4]]},"350":{"position":[[1630,4]]},"378":{"position":[[1836,4]]},"454":{"position":[[180,4]]},"512":{"position":[[1115,5]]},"528":{"position":[[210,4]]},"626":{"position":[[6833,4]]},"684":{"position":[[3234,4]]},"742":{"position":[[546,6]]},"794":{"position":[[427,4],[463,4]]},"800":{"position":[[666,4]]},"808":{"position":[[1311,4]]},"862":{"position":[[4245,4]]}}}],["harder",{"_index":446,"t":{"25":{"position":[[1258,6]]},"77":{"position":[[4431,6]]},"500":{"position":[[938,6]]},"596":{"position":[[926,6]]},"602":{"position":[[1225,6]]},"682":{"position":[[3275,6]]},"798":{"position":[[874,6]]},"834":{"position":[[2647,6]]}}}],["hardwar",{"_index":2035,"t":{"274":{"position":[[98,9],[116,9],[314,9],[356,8],[412,8]]},"292":{"position":[[1296,9]]}}}],["harvest",{"_index":3626,"t":{"584":{"position":[[475,7]]}}}],["hash",{"_index":1453,"t":{"133":{"position":[[370,4]]},"290":{"position":[[1145,4]]},"368":{"position":[[686,4]]},"422":{"position":[[228,4],[377,4],[1069,4]]},"430":{"position":[[566,5]]},"492":{"position":[[218,5]]},"534":{"position":[[1629,6]]},"684":{"position":[[1050,4],[1171,4]]},"694":{"position":[[173,5],[181,4]]},"708":{"position":[[1302,4]]},"714":{"position":[[1483,4]]},"764":{"position":[[826,4]]}}}],["haskel",{"_index":1243,"t":{"91":{"position":[[1851,8]]}}}],["hat",{"_index":293,"t":{"21":{"position":[[2531,3]]}}}],["hate",{"_index":457,"t":{"27":{"position":[[10,4]]},"95":{"position":[[11,4]]}}}],["have",{"_index":400,"t":{"23":{"position":[[2018,6]]},"51":{"position":[[815,6]]},"69":{"position":[[6052,6]]},"81":{"position":[[1500,6]]},"91":{"position":[[3342,6]]},"95":{"position":[[355,6]]},"207":{"position":[[753,6]]},"260":{"position":[[1348,6]]},"280":{"position":[[679,6]]},"450":{"position":[[5461,6]]},"488":{"position":[[1205,6]]},"492":{"position":[[107,6]]},"528":{"position":[[762,6]]},"596":{"position":[[73,6]]},"720":{"position":[[88,6]]},"742":{"position":[[101,6]]},"754":{"position":[[1344,6]]},"796":{"position":[[323,6],[2185,6]]},"798":{"position":[[1185,6]]},"852":{"position":[[2514,6]]},"858":{"position":[[1489,6]]},"997":{"position":[[463,6]]}}}],["haven't",{"_index":1935,"t":{"257":{"position":[[3,7]]},"286":{"position":[[39,7]]},"344":{"position":[[1639,7]]},"700":{"position":[[1384,7]]},"760":{"position":[[3355,7]]},"778":{"position":[[1580,7]]},"898":{"position":[[1546,7]]}}}],["he'll",{"_index":5094,"t":{"890":{"position":[[1835,5]]}}}],["head",{"_index":370,"t":{"23":{"position":[[1102,4]]},"225":{"position":[[177,6],[304,7]]},"253":{"position":[[2779,4],[2827,4]]},"257":{"position":[[3872,4]]},"374":{"position":[[178,4],[478,4]]},"376":{"position":[[5714,4],[5999,7]]},"424":{"position":[[225,4]]},"426":{"position":[[372,4]]},"430":{"position":[[396,4]]},"438":{"position":[[419,4]]},"446":{"position":[[13,4],[66,4],[270,5],[425,4],[838,4],[1295,4],[2569,5],[3473,4],[3882,4],[4061,7],[4148,4]]},"448":{"position":[[271,4],[407,4]]},"450":{"position":[[2368,7]]},"456":{"position":[[393,4]]},"458":{"position":[[83,4],[129,4]]},"512":{"position":[[1069,4]]},"574":{"position":[[1859,4],[2274,4]]},"612":{"position":[[824,4]]},"658":{"position":[[886,9],[2175,4]]},"660":{"position":[[739,4],[881,5]]},"666":{"position":[[905,6],[962,5],[1015,5],[1462,4],[1660,4]]},"672":{"position":[[891,4]]},"688":{"position":[[3308,5]]},"694":{"position":[[1102,6],[1799,6]]},"696":{"position":[[3239,4],[5327,5]]},"700":{"position":[[815,4],[838,4],[903,4],[2283,4]]},"702":{"position":[[413,5],[1301,5],[1311,4],[1386,4],[1409,4],[1541,4],[1732,5],[1947,4],[2151,4],[2216,5],[2687,7],[2833,6],[3046,4]]},"704":{"position":[[1906,4]]},"764":{"position":[[855,9]]},"778":{"position":[[781,7]]},"927":{"position":[[1859,4],[2001,4]]}}}],["head\"12",{"_index":3023,"t":{"454":{"position":[[148,7]]}}}],["head\"title\"\"black",{"_index":2911,"t":{"450":{"position":[[208,17]]}}}],["head\"zootopia",{"_index":3040,"t":{"454":{"position":[[606,13]]}}}],["head)merg",{"_index":4157,"t":{"694":{"position":[[409,12]]}}}],["headach",{"_index":3677,"t":{"588":{"position":[[1138,8]]},"796":{"position":[[1487,9]]}}}],["headalia",{"_index":4196,"t":{"696":{"position":[[3013,9]]}}}],["header",{"_index":2711,"t":{"402":{"position":[[307,9]]},"446":{"position":[[3405,7]]}}}],["headland",{"_index":4786,"t":{"804":{"position":[[2630,9]]},"806":{"position":[[3503,9]]}}}],["head~1",{"_index":4220,"t":{"702":{"position":[[2803,6]]},"704":{"position":[[1882,6]]},"927":{"position":[[1835,6]]}}}],["hear",{"_index":3525,"t":{"550":{"position":[[1434,4]]}}}],["heard",{"_index":2171,"t":{"302":{"position":[[141,5]]},"752":{"position":[[329,5]]}}}],["heavi",{"_index":1852,"t":{"243":{"position":[[816,5]]}}}],["held",{"_index":3192,"t":{"486":{"position":[[115,4]]}}}],["hell",{"_index":1733,"t":{"221":{"position":[[327,4]]}}}],["hello",{"_index":1150,"t":{"79":{"position":[[104,6],[152,5]]},"422":{"position":[[711,6]]},"430":{"position":[[1165,6]]},"432":{"position":[[202,6],[1447,6],[1509,6]]},"482":{"position":[[333,6],[390,5]]},"508":{"position":[[257,7],[1175,7],[2318,7],[2699,7]]},"760":{"position":[[1066,5],[1087,5],[1502,5],[2238,5],[3008,5]]},"770":{"position":[[993,5],[1050,5],[1124,5],[1219,5]]},"862":{"position":[[1945,5]]},"1027":{"position":[[9807,5]]}}}],["hellotest123.effect",{"_index":2290,"t":{"338":{"position":[[1150,22]]}}}],["key",{"_index":160,"t":{"17":{"position":[[50,3]]},"21":{"position":[[780,3],[806,3],[901,4],[2396,4],[2415,5],[2804,3],[2826,5],[4077,5]]},"31":{"position":[[2484,3]]},"41":{"position":[[854,5]]},"43":{"position":[[94,5],[576,4]]},"61":{"position":[[378,4]]},"63":{"position":[[115,3],[259,4]]},"81":{"position":[[1780,3]]},"89":{"position":[[29,3]]},"137":{"position":[[740,3]]},"151":{"position":[[325,5]]},"163":{"position":[[144,5]]},"165":{"position":[[834,4]]},"171":{"position":[[104,4]]},"231":{"position":[[969,3]]},"237":{"position":[[1411,3]]},"251":{"position":[[1071,3]]},"255":{"position":[[1084,4],[1123,4]]},"278":{"position":[[1015,3]]},"290":{"position":[[14,3]]},"302":{"position":[[1612,3]]},"310":{"position":[[251,3]]},"332":{"position":[[41,3],[536,3]]},"342":{"position":[[1302,3]]},"376":{"position":[[72,4],[479,5],[1839,4],[3854,5],[3939,3],[4123,12],[4338,3],[4681,4],[4884,14],[5031,4]]},"394":{"position":[[199,4]]},"474":{"position":[[360,3]]},"612":{"position":[[448,3]]},"634":{"position":[[837,3]]},"636":{"position":[[1072,3]]},"640":{"position":[[1616,3]]},"654":{"position":[[3092,3],[3129,4],[3235,4],[3366,4]]},"670":{"position":[[2970,4]]},"672":{"position":[[61,3]]},"716":{"position":[[1409,4],[1448,3],[1632,4],[5086,4]]},"760":{"position":[[599,4],[1898,3],[3145,3]]},"766":{"position":[[1326,4],[1485,4]]},"770":{"position":[[1516,4]]},"782":{"position":[[133,3]]},"800":{"position":[[1606,3]]},"810":{"position":[[413,3]]},"818":{"position":[[152,3]]},"826":{"position":[[94,3],[214,3],[297,4],[376,4],[585,3]]},"828":{"position":[[538,4]]},"830":{"position":[[776,5],[945,3]]},"834":{"position":[[3880,3],[4241,3],[4844,4]]},"842":{"position":[[582,3],[1801,3]]},"884":{"position":[[624,7],[647,5],[666,5],[846,3],[970,4],[1004,3]]},"890":{"position":[[57,4],[98,3],[116,4],[145,3],[170,3],[210,4],[295,4],[379,3],[421,3],[560,4],[954,4],[978,3],[1002,3],[1125,3],[1157,3],[1326,4],[1431,3],[1461,3],[1552,4],[1606,3],[1858,3],[1889,4],[1911,4],[2052,4],[2204,4],[2265,4],[2424,5],[2460,3]]},"892":{"position":[[50,3],[75,3],[110,3],[276,3],[389,5],[534,3],[575,3],[686,3],[746,3],[971,3],[1041,4],[1195,4],[1263,3],[1405,3],[1510,3],[1555,3],[1907,3],[1930,4],[2182,5],[2255,3],[2266,3],[2375,3]]},"896":{"position":[[185,3],[266,4],[338,3],[387,3],[407,3],[454,3],[649,3],[1943,3],[1983,3],[2007,3],[2151,4],[2215,3],[2246,3]]},"898":{"position":[[1006,3],[1327,3],[1402,3]]},"900":{"position":[[50,3],[223,3],[376,3],[428,3],[472,4],[575,3],[1646,4]]},"902":{"position":[[83,3],[957,3],[1230,3]]},"908":{"position":[[617,3],[1047,4]]},"910":{"position":[[1401,3],[1511,3],[1647,4]]},"912":{"position":[[55,4],[192,4],[804,3]]},"1027":{"position":[[5583,7],[5818,4]]}}}],["key'",{"_index":5112,"t":{"892":{"position":[[1644,5]]}}}],["key.pem",{"_index":5158,"t":{"900":{"position":[[318,8],[751,7],[1317,8],[1382,7]]}}}],["key.pempermiss",{"_index":5162,"t":{"900":{"position":[[480,17]]}}}],["keybind",{"_index":4893,"t":{"834":{"position":[[4984,11],[5069,11]]}}}],["keyboard",{"_index":188,"t":{"21":{"position":[[31,8],[3886,8]]},"25":{"position":[[1524,8]]},"33":{"position":[[70,8]]},"43":{"position":[[258,8]]},"87":{"position":[[491,8]]},"181":{"position":[[53,8],[384,8]]},"247":{"position":[[892,8]]},"249":{"position":[[1748,8],[1921,9],[2145,9]]},"266":{"position":[[439,9],[883,9]]},"272":{"position":[[338,9]]},"282":{"position":[[160,8],[314,9],[1452,8],[1681,8],[1855,8]]},"284":{"position":[[784,8]]},"622":{"position":[[414,8]]},"632":{"position":[[227,8]]},"754":{"position":[[1657,8]]},"758":{"position":[[96,8],[176,8],[273,8]]},"760":{"position":[[611,8]]},"904":{"position":[[1596,8]]},"1019":{"position":[[70,9]]}}}],["keyboardinterrupt",{"_index":4839,"t":{"810":{"position":[[543,17]]}}}],["keyc",{"_index":4909,"t":{"842":{"position":[[144,4]]}}}],["keygen",{"_index":5096,"t":{"892":{"position":[[19,6],[408,6]]}}}],["keyring.../usr/sbin/usr/sbin/cupsd/usr/sbin/pppdump",{"_index":1594,"t":{"189":{"position":[[2329,54]]}}}],["keyring/bin/dpkg",{"_index":1711,"t":{"217":{"position":[[1356,16]]}}}],["keystrok",{"_index":285,"t":{"21":{"position":[[2307,9]]},"37":{"position":[[253,10]]},"308":{"position":[[444,11],[637,9]]},"624":{"position":[[1485,11]]},"626":{"position":[[232,10]]},"760":{"position":[[1029,10],[1236,10],[2013,10],[2202,10],[2417,10],[2669,10],[2763,10],[2827,10]]},"764":{"position":[[1070,10],[2417,10]]},"766":{"position":[[942,10],[960,10],[1736,10],[1889,10]]},"768":{"position":[[290,10]]},"770":{"position":[[695,10]]},"772":{"position":[[707,10],[1222,11]]},"774":{"position":[[581,10],[1085,10],[1402,10]]},"776":{"position":[[585,10]]},"778":{"position":[[1958,10]]},"987":{"position":[[58,11]]},"997":{"position":[[350,11]]}}}],["keyword",{"_index":3187,"t":{"484":{"position":[[1323,7]]},"510":{"position":[[1282,7],[1441,7]]},"522":{"position":[[638,7],[1139,8],[1867,7]]},"540":{"position":[[67,8],[150,7],[179,7],[247,7],[532,8]]},"550":{"position":[[456,8]]},"554":{"position":[[1058,7]]},"556":{"position":[[892,7]]},"580":{"position":[[1787,8]]},"604":{"position":[[297,7]]}}}],["kgo",{"_index":4618,"t":{"766":{"position":[[1151,3]]}}}],["kib",{"_index":3923,"t":{"654":{"position":[[2421,3]]}}}],["kib/",{"_index":3953,"t":{"658":{"position":[[1649,6]]}}}],["kill",{"_index":1557,"t":{"181":{"position":[[284,4]]},"227":{"position":[[531,4]]},"231":{"position":[[92,4]]},"235":{"position":[[626,4],[642,4],[766,4],[862,4],[1081,5]]},"239":{"position":[[356,4],[614,4]]},"243":{"position":[[381,4],[1404,4]]},"842":{"position":[[395,4],[909,4],[932,4],[963,4],[979,4],[1643,4]]},"858":{"position":[[3220,4],[3622,4]]}}}],["kilobyt",{"_index":1138,"t":{"77":{"position":[[4075,9],[4337,10]]}}}],["kind",{"_index":183,"t":{"17":{"position":[[388,4]]},"57":{"position":[[1352,5]]},"71":{"position":[[721,4]]},"95":{"position":[[107,5]]},"113":{"position":[[292,4]]},"241":{"position":[[21,4]]},"257":{"position":[[5067,4]]},"266":{"position":[[293,4],[1571,4]]},"280":{"position":[[138,4]]},"286":{"position":[[223,4],[304,4]]},"338":{"position":[[795,5]]},"376":{"position":[[962,4]]},"378":{"position":[[4513,4]]},"404":{"position":[[190,4]]},"484":{"position":[[713,4],[889,4]]},"506":{"position":[[2735,4]]},"696":{"position":[[183,4]]},"708":{"position":[[1775,4]]},"720":{"position":[[7186,4]]},"728":{"position":[[361,4]]},"796":{"position":[[443,4]]},"834":{"position":[[3364,4]]},"876":{"position":[[296,4]]},"951":{"position":[[398,4]]}}}],["king",{"_index":3074,"t":{"456":{"position":[[648,5]]}}}],["kirk",{"_index":412,"t":{"25":{"position":[[123,4]]}}}],["kkchaudhary11",{"_index":2052,"t":{"276":{"position":[[958,13]]}}}],["knight",{"_index":25,"t":{"4":{"position":[[255,8]]},"450":{"position":[[4944,6],[5166,6]]}}}],["know",{"_index":162,"t":{"17":{"position":[[83,5]]},"23":{"position":[[1254,4]]},"25":{"position":[[1106,4]]},"37":{"position":[[78,4]]},"47":{"position":[[1650,4],[1789,7],[3380,4]]},"49":{"position":[[231,4]]},"57":{"position":[[13,4]]},"67":{"position":[[802,5]]},"69":{"position":[[1644,4]]},"71":{"position":[[1185,4],[1854,4]]},"77":{"position":[[3722,4]]},"87":{"position":[[28,4]]},"99":{"position":[[184,4]]},"105":{"position":[[693,4]]},"107":{"position":[[1288,5]]},"111":{"position":[[121,4]]},"115":{"position":[[896,7],[939,4]]},"133":{"position":[[1034,4]]},"135":{"position":[[450,5],[607,4]]},"139":{"position":[[61,4],[1121,4]]},"175":{"position":[[205,4]]},"195":{"position":[[2619,5]]},"201":{"position":[[288,4]]},"209":{"position":[[510,4]]},"219":{"position":[[217,4]]},"231":{"position":[[1487,4]]},"235":{"position":[[577,4]]},"237":{"position":[[1155,4]]},"251":{"position":[[1099,4],[1765,7]]},"257":{"position":[[1770,4],[3040,4]]},"262":{"position":[[3472,4]]},"268":{"position":[[275,4]]},"274":{"position":[[552,4]]},"286":{"position":[[123,5],[634,4]]},"292":{"position":[[970,5]]},"304":{"position":[[804,4]]},"306":{"position":[[760,4]]},"332":{"position":[[453,4]]},"352":{"position":[[170,7]]},"356":{"position":[[83,7]]},"366":{"position":[[3148,5]]},"374":{"position":[[740,4]]},"380":{"position":[[696,4]]},"390":{"position":[[1341,4]]},"400":{"position":[[1966,7]]},"432":{"position":[[467,4]]},"442":{"position":[[1729,4]]},"446":{"position":[[340,4],[375,4]]},"464":{"position":[[873,4],[1970,4]]},"500":{"position":[[555,5]]},"506":{"position":[[1773,4],[2631,4]]},"508":{"position":[[1325,4],[2090,4],[4936,4]]},"554":{"position":[[270,4]]},"560":{"position":[[158,4]]},"572":{"position":[[1432,4]]},"574":{"position":[[12,4],[2346,4],[2417,4]]},"588":{"position":[[2527,4]]},"594":{"position":[[1625,4]]},"604":{"position":[[225,4]]},"618":{"position":[[554,4]]},"624":{"position":[[118,4]]},"656":{"position":[[1161,5]]},"658":{"position":[[2263,5]]},"666":{"position":[[1068,4]]},"670":{"position":[[1459,7]]},"680":{"position":[[1328,4]]},"688":{"position":[[1246,5]]},"696":{"position":[[3553,4]]},"698":{"position":[[169,4]]},"700":{"position":[[1309,5],[2986,4]]},"716":{"position":[[1682,5]]},"720":{"position":[[5103,5]]},"734":{"position":[[1175,4]]},"742":{"position":[[131,4]]},"752":{"position":[[409,4]]},"770":{"position":[[12,4]]},"794":{"position":[[957,4]]},"798":{"position":[[1611,4]]},"800":{"position":[[1309,4],[1497,4]]},"806":{"position":[[1261,4]]},"818":{"position":[[23,4],[58,7]]},"872":{"position":[[148,4]]},"874":{"position":[[549,4],[740,4]]},"884":{"position":[[906,4]]},"888":{"position":[[392,4]]},"890":{"position":[[248,5]]},"892":{"position":[[1286,4]]},"896":{"position":[[1614,5],[2710,4]]},"908":{"position":[[3622,4]]},"912":{"position":[[764,4]]},"969":{"position":[[115,4]]},"1027":{"position":[[5809,4]]}}}],["knowledg",{"_index":1294,"t":{"93":{"position":[[1298,9]]},"95":{"position":[[910,9]]},"219":{"position":[[2157,9]]},"384":{"position":[[88,9]]},"618":{"position":[[354,9]]},"716":{"position":[[4412,9]]},"756":{"position":[[247,9]]},"890":{"position":[[794,9]]},"1037":{"position":[[433,9]]}}}],["known",{"_index":1016,"t":{"67":{"position":[[951,5]]},"91":{"position":[[3133,6]]},"249":{"position":[[793,5]]},"522":{"position":[[163,5]]},"686":{"position":[[181,5]]},"796":{"position":[[1034,5],[1608,5]]},"804":{"position":[[2259,5]]},"892":{"position":[[244,5]]},"898":{"position":[[1413,5],[2260,5]]},"1019":{"position":[[285,5]]}}}],["kokaina",{"_index":20,"t":{"4":{"position":[[201,9]]}}}],["kong",{"_index":3075,"t":{"456":{"position":[[654,4]]}}}],["konsol",{"_index":3782,"t":{"622":{"position":[[313,8]]},"646":{"position":[[384,8]]}}}],["korn",{"_index":2104,"t":{"288":{"position":[[1060,4]]},"320":{"position":[[24,4]]}}}],["kotlin",{"_index":1237,"t":{"91":{"position":[[1669,6]]}}}],["ksh",{"_index":2239,"t":{"320":{"position":[[95,4]]}}}],["kubectl",{"_index":2212,"t":{"308":{"position":[[552,7]]},"450":{"position":[[869,7],[1526,7],[1992,7],[2499,7]]},"470":{"position":[[432,7],[617,7],[658,7]]},"626":{"position":[[468,10]]}}}],["kubernet",{"_index":1297,"t":{"93":{"position":[[1410,10]]},"378":{"position":[[371,11]]},"450":{"position":[[773,10]]},"470":{"position":[[148,10],[205,10]]}}}],["l",{"_index":606,"t":{"41":{"position":[[167,1],[233,1]]},"45":{"position":[[564,3]]},"131":{"position":[[554,2],[596,1]]},"145":{"position":[[284,1]]},"177":{"position":[[47,2]]},"189":{"position":[[969,1]]},"205":{"position":[[686,1],[781,1]]},"217":{"position":[[867,1],[1284,1],[1321,1]]},"253":{"position":[[1315,1],[1353,1]]},"260":{"position":[[334,1],[871,1]]},"302":{"position":[[377,1],[447,1],[512,1]]},"306":{"position":[[146,1],[541,1]]},"434":{"position":[[1650,1],[2155,1]]},"436":{"position":[[222,1]]},"466":{"position":[[534,1],[585,1],[1007,1],[1108,1],[1242,1],[1390,1]]},"468":{"position":[[1499,1]]},"470":{"position":[[651,1],[871,1]]},"472":{"position":[[140,1],[581,1]]},"562":{"position":[[418,1]]},"626":{"position":[[1294,1]]},"634":{"position":[[481,2]]},"714":{"position":[[661,2]]},"718":{"position":[[554,1],[779,2],[2014,1]]},"766":{"position":[[694,1]]},"834":{"position":[[4915,1]]},"858":{"position":[[3215,1],[3226,1]]}}}],["l)for",{"_index":3672,"t":{"588":{"position":[[642,5],[1886,5]]}}}],["l='l",{"_index":3803,"t":{"624":{"position":[[759,5]]},"734":{"position":[[2719,5]]}}}],["la",{"_index":3077,"t":{"456":{"position":[[667,3],[712,3],[716,2]]},"624":{"position":[[1429,2]]}}}],["la='l",{"_index":3801,"t":{"624":{"position":[[743,6]]},"734":{"position":[[2703,6]]}}}],["label",{"_index":2592,"t":{"390":{"position":[[363,6]]},"670":{"position":[[1351,6]]},"896":{"position":[[1017,5]]}}}],["lack",{"_index":4451,"t":{"734":{"position":[[1660,5]]}}}],["ladi",{"_index":3081,"t":{"456":{"position":[[732,5]]}}}],["land",{"_index":2060,"t":{"278":{"position":[[78,7]]},"456":{"position":[[719,4]]},"914":{"position":[[30,6]]}}}],["landscap",{"_index":1186,"t":{"87":{"position":[[626,9]]},"89":{"position":[[59,9]]},"91":{"position":[[1487,10]]}}}],["languag",{"_index":935,"t":{"51":{"position":[[211,9],[985,10]]},"69":{"position":[[4688,8]]},"71":{"position":[[1392,8]]},"87":{"position":[[838,9]]},"91":{"position":[[33,9],[498,9],[655,10],[729,10],[782,9],[857,9],[924,10],[948,8],[1091,8],[1230,8],[1369,10],[1404,9],[1592,9],[1750,10],[1761,9],[1894,9],[2007,10],[2107,10],[2429,10],[2684,10],[3388,10]]},"93":{"position":[[34,9]]},"205":{"position":[[1057,10]]},"229":{"position":[[600,9]]},"260":{"position":[[1247,9]]},"262":{"position":[[757,10],[917,10],[1076,10]]},"280":{"position":[[491,8]]},"284":{"position":[[1550,9]]},"354":{"position":[[175,10]]},"366":{"position":[[4767,9],[4805,9]]},"382":{"position":[[237,8]]},"384":{"position":[[210,8],[419,8]]},"386":{"position":[[1290,8]]},"464":{"position":[[1939,8]]},"498":{"position":[[116,9]]},"522":{"position":[[213,9]]},"528":{"position":[[858,8]]},"536":{"position":[[203,10]]},"550":{"position":[[1573,9],[1822,9]]},"580":{"position":[[2083,10]]},"584":{"position":[[1198,9],[2455,8]]},"588":{"position":[[2882,9]]},"590":{"position":[[29,9]]},"596":{"position":[[1804,8],[2136,8]]},"600":{"position":[[1321,9]]},"610":{"position":[[1088,9]]},"658":{"position":[[851,8]]},"754":{"position":[[295,10]]},"796":{"position":[[34,9],[220,8],[364,8],[408,8],[570,9],[587,8],[694,9],[978,8],[1322,9],[1781,8]]},"800":{"position":[[1673,9]]},"812":{"position":[[200,8]]},"876":{"position":[[883,8],[972,9],[1271,8]]}}}],["lao",{"_index":1420,"t":{"117":{"position":[[658,4],[682,4]]}}}],["larg",{"_index":834,"t":{"47":{"position":[[6244,5],[6343,6]]},"69":{"position":[[1082,5],[3087,6]]},"219":{"position":[[423,5]]},"231":{"position":[[191,5]]},"251":{"position":[[1613,5]]},"282":{"position":[[954,6],[1053,5]]},"376":{"position":[[6175,5]]},"392":{"position":[[521,5],[555,5]]},"418":{"position":[[199,5]]},"446":{"position":[[386,6]]},"588":{"position":[[1204,5]]},"626":{"position":[[3895,5]]},"724":{"position":[[501,5]]},"738":{"position":[[6456,5],[6699,5]]},"794":{"position":[[417,5]]},"798":{"position":[[527,5]]},"810":{"position":[[1371,5]]},"816":{"position":[[693,5]]},"876":{"position":[[683,5]]}}}],["larger",{"_index":444,"t":{"25":{"position":[[1229,6]]},"91":{"position":[[746,6]]},"282":{"position":[[1487,6]]},"344":{"position":[[2239,6],[2250,7],[3881,6]]},"456":{"position":[[118,6]]},"612":{"position":[[387,6]]},"734":{"position":[[727,6]]},"744":{"position":[[342,6]]}}}],["last",{"_index":1187,"t":{"87":{"position":[[645,4]]},"121":{"position":[[1230,4]]},"143":{"position":[[4,4],[134,4],[242,4]]},"145":{"position":[[942,4]]},"183":{"position":[[150,4],[196,4],[381,4]]},"253":{"position":[[3139,4]]},"266":{"position":[[614,4]]},"286":{"position":[[4,4]]},"350":{"position":[[414,4],[746,4]]},"368":{"position":[[1731,4]]},"400":{"position":[[1428,4]]},"406":{"position":[[970,4]]},"422":{"position":[[1802,4]]},"424":{"position":[[344,4]]},"426":{"position":[[552,4]]},"468":{"position":[[562,4]]},"490":{"position":[[405,4]]},"500":{"position":[[488,4]]},"534":{"position":[[1145,4]]},"536":{"position":[[1427,4]]},"596":{"position":[[684,4],[1243,4]]},"604":{"position":[[617,4]]},"668":{"position":[[2265,4]]},"684":{"position":[[2863,4]]},"688":{"position":[[2640,4],[2734,4],[2803,4]]},"696":{"position":[[1641,4]]},"718":{"position":[[1142,4]]},"720":{"position":[[6645,4]]},"722":{"position":[[2254,4]]},"744":{"position":[[977,4]]},"832":{"position":[[325,4],[1175,4]]},"842":{"position":[[855,4]]},"860":{"position":[[2385,4]]},"876":{"position":[[116,4]]},"1027":{"position":[[8549,4]]},"1033":{"position":[[3603,4]]}}}],["late",{"_index":4852,"t":{"820":{"position":[[98,4]]},"959":{"position":[[37,4]]},"961":{"position":[[35,4]]}}}],["later",{"_index":407,"t":{"23":{"position":[[2181,5],[2315,5]]},"31":{"position":[[598,5],[2926,6],[2973,5]]},"33":{"position":[[320,6],[716,6]]},"47":{"position":[[4887,5]]},"71":{"position":[[544,7],[1900,5],[2246,5]]},"75":{"position":[[0,5]]},"77":{"position":[[139,5],[816,6]]},"81":{"position":[[1093,5]]},"91":{"position":[[829,6],[1464,5],[1799,5]]},"105":{"position":[[490,5]]},"107":{"position":[[793,5]]},"117":{"position":[[938,6]]},"121":{"position":[[740,6]]},"131":{"position":[[807,6]]},"147":{"position":[[672,5],[852,5]]},"185":{"position":[[975,5]]},"195":{"position":[[2796,5]]},"207":{"position":[[1021,5]]},"209":{"position":[[525,5]]},"213":{"position":[[1123,6]]},"225":{"position":[[724,5]]},"241":{"position":[[816,6],[1289,5]]},"249":{"position":[[839,5]]},"257":{"position":[[3134,7],[4810,5]]},"262":{"position":[[338,5],[427,5],[2465,5]]},"288":{"position":[[734,5],[994,5]]},"292":{"position":[[1982,5],[2068,5]]},"300":{"position":[[867,5]]},"302":{"position":[[1589,5]]},"306":{"position":[[72,6],[725,6]]},"326":{"position":[[417,5]]},"330":{"position":[[214,5]]},"354":{"position":[[1131,5]]},"366":{"position":[[2726,5]]},"370":{"position":[[1290,6]]},"378":{"position":[[1168,5],[4211,6]]},"384":{"position":[[505,5]]},"420":{"position":[[779,5]]},"422":{"position":[[2192,6]]},"442":{"position":[[1479,6]]},"450":{"position":[[5274,5]]},"462":{"position":[[426,5]]},"482":{"position":[[777,6],[862,5]]},"488":{"position":[[1087,5],[1614,5]]},"522":{"position":[[2165,5]]},"540":{"position":[[878,5]]},"586":{"position":[[2479,5]]},"588":{"position":[[1780,6],[2288,5]]},"624":{"position":[[1338,5]]},"670":{"position":[[1912,5]]},"680":{"position":[[1498,6]]},"682":{"position":[[1855,5],[3455,5]]},"686":{"position":[[285,5]]},"688":{"position":[[3341,7]]},"714":{"position":[[2146,5]]},"716":{"position":[[5751,5]]},"720":{"position":[[1518,5]]},"736":{"position":[[1444,5]]},"772":{"position":[[489,6],[1536,6]]},"818":{"position":[[449,6],[1038,5]]},"828":{"position":[[2056,5]]},"832":{"position":[[159,6]]},"856":{"position":[[257,5]]},"884":{"position":[[1027,5]]}}}],["later.history_fil",{"_index":3585,"t":{"574":{"position":[[1017,21]]}}}],["later.history_lines=1000",{"_index":3586,"t":{"574":{"position":[[1075,24]]}}}],["latest",{"_index":1022,"t":{"67":{"position":[[1245,6],[1577,6]]},"69":{"position":[[627,6],[3123,6]]},"71":{"position":[[1133,6],[2002,6]]},"660":{"position":[[233,6]]},"802":{"position":[[124,6]]},"822":{"position":[[230,6]]},"975":{"position":[[208,6]]}}}],["latin",{"_index":2988,"t":{"450":{"position":[[3931,5],[4040,5],[4540,5],[4623,5]]}}}],["launch",{"_index":5129,"t":{"896":{"position":[[723,7],[1295,7],[1386,6],[1819,8],[2362,7],[2382,9]]}}}],["laura",{"_index":3083,"t":{"456":{"position":[[751,6]]}}}],["layer",{"_index":151,"t":{"15":{"position":[[49,6],[128,6]]},"262":{"position":[[3113,5]]},"278":{"position":[[1143,5]]},"292":{"position":[[560,6]]},"892":{"position":[[1212,5]]}}}],["layout",{"_index":4849,"t":{"818":{"position":[[621,6]]},"991":{"position":[[97,6]]}}}],["layoutc",{"_index":4913,"t":{"842":{"position":[[235,7]]}}}],["lazi",{"_index":2340,"t":{"350":{"position":[[27,4],[68,4],[848,4],[1211,4],[1360,4],[1611,4],[1697,4]]},"352":{"position":[[204,4],[1482,4]]},"506":{"position":[[603,4]]},"979":{"position":[[94,4],[160,4]]},"1013":{"position":[[94,4],[160,4]]}}}],["lc)synopsi",{"_index":759,"t":{"47":{"position":[[3188,11],[4645,11]]}}}],["lchown",{"_index":743,"t":{"47":{"position":[[2737,7]]}}}],["le",{"_index":1431,"t":{"121":{"position":[[125,2],[185,2]]},"213":{"position":[[440,2]]},"215":{"position":[[466,2]]},"560":{"position":[[599,2]]}}}],["lead",{"_index":1118,"t":{"77":{"position":[[2967,7]]},"352":{"position":[[1094,4],[3127,4]]},"396":{"position":[[1098,7]]},"400":{"position":[[816,4]]},"424":{"position":[[600,7]]},"442":{"position":[[549,7]]},"522":{"position":[[2136,4]]},"538":{"position":[[2066,4]]},"540":{"position":[[292,4]]},"564":{"position":[[591,4]]},"596":{"position":[[797,4],[819,7],[1482,7]]},"696":{"position":[[298,4]]},"850":{"position":[[278,4]]}}}],["leader",{"_index":4857,"t":{"826":{"position":[[70,7],[82,6],[171,6],[290,6],[435,6],[578,6]]},"834":{"position":[[3533,7],[4053,6]]}}}],["learn",{"_index":566,"t":{"31":{"position":[[3151,5]]},"53":{"position":[[526,5]]},"57":{"position":[[627,5],[1400,5]]},"59":{"position":[[19,5]]},"69":{"position":[[5745,5]]},"81":{"position":[[2246,8]]},"83":{"position":[[100,5]]},"91":{"position":[[478,5]]},"95":{"position":[[2595,8]]},"139":{"position":[[1244,8]]},"175":{"position":[[2599,5]]},"185":{"position":[[36,7]]},"207":{"position":[[674,5]]},"215":{"position":[[30,6]]},"219":{"position":[[341,5]]},"336":{"position":[[1482,5],[1524,5]]},"356":{"position":[[240,5],[277,7]]},"376":{"position":[[2123,5],[2253,5]]},"382":{"position":[[348,8]]},"386":{"position":[[1118,5]]},"408":{"position":[[494,6]]},"448":{"position":[[2151,5]]},"474":{"position":[[350,5]]},"542":{"position":[[19,7]]},"670":{"position":[[156,5],[1122,8],[2750,8]]},"674":{"position":[[19,7]]},"714":{"position":[[2328,5]]},"724":{"position":[[651,5]]},"754":{"position":[[1559,8],[2031,5]]},"756":{"position":[[310,8]]},"762":{"position":[[125,9]]},"780":{"position":[[171,8]]},"782":{"position":[[200,8]]},"788":{"position":[[30,8]]},"790":{"position":[[217,8],[487,5]]},"800":{"position":[[767,8],[1442,8]]},"810":{"position":[[724,8]]},"834":{"position":[[4623,5]]},"844":{"position":[[3,5],[256,5]]},"846":{"position":[[156,7]]},"939":{"position":[[519,5]]},"953":{"position":[[147,9]]}}}],["learnt",{"_index":335,"t":{"21":{"position":[[4020,6]]},"33":{"position":[[19,7]]},"83":{"position":[[19,7]]},"386":{"position":[[155,6]]},"394":{"position":[[1819,6]]},"660":{"position":[[1631,6]]},"800":{"position":[[1624,6]]}}}],["leav",{"_index":526,"t":{"31":{"position":[[377,7],[2622,7]]},"69":{"position":[[1596,5]]},"237":{"position":[[692,5]]},"260":{"position":[[763,6]]},"290":{"position":[[831,5]]},"586":{"position":[[1372,6]]},"668":{"position":[[501,5]]},"686":{"position":[[355,5]]},"692":{"position":[[1768,5]]},"738":{"position":[[4112,5]]},"754":{"position":[[1300,7]]},"764":{"position":[[1744,7],[2816,7]]},"806":{"position":[[1935,5]]},"856":{"position":[[189,6]]},"892":{"position":[[913,5]]}}}],["led",{"_index":1209,"t":{"91":{"position":[[605,3]]},"352":{"position":[[950,3]]},"356":{"position":[[479,3]]}}}],["lee",{"_index":42,"t":{"4":{"position":[[446,3],[461,3]]}}}],["left",{"_index":290,"t":{"21":{"position":[[2401,4]]},"25":{"position":[[944,4]]},"61":{"position":[[229,4]]},"69":{"position":[[3908,4],[4768,4],[5420,4]]},"105":{"position":[[318,5]]},"171":{"position":[[84,4]]},"257":{"position":[[6214,4]]},"260":{"position":[[913,4]]},"514":{"position":[[513,8],[566,4]]},"566":{"position":[[497,4]]},"598":{"position":[[337,5]]},"626":{"position":[[3369,4]]},"708":{"position":[[52,4]]},"738":{"position":[[6126,4]]},"766":{"position":[[597,5],[1078,4],[1285,5]]},"802":{"position":[[1324,4]]},"816":{"position":[[710,4],[1539,4]]},"818":{"position":[[1072,4]]},"896":{"position":[[306,4],[1030,5]]},"953":{"position":[[228,4]]},"1035":{"position":[[1963,4],[2675,4]]}}}],["left/down/up/right",{"_index":4668,"t":{"778":{"position":[[310,19]]}}}],["legaci",{"_index":2219,"t":{"312":{"position":[[234,6]]},"330":{"position":[[59,6]]},"738":{"position":[[3235,6]]}}}],["legibl",{"_index":4013,"t":{"666":{"position":[[846,12]]}}}],["len(definit",{"_index":4810,"t":{"806":{"position":[[1821,15],[2005,15]]}}}],["len(separ",{"_index":4809,"t":{"806":{"position":[[1804,14]]}}}],["len(word",{"_index":4808,"t":{"806":{"position":[[1792,9]]}}}],["length",{"_index":898,"t":{"47":{"position":[[8429,6]]},"288":{"position":[[980,6]]},"352":{"position":[[913,6]]},"490":{"position":[[1236,6]]},"506":{"position":[[495,6],[535,6],[631,8],[640,9],[2088,6]]},"526":{"position":[[727,6]]},"560":{"position":[[200,6],[247,6]]},"594":{"position":[[598,6]]},"596":{"position":[[1214,7]]},"600":{"position":[[984,6],[1031,6]]},"624":{"position":[[615,6]]},"734":{"position":[[562,6]]},"806":{"position":[[292,6],[454,6],[972,8],[3880,6]]},"1027":{"position":[[3304,6],[3730,6],[3980,6],[4512,6],[5886,6],[5937,6],[6036,6]]}}}],["lennart",{"_index":5277,"t":{"953":{"position":[[482,7]]}}}],["less",{"_index":632,"t":{"43":{"position":[[342,4],[426,5],[1145,4],[1219,4],[1335,5],[1410,5]]},"47":{"position":[[91,5]]},"253":{"position":[[3278,4]]},"255":{"position":[[910,4]]},"260":{"position":[[244,4],[1135,4]]},"304":{"position":[[1693,4]]},"308":{"position":[[585,4]]},"396":{"position":[[1197,4]]},"406":{"position":[[573,4]]},"408":{"position":[[565,4]]},"456":{"position":[[629,4],[948,4]]},"458":{"position":[[1688,4]]},"480":{"position":[[494,4]]},"540":{"position":[[436,4],[464,4]]},"544":{"position":[[204,4]]},"560":{"position":[[581,4],[630,4]]},"564":{"position":[[315,4]]},"594":{"position":[[621,4]]},"600":{"position":[[1763,5]]},"612":{"position":[[204,4]]},"686":{"position":[[167,5]]},"796":{"position":[[1024,4],[1598,4],[1645,4]]},"800":{"position":[[677,4]]},"927":{"position":[[2006,4]]},"1037":{"position":[[475,4]]}}}],["lesson",{"_index":4705,"t":{"800":{"position":[[1610,7]]}}}],["let",{"_index":939,"t":{"51":{"position":[[463,4]]},"81":{"position":[[682,4]]},"207":{"position":[[542,4]]},"221":{"position":[[81,4]]},"231":{"position":[[1479,4]]},"259":{"position":[[457,4]]},"338":{"position":[[434,4]]},"348":{"position":[[106,4]]},"378":{"position":[[3453,4]]},"390":{"position":[[1415,4]]},"400":{"position":[[1347,4]]},"410":{"position":[[233,4]]},"468":{"position":[[979,4],[2395,4]]},"508":{"position":[[2077,7]]},"670":{"position":[[1824,4]]},"748":{"position":[[762,4]]},"766":{"position":[[1620,4]]},"830":{"position":[[741,4]]},"840":{"position":[[400,4]]}}}],["let'",{"_index":480,"t":{"29":{"position":[[158,5]]},"31":{"position":[[0,5]]},"47":{"position":[[7026,5]]},"49":{"position":[[171,5],[388,5]]},"59":{"position":[[4,5]]},"77":{"position":[[628,5],[1042,5],[1205,5],[1832,5],[3699,5],[5107,5]]},"81":{"position":[[721,5]]},"89":{"position":[[3,5]]},"99":{"position":[[635,5],[904,5],[1927,5]]},"101":{"position":[[170,5]]},"103":{"position":[[306,5]]},"105":{"position":[[95,5]]},"107":{"position":[[0,5]]},"109":{"position":[[0,5]]},"111":{"position":[[154,5]]},"113":{"position":[[90,5],[446,5]]},"115":{"position":[[237,5],[433,5],[694,5]]},"117":{"position":[[0,5]]},"119":{"position":[[111,5]]},"121":{"position":[[825,5],[870,5],[1242,5]]},"123":{"position":[[109,5],[361,5]]},"131":{"position":[[508,5]]},"135":{"position":[[649,5]]},"141":{"position":[[96,5]]},"151":{"position":[[0,5],[331,5]]},"159":{"position":[[63,5]]},"165":{"position":[[83,5]]},"189":{"position":[[118,5],[300,5],[2462,5]]},"191":{"position":[[174,5]]},"193":{"position":[[71,5]]},"195":{"position":[[1667,5]]},"207":{"position":[[922,5]]},"209":{"position":[[601,5]]},"225":{"position":[[0,5]]},"227":{"position":[[0,5],[861,5]]},"231":{"position":[[0,5],[602,5]]},"233":{"position":[[113,5]]},"237":{"position":[[1381,5]]},"253":{"position":[[0,5],[335,5]]},"255":{"position":[[4,5],[129,5]]},"257":{"position":[[47,5],[309,5],[454,5],[637,5],[893,5],[1817,5],[2069,5]]},"274":{"position":[[876,5]]},"280":{"position":[[1150,5],[1236,5]]},"288":{"position":[[1598,5]]},"298":{"position":[[178,5]]},"302":{"position":[[302,5]]},"306":{"position":[[83,5]]},"336":{"position":[[429,5]]},"338":{"position":[[551,5],[998,5]]},"340":{"position":[[465,5]]},"342":{"position":[[214,5],[310,5],[540,5],[936,5],[1813,5]]},"344":{"position":[[293,5],[731,5],[1141,5],[1693,5]]},"350":{"position":[[441,5]]},"362":{"position":[[265,5]]},"364":{"position":[[90,5],[1643,5]]},"366":{"position":[[346,5],[1671,5],[2336,5],[3399,5],[3891,5],[4904,5]]},"368":{"position":[[57,5]]},"374":{"position":[[72,5],[458,5]]},"376":{"position":[[0,5],[447,5],[902,5],[2635,5],[2674,5],[3248,5],[3358,5],[3864,5],[4825,5],[5164,5],[5641,5]]},"378":{"position":[[163,5],[498,5],[825,5],[1270,5]]},"390":{"position":[[1975,5]]},"394":{"position":[[647,5],[693,5],[926,5]]},"396":{"position":[[424,5],[2290,5]]},"400":{"position":[[863,5],[1466,5]]},"416":{"position":[[0,5]]},"418":{"position":[[389,5]]},"420":{"position":[[70,5],[1333,5]]},"424":{"position":[[295,5],[1755,5]]},"428":{"position":[[308,5]]},"434":{"position":[[421,5],[692,5]]},"446":{"position":[[253,5]]},"448":{"position":[[164,5]]},"450":{"position":[[130,5],[735,5],[3048,5],[4156,5]]},"454":{"position":[[66,5],[2312,5]]},"462":{"position":[[274,5],[384,5],[1304,5],[1838,5],[2539,5],[2767,5]]},"464":{"position":[[80,5],[259,5],[1141,5]]},"470":{"position":[[93,5]]},"484":{"position":[[827,5]]},"488":{"position":[[108,5],[1533,5]]},"500":{"position":[[223,5]]},"502":{"position":[[177,5]]},"506":{"position":[[444,5]]},"508":{"position":[[188,5]]},"512":{"position":[[197,5]]},"518":{"position":[[293,5]]},"522":{"position":[[969,5]]},"524":{"position":[[161,5],[324,5]]},"526":{"position":[[75,5]]},"528":{"position":[[230,5]]},"530":{"position":[[829,5]]},"534":{"position":[[163,5]]},"536":{"position":[[603,5]]},"538":{"position":[[346,5],[1205,5],[2481,5]]},"544":{"position":[[396,5],[435,5],[984,5],[1122,5]]},"550":{"position":[[496,5]]},"558":{"position":[[158,5]]},"570":{"position":[[124,5],[915,5]]},"574":{"position":[[282,5]]},"580":{"position":[[451,5]]},"584":{"position":[[409,5],[1620,5]]},"586":{"position":[[892,5]]},"588":{"position":[[258,5]]},"602":{"position":[[430,5]]},"606":{"position":[[181,5]]},"612":{"position":[[248,5]]},"624":{"position":[[0,5],[1637,5]]},"626":{"position":[[110,5],[5089,5]]},"630":{"position":[[439,5]]},"634":{"position":[[250,5]]},"636":{"position":[[1526,5]]},"638":{"position":[[854,5]]},"654":{"position":[[3408,5]]},"658":{"position":[[280,5],[375,5]]},"664":{"position":[[552,5]]},"672":{"position":[[30,5]]},"680":{"position":[[216,5]]},"682":{"position":[[1160,5],[2556,5],[3750,5]]},"684":{"position":[[2152,5],[2486,5]]},"686":{"position":[[1100,5]]},"688":{"position":[[787,5],[868,5],[1286,5],[2828,5]]},"690":{"position":[[921,5]]},"692":{"position":[[0,5],[1042,5]]},"696":{"position":[[1198,5],[2276,5]]},"700":{"position":[[189,5],[1863,5],[2390,5],[3327,5]]},"702":{"position":[[329,5]]},"708":{"position":[[495,5],[1817,5]]},"710":{"position":[[699,5]]},"714":{"position":[[1814,5],[2448,5]]},"718":{"position":[[1289,5],[2398,5]]},"720":{"position":[[607,5]]},"722":{"position":[[567,5]]},"732":{"position":[[1788,5]]},"734":{"position":[[101,5],[3753,5]]},"736":{"position":[[1231,5]]},"738":{"position":[[94,5],[984,5],[2067,5],[4572,5],[6376,5],[7956,5]]},"744":{"position":[[801,5],[1125,5],[1218,5],[1346,5],[2164,5]]},"752":{"position":[[367,5]]},"760":{"position":[[891,5],[2178,5],[2368,5],[3398,5]]},"762":{"position":[[256,5]]},"764":{"position":[[59,5],[983,5],[1196,5],[1441,5],[1519,5],[2376,5]]},"766":{"position":[[152,5],[416,5],[812,5]]},"768":{"position":[[220,5]]},"770":{"position":[[661,5],[1609,5]]},"772":{"position":[[668,5]]},"774":{"position":[[206,5],[483,5],[1041,5]]},"778":{"position":[[1165,5],[1335,5],[1662,5],[1908,5]]},"800":{"position":[[1898,5]]},"802":{"position":[[7,5],[865,5],[1848,5],[2618,5],[3396,5]]},"804":{"position":[[2800,5]]},"824":{"position":[[27,5]]},"828":{"position":[[192,5],[1242,5]]},"830":{"position":[[204,5],[607,5]]},"834":{"position":[[1061,5]]},"852":{"position":[[222,5]]},"860":{"position":[[107,5]]},"862":{"position":[[1869,5]]},"884":{"position":[[921,5]]},"886":{"position":[[208,5]]},"890":{"position":[[2430,5]]},"892":{"position":[[261,5],[2296,5]]},"894":{"position":[[1486,5]]},"898":{"position":[[2986,5]]},"908":{"position":[[1218,5],[1916,5],[2625,5]]},"1019":{"position":[[1589,5]]},"1021":{"position":[[0,5]]},"1027":{"position":[[1608,5]]},"1033":{"position":[[578,5],[1471,5]]}}}],["letter",{"_index":1456,"t":{"133":{"position":[[919,7]]},"183":{"position":[[205,8]]},"195":{"position":[[135,7],[442,7]]},"201":{"position":[[96,6]]},"205":{"position":[[635,6]]},"259":{"position":[[331,6]]},"344":{"position":[[914,7],[1076,7]]},"346":{"position":[[1123,7]]},"366":{"position":[[1287,7],[1741,7]]},"396":{"position":[[832,7]]},"398":{"position":[[193,7]]},"468":{"position":[[2460,7]]},"694":{"position":[[203,7]]},"702":{"position":[[1158,7]]},"744":{"position":[[154,7]]},"806":{"position":[[1890,7]]},"826":{"position":[[589,6],[603,6]]},"860":{"position":[[437,7],[513,6],[1803,6],[1879,6],[1993,6]]},"902":{"position":[[1197,7]]}}}],["let’",{"_index":1773,"t":{"225":{"position":[[1789,5]]},"422":{"position":[[591,5]]}}}],["level",{"_index":976,"t":{"57":{"position":[[1005,5]]},"185":{"position":[[402,5]]},"384":{"position":[[189,5]]},"634":{"position":[[2603,5]]},"714":{"position":[[1137,6]]},"796":{"position":[[1137,5]]},"844":{"position":[[298,6]]},"852":{"position":[[1718,6]]},"945":{"position":[[68,7]]},"955":{"position":[[163,5]]},"1037":{"position":[[39,5]]}}}],["lexic",{"_index":3377,"t":{"522":{"position":[[303,7],[320,7],[776,7]]}}}],["lexograph",{"_index":2859,"t":{"442":{"position":[[892,11],[1063,11]]}}}],["lgo",{"_index":4615,"t":{"766":{"position":[[1035,3]]}}}],["lh",{"_index":1127,"t":{"77":{"position":[[3789,2],[3862,2],[4198,2],[4628,2]]},"81":{"position":[[2155,3]]}}}],["li",{"_index":43,"t":{"4":{"position":[[450,4],[465,4]]}}}],["liana",{"_index":4725,"t":{"802":{"position":[[2479,5]]},"806":{"position":[[3236,9]]}}}],["libc",{"_index":758,"t":{"47":{"position":[[3180,6],[4637,6]]}}}],["librari",{"_index":582,"t":{"37":{"position":[[325,7]]},"47":{"position":[[456,7],[496,10],[2980,7],[3037,7],[3093,7],[3172,7],[3561,7],[3693,7],[3866,7],[4453,7],[4629,7],[4805,7]]},"185":{"position":[[181,7],[248,7]]},"257":{"position":[[566,9],[624,8]]},"278":{"position":[[239,7],[623,8]]},"290":{"position":[[1604,8],[1639,7]]},"796":{"position":[[1150,10],[2130,7]]},"800":{"position":[[1209,7]]},"943":{"position":[[302,7]]}}}],["licens",{"_index":2813,"t":{"432":{"position":[[1134,9]]}}}],["lieder",{"_index":4726,"t":{"802":{"position":[[2486,6]]}}}],["life",{"_index":1487,"t":{"151":{"position":[[188,4]]}}}],["lifesav",{"_index":2709,"t":{"400":{"position":[[2014,9]]}}}],["light",{"_index":4279,"t":{"716":{"position":[[2150,5],[3002,5]]},"720":{"position":[[2220,5]]}}}],["limit",{"_index":363,"t":{"23":{"position":[[978,11]]},"219":{"position":[[1856,5]]},"278":{"position":[[177,7],[426,5]]},"370":{"position":[[297,5]]},"382":{"position":[[392,7]]},"390":{"position":[[1508,7]]},"408":{"position":[[440,7]]},"424":{"position":[[760,5]]},"468":{"position":[[1741,7]]},"470":{"position":[[301,7]]},"482":{"position":[[1294,7]]},"508":{"position":[[3480,8],[3656,5],[3736,5],[4448,5]]},"718":{"position":[[196,5]]},"722":{"position":[[385,5],[493,5],[1879,5]]},"794":{"position":[[538,7]]},"800":{"position":[[1153,5]]},"806":{"position":[[282,5],[443,6]]},"840":{"position":[[845,11]]},"985":{"position":[[1530,5]]},"1035":{"position":[[3330,11]]},"1037":{"position":[[681,11]]}}}],["limitless",{"_index":4428,"t":{"724":{"position":[[1272,10]]}}}],["line",{"_index":134,"t":{"13":{"position":[[114,5]]},"21":{"position":[[1283,4]]},"33":{"position":[[542,5]]},"41":{"position":[[743,4]]},"43":{"position":[[543,5]]},"47":{"position":[[6450,4]]},"87":{"position":[[112,4],[732,4]]},"91":{"position":[[3177,4]]},"95":{"position":[[798,5]]},"151":{"position":[[256,5]]},"161":{"position":[[70,4],[157,5],[197,4],[212,4]]},"163":{"position":[[53,5]]},"165":{"position":[[327,4],[370,6],[490,4],[584,5]]},"167":{"position":[[49,4]]},"175":{"position":[[324,4],[723,5],[1633,4],[2237,4]]},"179":{"position":[[321,4],[828,4]]},"185":{"position":[[231,4],[1120,4],[1229,4]]},"229":{"position":[[114,4]]},"253":{"position":[[1229,5],[1538,5],[2847,5],[2910,5],[3144,5],[3489,5],[3528,5]]},"255":{"position":[[1622,5],[1880,4]]},"260":{"position":[[216,5],[672,5],[738,6],[842,5],[903,5],[1013,5]]},"262":{"position":[[2172,5],[2313,5],[2811,4]]},"284":{"position":[[1163,5]]},"290":{"position":[[747,5],[792,5],[2008,5]]},"300":{"position":[[738,5]]},"336":{"position":[[1121,5]]},"340":{"position":[[536,5],[605,4]]},"344":{"position":[[4865,4]]},"346":{"position":[[287,4],[412,5],[577,5],[608,5],[692,5],[726,5],[1096,4],[1399,4],[1415,4]]},"348":{"position":[[299,4]]},"350":{"position":[[773,4]]},"366":{"position":[[339,6],[1362,4],[1714,5],[2469,4],[2485,4],[2514,5],[2559,4],[2630,4],[2658,4],[2780,4],[2797,6],[2971,5],[3164,4],[3253,5],[3974,4],[4001,5]]},"368":{"position":[[629,5],[788,6],[1293,5],[1315,4],[1347,4],[1409,4],[1492,7],[1500,4],[1514,4],[1642,4],[1687,4],[1714,4],[1736,4],[1748,5],[1773,5],[1830,4]]},"370":{"position":[[73,5],[131,5],[208,4],[261,6],[281,4],[1097,5],[1152,5],[1350,6]]},"372":{"position":[[69,5],[166,4],[217,4],[946,4],[1014,4],[1379,4]]},"374":{"position":[[55,5],[751,4],[803,5],[943,5]]},"376":{"position":[[1358,5],[1600,6],[2690,5],[3112,5],[3309,5],[3568,5],[3796,5]]},"378":{"position":[[1068,4],[1112,5],[1645,6]]},"384":{"position":[[568,4]]},"386":{"position":[[360,4],[549,5],[576,4],[637,5],[685,5],[771,5]]},"390":{"position":[[373,4],[488,5],[566,4],[635,4],[704,5],[721,4]]},"392":{"position":[[289,5]]},"394":{"position":[[425,5],[1015,5],[1683,4],[2417,4],[2508,4]]},"396":{"position":[[147,4],[229,5]]},"400":{"position":[[569,5],[1372,5]]},"402":{"position":[[407,4],[1430,5],[1480,4]]},"404":{"position":[[148,5]]},"406":{"position":[[669,5]]},"410":{"position":[[419,5],[487,4]]},"422":{"position":[[13,5],[759,8],[821,6],[925,5],[969,4],[1821,4]]},"424":{"position":[[354,5]]},"426":{"position":[[103,5],[166,6],[502,5],[574,5]]},"440":{"position":[[163,5]]},"442":{"position":[[242,5],[411,6],[1086,4],[1124,4]]},"446":{"position":[[876,5],[1257,5],[2603,5],[2876,4],[3295,4],[3359,4],[3422,5],[4028,4],[4069,5]]},"448":{"position":[[761,4]]},"450":{"position":[[91,4],[1467,5],[1901,5],[1976,4],[2376,5],[3042,5],[4135,4]]},"454":{"position":[[1005,5],[1171,5],[1238,5],[2480,5]]},"458":{"position":[[112,5],[167,5],[227,6],[263,5],[324,5],[737,4],[1048,5],[1144,4],[1428,5],[1560,5]]},"462":{"position":[[2020,4]]},"464":{"position":[[1710,4]]},"466":{"position":[[479,5],[541,6],[677,4],[1066,5],[1192,5]]},"468":{"position":[[1569,5]]},"508":{"position":[[66,4],[426,5],[542,4],[2597,4],[3387,4],[4243,4],[4293,5],[4334,4],[4418,4]]},"512":{"position":[[471,5],[840,5],[1150,5],[1322,5],[1365,4]]},"524":{"position":[[1046,4]]},"534":{"position":[[1150,4]]},"544":{"position":[[570,4],[1001,4],[1169,4],[1201,4],[1459,9],[1507,9]]},"554":{"position":[[58,4],[158,4],[234,4],[513,4],[756,6],[836,5],[900,4],[1140,5]]},"572":{"position":[[1308,5]]},"574":{"position":[[503,5],[1116,5],[1941,5]]},"582":{"position":[[757,5]]},"588":{"position":[[1636,5]]},"596":{"position":[[49,4],[165,5],[193,5],[603,4],[611,5],[642,5],[689,4],[963,4],[976,7],[1132,8],[1192,4],[1248,4],[1825,5]]},"604":{"position":[[101,5],[170,6],[317,4],[539,5],[787,4],[1096,6],[1188,5],[1217,5]]},"612":{"position":[[1267,4]]},"624":{"position":[[424,5],[433,5]]},"626":{"position":[[2650,4],[2729,5],[4466,6],[4530,4]]},"634":{"position":[[501,4],[1543,5]]},"636":{"position":[[881,5]]},"648":{"position":[[363,4]]},"654":{"position":[[1830,6]]},"662":{"position":[[321,4]]},"668":{"position":[[491,5],[553,4],[2802,4]]},"670":{"position":[[414,5],[2151,5]]},"674":{"position":[[216,5]]},"684":{"position":[[296,5],[760,5],[1749,5]]},"686":{"position":[[142,4],[204,4],[369,4],[392,4]]},"692":{"position":[[1370,5]]},"694":{"position":[[1052,4]]},"696":{"position":[[3244,4],[3306,4],[3353,4],[3402,4],[3532,4],[3993,5],[4062,5],[4187,5],[4942,5]]},"704":{"position":[[1599,4]]},"716":{"position":[[4252,5]]},"718":{"position":[[413,5],[809,6],[901,4]]},"720":{"position":[[7475,5],[7646,4]]},"722":{"position":[[636,4],[774,4],[821,4],[1040,5],[1446,4],[2740,5]]},"724":{"position":[[1099,5]]},"734":{"position":[[371,5],[380,5]]},"738":{"position":[[2241,4],[2347,4],[3276,4],[6480,4],[6582,4],[7285,4],[7335,5]]},"742":{"position":[[695,5]]},"746":{"position":[[771,4],[1148,4],[1323,4]]},"760":{"position":[[334,4],[1591,4],[2894,5]]},"764":{"position":[[516,4],[1287,4],[1913,5]]},"766":{"position":[[658,5],[688,5],[1730,5],[1774,4],[1808,4],[1927,4]]},"768":{"position":[[191,6],[380,5]]},"770":{"position":[[412,5],[490,5],[509,4],[547,4],[570,5],[762,4],[781,4],[890,4],[1017,4],[1146,4],[1165,5]]},"772":{"position":[[977,4],[1293,4],[1361,5],[1383,4],[1438,5],[1466,5],[1492,4],[1573,5],[1627,5],[1681,5]]},"774":{"position":[[1228,4]]},"778":{"position":[[153,5],[448,5],[467,5],[1412,4],[1656,5],[1704,4],[1799,4]]},"798":{"position":[[487,4],[495,5]]},"802":{"position":[[1358,4],[1556,4],[2723,5],[2783,4]]},"806":{"position":[[480,5],[602,5],[967,4],[3875,4]]},"810":{"position":[[1208,4]]},"852":{"position":[[1433,5],[1533,4],[1674,5],[1818,5]]},"860":{"position":[[2310,5]]},"880":{"position":[[266,4]]},"927":{"position":[[1552,4]]},"935":{"position":[[35,4],[216,4],[252,4]]},"947":{"position":[[75,5]]},"951":{"position":[[54,4]]},"953":{"position":[[345,5]]},"969":{"position":[[251,5]]},"989":{"position":[[198,4],[745,4]]},"1019":{"position":[[1005,4]]},"1023":{"position":[[210,4],[257,5],[348,4],[605,4],[864,4]]},"1025":{"position":[[144,4],[335,4]]},"1027":{"position":[[629,4],[929,4],[1127,4],[3594,4],[4128,4],[5719,4],[6235,4],[6911,4],[7292,4],[7805,5],[8330,4],[8800,4]]},"1029":{"position":[[355,4]]}}}],["line\"^b",{"_index":1547,"t":{"181":{"position":[[121,8]]}}}],["line\"^f",{"_index":1550,"t":{"181":{"position":[[179,8]]}}}],["line\"^k",{"_index":1556,"t":{"181":{"position":[[275,8]]}}}],["line\"^l",{"_index":1558,"t":{"181":{"position":[[289,8]]}}}],["line\"don",{"_index":3724,"t":{"596":{"position":[[214,10],[1002,10]]}}}],["line.funct",{"_index":3817,"t":{"626":{"position":[[1372,13]]}}}],["line=\"$1",{"_index":3503,"t":{"544":{"position":[[1430,9]]}}}],["liner",{"_index":1755,"t":{"225":{"position":[[508,5]]},"604":{"position":[[599,5]]}}}],["lingo",{"_index":123,"t":{"10":{"position":[[444,5]]}}}],["link",{"_index":150,"t":{"15":{"position":[[35,4],[114,4]]},"69":{"position":[[1994,4],[2072,4]]},"95":{"position":[[1375,6]]},"205":{"position":[[696,6]]},"217":{"position":[[1458,6]]},"225":{"position":[[214,5]]},"262":{"position":[[2966,5]]},"428":{"position":[[1257,4]]},"438":{"position":[[738,4],[859,5],[883,4]]},"538":{"position":[[439,4],[936,4],[1106,4],[1813,4]]},"544":{"position":[[1842,4]]},"558":{"position":[[965,4],[1318,4]]},"562":{"position":[[462,5]]},"588":{"position":[[648,4],[677,5],[723,5],[861,5],[912,5],[1892,4],[1921,5],[2056,5]]},"612":{"position":[[1518,4]]},"626":{"position":[[5156,5],[5734,4]]},"666":{"position":[[158,4],[1352,4],[1402,4],[1506,7],[1589,7],[1950,4]]},"714":{"position":[[2109,5]]},"742":{"position":[[553,5],[602,4],[626,4]]},"744":{"position":[[2556,5]]},"746":{"position":[[43,5],[157,5],[370,5],[402,5]]},"808":{"position":[[827,4]]},"914":{"position":[[79,4]]},"931":{"position":[[190,4]]},"967":{"position":[[27,5]]},"973":{"position":[[29,4]]},"1001":{"position":[[6,5]]}}}],["link\"don",{"_index":3674,"t":{"588":{"position":[[683,10],[1927,11]]}}}],["link=$(echo",{"_index":4023,"t":{"666":{"position":[[1414,11]]}}}],["linkedin",{"_index":1178,"t":{"87":{"position":[[278,8]]}}}],["linksdo",{"_index":3673,"t":{"588":{"position":[[656,8],[1900,8]]}}}],["linu",{"_index":4084,"t":{"678":{"position":[[670,5],[757,5]]}}}],["linux",{"_index":200,"t":{"21":{"position":[[247,5],[3532,5],[3546,5]]},"23":{"position":[[400,5],[1362,5],[1389,5]]},"31":{"position":[[2667,5],[2770,5]]},"33":{"position":[[786,6]]},"45":{"position":[[998,5]]},"47":{"position":[[2609,5],[3796,5],[5915,5]]},"57":{"position":[[349,5]]},"61":{"position":[[538,5]]},"65":{"position":[[19,5]]},"67":{"position":[[66,5],[254,5],[340,5],[464,6],[524,5],[607,5],[878,5],[986,6],[1219,6]]},"69":{"position":[[29,5],[134,5],[223,5],[259,5],[760,5],[891,5],[998,5],[1137,5],[2618,5],[2995,6],[3828,7],[5810,6],[5843,5],[5937,5],[6211,6]]},"73":{"position":[[30,5]]},"81":{"position":[[1236,5],[2488,5]]},"83":{"position":[[247,5],[346,5],[1068,6]]},"93":{"position":[[493,5],[682,5],[793,5],[889,5],[952,6],[1272,5],[1374,5],[1615,6],[1684,5],[1756,6],[1766,5]]},"107":{"position":[[460,6]]},"125":{"position":[[343,5]]},"135":{"position":[[3,6]]},"139":{"position":[[104,5]]},"189":{"position":[[1376,5],[1961,6]]},"221":{"position":[[375,5]]},"249":{"position":[[1569,5]]},"251":{"position":[[1851,5]]},"262":{"position":[[3332,5]]},"282":{"position":[[2491,6]]},"290":{"position":[[919,5],[2361,5],[2536,5],[2662,5]]},"292":{"position":[[161,5],[228,6],[278,5]]},"300":{"position":[[552,5]]},"332":{"position":[[57,5]]},"378":{"position":[[4268,6]]},"442":{"position":[[1858,5],[1930,5],[1969,5]]},"448":{"position":[[1815,5]]},"482":{"position":[[928,5],[1557,5]]},"622":{"position":[[790,5],[1216,5]]},"624":{"position":[[192,5]]},"636":{"position":[[1637,5],[1711,5]]},"678":{"position":[[701,6],[820,5]]},"708":{"position":[[472,5]]},"720":{"position":[[3724,5]]},"738":{"position":[[853,5]]},"744":{"position":[[22,5]]},"756":{"position":[[382,5]]},"788":{"position":[[363,5]]},"808":{"position":[[1710,5],[1974,5]]},"820":{"position":[[172,5]]},"838":{"position":[[839,5],[1355,5],[2068,5],[2372,5],[2525,5]]},"862":{"position":[[658,6]]},"864":{"position":[[31,5],[456,6],[1010,5]]},"896":{"position":[[939,5]]},"898":{"position":[[2128,5],[2298,5],[2353,5]]},"902":{"position":[[427,5],[1102,5],[1422,5]]},"904":{"position":[[250,5],[1088,5]]},"908":{"position":[[1845,5],[2097,5],[2152,5]]},"937":{"position":[[26,5]]},"943":{"position":[[202,5]]},"967":{"position":[[189,5],[245,5]]},"969":{"position":[[61,5],[189,6],[237,5]]},"975":{"position":[[146,5]]}}}],["linux:~/defintion.txt",{"_index":5223,"t":{"908":{"position":[[3011,21]]}}}],["linux:~lookup.pi",{"_index":5203,"t":{"908":{"position":[[1572,16]]}}}],["linuxlast",{"_index":5207,"t":{"908":{"position":[[1994,9]]}}}],["linuxtyp",{"_index":2812,"t":{"432":{"position":[[1090,9]]}}}],["list",{"_index":422,"t":{"25":{"position":[[388,4]]},"29":{"position":[[189,4]]},"31":{"position":[[35,4],[100,4],[138,4]]},"43":{"position":[[1155,5]]},"69":{"position":[[1762,4]]},"77":{"position":[[1934,4],[2281,4],[2485,4],[3836,5],[4167,4],[4220,4]]},"81":{"position":[[419,5],[2165,5],[2324,5]]},"83":{"position":[[539,5]]},"91":{"position":[[1815,4]]},"131":{"position":[[289,4],[586,5],[667,4]]},"133":{"position":[[195,4]]},"137":{"position":[[1042,4]]},"145":{"position":[[196,6],[341,4]]},"181":{"position":[[41,4]]},"189":{"position":[[626,4],[2142,4]]},"207":{"position":[[560,4]]},"213":{"position":[[689,4]]},"219":{"position":[[1561,4]]},"235":{"position":[[293,4]]},"243":{"position":[[270,4]]},"249":{"position":[[1219,4]]},"253":{"position":[[210,4]]},"255":{"position":[[957,4]]},"259":{"position":[[237,4]]},"338":{"position":[[729,4],[779,4]]},"394":{"position":[[822,4]]},"406":{"position":[[496,4]]},"418":{"position":[[306,4]]},"438":{"position":[[1342,4]]},"446":{"position":[[972,4],[1012,4]]},"448":{"position":[[179,4]]},"456":{"position":[[536,4]]},"462":{"position":[[1221,4],[1324,4],[1614,4],[1644,4],[1727,4],[1883,4],[2402,4]]},"468":{"position":[[598,4],[890,4]]},"484":{"position":[[1704,4]]},"518":{"position":[[224,4]]},"526":{"position":[[486,4]]},"528":{"position":[[130,4],[617,4]]},"560":{"position":[[86,4]]},"580":{"position":[[68,5],[281,5],[331,4]]},"584":{"position":[[202,8]]},"594":{"position":[[498,4],[824,5],[1116,4],[1170,4],[1242,5],[1272,5],[1303,5],[1334,5],[1365,5],[1395,5],[1426,5]]},"602":{"position":[[1089,4]]},"608":{"position":[[1570,4],[1644,4],[2277,4]]},"612":{"position":[[1179,6]]},"616":{"position":[[831,5]]},"626":{"position":[[2189,4]]},"648":{"position":[[733,6],[981,6],[1034,5],[1122,4],[1465,5],[1558,4],[2137,4]]},"668":{"position":[[1831,4],[2985,4]]},"682":{"position":[[1061,4]]},"684":{"position":[[1780,5]]},"688":{"position":[[247,6]]},"700":{"position":[[3997,5]]},"714":{"position":[[211,6]]},"718":{"position":[[373,5],[708,5]]},"720":{"position":[[6197,4]]},"722":{"position":[[2685,5]]},"738":{"position":[[3605,5]]},"772":{"position":[[223,6]]},"778":{"position":[[842,4]]},"800":{"position":[[105,4]]},"802":{"position":[[1635,4]]},"804":{"position":[[23,4]]},"808":{"position":[[1558,4],[1745,4],[1801,4]]},"828":{"position":[[1108,4]]},"830":{"position":[[720,4],[1168,4]]},"832":{"position":[[853,5],[1378,5]]},"840":{"position":[[376,4]]},"842":{"position":[[308,4],[736,4],[1226,5]]},"858":{"position":[[3177,4],[3299,7]]},"860":{"position":[[299,4],[415,4]]},"868":{"position":[[243,5]]},"898":{"position":[[2252,4]]},"918":{"position":[[38,4],[505,5]]},"985":{"position":[[778,6]]},"1033":{"position":[[2791,4]]},"1035":{"position":[[354,4]]}}}],["list\"^",{"_index":1549,"t":{"181":{"position":[[163,8]]}}}],["list*.l",{"_index":1452,"t":{"133":{"position":[[286,9]]}}}],["lit",{"_index":2004,"t":{"262":{"position":[[3062,4]]}}}],["liter",{"_index":2297,"t":{"340":{"position":[[357,7]]},"344":{"position":[[4278,7],[4608,7]]},"364":{"position":[[1912,7]]},"390":{"position":[[1562,7]]},"396":{"position":[[786,7]]},"484":{"position":[[1116,7]]},"496":{"position":[[80,7]]},"500":{"position":[[596,7]]},"504":{"position":[[251,7]]},"510":{"position":[[744,7]]},"608":{"position":[[1070,10]]},"718":{"position":[[1057,10]]}}}],["littl",{"_index":472,"t":{"27":{"position":[[721,6]]},"47":{"position":[[6003,6]]},"67":{"position":[[121,6]]},"69":{"position":[[5081,6],[6403,6]]},"71":{"position":[[243,6]]},"75":{"position":[[21,6]]},"91":{"position":[[350,6]]},"105":{"position":[[601,6]]},"131":{"position":[[600,6]]},"133":{"position":[[1159,6]]},"155":{"position":[[6,6]]},"165":{"position":[[936,6]]},"193":{"position":[[704,6]]},"213":{"position":[[1116,6]]},"251":{"position":[[1674,6]]},"253":{"position":[[1957,6],[3716,6]]},"257":{"position":[[2711,6],[3951,6]]},"262":{"position":[[2873,6]]},"360":{"position":[[318,6]]},"370":{"position":[[1163,6]]},"378":{"position":[[4227,6]]},"390":{"position":[[1101,6]]},"396":{"position":[[1671,6]]},"404":{"position":[[81,6]]},"422":{"position":[[2007,6]]},"430":{"position":[[585,6]]},"462":{"position":[[200,6]]},"508":{"position":[[1276,6]]},"526":{"position":[[1322,6]]},"528":{"position":[[203,6]]},"544":{"position":[[1607,6]]},"586":{"position":[[2467,6]]},"600":{"position":[[1351,6],[1840,6]]},"612":{"position":[[380,6]]},"666":{"position":[[1038,6]]},"682":{"position":[[3268,6],[3448,6]]},"688":{"position":[[2598,6],[3334,6]]},"694":{"position":[[1498,6]]},"696":{"position":[[5124,6]]},"698":{"position":[[622,6]]},"714":{"position":[[1793,6]]},"726":{"position":[[305,6]]},"738":{"position":[[2766,6],[4790,6],[6285,6]]},"748":{"position":[[833,6]]},"756":{"position":[[240,6]]},"766":{"position":[[1426,6]]},"774":{"position":[[554,6]]},"806":{"position":[[83,6]]},"808":{"position":[[151,6]]},"820":{"position":[[351,6],[532,6]]},"834":{"position":[[5021,6]]},"870":{"position":[[26,6]]},"876":{"position":[[102,6]]},"1033":{"position":[[4637,6]]}}}],["live",{"_index":769,"t":{"47":{"position":[[4037,4],[8002,6]]},"135":{"position":[[567,5]]},"175":{"position":[[236,5]]},"253":{"position":[[1931,5]]},"292":{"position":[[775,5]]},"394":{"position":[[1575,6]]},"766":{"position":[[1527,5]]},"818":{"position":[[1146,5]]},"838":{"position":[[1043,4]]},"985":{"position":[[161,4],[361,4]]},"987":{"position":[[418,4]]},"989":{"position":[[415,4]]}}}],["ll='l",{"_index":3799,"t":{"624":{"position":[[725,6]]},"734":{"position":[[2685,6]]}}}],["lleh",{"_index":3018,"t":{"452":{"position":[[470,5],[515,5]]}}}],["ln",{"_index":2836,"t":{"438":{"position":[[793,2],[848,2]]},"512":{"position":[[1517,2]]},"544":{"position":[[1887,2],[1899,2]]},"558":{"position":[[883,2]]},"574":{"position":[[3399,2]]},"612":{"position":[[1563,2],[1575,2]]},"742":{"position":[[359,2],[448,2]]},"744":{"position":[[2246,2]]},"808":{"position":[[1045,2]]},"852":{"position":[[1128,2]]}}}],["loa",{"_index":1386,"t":{"107":{"position":[[1131,4]]}}}],["load",{"_index":941,"t":{"51":{"position":[[509,4]]},"95":{"position":[[1321,4]]},"225":{"position":[[413,4],[1126,7]]},"231":{"position":[[1146,5],[1530,4]]},"434":{"position":[[114,4],[1198,4]]},"596":{"position":[[83,4]]},"622":{"position":[[833,6],[913,6]]},"626":{"position":[[408,7],[4272,4],[4622,6],[7356,6]]},"634":{"position":[[1404,7],[1963,4],[2483,4],[2893,4]]},"636":{"position":[[2527,4]]},"638":{"position":[[145,6],[282,6],[526,4],[579,4],[693,4],[768,4]]},"640":{"position":[[41,5],[1554,4],[1727,4]]},"642":{"position":[[58,5]]},"644":{"position":[[15,4],[139,5],[307,7]]},"646":{"position":[[277,5],[437,6],[570,6],[847,6],[874,4]]},"650":{"position":[[236,4]]},"736":{"position":[[630,4]]},"738":{"position":[[744,6],[1310,7],[1783,7]]},"742":{"position":[[1244,7]]},"744":{"position":[[1036,6],[1484,7],[2499,4]]},"764":{"position":[[316,4]]},"800":{"position":[[145,6]]},"892":{"position":[[1186,4]]},"908":{"position":[[1061,6]]},"979":{"position":[[99,8],[165,7]]},"985":{"position":[[350,10]]},"987":{"position":[[407,10]]},"989":{"position":[[404,10]]},"1013":{"position":[[99,8],[165,7]]}}}],["loader!./images/asymmetr",{"_index":5315,"t":{"981":{"position":[[108,26]]},"1015":{"position":[[108,26]]}}}],["local",{"_index":946,"t":{"51":{"position":[[850,8]]},"105":{"position":[[558,5]]},"300":{"position":[[891,5]]},"302":{"position":[[48,5]]},"304":{"position":[[63,5],[109,5],[311,6],[351,7],[689,5]]},"316":{"position":[[18,5]]},"376":{"position":[[672,5]]},"438":{"position":[[150,5],[674,5],[1670,5]]},"484":{"position":[[400,5]]},"488":{"position":[[1512,5]]},"522":{"position":[[630,7],[697,7],[859,5],[1131,7],[1291,7],[1859,7]]},"524":{"position":[[202,5],[218,5],[234,5],[636,5],[1113,5]]},"526":{"position":[[157,5],[960,5],[971,5]]},"528":{"position":[[390,5],[480,5]]},"530":{"position":[[131,5]]},"532":{"position":[[121,5]]},"534":{"position":[[341,5]]},"544":{"position":[[1424,5],[1440,5],[1486,5]]},"556":{"position":[[342,5],[422,5],[834,5]]},"558":{"position":[[660,5],[1001,5],[1356,5]]},"648":{"position":[[308,5]]},"654":{"position":[[55,5],[119,5],[1339,5],[1631,5],[2834,5]]},"656":{"position":[[91,7],[1014,5]]},"658":{"position":[[2162,5]]},"660":{"position":[[583,5],[1482,5]]},"668":{"position":[[1674,5],[1727,5],[1798,5],[1920,5]]},"672":{"position":[[581,5]]},"720":{"position":[[734,5],[776,5],[816,5],[858,5],[901,5],[942,5],[986,5],[1027,5],[1091,5],[1133,5],[1173,5],[1215,5],[1258,5],[1299,5],[1343,5],[1384,5],[2306,5],[2340,5],[2372,5],[2417,5],[2462,5],[5926,5],[6040,5],[6093,5],[6164,5],[6286,5]]},"746":{"position":[[296,8],[554,5]]},"748":{"position":[[332,5]]},"808":{"position":[[1192,5]]},"816":{"position":[[846,7]]},"834":{"position":[[4355,5]]},"838":{"position":[[46,5],[95,5],[460,5],[783,5]]},"862":{"position":[[1178,5],[1197,5],[3311,5],[3330,5]]},"896":{"position":[[599,5]]},"898":{"position":[[2501,5]]},"908":{"position":[[382,6],[667,5],[939,5],[1403,5],[1647,5],[2730,5]]}}}],["localhost",{"_index":2663,"t":{"400":{"position":[[235,12]]}}}],["localloc",{"_index":2188,"t":{"304":{"position":[[892,10]]}}}],["locat",{"_index":1035,"t":{"69":{"position":[[1416,9]]},"71":{"position":[[1768,8]]},"77":{"position":[[1765,7],[2245,8]]},"79":{"position":[[540,8]]},"81":{"position":[[530,8]]},"107":{"position":[[990,9]]},"125":{"position":[[131,9]]},"135":{"position":[[223,8]]},"137":{"position":[[1222,8]]},"141":{"position":[[55,8],[874,9]]},"143":{"position":[[139,8]]},"145":{"position":[[459,8],[947,8]]},"165":{"position":[[560,8]]},"249":{"position":[[888,8]]},"282":{"position":[[1524,9]]},"284":{"position":[[658,10]]},"300":{"position":[[538,9],[841,9],[1028,9]]},"302":{"position":[[1234,8]]},"328":{"position":[[153,8]]},"330":{"position":[[251,10]]},"432":{"position":[[315,8],[418,8]]},"438":{"position":[[540,8]]},"446":{"position":[[2414,8]]},"468":{"position":[[254,9],[511,8]]},"472":{"position":[[74,9]]},"482":{"position":[[349,10]]},"508":{"position":[[1370,9]]},"512":{"position":[[1711,9]]},"538":{"position":[[1610,8]]},"556":{"position":[[552,8]]},"574":{"position":[[3593,9]]},"654":{"position":[[163,8]]},"704":{"position":[[379,8]]},"736":{"position":[[1266,8]]},"738":{"position":[[5288,9]]},"808":{"position":[[504,6],[593,9]]},"850":{"position":[[1347,8]]},"874":{"position":[[749,8]]},"892":{"position":[[643,8]]},"902":{"position":[[67,8]]},"908":{"position":[[1728,8]]},"923":{"position":[[116,9]]},"925":{"position":[[49,9]]},"985":{"position":[[853,8]]},"1033":{"position":[[3983,9]]}}}],["lock",{"_index":193,"t":{"21":{"position":[[93,6]]},"648":{"position":[[2369,4]]}}}],["log",{"_index":992,"t":{"61":{"position":[[432,6]]},"63":{"position":[[313,6]]},"69":{"position":[[4948,3],[5288,3],[5367,6]]},"195":{"position":[[143,3],[450,3],[595,4],[692,4],[761,3],[862,5],[946,4],[1206,5],[1248,4],[1274,4],[1376,5],[1459,4],[1523,4],[1555,4],[1820,7],[1887,5],[1953,5]]},"197":{"position":[[390,6],[494,5]]},"199":{"position":[[102,6]]},"255":{"position":[[2105,3]]},"257":{"position":[[4781,3]]},"394":{"position":[[852,6]]},"398":{"position":[[252,3],[305,3],[322,4]]},"402":{"position":[[1314,6],[1594,6]]},"404":{"position":[[248,3],[573,6],[775,6]]},"446":{"position":[[1602,3]]},"450":{"position":[[3077,4],[4742,3]]},"454":{"position":[[1414,3],[2399,3],[2419,3]]},"584":{"position":[[1705,4]]},"622":{"position":[[202,3]]},"636":{"position":[[605,3],[856,6],[1568,7],[2404,4]]},"648":{"position":[[174,4],[420,4],[2407,7]]},"654":{"position":[[4251,3]]},"658":{"position":[[1999,3]]},"660":{"position":[[697,3],[850,3]]},"686":{"position":[[270,3]]},"694":{"position":[[279,3],[296,5],[321,3],[884,3],[1012,3],[1081,3],[1768,3],[2316,3]]},"696":{"position":[[1426,3],[1689,5],[3060,3],[3826,3],[4515,3],[5249,3],[5296,3]]},"702":{"position":[[364,3],[382,3],[917,3],[1505,4]]},"704":{"position":[[1514,3],[1527,3],[1547,3],[1579,3]]},"722":{"position":[[2087,4],[2229,5]]},"927":{"position":[[1467,3],[1480,3],[1500,3],[1532,3]]}}}],["log\"=======alia",{"_index":4197,"t":{"696":{"position":[[3033,16]]}}}],["log\"alia",{"_index":4200,"t":{"696":{"position":[[4493,9]]}}}],["log*\"./logs./logs/web",{"_index":1603,"t":{"195":{"position":[[175,23]]}}}],["log.info(`serv",{"_index":2673,"t":{"400":{"position":[[443,16]]}}}],["logcommit",{"_index":4155,"t":{"694":{"position":[[358,9]]}}}],["logic",{"_index":2206,"t":{"306":{"position":[[673,6],[794,6]]},"310":{"position":[[467,5]]},"440":{"position":[[423,5]]},"514":{"position":[[704,5]]},"518":{"position":[[726,7]]},"536":{"position":[[448,6]]},"572":{"position":[[1327,5]]},"576":{"position":[[119,6],[178,7],[271,5]]},"586":{"position":[[1069,7]]},"598":{"position":[[491,5]]},"626":{"position":[[6806,5]]},"634":{"position":[[2137,6]]},"850":{"position":[[2179,6]]},"876":{"position":[[312,5]]}}}],["login",{"_index":3858,"t":{"630":{"position":[[284,5]]},"636":{"position":[[9,5],[118,5],[133,5],[367,5],[475,5],[508,5],[744,5],[945,5],[1046,5],[1100,5],[1254,5],[1303,5],[1513,5],[2087,5],[2100,5],[2190,5],[2323,5],[2498,5]]},"638":{"position":[[453,5]]},"640":{"position":[[7,5],[777,5],[797,5]]},"642":{"position":[[24,5]]},"646":{"position":[[101,5],[134,5],[481,5],[768,5]]},"648":{"position":[[844,5],[1817,5]]},"650":{"position":[[137,5],[151,5],[507,5],[521,5],[710,5]]},"908":{"position":[[2004,6]]}}}],["logs\"./logs/apm",{"_index":1618,"t":{"195":{"position":[[1232,15]]},"199":{"position":[[148,18]]}}}],["logs*\"./logs/apm",{"_index":1619,"t":{"195":{"position":[[1442,16]]},"197":{"position":[[119,16]]}}}],["logs./logs/apm",{"_index":1605,"t":{"195":{"position":[[225,14]]},"197":{"position":[[136,14]]}}}],["logs.txt",{"_index":1685,"t":{"213":{"position":[[505,8]]},"215":{"position":[[540,8]]},"398":{"position":[[427,8]]},"450":{"position":[[4236,8],[4420,8]]},"454":{"position":[[1471,8],[2380,8]]}}}],["logs.txt./logs/apm",{"_index":1604,"t":{"195":{"position":[[206,18]]}}}],["logs.txt2020",{"_index":2969,"t":{"450":{"position":[[3129,12]]}}}],["logs/*.log",{"_index":2714,"t":{"402":{"position":[[495,11]]}}}],["logs/apm",{"_index":2713,"t":{"402":{"position":[[484,10],[511,10],[871,10],[1506,10]]}}}],["logs/apm00.log",{"_index":1616,"t":{"195":{"position":[[1107,15]]}}}],["logs/apm00.logs./logs/apm",{"_index":1609,"t":{"195":{"position":[[318,25]]},"197":{"position":[[229,25]]},"199":{"position":[[245,25]]}}}],["logs/apm01.logs./logs/apm",{"_index":1610,"t":{"195":{"position":[[344,25]]},"197":{"position":[[255,25]]},"199":{"position":[[271,25]]}}}],["logs/apm02.logs./logs/apm",{"_index":1607,"t":{"195":{"position":[[266,25]]},"197":{"position":[[177,25]]},"199":{"position":[[193,25]]}}}],["logs/apm02.logs:34893:2020",{"_index":2715,"t":{"402":{"position":[[522,26]]}}}],["logs/apm02.logs:34906",{"_index":2741,"t":{"402":{"position":[[1517,21]]}}}],["logs/apm02.logs:34906:2020",{"_index":2734,"t":{"402":{"position":[[882,26]]}}}],["logs/apm03.logs./logs/apm",{"_index":1608,"t":{"195":{"position":[[292,25]]},"197":{"position":[[203,25]]},"199":{"position":[[219,25]]}}}],["logs/apm04.log",{"_index":1611,"t":{"195":{"position":[[370,15]]},"197":{"position":[[281,15]]},"199":{"position":[[297,15]]}}}],["logs/apm05.logs./logs/apm",{"_index":1606,"t":{"195":{"position":[[240,25]]},"197":{"position":[[151,25]]},"199":{"position":[[167,25]]}}}],["logs/web",{"_index":2744,"t":{"404":{"position":[[320,10]]}}}],["logsfound",{"_index":3643,"t":{"584":{"position":[[2066,10]]}}}],["long",{"_index":224,"t":{"21":{"position":[[716,4]]},"47":{"position":[[4736,4]]},"49":{"position":[[456,5]]},"77":{"position":[[3831,4],[4215,4]]},"87":{"position":[[1004,4]]},"93":{"position":[[1135,4]]},"95":{"position":[[169,4]]},"151":{"position":[[251,4]]},"163":{"position":[[248,4]]},"175":{"position":[[72,4]]},"237":{"position":[[1254,5],[1260,4]]},"255":{"position":[[952,4]]},"259":{"position":[[10,4]]},"302":{"position":[[813,4]]},"330":{"position":[[269,4]]},"336":{"position":[[1202,5]]},"352":{"position":[[1305,4]]},"376":{"position":[[2370,4]]},"404":{"position":[[3,4]]},"408":{"position":[[60,4]]},"426":{"position":[[138,4]]},"440":{"position":[[158,4]]},"512":{"position":[[1379,4]]},"580":{"position":[[652,4]]},"594":{"position":[[229,4],[586,4]]},"596":{"position":[[1180,4]]},"600":{"position":[[274,4],[580,5]]},"604":{"position":[[180,4]]},"626":{"position":[[6285,4]]},"634":{"position":[[469,4]]},"638":{"position":[[317,4]]},"648":{"position":[[1432,4]]},"686":{"position":[[1022,4]]},"696":{"position":[[870,4]]},"702":{"position":[[1120,6]]},"720":{"position":[[598,5],[5113,4]]},"722":{"position":[[631,4],[761,4],[769,4],[811,4],[816,4]]},"734":{"position":[[2756,4]]},"858":{"position":[[2235,4],[2316,5],[2821,4]]},"876":{"position":[[38,4]]}}}],["long.until",{"_index":3733,"t":{"600":{"position":[[750,10]]}}}],["longer",{"_index":401,"t":{"23":{"position":[[2033,6]]},"231":{"position":[[1050,6]]},"262":{"position":[[3766,6]]},"350":{"position":[[224,6]]},"760":{"position":[[2316,6]]},"876":{"position":[[1145,6]]}}}],["longhand",{"_index":1149,"t":{"77":{"position":[[4675,8],[4782,8]]}}}],["look",{"_index":301,"t":{"21":{"position":[[2745,7],[4090,4]]},"23":{"position":[[1657,5]]},"25":{"position":[[758,4]]},"29":{"position":[[177,4]]},"31":{"position":[[1862,5],[2393,7],[2982,7]]},"37":{"position":[[98,4],[270,7],[647,4],[889,4]]},"41":{"position":[[1011,7]]},"43":{"position":[[280,4]]},"45":{"position":[[17,4],[381,7]]},"47":{"position":[[79,4],[1035,7],[1514,7],[2037,7],[7685,7]]},"49":{"position":[[549,7]]},"51":{"position":[[635,4]]},"53":{"position":[[19,6],[113,4]]},"57":{"position":[[235,5],[277,5],[317,5]]},"69":{"position":[[2328,5]]},"71":{"position":[[918,5]]},"77":{"position":[[782,4],[1851,4],[2666,7],[3575,4],[3976,5],[4890,4],[5113,4]]},"87":{"position":[[762,4]]},"89":{"position":[[9,4]]},"99":{"position":[[1933,4]]},"103":{"position":[[312,4]]},"107":{"position":[[13,4],[239,4]]},"113":{"position":[[582,5],[634,7]]},"115":{"position":[[711,6]]},"121":{"position":[[106,4],[666,4],[1636,4]]},"123":{"position":[[247,4]]},"131":{"position":[[514,4]]},"141":{"position":[[711,4],[783,4]]},"143":{"position":[[182,4]]},"147":{"position":[[55,4],[334,4]]},"165":{"position":[[1032,4]]},"177":{"position":[[127,6]]},"185":{"position":[[296,7]]},"189":{"position":[[2468,4]]},"191":{"position":[[180,4]]},"195":{"position":[[819,7],[1673,4]]},"199":{"position":[[408,7]]},"205":{"position":[[55,6],[1011,6]]},"207":{"position":[[941,4]]},"209":{"position":[[542,4],[607,4]]},"215":{"position":[[152,4]]},"221":{"position":[[248,4]]},"225":{"position":[[153,5],[1802,5]]},"227":{"position":[[867,4]]},"229":{"position":[[167,4]]},"241":{"position":[[1256,4]]},"247":{"position":[[214,4]]},"249":{"position":[[610,4],[2252,7]]},"251":{"position":[[1636,4]]},"253":{"position":[[19,4],[3708,5]]},"255":{"position":[[10,4],[2181,7]]},"257":{"position":[[1271,5],[1722,4],[1836,4],[2047,4],[3076,7]]},"262":{"position":[[528,6]]},"266":{"position":[[167,5],[547,4]]},"274":{"position":[[882,4]]},"276":{"position":[[424,4]]},"278":{"position":[[1059,4]]},"282":{"position":[[556,4]]},"284":{"position":[[58,4]]},"286":{"position":[[458,5],[820,4]]},"288":{"position":[[768,4]]},"290":{"position":[[1311,5]]},"302":{"position":[[66,5],[842,4],[1549,4]]},"332":{"position":[[516,4]]},"338":{"position":[[790,4]]},"340":{"position":[[164,4],[490,4]]},"342":{"position":[[953,6],[1819,4]]},"344":{"position":[[299,4],[737,4],[1158,5]]},"350":{"position":[[447,4]]},"360":{"position":[[349,4]]},"362":{"position":[[283,4]]},"364":{"position":[[109,4],[1649,4]]},"366":{"position":[[2342,4],[3904,4],[4910,4]]},"374":{"position":[[91,4]]},"376":{"position":[[6,4],[908,4],[1057,5],[5699,5],[6058,4]]},"378":{"position":[[2980,4]]},"384":{"position":[[526,4]]},"386":{"position":[[564,6]]},"390":{"position":[[1759,7],[1846,5]]},"394":{"position":[[207,4],[327,7]]},"404":{"position":[[232,4]]},"422":{"position":[[597,4],[2144,4]]},"424":{"position":[[301,4],[930,4],[1454,4],[1761,4]]},"426":{"position":[[230,4],[402,4]]},"428":{"position":[[659,5]]},"430":{"position":[[195,4]]},"432":{"position":[[886,5]]},"434":{"position":[[734,5]]},"436":{"position":[[272,4]]},"438":{"position":[[66,4],[213,5]]},"440":{"position":[[404,4]]},"442":{"position":[[95,5],[967,4]]},"446":{"position":[[417,5],[1495,5],[1556,7]]},"448":{"position":[[20,4]]},"450":{"position":[[3061,4]]},"456":{"position":[[105,4],[514,7]]},"462":{"position":[[704,4]]},"464":{"position":[[1158,5]]},"474":{"position":[[161,5],[490,4]]},"476":{"position":[[40,6],[159,4],[363,4],[471,4]]},"480":{"position":[[929,4]]},"486":{"position":[[537,4]]},"488":{"position":[[121,4],[494,7],[1468,6],[1539,4]]},"492":{"position":[[889,4]]},"494":{"position":[[269,4]]},"502":{"position":[[190,4]]},"506":{"position":[[457,4],[1930,4]]},"508":{"position":[[207,4],[2353,4],[4166,4]]},"510":{"position":[[1375,4]]},"512":{"position":[[203,4]]},"514":{"position":[[19,6]]},"518":{"position":[[306,4]]},"524":{"position":[[174,4]]},"526":{"position":[[643,4],[2053,4]]},"530":{"position":[[835,4]]},"534":{"position":[[176,4],[1697,4]]},"544":{"position":[[279,4],[1343,5]]},"546":{"position":[[19,6],[215,4]]},"558":{"position":[[1397,5]]},"564":{"position":[[466,4]]},"566":{"position":[[1028,4]]},"568":{"position":[[445,5],[551,5]]},"570":{"position":[[137,4],[921,4]]},"572":{"position":[[1247,4]]},"574":{"position":[[288,4],[1621,4],[1947,4]]},"576":{"position":[[19,6],[154,4]]},"580":{"position":[[745,5]]},"586":{"position":[[307,5],[921,4],[1710,7],[2060,5]]},"588":{"position":[[2447,7]]},"596":{"position":[[277,4]]},"604":{"position":[[1438,4]]},"614":{"position":[[19,6],[195,6],[300,4]]},"618":{"position":[[171,4]]},"624":{"position":[[13,4],[1643,4]]},"626":{"position":[[3440,4]]},"634":{"position":[[430,5],[774,4]]},"638":{"position":[[867,4]]},"640":{"position":[[1589,4]]},"642":{"position":[[506,7]]},"650":{"position":[[93,6],[393,4]]},"654":{"position":[[3414,4],[4215,4]]},"656":{"position":[[704,4]]},"658":{"position":[[1079,5],[1782,5],[2141,5]]},"660":{"position":[[570,4]]},"662":{"position":[[1036,4]]},"668":{"position":[[305,5],[1309,4],[1375,4],[2760,7]]},"674":{"position":[[141,6],[554,4]]},"682":{"position":[[569,4],[1173,4],[2049,4],[3756,4]]},"684":{"position":[[2810,4]]},"686":{"position":[[253,4],[1113,4]]},"688":{"position":[[1720,5],[2043,4],[3136,4]]},"690":{"position":[[831,4],[927,4]]},"692":{"position":[[1026,4],[2196,4]]},"696":{"position":[[2919,4],[3724,4],[4767,5]]},"702":{"position":[[250,6],[348,4],[1753,4],[1998,4]]},"704":{"position":[[19,6],[283,4]]},"708":{"position":[[508,4],[1830,5]]},"710":{"position":[[451,5]]},"714":{"position":[[157,6]]},"718":{"position":[[623,4]]},"720":{"position":[[7291,5],[8337,4]]},"722":{"position":[[580,4],[2164,4]]},"724":{"position":[[558,6]]},"726":{"position":[[19,6],[242,6],[412,4]]},"732":{"position":[[1801,4]]},"734":{"position":[[114,4],[195,4],[226,4],[2062,7]]},"738":{"position":[[471,7],[3580,4],[7962,4]]},"742":{"position":[[1236,4]]},"748":{"position":[[19,6],[86,6]]},"752":{"position":[[380,4]]},"760":{"position":[[2992,4]]},"764":{"position":[[952,4]]},"766":{"position":[[236,5]]},"770":{"position":[[1615,4]]},"774":{"position":[[511,4]]},"778":{"position":[[1051,4],[1134,5],[1171,4]]},"790":{"position":[[69,6],[261,4]]},"796":{"position":[[1929,7]]},"800":{"position":[[505,6],[927,6],[1090,5]]},"802":{"position":[[1198,7],[3402,4]]},"804":{"position":[[1713,4],[2010,5],[2806,4]]},"806":{"position":[[2576,4],[2719,7]]},"812":{"position":[[19,6],[95,6],[149,6]]},"818":{"position":[[1725,4]]},"820":{"position":[[423,4]]},"828":{"position":[[205,4],[877,5],[1350,5]]},"830":{"position":[[1220,5]]},"832":{"position":[[704,5]]},"834":{"position":[[148,4]]},"842":{"position":[[94,4]]},"846":{"position":[[231,6]]},"870":{"position":[[193,5]]},"878":{"position":[[1463,5]]},"880":{"position":[[148,4]]},"884":{"position":[[936,7]]},"886":{"position":[[177,4],[227,4]]},"890":{"position":[[2436,4]]},"894":{"position":[[270,4]]},"898":{"position":[[2992,4]]},"906":{"position":[[604,4],[1022,4]]},"908":{"position":[[494,4]]},"910":{"position":[[100,4]]},"912":{"position":[[439,6]]},"914":{"position":[[254,7]]},"975":{"position":[[281,7]]},"997":{"position":[[414,4]]},"1021":{"position":[[13,4]]},"1027":{"position":[[268,6],[1614,4]]},"1033":{"position":[[591,4],[3920,4],[4515,4],[4754,4]]},"1037":{"position":[[86,6]]}}}],["lookahead",{"_index":2358,"t":{"352":{"position":[[1686,9],[1899,10],[2270,11]]}}}],["lookaround",{"_index":2357,"t":{"352":{"position":[[1539,11],[2340,10],[2630,11],[2936,11]]}}}],["lookbehind",{"_index":2359,"t":{"352":{"position":[[1788,10],[1914,11]]}}}],["lookup",{"_index":4711,"t":{"802":{"position":[[1256,6],[1643,8],[2750,7]]},"804":{"position":[[439,6],[1729,6]]},"806":{"position":[[2995,6],[3742,6]]},"808":{"position":[[1241,6]]},"810":{"position":[[944,6]]},"908":{"position":[[1258,6],[1450,6],[2512,6]]}}}],["lookup.pi",{"_index":4824,"t":{"808":{"position":[[367,9],[1362,9]]},"908":{"position":[[2191,11],[2827,11]]}}}],["lookup.py[ec2",{"_index":5220,"t":{"908":{"position":[[2785,15]]}}}],["loop",{"_index":2555,"t":{"378":{"position":[[3461,6],[4153,5]]},"490":{"position":[[2109,6]]},"502":{"position":[[1145,5]]},"544":{"position":[[1589,4]]},"576":{"position":[[209,6]]},"580":{"position":[[19,4],[82,4],[224,4],[473,4],[521,4],[1299,5],[1411,5],[1524,4],[1758,4],[1940,4]]},"582":{"position":[[101,4],[147,5],[709,4]]},"584":{"position":[[8,4],[50,4],[318,5],[915,4],[1548,6]]},"586":{"position":[[49,4],[60,4],[178,4],[2418,4]]},"588":{"position":[[36,4],[151,4],[294,4],[377,4]]},"590":{"position":[[120,5],[254,4],[314,5],[418,5],[502,4],[587,4],[652,4],[726,5],[772,4]]},"592":{"position":[[32,4],[404,6],[481,4],[518,4],[584,5],[631,4],[763,4],[857,5],[892,4],[907,4]]},"594":{"position":[[10,4],[20,4],[92,4],[275,4],[365,4],[467,4],[1185,4],[1214,4],[1605,4],[1744,5]]},"596":{"position":[[20,4],[560,5],[1163,4],[1448,5],[1552,4],[1591,4]]},"598":{"position":[[37,4],[199,4],[574,4]]},"600":{"position":[[10,4],[44,5],[137,4],[165,5],[327,4],[417,4],[516,4],[692,7],[1203,5],[1227,6],[1240,5],[1339,5],[1403,4],[1421,4],[1516,4],[1794,4]]},"602":{"position":[[41,5],[97,5],[115,5],[168,5],[298,4],[389,5],[1190,5],[1245,4]]},"604":{"position":[[50,4],[522,4],[889,5],[1301,5],[1611,5]]},"606":{"position":[[403,5]]},"608":{"position":[[133,5],[629,4],[922,4]]},"612":{"position":[[260,4],[1027,4],[1251,4]]},"614":{"position":[[59,5]]},"744":{"position":[[1880,4]]},"746":{"position":[[1089,5]]},"802":{"position":[[2682,4]]},"804":{"position":[[1690,4]]},"860":{"position":[[946,5],[2210,5]]},"868":{"position":[[637,4]]},"918":{"position":[[498,6]]},"985":{"position":[[1231,4],[1301,7]]},"1019":{"position":[[1299,5]]},"1023":{"position":[[803,5]]},"1027":{"position":[[5857,4]]},"1033":{"position":[[1337,4],[3531,4]]},"1035":{"position":[[1182,4],[3201,5],[3265,5]]}}}],["loop.random_numb",{"_index":3732,"t":{"600":{"position":[[661,22]]}}}],["loop.whil",{"_index":3709,"t":{"594":{"position":[[649,10]]}}}],["lose",{"_index":1526,"t":{"175":{"position":[[1360,4]]},"262":{"position":[[3493,4]]},"596":{"position":[[1477,4]]},"906":{"position":[[133,4]]}}}],["lost",{"_index":1529,"t":{"175":{"position":[[1482,4]]}}}],["lot",{"_index":130,"t":{"13":{"position":[[39,3]]},"21":{"position":[[753,6]]},"23":{"position":[[76,3]]},"25":{"position":[[1033,3],[1225,3]]},"29":{"position":[[888,4]]},"31":{"position":[[2856,3],[2961,3],[3225,3]]},"33":{"position":[[598,3],[793,4]]},"39":{"position":[[155,3]]},"43":{"position":[[366,4]]},"47":{"position":[[1684,3],[3788,4],[8142,3]]},"49":{"position":[[335,3]]},"77":{"position":[[100,4],[800,3],[871,3]]},"81":{"position":[[1644,3]]},"99":{"position":[[281,3]]},"121":{"position":[[710,3],[1675,3]]},"129":{"position":[[1116,3]]},"131":{"position":[[464,3]]},"141":{"position":[[556,4]]},"163":{"position":[[229,3]]},"177":{"position":[[169,3]]},"207":{"position":[[2,3]]},"211":{"position":[[793,3]]},"241":{"position":[[693,3]]},"243":{"position":[[876,3]]},"251":{"position":[[1423,3],[1513,3],[1734,3]]},"253":{"position":[[891,3],[1026,3],[3643,3]]},"255":{"position":[[196,3],[274,3],[541,3]]},"257":{"position":[[404,4]]},"260":{"position":[[1355,4]]},"262":{"position":[[285,3],[504,4],[2343,3],[3762,3]]},"266":{"position":[[130,3]]},"272":{"position":[[83,3]]},"284":{"position":[[818,4],[1622,3]]},"286":{"position":[[893,3]]},"288":{"position":[[2,3]]},"290":{"position":[[837,4]]},"302":{"position":[[176,3]]},"306":{"position":[[55,3],[709,3]]},"312":{"position":[[217,3]]},"332":{"position":[[346,3],[667,4]]},"376":{"position":[[412,3],[1034,4]]},"378":{"position":[[2992,3]]},"390":{"position":[[808,4],[859,4]]},"396":{"position":[[2088,4]]},"398":{"position":[[31,4]]},"406":{"position":[[1026,3]]},"422":{"position":[[1175,3]]},"424":{"position":[[281,3]]},"446":{"position":[[1154,3],[3226,3],[4170,3]]},"462":{"position":[[1123,3]]},"466":{"position":[[894,4]]},"494":{"position":[[17,3]]},"570":{"position":[[1403,3]]},"596":{"position":[[922,3]]},"626":{"position":[[487,3],[4342,3],[4677,4],[6494,3]]},"630":{"position":[[300,3]]},"668":{"position":[[3313,4]]},"670":{"position":[[2841,3]]},"684":{"position":[[3171,3]]},"686":{"position":[[1003,3]]},"696":{"position":[[778,3]]},"698":{"position":[[190,3]]},"704":{"position":[[530,3]]},"712":{"position":[[264,3]]},"716":{"position":[[99,4],[5689,3]]},"724":{"position":[[1234,3]]},"770":{"position":[[1485,4]]},"802":{"position":[[235,3]]},"818":{"position":[[1186,4]]},"828":{"position":[[1320,3],[1911,3]]},"830":{"position":[[96,4]]},"832":{"position":[[738,3],[773,4]]},"834":{"position":[[126,3]]},"943":{"position":[[135,3]]},"955":{"position":[[72,3]]},"1027":{"position":[[301,3]]}}}],["louder",{"_index":1940,"t":{"257":{"position":[[351,6]]}}}],["love",{"_index":1300,"t":{"95":{"position":[[0,4]]},"109":{"position":[[20,4]]},"253":{"position":[[2768,4]]},"464":{"position":[[902,5]]}}}],["lovejoy",{"_index":495,"t":{"29":{"position":[[491,7],[859,7]]},"31":{"position":[[784,7],[1046,7],[1239,7]]},"251":{"position":[[331,7],[734,7]]}}}],["lovejoyarti",{"_index":415,"t":{"25":{"position":[[146,12]]},"29":{"position":[[355,12]]},"31":{"position":[[648,12]]},"251":{"position":[[195,12]]}}}],["lovejoyjohn",{"_index":489,"t":{"29":{"position":[[419,11],[766,11]]},"31":{"position":[[712,11],[953,11],[1161,11]]},"251":{"position":[[259,11],[656,11]]}}}],["lovejoytimothi",{"_index":509,"t":{"29":{"position":[[844,14]]},"31":{"position":[[1031,14]]}}}],["low",{"_index":4694,"t":{"796":{"position":[[1133,3]]},"894":{"position":[[946,3]]},"955":{"position":[[159,3]]}}}],["lower",{"_index":975,"t":{"57":{"position":[[999,5]]},"185":{"position":[[396,5]]},"253":{"position":[[3059,5]]},"257":{"position":[[581,11],[724,11],[1238,11],[3735,11]]},"448":{"position":[[1631,13],[1734,5]]},"532":{"position":[[233,11]]},"680":{"position":[[1783,5]]},"772":{"position":[[533,8],[1603,7]]},"816":{"position":[[898,5]]},"1037":{"position":[[33,5]]}}}],["lowercas",{"_index":2324,"t":{"344":{"position":[[1110,11]]},"448":{"position":[[1746,10]]},"482":{"position":[[474,10],[591,9]]},"506":{"position":[[1414,9],[1500,10]]},"532":{"position":[[107,11],[179,9],[1104,9]]},"772":{"position":[[518,9],[1588,9]]},"774":{"position":[[1233,9]]},"1027":{"position":[[9473,9],[9688,10]]}}}],["lp",{"_index":726,"t":{"47":{"position":[[2189,3]]}}}],["ls",{"_index":651,"t":{"43":{"position":[[1127,2]]},"47":{"position":[[7774,2]]},"77":{"position":[[1903,2],[1910,2],[2215,3],[2371,2],[2546,2],[2910,2],[3205,2],[3858,2],[4162,2]]},"81":{"position":[[416,2],[2018,2],[2151,2],[2321,2]]},"83":{"position":[[528,2]]},"99":{"position":[[1986,2]]},"103":{"position":[[344,2]]},"123":{"position":[[273,2]]},"131":{"position":[[228,2],[271,2],[487,2],[592,2]]},"137":{"position":[[158,2],[207,2],[944,4],[1036,2],[1112,2]]},"139":{"position":[[411,3]]},"145":{"position":[[193,2],[280,2]]},"217":{"position":[[416,2],[863,2]]},"249":{"position":[[978,2],[998,2],[1211,2]]},"255":{"position":[[871,2]]},"257":{"position":[[5942,2],[6542,2]]},"296":{"position":[[227,2]]},"302":{"position":[[373,2],[443,2],[508,2]]},"308":{"position":[[221,2],[276,2],[327,2],[370,2]]},"322":{"position":[[108,2]]},"326":{"position":[[359,2]]},"328":{"position":[[212,2]]},"406":{"position":[[793,2]]},"462":{"position":[[2627,2]]},"502":{"position":[[1206,2]]},"532":{"position":[[806,2]]},"580":{"position":[[1367,2]]},"584":{"position":[[1638,2],[1675,2],[1785,2]]},"592":{"position":[[307,2]]},"602":{"position":[[1129,2]]},"616":{"position":[[728,2],[820,2]]},"624":{"position":[[709,2],[1457,2]]},"718":{"position":[[370,2]]},"732":{"position":[[193,3]]},"734":{"position":[[2201,2],[2620,2],[2669,2]]},"744":{"position":[[2593,2],[2675,2]]},"842":{"position":[[733,2]]},"862":{"position":[[1693,2],[1729,2],[1762,2],[2500,2]]},"892":{"position":[[2190,2]]},"900":{"position":[[656,2],[695,2],[1326,2]]},"927":{"position":[[41,2]]},"1035":{"position":[[371,2]]}}}],["ls/bin/l",{"_index":2242,"t":{"324":{"position":[[124,9]]}}}],["ls169",{"_index":2791,"t":{"424":{"position":[[1520,5]]}}}],["ls5",{"_index":3768,"t":{"612":{"position":[[1773,4]]}}}],["ls7",{"_index":3493,"t":{"544":{"position":[[351,4]]}}}],["ls='l",{"_index":2244,"t":{"326":{"position":[[315,6]]},"734":{"position":[[2357,6]]}}}],["lsalia",{"_index":2243,"t":{"326":{"position":[[307,7]]}}}],["lsl",{"_index":2207,"t":{"308":{"position":[[255,4]]},"322":{"position":[[91,5]]},"326":{"position":[[338,4]]},"328":{"position":[[191,4],[297,4]]},"472":{"position":[[144,4]]}}}],["lsof",{"_index":1863,"t":{"243":{"position":[[1620,4]]}}}],["lsreadme.md",{"_index":3963,"t":{"660":{"position":[[634,11]]}}}],["lt",{"_index":3543,"t":{"560":{"position":[[550,2]]},"564":{"position":[[177,2],[524,2]]},"566":{"position":[[237,2]]},"594":{"position":[[685,2]]},"600":{"position":[[1590,2],[1757,2]]},"636":{"position":[[1791,3]]},"858":{"position":[[1982,2]]}}}],["m",{"_index":1489,"t":{"151":{"position":[[210,2]]},"225":{"position":[[583,1],[1001,1],[1284,1]]},"229":{"position":[[196,1],[546,1]]},"231":{"position":[[317,1],[408,1],[632,1],[783,1],[1445,1],[1697,1],[2011,1]]},"233":{"position":[[185,1]]},"235":{"position":[[338,1],[680,1]]},"488":{"position":[[307,1]]},"534":{"position":[[365,2]]},"538":{"position":[[561,2],[1369,2],[2654,2]]},"626":{"position":[[365,1],[5342,2]]},"654":{"position":[[2070,1],[3701,1]]},"688":{"position":[[1353,1],[1544,1],[2982,1]]},"692":{"position":[[280,1],[412,1],[765,1]]},"696":{"position":[[1492,1],[1737,1]]},"704":{"position":[[1056,1],[1379,1]]},"720":{"position":[[3130,2],[3275,2]]},"778":{"position":[[1501,1],[1831,1],[2007,1],[2112,1]]},"852":{"position":[[478,2],[1050,2]]},"892":{"position":[[416,1]]},"927":{"position":[[249,1],[286,1],[1009,1],[1332,1]]}}}],["mac",{"_index":327,"t":{"21":{"position":[[3744,3],[3837,3]]},"47":{"position":[[5882,3],[6176,3],[7471,3]]},"57":{"position":[[293,4]]},"63":{"position":[[19,4]]},"71":{"position":[[21,4]]},"77":{"position":[[1739,4],[2139,4]]},"83":{"position":[[239,3],[327,3]]},"107":{"position":[[418,4]]},"139":{"position":[[526,4]]}}}],["macbook",{"_index":4895,"t":{"838":{"position":[[1008,7]]}}}],["machin",{"_index":410,"t":{"23":{"position":[[2536,8]]},"69":{"position":[[206,8],[1033,8],[2539,8],[2569,7],[2795,7],[2907,8],[2936,7],[3743,8],[3809,7],[3958,7],[4087,7],[4171,7],[4208,7],[5239,7],[5301,7],[5692,7],[5973,8],[6079,8]]},"105":{"position":[[564,8]]},"175":{"position":[[1760,8]]},"225":{"position":[[541,7]]},"241":{"position":[[901,9]]},"262":{"position":[[3349,9]]},"282":{"position":[[984,9],[1059,7],[1662,8],[1806,8],[1898,7]]},"286":{"position":[[840,7],[851,8]]},"326":{"position":[[190,7]]},"330":{"position":[[125,9]]},"414":{"position":[[647,8]]},"438":{"position":[[1676,7]]},"470":{"position":[[353,7]]},"568":{"position":[[1111,7]]},"618":{"position":[[503,9]]},"622":{"position":[[213,7]]},"624":{"position":[[154,7],[206,8]]},"626":{"position":[[3658,7]]},"636":{"position":[[914,8],[1591,7],[1651,7],[1725,8]]},"654":{"position":[[125,7],[254,8],[471,8]]},"662":{"position":[[284,8],[811,8]]},"668":{"position":[[2834,7]]},"704":{"position":[[484,8]]},"708":{"position":[[342,7],[402,7],[486,8],[809,7]]},"720":{"position":[[8516,9]]},"736":{"position":[[305,9],[352,7]]},"748":{"position":[[545,9]]},"754":{"position":[[2492,9]]},"796":{"position":[[265,8]]},"818":{"position":[[835,8],[948,7],[1710,8]]},"834":{"position":[[574,8],[3664,7],[3830,8],[4361,8]]},"838":{"position":[[52,8],[139,7],[187,8],[372,8],[789,7],[996,8],[1082,8],[1208,8],[1990,7]]},"840":{"position":[[306,8]]},"846":{"position":[[290,8]]},"884":{"position":[[136,8],[338,8],[376,8]]},"888":{"position":[[534,7],[609,7],[940,7]]},"890":{"position":[[2152,8],[2185,7],[2340,7]]},"892":{"position":[[2323,7],[2449,7]]},"894":{"position":[[32,7],[1509,7]]},"896":{"position":[[111,7],[246,8],[605,7],[828,7],[885,7],[1088,7],[1744,7],[2758,7],[2848,8]]},"898":{"position":[[35,8],[149,8],[211,8],[370,7],[737,8],[1572,7],[1618,7],[1757,8],[1987,7],[2142,8],[2464,8],[2507,8],[2698,7],[2876,8],[2960,7],[3066,8]]},"900":{"position":[[78,8]]},"902":{"position":[[116,9],[335,8],[1022,8]]},"904":{"position":[[51,7],[820,8]]},"906":{"position":[[81,8]]},"908":{"position":[[91,7],[447,9],[673,7],[1409,8],[1479,8],[2736,8],[3570,8]]},"912":{"position":[[164,7],[486,9]]},"943":{"position":[[344,9]]},"953":{"position":[[139,7]]}}}],["machine'",{"_index":4435,"t":{"734":{"position":[[243,9]]}}}],["machine'wcicomc",{"_index":2901,"t":{"448":{"position":[[1081,16]]}}}],["macii",{"_index":2903,"t":{"448":{"position":[[1105,5]]}}}],["maco",{"_index":201,"t":{"21":{"position":[[262,5],[3738,5],[3907,5]]},"23":{"position":[[410,6],[1769,5],[1798,5]]},"67":{"position":[[151,5],[678,5]]},"71":{"position":[[212,5],[334,5],[1431,7]]},"81":{"position":[[1192,6],[2469,6]]},"135":{"position":[[22,5]]},"189":{"position":[[851,5],[893,6],[1087,5],[1215,5],[1326,6],[1518,6]]},"217":{"position":[[490,5],[732,5]]},"282":{"position":[[2459,5]]},"290":{"position":[[467,5]]},"438":{"position":[[1577,7]]},"646":{"position":[[707,5]]},"732":{"position":[[315,5]]},"878":{"position":[[1666,6]]},"975":{"position":[[226,5]]}}}],["macro",{"_index":693,"t":{"47":{"position":[[672,5]]},"780":{"position":[[334,6]]}}}],["made",{"_index":81,"t":{"8":{"position":[[358,4]]},"71":{"position":[[1927,4]]},"93":{"position":[[1293,4],[1789,4]]},"95":{"position":[[2449,4]]},"109":{"position":[[361,4]]},"113":{"position":[[358,4]]},"121":{"position":[[1600,4]]},"133":{"position":[[898,4]]},"201":{"position":[[59,4]]},"257":{"position":[[845,4]]},"292":{"position":[[1682,5]]},"302":{"position":[[739,4]]},"348":{"position":[[429,4]]},"352":{"position":[[2903,4]]},"366":{"position":[[1385,4]]},"368":{"position":[[1414,4]]},"396":{"position":[[670,4]]},"428":{"position":[[750,4]]},"438":{"position":[[441,4]]},"456":{"position":[[777,4]]},"626":{"position":[[3549,4]]},"634":{"position":[[1479,5]]},"654":{"position":[[34,4],[4116,4]]},"656":{"position":[[86,4]]},"658":{"position":[[109,4]]},"664":{"position":[[1076,4]]},"678":{"position":[[401,5],[422,5],[432,4]]},"682":{"position":[[3142,5]]},"686":{"position":[[882,4],[929,4]]},"688":{"position":[[25,4]]},"692":{"position":[[1898,4],[2151,4],[2312,4]]},"694":{"position":[[1653,4]]},"696":{"position":[[143,4],[2170,4]]},"700":{"position":[[2251,5]]},"702":{"position":[[2100,4],[2901,4]]},"716":{"position":[[926,4]]},"720":{"position":[[7556,4]]},"890":{"position":[[2060,4]]},"900":{"position":[[1548,4]]}}}],["magenta",{"_index":4275,"t":{"716":{"position":[[2059,10]]}}}],["magic",{"_index":1953,"t":{"257":{"position":[[2325,7],[3302,5]]},"965":{"position":[[7,6]]}}}],["mail",{"_index":2003,"t":{"262":{"position":[[3054,7]]},"338":{"position":[[1376,4],[1456,4]]}}}],["main",{"_index":69,"t":{"8":{"position":[[192,7]]},"69":{"position":[[518,4],[4136,4]]},"77":{"position":[[2223,4]]},"175":{"position":[[1863,4]]},"179":{"position":[[710,4]]},"400":{"position":[[507,7]]},"442":{"position":[[473,4],[801,4]]},"654":{"position":[[2072,5],[2614,4],[2633,6],[2670,6],[3703,4],[3744,4],[3796,4],[3865,4],[3893,4]]},"656":{"position":[[940,4],[1020,4],[1048,4],[1220,4]]},"658":{"position":[[1735,4],[2394,4],[2485,4]]},"660":{"position":[[165,4],[765,4],[831,4],[890,5],[1298,6]]},"668":{"position":[[1966,6],[3119,4]]},"678":{"position":[[630,6]]},"680":{"position":[[1248,4],[1406,6],[1598,4]]},"684":{"position":[[389,6],[853,6],[2422,6]]},"688":{"position":[[67,7],[1852,4],[1933,7],[2027,4],[2285,4],[2383,4],[2923,6],[3077,5]]},"690":{"position":[[185,4],[314,5],[778,4],[851,4]]},"692":{"position":[[587,4],[635,6],[1087,4],[1499,4],[2235,4]]},"694":{"position":[[2044,6]]},"696":{"position":[[922,4],[1001,4],[1081,4],[1113,5],[1542,4],[1590,6],[5036,4],[5336,5],[5695,6]]},"700":{"position":[[979,6]]},"702":{"position":[[422,5],[781,6],[2439,4],[2528,4],[2572,4]]},"720":{"position":[[5870,7],[6332,6]]},"762":{"position":[[607,4],[627,5]]},"764":{"position":[[2028,6]]},"804":{"position":[[1685,4]]},"1007":{"position":[[203,4]]}}}],["main\"alia",{"_index":4195,"t":{"696":{"position":[[2980,10],[3785,10],[4452,10]]}}}],["main'\"[alias",{"_index":4136,"t":{"688":{"position":[[3019,14]]}}}],["main'f61369d",{"_index":4176,"t":{"694":{"position":[[1328,12]]}}}],["main1",{"_index":2860,"t":{"442":{"position":[[998,5]]}}}],["main3",{"_index":2853,"t":{"442":{"position":[[584,5]]}}}],["mainbranch",{"_index":3934,"t":{"654":{"position":[[2622,10]]}}}],["mainchang",{"_index":4205,"t":{"700":{"position":[[514,11],[1104,11],[3205,11]]}}}],["mainecho",{"_index":4148,"t":{"692":{"position":[[691,8]]},"696":{"position":[[1663,8]]}}}],["mainfram",{"_index":1224,"t":{"91":{"position":[[1305,10]]},"266":{"position":[[1232,9]]},"282":{"position":[[1071,13],[1494,9]]}}}],["maingit",{"_index":2843,"t":{"442":{"position":[[147,7],[281,7]]}}}],["maininiti",{"_index":4590,"t":{"762":{"position":[[693,15]]}}}],["mainmatt",{"_index":5370,"t":{"1007":{"position":[[154,10]]}}}],["mainno",{"_index":4101,"t":{"682":{"position":[[282,6],[1221,6],[2681,6]]}}}],["mainnoth",{"_index":4120,"t":{"684":{"position":[[2962,11]]},"700":{"position":[[3479,11],[3905,11]]}}}],["mainswitch",{"_index":4133,"t":{"688":{"position":[[1910,12]]}}}],["maintain",{"_index":921,"t":{"49":{"position":[[773,11]]},"95":{"position":[[592,15]]},"179":{"position":[[366,8]]},"290":{"position":[[1620,8]]},"384":{"position":[[287,8]]},"508":{"position":[[1716,8]]},"574":{"position":[[3268,9]]},"596":{"position":[[2172,12]]},"664":{"position":[[1458,10],[1789,10],[2109,11]]},"678":{"position":[[345,8],[464,8]]},"754":{"position":[[1184,11],[1389,8]]},"876":{"position":[[815,12]]}}}],["mainuntrack",{"_index":4116,"t":{"684":{"position":[[2202,13]]}}}],["mainusernam",{"_index":3907,"t":{"654":{"position":[[2097,12]]}}}],["mainyou",{"_index":4188,"t":{"696":{"position":[[2331,7]]}}}],["major",{"_index":2058,"t":{"278":{"position":[[9,8]]},"532":{"position":[[693,8]]}}}],["make",{"_index":51,"t":{"8":{"position":[[35,4]]},"10":{"position":[[216,4]]},"23":{"position":[[183,4],[529,4],[1209,4],[1839,6],[2212,4]]},"31":{"position":[[3241,4]]},"59":{"position":[[104,4]]},"61":{"position":[[503,4]]},"63":{"position":[[366,4]]},"67":{"position":[[1158,4]]},"71":{"position":[[127,6],[1296,4],[1815,4]]},"77":{"position":[[427,4]]},"81":{"position":[[1122,4]]},"105":{"position":[[668,4]]},"109":{"position":[[52,4],[238,5]]},"115":{"position":[[364,4],[768,5]]},"133":{"position":[[651,4],[847,4]]},"135":{"position":[[917,5]]},"139":{"position":[[457,5]]},"147":{"position":[[566,4]]},"151":{"position":[[164,4]]},"157":{"position":[[121,5]]},"175":{"position":[[2920,4]]},"185":{"position":[[1158,4]]},"193":{"position":[[693,5]]},"195":{"position":[[1393,4],[1737,4]]},"197":{"position":[[375,6]]},"209":{"position":[[104,5]]},"219":{"position":[[324,4],[1057,4]]},"221":{"position":[[776,4]]},"225":{"position":[[739,4]]},"249":{"position":[[2307,4]]},"253":{"position":[[282,4]]},"255":{"position":[[266,5],[730,5]]},"257":{"position":[[343,4],[360,6],[420,4],[3531,4],[3944,4]]},"274":{"position":[[743,6]]},"278":{"position":[[707,4]]},"280":{"position":[[511,6]]},"282":{"position":[[535,5]]},"284":{"position":[[1279,5]]},"286":{"position":[[186,4]]},"290":{"position":[[260,6]]},"304":{"position":[[1678,6]]},"308":{"position":[[297,4]]},"340":{"position":[[1096,4]]},"344":{"position":[[2319,4]]},"346":{"position":[[1475,6]]},"352":{"position":[[185,4],[218,4],[1463,6]]},"354":{"position":[[195,4],[1013,4]]},"360":{"position":[[453,4]]},"364":{"position":[[176,4],[1996,4]]},"366":{"position":[[478,4],[1410,4],[1818,4],[3378,4],[3522,4]]},"370":{"position":[[352,4],[480,4]]},"372":{"position":[[355,4]]},"376":{"position":[[3158,4],[3987,4],[5482,5]]},"378":{"position":[[636,4],[3230,5],[5006,4]]},"384":{"position":[[470,4]]},"386":{"position":[[283,4],[1385,6],[1635,5],[1699,4]]},"396":{"position":[[1293,4]]},"398":{"position":[[71,5],[111,5],[207,6]]},"402":{"position":[[392,5]]},"404":{"position":[[70,5],[684,4]]},"410":{"position":[[306,4]]},"414":{"position":[[517,4]]},"416":{"position":[[368,4]]},"424":{"position":[[1533,4]]},"428":{"position":[[393,4]]},"440":{"position":[[221,4],[300,4]]},"446":{"position":[[3378,5]]},"454":{"position":[[994,6],[2654,4]]},"456":{"position":[[87,4]]},"464":{"position":[[1874,4],[2281,4]]},"468":{"position":[[687,4],[1409,4]]},"470":{"position":[[878,4]]},"484":{"position":[[1483,4]]},"506":{"position":[[1247,4],[1409,4],[2841,4]]},"508":{"position":[[1257,4],[1494,4],[1678,5]]},"510":{"position":[[3172,4]]},"512":{"position":[[450,6]]},"518":{"position":[[754,4]]},"524":{"position":[[704,4]]},"538":{"position":[[215,5],[388,5],[618,4],[731,4],[3349,4]]},"540":{"position":[[560,4]]},"544":{"position":[[377,4],[402,4],[441,4],[1597,4]]},"552":{"position":[[913,4]]},"558":{"position":[[1131,6],[1261,6]]},"568":{"position":[[712,5]]},"570":{"position":[[1590,4],[1604,4],[1636,4]]},"574":{"position":[[3242,4]]},"584":{"position":[[1019,6],[2233,6],[2635,5]]},"604":{"position":[[1275,4]]},"612":{"position":[[468,5],[882,4],[1827,4]]},"624":{"position":[[1541,5]]},"626":{"position":[[1305,4],[5399,4],[5512,4],[6736,4],[6862,4]]},"634":{"position":[[2564,4]]},"648":{"position":[[689,4]]},"654":{"position":[[4285,4]]},"658":{"position":[[381,4]]},"662":{"position":[[140,4],[718,6]]},"664":{"position":[[918,4],[1499,4]]},"670":{"position":[[804,6],[887,4],[2210,6]]},"678":{"position":[[586,4]]},"684":{"position":[[3302,4]]},"686":{"position":[[235,4],[773,4],[805,5],[1040,4]]},"688":{"position":[[143,4]]},"694":{"position":[[79,4]]},"696":{"position":[[821,6],[3595,4],[4007,4],[4243,4],[4276,5]]},"700":{"position":[[1802,4]]},"702":{"position":[[1766,4],[1844,4]]},"708":{"position":[[544,4]]},"716":{"position":[[5804,4]]},"718":{"position":[[2447,4]]},"720":{"position":[[25,4],[171,4],[5078,4],[7738,4]]},"726":{"position":[[266,5]]},"734":{"position":[[708,6]]},"736":{"position":[[1419,4]]},"738":{"position":[[610,4],[877,4],[4762,4],[6448,4]]},"740":{"position":[[410,5]]},"742":{"position":[[994,4]]},"744":{"position":[[1131,4],[2649,4]]},"748":{"position":[[987,4]]},"754":{"position":[[1689,4]]},"766":{"position":[[856,4]]},"768":{"position":[[113,5]]},"770":{"position":[[591,4]]},"772":{"position":[[513,4],[545,4],[1583,4],[1637,4]]},"778":{"position":[[594,5],[1031,4]]},"784":{"position":[[210,6]]},"786":{"position":[[171,4]]},"790":{"position":[[373,5]]},"794":{"position":[[999,4]]},"798":{"position":[[60,5],[273,5],[608,6],[866,4],[1143,4]]},"800":{"position":[[340,4],[1274,4]]},"802":{"position":[[522,4]]},"804":{"position":[[1423,4],[2134,4]]},"806":{"position":[[63,6],[220,4],[2126,4],[3190,4]]},"808":{"position":[[774,4]]},"812":{"position":[[110,5]]},"820":{"position":[[341,4],[521,5]]},"828":{"position":[[1470,5]]},"834":{"position":[[3201,4],[3234,4],[3386,7],[4999,4]]},"836":{"position":[[275,4]]},"838":{"position":[[347,4]]},"850":{"position":[[86,5]]},"852":{"position":[[535,4],[648,4],[1314,4],[1829,5]]},"862":{"position":[[2173,4],[3941,4]]},"868":{"position":[[65,4]]},"876":{"position":[[476,6]]},"894":{"position":[[544,4]]},"896":{"position":[[1993,4]]},"898":{"position":[[3045,4]]},"902":{"position":[[150,4]]},"910":{"position":[[1880,4]]},"912":{"position":[[268,4]]},"927":{"position":[[430,4]]},"935":{"position":[[16,6]]},"939":{"position":[[26,4]]},"951":{"position":[[321,4]]},"1027":{"position":[[1364,4],[1583,4],[5828,4],[9160,4]]},"1033":{"position":[[3709,4]]},"1037":{"position":[[490,4]]}}}],["makefil",{"_index":2797,"t":{"424":{"position":[[1686,8]]},"728":{"position":[[524,11]]}}}],["malici",{"_index":2149,"t":{"292":{"position":[[1449,9]]}}}],["maman",{"_index":37,"t":{"4":{"position":[[393,7]]}}}],["man",{"_index":591,"t":{"37":{"position":[[734,3]]},"39":{"position":[[105,3]]},"41":{"position":[[52,4],[78,3],[653,3],[910,3]]},"43":{"position":[[104,3],[1053,3],[1057,3]]},"45":{"position":[[93,3],[944,3]]},"47":{"position":[[87,3],[288,3],[1056,3],[1188,3],[2077,3],[2657,3],[3069,3],[3496,3],[3544,3],[3831,3],[4085,3],[4415,3],[4994,3],[5586,3],[5642,3],[5701,3],[5840,3],[6158,3],[6654,3],[7042,3],[7326,3],[7360,3],[8028,3],[8291,3],[8312,3],[8577,3],[8591,3],[8730,3]]},"49":{"position":[[312,3]]},"53":{"position":[[89,3],[153,3],[211,3],[215,3]]},"205":{"position":[[1706,3]]},"217":{"position":[[1471,3]]},"243":{"position":[[678,3]]},"304":{"position":[[2161,3]]},"344":{"position":[[5161,3]]},"364":{"position":[[2512,3]]},"368":{"position":[[1804,3]]},"390":{"position":[[63,3]]},"394":{"position":[[1044,4],[1054,3],[1072,3],[1132,3],[1335,3],[1423,3],[1969,3],[1982,3],[2052,3],[2286,3]]},"396":{"position":[[120,3],[177,3],[252,4],[371,4],[385,3],[401,3],[415,3],[778,3],[840,4],[1613,5],[1651,4],[2055,3]]},"448":{"position":[[1876,3]]},"454":{"position":[[896,3]]},"474":{"position":[[320,3],[868,3]]},"494":{"position":[[385,3]]},"506":{"position":[[397,3]]},"510":{"position":[[2495,3]]},"552":{"position":[[1263,3]]},"560":{"position":[[102,3]]},"562":{"position":[[1023,3]]},"566":{"position":[[1109,3]]},"576":{"position":[[301,3]]},"616":{"position":[[441,3]]},"626":{"position":[[2261,3]]},"636":{"position":[[1905,4]]},"650":{"position":[[321,3]]},"720":{"position":[[4834,3]]},"726":{"position":[[592,3],[645,3]]},"808":{"position":[[2024,3]]},"810":{"position":[[940,3]]},"834":{"position":[[876,3]]},"860":{"position":[[882,4]]},"862":{"position":[[3813,3]]},"902":{"position":[[1518,3]]},"908":{"position":[[3757,3]]},"918":{"position":[[68,3],[119,3],[204,3],[274,3],[288,3],[299,3],[341,3],[430,3],[521,3],[589,3],[684,3],[804,3],[916,3]]},"1019":{"position":[[1540,3]]},"1035":{"position":[[2310,3]]}}}],["man'",{"_index":677,"t":{"47":{"position":[[236,5]]}}}],["man(7",{"_index":695,"t":{"47":{"position":[[710,7]]}}}],["man1",{"_index":678,"t":{"47":{"position":[[292,5]]}}}],["man?\"for",{"_index":3628,"t":{"584":{"position":[[527,8]]}}}],["manag",{"_index":1088,"t":{"71":{"position":[[640,9]]},"91":{"position":[[2760,6],[2881,6]]},"93":{"position":[[1677,6]]},"95":{"position":[[1613,10],[1685,7],[2164,6],[2255,6]]},"107":{"position":[[545,7]]},"237":{"position":[[1186,8]]},"241":{"position":[[560,6],[745,6],[865,6]]},"253":{"position":[[2968,10]]},"276":{"position":[[126,8]]},"300":{"position":[[1080,7]]},"394":{"position":[[1121,10],[2036,10]]},"438":{"position":[[1538,7],[1688,6]]},"454":{"position":[[2011,8],[2258,8]]},"476":{"position":[[130,8],[316,6]]},"518":{"position":[[791,7]]},"618":{"position":[[262,6],[459,6]]},"626":{"position":[[4703,6]]},"636":{"position":[[654,7],[769,7]]},"646":{"position":[[252,7],[592,8]]},"656":{"position":[[469,8]]},"664":{"position":[[1999,6]]},"670":{"position":[[664,8],[914,11]]},"674":{"position":[[492,6]]},"680":{"position":[[458,8]]},"696":{"position":[[83,6],[1180,6]]},"714":{"position":[[639,7]]},"720":{"position":[[8458,6]]},"726":{"position":[[322,7]]},"732":{"position":[[2013,6]]},"736":{"position":[[971,6]]},"748":{"position":[[195,6],[423,6]]},"754":{"position":[[1832,6],[1940,8]]},"794":{"position":[[435,6]]},"816":{"position":[[122,7],[388,11]]},"818":{"position":[[200,10],[884,6],[1090,10]]},"822":{"position":[[284,7]]},"828":{"position":[[232,8],[1498,6]]},"830":{"position":[[51,7]]},"834":{"position":[[426,8]]},"836":{"position":[[254,8]]},"840":{"position":[[337,8]]},"846":{"position":[[117,6],[264,6]]},"884":{"position":[[289,6],[675,6]]},"894":{"position":[[1286,10],[1326,10]]},"896":{"position":[[30,10]]},"910":{"position":[[1924,7]]},"927":{"position":[[229,10]]},"1029":{"position":[[487,6]]}}}],["manager'",{"_index":868,"t":{"47":{"position":[[7073,9]]}}}],["manchu",{"_index":4727,"t":{"802":{"position":[[2494,6]]}}}],["mani",{"_index":1,"t":{"4":{"position":[[7,4]]},"23":{"position":[[2371,4]]},"27":{"position":[[215,4]]},"37":{"position":[[126,4]]},"39":{"position":[[67,4]]},"43":{"position":[[731,4],[1303,4]]},"49":{"position":[[60,4]]},"51":{"position":[[723,4]]},"57":{"position":[[1053,4]]},"69":{"position":[[2768,4]]},"77":{"position":[[2402,4],[4655,4]]},"91":{"position":[[16,4],[670,4],[1321,4],[1358,4],[1384,4],[1741,4],[1877,4],[1998,4],[3093,4],[3513,4]]},"93":{"position":[[89,4],[257,4],[305,4],[1509,4]]},"95":{"position":[[1443,4],[1523,4],[2584,4]]},"117":{"position":[[863,4]]},"123":{"position":[[720,4]]},"125":{"position":[[769,4]]},"179":{"position":[[652,4]]},"185":{"position":[[116,4],[1216,4]]},"241":{"position":[[567,4],[951,4],[977,4]]},"243":{"position":[[1778,4],[2022,4]]},"247":{"position":[[0,4]]},"249":{"position":[[126,4],[213,4],[350,6]]},"253":{"position":[[1067,4],[3742,4]]},"257":{"position":[[6822,4]]},"260":{"position":[[145,4]]},"262":{"position":[[2671,4]]},"266":{"position":[[33,4],[1350,4]]},"280":{"position":[[172,4]]},"282":{"position":[[1016,4],[2001,4],[2381,4]]},"286":{"position":[[674,4]]},"288":{"position":[[708,4],[1024,4]]},"292":{"position":[[555,4]]},"296":{"position":[[193,4]]},"304":{"position":[[1353,4],[1887,4]]},"318":{"position":[[197,4]]},"332":{"position":[[403,4]]},"336":{"position":[[169,4],[1340,4]]},"342":{"position":[[628,6],[733,4],[1439,4]]},"344":{"position":[[4435,4],[5101,4]]},"360":{"position":[[63,4],[232,4]]},"368":{"position":[[1550,4]]},"378":{"position":[[4120,4]]},"396":{"position":[[1755,4]]},"402":{"position":[[1274,4]]},"408":{"position":[[339,4]]},"422":{"position":[[1978,4]]},"450":{"position":[[2910,4]]},"466":{"position":[[474,4],[1347,4]]},"474":{"position":[[210,4]]},"502":{"position":[[145,4]]},"506":{"position":[[337,4]]},"510":{"position":[[1620,4]]},"522":{"position":[[189,4]]},"526":{"position":[[1119,4]]},"536":{"position":[[1075,4]]},"538":{"position":[[101,4]]},"554":{"position":[[872,4]]},"560":{"position":[[10,4]]},"594":{"position":[[1634,4]]},"600":{"position":[[1304,4]]},"602":{"position":[[347,4]]},"618":{"position":[[46,4],[498,4]]},"622":{"position":[[767,4]]},"626":{"position":[[3056,4],[6661,4]]},"634":{"position":[[1497,4]]},"638":{"position":[[232,4]]},"648":{"position":[[1372,4]]},"654":{"position":[[4294,4]]},"662":{"position":[[965,4]]},"666":{"position":[[1992,4]]},"670":{"position":[[185,4]]},"678":{"position":[[850,4],[1180,4]]},"686":{"position":[[552,4]]},"688":{"position":[[3170,4]]},"696":{"position":[[1129,4],[3101,4]]},"698":{"position":[[710,4]]},"716":{"position":[[3265,4]]},"722":{"position":[[2869,4]]},"724":{"position":[[377,4]]},"732":{"position":[[85,4]]},"734":{"position":[[3,4],[3546,4]]},"736":{"position":[[772,4],[1051,4],[1084,4]]},"738":{"position":[[2738,4]]},"748":{"position":[[540,4]]},"754":{"position":[[0,4],[290,4],[483,4],[2597,4]]},"768":{"position":[[0,4],[70,4]]},"774":{"position":[[893,4]]},"778":{"position":[[570,4]]},"796":{"position":[[936,4],[2163,4]]},"798":{"position":[[1018,4],[1666,4]]},"802":{"position":[[216,4],[589,4]]},"808":{"position":[[1969,4]]},"810":{"position":[[1559,4]]},"816":{"position":[[64,4],[83,4]]},"818":{"position":[[537,4],[1224,4]]},"820":{"position":[[167,4]]},"834":{"position":[[569,4]]},"838":{"position":[[367,4]]},"852":{"position":[[1755,4]]},"862":{"position":[[121,4],[1659,4],[4284,4]]},"866":{"position":[[59,4]]},"874":{"position":[[1009,4]]},"876":{"position":[[73,4],[430,4],[558,4],[961,4]]},"888":{"position":[[731,4]]},"890":{"position":[[1771,4]]},"892":{"position":[[1925,4]]},"902":{"position":[[1438,4]]},"908":{"position":[[233,4],[2395,4],[3271,4],[3666,4]]},"910":{"position":[[1840,4]]},"912":{"position":[[828,4]]},"939":{"position":[[93,4]]},"943":{"position":[[98,4]]},"975":{"position":[[80,4]]},"985":{"position":[[755,4]]},"1027":{"position":[[3684,4],[6346,4],[7014,4],[7470,4],[8180,4],[8511,4]]},"1037":{"position":[[197,4]]}}}],["manipul",{"_index":475,"t":{"27":{"position":[[797,10]]},"31":{"position":[[2568,12]]},"151":{"position":[[376,10]]},"266":{"position":[[831,10]]},"332":{"position":[[9,12],[263,12]]},"356":{"position":[[350,10]]},"376":{"position":[[5003,14],[5892,12],[6576,12]]},"382":{"position":[[36,12]]},"386":{"position":[[100,10],[886,10],[1158,12]]},"424":{"position":[[973,12]]},"446":{"position":[[1194,13]]},"474":{"position":[[579,10]]},"506":{"position":[[2230,10],[2502,10],[2586,12],[2750,13]]},"722":{"position":[[2666,10]]},"754":{"position":[[1709,10]]},"758":{"position":[[217,10]]},"760":{"position":[[2451,10]]},"772":{"position":[[126,10]]},"794":{"position":[[235,13]]},"898":{"position":[[2750,10]]},"1027":{"position":[[9365,10],[9450,12]]}}}],["manipulation\"slug",{"_index":2468,"t":{"376":{"position":[[170,18],[561,18],[1442,18],[1931,18],[2805,18],[5760,18]]}}}],["manipulation\"valu",{"_index":2493,"t":{"376":{"position":[[4568,18]]}}}],["manipulation\"weight",{"_index":2498,"t":{"376":{"position":[[5338,20],[5794,20]]}}}],["manipulationin",{"_index":2504,"t":{"376":{"position":[[5851,14]]}}}],["manipulationweight",{"_index":2469,"t":{"376":{"position":[[203,19],[594,19],[1475,19],[1964,19],[2838,19],[3479,19]]}}}],["manner",{"_index":2193,"t":{"304":{"position":[[1505,7]]}}}],["manpag",{"_index":631,"t":{"43":{"position":[[170,11]]},"47":{"position":[[38,8],[3009,8],[4771,7],[6982,7],[7596,8],[8257,7]]},"205":{"position":[[1675,7]]},"344":{"position":[[5153,7]]},"396":{"position":[[2074,7]]},"468":{"position":[[2825,9]]},"810":{"position":[[860,8],[894,7]]},"834":{"position":[[862,8]]},"918":{"position":[[58,9],[768,8],[777,7],[880,8],[889,7]]}}}],["manual",{"_index":427,"t":{"25":{"position":[[561,8]]},"37":{"position":[[749,10]]},"39":{"position":[[43,6]]},"41":{"position":[[111,6],[675,6],[708,7]]},"43":{"position":[[66,6],[138,6]]},"45":{"position":[[42,6],[131,6],[425,6]]},"47":{"position":[[115,6],[168,6],[1020,6],[1229,6],[1343,6],[2124,6],[2702,6],[3111,6],[4137,6],[4471,6],[5043,6],[5962,6],[6136,8],[6207,6],[6712,6],[7083,6],[7580,6],[8230,7],[8673,8]]},"49":{"position":[[644,7]]},"53":{"position":[[125,6]]},"243":{"position":[[550,6],[635,7]]},"280":{"position":[[689,8]]},"304":{"position":[[2225,6],[2326,6]]},"316":{"position":[[84,6]]},"390":{"position":[[21,6],[100,6],[1240,6]]},"506":{"position":[[2917,6]]},"510":{"position":[[2636,8],[3124,6]]},"552":{"position":[[1154,6]]},"576":{"position":[[284,7]]},"608":{"position":[[2603,6]]},"622":{"position":[[1691,6]]},"696":{"position":[[191,6],[2250,8]]},"720":{"position":[[4822,6]]},"726":{"position":[[580,7]]},"746":{"position":[[4,6]]},"860":{"position":[[896,7]]},"862":{"position":[[4212,6]]},"923":{"position":[[23,8]]},"1019":{"position":[[1520,7]]}}}],["manuscript",{"_index":4846,"t":{"816":{"position":[[1277,10]]},"1007":{"position":[[109,10]]}}}],["map",{"_index":330,"t":{"21":{"position":[[3895,8]]},"81":{"position":[[269,3]]},"492":{"position":[[209,5]]}}}],["mardi",{"_index":1133,"t":{"77":{"position":[[4012,6]]},"117":{"position":[[706,5]]}}}],["marek",{"_index":30,"t":{"4":{"position":[[321,5]]}}}],["mark",{"_index":1124,"t":{"77":{"position":[[3264,5],[3320,6],[3550,6]]},"179":{"position":[[248,4]]},"181":{"position":[[91,4]]},"350":{"position":[[888,4]]},"378":{"position":[[3732,4]]},"508":{"position":[[2656,5]]},"668":{"position":[[872,4],[2207,4],[3156,4]]},"696":{"position":[[2485,4],[4564,4]]},"702":{"position":[[1280,6]]},"720":{"position":[[6587,4]]},"732":{"position":[[626,4]]},"1007":{"position":[[17,5]]},"1035":{"position":[[51,4],[99,5],[2104,4]]}}}],["markdown",{"_index":2470,"t":{"376":{"position":[[345,8]]},"658":{"position":[[867,10],[957,9]]},"764":{"position":[[673,11],[685,8],[888,8]]},"778":{"position":[[702,8]]},"987":{"position":[[147,8]]}}}],["marker",{"_index":4833,"t":{"808":{"position":[[1679,7]]}}}],["markua",{"_index":5365,"t":{"1003":{"position":[[71,6]]}}}],["mask",{"_index":3193,"t":{"486":{"position":[[179,8]]},"508":{"position":[[3161,4]]}}}],["masked_password",{"_index":3196,"t":{"486":{"position":[[481,24]]},"508":{"position":[[2997,17]]}}}],["masochist",{"_index":2216,"t":{"312":{"position":[[12,11]]}}}],["mass",{"_index":1293,"t":{"93":{"position":[[1183,7]]}}}],["master",{"_index":1559,"t":{"183":{"position":[[10,8]]},"382":{"position":[[319,6]]},"400":{"position":[[1844,6]]},"408":{"position":[[937,6]]},"536":{"position":[[426,9]]},"634":{"position":[[2115,9]]},"850":{"position":[[2157,9]]},"902":{"position":[[1579,6]]},"906":{"position":[[575,6]]},"947":{"position":[[55,7]]},"953":{"position":[[326,6]]},"995":{"position":[[124,6]]}}}],["masteri",{"_index":4429,"t":{"728":{"position":[[77,8]]}}}],["match",{"_index":1613,"t":{"195":{"position":[[743,5],[840,6],[1003,5],[1354,5]]},"199":{"position":[[432,5],[528,5],[782,5]]},"203":{"position":[[345,5]]},"253":{"position":[[3555,6]]},"304":{"position":[[1278,5]]},"336":{"position":[[498,7]]},"338":{"position":[[868,7]]},"340":{"position":[[561,8],[818,7],[1050,7],[1175,7]]},"342":{"position":[[400,7],[1469,10]]},"344":{"position":[[12,8],[30,5],[100,5],[479,7],[1057,7],[1428,5],[1601,5],[3336,5],[3825,7],[4316,5],[4395,7],[4459,7]]},"346":{"position":[[277,5],[353,5],[602,5],[1007,5],[1086,5]]},"348":{"position":[[38,5],[196,6],[304,8],[440,8],[486,5],[532,5]]},"350":{"position":[[98,7],[181,8],[231,8],[310,8],[419,6],[574,7],[1219,7],[1495,5]]},"352":{"position":[[780,6],[1044,5],[1169,6],[1504,8],[1740,5],[1833,5],[2061,7],[2443,7],[2690,7]]},"366":{"position":[[3993,7],[4373,5]]},"368":{"position":[[674,7],[1305,5],[1397,7],[1647,8],[1696,8]]},"374":{"position":[[415,5],[602,5],[718,7]]},"376":{"position":[[994,7],[2702,5],[3124,5],[3547,5],[3680,5],[4320,7],[6202,5]]},"386":{"position":[[876,5]]},"390":{"position":[[499,5],[549,7],[617,7],[690,7],[731,7],[1002,7],[1279,5]]},"394":{"position":[[1461,7]]},"398":{"position":[[181,7]]},"400":{"position":[[779,5],[1400,6],[1999,5]]},"402":{"position":[[233,7],[355,6],[1302,7],[1369,5]]},"404":{"position":[[160,5],[820,7],[866,7]]},"410":{"position":[[454,7],[584,7]]},"502":{"position":[[1248,5]]},"568":{"position":[[1277,6]]},"572":{"position":[[1131,5]]},"586":{"position":[[1338,5],[1845,5]]},"588":{"position":[[68,5]]},"720":{"position":[[3620,7],[3751,7]]},"734":{"position":[[891,5]]},"760":{"position":[[995,5]]},"910":{"position":[[1160,7]]},"1019":{"position":[[1445,7]]},"1027":{"position":[[4716,5],[7030,7],[8185,7]]},"1035":{"position":[[877,5],[2203,5],[2445,5],[2620,5]]}}}],["materi",{"_index":1081,"t":{"71":{"position":[[97,8]]}}}],["mathemat",{"_index":3274,"t":{"502":{"position":[[1018,14]]},"510":{"position":[[58,12],[229,12],[876,12]]},"526":{"position":[[805,12],[2163,12]]},"544":{"position":[[903,11]]},"568":{"position":[[1430,11]]},"582":{"position":[[46,11]]},"600":{"position":[[1149,12]]},"608":{"position":[[559,11]]},"616":{"position":[[632,11],[1100,12]]},"714":{"position":[[3746,12]]},"854":{"position":[[177,11]]},"1019":{"position":[[1122,12]]},"1027":{"position":[[393,11],[10503,12]]},"1031":{"position":[[323,12]]}}}],["matter",{"_index":1247,"t":{"91":{"position":[[1953,6]]},"139":{"position":[[291,6]]},"257":{"position":[[3021,6]]},"288":{"position":[[162,6]]},"292":{"position":[[1257,6]]},"378":{"position":[[401,6]]},"670":{"position":[[599,6]]},"888":{"position":[[720,6]]}}}],["matur",{"_index":1248,"t":{"91":{"position":[[2046,6]]}}}],["maven",{"_index":2690,"t":{"400":{"position":[[1573,5],[1726,5]]}}}],["max",{"_index":3126,"t":{"466":{"position":[[536,4]]}}}],["maxdepth",{"_index":1727,"t":{"219":{"position":[[1749,8],[1834,8]]}}}],["maximum",{"_index":1717,"t":{"219":{"position":[[538,7]]}}}],["mayb",{"_index":1405,"t":{"115":{"position":[[87,5]]},"227":{"position":[[51,5]]},"288":{"position":[[629,5]]},"390":{"position":[[927,5]]}}}],["md",{"_index":4594,"t":{"764":{"position":[[634,2]]}}}],["mean",{"_index":139,"t":{"13":{"position":[[205,8],[283,6]]},"21":{"position":[[3602,5],[3868,5]]},"29":{"position":[[74,5]]},"47":{"position":[[3597,7]]},"51":{"position":[[314,5],[764,5]]},"77":{"position":[[1527,7],[1553,5],[2996,5],[4266,5],[4577,5]]},"81":{"position":[[1432,5],[1484,5],[1542,5]]},"87":{"position":[[700,4]]},"95":{"position":[[1902,5]]},"105":{"position":[[1017,5]]},"115":{"position":[[762,5]]},"123":{"position":[[403,5],[436,5]]},"133":{"position":[[258,4]]},"135":{"position":[[891,5]]},"137":{"position":[[853,5]]},"145":{"position":[[584,5],[626,5]]},"175":{"position":[[680,5]]},"189":{"position":[[768,7]]},"195":{"position":[[461,5]]},"201":{"position":[[137,5]]},"203":{"position":[[313,7],[592,7]]},"213":{"position":[[826,7]]},"219":{"position":[[1397,5]]},"229":{"position":[[386,5]]},"237":{"position":[[155,7]]},"241":{"position":[[943,7]]},"247":{"position":[[487,5],[1079,5]]},"249":{"position":[[1873,5]]},"251":{"position":[[821,7],[921,5],[1406,4]]},"253":{"position":[[224,5],[1123,7],[3038,5],[3842,7]]},"257":{"position":[[3621,5],[5768,5]]},"262":{"position":[[1152,5],[1288,5]]},"304":{"position":[[1917,7]]},"330":{"position":[[403,5]]},"332":{"position":[[220,5]]},"340":{"position":[[204,5],[257,7],[322,7],[1217,6]]},"342":{"position":[[835,5],[908,5],[1557,7]]},"344":{"position":[[602,5],[3670,5],[4180,5],[4828,7]]},"348":{"position":[[382,5]]},"350":{"position":[[288,5],[928,5],[1272,7]]},"366":{"position":[[1342,7],[2617,7],[4342,5],[4449,7]]},"374":{"position":[[701,5]]},"376":{"position":[[1125,5],[1582,7],[2972,5],[3002,7],[3097,7],[4363,5]]},"386":{"position":[[1501,4]]},"390":{"position":[[1364,6],[1656,5]]},"394":{"position":[[1868,5]]},"396":{"position":[[993,7],[1028,5]]},"414":{"position":[[603,7]]},"420":{"position":[[420,5]]},"422":{"position":[[535,6]]},"428":{"position":[[420,5]]},"434":{"position":[[277,5]]},"446":{"position":[[2839,5]]},"454":{"position":[[1283,5]]},"464":{"position":[[74,5],[1986,5],[2216,5]]},"466":{"position":[[692,7]]},"468":{"position":[[804,5],[1274,5]]},"490":{"position":[[1900,5]]},"500":{"position":[[571,5]]},"508":{"position":[[3268,7],[3362,5]]},"510":{"position":[[1730,7]]},"522":{"position":[[475,5]]},"532":{"position":[[315,5]]},"538":{"position":[[1690,7],[2419,5],[2921,5]]},"550":{"position":[[345,5],[1712,5]]},"558":{"position":[[2217,5]]},"572":{"position":[[1046,5]]},"584":{"position":[[429,5]]},"588":{"position":[[1647,5]]},"596":{"position":[[1454,7]]},"608":{"position":[[716,5],[792,5],[1045,5],[1118,5]]},"622":{"position":[[1007,5]]},"630":{"position":[[139,4]]},"632":{"position":[[157,5]]},"634":{"position":[[1348,5]]},"640":{"position":[[935,5]]},"646":{"position":[[311,5],[794,5]]},"654":{"position":[[3992,5]]},"656":{"position":[[1066,5]]},"658":{"position":[[274,5],[2420,5]]},"664":{"position":[[1015,7]]},"668":{"position":[[577,5]]},"678":{"position":[[326,5]]},"680":{"position":[[1668,5]]},"686":{"position":[[316,6]]},"688":{"position":[[1606,5]]},"700":{"position":[[908,5]]},"702":{"position":[[2322,5]]},"710":{"position":[[604,6]]},"716":{"position":[[1829,7]]},"722":{"position":[[2959,5]]},"732":{"position":[[135,5],[1336,4]]},"738":{"position":[[4505,7]]},"746":{"position":[[1426,7]]},"752":{"position":[[105,5]]},"758":{"position":[[33,5]]},"760":{"position":[[584,5],[2331,7]]},"796":{"position":[[1377,7]]},"798":{"position":[[129,5],[602,5],[755,5]]},"800":{"position":[[1219,7]]},"802":{"position":[[2083,7],[2870,5]]},"810":{"position":[[916,7],[1004,7]]},"818":{"position":[[329,5],[911,5],[1452,5],[1579,5]]},"830":{"position":[[907,5]]},"832":{"position":[[171,5]]},"834":{"position":[[1226,5],[1538,5],[2148,5],[3587,5],[4086,5],[4870,5]]},"838":{"position":[[2320,5]]},"858":{"position":[[3053,5],[3408,5]]},"860":{"position":[[2518,5]]},"862":{"position":[[1745,7],[3656,7]]},"870":{"position":[[294,4],[453,5]]},"874":{"position":[[1074,4]]},"888":{"position":[[896,5]]},"890":{"position":[[385,7],[446,5],[1184,5],[1375,5]]},"892":{"position":[[2546,7]]},"898":{"position":[[1792,7]]},"900":{"position":[[1485,5]]},"902":{"position":[[1159,7]]},"904":{"position":[[700,5],[1574,7]]},"985":{"position":[[1499,5]]},"1027":{"position":[[2152,5]]},"1033":{"position":[[1200,5],[1274,5],[1549,7],[2233,5],[4302,5]]},"1035":{"position":[[1106,5],[2127,5],[2701,5]]}}}],["meant",{"_index":2230,"t":{"314":{"position":[[348,5]]}}}],["measur",{"_index":893,"t":{"47":{"position":[[8346,7]]}}}],["mechan",{"_index":2091,"t":{"282":{"position":[[1539,9]]},"384":{"position":[[627,10]]},"386":{"position":[[991,9]]},"522":{"position":[[286,9]]},"670":{"position":[[2407,9]]},"890":{"position":[[878,9],[1977,9]]},"965":{"position":[[50,9]]}}}],["medium",{"_index":1072,"t":{"69":{"position":[[5212,7]]},"908":{"position":[[2446,6],[3322,6]]}}}],["member",{"_index":3403,"t":{"526":{"position":[[700,7]]},"582":{"position":[[597,7]]},"584":{"position":[[190,6]]},"654":{"position":[[1231,7]]},"900":{"position":[[1146,7]]},"1027":{"position":[[4373,7],[6549,8],[6607,7]]}}}],["memori",{"_index":195,"t":{"21":{"position":[[112,6]]},"274":{"position":[[141,7]]},"278":{"position":[[158,6]]},"292":{"position":[[663,6],[784,7],[854,6]]},"404":{"position":[[593,6],[828,6]]},"596":{"position":[[109,7]]}}}],["memory2",{"_index":2025,"t":{"272":{"position":[[139,8]]}}}],["mention",{"_index":49,"t":{"8":{"position":[[2,9]]},"47":{"position":[[6964,10],[7008,8]]},"217":{"position":[[20,10]]},"253":{"position":[[1855,9]]},"259":{"position":[[73,10]]},"302":{"position":[[226,9]]},"366":{"position":[[1319,8]]},"448":{"position":[[1484,10]]}}}],["menu",{"_index":244,"t":{"21":{"position":[[1223,4],[3372,5]]},"69":{"position":[[2049,5]]},"175":{"position":[[2855,4]]},"598":{"position":[[592,4]]},"868":{"position":[[37,4]]},"896":{"position":[[294,4]]}}}],["menu.txt",{"_index":3696,"t":{"592":{"position":[[266,8],[360,8]]}}}],["menu.txtcoffe",{"_index":3697,"t":{"592":{"position":[[315,14]]}}}],["menu.txtmilkshak",{"_index":3698,"t":{"592":{"position":[[330,17]]}}}],["menu.txttea",{"_index":3699,"t":{"592":{"position":[[348,11]]}}}],["merg",{"_index":3970,"t":{"660":{"position":[[945,5],[1061,5],[1236,9],[1594,7],[1606,5],[1965,5]]},"664":{"position":[[1165,5],[1557,5]]},"670":{"position":[[2458,5],[2579,5],[2668,5]]},"672":{"position":[[492,5],[815,5],[821,5],[1048,5]]},"690":{"position":[[20,5],[134,5],[259,5],[292,6],[324,5],[499,5],[539,5],[599,6],[639,6],[664,5],[799,5],[949,5],[966,7]]},"692":{"position":[[715,7],[796,7],[1048,5],[1106,5],[1207,5],[1286,5],[1320,6],[1700,6],[1874,5],[2168,5],[2346,6]]},"694":{"position":[[511,5],[1109,5],[1694,6],[1806,5],[1982,9],[2289,6]]},"696":{"position":[[163,6],[306,5],[407,5],[897,7],[1089,5],[1821,5],[1857,5],[1878,7],[1928,5],[1978,5],[2102,5],[2130,5],[2407,5],[2679,5],[2999,13],[3488,5],[4262,6],[4700,5],[4802,7],[5078,5],[5276,5],[5342,5],[5458,5],[5633,9]]},"698":{"position":[[20,5],[239,6],[305,5],[427,5],[678,5]]},"702":{"position":[[428,5],[544,5],[719,9]]},"704":{"position":[[190,5],[1440,5],[1455,5]]},"927":{"position":[[1393,5],[1408,5]]}}}],["merge\"alia",{"_index":4199,"t":{"696":{"position":[[3804,11],[4471,11]]}}}],["merge'a51ae1a",{"_index":4173,"t":{"694":{"position":[[1172,13]]}}}],["merge'commit",{"_index":4167,"t":{"694":{"position":[[687,12]]}}}],["merge)unmerg",{"_index":4190,"t":{"696":{"position":[[2435,14]]}}}],["merge_head",{"_index":4202,"t":{"696":{"position":[[4864,11]]}}}],["mess",{"_index":1830,"t":{"239":{"position":[[293,7],[644,7]]},"306":{"position":[[238,7]]},"710":{"position":[[577,4]]}}}],["messag",{"_index":106,"t":{"10":{"position":[[188,8]]},"47":{"position":[[6304,7],[6401,7]]},"79":{"position":[[42,7]]},"225":{"position":[[1585,7]]},"227":{"position":[[802,9]]},"247":{"position":[[929,7]]},"249":{"position":[[325,8]]},"253":{"position":[[761,7]]},"257":{"position":[[673,8],[1152,7],[1390,7],[1926,7],[3546,7],[3637,7],[6069,7]]},"262":{"position":[[3502,7]]},"398":{"position":[[1351,8]]},"404":{"position":[[273,8]]},"450":{"position":[[4746,8]]},"454":{"position":[[1400,8],[2403,8]]},"496":{"position":[[153,9],[212,10]]},"498":{"position":[[315,8],[342,8],[411,7]]},"500":{"position":[[315,10],[649,8]]},"506":{"position":[[1375,13],[1537,13]]},"508":{"position":[[2057,7],[2152,7]]},"518":{"position":[[462,8]]},"520":{"position":[[197,7],[528,8],[648,8]]},"534":{"position":[[982,7],[1079,7]]},"550":{"position":[[1179,7]]},"558":{"position":[[573,7],[761,9]]},"654":{"position":[[4255,8]]},"658":{"position":[[493,7],[1138,8],[2351,7]]},"666":{"position":[[112,7]]},"668":{"position":[[966,7]]},"680":{"position":[[1624,7]]},"684":{"position":[[270,7],[350,7],[603,7],[734,7],[814,7],[1233,8],[1697,7],[2587,7]]},"686":{"position":[[43,8],[637,9],[797,7],[978,8]]},"688":{"position":[[1554,8],[1674,8]]},"692":{"position":[[1198,8],[1258,7],[1424,7],[1638,8],[1659,7],[1757,7],[1824,7]]},"694":{"position":[[949,8]]},"696":{"position":[[4691,8],[4916,7],[4997,7],[5045,7],[5091,8]]},"704":{"position":[[1035,8],[1058,9],[1089,7],[1097,8]]},"760":{"position":[[536,7]]},"764":{"position":[[1887,7],[1968,7],[2212,8],[2408,8],[2595,7],[2794,7]]},"802":{"position":[[725,7]]},"806":{"position":[[3817,7]]},"810":{"position":[[530,8],[1125,8]]},"854":{"position":[[921,7]]},"862":{"position":[[1133,7],[1419,7]]},"866":{"position":[[511,7]]},"888":{"position":[[144,7],[232,7],[366,7],[450,8]]},"890":{"position":[[772,9],[919,8],[1056,8],[1099,8],[1233,7],[1279,8],[1352,8],[1396,7],[1580,8],[1634,7]]},"894":{"position":[[1240,8]]},"896":{"position":[[1576,7]]},"898":{"position":[[2409,7]]},"900":{"position":[[109,7]]},"908":{"position":[[2306,9],[2329,9],[2381,9],[3182,9],[3205,9],[3257,9]]},"927":{"position":[[988,8],[1011,9],[1042,7],[1050,8]]},"1027":{"position":[[2458,7],[2466,7],[3024,7],[3824,7],[9764,12]]}}}],["message/hello/goodby",{"_index":5433,"t":{"1027":{"position":[[8768,27]]}}}],["message1\"echo",{"_index":3252,"t":{"498":{"position":[[327,14]]}}}],["message1='hello\\nworld'message2=$'hello\\nworld'echo",{"_index":3251,"t":{"498":{"position":[[263,51]]}}}],["message2",{"_index":3253,"t":{"498":{"position":[[354,10]]}}}],["message=\"$1",{"_index":4998,"t":{"862":{"position":[[1184,12],[3317,12]]}}}],["message=\"don't",{"_index":3288,"t":{"506":{"position":[[1349,14],[1511,14]]}}}],["message=\"hello",{"_index":5431,"t":{"1027":{"position":[[8743,14],[9720,14]]}}}],["message^^}echo",{"_index":5435,"t":{"1027":{"position":[[9747,16]]}}}],["message}\\n",{"_index":5024,"t":{"862":{"position":[[3523,13]]}}}],["messi",{"_index":4816,"t":{"806":{"position":[[2581,5]]}}}],["met",{"_index":3359,"t":{"514":{"position":[[773,4]]},"550":{"position":[[100,4]]},"594":{"position":[[77,4]]},"892":{"position":[[2011,3]]}}}],["metacharact",{"_index":2316,"t":{"344":{"position":[[185,13],[427,14],[534,13],[588,13],[800,13],[2271,13],[2345,13],[2687,14],[2791,13],[2897,15],[4747,15],[5112,15]]},"352":{"position":[[71,14],[2092,15]]}}}],["metadata",{"_index":2220,"t":{"314":{"position":[[23,8]]},"670":{"position":[[1442,9]]},"672":{"position":[[374,9]]}}}],["metaphor",{"_index":2051,"t":{"276":{"position":[[907,8]]}}}],["meter",{"_index":2882,"t":{"446":{"position":[[1106,6]]}}}],["method",{"_index":1857,"t":{"243":{"position":[[1164,6]]},"260":{"position":[[1266,8]]},"304":{"position":[[850,6]]},"402":{"position":[[684,9],[1044,9]]},"530":{"position":[[534,6]]},"534":{"position":[[102,6]]},"588":{"position":[[2160,6]]},"700":{"position":[[2196,6],[2300,6]]},"738":{"position":[[3456,6]]},"890":{"position":[[1562,6]]}}}],["metric",{"_index":2943,"t":{"450":{"position":[[1226,7],[1746,7],[2221,7],[2730,7]]}}}],["metricbeat",{"_index":2947,"t":{"450":{"position":[[1284,10],[1337,10],[1390,10],[1786,10],[1818,10],[1850,10],[2261,10],[2293,10],[2325,10],[2770,10],[2802,10],[2834,10]]}}}],["mh",{"_index":2904,"t":{"448":{"position":[[1246,2]]}}}],["mib/",{"_index":3925,"t":{"654":{"position":[[2432,6]]}}}],["michael",{"_index":38,"t":{"4":{"position":[[407,7]]}}}],["microsoft",{"_index":984,"t":{"61":{"position":[[40,9]]},"67":{"position":[[269,9]]},"69":{"position":[[5889,9]]},"91":{"position":[[2181,9]]},"290":{"position":[[1972,9]]},"292":{"position":[[404,10]]}}}],["microsoft/vscod",{"_index":4005,"t":{"664":{"position":[[2281,16]]}}}],["middl",{"_index":1987,"t":{"259":{"position":[[375,7]]},"276":{"position":[[841,7]]}}}],["middleware/log_middleware.go:95unauthor",{"_index":2717,"t":{"402":{"position":[[585,43],[945,43]]}}}],["mind",{"_index":1145,"t":{"77":{"position":[[4597,4]]},"686":{"position":[[114,5]]},"796":{"position":[[712,4]]}}}],["mini",{"_index":1649,"t":{"205":{"position":[[1044,4]]}}}],["minikub",{"_index":3155,"t":{"470":{"position":[[690,8],[739,8],[806,8]]}}}],["minim",{"_index":4689,"t":{"788":{"position":[[352,10]]}}}],["minimum",{"_index":1716,"t":{"219":{"position":[[526,7]]},"636":{"position":[[223,7]]}}}],["minor",{"_index":996,"t":{"63":{"position":[[376,5]]},"540":{"position":[[261,5]]},"738":{"position":[[5022,5],[5078,5],[5927,5],[6013,5]]}}}],["minu",{"_index":4994,"t":{"860":{"position":[[2434,5]]}}}],["minut",{"_index":1991,"t":{"260":{"position":[[256,6],[1147,7]]},"876":{"position":[[1190,7]]}}}],["miscellan",{"_index":692,"t":{"47":{"position":[[647,13],[6565,14],[6686,13]]}}}],["mislead",{"_index":4516,"t":{"738":{"position":[[6324,10]]}}}],["miss",{"_index":910,"t":{"49":{"position":[[165,5]]},"77":{"position":[[3531,4]]},"147":{"position":[[602,7]]},"462":{"position":[[1465,7]]},"700":{"position":[[1327,8],[2915,8]]},"860":{"position":[[1587,7]]}}}],["mission",{"_index":1223,"t":{"91":{"position":[[1273,7]]}}}],["mistak",{"_index":1378,"t":{"105":{"position":[[673,8]]},"237":{"position":[[996,8]]},"292":{"position":[[1166,7]]},"386":{"position":[[1398,7],[1706,9]]},"488":{"position":[[1067,8]]},"490":{"position":[[538,8]]},"702":{"position":[[2908,7]]},"778":{"position":[[1269,8],[1319,7]]},"1037":{"position":[[495,8]]}}}],["mix",{"_index":2569,"t":{"382":{"position":[[166,3]]},"804":{"position":[[2196,6]]},"834":{"position":[[3171,3]]}}}],["mixtur",{"_index":3240,"t":{"490":{"position":[[1969,7]]}}}],["mkd",{"_index":3818,"t":{"626":{"position":[[1386,3]]}}}],["mkdir",{"_index":1407,"t":{"115":{"position":[[280,5],[330,5],[658,5]]},"119":{"position":[[164,5]]},"125":{"position":[[834,5]]},"257":{"position":[[73,5],[109,5],[225,6],[684,5],[1116,5],[1198,5],[3690,5],[4578,5],[5084,5],[5679,5]]},"420":{"position":[[886,5]]},"462":{"position":[[444,5]]},"488":{"position":[[293,5]]},"534":{"position":[[502,5]]},"538":{"position":[[1555,5],[2797,5]]},"550":{"position":[[596,6],[607,5],[680,5],[860,5],[1049,5],[1196,5],[1211,6]]},"552":{"position":[[380,5],[1006,5]]},"554":{"position":[[101,5],[989,5]]},"570":{"position":[[166,5],[822,5]]},"626":{"position":[[1392,5]]},"736":{"position":[[1357,5]]},"744":{"position":[[1189,5]]},"762":{"position":[[635,5]]},"850":{"position":[[1081,5],[1233,5],[1257,5]]},"852":{"position":[[1102,5]]},"870":{"position":[[199,6],[609,5]]},"927":{"position":[[48,5],[1965,5]]},"1023":{"position":[[316,5]]}}}],["mktemp",{"_index":4957,"t":{"858":{"position":[[668,6]]}}}],["mm",{"_index":3435,"t":{"534":{"position":[[334,2]]},"538":{"position":[[537,2]]},"626":{"position":[[5318,2]]},"852":{"position":[[454,2]]}}}],["mmul",{"_index":4366,"t":{"720":{"position":[[2445,5]]}}}],["mnpm",{"_index":2940,"t":{"450":{"position":[[1190,5]]}}}],["mnpmskube",{"_index":2961,"t":{"450":{"position":[[1730,9],[2205,9],[2714,9]]}}}],["modal",{"_index":4570,"t":{"756":{"position":[[491,5]]},"758":{"position":[[9,5]]},"760":{"position":[[1333,5],[3221,5]]},"772":{"position":[[1094,5]]}}}],["mode",{"_index":1055,"t":{"69":{"position":[[3767,5]]},"171":{"position":[[166,5]]},"185":{"position":[[679,5],[780,4]]},"292":{"position":[[1184,4]]},"302":{"position":[[355,6]]},"330":{"position":[[205,5],[384,4]]},"428":{"position":[[580,5]]},"484":{"position":[[998,4]]},"488":{"position":[[309,5]]},"660":{"position":[[365,4]]},"684":{"position":[[1641,4],[1671,4],[2723,4]]},"688":{"position":[[1476,4]]},"690":{"position":[[444,4]]},"692":{"position":[[2043,4],[2086,4]]},"738":{"position":[[2352,4],[3281,4]]},"758":{"position":[[65,7],[87,4],[167,4],[263,4]]},"760":{"position":[[573,5],[778,5],[1061,4],[1138,5],[1151,4],[1163,4],[1226,4],[1301,4],[1438,4],[1487,4],[1561,4],[1639,4],[1656,5],[1687,4],[1696,4],[1799,5],[2196,5],[2233,4],[2362,5],[2407,5],[2693,6],[2705,4],[2731,5],[2865,4],[3259,5],[3272,4],[3284,5]]},"764":{"position":[[1002,5],[1064,5],[1102,4],[1170,4],[1223,4],[2449,4],[2521,4],[2674,4]]},"766":{"position":[[894,4],[917,4]]},"770":{"position":[[150,5],[209,4],[322,5],[635,4],[839,4],[960,4],[1091,4],[1185,4],[1284,4],[1439,4]]},"772":{"position":[[377,4],[903,4],[1409,4]]},"776":{"position":[[281,4],[753,4],[889,4]]},"778":{"position":[[1990,4],[2082,4]]},"780":{"position":[[242,4]]},"790":{"position":[[109,5],[123,5]]},"810":{"position":[[971,4]]},"832":{"position":[[441,4],[1285,5]]},"834":{"position":[[4658,4],[4703,5]]},"840":{"position":[[615,4]]},"842":{"position":[[1100,5]]},"989":{"position":[[268,4],[765,5]]}}}],["mode.^b",{"_index":4867,"t":{"830":{"position":[[1098,7]]}}}],["mode.alia",{"_index":3814,"t":{"626":{"position":[[889,10]]}}}],["mode.exit",{"_index":3893,"t":{"648":{"position":[[2205,10]]}}}],["mode1",{"_index":4638,"t":{"770":{"position":[[1546,6]]}}}],["model",{"_index":1845,"t":{"241":{"position":[[936,6]]},"664":{"position":[[1860,5]]}}}],["modern",{"_index":257,"t":{"21":{"position":[[1464,6]]},"67":{"position":[[517,6],[1417,6]]},"91":{"position":[[2489,6],[2665,6],[3303,6]]},"93":{"position":[[429,6],[1558,6]]},"95":{"position":[[1539,6]]},"241":{"position":[[29,6],[527,6],[621,6],[1085,6]]},"270":{"position":[[129,6]]},"282":{"position":[[1212,6]]},"290":{"position":[[2307,6]]},"326":{"position":[[86,6]]},"328":{"position":[[62,6]]},"396":{"position":[[2014,6]]},"580":{"position":[[2064,6]]},"646":{"position":[[172,6]]},"696":{"position":[[3106,6]]},"716":{"position":[[3270,6],[4976,6]]},"738":{"position":[[4360,6]]},"786":{"position":[[69,8]]},"818":{"position":[[221,6]]},"820":{"position":[[202,6]]},"890":{"position":[[1705,6],[1938,6]]},"931":{"position":[[114,6]]},"951":{"position":[[130,6]]},"955":{"position":[[226,6]]},"969":{"position":[[305,6],[435,6]]},"975":{"position":[[461,6]]}}}],["modifi",{"_index":1450,"t":{"131":{"position":[[749,9]]},"219":{"position":[[665,9]]},"336":{"position":[[1309,6]]},"432":{"position":[[815,8]]},"604":{"position":[[633,8]]},"610":{"position":[[359,9]]},"624":{"position":[[1594,6]]},"696":{"position":[[2507,9]]},"760":{"position":[[124,8]]},"776":{"position":[[17,9]]}}}],["modul",{"_index":2131,"t":{"292":{"position":[[174,7],[267,6]]},"806":{"position":[[627,6],[1137,6],[3574,6]]}}}],["modulu",{"_index":3332,"t":{"510":{"position":[[1940,7]]}}}],["moment",{"_index":1951,"t":{"257":{"position":[[1620,6]]},"346":{"position":[[7,7]]},"402":{"position":[[87,6]]},"544":{"position":[[1068,7]]},"658":{"position":[[2118,7]]},"682":{"position":[[1469,6]]},"702":{"position":[[1473,6]]},"778":{"position":[[1157,7]]}}}],["monday",{"_index":3221,"t":{"490":{"position":[[1017,7]]},"582":{"position":[[366,7]]},"1033":{"position":[[997,6]]}}}],["mondaytuesdaywednesdaythursdayfridaysaturdaysunday",{"_index":5453,"t":{"1033":{"position":[[1731,50]]}}}],["money",{"_index":5122,"t":{"892":{"position":[[2583,6]]}}}],["month",{"_index":4237,"t":{"714":{"position":[[363,5]]}}}],["moolenaar",{"_index":4573,"t":{"760":{"position":[[106,9]]}}}],["more",{"_index":103,"t":{"10":{"position":[[162,4],[175,4]]},"21":{"position":[[4031,4],[4098,4]]},"23":{"position":[[80,4]]},"25":{"position":[[1037,4],[1421,4]]},"27":{"position":[[841,4]]},"33":{"position":[[602,4]]},"37":{"position":[[24,4],[835,4]]},"39":{"position":[[159,4]]},"43":{"position":[[1018,4],[1341,4]]},"45":{"position":[[885,4]]},"47":{"position":[[1096,4],[4859,4],[7553,5],[8518,4]]},"49":{"position":[[574,4]]},"51":{"position":[[225,5]]},"53":{"position":[[532,4]]},"57":{"position":[[633,4],[1133,4]]},"61":{"position":[[526,4]]},"69":{"position":[[4741,5]]},"71":{"position":[[1270,4],[1882,4],[2099,4],[2292,4]]},"75":{"position":[[28,4],[147,5]]},"77":{"position":[[804,4],[5125,4]]},"81":{"position":[[1648,4],[1821,4],[2536,4]]},"83":{"position":[[106,4]]},"87":{"position":[[184,4]]},"91":{"position":[[307,4],[357,4],[2839,4]]},"93":{"position":[[149,4],[600,4],[609,4],[1255,4],[1480,5],[1812,4]]},"95":{"position":[[1935,4],[2111,4],[2307,4],[2464,4],[2482,4],[2589,5],[2652,4]]},"101":{"position":[[74,4]]},"105":{"position":[[86,5]]},"107":{"position":[[110,4],[827,4],[1075,4],[1116,4],[1272,4]]},"115":{"position":[[1028,4]]},"117":{"position":[[922,4]]},"119":{"position":[[21,4]]},"121":{"position":[[714,4],[963,4],[1679,4]]},"129":{"position":[[589,4],[1120,4]]},"131":{"position":[[686,4],[763,5],[786,4]]},"133":{"position":[[667,4]]},"139":{"position":[[13,4]]},"155":{"position":[[13,4]]},"165":{"position":[[989,4]]},"175":{"position":[[2655,4]]},"183":{"position":[[345,4]]},"185":{"position":[[121,5],[1169,4]]},"189":{"position":[[1310,4],[1893,4]]},"193":{"position":[[711,4]]},"203":{"position":[[13,4]]},"205":{"position":[[136,4]]},"207":{"position":[[645,4],[1042,4]]},"209":{"position":[[131,4]]},"211":{"position":[[797,4]]},"213":{"position":[[1078,4]]},"217":{"position":[[1411,4]]},"219":{"position":[[347,5]]},"221":{"position":[[262,4]]},"227":{"position":[[709,4]]},"231":{"position":[[2187,4]]},"237":{"position":[[443,4]]},"239":{"position":[[439,4]]},"241":{"position":[[388,4],[667,4]]},"243":{"position":[[477,4],[744,4]]},"247":{"position":[[184,4]]},"251":{"position":[[1681,4],[1773,4]]},"253":{"position":[[1323,4],[2963,4],[3180,4],[3747,5]]},"255":{"position":[[1224,4]]},"257":{"position":[[3958,4],[6870,4]]},"259":{"position":[[746,4]]},"260":{"position":[[1180,4],[1324,4]]},"262":{"position":[[312,4],[321,4],[2347,4],[2384,4],[2880,4],[3200,4]]},"266":{"position":[[1546,4]]},"274":{"position":[[899,4]]},"280":{"position":[[911,4],[1196,4]]},"282":{"position":[[1207,4]]},"284":{"position":[[1024,4],[1593,4],[1626,5]]},"288":{"position":[[449,4]]},"290":{"position":[[92,4],[287,4],[2264,4]]},"292":{"position":[[1314,4]]},"298":{"position":[[213,5]]},"302":{"position":[[180,4],[1077,4]]},"304":{"position":[[1048,4],[1753,4],[2438,4]]},"306":{"position":[[59,4],[713,4]]},"308":{"position":[[666,5]]},"310":{"position":[[210,4]]},"322":{"position":[[165,4]]},"326":{"position":[[264,4]]},"338":{"position":[[312,4]]},"342":{"position":[[1713,4]]},"344":{"position":[[716,4],[1772,4],[1954,4],[3820,4],[5141,4]]},"346":{"position":[[1503,4]]},"348":{"position":[[819,4]]},"352":{"position":[[244,4],[405,4],[1513,4],[2124,4],[2540,4],[2908,4]]},"354":{"position":[[364,4],[533,4],[1106,4]]},"366":{"position":[[3393,5],[4715,4]]},"368":{"position":[[1819,4]]},"370":{"position":[[1265,4]]},"376":{"position":[[6085,4]]},"378":{"position":[[3525,4],[4199,4],[5061,4]]},"382":{"position":[[108,4]]},"384":{"position":[[662,4]]},"386":{"position":[[593,4],[790,4],[1134,4],[1647,4]]},"390":{"position":[[512,4],[962,4],[1108,4],[1304,4],[1377,4],[1718,4]]},"392":{"position":[[708,4]]},"394":{"position":[[2335,4]]},"396":{"position":[[1318,4],[1923,4],[2465,4]]},"404":{"position":[[691,4],[983,4]]},"406":{"position":[[413,4],[1030,4]]},"408":{"position":[[276,4],[1048,4]]},"410":{"position":[[270,4]]},"416":{"position":[[376,4]]},"422":{"position":[[1367,4],[2040,4],[2049,4]]},"424":{"position":[[1025,4]]},"432":{"position":[[1148,4]]},"434":{"position":[[1492,4]]},"436":{"position":[[20,4]]},"440":{"position":[[318,4]]},"442":{"position":[[918,4],[1429,4],[1734,4]]},"446":{"position":[[1531,4],[2490,4],[2539,4]]},"448":{"position":[[1332,4],[1842,4],[2136,4]]},"450":{"position":[[44,4],[5255,4],[5475,4]]},"452":{"position":[[723,4]]},"462":{"position":[[1500,4]]},"470":{"position":[[1307,4]]},"472":{"position":[[398,4]]},"474":{"position":[[235,5],[251,4]]},"476":{"position":[[83,4],[484,4],[522,4],[585,5]]},"482":{"position":[[1509,4]]},"484":{"position":[[1499,4]]},"488":{"position":[[61,4]]},"490":{"position":[[2079,4]]},"492":{"position":[[0,4],[746,4]]},"502":{"position":[[1100,4],[1301,4]]},"506":{"position":[[2884,4]]},"510":{"position":[[1580,4],[2440,4]]},"512":{"position":[[176,4],[584,4]]},"514":{"position":[[848,4]]},"524":{"position":[[662,4],[720,4],[1022,4],[1087,4],[1135,4]]},"526":{"position":[[1728,4]]},"530":{"position":[[744,4]]},"532":{"position":[[88,4]]},"540":{"position":[[578,4]]},"546":{"position":[[61,4],[228,4]]},"552":{"position":[[935,4]]},"554":{"position":[[205,4],[776,4],[1160,4],[1222,4]]},"566":{"position":[[84,4],[1060,4],[1284,4],[1554,4],[1623,4],[1650,4]]},"572":{"position":[[1104,4],[1303,4],[1366,4]]},"574":{"position":[[2987,4]]},"580":{"position":[[2059,4]]},"584":{"position":[[2398,4],[2480,4]]},"586":{"position":[[2001,4],[2474,4]]},"588":{"position":[[2938,4]]},"596":{"position":[[2167,4]]},"600":{"position":[[1262,4],[1358,4]]},"604":{"position":[[911,4]]},"606":{"position":[[216,4]]},"616":{"position":[[311,4],[404,4],[881,4]]},"618":{"position":[[88,4]]},"624":{"position":[[492,4],[704,4]]},"626":{"position":[[929,4],[969,4],[1146,4],[1573,4]]},"628":{"position":[[338,5]]},"634":{"position":[[2858,4]]},"636":{"position":[[2459,4]]},"654":{"position":[[646,4],[3142,4],[4268,5]]},"664":{"position":[[2397,4]]},"668":{"position":[[143,4],[3010,4]]},"670":{"position":[[1057,4],[1560,4],[1604,5]]},"674":{"position":[[238,4],[305,5]]},"682":{"position":[[1908,4]]},"684":{"position":[[3263,4],[3307,4]]},"686":{"position":[[342,4],[680,4]]},"690":{"position":[[39,4],[937,4]]},"694":{"position":[[999,4]]},"696":{"position":[[5131,4]]},"698":{"position":[[523,5],[564,4]]},"700":{"position":[[2316,4]]},"712":{"position":[[268,4]]},"714":{"position":[[1800,4]]},"718":{"position":[[2490,4]]},"720":{"position":[[430,4],[7746,4]]},"724":{"position":[[382,4],[427,4]]},"728":{"position":[[183,4],[424,4]]},"734":{"position":[[439,4],[919,4],[2664,4],[3229,4]]},"738":{"position":[[3347,5]]},"742":{"position":[[1008,4]]},"744":{"position":[[353,4],[2657,4]]},"754":{"position":[[2452,4]]},"756":{"position":[[297,4]]},"760":{"position":[[1317,4],[1884,4],[3464,4]]},"764":{"position":[[1658,4]]},"768":{"position":[[131,4]]},"778":{"position":[[1381,4],[1510,4],[1840,4],[2016,4],[2121,4]]},"780":{"position":[[147,5]]},"786":{"position":[[199,4]]},"790":{"position":[[152,5],[514,6]]},"794":{"position":[[266,4],[1086,4]]},"798":{"position":[[439,4]]},"802":{"position":[[1082,4]]},"806":{"position":[[1180,4],[1227,5]]},"808":{"position":[[638,4],[2045,5]]},"810":{"position":[[350,4],[583,4],[1187,4],[1536,4]]},"818":{"position":[[113,4]]},"820":{"position":[[197,4],[358,4]]},"828":{"position":[[1432,4]]},"834":{"position":[[4772,4],[5028,4],[5124,4]]},"836":{"position":[[322,4],[362,4]]},"840":{"position":[[161,5],[872,4],[947,4]]},"844":{"position":[[9,4],[214,4]]},"852":{"position":[[2157,4]]},"858":{"position":[[1744,4]]},"860":{"position":[[835,4]]},"862":{"position":[[576,4],[602,4],[3779,4],[3956,4],[4194,4]]},"876":{"position":[[785,4],[1174,4]]},"892":{"position":[[1870,4]]},"896":{"position":[[1232,4]]},"902":{"position":[[287,4]]},"906":{"position":[[235,4]]},"908":{"position":[[3732,4]]},"910":{"position":[[193,4],[1885,4]]},"912":{"position":[[653,4]]},"927":{"position":[[2011,4]]},"953":{"position":[[435,5]]},"955":{"position":[[221,4]]},"959":{"position":[[206,5]]},"961":{"position":[[92,5]]},"985":{"position":[[726,4]]},"1019":{"position":[[1627,4]]},"1027":{"position":[[1591,4],[10316,4]]},"1033":{"position":[[4644,4]]},"1035":{"position":[[3219,4],[3358,4]]}}}],["more_alias",{"_index":3976,"t":{"660":{"position":[[1074,17],[1102,14]]},"692":{"position":[[154,15],[494,12],[1058,12],[1112,12],[1220,15],[1724,13],[2255,12]]},"694":{"position":[[1186,14],[1819,15],[1848,14],[2257,12]]},"696":{"position":[[5471,17],[5499,14]]},"702":{"position":[[557,17],[585,14]]}}}],["more_aliases'a95bd90",{"_index":4172,"t":{"694":{"position":[[1122,21]]}}}],["more_aliases'commit",{"_index":4164,"t":{"694":{"position":[[524,20]]}}}],["more_aliases)author",{"_index":4169,"t":{"694":{"position":[[741,21]]}}}],["more_aliasesmerg",{"_index":4151,"t":{"692":{"position":[[1880,17]]}}}],["more_aliasestouch",{"_index":4143,"t":{"692":{"position":[[216,17]]}}}],["more_chang",{"_index":3984,"t":{"660":{"position":[[1313,14]]}}}],["mostli",{"_index":4432,"t":{"732":{"position":[[563,6]]}}}],["motherword",{"_index":3755,"t":{"610":{"position":[[685,11]]}}}],["motion",{"_index":4611,"t":{"766":{"position":[[45,10],[74,7],[342,7],[438,6],[527,8],[536,6],[549,6],[837,7],[1013,9],[1027,7],[1052,9],[1066,7],[1090,9],[1104,7],[1128,7],[1143,7],[1165,9],[1179,7],[1197,6],[1261,7],[1686,7],[1786,7],[1820,7],[1840,7],[1939,7],[1975,7],[2011,7]]},"768":{"position":[[119,7],[273,6],[343,9],[357,7],[393,9],[407,7],[448,9],[462,7]]},"770":{"position":[[748,7],[1575,7]]},"772":{"position":[[93,7],[635,6]]},"774":{"position":[[26,7],[115,7],[251,8],[260,6],[503,7],[848,7],[916,6],[950,7],[983,7],[1076,8],[1450,6],[1510,7],[1563,7]]},"778":{"position":[[215,7],[271,7],[575,7],[1525,8],[2031,8],[2136,8]]},"790":{"position":[[129,8]]}}}],["motions'~~~\"/tmp/bash",{"_index":4672,"t":{"778":{"position":[[1855,21]]}}}],["motionsvim",{"_index":4612,"t":{"766":{"position":[[331,10]]},"778":{"position":[[204,10]]}}}],["mountain",{"_index":1135,"t":{"77":{"position":[[4029,8]]}}}],["mous",{"_index":323,"t":{"21":{"position":[[3518,5]]},"25":{"position":[[1288,5],[1397,6]]},"266":{"position":[[980,5]]},"272":{"position":[[331,6]]},"752":{"position":[[147,6]]},"754":{"position":[[1364,6]]},"834":{"position":[[4259,5],[4287,6],[4394,5],[4487,5],[4652,5],[4690,5]]},"840":{"position":[[218,6],[609,5],[655,5]]},"844":{"position":[[75,5]]},"961":{"position":[[54,6]]}}}],["mouseless",{"_index":4889,"t":{"834":{"position":[[4556,11]]}}}],["move",{"_index":511,"t":{"29":{"position":[[1013,4]]},"43":{"position":[[49,4]]},"69":{"position":[[3661,4]]},"75":{"position":[[190,4]]},"77":{"position":[[1048,4]]},"81":{"position":[[56,4],[1699,4]]},"83":{"position":[[992,4]]},"93":{"position":[[636,5]]},"99":{"position":[[35,6],[1041,4]]},"113":{"position":[[198,6],[240,6],[709,5]]},"117":{"position":[[200,7]]},"119":{"position":[[284,4]]},"125":{"position":[[788,6],[807,4]]},"133":{"position":[[35,4],[141,4],[1085,4]]},"135":{"position":[[1015,4],[1258,4],[1350,4]]},"137":{"position":[[1330,4]]},"139":{"position":[[359,4],[463,6]]},"141":{"position":[[41,4],[397,6],[860,4]]},"143":{"position":[[152,5]]},"147":{"position":[[842,6]]},"151":{"position":[[360,4]]},"165":{"position":[[540,4]]},"175":{"position":[[1914,5]]},"185":{"position":[[582,4],[1096,4]]},"207":{"position":[[184,4]]},"231":{"position":[[275,4],[573,6],[905,6],[2199,4]]},"237":{"position":[[961,4]]},"239":{"position":[[157,4]]},"243":{"position":[[206,4],[291,4],[1425,4],[1882,5]]},"280":{"position":[[916,6]]},"284":{"position":[[1203,6]]},"296":{"position":[[281,4]]},"300":{"position":[[855,4]]},"302":{"position":[[1212,4]]},"354":{"position":[[354,4]]},"384":{"position":[[376,4]]},"456":{"position":[[158,4]]},"508":{"position":[[4384,5]]},"538":{"position":[[1757,4]]},"586":{"position":[[2427,5]]},"598":{"position":[[283,4],[307,4]]},"602":{"position":[[307,4]]},"626":{"position":[[1352,4]]},"660":{"position":[[221,4],[437,5]]},"672":{"position":[[874,4]]},"678":{"position":[[210,5]]},"680":{"position":[[1016,5]]},"688":{"position":[[3530,4]]},"700":{"position":[[2999,4],[3548,5],[3583,4]]},"702":{"position":[[1377,4],[1937,5],[2141,5],[2409,4],[2538,5],[2632,4],[2770,4],[2823,5],[3036,5]]},"704":{"position":[[1889,4],[1969,4]]},"738":{"position":[[6100,4]]},"758":{"position":[[236,4]]},"760":{"position":[[2470,4]]},"762":{"position":[[541,4]]},"766":{"position":[[18,4],[125,6],[371,4],[1280,4],[1493,4],[1579,6],[1848,4]]},"768":{"position":[[176,4],[245,5]]},"770":{"position":[[24,4],[75,4],[260,6],[1524,4]]},"772":{"position":[[360,4],[1392,4]]},"774":{"position":[[538,6],[1471,4],[1520,4]]},"778":{"position":[[244,4],[294,4],[616,4],[1630,6]]},"828":{"position":[[279,6],[483,4],[596,6],[696,6],[788,4],[930,6],[1625,4],[1654,4]]},"830":{"position":[[828,4]]},"832":{"position":[[1423,4],[1449,4]]},"834":{"position":[[4852,4]]},"842":{"position":[[544,4],[1271,4],[1297,4],[1401,4],[1430,4],[1755,4]]},"892":{"position":[[313,4]]},"927":{"position":[[1842,4],[1922,4]]}}}],["movement",{"_index":1288,"t":{"93":{"position":[[980,8]]},"155":{"position":[[31,9]]},"185":{"position":[[11,8]]},"704":{"position":[[1778,8]]},"754":{"position":[[2327,8]]},"770":{"position":[[49,8]]},"772":{"position":[[1182,9]]},"927":{"position":[[1731,8]]}}}],["movi",{"_index":1116,"t":{"77":{"position":[[2509,6],[2549,6]]},"374":{"position":[[103,6],[385,5]]},"446":{"position":[[980,7]]}}}],["mqv9d",{"_index":2945,"t":{"450":{"position":[[1245,5]]}}}],["mqv9dmetricbeat",{"_index":2962,"t":{"450":{"position":[[1765,15],[2240,15],[2749,15]]}}}],["msdn",{"_index":2128,"t":{"290":{"position":[[2475,5]]}}}],["mtime",{"_index":1720,"t":{"219":{"position":[[608,5],[702,5]]}}}],["much",{"_index":445,"t":{"25":{"position":[[1253,4]]},"31":{"position":[[3033,4]]},"57":{"position":[[1266,4]]},"77":{"position":[[2384,4],[2581,4]]},"81":{"position":[[1816,4]]},"91":{"position":[[2834,4]]},"113":{"position":[[668,4]]},"117":{"position":[[917,4]]},"207":{"position":[[336,4]]},"241":{"position":[[122,4]]},"266":{"position":[[473,4],[559,4],[922,4]]},"274":{"position":[[220,4]]},"278":{"position":[[439,4]]},"282":{"position":[[1563,4],[2107,4]]},"290":{"position":[[2259,4]]},"304":{"position":[[1424,4],[1688,4],[1748,4]]},"348":{"position":[[814,4]]},"350":{"position":[[369,4]]},"376":{"position":[[1539,4]]},"408":{"position":[[271,4]]},"456":{"position":[[485,4]]},"514":{"position":[[843,4]]},"528":{"position":[[730,4]]},"602":{"position":[[1220,4]]},"634":{"position":[[2853,4]]},"686":{"position":[[417,4]]},"720":{"position":[[33,4],[425,4],[4891,4]]},"736":{"position":[[67,4]]},"754":{"position":[[1669,4]]},"770":{"position":[[599,4]]},"786":{"position":[[194,4]]},"796":{"position":[[670,4]]},"800":{"position":[[1527,4]]},"818":{"position":[[211,4]]},"834":{"position":[[3326,4]]},"838":{"position":[[384,4]]},"876":{"position":[[700,4]]},"902":{"position":[[282,4]]},"953":{"position":[[430,4]]},"959":{"position":[[75,4]]}}}],["multi",{"_index":3831,"t":{"626":{"position":[[2644,5]]},"738":{"position":[[6474,5],[6576,5],[7279,5]]}}}],["multilin",{"_index":5346,"t":{"987":{"position":[[197,9]]}}}],["multipl",{"_index":409,"t":{"23":{"position":[[2527,8]]},"47":{"position":[[1558,8],[3402,8]]},"77":{"position":[[2442,8]]},"117":{"position":[[267,8]]},"125":{"position":[[1256,8]]},"143":{"position":[[297,8]]},"175":{"position":[[2501,8]]},"189":{"position":[[2241,8]]},"199":{"position":[[15,8]]},"237":{"position":[[1195,8]]},"336":{"position":[[1112,8]]},"352":{"position":[[3094,8]]},"366":{"position":[[46,8]]},"372":{"position":[[1302,8]]},"378":{"position":[[1103,8],[1636,8]]},"386":{"position":[[400,8]]},"402":{"position":[[187,8]]},"426":{"position":[[157,8]]},"458":{"position":[[1181,8]]},"468":{"position":[[1800,8],[2185,8]]},"490":{"position":[[36,8]]},"510":{"position":[[1823,14]]},"512":{"position":[[1313,8]]},"564":{"position":[[29,8],[122,8]]},"604":{"position":[[1087,8]]},"626":{"position":[[7176,8],[7258,8],[7426,8]]},"656":{"position":[[246,8]]},"670":{"position":[[2786,8]]},"678":{"position":[[473,8]]},"716":{"position":[[992,8]]},"744":{"position":[[57,8],[436,8]]},"818":{"position":[[274,8]]},"840":{"position":[[297,8]]},"860":{"position":[[2201,8]]},"874":{"position":[[806,8]]},"876":{"position":[[503,8]]},"939":{"position":[[125,8]]},"1023":{"position":[[489,8]]},"1033":{"position":[[2062,8],[2417,8]]}}}],["multiplex",{"_index":1840,"t":{"241":{"position":[[445,12],[482,12],[710,12],[982,11],[1162,11],[1236,13]]},"290":{"position":[[549,12],[593,13]]},"728":{"position":[[631,15]]},"790":{"position":[[307,12]]},"816":{"position":[[11,11],[110,11],[347,12],[513,11],[620,11],[1096,12],[1460,11]]},"818":{"position":[[41,12],[176,11],[249,12],[863,11],[1205,12],[1405,11],[1523,12],[1782,12]]},"820":{"position":[[21,12],[209,12],[572,13]]},"826":{"position":[[145,12],[202,11],[314,11]]},"840":{"position":[[116,11]]},"846":{"position":[[54,13]]},"902":{"position":[[1590,12]]},"906":{"position":[[586,11]]},"995":{"position":[[135,12]]}}}],["muscl",{"_index":194,"t":{"21":{"position":[[105,6]]}}}],["music",{"_index":1107,"t":{"77":{"position":[[1168,6],[2074,7],[2342,5],[2374,5]]}}}],["mv",{"_index":1399,"t":{"113":{"position":[[195,2],[388,2],[782,2]]},"117":{"position":[[126,2]]},"125":{"position":[[785,2]]},"302":{"position":[[1289,2]]},"378":{"position":[[2921,2]]},"580":{"position":[[1379,3]]},"700":{"position":[[2450,2],[3413,2],[3545,2],[3644,2],[3840,3],[3850,2]]},"704":{"position":[[1742,2]]},"927":{"position":[[1695,2]]}}}],["mwarneck",{"_index":32,"t":{"4":{"position":[[341,11]]}}}],["mwebbrows",{"_index":4028,"t":{"666":{"position":[[1577,11]]}}}],["my_opt",{"_index":4949,"t":{"854":{"position":[[730,10],[769,13],[800,13]]}}}],["my_project",{"_index":4509,"t":{"738":{"position":[[5354,11],[5383,12]]}}}],["myproject",{"_index":5238,"t":{"910":{"position":[[942,11],[1179,11]]}}}],["myself",{"_index":644,"t":{"43":{"position":[[794,6]]},"81":{"position":[[1490,9]]},"183":{"position":[[297,6]]},"738":{"position":[[4094,6]]}}}],["myserver.com",{"_index":5197,"t":{"908":{"position":[[727,13]]}}}],["n",{"_index":371,"t":{"23":{"position":[[1108,1]]},"41":{"position":[[184,2],[250,2]]},"43":{"position":[[627,1],[672,1]]},"179":{"position":[[833,1],[846,3]]},"205":{"position":[[108,1]]},"215":{"position":[[342,2],[410,2],[482,2],[553,1],[706,1]]},"243":{"position":[[1632,1]]},"253":{"position":[[2785,1]]},"374":{"position":[[184,1],[484,1]]},"376":{"position":[[2731,1],[2958,1],[3399,1],[4105,1],[4464,1],[4866,1],[5241,1],[5436,1],[5720,1],[6489,1]]},"378":{"position":[[2512,1]]},"402":{"position":[[363,1]]},"406":{"position":[[953,1]]},"424":{"position":[[170,1],[218,1],[231,1]]},"426":{"position":[[307,1],[363,1],[378,1]]},"430":{"position":[[341,1],[389,1],[402,1]]},"438":{"position":[[364,1],[412,1],[425,1]]},"442":{"position":[[681,1]]},"446":{"position":[[1225,1],[1301,1],[1908,1],[2626,1],[3251,1],[3520,1],[3988,1]]},"448":{"position":[[277,1],[413,1],[460,4],[623,4],[688,2]]},"450":{"position":[[1928,1],[2034,1],[4198,1],[4382,1],[4833,1],[5048,1]]},"458":{"position":[[135,1],[195,1],[286,1],[300,1]]},"470":{"position":[[1026,3]]},"472":{"position":[[230,5]]},"508":{"position":[[3698,1],[3788,1],[3941,1],[4211,1]]},"510":{"position":[[2302,2]]},"512":{"position":[[359,1],[407,1],[417,1],[992,1],[1060,1],[1075,1]]},"518":{"position":[[118,3]]},"526":{"position":[[927,3],[1003,4]]},"528":{"position":[[308,3],[512,4]]},"560":{"position":[[186,1]]},"572":{"position":[[621,1],[625,1]]},"574":{"position":[[654,1],[722,1],[734,1],[1782,1],[1850,1],[1865,1],[2163,1],[2265,1],[2280,1]]},"582":{"position":[[279,1],[625,1]]},"596":{"position":[[974,1],[1130,1]]},"612":{"position":[[747,1],[815,1],[830,1]]},"714":{"position":[[709,2]]},"720":{"position":[[3507,1],[4525,4]]},"734":{"position":[[1492,1]]},"828":{"position":[[1623,1]]},"842":{"position":[[1399,1]]}}}],["n${push_output",{"_index":4020,"t":{"666":{"position":[[1256,18]]}}}],["n'\"rank\"\"rating\"\"title\"\"review",{"_index":2898,"t":{"448":{"position":[[324,34]]}}}],["n1",{"_index":3540,"t":{"560":{"position":[[440,2],[470,2],[491,2],[521,2],[546,2],[575,2],[595,2],[624,2],[656,2],[685,2],[708,2],[737,2]]},"666":{"position":[[1468,2]]}}}],["n1|sed",{"_index":4476,"t":{"734":{"position":[[2914,6]]}}}],["n2",{"_index":3541,"t":{"560":{"position":[[447,2],[477,2],[498,2],[528,2],[553,2],[591,3],[602,2],[652,3],[663,2],[704,3],[715,2],[768,3]]}}}],["n7kxm",{"_index":2952,"t":{"450":{"position":[[1401,5],[1861,5],[2336,5],[2845,5]]}}}],["n=$1",{"_index":3406,"t":{"526":{"position":[[966,4]]},"528":{"position":[[396,4]]}}}],["n>done",{"_index":3606,"t":{"580":{"position":[[209,6]]},"590":{"position":[[242,6]]},"594":{"position":[[219,6]]},"600":{"position":[[264,6]]}}}],["n>fi",{"_index":3521,"t":{"550":{"position":[[243,4]]}}}],["nadd",{"_index":3717,"t":{"594":{"position":[[1285,4],[1408,4]]}}}],["naevi",{"_index":4729,"t":{"802":{"position":[[2511,5]]},"804":{"position":[[2540,5]]}}}],["name",{"_index":521,"t":{"31":{"position":[[178,4]]},"41":{"position":[[459,5],[528,5]]},"47":{"position":[[1762,4],[3745,5]]},"69":{"position":[[3796,4],[4894,4]]},"113":{"position":[[729,5]]},"115":{"position":[[33,4]]},"121":{"position":[[1170,4]]},"123":{"position":[[573,4]]},"137":{"position":[[121,4],[145,4]]},"195":{"position":[[16,4],[66,5],[154,5],[170,4],[857,4],[941,4],[956,4],[1035,4],[1079,4],[1141,4],[1169,5],[1222,4],[1264,5],[1327,5],[1431,4],[1569,4],[1815,4],[1882,4]]},"199":{"position":[[143,4],[342,4],[628,4],[645,4]]},"201":{"position":[[126,5],[196,4],[213,4],[230,4],[246,4],[360,4]]},"203":{"position":[[146,4],[163,4],[333,5]]},"205":{"position":[[91,4],[115,5],[199,4],[262,4],[675,5],[793,4],[958,5]]},"209":{"position":[[339,4]]},"211":{"position":[[81,4],[638,4]]},"213":{"position":[[242,4]]},"215":{"position":[[243,4]]},"253":{"position":[[1648,6]]},"257":{"position":[[4209,4]]},"278":{"position":[[1082,4]]},"280":{"position":[[50,4]]},"288":{"position":[[932,4],[1098,4]]},"300":{"position":[[129,5]]},"304":{"position":[[753,5],[1288,5]]},"330":{"position":[[241,5]]},"354":{"position":[[564,5],[662,6]]},"364":{"position":[[713,4]]},"378":{"position":[[233,5],[946,5],[1481,5],[2223,5],[3207,4],[3324,4],[3502,4],[3625,4],[3848,4]]},"380":{"position":[[444,4]]},"392":{"position":[[12,5],[61,4],[171,4]]},"400":{"position":[[1222,5],[1228,9]]},"402":{"position":[[338,4]]},"406":{"position":[[707,4],[919,5]]},"420":{"position":[[1101,5]]},"430":{"position":[[451,4],[481,5]]},"432":{"position":[[1395,4]]},"438":{"position":[[991,4]]},"446":{"position":[[1113,4]]},"450":{"position":[[760,5],[1443,4]]},"454":{"position":[[427,4]]},"456":{"position":[[886,4]]},"462":{"position":[[180,4]]},"464":{"position":[[124,6],[430,4],[2122,5],[2259,6]]},"468":{"position":[[324,4],[1052,4],[1923,4]]},"470":{"position":[[637,4]]},"480":{"position":[[386,5]]},"482":{"position":[[64,4],[249,4],[340,5],[614,6]]},"488":{"position":[[36,4],[99,4],[610,4],[858,4],[1036,4],[1428,4]]},"490":{"position":[[1755,5]]},"506":{"position":[[1661,4],[1782,4],[1830,5],[1965,5]]},"508":{"position":[[326,5],[938,4],[1057,4],[1103,4],[1183,8],[1233,4],[1444,5],[1475,4],[1582,5],[1780,4],[2301,5],[2326,6],[2387,5],[2687,5]]},"514":{"position":[[394,4]]},"518":{"position":[[50,5],[143,4],[570,4]]},"522":{"position":[[1702,5]]},"524":{"position":[[679,6],[1152,5]]},"534":{"position":[[1032,4]]},"540":{"position":[[360,4],[647,4]]},"544":{"position":[[1040,4],[1116,5]]},"568":{"position":[[894,4]]},"574":{"position":[[1237,4],[1341,4],[2522,4],[2592,4]]},"580":{"position":[[120,6],[571,4],[1489,4]]},"584":{"position":[[27,5],[123,4]]},"588":{"position":[[1719,5]]},"622":{"position":[[619,4]]},"626":{"position":[[1784,4],[1970,4],[2367,4],[2796,6],[3594,4],[4574,5],[5137,5]]},"648":{"position":[[1321,4]]},"654":{"position":[[145,5],[1104,4],[3919,5]]},"656":{"position":[[213,4],[1113,4]]},"658":{"position":[[639,5]]},"660":{"position":[[531,5],[1763,4],[1788,4]]},"666":{"position":[[892,5],[1629,4],[1687,4]]},"668":{"position":[[1636,5],[2681,4],[3101,4]]},"678":{"position":[[784,5]]},"680":{"position":[[1120,4],[1394,4],[1465,4],[1556,4],[1752,5]]},"682":{"position":[[533,4]]},"688":{"position":[[61,5],[766,4],[2159,4]]},"692":{"position":[[1718,5]]},"694":{"position":[[1585,6]]},"696":{"position":[[4386,4]]},"702":{"position":[[2672,5]]},"704":{"position":[[1307,6],[1337,4],[1418,4],[1472,5]]},"708":{"position":[[611,4]]},"714":{"position":[[772,4],[1280,4]]},"716":{"position":[[4627,4]]},"720":{"position":[[1481,5],[1653,4],[2775,4]]},"738":{"position":[[305,4],[365,5],[548,4],[789,4],[4980,4],[5819,4]]},"762":{"position":[[621,5]]},"764":{"position":[[117,5],[543,5]]},"788":{"position":[[256,4]]},"806":{"position":[[1310,5]]},"822":{"position":[[321,4]]},"828":{"position":[[1443,5],[1966,5]]},"830":{"position":[[226,4],[637,5],[1001,4]]},"832":{"position":[[1205,4],[1235,5],[1241,4],[1266,4],[1305,5],[1319,4]]},"834":{"position":[[624,5],[2013,5],[2083,4],[2161,5],[2356,5],[2742,4],[2812,4]]},"842":{"position":[[763,5],[813,5],[835,5],[926,5],[947,5],[953,4],[1081,4],[1120,5],[1134,4]]},"852":{"position":[[1883,5],[2016,5]]},"858":{"position":[[2947,4],[3649,4]]},"860":{"position":[[619,4],[810,6]]},"864":{"position":[[367,5]]},"884":{"position":[[197,5]]},"892":{"position":[[677,4]]},"896":{"position":[[81,4],[427,4]]},"902":{"position":[[61,5],[733,5],[889,4],[1322,4]]},"908":{"position":[[764,5],[1026,4]]},"910":{"position":[[1088,5]]},"927":{"position":[[251,4],[274,4],[279,5],[313,5],[326,4],[357,5],[371,4],[397,4],[402,5],[425,4],[435,4],[1260,6],[1290,4],[1371,4],[1425,5]]},"995":{"position":[[473,6]]},"997":{"position":[[259,5]]},"1011":{"position":[[11,5]]},"1019":{"position":[[844,5]]},"1027":{"position":[[543,4],[750,4],[843,4],[1309,4],[1410,4],[1734,5],[2019,5],[3569,4],[3641,4],[4283,4],[4317,5],[4544,5],[4554,9],[4592,4],[4642,5],[4946,5],[5434,6],[5974,5],[6491,5],[8288,5],[8361,5],[9920,4],[10029,4],[10297,6]]},"1029":{"position":[[592,4],[685,5],[930,4]]},"1033":{"position":[[3034,5],[3616,5]]},"1035":{"position":[[1857,5]]}}}],["name=\"dave\"location=\"singapore\"echo",{"_index":3172,"t":{"482":{"position":[[297,35]]}}}],["name?\"read",{"_index":3299,"t":{"508":{"position":[[1155,10]]}}}],["name?\"readecho",{"_index":3295,"t":{"508":{"position":[[242,14]]}}}],["name?davehello",{"_index":3298,"t":{"508":{"position":[[496,15]]}}}],["nameecho",{"_index":3300,"t":{"508":{"position":[[1166,8],[2309,8]]}}}],["nameelast",{"_index":2968,"t":{"450":{"position":[[2543,11]]}}}],["namepod/mi",{"_index":3151,"t":{"470":{"position":[[452,10]]}}}],["namesar",{"_index":5144,"t":{"898":{"position":[[1432,8]]}}}],["namespac",{"_index":2515,"t":{"378":{"position":[[260,10],[645,11],[973,10],[1508,10],[2250,10]]}}}],["namespace%stringdata",{"_index":2522,"t":{"378":{"position":[[984,22]]}}}],["namespace=product",{"_index":2533,"t":{"378":{"position":[[1936,21]]}}}],["nank",{"_index":4776,"t":{"804":{"position":[[2455,7]]},"806":{"position":[[3401,7]]}}}],["nankeen",{"_index":4728,"t":{"802":{"position":[[2502,7]]},"804":{"position":[[2406,7]]}}}],["nano",{"_index":1524,"t":{"175":{"position":[[759,4],[2780,4],[2793,4]]},"434":{"position":[[531,5],[840,5],[1297,5]]},"684":{"position":[[1340,4]]},"738":{"position":[[2691,4]]},"752":{"position":[[346,5]]}}}],["narg",{"_index":4798,"t":{"806":{"position":[[991,10]]}}}],["nation",{"_index":4265,"t":{"716":{"position":[[602,8]]}}}],["nativ",{"_index":1019,"t":{"67":{"position":[[1080,9]]},"979":{"position":[[76,6]]},"1013":{"position":[[76,6]]}}}],["natur",{"_index":3572,"t":{"570":{"position":[[1543,6]]},"604":{"position":[[860,6]]}}}],["navig",{"_index":633,"t":{"43":{"position":[[398,8]]},"99":{"position":[[1004,10]]},"111":{"position":[[87,10]]},"129":{"position":[[54,8],[158,9]]},"137":{"position":[[1208,8]]},"139":{"position":[[1260,8]]},"151":{"position":[[233,10]]},"165":{"position":[[24,10]]},"221":{"position":[[550,10]]},"249":{"position":[[1275,10]]},"284":{"position":[[1169,8],[1248,10],[1314,8]]},"754":{"position":[[2293,8]]},"766":{"position":[[1562,8],[1629,8]]},"828":{"position":[[1143,10],[1852,9]]},"842":{"position":[[1628,9]]}}}],["ne",{"_index":3542,"t":{"560":{"position":[[495,2]]}}}],["near",{"_index":3529,"t":{"554":{"position":[[636,4]]}}}],["nearer",{"_index":3654,"t":{"584":{"position":[[2700,6]]}}}],["neat",{"_index":3021,"t":{"452":{"position":[[560,4]]}}}],["necessari",{"_index":1203,"t":{"91":{"position":[[419,9]]},"211":{"position":[[377,9]]},"424":{"position":[[1129,9]]},"468":{"position":[[1630,10]]}}}],["necessarili",{"_index":1010,"t":{"67":{"position":[[742,11]]},"310":{"position":[[166,11]]}}}],["need",{"_index":249,"t":{"21":{"position":[[1340,6],[2845,4],[2941,4]]},"23":{"position":[[280,4],[833,4],[1246,4],[1531,4],[2124,4]]},"31":{"position":[[52,4],[160,4],[2262,4]]},"37":{"position":[[158,4]]},"43":{"position":[[1400,4]]},"47":{"position":[[1466,4],[3432,4],[5429,4]]},"49":{"position":[[183,4],[483,4],[794,4]]},"59":{"position":[[96,4]]},"63":{"position":[[38,4]]},"69":{"position":[[1556,7],[1682,4],[3333,4],[4478,4],[5140,4]]},"71":{"position":[[1177,4]]},"77":{"position":[[2818,4],[4948,4]]},"83":{"position":[[1041,4]]},"87":{"position":[[392,4],[1014,4]]},"93":{"position":[[593,6]]},"99":{"position":[[2039,4]]},"103":{"position":[[33,4]]},"105":{"position":[[58,4]]},"113":{"position":[[555,4]]},"117":{"position":[[397,4]]},"119":{"position":[[73,4]]},"133":{"position":[[447,4]]},"135":{"position":[[1342,4]]},"137":{"position":[[1200,4]]},"141":{"position":[[852,4]]},"143":{"position":[[265,4],[359,4]]},"195":{"position":[[2562,4]]},"209":{"position":[[52,4],[460,4]]},"213":{"position":[[716,4]]},"225":{"position":[[445,4]]},"227":{"position":[[747,4]]},"235":{"position":[[1073,4]]},"239":{"position":[[387,7],[418,4]]},"243":{"position":[[1756,7]]},"251":{"position":[[1091,4]]},"253":{"position":[[1051,4]]},"257":{"position":[[4230,4]]},"260":{"position":[[593,4]]},"270":{"position":[[92,4]]},"274":{"position":[[544,4]]},"276":{"position":[[233,7]]},"304":{"position":[[445,4]]},"314":{"position":[[272,4]]},"330":{"position":[[86,6]]},"338":{"position":[[202,4],[332,4]]},"342":{"position":[[129,4]]},"344":{"position":[[4547,4]]},"346":{"position":[[396,4]]},"352":{"position":[[2333,6]]},"366":{"position":[[293,6],[3129,6],[3266,4]]},"376":{"position":[[2216,4],[2557,4],[3296,4],[6216,4]]},"378":{"position":[[798,4],[3878,4]]},"386":{"position":[[79,4],[348,7]]},"392":{"position":[[504,6]]},"400":{"position":[[1906,4]]},"410":{"position":[[48,4]]},"414":{"position":[[313,4]]},"416":{"position":[[117,6]]},"420":{"position":[[958,7]]},"424":{"position":[[641,4],[808,4]]},"426":{"position":[[89,5]]},"432":{"position":[[25,4]]},"450":{"position":[[5448,4]]},"462":{"position":[[1136,4],[1404,4]]},"464":{"position":[[748,5],[1790,4],[1962,4]]},"466":{"position":[[220,4]]},"468":{"position":[[136,4],[854,4],[2441,4]]},"484":{"position":[[692,4]]},"488":{"position":[[143,4]]},"494":{"position":[[364,4]]},"506":{"position":[[2494,4]]},"508":{"position":[[355,6],[438,4],[2100,4],[4091,4],[4270,4]]},"510":{"position":[[2569,6]]},"526":{"position":[[1387,4]]},"534":{"position":[[143,4]]},"536":{"position":[[1258,4]]},"544":{"position":[[125,4]]},"554":{"position":[[248,4]]},"556":{"position":[[871,4]]},"568":{"position":[[169,4],[1449,4]]},"570":{"position":[[655,4],[1032,4]]},"574":{"position":[[2926,5]]},"580":{"position":[[1571,4]]},"584":{"position":[[1157,7]]},"588":{"position":[[1162,5]]},"594":{"position":[[1675,4]]},"612":{"position":[[125,4]]},"616":{"position":[[651,4]]},"618":{"position":[[383,4]]},"624":{"position":[[1006,4]]},"626":{"position":[[5924,4],[7032,4]]},"628":{"position":[[28,4]]},"630":{"position":[[62,4]]},"634":{"position":[[2046,4],[2885,4]]},"640":{"position":[[749,6],[1328,4],[1689,4]]},"644":{"position":[[7,4]]},"648":{"position":[[681,4],[1012,4],[1585,4]]},"654":{"position":[[910,4],[4349,4]]},"656":{"position":[[1086,4]]},"658":{"position":[[2576,4]]},"682":{"position":[[915,4]]},"684":{"position":[[1415,6]]},"692":{"position":[[1295,7]]},"696":{"position":[[2720,4],[2854,4]]},"700":{"position":[[666,4]]},"702":{"position":[[1041,4],[1135,4]]},"714":{"position":[[2298,7],[2412,4],[3404,4],[3976,4]]},"716":{"position":[[3752,4],[3789,5],[5223,4],[5403,4]]},"718":{"position":[[188,4],[1111,4],[1780,4]]},"720":{"position":[[4334,4],[5021,4]]},"724":{"position":[[880,4]]},"732":{"position":[[1117,4],[1207,4]]},"736":{"position":[[322,4]]},"738":{"position":[[815,6],[3321,4],[4350,6],[4439,4]]},"740":{"position":[[489,4]]},"742":{"position":[[663,4]]},"754":{"position":[[1245,4]]},"762":{"position":[[425,4]]},"800":{"position":[[296,4]]},"802":{"position":[[783,4],[2037,4]]},"806":{"position":[[1872,4],[2701,7]]},"808":{"position":[[173,4],[273,5],[999,4]]},"816":{"position":[[1010,4]]},"818":{"position":[[652,6]]},"830":{"position":[[572,4]]},"838":{"position":[[1667,4],[2488,4]]},"850":{"position":[[2401,4]]},"864":{"position":[[220,4]]},"870":{"position":[[468,4]]},"888":{"position":[[384,4]]},"896":{"position":[[1266,6],[1315,4],[2702,4]]},"898":{"position":[[161,4]]},"902":{"position":[[1037,4],[1301,4]]},"904":{"position":[[10,4]]},"973":{"position":[[0,4]]},"989":{"position":[[77,4]]},"995":{"position":[[221,4]]},"1035":{"position":[[3351,4]]}}}],["needed5",{"_index":1993,"t":{"260":{"position":[[402,7]]}}}],["needlessli",{"_index":4692,"t":{"794":{"position":[[1022,10]]}}}],["neg",{"_index":2360,"t":{"352":{"position":[[1888,10]]},"572":{"position":[[1205,8]]}}}],["negat",{"_index":2329,"t":{"344":{"position":[[3204,8],[3262,6]]}}}],["neighbor",{"_index":3044,"t":{"454":{"position":[[665,9]]}}}],["neil",{"_index":4682,"t":{"784":{"position":[[39,4],[147,4]]}}}],["neil'",{"_index":4683,"t":{"786":{"position":[[36,6]]}}}],["neovim",{"_index":5295,"t":{"969":{"position":[[367,7]]}}}],["nepal",{"_index":1421,"t":{"117":{"position":[[700,5]]}}}],["ness",{"_index":4730,"t":{"802":{"position":[[2518,4]]},"806":{"position":[[3470,8]]}}}],["nest",{"_index":3602,"t":{"574":{"position":[[3032,6]]},"834":{"position":[[3518,6],[3610,6],[3740,6],[3850,6],[3938,6],[4065,6],[4156,6],[4201,6]]},"852":{"position":[[2162,7],[2174,6]]},"1029":{"position":[[1063,4]]}}}],["network",{"_index":2026,"t":{"272":{"position":[[171,7]]},"274":{"position":[[151,7]]},"282":{"position":[[1976,8]]},"292":{"position":[[1016,8],[1034,7],[1066,7],[1554,7]]},"884":{"position":[[153,7]]},"906":{"position":[[138,7]]},"910":{"position":[[418,8],[559,7],[1566,7],[1670,7]]},"912":{"position":[[352,7]]}}}],["never",{"_index":1101,"t":{"77":{"position":[[12,5]]},"95":{"position":[[2641,5]]},"205":{"position":[[1482,5]]},"396":{"position":[[1767,5]]},"558":{"position":[[2229,5]]},"834":{"position":[[3553,5]]}}}],["new",{"_index":175,"t":{"17":{"position":[[248,3]]},"23":{"position":[[1276,3]]},"47":{"position":[[5370,4]]},"69":{"position":[[3713,5],[3731,3],[5865,3]]},"71":{"position":[[1965,3]]},"91":{"position":[[1746,3],[2003,3]]},"111":{"position":[[293,3]]},"113":{"position":[[725,3]]},"115":{"position":[[221,3],[404,3]]},"123":{"position":[[36,3]]},"141":{"position":[[360,3]]},"183":{"position":[[238,3]]},"225":{"position":[[194,3]]},"241":{"position":[[104,3]]},"253":{"position":[[390,3]]},"255":{"position":[[1618,3]]},"257":{"position":[[2681,3],[4191,3]]},"262":{"position":[[3009,3]]},"290":{"position":[[2594,3]]},"302":{"position":[[629,3]]},"366":{"position":[[3413,3]]},"380":{"position":[[632,3]]},"400":{"position":[[1174,3]]},"418":{"position":[[24,3]]},"424":{"position":[[1325,3]]},"428":{"position":[[1071,3]]},"434":{"position":[[210,3],[1460,3],[2082,3]]},"446":{"position":[[2872,3]]},"476":{"position":[[299,3]]},"486":{"position":[[720,3]]},"508":{"position":[[2891,3],[3083,3],[3383,3]]},"512":{"position":[[9,3],[1458,3]]},"514":{"position":[[196,3]]},"520":{"position":[[822,3]]},"538":{"position":[[396,3],[1060,3]]},"574":{"position":[[768,3],[3340,3]]},"626":{"position":[[5650,3],[7289,3]]},"638":{"position":[[209,3]]},"648":{"position":[[2495,3]]},"654":{"position":[[2601,4],[3570,3]]},"658":{"position":[[1177,3],[2024,3],[2426,4],[2557,3]]},"672":{"position":[[762,3],[799,3],[901,3]]},"678":{"position":[[136,3]]},"680":{"position":[[1360,3]]},"682":{"position":[[1309,3],[1330,3],[1359,3],[1419,3],[2769,3],[2790,3]]},"684":{"position":[[439,3],[461,3],[903,3],[925,3]]},"688":{"position":[[89,3],[397,3],[488,3],[651,4],[706,3],[755,3],[802,3],[1229,3],[1741,3]]},"692":{"position":[[116,3],[476,3],[520,3],[819,3],[1481,3],[1578,3],[2393,3]]},"696":{"position":[[1291,3],[2148,3],[3528,3],[3989,3],[4211,3],[5272,3]]},"700":{"position":[[2939,3]]},"702":{"position":[[2347,3],[2383,3]]},"704":{"position":[[651,3],[1270,3]]},"714":{"position":[[3886,3]]},"716":{"position":[[3431,3],[4408,3]]},"718":{"position":[[1537,3]]},"722":{"position":[[975,3],[1036,3]]},"744":{"position":[[1711,3],[2552,3]]},"762":{"position":[[529,3]]},"764":{"position":[[14,3],[177,5],[408,3],[437,3],[582,5],[1378,5],[1456,3],[2078,3],[2331,3]]},"770":{"position":[[505,3],[543,3]]},"772":{"position":[[879,3]]},"778":{"position":[[698,3],[1942,3]]},"804":{"position":[[234,3],[358,3],[1653,3]]},"820":{"position":[[565,3]]},"828":{"position":[[998,3]]},"830":{"position":[[277,3],[379,3],[975,3],[1029,3],[1062,3]]},"832":{"position":[[1198,3],[1218,3],[1251,3]]},"834":{"position":[[1725,3],[1904,3],[1962,3],[3778,3]]},"838":{"position":[[568,3],[667,3],[725,3]]},"842":{"position":[[263,3],[755,3],[777,3],[1066,3]]},"870":{"position":[[209,5],[227,5],[590,3],[716,3]]},"878":{"position":[[934,3]]},"884":{"position":[[728,3]]},"892":{"position":[[2147,3],[2311,3]]},"896":{"position":[[2516,3]]},"898":{"position":[[725,3]]},"914":{"position":[[351,3]]},"927":{"position":[[604,3],[1223,3]]},"939":{"position":[[708,3]]},"975":{"position":[[175,3]]}}}],["new_branch_nam",{"_index":4137,"t":{"688":{"position":[[3436,18]]}}}],["new_length",{"_index":4812,"t":{"806":{"position":[[1992,10]]}}}],["new_nam",{"_index":4221,"t":{"704":{"position":[[1381,10],[1426,9]]},"927":{"position":[[1334,10],[1379,9]]}}}],["newer",{"_index":3549,"t":{"562":{"position":[[764,5]]}}}],["newfil",{"_index":4320,"t":{"718":{"position":[[1375,7],[1558,7],[2174,7]]}}}],["newfound",{"_index":903,"t":{"47":{"position":[[8568,8]]}}}],["newli",{"_index":3903,"t":{"654":{"position":[[1665,5]]}}}],["newlin",{"_index":2319,"t":{"344":{"position":[[634,9]]},"390":{"position":[[661,8]]},"448":{"position":[[716,7]]},"464":{"position":[[2064,9]]},"498":{"position":[[239,8]]},"508":{"position":[[3445,7],[4111,7],[4867,7],[5032,7]]},"582":{"position":[[652,8]]},"588":{"position":[[1574,9],[2359,7]]},"596":{"position":[[717,8],[1279,7]]},"610":{"position":[[240,7]]},"612":{"position":[[1212,9]]},"714":{"position":[[714,7]]},"802":{"position":[[3067,7]]},"1033":{"position":[[2264,7],[2696,8],[4724,9]]}}}],["newline3",{"_index":3684,"t":{"588":{"position":[[2496,8]]}}}],["newlines.old_ifs=$ifsifs=$'\\n",{"_index":3679,"t":{"588":{"position":[[1796,31]]}}}],["next",{"_index":284,"t":{"21":{"position":[[2302,4]]},"31":{"position":[[2371,4]]},"43":{"position":[[656,4]]},"47":{"position":[[3858,4]]},"57":{"position":[[1380,4]]},"59":{"position":[[250,4]]},"61":{"position":[[619,4]]},"63":{"position":[[347,4]]},"69":{"position":[[2501,4],[3442,4],[3675,4]]},"77":{"position":[[4860,4]]},"83":{"position":[[1006,4]]},"137":{"position":[[718,4]]},"159":{"position":[[91,4],[119,4]]},"165":{"position":[[54,4]]},"179":{"position":[[268,4]]},"195":{"position":[[1703,5]]},"221":{"position":[[222,4]]},"243":{"position":[[1926,5]]},"251":{"position":[[1380,4]]},"255":{"position":[[2336,4]]},"348":{"position":[[781,4]]},"352":{"position":[[539,4]]},"356":{"position":[[305,4]]},"366":{"position":[[4403,4]]},"372":{"position":[[1410,4]]},"424":{"position":[[475,4]]},"428":{"position":[[358,4]]},"440":{"position":[[385,4]]},"442":{"position":[[248,4]]},"448":{"position":[[4,4]]},"450":{"position":[[4,4]]},"454":{"position":[[1258,4]]},"458":{"position":[[1591,4]]},"466":{"position":[[1417,4]]},"474":{"position":[[471,4]]},"508":{"position":[[4413,4]]},"514":{"position":[[656,4]]},"534":{"position":[[1730,5]]},"544":{"position":[[535,5],[978,5]]},"546":{"position":[[169,4]]},"564":{"position":[[736,4]]},"566":{"position":[[1041,5]]},"576":{"position":[[133,4]]},"586":{"position":[[2440,4]]},"602":{"position":[[321,4],[552,4]]},"604":{"position":[[1183,4],[1476,4]]},"612":{"position":[[296,4]]},"614":{"position":[[281,4]]},"624":{"position":[[1083,4]]},"626":{"position":[[3936,4]]},"640":{"position":[[1607,4]]},"650":{"position":[[372,4]]},"656":{"position":[[712,6]]},"670":{"position":[[1085,4]]},"674":{"position":[[524,4]]},"682":{"position":[[609,4]]},"704":{"position":[[262,4]]},"708":{"position":[[640,4],[861,4],[876,4]]},"720":{"position":[[1742,5],[2010,5],[8316,4]]},"726":{"position":[[393,4]]},"738":{"position":[[2061,5],[3593,4],[7775,4]]},"748":{"position":[[351,4]]},"766":{"position":[[1251,4],[1347,4]]},"774":{"position":[[301,4],[401,4],[607,4],[1425,4],[1483,4]]},"780":{"position":[[421,4]]},"790":{"position":[[242,4]]},"806":{"position":[[597,4],[1411,4]]},"828":{"position":[[1637,4],[2095,4]]},"832":{"position":[[1431,4]]},"834":{"position":[[4016,4]]},"840":{"position":[[546,4]]},"842":{"position":[[230,4],[500,4],[1279,4],[1413,4]]},"844":{"position":[[293,4]]},"878":{"position":[[2159,4]]},"880":{"position":[[111,4]]},"896":{"position":[[2503,4],[2723,4]]},"1037":{"position":[[640,4]]}}}],["nginx",{"_index":3159,"t":{"470":{"position":[[773,7]]}}}],["nice",{"_index":1965,"t":{"257":{"position":[[3972,4]]},"394":{"position":[[1584,4]]},"434":{"position":[[1913,4]]},"450":{"position":[[4760,4]]},"452":{"position":[[211,4]]},"462":{"position":[[539,4]]},"664":{"position":[[1912,4]]},"688":{"position":[[2593,4]]},"806":{"position":[[3538,4]]},"870":{"position":[[21,4]]},"1027":{"position":[[4771,4]]}}}],["nicer",{"_index":1403,"t":{"113":{"position":[[673,6]]},"806":{"position":[[1480,5]]}}}],["niel",{"_index":5268,"t":{"945":{"position":[[92,5]]},"969":{"position":[[380,4]]}}}],["nifti",{"_index":1164,"t":{"81":{"position":[[1279,5]]}}}],["night",{"_index":3027,"t":{"454":{"position":[[191,5],[207,5]]}}}],["nightli",{"_index":1920,"t":{"255":{"position":[[379,9]]}}}],["nine",{"_index":879,"t":{"47":{"position":[[7459,5]]},"396":{"position":[[772,5]]}}}],["nl",{"_index":3163,"t":{"472":{"position":[[176,7]]}}}],["nn",{"_index":3581,"t":{"572":{"position":[[898,6]]}}}],["nnn",{"_index":4249,"t":{"714":{"position":[[1504,4],[1557,4]]}}}],["nobang",{"_index":5063,"t":{"878":{"position":[[1482,7]]}}}],["node",{"_index":2570,"t":{"384":{"position":[[168,4]]}}}],["node.j",{"_index":922,"t":{"49":{"position":[[799,7]]},"91":{"position":[[2932,7]]},"185":{"position":[[99,7]]},"430":{"position":[[1194,8],[1248,10]]},"796":{"position":[[1291,7]]}}}],["nodej",{"_index":4695,"t":{"796":{"position":[[1282,6]]}}}],["nois",{"_index":1541,"t":{"177":{"position":[[176,5]]},"995":{"position":[[341,5]]}}}],["noisi",{"_index":4838,"t":{"810":{"position":[[518,5]]}}}],["non",{"_index":702,"t":{"47":{"position":[[822,4]]},"49":{"position":[[65,3]]},"93":{"position":[[721,3]]},"107":{"position":[[603,3]]},"205":{"position":[[305,3]]},"219":{"position":[[1067,3]]},"320":{"position":[[167,3]]},"344":{"position":[[4923,3],[5001,3],[5068,3]]},"366":{"position":[[4127,3]]},"442":{"position":[[1300,3],[1395,3],[1456,3]]},"536":{"position":[[548,3],[1218,3]]},"538":{"position":[[2459,3]]},"550":{"position":[[1731,3]]},"560":{"position":[[222,3]]},"630":{"position":[[249,3]]},"634":{"position":[[119,3],[188,3],[1059,3],[1233,3],[1319,3],[1936,3],[2166,3],[2426,3],[2456,3]]},"636":{"position":[[2174,3]]},"638":{"position":[[727,3]]},"642":{"position":[[20,3]]},"644":{"position":[[41,3]]},"650":{"position":[[147,3],[181,3],[517,3]]},"714":{"position":[[1640,3],[1690,3]]},"716":{"position":[[5287,4],[5508,4]]},"734":{"position":[[1153,4]]},"738":{"position":[[2012,3]]},"760":{"position":[[1329,3]]},"908":{"position":[[2362,3],[3238,3]]}}}],["none",{"_index":1614,"t":{"195":{"position":[[921,4]]},"197":{"position":[[450,4]]},"344":{"position":[[3681,4]]},"588":{"position":[[1252,4]]},"626":{"position":[[448,6]]}}}],["nonetheless",{"_index":3583,"t":{"572":{"position":[[1408,11]]}}}],["nopen",{"_index":4026,"t":{"666":{"position":[[1529,11]]}}}],["norc",{"_index":1041,"t":{"69":{"position":[[2255,4]]}}}],["normal",{"_index":569,"t":{"33":{"position":[[154,8]]},"43":{"position":[[773,8],[1378,8]]},"47":{"position":[[7257,8]]},"57":{"position":[[108,8]]},"77":{"position":[[3669,8]]},"139":{"position":[[720,8]]},"171":{"position":[[151,6]]},"193":{"position":[[525,8]]},"205":{"position":[[414,6]]},"219":{"position":[[822,8]]},"225":{"position":[[641,8]]},"229":{"position":[[918,7]]},"235":{"position":[[855,6],[944,8]]},"247":{"position":[[427,8]]},"257":{"position":[[2654,8]]},"262":{"position":[[1313,8]]},"274":{"position":[[327,6]]},"280":{"position":[[618,8]]},"282":{"position":[[361,9],[2126,9]]},"284":{"position":[[232,8]]},"300":{"position":[[483,8]]},"310":{"position":[[482,8]]},"442":{"position":[[1228,8]]},"550":{"position":[[1590,8]]},"558":{"position":[[1500,6]]},"610":{"position":[[834,8]]},"622":{"position":[[1092,8]]},"636":{"position":[[150,8],[662,8],[1130,8],[2117,8]]},"638":{"position":[[570,8]]},"640":{"position":[[1674,8],[1718,8]]},"646":{"position":[[268,8],[521,8]]},"648":{"position":[[2193,6]]},"702":{"position":[[1105,8],[1202,8]]},"708":{"position":[[986,6]]},"716":{"position":[[2140,9],[2992,9],[3158,8]]},"722":{"position":[[161,8],[237,8],[332,8]]},"732":{"position":[[485,8],[788,8]]},"738":{"position":[[3265,8]]},"742":{"position":[[516,6]]},"758":{"position":[[307,6]]},"764":{"position":[[768,6]]},"816":{"position":[[365,8]]},"834":{"position":[[39,8],[4305,8]]},"852":{"position":[[1477,6]]},"868":{"position":[[814,8]]},"888":{"position":[[61,8]]},"904":{"position":[[566,8]]},"908":{"position":[[345,8]]},"985":{"position":[[152,8]]}}}],["notat",{"_index":2832,"t":{"436":{"position":[[78,9]]},"490":{"position":[[338,8]]},"558":{"position":[[1599,9]]},"626":{"position":[[4656,10]]},"718":{"position":[[675,8],[928,9]]}}}],["note",{"_index":1039,"t":{"69":{"position":[[2221,4]]},"107":{"position":[[1214,4]]},"133":{"position":[[232,4],[300,4]]},"137":{"position":[[753,4],[963,4]]},"193":{"position":[[446,4]]},"195":{"position":[[868,4],[1722,4]]},"197":{"position":[[304,4]]},"215":{"position":[[555,4]]},"217":{"position":[[1082,4]]},"219":{"position":[[487,4],[711,4],[1510,4]]},"235":{"position":[[389,4]]},"255":{"position":[[1484,4]]},"286":{"position":[[464,4]]},"304":{"position":[[458,5]]},"336":{"position":[[1069,4]]},"340":{"position":[[894,4]]},"366":{"position":[[4471,4]]},"376":{"position":[[89,4]]},"378":{"position":[[4395,4],[4683,4]]},"386":{"position":[[1299,4]]},"394":{"position":[[1436,4]]},"402":{"position":[[1231,4]]},"442":{"position":[[1501,4]]},"454":{"position":[[1200,4]]},"484":{"position":[[1023,4]]},"490":{"position":[[1595,4]]},"496":{"position":[[289,4]]},"502":{"position":[[765,5]]},"508":{"position":[[2417,4],[3182,4]]},"510":{"position":[[2916,4]]},"512":{"position":[[1589,4]]},"520":{"position":[[240,4]]},"556":{"position":[[852,4]]},"574":{"position":[[2947,4],[3471,4]]},"600":{"position":[[952,4]]},"608":{"position":[[672,4],[967,4]]},"668":{"position":[[2152,4]]},"684":{"position":[[974,4]]},"714":{"position":[[3461,4]]},"720":{"position":[[2170,4],[6532,4]]},"722":{"position":[[2235,4]]},"738":{"position":[[1088,5],[3175,4],[4805,4]]},"742":{"position":[[410,4]]},"744":{"position":[[526,5]]},"746":{"position":[[305,4],[1226,4]]},"778":{"position":[[48,5],[86,5],[672,4]]},"802":{"position":[[3184,4]]},"808":{"position":[[1348,4],[1612,4]]},"862":{"position":[[363,4]]},"866":{"position":[[361,4]]},"872":{"position":[[76,5]]},"908":{"position":[[1781,4]]},"931":{"position":[[101,4]]},"975":{"position":[[372,5]]},"985":{"position":[[65,4]]},"1027":{"position":[[4207,4]]},"1033":{"position":[[781,4]]},"1035":{"position":[[1824,4]]}}}],["noth",{"_index":393,"t":{"23":{"position":[[1775,7]]},"47":{"position":[[5812,7]]},"121":{"position":[[533,7]]},"147":{"position":[[585,7]]},"195":{"position":[[558,9]]},"197":{"position":[[433,8]]},"227":{"position":[[412,7]]},"257":{"position":[[5638,7],[5954,8],[6091,10],[6554,8]]},"282":{"position":[[2268,7]]},"308":{"position":[[658,7]]},"366":{"position":[[4439,7]]},"474":{"position":[[707,8]]},"586":{"position":[[1092,7]]},"616":{"position":[[731,9],[769,9]]},"654":{"position":[[3825,7]]},"682":{"position":[[3537,7]]},"802":{"position":[[1316,7]]},"834":{"position":[[770,7]]}}}],["notic",{"_index":629,"t":{"43":{"position":[[26,6]]},"105":{"position":[[264,6]]},"113":{"position":[[15,7]]},"121":{"position":[[1052,6]]},"137":{"position":[[54,7]]},"257":{"position":[[5306,6]]},"344":{"position":[[3787,6]]},"352":{"position":[[1338,10]]},"366":{"position":[[1247,6]]},"368":{"position":[[495,6]]},"376":{"position":[[4661,7]]},"434":{"position":[[862,6]]},"468":{"position":[[18,7]]},"484":{"position":[[1504,11]]},"490":{"position":[[1833,7]]},"500":{"position":[[397,6]]},"506":{"position":[[2011,6]]},"522":{"position":[[1070,6],[1659,6]]},"528":{"position":[[440,6]]},"580":{"position":[[1195,6]]},"664":{"position":[[720,6]]},"702":{"position":[[1242,6]]},"714":{"position":[[2747,6],[3839,7]]},"716":{"position":[[3059,6]]},"720":{"position":[[4880,6]]},"738":{"position":[[3956,6]]},"760":{"position":[[1401,7],[2258,6]]},"826":{"position":[[17,6]]},"828":{"position":[[1404,6]]},"852":{"position":[[832,6],[2079,6]]},"868":{"position":[[587,6]]},"906":{"position":[[179,6]]},"1027":{"position":[[6311,7],[7362,6],[8399,6]]}}}],["notif",{"_index":5248,"t":{"914":{"position":[[402,13]]}}}],["notifi",{"_index":1786,"t":{"229":{"position":[[447,8]]},"914":{"position":[[327,8]]}}}],["notori",{"_index":2249,"t":{"336":{"position":[[103,10]]}}}],["novic",{"_index":5269,"t":{"947":{"position":[[174,6]]}}}],["now",{"_index":317,"t":{"21":{"position":[[3386,3],[3970,3]]},"23":{"position":[[944,3],[1233,3]]},"25":{"position":[[0,3],[485,3],[1087,3],[1571,3]]},"29":{"position":[[677,3],[1002,3]]},"31":{"position":[[834,3],[1884,3],[2248,3]]},"45":{"position":[[808,4],[919,3]]},"49":{"position":[[384,3],[562,3]]},"51":{"position":[[631,3],[1195,3]]},"57":{"position":[[989,4]]},"59":{"position":[[0,3]]},"67":{"position":[[1098,3]]},"69":{"position":[[1890,3],[5490,3],[5673,3],[6495,3]]},"71":{"position":[[1863,4]]},"75":{"position":[[161,4]]},"77":{"position":[[831,3],[3557,3],[3881,3],[5098,4],[5103,3]]},"81":{"position":[[2507,3]]},"83":{"position":[[988,3]]},"91":{"position":[[521,3]]},"95":{"position":[[186,3]]},"99":{"position":[[331,3],[805,3],[1891,3],[1960,4],[2032,3]]},"101":{"position":[[187,4],[279,3]]},"103":{"position":[[6,3],[302,3],[374,3]]},"105":{"position":[[0,3],[961,3]]},"107":{"position":[[334,3]]},"111":{"position":[[168,4]]},"113":{"position":[[494,3],[680,3]]},"115":{"position":[[429,3],[866,3]]},"117":{"position":[[390,3]]},"119":{"position":[[0,3]]},"121":{"position":[[820,4]]},"131":{"position":[[503,4]]},"133":{"position":[[1021,3]]},"135":{"position":[[758,4]]},"137":{"position":[[834,4]]},"139":{"position":[[1109,3]]},"159":{"position":[[59,3]]},"175":{"position":[[353,3],[1159,3],[2708,3]]},"189":{"position":[[2458,3]]},"207":{"position":[[272,3]]},"215":{"position":[[0,3]]},"225":{"position":[[901,3],[1029,4]]},"227":{"position":[[21,3],[741,3]]},"233":{"position":[[493,3]]},"235":{"position":[[566,3]]},"251":{"position":[[934,3],[1626,3]]},"253":{"position":[[1655,3],[3737,4]]},"255":{"position":[[0,3]]},"257":{"position":[[305,3],[633,3],[1591,3],[6662,3]]},"260":{"position":[[627,3],[923,3]]},"278":{"position":[[1002,3]]},"284":{"position":[[0,3]]},"288":{"position":[[2042,4]]},"298":{"position":[[110,3]]},"302":{"position":[[693,3],[722,3]]},"306":{"position":[[114,4],[740,3]]},"310":{"position":[[6,3]]},"340":{"position":[[779,3]]},"342":{"position":[[865,3],[1809,3]]},"344":{"position":[[1931,3],[2753,3],[3927,3]]},"348":{"position":[[284,3],[831,4]]},"352":{"position":[[503,3]]},"356":{"position":[[267,3]]},"366":{"position":[[1526,3],[2455,3],[3259,3]]},"370":{"position":[[1038,3]]},"372":{"position":[[1053,3]]},"376":{"position":[[298,4],[1564,3],[2631,3],[2861,3],[3244,3],[3514,3],[3860,3],[4220,3],[4642,3],[6031,3]]},"378":{"position":[[1266,3],[2715,7],[2965,3]]},"394":{"position":[[1503,3]]},"396":{"position":[[0,3],[845,3]]},"402":{"position":[[1436,3]]},"424":{"position":[[1186,3],[1724,3]]},"428":{"position":[[304,3],[723,3]]},"438":{"position":[[1055,3]]},"442":{"position":[[531,3]]},"450":{"position":[[1428,3]]},"462":{"position":[[1236,3],[2297,3],[2763,3]]},"464":{"position":[[1164,4],[1565,3]]},"466":{"position":[[635,3]]},"468":{"position":[[29,3]]},"476":{"position":[[25,3]]},"488":{"position":[[1529,3]]},"508":{"position":[[2333,3]]},"512":{"position":[[1246,3]]},"528":{"position":[[475,4]]},"534":{"position":[[2204,3]]},"536":{"position":[[763,3]]},"538":{"position":[[696,3],[857,3],[1278,3],[1399,4],[1738,3],[2477,3]]},"544":{"position":[[1572,3],[1966,3]]},"556":{"position":[[672,3]]},"574":{"position":[[0,3],[2484,3]]},"588":{"position":[[2010,3]]},"612":{"position":[[394,4],[968,3],[1642,3]]},"618":{"position":[[37,3]]},"624":{"position":[[1633,3]]},"626":{"position":[[5477,3]]},"654":{"position":[[3708,3]]},"658":{"position":[[1292,3],[1335,3]]},"660":{"position":[[619,3],[1468,3]]},"662":{"position":[[65,3]]},"664":{"position":[[891,3]]},"668":{"position":[[2966,3]]},"674":{"position":[[384,3]]},"678":{"position":[[830,3]]},"680":{"position":[[1031,3],[1308,3]]},"682":{"position":[[3,3],[1385,3],[3021,3],[3746,3]]},"684":{"position":[[126,3],[1988,3],[2086,3],[2762,3]]},"686":{"position":[[1096,3]]},"688":{"position":[[741,3],[1686,3],[3132,3]]},"692":{"position":[[562,3],[1022,3],[2125,3]]},"696":{"position":[[1517,3],[2246,3],[4545,3],[5258,3]]},"714":{"position":[[1837,3]]},"716":{"position":[[431,3]]},"720":{"position":[[6864,3],[7453,3]]},"726":{"position":[[336,3]]},"738":{"position":[[980,3],[4568,3],[7952,3]]},"742":{"position":[[0,3],[652,3],[1198,4]]},"744":{"position":[[1214,3],[1342,3],[2023,3],[2315,3]]},"746":{"position":[[976,7],[1626,6],[1653,3]]},"760":{"position":[[3183,3],[3394,3]]},"764":{"position":[[3,3],[1192,3],[2545,3]]},"766":{"position":[[205,3],[388,3]]},"770":{"position":[[0,3],[1553,3]]},"774":{"position":[[479,3]]},"796":{"position":[[1685,3]]},"802":{"position":[[1717,4],[3095,3],[3304,3],[3392,3]]},"804":{"position":[[0,3],[1060,3],[2796,3]]},"806":{"position":[[1241,4],[2991,3]]},"808":{"position":[[739,3],[1159,3]]},"824":{"position":[[0,3]]},"828":{"position":[[64,3]]},"830":{"position":[[662,3]]},"832":{"position":[[592,3]]},"834":{"position":[[684,3]]},"838":{"position":[[743,3],[1476,3],[2476,3]]},"852":{"position":[[613,3],[739,3]]},"876":{"position":[[1082,3]]},"888":{"position":[[638,3]]},"890":{"position":[[182,3],[565,3],[731,3],[2469,4]]},"892":{"position":[[300,4],[2098,4],[2134,3],[2292,3]]},"894":{"position":[[302,4],[1257,3],[1411,3],[1482,3]]},"896":{"position":[[636,3],[749,3],[1273,3]]},"898":{"position":[[0,3],[636,3],[2647,3],[2982,3]]},"902":{"position":[[982,3]]},"904":{"position":[[562,3]]},"906":{"position":[[654,3]]},"908":{"position":[[1944,3]]},"910":{"position":[[695,3]]},"1019":{"position":[[1585,3]]},"1027":{"position":[[1635,4],[5232,3]]},"1033":{"position":[[1467,3]]}}}],["nowaday",{"_index":4433,"t":{"732":{"position":[[1279,9]]},"796":{"position":[[1614,8]]}}}],["nowher",{"_index":1970,"t":{"257":{"position":[[4827,7]]}}}],["npm",{"_index":918,"t":{"49":{"position":[[721,3]]}}}],["nt",{"_index":3548,"t":{"562":{"position":[[727,2]]}}}],["nu",{"_index":5302,"t":{"975":{"position":[[535,3],[604,2]]}}}],["nuanc",{"_index":2379,"t":{"362":{"position":[[63,6]]}}}],["nugget",{"_index":2067,"t":{"278":{"position":[[1210,6]]}}}],["nul",{"_index":852,"t":{"47":{"position":[[6823,3]]},"464":{"position":[[1753,3]]},"474":{"position":[[619,4]]}}}],["null",{"_index":2001,"t":{"262":{"position":[[1875,6]]},"390":{"position":[[391,5]]},"464":{"position":[[1546,6],[1624,6],[1670,6]]},"474":{"position":[[697,6]]},"534":{"position":[[2004,6],[2329,4]]},"586":{"position":[[1498,4],[1877,4]]},"1027":{"position":[[2297,4],[2422,4],[3198,4]]}}}],["nullglob",{"_index":3664,"t":{"586":{"position":[[1479,10],[1760,10]]},"616":{"position":[[979,8]]}}}],["nullglobfor",{"_index":3667,"t":{"586":{"position":[[1541,11]]}}}],["num",{"_index":791,"t":{"47":{"position":[[4558,3]]},"390":{"position":[[237,4],[246,4]]},"604":{"position":[[378,3]]}}}],["num\"don",{"_index":3745,"t":{"604":{"position":[[408,10]]}}}],["number",{"_index":477,"t":{"29":{"position":[[107,6]]},"47":{"position":[[52,7],[101,6],[130,7],[1745,6],[3765,6],[3843,6],[4373,7]]},"61":{"position":[[12,6]]},"77":{"position":[[4409,6]]},"91":{"position":[[701,6],[753,6],[3378,6]]},"179":{"position":[[261,6],[293,6],[326,6]]},"191":{"position":[[73,6]]},"213":{"position":[[187,6]]},"229":{"position":[[528,7],[690,6]]},"243":{"position":[[311,6],[371,6],[403,6],[437,6]]},"249":{"position":[[739,6]]},"253":{"position":[[1219,6]]},"257":{"position":[[2573,7]]},"260":{"position":[[893,6]]},"288":{"position":[[1888,6]]},"340":{"position":[[270,6],[448,6]]},"342":{"position":[[500,6],[553,6],[846,6],[1571,6]]},"344":{"position":[[58,6],[925,7]]},"376":{"position":[[6435,8]]},"390":{"position":[[1783,7],[1820,7]]},"394":{"position":[[1688,7],[2422,6]]},"396":{"position":[[728,6]]},"400":{"position":[[1362,6]]},"402":{"position":[[376,9],[412,6],[1485,7]]},"404":{"position":[[942,6]]},"406":{"position":[[716,7]]},"408":{"position":[[129,6]]},"410":{"position":[[492,6]]},"414":{"position":[[267,6]]},"418":{"position":[[205,6],[275,6]]},"420":{"position":[[765,7]]},"442":{"position":[[1182,7]]},"446":{"position":[[1122,6],[1247,6],[2593,6]]},"450":{"position":[[3011,6]]},"458":{"position":[[32,6],[217,6]]},"466":{"position":[[1056,6]]},"474":{"position":[[637,6],[817,6]]},"490":{"position":[[677,6],[1991,7]]},"506":{"position":[[262,6],[2120,6],[2264,6]]},"508":{"position":[[3700,7],[3746,6],[5365,6]]},"510":{"position":[[348,7],[406,7],[446,7],[595,7],[619,7],[703,7],[727,7],[968,7],[1008,7],[1169,7],[1193,7]]},"512":{"position":[[461,6],[526,6],[830,6],[897,6],[1140,6],[1185,6]]},"524":{"position":[[17,6]]},"526":{"position":[[12,6],[330,6],[555,6],[1063,6],[1485,6],[1788,6],[2013,6]]},"528":{"position":[[63,6],[352,6]]},"530":{"position":[[151,6],[282,9],[364,6],[470,8]]},"544":{"position":[[722,6],[1158,6],[1223,6],[2025,6],[2102,6]]},"560":{"position":[[462,7],[513,7],[568,6],[617,6],[678,6],[730,6]]},"562":{"position":[[68,6]]},"566":{"position":[[316,6],[747,7],[868,8]]},"572":{"position":[[414,6]]},"574":{"position":[[493,6],[560,6],[1106,6],[1173,6]]},"588":{"position":[[1210,6],[2186,6]]},"590":{"position":[[681,8]]},"592":{"position":[[101,6],[535,7],[679,8],[799,7]]},"594":{"position":[[513,8],[709,7],[885,6],[1021,8],[1102,6],[1447,8],[1565,7]]},"600":{"position":[[542,6],[611,6],[717,6],[833,7],[925,7],[1639,7]]},"604":{"position":[[385,14]]},"606":{"position":[[5,6]]},"612":{"position":[[289,6],[1079,6]]},"622":{"position":[[12,6]]},"624":{"position":[[1227,6]]},"628":{"position":[[125,6]]},"654":{"position":[[505,6]]},"666":{"position":[[70,6]]},"668":{"position":[[1655,6],[2907,6]]},"678":{"position":[[1031,6]]},"694":{"position":[[54,7],[126,6],[215,7]]},"698":{"position":[[10,6]]},"702":{"position":[[2741,6]]},"708":{"position":[[1686,6]]},"714":{"position":[[614,6],[1349,6],[1388,6],[1550,6],[2854,6],[2887,7],[3178,8],[3313,6],[3369,6]]},"716":{"position":[[1508,7]]},"718":{"position":[[452,6],[799,6],[1639,6]]},"720":{"position":[[5978,6],[7597,6]]},"722":{"position":[[395,6],[503,6],[1889,6]]},"734":{"position":[[60,6],[655,6],[3369,6]]},"738":{"position":[[6705,6],[6765,6]]},"788":{"position":[[111,6]]},"798":{"position":[[928,7]]},"804":{"position":[[167,6]]},"806":{"position":[[431,6],[1350,7],[1371,7]]},"810":{"position":[[1361,6]]},"828":{"position":[[1705,8],[1728,6]]},"834":{"position":[[2400,6],[2484,7],[2629,7],[2693,6],[4717,6],[4974,6]]},"842":{"position":[[1481,8],[1504,6]]},"852":{"position":[[1684,6]]},"854":{"position":[[298,6]]},"858":{"position":[[3022,6]]},"860":{"position":[[270,6]]},"862":{"position":[[793,6],[1146,6],[1443,6],[2926,6]]},"868":{"position":[[338,7],[486,7],[529,7],[572,7]]},"908":{"position":[[1142,7]]},"985":{"position":[[967,6],[1020,6],[1272,6],[1323,6],[1382,6],[1567,6]]},"1007":{"position":[[93,8],[220,8]]},"1023":{"position":[[556,7]]},"1027":{"position":[[1510,6],[3507,6],[8867,6],[8952,6]]},"1033":{"position":[[811,7]]},"1035":{"position":[[228,6]]}}}],["number1",{"_index":3314,"t":{"510":{"position":[[501,8],[1070,8]]}}}],["number1read",{"_index":3311,"t":{"510":{"position":[[416,11],[978,11]]}}}],["number2",{"_index":3315,"t":{"510":{"position":[[514,8],[1083,8],[1410,8]]}}}],["number2))echo",{"_index":3321,"t":{"510":{"position":[[1043,14]]}}}],["number2sum=$(($number1",{"_index":3320,"t":{"510":{"position":[[1018,22]]}}}],["number2sum=$number1+$number2echo",{"_index":3312,"t":{"510":{"position":[[456,32]]}}}],["number=$1",{"_index":3418,"t":{"530":{"position":[[137,9]]}}}],["number=33",{"_index":3424,"t":{"530":{"position":[[451,10]]}}}],["numbers.random_numb",{"_index":3708,"t":{"594":{"position":[[556,26]]}}}],["numbers.sum",{"_index":3399,"t":{"524":{"position":[[836,13]]},"526":{"position":[[141,13]]}}}],["numbers=(0",{"_index":3743,"t":{"604":{"position":[[353,10]]}}}],["numer",{"_index":2459,"t":{"374":{"position":[[425,7]]},"400":{"position":[[960,7]]},"424":{"position":[[695,11]]},"442":{"position":[[654,11],[814,7],[846,11]]},"492":{"position":[[116,7]]},"670":{"position":[[3113,8]]},"1007":{"position":[[125,8],[234,9]]}}}],["nushel",{"_index":5298,"t":{"975":{"position":[[191,8]]}}}],["nut",{"_index":2048,"t":{"276":{"position":[[733,3]]},"452":{"position":[[97,3]]}}}],["nutshel",{"_index":149,"t":{"15":{"position":[[16,9]]},"71":{"position":[[1319,8]]},"804":{"position":[[1391,8]]}}}],["nvim",{"_index":2630,"t":{"394":{"position":[[1315,4],[2260,4],[2543,4]]}}}],["nyou",{"_index":3307,"t":{"508":{"position":[[4199,4]]}}}],["o",{"_index":392,"t":{"23":{"position":[[1766,2]]},"47":{"position":[[7180,2]]},"99":{"position":[[1630,1],[1806,1]]},"125":{"position":[[121,1]]},"185":{"position":[[795,1]]},"470":{"position":[[450,1],[635,1]]},"538":{"position":[[3211,1]]},"564":{"position":[[419,1]]},"566":{"position":[[655,1]]},"666":{"position":[[1449,1]]},"770":{"position":[[496,1],[534,1]]},"842":{"position":[[153,1]]},"850":{"position":[[393,1],[995,1],[2440,1]]},"852":{"position":[[2351,1]]},"858":{"position":[[1097,1]]},"892":{"position":[[1690,2],[1701,2],[1714,2],[1734,1],[1741,1],[1747,1],[1749,1],[1758,1],[1760,1],[1766,1],[1775,1],[1777,1],[1781,5],[1793,6]]}}}],["ovoid",{"_index":68,"t":{"8":{"position":[[178,13]]}}}],["stdlib.h",{"_index":794,"t":{"47":{"position":[[4666,10]]}}}],["stdout",{"_index":1874,"t":{"247":{"position":[[1051,6]]},"249":{"position":[[58,6],[162,6]]},"251":{"position":[[860,6],[1651,6]]},"253":{"position":[[1016,7],[2505,7]]},"257":{"position":[[1059,6],[2019,7],[2426,7],[2947,7],[3506,6],[3607,7],[3656,6],[5402,7],[5527,6],[5622,7],[5893,6],[6021,6],[6442,8],[6628,6],[6685,6]]},"262":{"position":[[895,6],[2607,6]]},"296":{"position":[[356,6]]},"360":{"position":[[177,7]]},"394":{"position":[[1726,7]]},"530":{"position":[[820,6]]},"532":{"position":[[26,7],[302,7],[610,7],[1268,7]]},"534":{"position":[[73,6],[1337,6],[1904,7]]},"536":{"position":[[1350,6]]},"626":{"position":[[6216,6]]},"806":{"position":[[1542,6],[2394,6]]},"852":{"position":[[1245,6]]},"862":{"position":[[3160,6]]}}}],["steal",{"_index":5105,"t":{"892":{"position":[[1243,6]]}}}],["step",{"_index":1054,"t":{"69":{"position":[[3680,5],[4458,4],[4869,4]]},"251":{"position":[[1332,4],[1385,5]]},"338":{"position":[[707,6]]},"352":{"position":[[3103,6]]},"378":{"position":[[2362,4]]},"570":{"position":[[1682,4],[1738,5],[1781,4],[1801,4]]},"638":{"position":[[487,5],[654,5]]},"742":{"position":[[1106,5]]},"746":{"position":[[11,5]]},"896":{"position":[[860,4],[1126,4],[1362,4],[2728,6]]}}}],["still",{"_index":993,"t":{"61":{"position":[[465,5]]},"67":{"position":[[784,5]]},"91":{"position":[[1326,5]]},"93":{"position":[[1503,5]]},"95":{"position":[[777,5]]},"113":{"position":[[759,6]]},"147":{"position":[[523,5]]},"227":{"position":[[223,5]]},"229":{"position":[[410,5]]},"235":{"position":[[79,5],[157,5],[434,5],[513,5]]},"257":{"position":[[1451,5]]},"260":{"position":[[757,5]]},"282":{"position":[[1413,5],[1552,5]]},"376":{"position":[[3216,5]]},"416":{"position":[[228,5]]},"508":{"position":[[789,5]]},"538":{"position":[[3067,5]]},"556":{"position":[[865,5]]},"596":{"position":[[1514,5],[1601,5]]},"604":{"position":[[1077,5]]},"684":{"position":[[2046,5],[2409,5]]},"714":{"position":[[2346,5]]},"718":{"position":[[1477,5],[1584,5]]},"720":{"position":[[5015,5]]},"722":{"position":[[3051,5]]},"738":{"position":[[724,5]]},"802":{"position":[[202,5]]},"816":{"position":[[1342,5]]},"818":{"position":[[791,5]]},"832":{"position":[[238,5]]},"834":{"position":[[2821,5],[3143,5]]},"850":{"position":[[1310,5]]},"858":{"position":[[1621,5]]},"888":{"position":[[767,5]]},"898":{"position":[[452,5]]},"939":{"position":[[144,5]]},"959":{"position":[[109,5]]}}}],["stock",{"_index":3852,"t":{"626":{"position":[[6370,6]]}}}],["stop",{"_index":1516,"t":{"171":{"position":[[112,4]]},"227":{"position":[[308,4],[621,6]]},"237":{"position":[[407,4]]},"239":{"position":[[132,4]]},"243":{"position":[[1032,6],[1449,4]]},"257":{"position":[[5646,8]]},"350":{"position":[[138,5]]},"420":{"position":[[971,5]]},"508":{"position":[[4546,4]]},"534":{"position":[[2083,4]]},"538":{"position":[[1933,4],[2738,7],[3491,4],[3811,4]]},"602":{"position":[[151,4],[251,4],[645,4]]},"608":{"position":[[334,5]]},"624":{"position":[[889,4]]},"626":{"position":[[2580,7]]},"634":{"position":[[2010,5]]},"738":{"position":[[1778,4],[4307,4]]},"740":{"position":[[727,4]]},"742":{"position":[[1193,4]]},"802":{"position":[[1496,4]]},"816":{"position":[[1027,4]]},"818":{"position":[[988,4]]},"858":{"position":[[3349,4]]},"896":{"position":[[2953,4]]},"985":{"position":[[1402,7]]},"1027":{"position":[[8462,4]]}}}],["stop_underline=$(tput",{"_index":4365,"t":{"720":{"position":[[2423,21]]}}}],["store",{"_index":1079,"t":{"69":{"position":[[6286,5]]},"71":{"position":[[742,5]]},"83":{"position":[[771,6]]},"129":{"position":[[635,6]]},"292":{"position":[[727,7]]},"378":{"position":[[565,6]]},"480":{"position":[[84,5]]},"486":{"position":[[43,5],[743,5]]},"488":{"position":[[1587,5]]},"490":{"position":[[30,5]]},"508":{"position":[[579,6]]},"534":{"position":[[1498,5]]},"574":{"position":[[2582,5]]},"588":{"position":[[2238,5]]},"612":{"position":[[614,5],[649,5],[985,6]]},"624":{"position":[[1249,5]]},"634":{"position":[[1738,6]]},"654":{"position":[[43,6],[104,6]]},"660":{"position":[[1131,5],[1176,5]]},"670":{"position":[[1791,5],[1833,5]]},"680":{"position":[[96,6]]},"682":{"position":[[1615,6]]},"684":{"position":[[1212,6]]},"692":{"position":[[297,5],[429,5]]},"694":{"position":[[843,5],[1215,5],[1256,5],[1877,5],[1922,5]]},"696":{"position":[[5528,5],[5573,5]]},"702":{"position":[[614,5],[659,5]]},"720":{"position":[[144,5],[1436,5]]},"732":{"position":[[971,6],[1050,6]]},"738":{"position":[[7421,5],[7498,5]]},"744":{"position":[[92,6],[2361,5]]},"860":{"position":[[1382,7],[2361,6]]},"892":{"position":[[373,5]]},"902":{"position":[[231,6]]},"1029":{"position":[[501,5],[628,5]]}}}],["stori",{"_index":2245,"t":{"330":{"position":[[274,6]]},"392":{"position":[[724,5]]},"446":{"position":[[614,5],[3642,5]]},"450":{"position":[[283,5]]},"454":{"position":[[763,5],[783,5],[803,5]]}}}],["straight",{"_index":437,"t":{"25":{"position":[[961,8]]},"253":{"position":[[2938,8]]},"259":{"position":[[839,8]]},"262":{"position":[[2226,8]]},"390":{"position":[[1985,8]]},"1033":{"position":[[3765,8]]}}}],["straightforward",{"_index":417,"t":{"25":{"position":[[289,16]]},"352":{"position":[[249,16]]},"378":{"position":[[1681,15],[1910,16]]},"638":{"position":[[403,15]]},"666":{"position":[[734,15]]},"670":{"position":[[839,16]]},"756":{"position":[[187,15]]}}}],["strang",{"_index":3429,"t":{"532":{"position":[[551,7]]}}}],["strategi",{"_index":4076,"t":{"670":{"position":[[2464,11],[2585,10],[2674,8]]},"692":{"position":[[1922,9]]},"698":{"position":[[26,10],[311,10],[684,11]]}}}],["straub",{"_index":4083,"t":{"670":{"position":[[3227,7]]},"939":{"position":[[647,6]]}}}],["stream",{"_index":734,"t":{"47":{"position":[[2418,7]]},"251":{"position":[[1724,7]]},"253":{"position":[[191,6],[812,7],[1889,6]]},"257":{"position":[[924,8],[1001,6]]},"259":{"position":[[466,6]]},"262":{"position":[[50,8],[587,7],[675,6],[833,6],[992,6],[2206,6],[2725,7],[2749,7]]},"332":{"position":[[31,7]]},"360":{"position":[[4,6],[45,6]]},"386":{"position":[[40,6]]},"454":{"position":[[1184,6]]},"632":{"position":[[80,7]]},"634":{"position":[[66,7]]},"862":{"position":[[3167,6]]}}}],["stretch",{"_index":4704,"t":{"800":{"position":[[1402,7]]}}}],["strictli",{"_index":2494,"t":{"376":{"position":[[4781,8]]}}}],["strike",{"_index":4858,"t":{"826":{"position":[[365,6]]}}}],["string",{"_index":2252,"t":{"336":{"position":[[491,6]]},"346":{"position":[[533,7]]},"352":{"position":[[933,7],[1310,7]]},"464":{"position":[[1898,7]]},"468":{"position":[[945,7],[1251,7],[2178,6],[2487,7],[2754,7],[2886,6]]},"490":{"position":[[1980,7]]},"492":{"position":[[173,7]]},"506":{"position":[[1133,6],[2195,7],[2743,6]]},"526":{"position":[[1886,6],[2246,6]]},"560":{"position":[[212,6],[259,6],[349,7],[403,7]]},"600":{"position":[[618,6],[977,6]]},"710":{"position":[[100,6],[659,7]]},"712":{"position":[[28,6]]},"714":{"position":[[150,6]]},"718":{"position":[[60,7],[73,6],[997,7]]},"720":{"position":[[3632,6],[3763,6],[4106,6],[4583,6],[4943,6],[5821,6]]},"724":{"position":[[61,6],[221,7]]},"802":{"position":[[1478,6],[3260,7]]},"860":{"position":[[395,8],[474,6],[706,6],[1763,6]]},"1027":{"position":[[3175,6],[4461,6],[4486,6],[6629,6]]}}}],["string2",{"_index":3669,"t":{"586":{"position":[[1882,8]]}}}],["strip",{"_index":467,"t":{"27":{"position":[[490,5]]},"368":{"position":[[704,6]]},"370":{"position":[[1066,6]]},"386":{"position":[[489,5]]},"446":{"position":[[3395,5]]},"450":{"position":[[1885,5]]},"802":{"position":[[1656,5],[3174,9]]},"804":{"position":[[1744,5]]},"1027":{"position":[[7131,5],[7461,5]]}}}],["stripped_word",{"_index":4754,"t":{"804":{"position":[[1820,13]]}}}],["strong",{"_index":2344,"t":{"350":{"position":[[817,9]]}}}],["strong>boldmi",{"_index":1745,"t":{"225":{"position":[[184,9]]}}}],["title_messag",{"_index":3368,"t":{"520":{"position":[[326,18],[537,17]]}}}],["title_message=\"${titl",{"_index":3367,"t":{"520":{"position":[[275,23]]}}}],["titlemi",{"_index":3363,"t":{"518":{"position":[[650,7]]}}}],["tldr",{"_index":592,"t":{"37":{"position":[[814,5]]},"49":{"position":[[426,4],[501,4],[706,4],[736,5],[891,4]]},"51":{"position":[[753,5],[845,4]]},"53":{"position":[[223,4]]},"390":{"position":[[937,4],[987,4]]}}}],["tloc",{"_index":763,"t":{"47":{"position":[[3237,10]]}}}],["tmp",{"_index":1663,"t":{"209":{"position":[[303,4],[344,7]]},"211":{"position":[[86,7],[643,7]]},"217":{"position":[[521,4],[788,5],[794,4]]},"462":{"position":[[732,4]]},"538":{"position":[[1081,3]]},"626":{"position":[[5711,5]]}}}],["tmp/$(date",{"_index":3468,"t":{"538":{"position":[[1351,12],[2636,12]]}}}],["tmp/2021",{"_index":3441,"t":{"534":{"position":[[849,11]]},"538":{"position":[[1445,9],[2681,9]]},"852":{"position":[[1111,9],[1135,9]]}}}],["tmp/one",{"_index":5377,"t":{"1023":{"position":[[380,8]]}}}],["tmp/three",{"_index":5379,"t":{"1023":{"position":[[398,10]]}}}],["tmp/two",{"_index":5378,"t":{"1023":{"position":[[389,8]]}}}],["tmp/{one,two,thre",{"_index":5375,"t":{"1023":{"position":[[322,21]]}}}],["tmp_dir",{"_index":4962,"t":{"858":{"position":[[901,12],[929,13],[1158,12]]}}}],["tmp_tar",{"_index":4961,"t":{"858":{"position":[[852,12],[876,13],[1099,13],[1142,12]]}}}],["tmpdir_today",{"_index":3439,"t":{"534":{"position":[[480,21],[511,17],[534,18]]}}}],["tmpdir_today=\"/tmp/${today",{"_index":3438,"t":{"534":{"position":[[429,28]]}}}],["tmux",{"_index":1841,"t":{"241":{"position":[[476,5],[1133,5]]},"290":{"position":[[427,4],[521,4]]},"816":{"position":[[615,4],[1327,4]]},"820":{"position":[[62,5],[187,4],[431,4],[459,4],[613,4]]},"822":{"position":[[0,4],[74,4],[126,4],[248,5],[262,4],[356,5],[425,4],[451,4]]},"824":{"position":[[9,4]]},"826":{"position":[[43,4],[417,5],[536,4]]},"828":{"position":[[8,4],[52,4],[1233,4],[1982,4]]},"830":{"position":[[12,4],[216,5],[272,4],[433,4],[532,4],[584,4],[782,4],[979,4],[1085,4]]},"832":{"position":[[289,4],[356,4],[385,4],[579,4],[672,4],[731,4],[918,4],[1149,4],[1193,4],[1222,4]]},"834":{"position":[[119,4],[260,4],[604,5],[880,4],[970,5],[1183,4],[1255,4],[1747,4],[2042,4],[2373,4],[2476,4],[2791,4],[3568,4],[3689,4],[3747,4],[4110,4],[4135,4],[4404,5],[4663,5],[4757,4]]},"836":{"position":[[69,4],[267,4],[458,5]]},"838":{"position":[[123,4],[466,4],[484,4],[671,4],[692,4],[845,4],[1152,4],[1361,4],[1463,4],[1911,4],[2260,4],[2288,4],[2398,4],[2457,4]]},"840":{"position":[[46,4],[133,4],[249,4],[390,4],[715,4]]},"842":{"position":[[31,4],[714,4],[728,4],[750,4],[781,4],[819,4],[904,4],[958,4]]},"844":{"position":[[20,4],[56,4],[247,4]]},"846":{"position":[[97,5],[181,4],[256,4]]},"878":{"position":[[1748,4]]},"995":{"position":[[198,4],[249,4],[309,4],[448,5]]},"997":{"position":[[234,5]]}}}],["tmux'",{"_index":4870,"t":{"834":{"position":[[0,6]]},"838":{"position":[[314,6]]}}}],["tmux.conf",{"_index":4872,"t":{"834":{"position":[[630,10],[671,12]]}}}],["tmux1",{"_index":4874,"t":{"834":{"position":[[1054,6]]}}}],["to:cd",{"_index":5386,"t":{"1025":{"position":[[167,5],[358,5]]}}}],["to:echo",{"_index":5389,"t":{"1027":{"position":[[652,7],[952,7],[1150,7],[3617,7],[4151,7],[5742,7],[6258,7],[6934,7],[7315,7],[7830,7],[8353,7],[8823,7]]},"1029":{"position":[[378,7]]}}}],["to:effect",{"_index":5182,"t":{"904":{"position":[[1338,12]]}}}],["to:for",{"_index":5385,"t":{"1023":{"position":[[887,6]]}}}],["to:mdkir",{"_index":5376,"t":{"1023":{"position":[[371,8]]}}}],["to:ssh",{"_index":5171,"t":{"902":{"position":[[1395,6]]}}}],["to:touch",{"_index":5381,"t":{"1023":{"position":[[628,8]]}}}],["tobia",{"_index":15,"t":{"4":{"position":[[163,6]]}}}],["today",{"_index":1246,"t":{"91":{"position":[[1932,6],[2817,5]]},"282":{"position":[[1577,6]]},"534":{"position":[[254,5],[408,5]]},"538":{"position":[[375,7],[1023,7],[1270,7],[1327,7],[1769,5],[2530,7]]},"626":{"position":[[5143,7],[5773,8]]},"852":{"position":[[296,7]]},"959":{"position":[[157,6]]}}}],["today'",{"_index":3433,"t":{"534":{"position":[[302,7],[736,7]]},"538":{"position":[[505,7],[589,7]]},"626":{"position":[[5286,7],[5370,7],[5691,7]]},"852":{"position":[[422,7],[506,7]]},"1027":{"position":[[7245,8],[7323,8]]}}}],["today.sh",{"_index":3466,"t":{"538":{"position":[[981,11],[1252,14],[2512,14]]},"852":{"position":[[275,8]]}}}],["today.sh/tmp/2021",{"_index":3467,"t":{"538":{"position":[[993,19]]}}}],["today.shmkdir",{"_index":3469,"t":{"538":{"position":[[1428,16],[2664,16]]}}}],["today=$(d",{"_index":3437,"t":{"534":{"position":[[347,12]]}}}],["today=2021",{"_index":4938,"t":{"852":{"position":[[1057,10]]}}}],["todaybash",{"_index":3471,"t":{"538":{"position":[[1497,12]]}}}],["todd",{"_index":26,"t":{"4":{"position":[[272,6]]}}}],["todo",{"_index":125,"t":{"10":{"position":[[466,4]]},"1001":{"position":[[0,5]]},"1003":{"position":[[0,5]]},"1005":{"position":[[0,4]]},"1007":{"position":[[0,4],[255,4]]},"1011":{"position":[[0,4]]}}}],["togeth",{"_index":442,"t":{"25":{"position":[[1146,9]]},"33":{"position":[[399,8]]},"121":{"position":[[471,10],[1118,8]]},"203":{"position":[[50,8],[287,8]]},"260":{"position":[[678,8],[1405,8]]},"262":{"position":[[2716,8]]},"284":{"position":[[1368,8]]},"366":{"position":[[3094,8]]},"376":{"position":[[5185,8]]},"378":{"position":[[4357,8]]},"384":{"position":[[603,8]]},"404":{"position":[[403,9],[544,9],[672,8]]},"430":{"position":[[744,9]]},"452":{"position":[[755,8]]},"462":{"position":[[2025,8]]},"510":{"position":[[356,8]]},"526":{"position":[[2223,8]]},"570":{"position":[[25,8],[243,8]]},"664":{"position":[[2244,9]]},"692":{"position":[[2370,8]]},"742":{"position":[[1045,8]]},"1037":{"position":[[291,8]]}}}],["toggl",{"_index":4691,"t":{"794":{"position":[[837,6]]}}}],["token",{"_index":3530,"t":{"554":{"position":[[652,5]]}}}],["told",{"_index":2484,"t":{"376":{"position":[[3040,5]]},"462":{"position":[[2307,4]]},"464":{"position":[[1580,4]]},"658":{"position":[[2317,4]]},"660":{"position":[[128,4]]},"682":{"position":[[2234,4]]},"700":{"position":[[1403,4]]},"764":{"position":[[2308,4]]}}}],["tomato",{"_index":2880,"t":{"446":{"position":[[1049,9],[1099,6]]}}}],["took",{"_index":1652,"t":{"205":{"position":[[1359,4]]},"260":{"position":[[239,4]]}}}],["tool",{"_index":478,"t":{"29":{"position":[[117,5]]},"37":{"position":[[784,4]]},"39":{"position":[[5,5],[134,5]]},"41":{"position":[[696,5],[786,5],[1002,4]]},"43":{"position":[[217,5]]},"47":{"position":[[17,5],[1291,6]]},"49":{"position":[[78,5],[711,4],[832,5]]},"51":{"position":[[173,5],[258,4],[452,4],[796,5],[835,4],[938,6]]},"53":{"position":[[93,4],[228,4],[269,5],[389,5]]},"69":{"position":[[107,4],[140,5],[766,6],[2871,5]]},"71":{"position":[[595,4],[652,4]]},"87":{"position":[[117,7],[749,6]]},"91":{"position":[[2028,7],[2581,8],[2997,7],[3182,5],[3457,8]]},"93":{"position":[[58,7]]},"189":{"position":[[1409,5],[1683,5],[1811,5]]},"221":{"position":[[71,4]]},"237":{"position":[[1295,5]]},"249":{"position":[[631,5]]},"300":{"position":[[909,5],[994,6]]},"316":{"position":[[225,4]]},"332":{"position":[[476,4],[852,5]]},"338":{"position":[[357,4]]},"352":{"position":[[1392,5]]},"354":{"position":[[10,5],[323,4],[416,5],[1339,6]]},"356":{"position":[[207,4]]},"360":{"position":[[444,4]]},"376":{"position":[[6313,6]]},"378":{"position":[[4283,5]]},"382":{"position":[[49,4]]},"384":{"position":[[667,5]]},"386":{"position":[[138,4],[1110,4]]},"390":{"position":[[942,4]]},"392":{"position":[[47,4]]},"396":{"position":[[1283,6],[1434,5]]},"404":{"position":[[959,6]]},"406":{"position":[[235,5]]},"408":{"position":[[29,4],[486,4],[1003,6],[1068,5]]},"410":{"position":[[35,5],[122,5]]},"438":{"position":[[1546,5]]},"446":{"position":[[4220,5]]},"448":{"position":[[9,4]]},"450":{"position":[[5233,5],[5268,5],[5488,6]]},"454":{"position":[[979,5]]},"458":{"position":[[48,5]]},"464":{"position":[[953,5]]},"470":{"position":[[1292,5]]},"584":{"position":[[2623,5]]},"622":{"position":[[772,5]]},"626":{"position":[[4934,5]]},"640":{"position":[[1223,5]]},"670":{"position":[[91,5],[217,4],[611,4],[878,5]]},"674":{"position":[[397,5]]},"736":{"position":[[906,6]]},"738":{"position":[[2122,5]]},"748":{"position":[[404,4],[506,4]]},"754":{"position":[[653,4],[776,5],[2016,5],[2587,5]]},"790":{"position":[[287,4]]},"794":{"position":[[150,5],[583,5],[640,6],[732,4],[1199,5]]},"796":{"position":[[347,5],[556,5],[1718,5],[1979,4],[2294,6]]},"798":{"position":[[22,4],[117,6],[170,4],[242,5],[296,5],[384,5],[708,5],[1078,6],[1152,4],[1328,5],[1409,4],[1483,5],[1605,5]]},"800":{"position":[[47,5],[229,4],[586,4],[853,4],[1137,4],[1284,4]]},"802":{"position":[[580,5],[613,4],[813,5]]},"806":{"position":[[267,6]]},"808":{"position":[[120,4],[1036,4],[1048,4],[1180,4],[1860,4],[1980,5]]},"810":{"position":[[910,5],[1213,5]]},"812":{"position":[[118,4],[253,5],[334,5]]},"866":{"position":[[720,5]]},"908":{"position":[[1265,4],[3747,4]]},"953":{"position":[[296,6]]},"963":{"position":[[51,4]]},"995":{"position":[[14,4]]},"997":{"position":[[576,5]]}}}],["toolbar",{"_index":245,"t":{"21":{"position":[[1236,8]]}}}],["toolkit",{"_index":377,"t":{"23":{"position":[[1296,7]]},"237":{"position":[[1308,7]]},"384":{"position":[[681,8]]},"408":{"position":[[1082,8]]},"618":{"position":[[23,7],[406,7]]},"818":{"position":[[1978,8]]},"880":{"position":[[223,8]]}}}],["top",{"_index":1230,"t":{"91":{"position":[[1584,3],[3064,3]]},"141":{"position":[[469,3]]},"175":{"position":[[2088,3]]},"260":{"position":[[791,4]]},"376":{"position":[[266,3],[5659,3]]},"446":{"position":[[102,3],[1024,3]]},"526":{"position":[[923,3],[998,4]]},"528":{"position":[[304,3],[507,4]]},"664":{"position":[[764,3],[1935,3]]},"678":{"position":[[1350,4]]},"738":{"position":[[2917,3],[3639,3]]},"808":{"position":[[350,3]]},"850":{"position":[[365,3]]},"878":{"position":[[63,4]]},"918":{"position":[[50,3]]}}}],["top100.csv\"rank\",\"rating\",\"title\",\"reviews\"\"1\",\"97\",\"black",{"_index":2454,"t":{"374":{"position":[[188,58]]}}}],["topic",{"_index":560,"t":{"31":{"position":[[2873,6]]},"53":{"position":[[143,5]]},"115":{"position":[[516,6]]},"185":{"position":[[325,5]]},"290":{"position":[[186,6]]},"292":{"position":[[1958,5]]},"352":{"position":[[419,7]]},"494":{"position":[[51,5]]},"502":{"position":[[1113,5]]},"608":{"position":[[2619,5]]},"626":{"position":[[3908,5]]},"648":{"position":[[1726,5]]},"650":{"position":[[619,5]]},"670":{"position":[[589,6],[1097,6],[2609,5]]},"674":{"position":[[252,6]]},"692":{"position":[[1354,5]]},"698":{"position":[[644,5]]},"728":{"position":[[256,6]]},"748":{"position":[[880,5]]},"780":{"position":[[14,5]]},"818":{"position":[[1917,5]]},"836":{"position":[[376,5]]},"838":{"position":[[227,6]]},"876":{"position":[[1003,5]]},"912":{"position":[[548,5]]},"939":{"position":[[365,7],[589,6],[752,6]]},"953":{"position":[[95,6]]},"1027":{"position":[[5487,5]]},"1033":{"position":[[28,5]]}}}],["torvald",{"_index":4085,"t":{"678":{"position":[[676,8]]}}}],["total",{"_index":729,"t":{"47":{"position":[[2301,5]]},"526":{"position":[[274,5],[284,10],[389,5]]},"658":{"position":[[1547,5]]}}}],["total=$((tot",{"_index":3401,"t":{"526":{"position":[[191,14]]}}}],["total=0",{"_index":3400,"t":{"526":{"position":[[163,7]]}}}],["total_tim",{"_index":4974,"t":{"858":{"position":[[2333,13]]}}}],["total_time=$((total_tim",{"_index":4975,"t":{"858":{"position":[[2372,24]]}}}],["touch",{"_index":1891,"t":{"249":{"position":[[1662,5]]},"262":{"position":[[2532,7]]},"420":{"position":[[1050,5]]},"462":{"position":[[502,5]]},"464":{"position":[[133,5]]},"466":{"position":[[126,5]]},"502":{"position":[[577,5],[610,5]]},"534":{"position":[[1572,9],[1620,5]]},"536":{"position":[[394,7]]},"538":{"position":[[1345,5],[2630,5]]},"592":{"position":[[237,5]]},"604":{"position":[[568,5]]},"606":{"position":[[46,7]]},"608":{"position":[[502,7],[1710,5]]},"718":{"position":[[1369,5],[1552,5],[2168,5]]},"738":{"position":[[164,5]]},"754":{"position":[[1354,5]]},"834":{"position":[[665,5]]},"836":{"position":[[20,7]]},"838":{"position":[[1251,7]]},"840":{"position":[[18,7],[205,8]]},"870":{"position":[[242,5]]},"927":{"position":[[1980,5]]},"1023":{"position":[[579,5]]},"1035":{"position":[[2850,5],[3031,5]]}}}],["tough",{"_index":1982,"t":{"257":{"position":[[6764,5]]}}}],["toward",{"_index":4936,"t":{"852":{"position":[[930,7]]}}}],["tpm",{"_index":2707,"t":{"400":{"position":[[1872,3]]}}}],["tput",{"_index":4354,"t":{"720":{"position":[[1801,4],[2518,4]]},"734":{"position":[[1551,4]]},"862":{"position":[[3899,4],[4130,6]]}}}],["tr",{"_index":367,"t":{"23":{"position":[[1089,2]]},"31":{"position":[[1311,2],[1535,2],[1964,2],[2137,2]]},"33":{"position":[[662,2]]},"257":{"position":[[468,2],[578,2],[721,2],[1235,2],[1334,2],[1518,2],[3569,2],[3684,3],[3732,2]]},"262":{"position":[[2121,2]]},"376":{"position":[[6015,4]]},"448":{"position":[[31,2],[110,2],[317,2],[453,2],[467,2],[616,2],[869,2],[940,2],[1070,2],[1501,3],[1628,2]]},"458":{"position":[[522,2],[611,2]]},"532":{"position":[[218,2]]},"538":{"position":[[3031,2]]},"718":{"position":[[558,2],[848,2],[2018,2]]},"1027":{"position":[[9147,2]]}}}],["trace",{"_index":3112,"t":{"464":{"position":[[552,7],[2300,7]]},"468":{"position":[[790,5]]},"474":{"position":[[115,6]]},"722":{"position":[[305,7],[1398,9],[1439,6]]},"740":{"position":[[582,7],[719,7]]},"852":{"position":[[52,5],[137,5],[388,7],[731,7],[868,7],[922,7],[1427,5],[1668,5],[1800,6],[1835,7],[2405,7],[2535,5]]},"874":{"position":[[330,7]]},"880":{"position":[[260,5]]}}}],["track",{"_index":1527,"t":{"175":{"position":[[1365,5],[2550,5]]},"257":{"position":[[2615,5]]},"316":{"position":[[70,8]]},"618":{"position":[[518,5]]},"648":{"position":[[295,5]]},"654":{"position":[[2650,5],[3956,5]]},"658":{"position":[[2496,6]]},"664":{"position":[[996,7],[1981,5]]},"668":{"position":[[1516,7]]},"670":{"position":[[1207,5],[1402,8]]},"672":{"position":[[246,8]]},"678":{"position":[[285,5]]},"680":{"position":[[68,8],[273,5]]},"682":{"position":[[217,5],[478,6],[694,6],[882,5],[3087,8]]},"684":{"position":[[2121,6],[2364,6],[2477,8],[2924,7]]},"688":{"position":[[1200,6],[1277,8]]},"720":{"position":[[646,5]]},"736":{"position":[[1485,5]]},"762":{"position":[[384,5]]}}}],["tradit",{"_index":1839,"t":{"241":{"position":[[322,11]]},"951":{"position":[[76,11]]}}}],["trail",{"_index":2594,"t":{"390":{"position":[[652,8]]},"582":{"position":[[643,8]]}}}],["transfer",{"_index":1011,"t":{"67":{"position":[[754,12]]}}}],["transform",{"_index":546,"t":{"31":{"position":[[1896,11]]},"253":{"position":[[406,9]]},"360":{"position":[[251,15]]},"382":{"position":[[265,15]]},"386":{"position":[[87,9]]},"448":{"position":[[1703,12]]},"458":{"position":[[1755,14]]},"506":{"position":[[1323,11],[1485,11]]}}}],["transit",{"_index":159,"t":{"17":{"position":[[9,13]]},"91":{"position":[[1498,10]]},"107":{"position":[[704,13]]},"135":{"position":[[791,13]]},"147":{"position":[[172,10]]},"454":{"position":[[1589,13],[1982,13],[2229,13]]}}}],["translat",{"_index":1942,"t":{"257":{"position":[[471,10]]},"278":{"position":[[959,10]]},"448":{"position":[[34,10]]},"458":{"position":[[540,9]]},"532":{"position":[[141,9]]},"760":{"position":[[3092,10]]},"900":{"position":[[871,10]]}}}],["transmiss",{"_index":1871,"t":{"247":{"position":[[501,13]]},"262":{"position":[[1166,13]]},"738":{"position":[[4455,12]]},"802":{"position":[[1436,13],[1988,12],[2069,13],[2943,13]]},"868":{"position":[[749,12]]}}}],["transpil",{"_index":1265,"t":{"91":{"position":[[2749,10]]}}}],["transpos",{"_index":1562,"t":{"183":{"position":[[136,9],[182,9]]}}}],["trap",{"_index":4953,"t":{"858":{"position":[[16,4],[21,5],[220,5],[410,4],[1247,4],[1669,5],[2897,4],[3113,5],[3209,4],[3336,4],[3389,4],[3485,4],[3501,4],[3534,4],[3565,5]]}}}],["trash",{"_index":1540,"t":{"177":{"position":[[82,8]]}}}],["travel",{"_index":5056,"t":{"872":{"position":[[129,7]]}}}],["treat",{"_index":555,"t":{"31":{"position":[[2512,5]]},"33":{"position":[[731,5]]},"344":{"position":[[4229,5]]},"422":{"position":[[243,7]]},"500":{"position":[[202,7],[721,7]]},"596":{"position":[[765,7]]},"608":{"position":[[1062,7],[1517,5]]},"718":{"position":[[1207,5]]},"1027":{"position":[[9013,8]]},"1037":{"position":[[588,8]]}}}],["tree",{"_index":1384,"t":{"107":{"position":[[287,4],[342,4],[441,4],[588,6],[757,4],[898,4],[933,4]]},"109":{"position":[[313,4]]},"113":{"position":[[456,4],[568,4]]},"117":{"position":[[599,4]]},"121":{"position":[[4,4],[49,4]]},"123":{"position":[[264,4]]},"125":{"position":[[577,4],[897,4]]},"280":{"position":[[1130,4]]},"284":{"position":[[1444,4]]},"434":{"position":[[1702,6],[1740,5]]},"634":{"position":[[322,4],[382,4],[950,5]]},"658":{"position":[[195,5]]},"668":{"position":[[1573,4],[2591,5],[2628,5]]},"670":{"position":[[1551,4],[1805,4],[2038,5]]},"672":{"position":[[207,4],[680,4]]},"682":{"position":[[81,4],[155,5],[173,4],[2077,4],[3617,5]]},"684":{"position":[[2916,4],[2993,4],[3046,5]]},"688":{"position":[[333,5],[564,4],[935,5],[1941,8],[2063,4],[2509,8]]},"700":{"position":[[278,4],[392,4],[3067,4],[3510,4],[3936,4]]},"702":{"position":[[144,4],[1582,4],[2032,4]]},"704":{"position":[[96,4],[713,4],[1164,5]]},"748":{"position":[[751,5]]},"878":{"position":[[1587,4]]},"927":{"position":[[99,4],[666,4],[1117,5]]}}}],["trend",{"_index":5299,"t":{"975":{"position":[[304,6]]}}}],["trevor",{"_index":44,"t":{"4":{"position":[[470,6]]}}}],["tri",{"_index":214,"t":{"21":{"position":[[478,5],[2680,3]]},"31":{"position":[[411,3],[2681,3]]},"37":{"position":[[11,6]]},"43":{"position":[[1117,3]]},"47":{"position":[[5897,3],[7770,3]]},"49":{"position":[[29,6]]},"53":{"position":[[480,3]]},"79":{"position":[[290,3]]},"81":{"position":[[1292,6]]},"101":{"position":[[176,3]]},"105":{"position":[[647,3],[709,6],[777,3]]},"107":{"position":[[881,3]]},"119":{"position":[[385,3]]},"129":{"position":[[891,3]]},"133":{"position":[[122,3]]},"183":{"position":[[100,3]]},"195":{"position":[[2123,3],[2205,5]]},"207":{"position":[[664,6]]},"215":{"position":[[172,3]]},"217":{"position":[[504,3]]},"225":{"position":[[1715,3]]},"227":{"position":[[349,3],[393,3]]},"235":{"position":[[115,5]]},"237":{"position":[[383,3]]},"243":{"position":[[1274,5]]},"255":{"position":[[843,3]]},"257":{"position":[[315,3],[3241,3],[5932,3],[6529,3],[6796,6]]},"288":{"position":[[1768,3]]},"302":{"position":[[1462,3]]},"304":{"position":[[654,6]]},"332":{"position":[[655,3]]},"352":{"position":[[754,3]]},"354":{"position":[[1422,5]]},"362":{"position":[[99,3]]},"374":{"position":[[464,3]]},"382":{"position":[[74,6]]},"396":{"position":[[2251,3]]},"398":{"position":[[271,3],[369,3],[655,6],[787,6],[919,6],[1051,6],[1179,6]]},"404":{"position":[[425,6]]},"414":{"position":[[487,6]]},"422":{"position":[[390,3],[693,6],[1202,6]]},"442":{"position":[[1593,3]]},"446":{"position":[[2914,3]]},"454":{"position":[[1537,6],[1690,6],[1795,6]]},"456":{"position":[[305,6]]},"464":{"position":[[265,3],[594,5]]},"468":{"position":[[2299,5]]},"470":{"position":[[942,6]]},"492":{"position":[[715,6]]},"496":{"position":[[317,5],[440,5]]},"510":{"position":[[333,3]]},"522":{"position":[[1205,3]]},"534":{"position":[[85,5],[635,3],[1490,3]]},"538":{"position":[[1750,3]]},"540":{"position":[[907,5]]},"550":{"position":[[566,3]]},"554":{"position":[[548,3]]},"570":{"position":[[483,6]]},"574":{"position":[[3207,6]]},"584":{"position":[[2272,6]]},"588":{"position":[[26,6]]},"626":{"position":[[2515,3],[2744,3],[6851,3]]},"634":{"position":[[1421,3]]},"648":{"position":[[2418,5]]},"668":{"position":[[1001,3]]},"686":{"position":[[120,3]]},"690":{"position":[[655,5]]},"696":{"position":[[1814,3],[2663,6],[4880,3]]},"698":{"position":[[340,3]]},"714":{"position":[[3634,6],[3789,6]]},"716":{"position":[[931,6]]},"718":{"position":[[1200,3]]},"738":{"position":[[4105,3]]},"754":{"position":[[1608,3]]},"804":{"position":[[45,3],[564,3],[638,4],[1064,3],[1324,3],[1599,3]]},"822":{"position":[[63,6]]},"834":{"position":[[2052,3]]},"850":{"position":[[1810,6]]},"860":{"position":[[2533,3]]},"866":{"position":[[348,3]]},"896":{"position":[[1652,3]]},"900":{"position":[[29,3]]},"908":{"position":[[1101,3]]},"914":{"position":[[153,6]]},"1027":{"position":[[1271,6]]}}}],["trial",{"_index":2756,"t":{"420":{"position":[[24,5]]}}}],["trick",{"_index":473,"t":{"27":{"position":[[728,5]]},"77":{"position":[[2932,5]]},"81":{"position":[[675,6],[1285,6]]},"115":{"position":[[958,5]]},"121":{"position":[[855,7],[1235,6]]},"139":{"position":[[1012,5]]},"143":{"position":[[9,5]]},"175":{"position":[[15,6],[184,6]]},"179":{"position":[[51,6]]},"253":{"position":[[1673,6],[1964,5],[2306,5],[2378,5],[2662,5],[3361,5]]},"255":{"position":[[1203,5]]},"257":{"position":[[1985,5],[3977,5],[4537,5],[4752,5],[5384,6]]},"262":{"position":[[97,6],[3379,6]]},"370":{"position":[[1170,5]]},"372":{"position":[[97,5],[264,6]]},"394":{"position":[[363,6],[1589,5],[2345,5]]},"398":{"position":[[1272,6]]},"432":{"position":[[529,5]]},"446":{"position":[[3212,5]]},"450":{"position":[[4765,5]]},"452":{"position":[[216,5],[565,5]]},"462":{"position":[[550,5]]},"476":{"position":[[546,6]]},"508":{"position":[[3142,5],[5222,6]]},"534":{"position":[[2057,5]]},"574":{"position":[[2058,5]]},"596":{"position":[[1092,6]]},"854":{"position":[[1150,5]]},"870":{"position":[[33,5],[182,5]]},"888":{"position":[[743,6]]},"967":{"position":[[104,6]]},"1027":{"position":[[4776,5]]}}}],["tricki",{"_index":449,"t":{"25":{"position":[[1426,7]]}}}],["trim",{"_index":4427,"t":{"724":{"position":[[1031,8]]}}}],["tropical.lied",{"_index":4767,"t":{"804":{"position":[[2323,15]]},"806":{"position":[[3277,16]]}}}],["troubl",{"_index":1484,"t":{"151":{"position":[[117,7]]},"670":{"position":[[2848,7]]},"834":{"position":[[3708,7]]}}}],["troubleshoot",{"_index":4941,"t":{"852":{"position":[[1361,12]]}}}],["true",{"_index":2436,"t":{"370":{"position":[[436,6],[1033,4],[1129,4]]},"470":{"position":[[1170,4]]},"538":{"position":[[3751,4],[3792,6]]},"550":{"position":[[1877,7]]},"552":{"position":[[87,4],[120,4]]},"556":{"position":[[132,5]]},"558":{"position":[[152,5],[2153,5]]},"560":{"position":[[188,4],[235,4],[279,4],[337,4],[391,4],[450,4],[501,4],[556,4],[605,4],[666,4],[718,4]]},"562":{"position":[[269,4],[313,4],[370,4],[420,4],[471,4],[515,4],[576,4],[620,4],[736,4],[798,4],[860,4]]},"566":{"position":[[533,5]]},"598":{"position":[[217,4]]},"668":{"position":[[1595,6]]},"738":{"position":[[4733,5]]},"744":{"position":[[2150,4]]},"802":{"position":[[1343,5],[2768,5]]},"850":{"position":[[2074,4],[2291,4]]},"858":{"position":[[2302,5]]},"985":{"position":[[1089,4],[1161,4],[1262,4]]}}}],["truecp",{"_index":2438,"t":{"370":{"position":[[654,6],[701,6],[753,6],[804,6],[872,6],[919,6],[982,6]]}}}],["truedo",{"_index":3731,"t":{"598":{"position":[[267,6]]}}}],["truli",{"_index":4685,"t":{"786":{"position":[[120,5]]},"840":{"position":[[74,5]]}}}],["trust",{"_index":5086,"t":{"888":{"position":[[914,5]]}}}],["tti",{"_index":96,"t":{"10":{"position":[[93,3],[386,3],[471,3]]},"13":{"position":[[130,4]]},"243":{"position":[[1027,4],[1077,5]]},"262":{"position":[[3138,4],[3171,3]]},"268":{"position":[[166,4]]},"292":{"position":[[382,6],[1806,5],[1905,3]]},"838":{"position":[[1786,4]]},"904":{"position":[[1043,4]]}}}],["tue",{"_index":4160,"t":{"694":{"position":[[480,3],[628,3],[798,3]]},"714":{"position":[[396,4]]},"908":{"position":[[2011,3]]},"1029":{"position":[[400,3]]}}}],["tuesday",{"_index":1926,"t":{"255":{"position":[[1674,8]]},"490":{"position":[[169,9],[829,9],[1025,7]]},"582":{"position":[[187,9],[374,8]]},"1027":{"position":[[4039,9],[4159,8],[5633,9],[6121,9]]},"1033":{"position":[[878,7],[1004,7],[1614,7]]}}}],["tun",{"_index":3014,"t":{"452":{"position":[[143,3]]}}}],["tuna",{"_index":3009,"t":{"452":{"position":[[114,5]]}}}],["tune",{"_index":4068,"t":{"670":{"position":[[1311,4]]},"738":{"position":[[7066,4]]},"834":{"position":[[217,4]]}}}],["tunnel",{"_index":5230,"t":{"910":{"position":[[167,9]]}}}],["turn",{"_index":520,"t":{"31":{"position":[[168,4]]},"33":{"position":[[443,4]]},"69":{"position":[[6107,5]]},"255":{"position":[[1888,4]]},"292":{"position":[[904,4]]},"458":{"position":[[570,5]]},"538":{"position":[[2368,4],[2380,4]]},"588":{"position":[[1082,6]]},"598":{"position":[[332,4],[352,4]]},"600":{"position":[[1388,4]]},"608":{"position":[[2239,5]]},"646":{"position":[[864,4]]},"734":{"position":[[1328,7]]}}}],["tutor",{"_index":1206,"t":{"91":{"position":[[468,6]]}}}],["tutori",{"_index":357,"t":{"23":{"position":[[751,9],[1648,8]]},"626":{"position":[[1001,9]]},"896":{"position":[[544,9]]}}}],["tweak",{"_index":1020,"t":{"67":{"position":[[1116,5]]},"69":{"position":[[3948,5]]},"93":{"position":[[573,5]]},"372":{"position":[[1057,7]]}}}],["twenti",{"_index":2315,"t":{"342":{"position":[[1772,6]]}}}],["twice",{"_index":292,"t":{"21":{"position":[[2486,6]]},"183":{"position":[[404,7]]},"772":{"position":[[1315,6]]},"858":{"position":[[1881,5],[2472,5]]},"892":{"position":[[1409,6]]}}}],["two",{"_index":359,"t":{"23":{"position":[[859,3],[1272,3]]},"77":{"position":[[3367,3],[3996,3]]},"81":{"position":[[1375,3]]},"83":{"position":[[841,3]]},"95":{"position":[[1068,3]]},"135":{"position":[[121,3],[1064,3]]},"137":{"position":[[89,3],[283,3]]},"175":{"position":[[2066,3]]},"183":{"position":[[155,3],[201,3],[220,3],[386,3]]},"203":{"position":[[296,3]]},"217":{"position":[[201,3]]},"239":{"position":[[13,3]]},"247":{"position":[[1021,3]]},"259":{"position":[[487,3]]},"274":{"position":[[837,3]]},"344":{"position":[[382,3]]},"350":{"position":[[1120,3]]},"366":{"position":[[131,3]]},"376":{"position":[[2875,3],[3841,3]]},"396":{"position":[[813,3]]},"430":{"position":[[426,3],[740,3]]},"446":{"position":[[4208,3]]},"448":{"position":[[515,3]]},"450":{"position":[[5017,3]]},"454":{"position":[[0,3]]},"458":{"position":[[1078,3]]},"510":{"position":[[344,3],[699,3],[723,3]]},"512":{"position":[[1111,3]]},"524":{"position":[[832,3]]},"526":{"position":[[428,3]]},"530":{"position":[[48,3]]},"566":{"position":[[169,3]]},"570":{"position":[[230,3]]},"586":{"position":[[1408,3]]},"588":{"position":[[1097,3]]},"612":{"position":[[860,3]]},"616":{"position":[[210,3],[282,3]]},"634":{"position":[[2522,3]]},"642":{"position":[[206,3]]},"668":{"position":[[487,3],[754,3]]},"682":{"position":[[3044,3]]},"684":{"position":[[1719,3],[2132,3]]},"690":{"position":[[32,3],[674,3]]},"692":{"position":[[516,3],[540,3],[2357,3]]},"696":{"position":[[1793,3]]},"698":{"position":[[390,3]]},"720":{"position":[[7471,3]]},"722":{"position":[[2259,3]]},"738":{"position":[[2193,3]]},"756":{"position":[[142,3]]},"772":{"position":[[827,3]]},"774":{"position":[[931,3]]},"802":{"position":[[53,3]]},"816":{"position":[[759,3],[1152,3]]},"820":{"position":[[4,3]]},"838":{"position":[[1069,3]]},"850":{"position":[[318,3]]},"852":{"position":[[2466,3]]},"860":{"position":[[355,3]]},"874":{"position":[[464,3]]},"890":{"position":[[77,3]]},"892":{"position":[[2143,3]]},"902":{"position":[[804,3]]},"908":{"position":[[436,3]]},"910":{"position":[[189,3],[348,3]]},"987":{"position":[[494,3],[595,3]]},"1033":{"position":[[3060,3]]}}}],["twotwo",{"_index":4721,"t":{"802":{"position":[[2203,6]]}}}],["txt",{"_index":1440,"t":{"121":{"position":[[1046,5]]},"213":{"position":[[247,7]]},"215":{"position":[[210,6],[248,7]]},"468":{"position":[[329,7],[1057,7],[1928,7]]},"502":{"position":[[1209,5],[1275,5]]},"1035":{"position":[[2638,5]]}}}],["type",{"_index":145,"t":{"13":{"position":[[336,5]]},"21":{"position":[[2472,4]]},"23":{"position":[[2028,4]]},"61":{"position":[[263,4],[346,4]]},"63":{"position":[[141,4],[227,4]]},"69":{"position":[[3843,4],[3879,5]]},"77":{"position":[[4833,5]]},"133":{"position":[[865,4]]},"135":{"position":[[125,5]]},"165":{"position":[[741,5]]},"183":{"position":[[433,6]]},"193":{"position":[[5,4],[140,4],[158,4],[325,4],[375,4]]},"199":{"position":[[135,4],[332,4]]},"201":{"position":[[314,5]]},"205":{"position":[[182,4]]},"215":{"position":[[700,5],[720,4]]},"219":{"position":[[1741,4]]},"225":{"position":[[689,4]]},"231":{"position":[[828,5]]},"239":{"position":[[319,4]]},"249":{"position":[[1909,4]]},"253":{"position":[[1680,4],[2201,4]]},"266":{"position":[[790,6],[893,6],[1207,4]]},"280":{"position":[[304,6],[1001,5]]},"284":{"position":[[295,5]]},"296":{"position":[[487,5]]},"298":{"position":[[15,5]]},"304":{"position":[[871,4],[887,4],[926,4],[991,4],[1170,4],[1247,4]]},"306":{"position":[[427,4],[472,4]]},"308":{"position":[[29,4],[247,4]]},"310":{"position":[[660,4],[679,4]]},"312":{"position":[[210,4]]},"314":{"position":[[357,4]]},"322":{"position":[[77,5],[130,4]]},"324":{"position":[[141,4]]},"328":{"position":[[0,4],[122,4],[183,4],[289,4]]},"330":{"position":[[135,4]]},"354":{"position":[[467,5]]},"356":{"position":[[113,5]]},"376":{"position":[[1615,4]]},"394":{"position":[[148,5],[2364,4],[2478,6]]},"406":{"position":[[995,5]]},"414":{"position":[[321,4]]},"422":{"position":[[290,4]]},"428":{"position":[[1273,4]]},"438":{"position":[[529,6]]},"464":{"position":[[169,4],[293,4],[617,4],[1225,4]]},"466":{"position":[[152,4],[569,4],[1092,4]]},"468":{"position":[[2952,4]]},"490":{"position":[[2053,5]]},"494":{"position":[[282,4]]},"506":{"position":[[889,5]]},"508":{"position":[[3260,5],[3839,6],[3901,4],[4204,6],[5140,4],[5251,4],[5437,6]]},"534":{"position":[[1426,4],[1794,4],[2148,4],[2267,4]]},"536":{"position":[[707,4],[1528,4]]},"542":{"position":[[578,4]]},"562":{"position":[[361,5]]},"588":{"position":[[637,4],[1881,4]]},"594":{"position":[[1124,6]]},"604":{"position":[[437,6],[1013,6],[1110,4],[1174,4],[1233,4]]},"608":{"position":[[2406,5]]},"614":{"position":[[50,5]]},"626":{"position":[[206,6]]},"630":{"position":[[95,5],[415,5]]},"632":{"position":[[257,4]]},"638":{"position":[[354,5]]},"668":{"position":[[566,5]]},"684":{"position":[[636,4]]},"690":{"position":[[531,4],[631,4]]},"708":{"position":[[117,4]]},"716":{"position":[[4765,4]]},"736":{"position":[[604,4]]},"738":{"position":[[5802,4]]},"758":{"position":[[328,6]]},"760":{"position":[[236,4],[277,4],[301,4],[345,4]]},"764":{"position":[[2549,4]]},"772":{"position":[[1305,4]]},"802":{"position":[[1936,4]]},"804":{"position":[[2418,4]]},"806":{"position":[[3364,4]]},"828":{"position":[[577,5]]},"854":{"position":[[487,5]]},"896":{"position":[[983,5],[1120,5],[1153,4],[1195,5]]},"898":{"position":[[2040,6]]},"902":{"position":[[1178,4]]},"997":{"position":[[370,6],[483,6]]},"1019":{"position":[[56,6]]},"1027":{"position":[[9090,5]]},"1029":{"position":[[584,4],[922,4]]},"1035":{"position":[[1049,5]]},"1037":{"position":[[106,5]]}}}],["type=\"text/css",{"_index":1748,"t":{"225":{"position":[[237,15]]}}}],["type=int",{"_index":4797,"t":{"806":{"position":[[981,9]]}}}],["typer",{"_index":4840,"t":{"810":{"position":[[1412,5]]}}}],["typescript",{"_index":1235,"t":{"91":{"position":[[1653,10]]}}}],["typic",{"_index":271,"t":{"21":{"position":[[2030,9]]},"272":{"position":[[47,7]]},"386":{"position":[[1829,9]]},"550":{"position":[[1749,9]]},"572":{"position":[[325,9]]},"590":{"position":[[369,9]]},"594":{"position":[[1583,9]]},"658":{"position":[[723,9]]},"664":{"position":[[313,9],[1405,9]]},"686":{"position":[[520,7]]},"732":{"position":[[1326,9]]},"826":{"position":[[263,9]]},"860":{"position":[[904,9],[1043,9]]},"884":{"position":[[394,9]]}}}],["typo",{"_index":3828,"t":{"626":{"position":[[2526,5],[2759,5]]},"738":{"position":[[5933,6],[6019,6]]}}}],["t{charact",{"_index":4650,"t":{"774":{"position":[[379,12],[427,12]]}}}],["u",{"_index":637,"t":{"43":{"position":[[489,1]]},"161":{"position":[[87,2],[170,1]]},"255":{"position":[[1082,1]]},"400":{"position":[[1782,1],[1799,1]]},"626":{"position":[[446,1],[2158,1]]},"654":{"position":[[2088,1],[3856,1],[3938,1]]},"656":{"position":[[931,1],[950,1]]},"666":{"position":[[199,1],[1231,1]]},"714":{"position":[[1017,2]]},"716":{"position":[[465,2]]},"720":{"position":[[4495,2]]},"726":{"position":[[151,2]]}}}],["u@\\h",{"_index":4374,"t":{"720":{"position":[[2863,5]]}}}],["ubiquit",{"_index":1306,"t":{"95":{"position":[[199,11]]}}}],["ubuntu",{"_index":216,"t":{"21":{"position":[[538,7]]},"47":{"position":[[323,6]]},"69":{"position":[[2972,6],[3027,7],[3202,6],[3622,6],[4390,6],[4609,6],[4656,7],[4843,7],[5708,6],[6307,9],[6322,6],[6508,6],[6538,6]]},"290":{"position":[[942,7]]},"624":{"position":[[79,6],[144,6]]},"626":{"position":[[3581,6],[4436,6]]},"636":{"position":[[1756,6],[1776,6]]},"708":{"position":[[289,6],[327,6],[773,7],[1101,6],[1156,6]]},"710":{"position":[[289,6]]},"712":{"position":[[88,6]]},"714":{"position":[[2569,6]]},"716":{"position":[[83,6],[191,6],[240,6],[4839,6]]},"718":{"position":[[136,6]]},"720":{"position":[[5402,6],[5482,6],[5571,6],[7390,6]]},"722":{"position":[[2204,6]]},"734":{"position":[[157,6]]}}}],["ubuntu_64",{"_index":1056,"t":{"69":{"position":[[3852,11]]}}}],["uganda",{"_index":4577,"t":{"760":{"position":[[227,8]]}}}],["uid_t",{"_index":753,"t":{"47":{"position":[[2846,5]]}}}],["unabl",{"_index":3478,"t":{"538":{"position":[[3649,6]]},"568":{"position":[[1065,7]]}}}],["unam",{"_index":5029,"t":{"864":{"position":[[338,5],[422,10]]}}}],["unambigu",{"_index":5390,"t":{"1027":{"position":[[820,13]]}}}],["unauthor",{"_index":2733,"t":{"402":{"position":[[855,15],[1215,15]]}}}],["unchang",{"_index":4115,"t":{"684":{"position":[[2052,10]]}}}],["uncom",{"_index":4442,"t":{"734":{"position":[[1260,9]]},"738":{"position":[[5177,9],[5329,11],[7573,9],[7669,9],[7741,9]]},"852":{"position":[[2363,9]]}}}],["uncommit",{"_index":4036,"t":{"668":{"position":[[896,11]]}}}],["undefin",{"_index":3188,"t":{"484":{"position":[[1355,9]]},"522":{"position":[[1253,10]]}}}],["under",{"_index":85,"t":{"8":{"position":[[460,5]]},"67":{"position":[[209,5]]},"69":{"position":[[35,5]]},"185":{"position":[[189,5]]},"278":{"position":[[920,5]]},"394":{"position":[[154,5]]},"464":{"position":[[1199,5]]},"506":{"position":[[2924,5]]},"654":{"position":[[1390,5]]},"724":{"position":[[826,5]]},"808":{"position":[[715,5],[1115,5]]}}}],["underli",{"_index":4003,"t":{"664":{"position":[[1946,10]]},"678":{"position":[[1285,10]]}}}],["underlin",{"_index":4059,"t":{"668":{"position":[[3078,11]]},"720":{"position":[[5855,11]]},"862":{"position":[[873,9]]},"1003":{"position":[[118,10]]}}}],["underscor",{"_index":537,"t":{"31":{"position":[[1273,11],[2170,11]]},"720":{"position":[[1626,10]]},"1003":{"position":[[102,11]]}}}],["understand",{"_index":98,"t":{"10":{"position":[[106,10]]},"13":{"position":[[232,10]]},"45":{"position":[[847,13]]},"77":{"position":[[853,10],[4992,10]]},"95":{"position":[[1991,10]]},"129":{"position":[[36,10]]},"147":{"position":[[618,13]]},"217":{"position":[[63,10]]},"243":{"position":[[1122,13]]},"251":{"position":[[1107,10]]},"257":{"position":[[881,11]]},"262":{"position":[[205,13],[3619,13]]},"268":{"position":[[60,10]]},"270":{"position":[[3,10]]},"288":{"position":[[492,13]]},"296":{"position":[[28,11],[407,10]]},"310":{"position":[[35,13]]},"332":{"position":[[831,13]]},"350":{"position":[[1591,10],[1663,10]]},"384":{"position":[[300,10]]},"416":{"position":[[272,10]]},"422":{"position":[[82,10],[1517,11]]},"428":{"position":[[691,13]]},"464":{"position":[[1805,10]]},"466":{"position":[[1302,10]]},"472":{"position":[[431,13]]},"502":{"position":[[1431,13]]},"508":{"position":[[1293,11]]},"512":{"position":[[13,13]]},"624":{"position":[[798,10]]},"626":{"position":[[653,13]]},"630":{"position":[[21,10],[70,10],[383,10]]},"638":{"position":[[329,10],[422,10]]},"670":{"position":[[2476,13]]},"694":{"position":[[2347,10]]},"710":{"position":[[556,10]]},"714":{"position":[[1988,10]]},"724":{"position":[[773,10]]},"728":{"position":[[315,10]]},"766":{"position":[[56,13]]},"796":{"position":[[627,10]]},"866":{"position":[[26,13]]},"1037":{"position":[[323,10]]}}}],["understood",{"_index":4697,"t":{"796":{"position":[[1663,10]]}}}],["undo",{"_index":1427,"t":{"119":{"position":[[330,4]]},"157":{"position":[[100,4],[163,4]]},"159":{"position":[[166,4]]}}}],["unexpect",{"_index":3175,"t":{"482":{"position":[[758,10]]},"522":{"position":[[2144,10]]},"538":{"position":[[2087,10]]},"540":{"position":[[860,10]]},"554":{"position":[[641,10]]},"610":{"position":[[806,10]]},"738":{"position":[[2038,10]]},"850":{"position":[[286,10]]}}}],["unexpectedli",{"_index":875,"t":{"47":{"position":[[7300,13]]},"722":{"position":[[3002,13]]}}}],["unfair",{"_index":4062,"t":{"670":{"position":[[523,6]]}}}],["unfamiliar",{"_index":529,"t":{"31":{"position":[[519,11]]},"352":{"position":[[369,11]]},"416":{"position":[[207,11]]},"428":{"position":[[665,10]]},"862":{"position":[[3186,11]]}}}],["unformat",{"_index":459,"t":{"27":{"position":[[97,11]]}}}],["unfortun",{"_index":4686,"t":{"788":{"position":[[209,13]]}}}],["unhandl",{"_index":2653,"t":{"398":{"position":[[632,9],[764,9],[896,9],[1028,9],[1156,9]]},"454":{"position":[[1514,9],[1667,9],[1772,9]]}}}],["unheard",{"_index":1303,"t":{"95":{"position":[[136,7]]}}}],["unifi",{"_index":1307,"t":{"95":{"position":[[228,5]]}}}],["uniform",{"_index":1281,"t":{"93":{"position":[[482,8],[1260,7]]}}}],["uniformli",{"_index":779,"t":{"47":{"position":[[4273,9]]}}}],["uninstal",{"_index":4002,"t":{"664":{"position":[[1751,9]]}}}],["unintend",{"_index":3386,"t":{"522":{"position":[[1396,10]]}}}],["unintuit",{"_index":3458,"t":{"538":{"position":[[151,11]]}}}],["uniq",{"_index":538,"t":{"31":{"position":[[1304,4],[1528,4],[1957,4],[2109,4]]},"33":{"position":[[656,5]]},"251":{"position":[[488,4]]},"259":{"position":[[197,4]]},"260":{"position":[[308,4],[687,4]]},"424":{"position":[[186,4],[855,4]]},"426":{"position":[[327,4]]},"430":{"position":[[357,4]]},"438":{"position":[[380,4]]},"442":{"position":[[377,4]]},"454":{"position":[[60,5],[1040,6],[1140,4],[1368,4],[2538,4]]},"458":{"position":[[1529,4]]},"512":{"position":[[375,4],[1024,4]]},"574":{"position":[[686,4],[1814,4],[2229,4]]},"612":{"position":[[779,4]]}}}],["uniqagn",{"_index":535,"t":{"31":{"position":[[1101,9]]},"251":{"position":[[596,9]]}}}],["uniqerror",{"_index":3055,"t":{"454":{"position":[[1502,9]]}}}],["uniqu",{"_index":1989,"t":{"259":{"position":[[596,6]]},"260":{"position":[[150,6]]},"454":{"position":[[1011,7],[1387,6],[2555,6]]},"694":{"position":[[228,8]]},"702":{"position":[[1169,8]]},"1011":{"position":[[25,6]]}}}],["unistd.h",{"_index":748,"t":{"47":{"position":[[2807,10]]}}}],["unit",{"_index":1827,"t":{"237":{"position":[[1204,5]]},"292":{"position":[[476,5]]},"1019":{"position":[[1268,5]]}}}],["univers",{"_index":238,"t":{"21":{"position":[[1066,11]]},"107":{"position":[[607,9],[832,9]]},"249":{"position":[[440,12]]},"796":{"position":[[1543,11],[2041,9]]}}}],["unix",{"_index":590,"t":{"37":{"position":[[706,4]]},"43":{"position":[[1308,4]]},"47":{"position":[[215,4]]},"65":{"position":[[28,4]]},"67":{"position":[[199,5],[349,4]]},"93":{"position":[[664,4]]},"189":{"position":[[1955,5]]},"207":{"position":[[734,4]]},"249":{"position":[[1560,4]]},"260":{"position":[[1284,4]]},"262":{"position":[[2638,4]]},"288":{"position":[[825,4]]},"290":{"position":[[2826,4]]},"328":{"position":[[20,4]]},"330":{"position":[[120,4]]},"332":{"position":[[67,4]]},"392":{"position":[[492,4],[787,4]]},"404":{"position":[[911,4]]},"432":{"position":[[1355,4]]},"532":{"position":[[928,4]]},"622":{"position":[[761,5],[781,4],[1207,4]]},"636":{"position":[[2265,5]]},"716":{"position":[[745,5]]},"738":{"position":[[862,5]]},"744":{"position":[[32,4]]},"834":{"position":[[289,4]]},"862":{"position":[[649,4]]},"864":{"position":[[22,4]]},"874":{"position":[[902,4],[1014,4]]},"876":{"position":[[185,4]]},"943":{"position":[[212,4]]},"951":{"position":[[88,4]]},"965":{"position":[[71,4],[139,4]]},"969":{"position":[[206,4]]}}}],["unknown",{"_index":4099,"t":{"680":{"position":[[1639,7]]}}}],["unless",{"_index":715,"t":{"47":{"position":[[1499,6],[2522,6]]},"69":{"position":[[1633,6],[3929,6]]},"125":{"position":[[516,6]]},"175":{"position":[[785,6]]},"320":{"position":[[63,6]]},"376":{"position":[[3033,6]]},"634":{"position":[[2726,6]]},"638":{"position":[[798,6]]},"714":{"position":[[1435,6]]},"732":{"position":[[197,6]]},"734":{"position":[[1165,6]]},"760":{"position":[[1747,6]]},"854":{"position":[[891,6]]},"1027":{"position":[[2501,7]]}}}],["unlik",{"_index":1371,"t":{"105":{"position":[[376,6]]},"125":{"position":[[302,6]]},"209":{"position":[[448,8]]},"243":{"position":[[850,9]]},"320":{"position":[[44,8]]}}}],["unmatch",{"_index":3665,"t":{"586":{"position":[[1507,9]]}}}],["unmerg",{"_index":4189,"t":{"696":{"position":[[2344,8]]}}}],["unnecessari",{"_index":4492,"t":{"738":{"position":[[3647,12]]}}}],["unnecessarili",{"_index":4627,"t":{"770":{"position":[[274,14]]}}}],["unneed",{"_index":4072,"t":{"670":{"position":[[2005,8]]}}}],["unpack",{"_index":1366,"t":{"103":{"position":[[53,6]]}}}],["unprotect",{"_index":5156,"t":{"900":{"position":[[203,11]]}}}],["unquot",{"_index":2486,"t":{"376":{"position":[[3845,8],[5214,8]]},"616":{"position":[[47,8]]}}}],["unrel",{"_index":1004,"t":{"67":{"position":[[303,9]]}}}],["unrespons",{"_index":5189,"t":{"906":{"position":[[382,12]]}}}],["unset",{"_index":3668,"t":{"586":{"position":[[1635,5]]},"626":{"position":[[2132,5],[2160,6],[7091,5],[7108,5]]},"738":{"position":[[4020,5]]},"744":{"position":[[1689,5]]},"856":{"position":[[89,5],[95,6]]},"927":{"position":[[168,5]]},"1025":{"position":[[209,5],[299,5]]},"1027":{"position":[[2305,5],[2430,6],[3206,6],[5333,5]]}}}],["unstag",{"_index":3989,"t":{"660":{"position":[[1511,8]]},"672":{"position":[[709,7]]},"682":{"position":[[1300,8],[1744,11],[2760,8]]},"700":{"position":[[584,8],[1899,9],[3275,8],[3743,8]]},"704":{"position":[[140,7],[813,7],[889,7]]},"927":{"position":[[766,7],[842,7]]}}}],["unsupport",{"_index":5037,"t":{"864":{"position":[[555,12]]}}}],["unsur",{"_index":1385,"t":{"107":{"position":[[1010,6]]}}}],["untar",{"_index":4927,"t":{"850":{"position":[[838,5],[1175,5]]}}}],["until",{"_index":1348,"t":{"99":{"position":[[175,5]]},"111":{"position":[[249,5]]},"175":{"position":[[1120,5]]},"205":{"position":[[1419,5]]},"227":{"position":[[299,5]]},"243":{"position":[[427,5],[1265,5]]},"247":{"position":[[901,6]]},"260":{"position":[[517,5]]},"292":{"position":[[792,5]]},"350":{"position":[[203,5],[736,5],[799,5],[997,5],[1232,5]]},"366":{"position":[[4152,5],[4393,5]]},"400":{"position":[[89,5]]},"408":{"position":[[407,5]]},"508":{"position":[[396,5],[4744,5]]},"594":{"position":[[48,5]]},"596":{"position":[[617,5]]},"598":{"position":[[643,5]]},"600":{"position":[[4,5],[70,5],[131,5],[171,5],[510,5],[700,5],[1221,5],[1333,5],[1397,5]]},"604":{"position":[[792,5],[1223,5]]},"802":{"position":[[1301,5]]},"860":{"position":[[980,5]]},"927":{"position":[[2034,5]]},"985":{"position":[[1657,5]]},"1027":{"position":[[7534,5],[8539,5]]}}}],["untrack",{"_index":4104,"t":{"682":{"position":[[436,9],[764,12]]},"684":{"position":[[482,9],[946,9],[2322,9]]},"688":{"position":[[1158,9]]},"700":{"position":[[2943,9]]}}}],["ununtu",{"_index":4229,"t":{"708":{"position":[[1339,6],[1396,6]]}}}],["unusu",{"_index":3877,"t":{"636":{"position":[[2222,7]]}}}],["unwieldi",{"_index":4842,"t":{"810":{"position":[[1509,8]]}}}],["unzip",{"_index":1354,"t":{"99":{"position":[[521,5],[2063,5]]},"101":{"position":[[296,5]]},"103":{"position":[[266,5]]},"105":{"position":[[30,8],[796,8]]},"123":{"position":[[94,5]]},"125":{"position":[[374,5],[392,5]]},"923":{"position":[[45,5]]}}}],["unzoom",{"_index":4863,"t":{"828":{"position":[[758,8]]}}}],["up",{"_index":180,"t":{"17":{"position":[[347,2]]},"25":{"position":[[1331,2]]},"27":{"position":[[560,2]]},"37":{"position":[[110,3],[278,2]]},"41":{"position":[[807,2]]},"43":{"position":[[500,2],[538,2]]},"45":{"position":[[32,2],[389,2]]},"47":{"position":[[1154,2],[2045,2]]},"69":{"position":[[4223,2],[4632,3],[6066,2],[6100,2],[6267,2],[6349,2]]},"77":{"position":[[2120,2],[4895,2]]},"81":{"position":[[97,4],[641,3]]},"91":{"position":[[909,2],[3023,2]]},"99":{"position":[[256,2],[484,2],[734,2]]},"115":{"position":[[964,2]]},"123":{"position":[[29,2],[153,2]]},"133":{"position":[[903,2]]},"137":{"position":[[902,3]]},"227":{"position":[[137,2]]},"235":{"position":[[263,3]]},"249":{"position":[[2035,2]]},"251":{"position":[[1458,2]]},"255":{"position":[[809,2],[1104,3]]},"260":{"position":[[77,3]]},"274":{"position":[[773,2]]},"276":{"position":[[201,2]]},"284":{"position":[[573,2]]},"288":{"position":[[1542,2]]},"292":{"position":[[1979,2]]},"306":{"position":[[321,2]]},"338":{"position":[[1371,2]]},"348":{"position":[[121,2]]},"350":{"position":[[796,2]]},"352":{"position":[[3052,2]]},"354":{"position":[[774,2],[797,2]]},"356":{"position":[[464,2]]},"366":{"position":[[4149,2],[4390,2]]},"368":{"position":[[1419,2]]},"378":{"position":[[1616,2]]},"380":{"position":[[484,2]]},"386":{"position":[[288,2]]},"394":{"position":[[187,2]]},"396":{"position":[[675,2]]},"400":{"position":[[1418,2]]},"422":{"position":[[914,2]]},"426":{"position":[[109,3],[508,2]]},"446":{"position":[[4249,2]]},"462":{"position":[[438,3]]},"464":{"position":[[2170,2]]},"466":{"position":[[1046,2],[1174,2]]},"468":{"position":[[2214,2]]},"506":{"position":[[1049,2]]},"508":{"position":[[4741,2],[5322,2]]},"538":{"position":[[1217,2],[2493,2],[3246,2]]},"584":{"position":[[281,2],[728,2],[930,2],[1092,4],[1603,3],[2203,2],[2299,2]]},"588":{"position":[[797,3],[1671,2]]},"600":{"position":[[651,2]]},"608":{"position":[[61,2]]},"610":{"position":[[138,2]]},"622":{"position":[[862,3]]},"624":{"position":[[134,2],[187,2],[1113,2]]},"626":{"position":[[106,3],[3554,2],[6786,3],[6889,2]]},"636":{"position":[[308,2],[834,2],[1625,2],[1706,2]]},"640":{"position":[[1448,2]]},"650":{"position":[[311,2],[417,2]]},"654":{"position":[[347,2],[923,2],[2328,2],[2644,2],[3082,2]]},"656":{"position":[[292,2]]},"664":{"position":[[608,2]]},"668":{"position":[[748,2]]},"682":{"position":[[1821,2]]},"684":{"position":[[213,3]]},"688":{"position":[[1636,2]]},"692":{"position":[[1167,2]]},"698":{"position":[[707,2]]},"704":{"position":[[371,2]]},"708":{"position":[[361,3],[384,2],[467,2],[549,2]]},"714":{"position":[[567,2]]},"716":{"position":[[661,2],[5070,4]]},"724":{"position":[[1053,2]]},"728":{"position":[[625,2]]},"732":{"position":[[165,2]]},"734":{"position":[[3450,2]]},"736":{"position":[[753,2],[1466,2]]},"738":{"position":[[2403,2],[4085,2],[5714,2],[7427,2],[7504,2]]},"740":{"position":[[981,3]]},"766":{"position":[[669,3],[1155,2],[1297,2]]},"778":{"position":[[1918,2]]},"800":{"position":[[934,2]]},"804":{"position":[[2823,2]]},"816":{"position":[[1528,2]]},"818":{"position":[[1059,2],[1339,2]]},"828":{"position":[[672,2]]},"832":{"position":[[55,3],[765,2]]},"834":{"position":[[3180,3]]},"856":{"position":[[46,2]]},"858":{"position":[[1562,2],[1633,2]]},"892":{"position":[[2436,2]]},"894":{"position":[[315,2],[392,2],[450,2],[1188,2]]},"896":{"position":[[1881,3],[2619,3]]},"906":{"position":[[906,3]]},"910":{"position":[[801,2]]},"914":{"position":[[298,2]]},"997":{"position":[[429,2]]},"1019":{"position":[[117,2],[1237,2]]},"1027":{"position":[[3889,2],[4803,2],[5008,3],[5242,2],[5313,3],[5833,2],[7163,2],[7531,2],[8098,2]]},"1033":{"position":[[4689,2]]}}}],["up2",{"_index":2799,"t":{"428":{"position":[[1000,4]]}}}],["up6",{"_index":1823,"t":{"237":{"position":[[761,4]]}}}],["updat",{"_index":1014,"t":{"67":{"position":[[834,7]]},"71":{"position":[[1638,6]]},"255":{"position":[[2096,6]]},"262":{"position":[[2955,6],[2996,7]]},"414":{"position":[[539,6]]},"446":{"position":[[3107,7]]},"512":{"position":[[675,6],[1467,6]]},"522":{"position":[[896,6]]},"544":{"position":[[549,6],[1739,7],[1830,6]]},"558":{"position":[[186,8]]},"574":{"position":[[50,6],[139,6],[3349,6]]},"604":{"position":[[605,7]]},"606":{"position":[[152,6]]},"612":{"position":[[1415,7],[1506,6]]},"626":{"position":[[5193,7]]},"648":{"position":[[2047,6],[2094,6]]},"664":{"position":[[1036,6]]},"692":{"position":[[1330,7]]},"696":{"position":[[1042,6],[3705,6],[4850,6]]},"700":{"position":[[1170,6],[1956,8],[2545,6]]},"708":{"position":[[1068,8]]},"716":{"position":[[4492,6]]},"722":{"position":[[2913,7]]},"734":{"position":[[2132,7]]},"736":{"position":[[1508,6]]},"738":{"position":[[7387,6]]},"744":{"position":[[1352,6],[2462,7]]},"760":{"position":[[1447,7]]},"766":{"position":[[209,6]]},"778":{"position":[[15,6]]},"804":{"position":[[1674,6]]},"860":{"position":[[134,6]]},"914":{"position":[[419,7]]},"951":{"position":[[108,8]]},"973":{"position":[[8,6]]}}}],["updated.md",{"_index":2501,"t":{"376":{"position":[[5630,10],[5725,10]]}}}],["upgrad",{"_index":1083,"t":{"71":{"position":[[297,7]]},"802":{"position":[[323,8]]}}}],["upload",{"_index":5127,"t":{"896":{"position":[[167,6],[436,6],[2253,9]]}}}],["upon",{"_index":2764,"t":{"420":{"position":[[726,4]]},"664":{"position":[[1291,4]]},"906":{"position":[[639,4]]}}}],["upper",{"_index":1948,"t":{"257":{"position":[[1250,11]]},"448":{"position":[[1776,5]]},"532":{"position":[[221,11]]},"772":{"position":[[565,8],[1657,7]]},"816":{"position":[[794,5]]}}}],["upper:]'b",{"_index":1944,"t":{"257":{"position":[[593,13]]}}}],["upper:]'mkdir",{"_index":1946,"t":{"257":{"position":[[736,17],[3747,17]]}}}],["upper:]]'us",{"_index":2910,"t":{"448":{"position":[[1645,16]]}}}],["uppercas",{"_index":1941,"t":{"257":{"position":[[383,10],[430,9],[850,10]]},"344":{"position":[[1091,11]]},"448":{"position":[[1788,10]]},"480":{"position":[[534,9]]},"482":{"position":[[627,9],[665,9]]},"506":{"position":[[1252,9],[1338,10]]},"508":{"position":[[709,10]]},"532":{"position":[[155,9]]},"772":{"position":[[550,9],[943,9],[1642,9]]},"774":{"position":[[1164,9]]},"1027":{"position":[[9486,9],[9584,10]]}}}],["upstream",{"_index":2704,"t":{"400":{"position":[[1828,8]]},"656":{"position":[[957,9],[1176,10]]},"658":{"position":[[1795,8],[2278,8]]},"660":{"position":[[146,10],[1913,10]]},"672":{"position":[[552,9],[951,8]]},"692":{"position":[[1338,8]]}}}],["upward",{"_index":1963,"t":{"257":{"position":[[3439,7]]}}}],["urandom",{"_index":773,"t":{"47":{"position":[[4167,7]]}}}],["urgency=low",{"_index":4474,"t":{"734":{"position":[[2835,11]]}}}],["url",{"_index":2724,"t":{"402":{"position":[[702,6],[1062,6]]},"804":{"position":[[643,3]]}}}],["urllib",{"_index":4841,"t":{"810":{"position":[[1455,6]]}}}],["urllib.error.httperror",{"_index":4745,"t":{"804":{"position":[[977,22]]}}}],["urllib.parse.quote(word.encode('utf8",{"_index":4737,"t":{"804":{"position":[[522,39]]}}}],["urllib.request.urlopen(url",{"_index":4739,"t":{"804":{"position":[[734,27],[825,27]]}}}],["ursula",{"_index":1430,"t":{"121":{"position":[[118,6]]}}}],["us",{"_index":83,"t":{"8":{"position":[[393,6],[403,3]]},"10":{"position":[[315,3],[380,3]]},"21":{"position":[[487,3],[824,4],[883,5],[957,4],[1078,4],[1625,4],[1825,4],[1958,4],[1966,5],[2969,3],[3110,6],[3136,5],[3238,5],[3312,3],[3390,3],[3570,5],[3621,3],[3763,3],[3821,3],[4111,3]]},"23":{"position":[[54,7],[741,5],[813,5],[1876,4],[2413,3],[2501,6]]},"25":{"position":[[46,3],[498,3],[1119,3],[1182,6],[1280,3],[1483,5],[1516,3],[1640,6]]},"27":{"position":[[746,7],[765,3]]},"29":{"position":[[94,3]]},"33":{"position":[[163,4],[334,3]]},"37":{"position":[[44,5],[356,6],[777,6],[863,4]]},"41":{"position":[[778,3]]},"43":{"position":[[391,3],[562,3],[801,5],[957,4],[996,3]]},"47":{"position":[[1049,6],[1614,4],[1640,6],[1824,6],[2372,4],[2505,3],[3258,3],[3440,3],[5526,4],[5732,3],[8009,3],[8384,4],[8444,4],[8557,5],[8616,3]]},"49":{"position":[[939,3]]},"51":{"position":[[1009,3]]},"53":{"position":[[105,4],[304,3],[345,4]]},"57":{"position":[[172,4],[471,4],[500,5],[598,5],[830,3],[983,5],[1151,3],[1232,5]]},"61":{"position":[[68,5],[567,4]]},"63":{"position":[[11,5]]},"65":{"position":[[11,5],[126,3],[156,3],[263,5]]},"67":{"position":[[88,3],[456,5],[599,5],[1046,5],[1348,3],[1485,6]]},"69":{"position":[[101,3],[191,3],[229,3],[619,3],[1591,4],[2063,3],[2086,5],[2245,3],[2275,4],[2525,3],[2838,3],[4295,4],[4969,3],[5738,3],[6023,3],[6534,3],[6569,5]]},"71":{"position":[[589,3],[657,4],[1365,5],[1547,4],[1673,3],[2146,4]]},"77":{"position":[[18,4],[226,4],[1378,4],[2826,3],[3778,5]]},"79":{"position":[[22,4]]},"81":{"position":[[216,6],[548,3],[949,4],[1218,3],[1260,3],[1944,5],[2367,4],[2436,4]]},"83":{"position":[[188,3]]},"87":{"position":[[83,3]]},"91":{"position":[[1058,3],[2862,3]]},"93":{"position":[[133,3],[281,4],[775,5]]},"95":{"position":[[1502,5],[2611,3]]},"99":{"position":[[313,3],[1379,6],[1621,3]]},"101":{"position":[[105,6]]},"103":{"position":[[225,3]]},"105":{"position":[[150,4],[1088,3]]},"107":{"position":[[279,3],[514,3],[595,5],[819,3]]},"109":{"position":[[96,5]]},"113":{"position":[[187,3],[778,3]]},"115":{"position":[[322,3]]},"117":{"position":[[145,3],[252,4],[491,3],[518,4],[790,4]]},"121":{"position":[[69,3],[981,3],[1248,3]]},"123":{"position":[[126,3]]},"125":{"position":[[112,3],[225,4],[287,6],[1192,4],[1298,3],[1394,3]]},"129":{"position":[[75,5],[97,3],[1020,4],[1054,3]]},"133":{"position":[[562,6]]},"135":{"position":[[936,3],[1236,6],[1328,6]]},"137":{"position":[[455,7],[773,3],[886,3],[979,4],[1107,4],[1369,4]]},"139":{"position":[[884,5],[1005,6]]},"141":{"position":[[516,4],[838,6]]},"143":{"position":[[194,3],[219,4],[367,3]]},"145":{"position":[[832,3],[903,3]]},"147":{"position":[[298,5],[433,5],[661,6]]},"157":{"position":[[74,6]]},"161":{"position":[[111,5]]},"163":{"position":[[118,5]]},"165":{"position":[[598,6],[659,4],[820,3],[1054,4]]},"171":{"position":[[76,3]]},"175":{"position":[[115,6],[254,3],[530,5],[698,4],[746,3],[1213,5],[1728,5],[2772,3]]},"177":{"position":[[24,3]]},"181":{"position":[[8,3],[335,6],[353,3]]},"183":{"position":[[166,3],[475,7]]},"185":{"position":[[168,3],[442,3],[530,4],[574,4],[726,3],[903,6],[1066,7]]},"189":{"position":[[39,4],[1078,5],[1270,3],[1396,3],[1698,6],[2094,3],[2407,3],[2490,5]]},"191":{"position":[[24,3],[104,4],[229,5]]},"193":{"position":[[27,4],[133,5],[318,5]]},"195":{"position":[[7,3],[403,4],[672,3],[775,5],[1135,4],[1751,3],[2007,4],[2739,5]]},"197":{"position":[[27,4]]},"199":{"position":[[316,5],[595,3]]},"201":{"position":[[328,3]]},"203":{"position":[[68,5],[90,5]]},"205":{"position":[[895,4],[1438,4],[1512,3],[1603,3]]},"207":{"position":[[398,5],[431,3],[996,5]]},"209":{"position":[[468,3]]},"211":{"position":[[45,5],[626,3]]},"213":{"position":[[8,3],[62,3],[517,3],[578,3],[1029,7]]},"215":{"position":[[162,3]]},"217":{"position":[[782,5],[1191,5]]},"219":{"position":[[457,4],[626,4],[741,4],[920,4],[1179,4],[1690,3],[1796,4]]},"221":{"position":[[118,5]]},"225":{"position":[[497,6],[627,6],[1723,3]]},"227":{"position":[[34,5]]},"229":{"position":[[816,4],[911,3]]},"231":{"position":[[1253,3],[1894,5],[2258,6]]},"235":{"position":[[246,4],[559,6],[786,4],[953,3],[1098,3]]},"237":{"position":[[585,4],[1025,6]]},"241":{"position":[[11,5],[1079,3]]},"243":{"position":[[29,7],[672,5],[1177,3],[1394,3],[1604,6]]},"247":{"position":[[533,3],[597,3],[1015,5]]},"249":{"position":[[697,7]]},"251":{"position":[[1506,4]]},"253":{"position":[[381,3],[543,3],[738,4],[799,4],[987,5],[1059,3],[1611,3],[1970,4],[3103,3],[3210,3],[3367,4]]},"255":{"position":[[690,3],[1068,3],[1196,6],[2038,6]]},"257":{"position":[[323,3],[460,3],[643,3],[1961,3],[2602,4],[2823,4],[2849,6],[3490,4],[4523,3],[4946,3],[5755,3]]},"259":{"position":[[710,6],[736,3]]},"260":{"position":[[1229,3]]},"262":{"position":[[165,5],[1189,3],[1322,4],[2016,5],[2596,3],[3375,3]]},"266":{"position":[[142,5],[258,5],[519,3],[663,4],[748,3],[875,5],[1175,4],[1313,3]]},"268":{"position":[[50,6]]},"276":{"position":[[900,6],[924,4]]},"278":{"position":[[298,3],[513,3]]},"280":{"position":[[766,4]]},"282":{"position":[[80,5],[152,5],[242,4],[1848,3],[2231,3]]},"284":{"position":[[522,4],[776,5],[1756,3]]},"286":{"position":[[495,3],[524,3],[654,4]]},"288":{"position":[[184,4],[1178,4]]},"290":{"position":[[643,5],[2233,4],[2656,3]]},"292":{"position":[[134,6]]},"296":{"position":[[525,3]]},"300":{"position":[[292,4]]},"302":{"position":[[326,5],[503,4]]},"304":{"position":[[290,4],[333,4],[522,5],[863,3],[1042,5],[1226,5],[1698,7],[2482,3],[2620,4]]},"306":{"position":[[220,6],[639,6]]},"308":{"position":[[319,3],[515,3]]},"310":{"position":[[320,4],[652,3]]},"314":{"position":[[65,4],[280,3]]},"316":{"position":[[60,6],[220,4]]},"320":{"position":[[56,3],[89,5]]},"326":{"position":[[380,6]]},"328":{"position":[[256,4]]},"330":{"position":[[161,4],[518,3],[539,3]]},"332":{"position":[[564,4],[686,3]]},"336":{"position":[[303,4],[355,7]]},"338":{"position":[[244,5],[369,4],[689,3],[1444,3]]},"340":{"position":[[676,6]]},"342":{"position":[[649,3],[797,4],[875,3],[1355,4]]},"344":{"position":[[405,3],[518,5],[910,3],[2265,3],[2391,4],[2671,4],[3231,3],[3634,4],[3872,5],[4113,3],[4555,3],[4806,4]]},"346":{"position":[[21,3],[483,3],[661,3],[1149,3],[1463,6]]},"348":{"position":[[68,5],[733,3]]},"350":{"position":[[867,5],[1350,5],[1653,6]]},"352":{"position":[[145,3],[1699,4],[2577,4],[2929,3]]},"354":{"position":[[336,6],[942,3],[1100,3],[1254,5]]},"362":{"position":[[152,3]]},"364":{"position":[[679,3],[1779,4],[2270,3],[2308,3],[2503,3]]},"366":{"position":[[7,3],[2772,3],[2848,3],[3103,5],[3331,4],[3968,3],[4034,3],[4495,4],[4527,3],[4579,3],[4668,5],[4729,4],[4827,3],[4938,3]]},"368":{"position":[[20,4],[63,3],[802,3],[1542,4]]},"370":{"position":[[86,3],[275,3]]},"372":{"position":[[255,3],[1296,5]]},"376":{"position":[[652,4],[753,3],[2092,6],[2647,5],[3194,6],[4038,3],[4264,5]]},"378":{"position":[[42,6],[73,3],[1076,5],[1576,4],[1830,5],[3067,5],[3305,3],[3633,4],[4504,3]]},"380":{"position":[[349,4],[686,6],[733,5]]},"384":{"position":[[612,5]]},"386":{"position":[[208,5],[384,3],[711,3],[953,4],[1315,4],[1623,5],[1724,3],[1789,3],[1860,3]]},"390":{"position":[[1271,4],[1942,3]]},"392":{"position":[[42,4],[207,4]]},"394":{"position":[[179,3],[253,3],[950,5],[980,3],[1519,3],[1606,3]]},"396":{"position":[[295,3],[470,5],[594,3],[662,4],[1139,5],[1260,4],[1342,3],[1374,3],[1420,3],[1506,5],[2050,4],[2255,5],[2426,4]]},"398":{"position":[[25,3],[287,5],[1265,6],[1289,3]]},"400":{"position":[[23,6],[59,4],[708,5],[743,3],[1301,6],[1957,6]]},"402":{"position":[[262,6],[449,3],[1389,4]]},"406":{"position":[[103,3]]},"408":{"position":[[24,4],[386,5],[549,4]]},"410":{"position":[[242,3],[532,3],[623,3]]},"414":{"position":[[383,3],[702,7]]},"416":{"position":[[67,4],[381,7]]},"418":{"position":[[83,4],[133,5]]},"420":{"position":[[400,5],[1151,5],[1245,3]]},"422":{"position":[[411,5],[661,3],[781,3],[1160,3],[1287,3],[1372,6]]},"424":{"position":[[134,4],[388,5],[528,3]]},"426":{"position":[[8,3],[271,4]]},"428":{"position":[[553,3],[1029,5],[1123,5],[1393,5]]},"430":{"position":[[110,4],[305,4],[893,4],[1075,3],[1120,3]]},"432":{"position":[[8,3],[122,3],[571,3],[709,4],[762,3],[1175,4],[1271,3],[1527,5],[1589,5]]},"434":{"position":[[57,3],[1172,3],[1371,3],[1511,3],[1709,5],[1890,6]]},"438":{"position":[[93,4],[328,4],[1367,3],[1614,4]]},"440":{"position":[[277,3]]},"442":{"position":[[373,3],[670,5],[717,5],[1545,3],[2052,6]]},"446":{"position":[[58,7],[74,4],[128,4],[189,5],[231,5],[1145,3],[1216,3],[1536,6],[2311,4],[2732,4],[3220,3],[3246,3],[3463,3],[4144,3]]},"448":{"position":[[102,3],[579,4],[744,4],[810,4],[934,5],[1367,3],[1599,4],[2141,6]]},"450":{"position":[[35,4],[105,5],[447,3],[727,7],[824,3],[1914,3],[4073,3],[4296,3],[4779,3],[5035,4],[5471,3]]},"452":{"position":[[188,6],[650,3]]},"454":{"position":[[1075,6],[1364,3]]},"456":{"position":[[79,4],[245,3],[389,3],[420,3]]},"458":{"position":[[184,5],[290,4],[439,4],[710,4],[804,4],[953,4],[1627,3],[1708,6]]},"462":{"position":[[60,4],[225,4],[280,3],[531,5],[800,4],[847,3],[1073,5],[1251,3],[2475,6],[2726,5]]},"464":{"position":[[541,3],[1079,3],[1122,3],[1748,4],[2286,3]]},"466":{"position":[[504,3],[1042,3],[1233,3],[1379,3]]},"468":{"position":[[662,4],[1436,3],[1490,3],[1752,5],[1793,3],[2158,3],[2449,3],[2728,4],[2801,4],[2893,4],[3005,4],[3084,3],[3148,3]]},"470":{"position":[[237,5],[312,5],[518,3],[865,4],[1276,4]]},"472":{"position":[[458,3],[572,3]]},"474":{"position":[[430,3],[664,4],[750,4]]},"476":{"position":[[259,3]]},"480":{"position":[[226,4],[271,4],[306,3],[338,3],[713,6],[809,4]]},"482":{"position":[[87,3],[182,4],[234,3],[587,3],[621,5],[661,3]]},"484":{"position":[[387,4],[503,3],[1044,5],[1252,3],[1308,3],[1542,4]]},"486":{"position":[[7,3],[645,3],[793,5]]},"488":{"position":[[8,3],[944,3],[1164,3],[1454,3]]},"490":{"position":[[76,5],[313,5],[643,4],[687,6],[1547,3],[1663,3]]},"492":{"position":[[274,5],[644,5]]},"494":{"position":[[320,4]]},"496":{"position":[[0,3],[47,5],[76,3],[449,3],[520,4]]},"498":{"position":[[84,3],[201,3],[211,3]]},"500":{"position":[[88,3],[588,3],[765,4],[885,3],[958,5]]},"502":{"position":[[435,3]]},"504":{"position":[[98,3],[229,3],[266,3]]},"506":{"position":[[296,3],[1713,6],[2545,3]]},"508":{"position":[[51,4],[167,4],[637,3],[658,3],[1663,4],[1765,3],[1872,4],[1893,3],[2186,5],[2235,3],[2433,5],[2496,3],[2760,4],[2815,6],[3128,4],[3200,3],[3422,3],[3689,3],[3931,4],[4501,3],[4687,4],[5004,5],[5277,3],[5518,5]]},"510":{"position":[[170,3],[902,3],[1270,3],[1503,3],[1594,4],[1715,5],[2491,3],[2680,3],[2934,3],[3074,4],[3092,3],[3152,5]]},"512":{"position":[[323,4],[956,4],[1618,3]]},"514":{"position":[[75,3]]},"520":{"position":[[723,3]]},"522":{"position":[[113,3],[267,3],[380,3],[598,4],[622,3],[772,3],[1121,5],[1287,3],[1686,4],[1851,3]]},"524":{"position":[[99,3],[472,4],[944,3]]},"526":{"position":[[423,4],[684,4],[835,3],[1225,4],[1570,3]]},"528":{"position":[[7,3],[251,3],[712,5],[838,5]]},"530":{"position":[[423,3]]},"534":{"position":[[1802,4],[2048,3]]},"536":{"position":[[224,3],[572,4],[820,3],[1028,3],[1518,3]]},"538":{"position":[[327,3],[2117,3],[2271,3],[3544,3],[3701,3]]},"540":{"position":[[48,5],[228,5],[513,3]]},"542":{"position":[[215,5],[293,3],[420,5],[492,3],[615,5]]},"544":{"position":[[588,3],[773,5],[812,5],[945,3]]},"546":{"position":[[36,3],[114,3],[265,6]]},"550":{"position":[[7,3],[530,4],[590,5],[1759,4]]},"552":{"position":[[42,4],[298,5],[501,4],[665,3],[1068,4],[1147,6]]},"554":{"position":[[326,3],[363,4]]},"556":{"position":[[8,3],[459,4],[879,3]]},"558":{"position":[[57,4],[259,5],[809,6]]},"560":{"position":[[37,4]]},"562":{"position":[[110,4],[238,6],[962,3]]},"564":{"position":[[58,3],[339,3],[387,5],[471,5],[654,3]]},"566":{"position":[[165,3],[406,3],[468,3],[644,5],[937,3],[956,6],[978,3],[1103,5],[1240,3],[1382,3],[1423,3],[1472,3],[1577,3]]},"568":{"position":[[9,3],[78,3],[154,7],[792,3]]},"570":{"position":[[252,5],[1467,6],[1479,5],[1626,5]]},"572":{"position":[[71,3],[388,3],[793,5],[1333,5]]},"574":{"position":[[24,3],[178,5],[618,4],[869,3],[1743,4],[2032,3],[2983,3],[3500,3]]},"580":{"position":[[7,3],[1333,3],[1395,3],[1513,3],[1579,3],[1652,3],[1681,3]]},"582":{"position":[[535,3],[689,4]]},"584":{"position":[[678,5],[1496,3],[2390,3],[2433,4]]},"586":{"position":[[37,5],[137,3],[1666,4],[1947,3],[2229,3]]},"588":{"position":[[104,3],[264,3],[1456,3],[1511,4],[2431,3],[2738,3],[2862,5]]},"590":{"position":[[12,4],[269,4],[379,4],[476,4],[634,3]]},"592":{"position":[[22,3],[133,3],[214,5],[392,4],[438,4],[501,4],[623,4],[725,4]]},"594":{"position":[[479,4],[1593,3]]},"596":{"position":[[8,3],[431,4],[1075,3],[1784,5],[2052,7],[2116,5]]},"598":{"position":[[209,3]]},"600":{"position":[[968,4],[1191,5]]},"602":{"position":[[64,4],[143,4],[182,3],[340,3],[509,4],[617,4],[1148,5]]},"604":{"position":[[191,3],[903,3],[1083,3],[1424,5]]},"606":{"position":[[257,3]]},"608":{"position":[[1618,3]]},"610":{"position":[[83,4],[124,4],[527,5],[644,5]]},"612":{"position":[[566,3],[673,4]]},"614":{"position":[[36,3]]},"616":{"position":[[307,3]]},"622":{"position":[[53,4],[155,3],[221,5],[1129,4],[1279,4],[1450,4],[1615,3]]},"624":{"position":[[864,3],[915,4],[1297,5]]},"626":{"position":[[692,3],[1026,3],[1867,3],[2097,5],[2303,6],[3016,5],[3080,4],[3214,3],[3276,4],[3745,5],[3861,5],[4535,4],[4629,6],[6765,3],[7081,5]]},"628":{"position":[[118,4],[236,5],[373,4],[491,4]]},"630":{"position":[[490,5]]},"632":{"position":[[293,5]]},"634":{"position":[[461,3],[822,6],[1159,4],[1429,3],[2803,5]]},"636":{"position":[[110,5],[698,5],[733,4],[1880,3]]},"640":{"position":[[186,4],[327,6]]},"642":{"position":[[442,3]]},"646":{"position":[[128,3],[192,3],[355,3],[475,3]]},"648":{"position":[[147,4],[389,4],[726,3],[970,3],[1086,3],[1263,4],[1515,3],[1751,3]]},"654":{"position":[[532,3],[734,5],[1143,3],[1609,3],[2322,5],[2918,5],[3948,4]]},"656":{"position":[[14,4],[205,3],[612,4],[662,4],[889,4]]},"658":{"position":[[771,3],[826,3]]},"660":{"position":[[1949,3]]},"664":{"position":[[176,4],[492,3],[558,3]]},"666":{"position":[[901,3],[1902,5],[1927,3],[1971,3]]},"668":{"position":[[133,5],[2520,4],[3301,5]]},"670":{"position":[[201,3],[396,5],[620,4],[1183,4],[1298,4],[1474,3],[1596,3],[1762,3],[1979,6],[2317,3],[2544,6],[2602,6],[2658,4],[2958,3],[3076,4]]},"674":{"position":[[34,3],[297,3]]},"678":{"position":[[543,4],[1268,3]]},"680":{"position":[[850,5],[1481,5],[1687,5]]},"682":{"position":[[44,3],[204,5],[317,4],[460,4],[871,3],[1089,4],[1264,4],[2513,3],[2724,4],[2825,4],[3178,5],[3322,3]]},"684":{"position":[[63,3],[1364,5],[1389,3],[2223,4],[2346,4],[3108,4],[3133,4],[3256,3]]},"686":{"position":[[8,3]]},"688":{"position":[[226,5],[583,4],[1045,4],[1182,4],[1534,4],[2708,3]]},"690":{"position":[[8,3]]},"694":{"position":[[120,3],[150,4],[267,3],[1024,3],[1534,4],[2328,6]]},"696":{"position":[[2397,4],[2457,4],[2566,4],[2770,3],[3181,4],[4124,3],[4549,3]]},"698":{"position":[[49,4],[159,6]]},"700":{"position":[[232,3],[543,4],[752,3],[834,3],[1139,4],[1201,4],[1534,4],[2514,4],[2576,4],[2682,4],[2791,4],[3234,4],[3533,3],[3702,4],[3830,5]]},"702":{"position":[[55,4],[92,3],[209,6],[2481,3],[2625,3],[2695,3],[2870,6]]},"704":{"position":[[572,3],[767,3],[837,3],[913,3],[1710,3]]},"708":{"position":[[686,4],[1455,6],[1696,6]]},"710":{"position":[[121,4],[217,5],[684,5]]},"712":{"position":[[148,3],[215,3]]},"714":{"position":[[117,4],[202,4],[517,4],[1320,4],[1498,5],[1562,4],[1820,3],[1903,4],[2068,5],[2366,4],[2641,4],[3078,4],[3162,4],[3478,5],[3549,3],[3644,3],[3984,3]]},"716":{"position":[[816,4],[1205,3],[1280,3],[1719,4],[1814,5],[3150,3],[3184,3],[3351,3],[3494,5],[3726,4],[3853,3],[4330,4],[4359,3],[4432,3],[5589,3]]},"718":{"position":[[354,3],[362,3],[662,4],[916,3],[972,3],[1087,5],[2278,5],[2339,6]]},"720":{"position":[[392,3],[1711,4],[1793,3],[2585,6],[2634,3],[3484,3],[3709,4],[3862,5],[4062,3],[4129,4],[4158,3],[4520,4],[5250,3],[5697,3],[5749,3],[7226,6],[7815,3]]},"722":{"position":[[54,3],[225,4],[276,5],[428,5],[536,5],[608,5],[930,4],[1133,4],[1354,4],[1812,3],[1952,3],[2356,4],[2445,3],[2637,4]]},"724":{"position":[[17,3],[728,5]]},"726":{"position":[[127,6],[457,3]]},"728":{"position":[[429,6]]},"732":{"position":[[379,4],[801,5]]},"734":{"position":[[848,4],[2586,4],[2779,3],[3269,6]]},"736":{"position":[[515,3],[802,4],[1010,7],[1076,4]]},"738":{"position":[[539,4],[662,3],[684,5],[1064,6],[2128,3],[2147,3],[2211,4],[2433,6],[2491,5],[2521,4],[2562,4],[2617,3],[2637,3],[2682,4],[2810,3],[2860,3],[3829,3],[4055,3],[4191,6],[4241,3],[4528,3],[5667,3],[6156,3],[6359,7],[7053,4]]},"742":{"position":[[439,4],[851,4]]},"744":{"position":[[1059,3],[1847,6],[1945,6]]},"746":{"position":[[84,5],[1238,3],[1448,3]]},"748":{"position":[[416,3],[497,3],[974,4],[1182,6]]},"752":{"position":[[132,4],[421,3]]},"754":{"position":[[450,3],[639,5],[1149,3],[2044,3],[2114,5],[2224,5],[2311,5],[2383,6]]},"756":{"position":[[206,3]]},"758":{"position":[[188,4],[285,4]]},"760":{"position":[[628,4],[658,4],[714,6],[2125,3],[2443,4],[2582,3],[3363,4],[3444,5]]},"762":{"position":[[248,7]]},"764":{"position":[[790,3],[1202,3],[1543,3],[1586,3],[1766,5],[2382,3],[2587,3],[2706,4]]},"766":{"position":[[363,4],[869,3],[1315,4],[1441,4],[1459,3],[1670,3]]},"770":{"position":[[116,3],[1232,3],[1360,3],[1502,3]]},"772":{"position":[[212,6],[614,3]]},"774":{"position":[[59,4],[130,4],[1064,5],[1384,3]]},"776":{"position":[[60,6],[170,3],[443,3],[535,3],[579,5]]},"778":{"position":[[236,4],[735,5],[869,4],[1198,3],[1393,3]]},"780":{"position":[[110,3],[139,3]]},"782":{"position":[[32,6],[159,3]]},"784":{"position":[[77,3]]},"786":{"position":[[20,5]]},"788":{"position":[[18,6],[195,3]]},"790":{"position":[[506,3]]},"794":{"position":[[327,5],[740,4],[981,6],[1178,5]]},"796":{"position":[[838,3],[1277,4],[1299,4],[1775,3],[1856,4],[2004,4],[2159,3]]},"798":{"position":[[162,3],[807,4],[977,5],[1085,5],[1167,4],[1302,4],[1324,3],[1447,5],[1489,3],[1595,5]]},"800":{"position":[[1129,3],[1185,5]]},"802":{"position":[[94,5],[185,4],[208,4],[544,5],[562,5],[672,3]]},"804":{"position":[[90,5],[596,5],[2652,4]]},"806":{"position":[[501,6],[716,3],[1754,3],[2424,6],[2843,6],[3525,4],[3555,5],[3654,4]]},"808":{"position":[[282,3],[480,4],[656,3],[721,6],[799,5],[1022,3]]},"810":{"position":[[259,6],[1351,4],[1477,4]]},"812":{"position":[[170,3],[231,6]]},"816":{"position":[[550,5]]},"818":{"position":[[35,3],[73,3],[168,5],[442,3],[1115,6],[1170,3],[1763,6]]},"820":{"position":[[120,4],[322,6]]},"826":{"position":[[282,3]]},"828":{"position":[[1010,3],[1299,6],[1557,6],[1720,3],[1890,6]]},"830":{"position":[[262,5],[394,5],[760,5]]},"832":{"position":[[464,5],[727,3],[984,6],[1082,6],[1180,4]]},"834":{"position":[[78,4],[113,5],[337,4],[1021,6],[1191,3],[1240,5],[1300,5],[1320,3],[1517,3],[2181,3],[2678,3],[3189,3],[3408,5],[3759,3],[3869,5],[3904,3],[3996,6],[4032,3],[4104,5],[4807,3],[4878,3]]},"838":{"position":[[550,5],[806,5],[1311,4],[1623,5],[1767,3]]},"840":{"position":[[154,3],[324,5],[709,5]]},"842":{"position":[[860,4],[1496,3]]},"846":{"position":[[252,3]]},"850":{"position":[[753,7],[1553,3],[1996,3],[2099,4]]},"852":{"position":[[8,3],[92,6],[241,6],[847,3]]},"854":{"position":[[64,4],[372,6],[440,4],[644,3],[982,6],[992,3],[1060,4],[1141,3]]},"856":{"position":[[81,3],[146,6]]},"858":{"position":[[8,3],[212,3],[1239,3],[1364,6],[2171,3],[3041,4]]},"860":{"position":[[8,3],[930,4],[1053,4],[2220,4],[2640,5],[2675,3]]},"862":{"position":[[47,4],[514,3],[1007,5],[1773,4],[1851,3],[2758,5],[3260,3],[3889,5],[4275,4],[4469,3]]},"864":{"position":[[137,4],[381,4],[907,3],[1002,4]]},"866":{"position":[[210,3],[382,5]]},"868":{"position":[[838,4],[946,6]]},"870":{"position":[[605,3]]},"874":{"position":[[189,4],[638,3]]},"876":{"position":[[853,5]]},"878":{"position":[[499,4],[697,4],[926,5],[1040,3],[1221,5],[1267,4],[1530,5]]},"880":{"position":[[68,6]]},"884":{"position":[[65,4],[530,3],[611,4],[750,4],[758,5],[1033,3]]},"888":{"position":[[8,3],[419,4],[469,4],[542,5],[754,5]]},"890":{"position":[[8,3],[735,3],[1040,4],[1617,4],[2102,3]]},"892":{"position":[[7,3],[101,3],[220,3],[627,3],[953,3],[2457,5],[2527,3]]},"894":{"position":[[99,5],[686,3],[732,5],[1448,3]]},"896":{"position":[[0,3],[90,4],[214,4]]},"898":{"position":[[51,3],[700,5],[949,5],[1921,6],[2551,4]]},"900":{"position":[[36,3],[1437,4]]},"902":{"position":[[966,4]]},"904":{"position":[[835,3],[979,5],[1023,3],[1588,3]]},"906":{"position":[[496,3]]},"908":{"position":[[7,3],[587,4],[1034,4],[1093,4],[1125,3],[1430,3],[1877,3],[2441,4],[3317,4],[3411,4]]},"910":{"position":[[265,3],[487,3],[649,3],[1052,5],[1518,4],[1813,3]]},"912":{"position":[[64,4],[754,6]]},"914":{"position":[[373,3]]},"918":{"position":[[221,6]]},"927":{"position":[[720,3],[790,3],[866,3],[1663,3]]},"931":{"position":[[143,5]]},"943":{"position":[[408,6]]},"947":{"position":[[116,6]]},"949":{"position":[[58,7]]},"951":{"position":[[252,3]]},"953":{"position":[[351,3],[379,3]]},"967":{"position":[[51,6],[182,6]]},"975":{"position":[[336,3]]},"979":{"position":[[7,3],[146,4],[193,3]]},"985":{"position":[[315,3]]},"987":{"position":[[34,4],[137,6],[372,3]]},"989":{"position":[[23,6],[223,4],[369,3]]},"991":{"position":[[34,6]]},"995":{"position":[[303,5],[401,3]]},"997":{"position":[[166,3]]},"1003":{"position":[[6,3],[90,4]]},"1013":{"position":[[7,3],[146,4],[193,3]]},"1019":{"position":[[368,4],[940,4],[1100,4]]},"1021":{"position":[[85,4]]},"1025":{"position":[[260,3]]},"1027":{"position":[[28,5],[175,4],[681,5],[1017,4],[1338,4],[1808,5],[1885,3],[2093,5],[2721,5],[2782,3],[3096,3],[3279,4],[3389,5],[3710,4],[4006,4],[4222,5],[4656,3],[4759,7],[4788,3],[4899,4],[4914,3],[5203,4],[5458,7],[6004,3],[6715,3],[7065,5],[7121,6],[7397,5],[7997,4],[8209,5],[8650,5],[8712,4],[9044,5],[9078,5],[9128,3],[9504,3],[9608,3],[9933,3],[10211,6],[10389,3]]},"1029":{"position":[[1077,5],[1252,4]]},"1031":{"position":[[116,4]]},"1033":{"position":[[534,3],[645,6],[1097,5],[2345,3],[2674,4],[3142,3],[3490,3]]},"1035":{"position":[[299,3],[2025,4],[2170,3],[2289,4],[3121,5]]},"1037":{"position":[[265,6],[758,6]]}}}],["usabl",{"_index":3166,"t":{"476":{"position":[[229,6]]}}}],["usag",{"_index":1031,"t":{"69":{"position":[[965,5]]},"87":{"position":[[200,5]]},"243":{"position":[[144,5]]},"488":{"position":[[284,8],[1620,6]]},"560":{"position":[[179,5]]},"562":{"position":[[260,5]]},"714":{"position":[[236,5]]},"766":{"position":[[543,5],[556,5]]},"860":{"position":[[1160,8]]},"894":{"position":[[915,6]]},"985":{"position":[[839,5]]},"987":{"position":[[679,5]]},"989":{"position":[[682,5]]}}}],["useless",{"_index":1774,"t":{"227":{"position":[[290,8]]}}}],["user",{"_index":169,"t":{"17":{"position":[[167,5],[447,4]]},"21":{"position":[[1318,4],[1380,4],[1416,4],[1611,5],[1739,4],[3748,5]]},"23":{"position":[[1395,4]]},"37":{"position":[[620,5]]},"47":{"position":[[5246,4],[5334,4]]},"57":{"position":[[904,4],[1242,4]]},"61":{"position":[[442,5]]},"63":{"position":[[323,5]]},"65":{"position":[[255,4]]},"69":{"position":[[591,4],[848,5],[1395,5],[3539,6],[4071,6],[5587,5],[5918,5]]},"71":{"position":[[762,6]]},"77":{"position":[[2725,5],[2877,6]]},"91":{"position":[[2645,4],[2695,5]]},"93":{"position":[[625,5]]},"103":{"position":[[137,4]]},"107":{"position":[[197,4],[737,4]]},"115":{"position":[[570,4],[1079,4]]},"129":{"position":[[295,4]]},"131":{"position":[[17,4],[646,4]]},"133":{"position":[[15,4]]},"139":{"position":[[129,4],[771,5],[797,4]]},"147":{"position":[[138,4]]},"175":{"position":[[865,4]]},"185":{"position":[[646,6],[1184,4]]},"211":{"position":[[720,4]]},"215":{"position":[[101,4]]},"219":{"position":[[1218,7]]},"221":{"position":[[819,6]]},"243":{"position":[[828,5]]},"253":{"position":[[3194,5]]},"266":{"position":[[57,5],[822,4],[957,4],[1011,5],[1302,5]]},"274":{"position":[[864,4]]},"276":{"position":[[172,5],[400,4],[1265,4]]},"278":{"position":[[37,5],[72,5],[368,4],[498,4],[941,5]]},"280":{"position":[[63,4],[844,4]]},"282":{"position":[[105,4],[237,4],[753,5]]},"284":{"position":[[277,5],[290,4]]},"286":{"position":[[702,4]]},"288":{"position":[[1115,5],[1309,5],[1476,4],[2012,5]]},"290":{"position":[[769,4],[1021,4],[2647,5]]},"296":{"position":[[392,5]]},"300":{"position":[[935,6]]},"302":{"position":[[715,6]]},"310":{"position":[[241,5]]},"316":{"position":[[238,6]]},"336":{"position":[[1250,4]]},"354":{"position":[[1161,4],[1248,5]]},"378":{"position":[[4869,4]]},"390":{"position":[[1318,5]]},"394":{"position":[[862,4]]},"396":{"position":[[1215,5]]},"402":{"position":[[783,5],[1143,5]]},"406":{"position":[[71,6]]},"440":{"position":[[373,4]]},"470":{"position":[[46,4],[1005,4]]},"472":{"position":[[15,4],[658,4]]},"474":{"position":[[175,4]]},"480":{"position":[[59,5],[853,4],[985,4]]},"482":{"position":[[842,4],[899,4],[950,4],[1146,4]]},"484":{"position":[[933,4]]},"488":{"position":[[394,5],[1578,4]]},"506":{"position":[[911,5]]},"508":{"position":[[1315,4],[2072,4],[2180,5],[2541,4],[3319,4],[3552,4],[3893,4],[4034,4],[4803,5],[4931,4],[5081,5],[5126,4],[5246,4]]},"514":{"position":[[132,4]]},"536":{"position":[[1141,5]]},"544":{"position":[[97,5]]},"554":{"position":[[1184,6]]},"556":{"position":[[188,4]]},"574":{"position":[[170,4],[2327,4]]},"594":{"position":[[725,4],[855,4]]},"598":{"position":[[123,5],[618,4]]},"602":{"position":[[440,5],[575,5],[885,4],[973,4],[1055,4]]},"612":{"position":[[97,5]]},"622":{"position":[[1160,6],[1292,6],[1312,6],[1376,4],[1479,4]]},"624":{"position":[[1394,4],[1584,5]]},"626":{"position":[[3614,4],[3790,5]]},"634":{"position":[[2598,4],[2632,4],[2721,4]]},"636":{"position":[[989,5],[1022,6],[1865,5],[2399,4],[2536,4]]},"640":{"position":[[879,5],[1310,4],[1438,5]]},"642":{"position":[[241,5],[296,4]]},"646":{"position":[[35,4]]},"648":{"position":[[71,4],[169,4],[314,4],[402,5],[415,4],[1203,5],[1333,4],[1385,5],[1503,5],[1611,5],[1653,4],[1972,4],[2200,4],[2276,5]]},"662":{"position":[[983,5]]},"664":{"position":[[1917,4]]},"666":{"position":[[1063,4]]},"670":{"position":[[190,5],[314,4],[2943,6]]},"674":{"position":[[616,5]]},"704":{"position":[[439,6]]},"708":{"position":[[631,4],[993,5],[1020,4],[1213,4],[1492,5],[1562,4]]},"714":{"position":[[1048,5],[1457,5]]},"720":{"position":[[435,4]]},"724":{"position":[[256,6]]},"726":{"position":[[170,5]]},"732":{"position":[[494,5],[1041,5],[1296,4],[1586,5]]},"734":{"position":[[1036,5],[1371,5]]},"736":{"position":[[1294,4]]},"744":{"position":[[726,6]]},"748":{"position":[[338,5],[635,4]]},"752":{"position":[[220,4]]},"754":{"position":[[380,4]]},"784":{"position":[[180,4]]},"786":{"position":[[204,4]]},"794":{"position":[[832,4]]},"796":{"position":[[2200,5]]},"798":{"position":[[50,6],[140,5],[1292,5]]},"800":{"position":[[482,4],[694,4],[890,4],[1114,4],[1227,5]]},"802":{"position":[[1410,4],[2917,4]]},"806":{"position":[[376,4],[515,4]]},"810":{"position":[[1016,4]]},"816":{"position":[[55,5],[69,6],[217,5]]},"818":{"position":[[134,5],[1467,4],[1546,5],[1623,6]]},"820":{"position":[[363,4],[503,4]]},"834":{"position":[[5052,5],[5186,5]]},"838":{"position":[[2138,4],[2147,4]]},"846":{"position":[[341,6]]},"856":{"position":[[263,6]]},"858":{"position":[[309,4],[1481,4],[1790,4],[1860,4],[2449,4]]},"862":{"position":[[1626,4]]},"868":{"position":[[57,4],[199,4],[729,4]]},"888":{"position":[[115,4],[163,5],[341,5],[414,4],[925,5]]},"890":{"position":[[2302,5]]},"892":{"position":[[361,5],[1875,4]]},"898":{"position":[[263,4],[317,4],[1046,4]]},"900":{"position":[[604,5],[943,4],[1039,5],[1087,5],[1522,5]]},"902":{"position":[[497,4],[506,4],[880,4],[901,4]]},"904":{"position":[[631,4]]},"908":{"position":[[759,4]]},"910":{"position":[[598,6],[1383,5]]},"927":{"position":[[218,5],[224,4],[260,4],[352,4],[383,4]]},"939":{"position":[[712,5]]},"943":{"position":[[43,5]]},"945":{"position":[[55,5]]},"947":{"position":[[163,5]]},"1019":{"position":[[51,4]]}}}],["user\"echo",{"_index":3168,"t":{"480":{"position":[[862,10]]}}}],["user'",{"_index":1113,"t":{"77":{"position":[[1572,6],[2753,6]]},"79":{"position":[[564,6]]},"83":{"position":[[782,6]]},"145":{"position":[[738,6]]},"175":{"position":[[1753,6]]},"209":{"position":[[265,6]]},"288":{"position":[[1236,6]]},"296":{"position":[[293,6]]},"480":{"position":[[879,6],[1006,6]]},"486":{"position":[[122,6]]},"568":{"position":[[298,6]]},"574":{"position":[[831,6]]},"588":{"position":[[332,6]]},"622":{"position":[[1345,6]]},"648":{"position":[[2235,6]]},"732":{"position":[[933,6],[1256,6]]},"810":{"position":[[815,6]]},"888":{"position":[[835,6]]},"908":{"position":[[807,6],[1019,6]]},"1025":{"position":[[276,6]]}}}],["user,pid,ppid,pgid,command",{"_index":5074,"t":{"878":{"position":[[1891,26]]}}}],["user.sudo",{"_index":3889,"t":{"648":{"position":[[2033,9]]}}}],["user/effect",{"_index":5177,"t":{"904":{"position":[[375,14],[1278,14]]}}}],["user@ec2",{"_index":5140,"t":{"898":{"position":[[806,8]]}}}],["user@ip",{"_index":5152,"t":{"898":{"position":[[2366,7],[2593,7]]},"908":{"position":[[2165,7],[2750,7],[2801,7],[2873,7]]}}}],["user@remote.com",{"_index":94,"t":{"10":{"position":[[65,15]]}}}],["user_backup",{"_index":3200,"t":{"488":{"position":[[214,12],[524,11]]}}}],["user_backup'\"mkdir",{"_index":3199,"t":{"488":{"position":[[193,20]]}}}],["useradd",{"_index":5252,"t":{"927":{"position":[[240,7]]}}}],["userdel",{"_index":5253,"t":{"927":{"position":[[363,7]]}}}],["usermod",{"_index":5254,"t":{"927":{"position":[[408,7]]}}}],["usernam",{"_index":991,"t":{"61":{"position":[[416,8]]},"63":{"position":[[297,8]]},"69":{"position":[[4923,8],[6427,8]]},"378":{"position":[[286,9],[543,8],[1007,9],[1538,9],[2283,9],[4741,11],[4853,8]]},"482":{"position":[[934,8]]},"506":{"position":[[806,9],[850,10],[861,11]]},"636":{"position":[[74,8]]},"654":{"position":[[2786,8],[2990,8],[3159,9]]},"708":{"position":[[718,8],[1238,8]]},"714":{"position":[[1024,8]]},"716":{"position":[[476,8],[4602,8]]},"720":{"position":[[4502,9]]},"884":{"position":[[464,8],[804,8]]},"888":{"position":[[87,8]]},"898":{"position":[[246,8],[1019,9]]},"902":{"position":[[91,8]]},"908":{"position":[[965,8]]}}}],["username@host",{"_index":4375,"t":{"720":{"position":[[2871,13]]}}}],["users/dwmkerr",{"_index":1115,"t":{"77":{"position":[[1776,15]]}}}],["users/dwmkerr/effect",{"_index":1900,"t":{"253":{"position":[[1405,24]]},"452":{"position":[[436,24]]}}}],["users/dwmkerr/pictures/2020",{"_index":1460,"t":{"135":{"position":[[1113,28]]}}}],["users/dwmkerr/repos/github/dwmkerr/effect",{"_index":1458,"t":{"135":{"position":[[334,45]]},"253":{"position":[[660,45]]}}}],["userusername=${us",{"_index":3281,"t":{"506":{"position":[[818,20]]}}}],["user}_backup",{"_index":3205,"t":{"488":{"position":[[698,16],[1226,14]]}}}],["user}_backup'\"mkdir",{"_index":3204,"t":{"488":{"position":[[675,22]]}}}],["user}echo",{"_index":3282,"t":{"506":{"position":[[839,10]]}}}],["usr",{"_index":1694,"t":{"217":{"position":[[871,4]]}}}],["usr/bin",{"_index":653,"t":{"43":{"position":[[1134,8],[1181,8]]},"189":{"position":[[2270,8]]},"205":{"position":[[783,8]]},"217":{"position":[[1151,7]]},"255":{"position":[[874,8]]},"257":{"position":[[5945,8],[6545,8]]},"300":{"position":[[598,8]]},"406":{"position":[[800,8]]},"438":{"position":[[1413,8],[1473,8]]}}}],["usr/bin/bash",{"_index":5057,"t":{"874":{"position":[[80,15]]}}}],["usr/bin/bash/usr/bin/zshell/usr/bin/new",{"_index":5465,"t":{"1033":{"position":[[3346,40]]}}}],["usr/bin/bash/usr/bin/zshell/usr/bin/newshel",{"_index":5461,"t":{"1033":{"position":[[2944,45]]}}}],["usr/bin/bashecho",{"_index":2804,"t":{"430":{"position":[[1145,19]]}}}],["usr/bin/dircolor",{"_index":4462,"t":{"734":{"position":[[2238,18]]}}}],["usr/bin/env",{"_index":2815,"t":{"432":{"position":[[1423,14],[1485,14]]},"438":{"position":[[230,14]]},"538":{"position":[[482,14],[2279,14]]},"746":{"position":[[243,14]]},"808":{"position":[[377,14]]},"874":{"position":[[646,14],[974,14]]},"878":{"position":[[249,14],[412,14],[607,14]]}}}],["usr/bin/new",{"_index":5457,"t":{"1033":{"position":[[2846,12]]}}}],["usr/bin/nodeconsole.log(\"hello",{"_index":2805,"t":{"430":{"position":[[1209,33]]}}}],["usr/bin/python3print('hello",{"_index":2803,"t":{"430":{"position":[[1003,30]]}}}],["usr/bin/rubi",{"_index":1090,"t":{"71":{"position":[[935,13]]},"432":{"position":[[358,15]]}}}],["usr/bin/rubyput",{"_index":2806,"t":{"432":{"position":[[182,19]]}}}],["usr/bin/sh",{"_index":2800,"t":{"430":{"position":[[211,14]]},"502":{"position":[[753,11]]},"506":{"position":[[234,11]]}}}],["usr/bin/touch)1",{"_index":3450,"t":{"534":{"position":[[1636,17]]}}}],["usr/bin/tput",{"_index":4446,"t":{"734":{"position":[[1532,13]]}}}],["usr/bin/usr/bin/usr/bin/uux/usr/bin/cpan/usr/bin/buildstrings/usr/bin/loads.d/usr/bin/writ",{"_index":1691,"t":{"217":{"position":[[228,96]]}}}],["usr/bin/zshel",{"_index":5456,"t":{"1033":{"position":[[2830,15]]}}}],["usr/binlrwxrwxrwx",{"_index":1698,"t":{"217":{"position":[[930,17]]}}}],["usr/local/bin",{"_index":1923,"t":{"255":{"position":[[883,14]]},"302":{"position":[[1296,15]]},"406":{"position":[[814,14]]},"438":{"position":[[770,14],[959,14],[1309,14],[1375,14],[1589,14]]},"512":{"position":[[1494,14]]},"574":{"position":[[3376,14]]}}}],["usr/local/bin/bash",{"_index":1095,"t":{"71":{"position":[[1780,20]]}}}],["usr/local/bin/common",{"_index":2837,"t":{"438":{"position":[[822,21]]},"512":{"position":[[1567,21]]},"544":{"position":[[1944,21]]},"556":{"position":[[261,21],[561,22]]},"558":{"position":[[306,21],[411,21],[932,21],[1285,21],[1771,21],[1876,21]]},"574":{"position":[[3449,21]]},"612":{"position":[[1620,21]]}}}],["usr/local/bin/lookup",{"_index":4828,"t":{"808":{"position":[[970,21]]}}}],["usr/local/bin/usr/bin/bin/usr/sbin/sbin",{"_index":2166,"t":{"300":{"position":[[762,40]]}}}],["usr/local/sbin/usr/local/bin/usr/sbin/usr/bin/sbin/bin/usr/games/usr/local/gam",{"_index":5471,"t":{"1033":{"position":[[4541,81]]}}}],["usr/sbin",{"_index":1709,"t":{"217":{"position":[[1163,8]]},"255":{"position":[[898,9]]}}}],["usr/sbin/usr/bin/usr/bin/fwupdtool/usr/bin/gnom",{"_index":1593,"t":{"189":{"position":[[2279,49]]}}}],["usr/sbindrwxr",{"_index":1700,"t":{"217":{"position":[[982,13]]}}}],["usr/share/doc/bash",{"_index":4479,"t":{"734":{"position":[[3120,19]]}}}],["usr/share/man/man2",{"_index":885,"t":{"47":{"position":[[7777,20]]}}}],["usual",{"_index":309,"t":{"21":{"position":[[3169,5]]},"47":{"position":[[533,8],[770,8],[5380,7]]},"310":{"position":[[397,7]]},"332":{"position":[[728,5]]},"608":{"position":[[825,5]]},"804":{"position":[[2315,7]]},"806":{"position":[[3269,7]]}}}],["utc",{"_index":4419,"t":{"722":{"position":[[1610,3]]}}}],["utc'th",{"_index":4418,"t":{"722":{"position":[[1566,7]]}}}],["util",{"_index":614,"t":{"41":{"position":[[340,7]]},"47":{"position":[[2221,7],[2248,8],[2267,7],[2388,7]]},"312":{"position":[[88,9]]},"390":{"position":[[438,7]]},"876":{"position":[[389,9]]},"892":{"position":[[54,8]]}}}],["utilities)descript",{"_index":712,"t":{"47":{"position":[[1302,21]]}}}],["utilitydescript",{"_index":727,"t":{"47":{"position":[[2193,18]]}}}],["uucp",{"_index":815,"t":{"47":{"position":[[5361,4]]}}}],["v",{"_index":208,"t":{"21":{"position":[[347,1],[356,1],[362,1],[498,1],[633,3],[2209,2],[2440,1],[2484,1],[2523,3],[2920,2],[3790,1]]},"27":{"position":[[312,3]]},"69":{"position":[[4501,2]]},"77":{"position":[[4746,1]]},"260":{"position":[[321,1],[802,1]]},"304":{"position":[[84,2]]},"326":{"position":[[161,1],[242,1],[305,1],[336,1]]},"404":{"position":[[107,1],[312,1],[591,1],[611,1]]},"410":{"position":[[518,1],[541,1]]},"450":{"position":[[2541,1]]},"656":{"position":[[540,1]]},"714":{"position":[[1054,2],[1098,2]]},"738":{"position":[[3137,1],[3372,1]]},"822":{"position":[[80,1]]},"834":{"position":[[3490,1],[3884,1]]},"862":{"position":[[1969,1],[2147,2]]},"866":{"position":[[227,1],[267,1]]}}}],["v1.pi",{"_index":4712,"t":{"802":{"position":[[1263,5]]}}}],["v1.pylouch",{"_index":4724,"t":{"802":{"position":[[2466,11]]}}}],["v1.pyoneon",{"_index":4720,"t":{"802":{"position":[[2190,11]]}}}],["v19",{"_index":2987,"t":{"450":{"position":[[3927,3],[4036,3],[4536,3],[4619,3]]}}}],["v1kind",{"_index":2513,"t":{"378":{"position":[[209,7],[922,7],[1457,7],[2199,7]]}}}],["v2.pi",{"_index":4733,"t":{"804":{"position":[[347,6],[446,5],[1736,5]]}}}],["v2.pylouch",{"_index":4756,"t":{"804":{"position":[[2117,11]]}}}],["v3.pi",{"_index":4793,"t":{"806":{"position":[[852,6],[3168,5],[3727,5],[3749,5],[4038,5]]}}}],["v=\"hello",{"_index":2183,"t":{"304":{"position":[[69,9]]}}}],["val",{"_index":2680,"t":{"400":{"position":[[1059,3],[1087,6],[1249,8]]}}}],["val2",{"_index":3228,"t":{"490":{"position":[[1313,4]]}}}],["valankar",{"_index":28,"t":{"4":{"position":[[294,10]]}}}],["valid",{"_index":2253,"t":{"336":{"position":[[525,5]]},"338":{"position":[[562,10],[651,8],[737,5],[805,5],[1249,6],[1349,5]]},"340":{"position":[[830,5]]},"344":{"position":[[1438,5]]},"354":{"position":[[916,8]]},"376":{"position":[[1824,5]]},"428":{"position":[[287,5]]},"572":{"position":[[691,5],[974,5]]},"648":{"position":[[838,5]]},"854":{"position":[[664,8]]},"860":{"position":[[1551,5]]}}}],["valn",{"_index":3229,"t":{"490":{"position":[[1318,5]]}}}],["valu",{"_index":672,"t":{"45":{"position":[[684,5]]},"47":{"position":[[4307,6]]},"69":{"position":[[4977,6]]},"79":{"position":[[267,5]]},"83":{"position":[[708,6]]},"308":{"position":[[107,5]]},"374":{"position":[[433,6]]},"376":{"position":[[81,7],[111,6],[3325,5],[3952,5],[4447,6],[4482,14],[4724,6],[4905,5],[4982,5],[5046,5],[5223,7],[6413,6],[6619,5]]},"378":{"position":[[657,5],[1769,7],[1847,6],[1862,6],[3678,5],[3807,5],[4801,5]]},"400":{"position":[[1243,5]]},"442":{"position":[[1169,5]]},"446":{"position":[[940,6]]},"454":{"position":[[479,7],[2562,6]]},"466":{"position":[[1018,6]]},"480":{"position":[[720,6]]},"482":{"position":[[158,5],[713,5]]},"484":{"position":[[1260,5],[1340,5]]},"490":{"position":[[45,7],[122,6],[1648,5],[1811,6]]},"492":{"position":[[151,6],[386,6]]},"496":{"position":[[55,5]]},"500":{"position":[[419,5],[604,5]]},"502":{"position":[[49,6],[469,5],[996,5]]},"504":{"position":[[259,6]]},"506":{"position":[[178,6],[673,5],[720,5],[902,5],[1296,5],[1458,5],[1630,5],[1743,5],[1856,5],[1980,5]]},"508":{"position":[[3957,5]]},"510":{"position":[[2724,5],[2781,5]]},"512":{"position":[[134,6],[1128,6]]},"514":{"position":[[341,5],[542,6]]},"520":{"position":[[395,5]]},"522":{"position":[[564,5],[1532,5],[1765,5]]},"524":{"position":[[70,5],[529,5]]},"526":{"position":[[175,5],[208,7],[261,6],[376,6],[931,6],[1008,7],[1016,11],[1200,7],[1365,8],[1495,6],[1916,5]]},"528":{"position":[[312,6],[362,6],[424,6],[517,7],[525,11],[554,5]]},"530":{"position":[[17,5],[89,5],[554,6],[671,5],[772,5]]},"536":{"position":[[1103,6],[1227,5],[1381,5]]},"544":{"position":[[936,5]]},"546":{"position":[[137,6]]},"550":{"position":[[1740,5],[1865,5]]},"568":{"position":[[1244,5],[1300,5]]},"572":{"position":[[424,7]]},"580":{"position":[[1727,5]]},"588":{"position":[[1489,6],[1744,5],[1960,5],[2256,5]]},"592":{"position":[[166,7],[658,6]]},"610":{"position":[[315,5],[862,5]]},"614":{"position":[[95,6]]},"640":{"position":[[278,6]]},"648":{"position":[[501,5]]},"714":{"position":[[466,6],[3518,5]]},"716":{"position":[[154,5],[5597,5]]},"720":{"position":[[1454,5],[2692,5],[3404,6],[3853,5],[3915,6],[4924,6]]},"722":{"position":[[1696,5],[1924,5]]},"802":{"position":[[3113,5]]},"806":{"position":[[398,5],[1022,5],[1070,5],[3071,6]]},"808":{"position":[[1916,5]]},"854":{"position":[[26,6],[413,6],[1201,5]]},"856":{"position":[[102,6]]},"860":{"position":[[577,6],[767,6],[1394,5],[1524,5],[1898,5],[2054,5],[2428,5]]},"880":{"position":[[236,5]]},"985":{"position":[[1488,5]]},"1019":{"position":[[478,6],[791,7],[822,5]]},"1025":{"position":[[89,5]]},"1027":{"position":[[584,6],[1491,6],[1648,6],[1711,5],[1764,5],[1791,5],[1862,5],[1933,6],[1996,5],[2049,5],[2076,5],[2208,7],[2381,5],[2413,5],[2666,5],[3110,5],[3241,6],[3257,5],[3365,5],[3471,5],[3737,5],[3987,6],[5951,5],[6769,5],[6828,6],[9552,5],[9656,5],[9877,5],[9993,5],[10041,5]]},"1033":{"position":[[1376,5],[2524,7],[3126,5],[3430,5],[3659,6],[3759,5],[3856,5]]}}}],["valuabl",{"_index":1013,"t":{"67":{"position":[[790,8]]},"93":{"position":[[1331,9]]},"290":{"position":[[2736,9]]},"336":{"position":[[1565,8]]}}}],["value._original_ps1=\"${ps1}\"set_ps1",{"_index":4326,"t":{"720":{"position":[[672,37]]}}}],["value1",{"_index":3395,"t":{"524":{"position":[[285,9]]}}}],["value1=$1",{"_index":3391,"t":{"524":{"position":[[208,9]]}}}],["value2",{"_index":3394,"t":{"524":{"position":[[259,8],[299,9]]}}}],["value2=$2",{"_index":3392,"t":{"524":{"position":[[224,9]]}}}],["values.echo",{"_index":3244,"t":{"492":{"position":[[477,11]]}}}],["values=${@:1:n",{"_index":3416,"t":{"528":{"position":[[486,15]]}}}],["values=${@:2:n",{"_index":3407,"t":{"526":{"position":[[977,15]]}}}],["van",{"_index":413,"t":{"25":{"position":[[128,3]]},"29":{"position":[[337,3],[788,3]]},"31":{"position":[[630,3],[975,3],[1183,3]]},"251":{"position":[[177,3],[678,3]]}}}],["vanilla",{"_index":4894,"t":{"836":{"position":[[448,9]]}}}],["var",{"_index":3144,"t":{"468":{"position":[[2584,4],[2609,4],[2647,4],[2689,4]]},"506":{"position":[[506,7],[558,4],[683,6],[742,3],[993,3],[1266,8],[1305,3],[1428,8],[1467,3]]},"560":{"position":[[275,3],[300,3]]},"738":{"position":[[6054,3]]}}}],["var/log/mylogfile.txt",{"_index":1913,"t":{"253":{"position":[[3253,22],[3454,23]]}}}],["var:start:count",{"_index":3284,"t":{"506":{"position":[[941,18]]}}}],["var=\"th",{"_index":3277,"t":{"506":{"position":[[563,8]]}}}],["var_nam",{"_index":3290,"t":{"506":{"position":[[1596,12],[1686,8],[1865,13],[1883,13]]},"1027":{"position":[[5274,8],[5317,15]]}}}],["var_nameecho",{"_index":3291,"t":{"506":{"position":[[1838,12]]}}}],["var_name}don",{"_index":5413,"t":{"1027":{"position":[[5339,15]]}}}],["vari",{"_index":306,"t":{"21":{"position":[[3030,6]]},"47":{"position":[[7442,4],[8135,4]]},"67":{"position":[[11,4]]},"288":{"position":[[1372,7]]},"304":{"position":[[1857,4]]},"380":{"position":[[549,4]]},"394":{"position":[[468,4]]},"408":{"position":[[78,4]]},"428":{"position":[[959,4]]},"640":{"position":[[1067,6]]},"716":{"position":[[888,4]]},"734":{"position":[[309,4]]},"796":{"position":[[1447,6]]},"1027":{"position":[[9300,4]]},"1033":{"position":[[4214,4]]}}}],["variabl",{"_index":649,"t":{"43":{"position":[[945,8]]},"45":{"position":[[708,10]]},"79":{"position":[[417,9],[444,9],[515,8]]},"83":{"position":[[679,10]]},"129":{"position":[[660,8],[696,8],[1066,9],[1143,9]]},"139":{"position":[[919,9]]},"145":{"position":[[144,8],[719,8]]},"175":{"position":[[609,9],[950,9]]},"253":{"position":[[781,8]]},"292":{"position":[[2026,9]]},"300":{"position":[[283,8],[380,9],[503,8],[644,9]]},"304":{"position":[[366,10]]},"378":{"position":[[1886,10],[2412,9],[3186,8],[3349,9],[3493,8],[3616,8],[3740,8],[3818,8],[3839,8],[3959,8],[4163,8],[4572,9],[4993,9],[5076,9],[5143,9]]},"394":{"position":[[632,8]]},"400":{"position":[[980,8],[1213,8]]},"432":{"position":[[734,10],[910,8]]},"434":{"position":[[342,9],[519,8],[917,8]]},"438":{"position":[[1258,8]]},"442":{"position":[[1276,9],[1327,8],[1563,8]]},"446":{"position":[[2440,8]]},"458":{"position":[[362,8]]},"472":{"position":[[258,8]]},"480":{"position":[[0,9],[115,9],[209,8],[312,8],[377,8],[519,8],[570,8],[593,8],[644,8],[658,8],[790,9]]},"482":{"position":[[31,9],[132,9],[240,8],[429,9],[531,9],[554,10],[605,8],[737,8],[847,9],[904,8],[1242,8],[1478,8]]},"484":{"position":[[4,9],[54,10],[141,9],[281,9],[358,9],[449,8],[581,8],[608,9],[630,9],[802,9],[839,8],[1065,8],[1284,9],[1437,9],[1522,9],[1592,8],[1736,9]]},"486":{"position":[[65,9],[100,8],[289,8],[781,8]]},"488":{"position":[[46,8],[90,8],[400,8],[508,8],[601,8],[849,8],[1027,8],[1173,8],[1366,8],[1490,9],[1518,10],[1601,8]]},"490":{"position":[[11,9],[1746,8]]},"492":{"position":[[297,9]]},"494":{"position":[[99,8],[188,8]]},"496":{"position":[[35,8],[142,10],[572,9]]},"498":{"position":[[253,9]]},"500":{"position":[[474,9]]},"502":{"position":[[37,8],[455,8],[702,8]]},"504":{"position":[[152,9]]},"506":{"position":[[72,8],[160,8],[549,8],[733,8],[997,9],[1571,8],[1643,8],[1695,9],[1754,8],[1794,9],[1821,8],[1956,8],[2677,11]]},"508":{"position":[[139,9],[602,8],[646,8],[697,8],[766,8],[807,8],[848,8],[929,8],[969,9],[1041,8],[1094,8],[1224,8],[1346,8],[1435,8],[1466,8],[1573,8],[1771,8],[1901,8],[1960,9]]},"510":{"position":[[129,10]]},"512":{"position":[[30,10],[146,9],[553,10],[766,9],[1225,10]]},"514":{"position":[[45,9],[87,10],[320,11],[350,8],[385,8],[556,9],[1071,9]]},"520":{"position":[[37,9],[262,12],[731,9],[792,10],[826,9]]},"522":{"position":[[86,8],[386,8],[502,8],[581,8],[658,8],[1112,8],[1299,9],[1435,8],[1567,8],[1644,8],[1693,8],[1889,9],[1958,8],[2028,8]]},"524":{"position":[[120,9],[508,9],[621,9],[642,9],[970,9],[1119,8]]},"526":{"position":[[40,9],[440,10],[458,8],[533,8],[633,9],[775,10],[901,10],[1261,8],[1552,9],[1613,8],[1934,8],[2133,10],[2329,10]]},"530":{"position":[[100,9],[374,9],[689,8]]},"532":{"position":[[374,8]]},"534":{"position":[[1536,9]]},"536":{"position":[[1399,9],[1484,8]]},"542":{"position":[[140,9]]},"544":{"position":[[786,9],[873,10]]},"560":{"position":[[291,8]]},"564":{"position":[[262,8]]},"568":{"position":[[645,8],[1204,8],[1400,10]]},"572":{"position":[[375,8]]},"574":{"position":[[429,9],[922,9],[2657,10],[2992,9]]},"580":{"position":[[1501,8],[1633,9],[1740,9]]},"582":{"position":[[16,10]]},"584":{"position":[[946,8]]},"588":{"position":[[2605,8]]},"594":{"position":[[1498,8],[1521,8]]},"600":{"position":[[1060,8],[1119,10]]},"604":{"position":[[1409,8]]},"606":{"position":[[102,9]]},"608":{"position":[[315,8],[529,10],[662,9],[687,8],[957,9],[982,8],[1036,8],[1183,9],[2060,8]]},"610":{"position":[[9,8],[52,9],[175,8],[333,9],[504,8],[747,8],[875,9]]},"612":{"position":[[637,9],[1010,9]]},"614":{"position":[[263,10]]},"616":{"position":[[56,10],[602,10],[1070,10]]},"624":{"position":[[1186,9]]},"626":{"position":[[2450,9],[3061,9],[3134,9],[3172,8],[3232,8],[3875,9],[6922,9],[7008,9],[7118,8]]},"634":{"position":[[1763,9]]},"636":{"position":[[321,8],[805,9],[1335,9],[1350,8]]},"638":{"position":[[844,9]]},"640":{"position":[[232,9],[1175,8]]},"644":{"position":[[89,8]]},"648":{"position":[[473,9]]},"666":{"position":[[1784,9]]},"668":{"position":[[110,9]]},"710":{"position":[[66,9],[131,8],[205,8]]},"712":{"position":[[54,9],[176,8]]},"714":{"position":[[29,9],[2438,9],[3650,9],[3716,10]]},"716":{"position":[[171,8],[4507,8],[5572,9]]},"718":{"position":[[20,9],[951,9],[1221,9],[1712,8],[1749,8],[2234,8]]},"720":{"position":[[158,9],[411,8],[1472,8],[1554,9],[1644,8],[1699,8],[2670,8],[3661,8],[3885,8],[4312,9],[4392,9],[5001,10],[8127,8]]},"722":{"position":[[21,9],[84,8],[1278,8],[1724,9],[1852,9],[2340,8]]},"724":{"position":[[68,10],[182,8]]},"726":{"position":[[83,9]]},"738":{"position":[[2197,9],[2298,8],[2508,8],[4038,8],[6177,8],[7036,9]]},"744":{"position":[[1081,8]]},"748":{"position":[[593,8],[962,8]]},"854":{"position":[[17,8],[114,9],[147,10],[464,9],[683,8],[936,8],[1011,9],[1083,8],[1119,9]]},"856":{"position":[[206,9]]},"860":{"position":[[631,8],[787,9],[1918,8],[2074,8],[2352,8]]},"880":{"position":[[309,10]]},"898":{"position":[[2578,9]]},"1019":{"position":[[835,8]]},"1025":{"position":[[108,9],[225,9]]},"1027":{"position":[[119,9],[363,10],[553,8],[4535,8],[4876,9],[4966,9],[5212,9],[5249,9],[5425,8],[10278,8],[10473,10]]},"1029":{"position":[[537,8]]},"1031":{"position":[[293,10]]},"1033":{"position":[[2020,9],[2186,8],[3953,9]]}}}],["variable.commands=$(tail",{"_index":3760,"t":{"612":{"position":[[705,24]]}}}],["variable.titl",{"_index":3366,"t":{"520":{"position":[[219,16]]}}}],["variables.title=\"mi",{"_index":3364,"t":{"520":{"position":[[107,19]]}}}],["variat",{"_index":1984,"t":{"257":{"position":[[6827,10]]},"448":{"position":[[519,10]]}}}],["varieti",{"_index":1276,"t":{"93":{"position":[[11,7]]},"310":{"position":[[56,7]]}}}],["variou",{"_index":299,"t":{"21":{"position":[[2733,7]]},"274":{"position":[[721,7]]},"276":{"position":[[208,7]]},"638":{"position":[[249,7]]},"965":{"position":[[63,7]]}}}],["varnam",{"_index":4508,"t":{"738":{"position":[[5146,8]]}}}],["vast",{"_index":1259,"t":{"91":{"position":[[2554,4]]},"278":{"position":[[4,4]]}}}],["vdir='vdir",{"_index":4467,"t":{"734":{"position":[[2415,10]]}}}],["vendor",{"_index":2039,"t":{"274":{"position":[[625,8]]},"716":{"position":[[756,6]]}}}],["verb",{"_index":2600,"t":{"392":{"position":[[79,4]]},"406":{"position":[[93,4]]}}}],["verbatim",{"_index":280,"t":{"21":{"position":[[2239,9]]}}}],["verbos",{"_index":3681,"t":{"588":{"position":[[2217,7]]},"656":{"position":[[542,9]]},"810":{"position":[[1070,7],[1089,7]]}}}],["veri",{"_index":82,"t":{"8":{"position":[[388,4]]},"13":{"position":[[191,4]]},"23":{"position":[[2496,4]]},"27":{"position":[[741,4]]},"33":{"position":[[120,4]]},"53":{"position":[[241,4]]},"91":{"position":[[2145,4]]},"133":{"position":[[832,4]]},"139":{"position":[[497,4]]},"151":{"position":[[23,4]]},"177":{"position":[[142,4]]},"185":{"position":[[898,4]]},"195":{"position":[[974,4]]},"197":{"position":[[322,4]]},"213":{"position":[[938,4]]},"241":{"position":[[505,4],[1150,4]]},"247":{"position":[[50,4],[125,4]]},"251":{"position":[[807,4],[1608,4]]},"253":{"position":[[3351,4]]},"262":{"position":[[3536,4]]},"266":{"position":[[1266,4]]},"282":{"position":[[949,4],[1372,4],[1558,4]]},"286":{"position":[[1009,4]]},"304":{"position":[[398,4],[1946,4]]},"310":{"position":[[373,4]]},"336":{"position":[[1328,4]]},"342":{"position":[[34,4],[164,4]]},"352":{"position":[[1124,4]]},"376":{"position":[[2365,4],[6148,4]]},"378":{"position":[[1717,4],[1905,4],[4418,4],[4950,4]]},"382":{"position":[[17,4],[202,4]]},"386":{"position":[[1353,4]]},"398":{"position":[[124,4],[219,4],[1260,4]]},"400":{"position":[[1952,4]]},"406":{"position":[[19,4]]},"408":{"position":[[10,4]]},"426":{"position":[[407,4]]},"446":{"position":[[31,4]]},"448":{"position":[[74,4]]},"452":{"position":[[11,4],[183,4],[657,4]]},"462":{"position":[[2279,4]]},"464":{"position":[[1470,4]]},"466":{"position":[[1254,4]]},"500":{"position":[[24,4]]},"514":{"position":[[892,4]]},"518":{"position":[[316,4],[410,4]]},"532":{"position":[[546,4]]},"538":{"position":[[309,4]]},"552":{"position":[[1054,4]]},"558":{"position":[[1403,4],[1615,4]]},"562":{"position":[[164,4]]},"570":{"position":[[1432,4],[1462,4]]},"572":{"position":[[738,4]]},"574":{"position":[[3124,4]]},"586":{"position":[[872,4]]},"624":{"position":[[780,4]]},"640":{"position":[[576,4]]},"644":{"position":[[170,4]]},"650":{"position":[[590,4]]},"662":{"position":[[695,4]]},"670":{"position":[[1974,4],[2539,4]]},"688":{"position":[[2132,4]]},"694":{"position":[[2323,4]]},"702":{"position":[[2595,4]]},"724":{"position":[[291,4]]},"754":{"position":[[1725,4],[2097,4]]},"764":{"position":[[919,4]]},"782":{"position":[[27,4]]},"794":{"position":[[21,4]]},"796":{"position":[[1395,4],[1570,4]]},"798":{"position":[[522,4],[1221,4]]},"806":{"position":[[1147,4]]},"808":{"position":[[146,4]]},"810":{"position":[[1299,4]]},"820":{"position":[[317,4],[680,4]]},"828":{"position":[[1885,4]]},"834":{"position":[[275,4]]},"858":{"position":[[200,4],[1359,4],[1685,4]]},"874":{"position":[[1143,4]]},"878":{"position":[[143,4]]},"890":{"position":[[1255,4]]},"894":{"position":[[786,4],[1075,4]]},"910":{"position":[[1695,4]]},"912":{"position":[[749,4]]},"955":{"position":[[25,4]]},"975":{"position":[[478,4]]},"1025":{"position":[[413,4]]},"1027":{"position":[[9168,4],[10206,4]]}}}],["verifi",{"_index":4531,"t":{"740":{"position":[[871,6]]},"890":{"position":[[1665,6]]},"900":{"position":[[633,6]]}}}],["versatil",{"_index":1715,"t":{"219":{"position":[[397,9]]}}}],["version",{"_index":943,"t":{"51":{"position":[[553,8],[600,7]]},"65":{"position":[[274,7]]},"67":{"position":[[1252,7],[1584,8]]},"69":{"position":[[3179,7],[3871,7],[5603,7],[5622,7]]},"71":{"position":[[160,7],[221,7],[1681,7],[2009,7]]},"77":{"position":[[576,7],[620,7],[928,7],[4706,8],[4772,9]]},"105":{"position":[[74,7]]},"189":{"position":[[1229,8],[1667,8],[1795,8]]},"300":{"position":[[982,8]]},"304":{"position":[[1387,9]]},"314":{"position":[[86,7]]},"378":{"position":[[853,7]]},"400":{"position":[[1588,7],[1741,7]]},"420":{"position":[[757,7]]},"492":{"position":[[12,8]]},"506":{"position":[[2469,8]]},"518":{"position":[[374,7],[665,7]]},"520":{"position":[[301,7],[309,11],[619,7],[674,7]]},"526":{"position":[[2187,7]]},"540":{"position":[[121,7]]},"544":{"position":[[1864,7]]},"566":{"position":[[622,8]]},"574":{"position":[[772,7]]},"600":{"position":[[1799,7]]},"612":{"position":[[1540,7]]},"670":{"position":[[532,7]]},"678":{"position":[[243,7],[855,7],[922,7],[1143,7],[1296,7]]},"680":{"position":[[1695,7],[1768,7]]},"696":{"position":[[42,7],[451,7],[1153,7],[3868,7]]},"714":{"position":[[1061,7]]},"716":{"position":[[1424,7]]},"720":{"position":[[7783,7]]},"748":{"position":[[388,7]]},"760":{"position":[[80,7],[376,7]]},"766":{"position":[[251,7]]},"796":{"position":[[1414,7]]},"798":{"position":[[920,7]]},"802":{"position":[[57,8],[131,7]]},"806":{"position":[[3971,7]]},"808":{"position":[[1399,7]]},"810":{"position":[[116,7]]},"822":{"position":[[115,8],[186,7],[237,7]]},"834":{"position":[[501,7]]},"862":{"position":[[637,8],[3551,7]]},"864":{"position":[[818,8]]},"866":{"position":[[708,8]]},"878":{"position":[[2019,8]]},"904":{"position":[[346,7],[1158,7]]},"953":{"position":[[401,7]]},"955":{"position":[[233,7]]},"975":{"position":[[215,7]]}}}],["version8do",{"_index":3605,"t":{"580":{"position":[[130,9]]}}}],["work",{"_index":265,"t":{"21":{"position":[[1633,7],[2949,4],[3024,5],[3190,4],[3286,5],[3913,4]]},"23":{"position":[[288,4],[514,7],[766,4],[1151,4],[1337,4],[2358,5],[2476,7],[2515,4]]},"29":{"position":[[142,4],[931,4]]},"31":{"position":[[2474,5],[3283,5]]},"33":{"position":[[247,4]]},"45":{"position":[[605,7],[963,5]]},"47":{"position":[[5872,4],[7978,4]]},"57":{"position":[[683,5],[756,4]]},"67":{"position":[[1403,4]]},"69":{"position":[[69,8],[173,4],[329,4],[1174,4]]},"71":{"position":[[405,4],[1249,6]]},"77":{"position":[[288,6],[988,5],[1661,7],[3600,5],[3656,6]]},"79":{"position":[[92,6]]},"81":{"position":[[353,7],[1670,4],[1936,4]]},"83":{"position":[[124,4],[510,7]]},"87":{"position":[[961,4]]},"91":{"position":[[3220,8]]},"95":{"position":[[976,4]]},"99":{"position":[[106,4],[1540,7],[2047,4]]},"109":{"position":[[295,7]]},"111":{"position":[[297,7]]},"113":{"position":[[507,7],[649,7]]},"117":{"position":[[380,5]]},"119":{"position":[[356,5]]},"121":{"position":[[734,5]]},"129":{"position":[[493,7],[610,7],[935,7]]},"131":{"position":[[182,7]]},"133":{"position":[[976,7],[1058,6],[1186,4]]},"135":{"position":[[854,7]]},"141":{"position":[[123,7],[364,7],[443,7],[587,7]]},"145":{"position":[[60,7],[105,7],[171,7],[392,7],[871,7]]},"147":{"position":[[407,6]]},"175":{"position":[[50,7],[1168,5]]},"185":{"position":[[455,5],[745,4],[1026,4]]},"189":{"position":[[322,7],[693,7],[840,7],[1146,7]]},"205":{"position":[[1633,5]]},"207":{"position":[[908,4]]},"215":{"position":[[65,5]]},"217":{"position":[[1447,5]]},"221":{"position":[[454,4]]},"225":{"position":[[395,5]]},"229":{"position":[[835,5]]},"235":{"position":[[1023,7]]},"237":{"position":[[1213,4]]},"239":{"position":[[558,4]]},"241":{"position":[[495,4],[752,4],[967,4]]},"243":{"position":[[883,4]]},"251":{"position":[[1132,5],[1590,4],[1822,7]]},"253":{"position":[[198,6],[2696,5]]},"255":{"position":[[224,7]]},"257":{"position":[[64,6],[531,6],[6595,5]]},"260":{"position":[[532,8]]},"262":{"position":[[241,5],[2777,5]]},"266":{"position":[[208,4],[1054,4]]},"268":{"position":[[192,5]]},"270":{"position":[[145,6]]},"274":{"position":[[581,4]]},"276":{"position":[[978,5]]},"280":{"position":[[815,7]]},"282":{"position":[[119,4],[1285,5],[1599,4],[1879,4]]},"284":{"position":[[1003,4]]},"288":{"position":[[1421,7]]},"290":{"position":[[118,7],[267,7]]},"292":{"position":[[219,5],[284,5],[537,4],[677,8]]},"302":{"position":[[1070,6]]},"314":{"position":[[306,5]]},"330":{"position":[[103,7]]},"332":{"position":[[80,4],[309,7],[572,4]]},"338":{"position":[[498,4]]},"352":{"position":[[223,7],[323,7]]},"354":{"position":[[216,4],[291,4],[838,5],[1318,5]]},"364":{"position":[[1423,6]]},"366":{"position":[[4745,4]]},"374":{"position":[[585,5]]},"376":{"position":[[287,7],[970,7],[2931,8],[3817,7],[4089,5],[5969,4]]},"378":{"position":[[479,4],[4968,7]]},"380":{"position":[[539,5]]},"384":{"position":[[229,5]]},"386":{"position":[[1047,5]]},"394":{"position":[[15,7],[1948,5]]},"396":{"position":[[341,5],[1533,5],[2244,6],[2365,5]]},"400":{"position":[[150,5],[604,4]]},"404":{"position":[[184,5],[435,4]]},"406":{"position":[[258,5]]},"410":{"position":[[597,5],[655,7]]},"424":{"position":[[1067,6],[1106,6]]},"432":{"position":[[264,4],[658,4],[1232,6]]},"438":{"position":[[1156,5]]},"440":{"position":[[122,4],[265,4]]},"442":{"position":[[56,6]]},"446":{"position":[[1467,5],[2369,4],[4179,7],[4257,5]]},"448":{"position":[[262,6],[960,5],[1018,4]]},"452":{"position":[[318,7]]},"456":{"position":[[273,7]]},"458":{"position":[[67,4]]},"462":{"position":[[1434,6]]},"464":{"position":[[1948,5]]},"468":{"position":[[2336,4],[2527,5]]},"476":{"position":[[286,5],[409,4]]},"482":{"position":[[642,5]]},"494":{"position":[[247,6]]},"500":{"position":[[14,4]]},"502":{"position":[[1155,7]]},"510":{"position":[[2471,5]]},"514":{"position":[[55,4]]},"526":{"position":[[1579,7]]},"550":{"position":[[1296,6],[1583,4]]},"558":{"position":[[1757,5]]},"562":{"position":[[118,4],[971,7]]},"566":{"position":[[891,4]]},"568":{"position":[[212,4],[1479,4]]},"570":{"position":[[160,5],[1232,6]]},"574":{"position":[[847,4],[987,4],[1049,4],[1224,4]]},"580":{"position":[[478,5]]},"584":{"position":[[1214,5],[2368,4],[2554,4]]},"588":{"position":[[1417,4],[2632,4]]},"606":{"position":[[301,4]]},"614":{"position":[[236,6]]},"618":{"position":[[598,4]]},"626":{"position":[[1038,4],[5833,7],[6014,7]]},"628":{"position":[[36,4]]},"634":{"position":[[2089,6]]},"636":{"position":[[257,4],[1164,7]]},"648":{"position":[[2505,5]]},"654":{"position":[[484,4],[690,4],[4374,7]]},"656":{"position":[[174,4]]},"658":{"position":[[187,7],[433,6]]},"664":{"position":[[237,7],[404,4],[586,6],[935,4]]},"666":{"position":[[1079,8],[1678,4]]},"668":{"position":[[1568,4],[2583,7],[2620,7],[2870,4]]},"670":{"position":[[179,5],[225,4],[270,5],[738,4],[823,4],[1543,7],[1797,7],[2030,7],[2506,5],[2556,7],[2802,7]]},"672":{"position":[[78,7],[199,7],[231,7],[672,7]]},"674":{"position":[[127,5],[406,4]]},"678":{"position":[[29,4],[551,7]]},"682":{"position":[[73,7],[147,7],[165,7],[1789,7],[2069,7],[3285,4],[3609,7]]},"684":{"position":[[2908,7],[2985,7],[3037,8]]},"686":{"position":[[1069,4],[1132,4]]},"688":{"position":[[325,7],[556,7],[927,7],[2055,7]]},"696":{"position":[[948,7]]},"698":{"position":[[605,6]]},"700":{"position":[[270,7],[384,7],[1252,7],[2627,7],[3059,7],[3502,7],[3928,7],[3962,4]]},"702":{"position":[[136,7],[1574,7],[2024,7]]},"704":{"position":[[88,7],[298,4],[502,4],[705,7],[1156,7]]},"708":{"position":[[901,7]]},"710":{"position":[[667,4]]},"714":{"position":[[1182,7],[1262,7],[2020,4],[3690,6]]},"716":{"position":[[978,6],[4647,7],[5696,4],[5826,4]]},"720":{"position":[[2905,7]]},"722":{"position":[[1785,7]]},"724":{"position":[[678,4],[821,4],[1013,7],[1118,5]]},"728":{"position":[[369,4]]},"738":{"position":[[625,4],[3934,5],[4609,7],[4767,7],[7087,6],[7897,4]]},"740":{"position":[[894,5]]},"742":{"position":[[19,7]]},"754":{"position":[[612,7],[1214,7],[1420,5],[1803,7],[2472,7]]},"756":{"position":[[102,4]]},"762":{"position":[[452,6]]},"764":{"position":[[102,4],[1597,4],[1696,4]]},"766":{"position":[[82,4]]},"778":{"position":[[1224,5]]},"796":{"position":[[855,5],[1120,7],[1889,7],[1989,4]]},"798":{"position":[[1070,4]]},"800":{"position":[[1289,5]]},"802":{"position":[[1038,7]]},"806":{"position":[[15,7]]},"810":{"position":[[1482,6]]},"816":{"position":[[934,7],[990,7],[1223,7]]},"818":{"position":[[813,7],[1647,5],[1683,7]]},"828":{"position":[[1578,7]]},"830":{"position":[[184,4]]},"832":{"position":[[93,5],[1071,5],[1102,7]]},"834":{"position":[[1678,7],[1765,7],[1878,7],[1937,7],[4337,7],[4573,7]]},"838":{"position":[[101,5],[352,7],[931,4]]},"846":{"position":[[208,7]]},"850":{"position":[[1872,4]]},"876":{"position":[[321,5],[491,4]]},"884":{"position":[[975,4]]},"906":{"position":[[1037,4]]},"908":{"position":[[3649,6]]},"914":{"position":[[0,4]]},"918":{"position":[[417,4]]},"927":{"position":[[658,7],[1109,7]]},"939":{"position":[[450,4]]},"959":{"position":[[137,4]]},"969":{"position":[[67,6]]},"987":{"position":[[188,5],[241,4]]},"995":{"position":[[162,7]]},"1019":{"position":[[1618,5]]},"1023":{"position":[[481,4]]},"1027":{"position":[[1220,4],[2179,5],[4352,4],[7637,5],[7733,7],[7842,7],[10255,4],[10357,6]]},"1035":{"position":[[3275,7]]},"1037":{"position":[[76,5]]}}}],["work.set",{"_index":4935,"t":{"852":{"position":[[769,8]]}}}],["workaround",{"_index":362,"t":{"23":{"position":[[958,10]]}}}],["workflow",{"_index":2747,"t":{"404":{"position":[[996,9]]},"670":{"position":[[3067,8]]},"840":{"position":[[745,8]]}}}],["workhors",{"_index":1215,"t":{"91":{"position":[[1034,10],[2095,11]]},"262":{"position":[[2360,10]]}}}],["workload",{"_index":1296,"t":{"93":{"position":[[1363,10],[1514,9]]}}}],["workspac",{"_index":4848,"t":{"818":{"position":[[356,9]]}}}],["world",{"_index":455,"t":{"25":{"position":[[1596,5]]},"91":{"position":[[634,5]]},"95":{"position":[[1341,5],[1389,5]]},"260":{"position":[[49,5]]},"332":{"position":[[680,5]]},"362":{"position":[[122,5]]},"366":{"position":[[1642,6],[4960,5]]},"392":{"position":[[497,6]]},"738":{"position":[[844,5]]},"896":{"position":[[1465,6]]}}}],["worri",{"_index":372,"t":{"23":{"position":[[1120,5]]},"31":{"position":[[493,5],[3023,5]]},"47":{"position":[[1474,5]]},"77":{"position":[[4845,5]]},"253":{"position":[[435,5],[3694,5]]},"262":{"position":[[256,7]]},"470":{"position":[[186,5]]},"668":{"position":[[2942,5]]},"684":{"position":[[3199,6]]},"710":{"position":[[491,6]]},"720":{"position":[[7029,5]]},"732":{"position":[[518,5]]},"800":{"position":[[1478,5]]}}}],["worst",{"_index":2428,"t":{"366":{"position":[[1625,5]]}}}],["worth",{"_index":926,"t":{"51":{"position":[[36,5]]},"147":{"position":[[804,5]]},"217":{"position":[[6,5]]},"257":{"position":[[6790,5]]},"414":{"position":[[162,5]]},"448":{"position":[[1478,5]]},"606":{"position":[[445,5]]},"810":{"position":[[1434,5]]}}}],["wouldn't",{"_index":186,"t":{"21":{"position":[[2,8]]},"175":{"position":[[1194,8]]}}}],["wow",{"_index":913,"t":{"49":{"position":[[321,4]]},"111":{"position":[[0,4]]},"390":{"position":[[803,4]]}}}],["wp:nfcc#4",{"_index":2012,"t":{"266":{"position":[[645,12]]}}}],["wq",{"_index":4610,"t":{"764":{"position":[[2554,3]]}}}],["wqwrite",{"_index":4678,"t":{"778":{"position":[[2145,8]]}}}],["wrap",{"_index":1625,"t":{"195":{"position":[[2570,4]]},"257":{"position":[[3862,4]]},"514":{"position":[[1044,4]]},"608":{"position":[[292,8]]},"686":{"position":[[464,4]]},"720":{"position":[[5029,4]]}}}],["wrapper",{"_index":2065,"t":{"278":{"position":[[1126,7]]}}}],["writabl",{"_index":3547,"t":{"562":{"position":[[607,9]]}}}],["write",{"_index":282,"t":{"21":{"position":[[2288,5],[2349,5],[2503,6],[2598,5],[2684,7]]},"31":{"position":[[2589,7]]},"47":{"position":[[2290,6],[3291,7]]},"57":{"position":[[1314,5]]},"67":{"position":[[857,7]]},"69":{"position":[[3168,7]]},"71":{"position":[[1085,7]]},"77":{"position":[[1683,6]]},"79":{"position":[[30,5],[132,6]]},"81":{"position":[[2375,5]]},"83":{"position":[[586,6]]},"91":{"position":[[1524,8]]},"107":{"position":[[160,7]]},"121":{"position":[[572,6],[1503,5],[1531,5]]},"125":{"position":[[1200,5],[1338,5]]},"129":{"position":[[1080,5]]},"135":{"position":[[309,7],[744,7]]},"151":{"position":[[50,8],[77,5],[130,7]]},"175":{"position":[[2160,5]]},"185":{"position":[[1003,7]]},"205":{"position":[[1311,5]]},"237":{"position":[[129,5]]},"247":{"position":[[568,7],[962,7]]},"249":{"position":[[227,5],[313,5],[464,6],[523,5],[625,5]]},"251":{"position":[[52,6],[1305,7]]},"253":{"position":[[751,5],[1000,5],[3812,5]]},"255":{"position":[[499,5],[1363,5],[1868,6]]},"257":{"position":[[983,6],[1133,7],[2925,6],[4080,5],[4173,5],[4968,5],[5882,5],[6346,5],[6455,5],[6499,5]]},"259":{"position":[[791,5]]},"260":{"position":[[484,7],[601,5]]},"262":{"position":[[2544,7],[2575,5]]},"266":{"position":[[1497,7]]},"280":{"position":[[470,5],[698,5]]},"282":{"position":[[2313,5]]},"284":{"position":[[627,5],[1469,7]]},"288":{"position":[[606,5]]},"290":{"position":[[2621,8],[2781,5]]},"292":{"position":[[746,7]]},"310":{"position":[[461,5]]},"338":{"position":[[1527,8]]},"352":{"position":[[829,5],[1252,7],[2392,5]]},"356":{"position":[[458,5]]},"360":{"position":[[156,6]]},"376":{"position":[[5455,5]]},"378":{"position":[[1097,5],[4482,7]]},"384":{"position":[[135,5],[541,5]]},"386":{"position":[[1217,5]]},"394":{"position":[[1716,6]]},"396":{"position":[[1559,5]]},"408":{"position":[[604,7]]},"422":{"position":[[1567,5],[2034,5]]},"424":{"position":[[55,5]]},"426":{"position":[[200,5]]},"430":{"position":[[226,5],[842,5],[946,5]]},"432":{"position":[[156,5]]},"438":{"position":[[249,5]]},"446":{"position":[[1074,8],[3095,7]]},"462":{"position":[[2104,6],[2372,6]]},"464":{"position":[[1408,6],[1503,6]]},"468":{"position":[[117,6],[1658,5]]},"476":{"position":[[218,7],[425,5]]},"486":{"position":[[270,5]]},"490":{"position":[[615,7]]},"508":{"position":[[313,7],[2049,5],[2140,5],[3437,5],[3607,7],[4278,5]]},"510":{"position":[[864,5]]},"512":{"position":[[244,5],[696,5]]},"518":{"position":[[558,7]]},"520":{"position":[[24,5],[188,6]]},"522":{"position":[[1518,5]]},"526":{"position":[[223,5]]},"528":{"position":[[772,5]]},"530":{"position":[[800,5]]},"532":{"position":[[6,5],[266,5],[568,5],[599,7],[759,5],[945,7],[1246,5]]},"534":{"position":[[37,7],[1328,5],[1377,5],[1721,5],[1894,6]]},"536":{"position":[[629,5],[1338,5]]},"538":{"position":[[807,5]]},"544":{"position":[[1011,6],[1128,5],[1186,6],[1380,5],[1579,5]]},"546":{"position":[[277,7]]},"554":{"position":[[692,7],[934,5]]},"556":{"position":[[156,5]]},"570":{"position":[[554,5],[1560,5]]},"572":{"position":[[21,7],[1317,5]]},"574":{"position":[[359,5]]},"582":{"position":[[731,5]]},"584":{"position":[[1655,5]]},"598":{"position":[[76,7]]},"600":{"position":[[1506,5]]},"602":{"position":[[1235,5]]},"604":{"position":[[484,7],[512,5],[881,5],[1045,7]]},"626":{"position":[[6207,5],[7191,5]]},"648":{"position":[[490,5]]},"664":{"position":[[2269,7]]},"666":{"position":[[422,5],[1834,5]]},"668":{"position":[[265,7],[1476,5]]},"686":{"position":[[619,5],[958,7]]},"710":{"position":[[143,5],[241,5]]},"716":{"position":[[1322,5],[1611,7],[3710,5]]},"718":{"position":[[1030,5]]},"720":{"position":[[7,5]]},"734":{"position":[[799,7]]},"738":{"position":[[4878,5],[7176,5]]},"760":{"position":[[2745,7]]},"764":{"position":[[754,5],[1212,7],[2561,5]]},"794":{"position":[[773,5]]},"796":{"position":[[538,5],[869,5],[1702,7],[1973,5],[2271,7]]},"798":{"position":[[12,7],[547,5]]},"800":{"position":[[24,5],[266,5],[728,5],[1012,5]]},"802":{"position":[[1788,5],[2270,6],[3311,5]]},"804":{"position":[[1895,5]]},"806":{"position":[[1442,6],[2273,5]]},"810":{"position":[[1179,7]]},"812":{"position":[[212,5]]},"816":{"position":[[564,5],[862,6]]},"852":{"position":[[173,5],[782,5],[1500,5]]},"856":{"position":[[11,7]]},"862":{"position":[[1120,6],[1899,5],[2403,7],[2536,7],[2834,7],[2878,7],[3584,5]]},"864":{"position":[[94,5]]},"876":{"position":[[584,5]]},"878":{"position":[[826,8]]},"900":{"position":[[901,5],[1276,5],[1500,5]]},"951":{"position":[[33,5]]},"967":{"position":[[63,7]]},"1027":{"position":[[2448,5],[4839,7],[10229,7]]}}}],["write_command_then_count",{"_index":3502,"t":{"544":{"position":[[1262,26],[1666,26]]}}}],["write_definition(word",{"_index":4803,"t":{"806":{"position":[[1495,22]]}}}],["writer",{"_index":2629,"t":{"394":{"position":[[1308,6],[2248,6]]},"506":{"position":[[2709,6]]}}}],["written",{"_index":296,"t":{"21":{"position":[[2650,7]]},"31":{"position":[[2754,7]]},"47":{"position":[[3850,7]]},"69":{"position":[[162,7]]},"91":{"position":[[990,7]]},"175":{"position":[[1490,7]]},"247":{"position":[[436,7]]},"253":{"position":[[2149,7]]},"257":{"position":[[1402,7],[1457,7],[6028,7],[6105,7]]},"292":{"position":[[829,7]]},"378":{"position":[[4902,7]]},"390":{"position":[[771,7]]},"396":{"position":[[799,8]]},"438":{"position":[[1039,8]]},"446":{"position":[[2262,7]]},"458":{"position":[[501,7]]},"508":{"position":[[2798,8]]},"524":{"position":[[753,7]]},"532":{"position":[[1079,7]]},"534":{"position":[[1240,8],[1673,7]]},"550":{"position":[[477,7]]},"552":{"position":[[742,7]]},"558":{"position":[[1555,7],[1714,7]]},"596":{"position":[[1945,7]]},"626":{"position":[[6316,7]]},"644":{"position":[[245,7]]},"678":{"position":[[659,7]]},"716":{"position":[[1457,7],[1520,7],[5352,7]]},"722":{"position":[[2000,7],[3067,7]]},"760":{"position":[[2070,7]]},"764":{"position":[[1315,7],[1392,7]]},"798":{"position":[[1401,7]]},"802":{"position":[[259,7]]},"852":{"position":[[1234,7],[1398,7]]},"862":{"position":[[2278,7]]},"866":{"position":[[519,7]]},"878":{"position":[[683,7]]},"945":{"position":[[76,7]]},"1029":{"position":[[877,7]]}}}],["wrong",{"_index":2213,"t":{"308":{"position":[[693,6]]},"468":{"position":[[307,6]]},"648":{"position":[[2336,5]]},"850":{"position":[[1731,5]]}}}],["wrote",{"_index":1990,"t":{"260":{"position":[[103,5]]},"392":{"position":[[579,5]]},"422":{"position":[[2119,5]]},"534":{"position":[[974,5]]}}}],["wsl",{"_index":358,"t":{"23":{"position":[[819,4],[997,3]]},"69":{"position":[[634,3]]},"290":{"position":[[2367,6]]}}}],["www.cheat.sh",{"_index":929,"t":{"51":{"position":[[65,13]]}}}],["www.effect",{"_index":5179,"t":{"904":{"position":[[447,13]]}}}],["www.github.com/effect",{"_index":4001,"t":{"664":{"position":[[679,24]]}}}],["x",{"_index":204,"t":{"21":{"position":[[288,1],[297,1],[303,1]]},"47":{"position":[[7185,1]]},"175":{"position":[[265,1]]},"217":{"position":[[999,1],[1044,1]]},"219":{"position":[[1159,1],[1256,1],[1305,1]]},"249":{"position":[[1023,1],[1079,1],[1134,1]]},"282":{"position":[[2465,2]]},"302":{"position":[[435,2],[461,1],[581,2]]},"330":{"position":[[400,2]]},"428":{"position":[[612,2]]},"538":{"position":[[978,2]]},"558":{"position":[[270,1],[304,1],[1030,1],[1162,2],[1874,1]]},"562":{"position":[[618,1]]},"602":{"position":[[844,1]]},"722":{"position":[[323,1],[1424,1],[1482,2]]},"732":{"position":[[321,1]]},"734":{"position":[[1530,1],[2236,1]]},"740":{"position":[[564,1]]},"744":{"position":[[1586,1],[1859,1],[2705,1],[2766,1]]},"778":{"position":[[1737,2]]},"808":{"position":[[874,2]]},"832":{"position":[[915,2],[1416,1]]},"842":{"position":[[1264,1]]},"852":{"position":[[415,2],[778,3],[856,1],[908,2],[2440,1]]},"908":{"position":[[2782,2]]},"969":{"position":[[199,2]]},"1023":{"position":[[824,1],[850,3],[894,1],[922,3]]}}}],["x'",{"_index":2180,"t":{"302":{"position":[[633,3]]}}}],["x/tmp/2021",{"_index":4940,"t":{"852":{"position":[[1176,11]]}}}],["x01",{"_index":2256,"t":{"336":{"position":[[684,14]]}}}],["x08\\x0b\\x0c\\x0e",{"_index":2257,"t":{"336":{"position":[[699,16],[991,16]]}}}],["x09\\x0b\\x0c\\x0e",{"_index":2261,"t":{"336":{"position":[[752,16],[1040,16]]}}}],["x1f\\x21",{"_index":2275,"t":{"336":{"position":[[1008,8]]}}}],["x1f\\x21\\x23",{"_index":2258,"t":{"336":{"position":[[716,12]]}}}],["x5a\\x53",{"_index":2276,"t":{"336":{"position":[[1017,8]]}}}],["x5b\\x5d",{"_index":2259,"t":{"336":{"position":[[729,8]]}}}],["x64",{"_index":5474,"t":{"1035":{"position":[[448,3],[1461,3]]}}}],["x64.tar.gz/home/dwmkerr/downloads/dotnet",{"_index":5476,"t":{"1035":{"position":[[516,40],[1529,40]]}}}],["x64.tar.gz/home/dwmkerr/downloads/effect",{"_index":5479,"t":{"1035":{"position":[[573,43],[1586,43]]}}}],["x7f",{"_index":2277,"t":{"336":{"position":[[1057,11]]}}}],["x7f])*\")@(?:(?:[a",{"_index":2262,"t":{"336":{"position":[[769,18]]}}}],["x7f]|\\\\[\\x01",{"_index":2260,"t":{"336":{"position":[[738,13],[1026,13]]}}}],["x86_64)...to",{"_index":3874,"t":{"636":{"position":[[1821,12]]}}}],["x^e",{"_index":4671,"t":{"778":{"position":[[1723,4]]}}}],["xarg",{"_index":1656,"t":{"207":{"position":[[439,5],[517,6],[528,5]]},"211":{"position":[[661,5]]},"462":{"position":[[4,5],[185,5],[216,5],[378,5],[835,5],[1796,5],[1891,6],[1987,5],[2159,5],[2208,5],[2312,5],[2600,5],[2681,5]]},"464":{"position":[[26,5],[302,5],[579,5],[626,5],[908,5],[1110,5],[1242,5],[1655,5],[1996,5],[2327,5]]},"466":{"position":[[11,5],[161,5],[244,6],[380,5],[464,5],[578,5],[1031,5],[1101,5]]},"468":{"position":[[42,5],[347,5],[583,5],[867,5],[992,5],[1075,5],[1285,5],[1641,5],[1697,5],[1946,5],[2201,5],[2554,5],[3113,6]]},"470":{"position":[[29,5],[340,6],[553,5],[644,5],[1048,5],[1184,5]]},"472":{"position":[[123,5],[499,5]]},"474":{"position":[[30,6],[272,5],[324,6],[434,5]]}}}],["xargs./file40.txt",{"_index":3100,"t":{"462":{"position":[[1916,17]]}}}],["xclip",{"_index":381,"t":{"23":{"position":[[1456,5],[1569,5]]}}}],["xclipalia",{"_index":388,"t":{"23":{"position":[[1693,10]]}}}],["xdg",{"_index":1163,"t":{"81":{"position":[[1264,3],[2420,3]]},"83":{"position":[[1075,3]]}}}],["xecho",{"_index":4943,"t":{"852":{"position":[[1877,5]]}}}],["xiaoyou",{"_index":11,"t":{"4":{"position":[[138,7]]}}}],["xii",{"_index":5369,"t":{"1007":{"position":[[149,4]]}}}],["xr",{"_index":1701,"t":{"217":{"position":[[996,2],[1041,2]]},"249":{"position":[[1017,2],[1020,2],[1073,2],[1076,2],[1128,2],[1131,2]]},"302":{"position":[[458,2]]},"744":{"position":[[2702,2],[2763,2]]}}}],["xterm",{"_index":4439,"t":{"734":{"position":[[1212,5]]}}}],["xxx",{"_index":79,"t":{"8":{"position":[[324,3]]},"918":{"position":[[303,3]]}}}],["xz",{"_index":4932,"t":{"850":{"position":[[1006,2]]}}}],["xzf",{"_index":4966,"t":{"858":{"position":[[1138,3]]}}}],["y",{"_index":387,"t":{"23":{"position":[[1691,1]]},"43":{"position":[[1313,1]]},"107":{"position":[[586,1]]},"215":{"position":[[725,1]]},"470":{"position":[[1019,3]]},"534":{"position":[[360,4]]},"538":{"position":[[556,4],[1364,4],[2649,4]]},"572":{"position":[[574,1],[578,1],[1063,3],[1070,3]]},"594":{"position":[[866,3],[923,3],[1131,3]]},"602":{"position":[[1067,3],[1117,3]]},"626":{"position":[[5337,4]]},"772":{"position":[[433,1]]},"822":{"position":[[449,1]]},"852":{"position":[[473,4],[1046,3]]}}}],["y/n",{"_index":3304,"t":{"508":{"position":[[3806,6],[4192,6]]},"594":{"position":[[830,6],[1248,6],[1278,6],[1309,6],[1340,6],[1371,6],[1401,6],[1432,6]]},"602":{"position":[[691,3]]}}}],["y/n/c",{"_index":3742,"t":{"602":{"position":[[946,8]]}}}],["yadd",{"_index":3715,"t":{"594":{"position":[[1255,4],[1316,4],[1347,4],[1378,4]]}}}],["yahoo.com",{"_index":2306,"t":{"342":{"position":[[327,11],[1106,10],[1259,10]]},"344":{"position":[[1348,10],[2138,10],[2601,10],[3121,10],[3562,10]]},"346":{"position":[[203,10],[898,10],[1307,10]]}}}],["yaml",{"_index":2467,"t":{"376":{"position":[[49,4],[326,4],[1834,4]]},"406":{"position":[[604,4]]}}}],["yank",{"_index":4639,"t":{"772":{"position":[[435,4],[1475,4]]},"780":{"position":[[247,4]]}}}],["yapt",{"_index":3891,"t":{"648":{"position":[[2102,4]]}}}],["yay",{"_index":3582,"t":{"572":{"position":[[1164,6]]}}}],["ye",{"_index":1689,"t":{"215":{"position":[[732,6]]},"257":{"position":[[5595,4]]},"470":{"position":[[1192,3]]},"572":{"position":[[507,5],[530,4],[582,3],[807,4],[1149,5],[1155,5]]},"734":{"position":[[1829,3]]},"838":{"position":[[2199,3]]},"898":{"position":[[1684,3],[2047,3]]}}}],["year",{"_index":1195,"t":{"89":{"position":[[81,5]]},"91":{"position":[[66,6],[87,5],[675,6],[2282,5]]},"115":{"position":[[507,4]]},"205":{"position":[[33,6],[1370,5]]},"336":{"position":[[174,5]]},"352":{"position":[[2833,5]]},"374":{"position":[[371,4]]},"376":{"position":[[2046,6]]},"408":{"position":[[121,5]]},"454":{"position":[[156,5]]},"564":{"position":[[148,5],[170,5],[198,6],[271,6],[499,5],[517,5],[545,6]]},"566":{"position":[[212,5],[230,5],[259,6]]},"678":{"position":[[1089,5]]},"744":{"position":[[295,5]]},"975":{"position":[[85,6]]}}}],["year/month/d",{"_index":4382,"t":{"720":{"position":[[3143,15]]}}}],["yellow",{"_index":4272,"t":{"716":{"position":[[1987,9],[2407,7],[2424,8]]}}}],["yes/no/[fingerprint",{"_index":5145,"t":{"898":{"position":[[1482,23]]}}}],["yeshost",{"_index":5237,"t":{"910":{"position":[[934,7]]}}}],["yesorno",{"_index":3306,"t":{"508":{"position":[[3846,11]]}}}],["yesornoecho",{"_index":3305,"t":{"508":{"position":[[3815,11]]}}}],["yetchang",{"_index":4105,"t":{"682":{"position":[[1236,10],[2696,10]]}}}],["yetuntrack",{"_index":4102,"t":{"682":{"position":[[297,12]]}}}],["you'd",{"_index":679,"t":{"47":{"position":[[310,5]]},"69":{"position":[[1676,5]]},"231":{"position":[[86,5]]},"300":{"position":[[654,5]]},"696":{"position":[[5964,5]]},"742":{"position":[[1206,5]]},"806":{"position":[[1204,5]]}}}],["you'll",{"_index":298,"t":{"21":{"position":[[2722,6],[3560,6]]},"31":{"position":[[2713,6],[3107,6],[3193,6]]},"37":{"position":[[151,6]]},"43":{"position":[[1323,6],[1371,6]]},"45":{"position":[[10,6]]},"47":{"position":[[0,6],[2485,6],[3816,6],[4427,6],[5713,6],[6118,6],[6594,6]]},"51":{"position":[[1163,6]]},"57":{"position":[[1393,6]]},"79":{"position":[[346,6]]},"99":{"position":[[1166,6]]},"121":{"position":[[13,6],[644,6]]},"175":{"position":[[214,6]]},"183":{"position":[[458,6]]},"207":{"position":[[80,6],[817,6]]},"247":{"position":[[774,6]]},"255":{"position":[[485,6],[915,6]]},"257":{"position":[[6010,6]]},"268":{"position":[[233,6]]},"292":{"position":[[1881,6]]},"360":{"position":[[409,6]]},"382":{"position":[[144,6]]},"422":{"position":[[2060,6]]},"442":{"position":[[1673,6]]},"446":{"position":[[210,6],[3039,6]]},"458":{"position":[[1614,6]]},"474":{"position":[[403,6]]},"510":{"position":[[555,6]]},"572":{"position":[[456,6]]},"626":{"position":[[6539,6]]},"654":{"position":[[4049,6]]},"660":{"position":[[595,6]]},"684":{"position":[[1465,6]]},"702":{"position":[[2037,6]]},"710":{"position":[[538,6]]},"738":{"position":[[3949,6]]},"756":{"position":[[158,6],[257,6],[319,6]]},"762":{"position":[[151,6]]},"766":{"position":[[1505,6]]},"800":{"position":[[1701,6]]},"818":{"position":[[1124,6],[1923,6]]},"826":{"position":[[10,6]]},"834":{"position":[[2561,6]]},"892":{"position":[[1144,6],[1416,6]]},"900":{"position":[[829,6]]}}}],["you'v",{"_index":373,"t":{"23":{"position":[[1170,6]]},"71":{"position":[[1242,6],[1920,6]]},"77":{"position":[[1281,6]]},"99":{"position":[[711,6]]},"183":{"position":[[3,6]]},"185":{"position":[[29,6]]},"201":{"position":[[33,6]]},"247":{"position":[[721,6]]},"276":{"position":[[444,6],[601,6]]},"302":{"position":[[133,7]]},"394":{"position":[[3,6],[42,6]]},"744":{"position":[[2562,6]]}}}],["your",{"_index":4493,"t":{"738":{"position":[[3713,6]]}}}],["yourself",{"_index":464,"t":{"27":{"position":[[354,8]]},"133":{"position":[[583,8]]},"163":{"position":[[98,8],[187,8]]},"262":{"position":[[407,8]]},"292":{"position":[[2160,10]]},"332":{"position":[[244,8]]},"382":{"position":[[383,8]]},"408":{"position":[[660,8],[767,8]]},"414":{"position":[[89,8]]},"438":{"position":[[1653,8]]},"446":{"position":[[222,8]]},"482":{"position":[[455,8]]},"492":{"position":[[635,8]]},"572":{"position":[[12,8]]},"626":{"position":[[197,8],[6898,9]]},"648":{"position":[[2374,8]]},"664":{"position":[[415,9]]},"670":{"position":[[1039,8]]},"694":{"position":[[1476,8]]},"700":{"position":[[136,8]]},"708":{"position":[[410,9]]},"754":{"position":[[2070,8]]},"810":{"position":[[1170,8]]},"834":{"position":[[104,8],[2581,8]]},"943":{"position":[[238,8]]}}}],["yourself3",{"_index":2839,"t":{"438":{"position":[[1695,10]]}}}],["yrandom",{"_index":3723,"t":{"594":{"position":[[1439,7]]}}}],["yy",{"_index":3580,"t":{"572":{"position":[[851,6],[1034,5]]},"772":{"position":[[1472,2]]}}}],["yyyi",{"_index":3434,"t":{"534":{"position":[[329,4]]},"538":{"position":[[532,4]]},"626":{"position":[[5313,4]]},"852":{"position":[[449,4]]}}}],["z",{"_index":1085,"t":{"71":{"position":[[383,1]]},"161":{"position":[[121,1]]},"231":{"position":[[844,2]]},"286":{"position":[[983,2]]},"288":{"position":[[1072,1]]},"290":{"position":[[611,2]]},"344":{"position":[[1089,1],[1108,1]]},"378":{"position":[[4777,1]]},"428":{"position":[[1133,1]]},"490":{"position":[[480,1]]},"508":{"position":[[2409,1],[2443,1]]},"538":{"position":[[3037,2],[3043,2]]},"560":{"position":[[233,1]]},"568":{"position":[[479,1],[509,1],[587,1]]},"574":{"position":[[192,1],[1925,1],[2876,1]]},"584":{"position":[[657,1],[684,1],[829,1]]},"612":{"position":[[959,1]]},"616":{"position":[[4,1]]},"622":{"position":[[1428,1]]},"668":{"position":[[2113,1]]},"714":{"position":[[1881,1],[2074,1],[2087,1],[2280,1],[2374,1]]},"720":{"position":[[3457,1],[3488,1],[4137,1],[4212,1],[4512,1],[4593,1],[4628,1],[4753,1],[4789,1],[6493,1]]},"724":{"position":[[0,1],[196,1],[248,1],[1161,1]]},"726":{"position":[[632,1]]},"738":{"position":[[1248,1],[1528,1],[7943,1]]},"742":{"position":[[726,1]]},"828":{"position":[[752,2],[855,1]]},"834":{"position":[[1246,1],[1283,1]]},"842":{"position":[[185,1],[1808,1]]},"878":{"position":[[707,1],[1014,1],[1027,1],[1951,1]]},"918":{"position":[[872,1]]},"975":{"position":[[255,1]]}}}],["z0",{"_index":2254,"t":{"336":{"position":[[628,2],[661,2],[788,2],[799,2],[809,2],[823,2],[834,2],[844,2],[966,2],[976,2]]},"344":{"position":[[1032,2],[1188,2],[1201,2],[1889,2],[1974,2],[1989,2],[2741,2],[4908,2]]}}}],["z[1",{"_index":1791,"t":{"231":{"position":[[374,8],[751,5]]}}}],["za",{"_index":2323,"t":{"344":{"position":[[1029,2],[1185,2],[1198,2],[1886,2],[1971,2],[1986,2],[2738,2],[4905,2]]}}}],["zegrep",{"_index":2582,"t":{"390":{"position":[[146,7]]}}}],["zero",{"_index":2307,"t":{"342":{"position":[[528,4],[1629,4]]},"368":{"position":[[1458,4]]},"396":{"position":[[764,4]]},"474":{"position":[[644,5],[739,4]]},"490":{"position":[[460,5]]},"536":{"position":[[516,6],[552,4],[1188,4],[1222,4]]},"538":{"position":[[2463,4]]},"550":{"position":[[333,4],[633,4],[886,4],[1106,4],[1486,4],[1599,4],[1695,6],[1735,4]]},"552":{"position":[[155,4]]},"560":{"position":[[226,5],[269,5]]},"562":{"position":[[567,5]]},"572":{"position":[[1096,4]]},"596":{"position":[[1209,4]]},"734":{"position":[[911,4]]}}}],["zfgrep",{"_index":2583,"t":{"390":{"position":[[154,6]]}}}],["zgrep",{"_index":2581,"t":{"390":{"position":[[139,6],[909,5]]}}}],["zhou",{"_index":35,"t":{"4":{"position":[[375,6]]}}}],["ziff",{"_index":416,"t":{"25":{"position":[[159,4]]}}}],["ziffagn",{"_index":493,"t":{"29":{"position":[[468,9]]},"31":{"position":[[761,9]]},"251":{"position":[[308,9]]}}}],["ziffarti",{"_index":500,"t":{"29":{"position":[[709,9],[719,9]]},"31":{"position":[[896,9],[906,9]]}}}],["ziffcletu",{"_index":501,"t":{"29":{"position":[[729,10]]},"31":{"position":[[916,10],[1124,10]]},"251":{"position":[[619,10]]}}}],["ziffkirk",{"_index":484,"t":{"29":{"position":[[328,8]]},"31":{"position":[[621,8]]},"251":{"position":[[168,8]]}}}],["ziffnick",{"_index":485,"t":{"29":{"position":[[368,8]]},"31":{"position":[[661,8]]},"251":{"position":[[208,8]]}}}],["zip",{"_index":1349,"t":{"99":{"position":[[249,6],[894,3],[2011,3],[2106,3]]},"101":{"position":[[268,3]]},"103":{"position":[[20,3],[430,3]]},"105":{"position":[[67,6],[280,3]]},"123":{"position":[[25,3],[134,3],[149,3],[189,3],[356,4],[394,3],[418,3],[474,3],[506,3],[693,7]]},"227":{"position":[[133,3]]},"406":{"position":[[836,3],[908,3]]},"923":{"position":[[153,3]]}}}],["zoom",{"_index":4862,"t":{"828":{"position":[[618,7],[641,6],[727,4],[896,4]]},"842":{"position":[[1810,4]]},"979":{"position":[[177,4]]},"1013":{"position":[[177,4]]}}}],["zsh",{"_index":1566,"t":{"185":{"position":[[75,3]]},"286":{"position":[[961,3]]},"288":{"position":[[2241,5]]},"290":{"position":[[434,4],[607,3],[656,5]]},"304":{"position":[[2855,4]]},"308":{"position":[[206,3]]},"320":{"position":[[100,3]]},"508":{"position":[[2483,3],[2581,4]]},"568":{"position":[[314,5],[379,4]]},"574":{"position":[[1519,5],[1908,5],[2812,3]]},"616":{"position":[[445,3]]},"622":{"position":[[1440,3]]},"630":{"position":[[184,3]]},"648":{"position":[[1941,3],[2115,4],[2124,5]]},"660":{"position":[[1137,5]]},"692":{"position":[[435,5]]},"694":{"position":[[849,5],[1221,5],[1883,5]]},"696":{"position":[[5534,5]]},"702":{"position":[[620,5]]},"714":{"position":[[1893,3]]},"720":{"position":[[4032,3],[4224,3]]},"724":{"position":[[311,4],[489,4],[741,4],[807,4]]},"738":{"position":[[690,4]]},"740":{"position":[[843,4]]},"878":{"position":[[622,3]]}}}],["zsh'.apt",{"_index":3890,"t":{"648":{"position":[[2084,9]]}}}],["zsh'.chsh",{"_index":3894,"t":{"648":{"position":[[2251,10]]}}}],["zsh_regex",{"_index":3559,"t":{"568":{"position":[[416,10]]}}}],["zsh_regex=\"zsh$\"if",{"_index":3558,"t":{"568":{"position":[[384,18]]}}}],["zsh_version",{"_index":4386,"t":{"720":{"position":[[3509,14],[3974,11]]}}}],["zshmisc",{"_index":4392,"t":{"720":{"position":[[4838,7]]},"726":{"position":[[649,7]]},"918":{"position":[[920,7]]}}}],["zshrc",{"_index":3787,"t":{"622":{"position":[[1457,8]]},"742":{"position":[[735,8]]},"744":{"position":[[2487,8]]}}}],["zshrc)for",{"_index":4555,"t":{"746":{"position":[[603,12]]}}}]],"pipeline":["stemmer"]}}] \ No newline at end of file diff --git a/pr-preview/pr-346/search/index.html b/pr-preview/pr-346/search/index.html new file mode 100644 index 00000000..36601e44 --- /dev/null +++ b/pr-preview/pr-346/search/index.html @@ -0,0 +1,26 @@ + + + + + +Search the documentation | Effective Shell + + + + + + + + + + + + + + +
+

Search the documentation

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/sitemap.xml b/pr-preview/pr-346/sitemap.xml new file mode 100644 index 00000000..e6a61d9e --- /dev/null +++ b/pr-preview/pr-346/sitemap.xml @@ -0,0 +1 @@ +https://effective-shell.com/donateweekly0.5https://effective-shell.com/donate/thanksweekly0.5https://effective-shell.com/searchweekly0.5https://effective-shell.com/weekly0.5https://effective-shell.com/appendices/thanks/weekly0.5https://effective-shell.com/core-skills/what-is-a-shell/hack-onweekly0.5https://effective-shell.com/part-1-transitioning-to-the-shell/weekly0.5https://effective-shell.com/part-1-transitioning-to-the-shell/become-a-clipboard-gymnast/weekly0.5https://effective-shell.com/part-1-transitioning-to-the-shell/getting-help/weekly0.5https://effective-shell.com/part-1-transitioning-to-the-shell/getting-started/weekly0.5https://effective-shell.com/part-1-transitioning-to-the-shell/managing-your-files/weekly0.5https://effective-shell.com/part-1-transitioning-to-the-shell/navigating-your-system/weekly0.5https://effective-shell.com/part-1-transitioning-to-the-shell/the-renaissance-of-the-shell/weekly0.5https://effective-shell.com/part-2-core-skill/weekly0.5https://effective-shell.com/part-2-core-skills/finding-filesweekly0.5https://effective-shell.com/part-2-core-skills/fly-on-the-command-lineweekly0.5https://effective-shell.com/part-2-core-skills/job-controlweekly0.5https://effective-shell.com/part-2-core-skills/thinking-in-pipelines/weekly0.5https://effective-shell.com/part-2-core-skills/understanding-commandsweekly0.5https://effective-shell.com/part-2-core-skills/what-is-a-shellweekly0.5https://effective-shell.com/part-3-manipulating-text/weekly0.5https://effective-shell.com/part-3-manipulating-text/advanced-text-manipulation/weekly0.5https://effective-shell.com/part-3-manipulating-text/build-commands-on-the-fly/weekly0.5https://effective-shell.com/part-3-manipulating-text/get-to-grips-with-grep/weekly0.5https://effective-shell.com/part-3-manipulating-text/regex-essentials/weekly0.5https://effective-shell.com/part-3-manipulating-text/shell-script-essentials/weekly0.5https://effective-shell.com/part-3-manipulating-text/slice-and-dice-text/weekly0.5https://effective-shell.com/part-3-manipulating-text/variables-reading-input-and-mathematics/weekly0.5https://effective-shell.com/part-4-shell-scripting/weekly0.5https://effective-shell.com/part-4-shell-scripting/functions-parameters-and-error-handlingweekly0.5https://effective-shell.com/part-4-shell-scripting/loops-and-working-with-files-and-foldersweekly0.5https://effective-shell.com/part-4-shell-scripting/mastering-conditional-logicweekly0.5https://effective-shell.com/part-4-shell-scripting/useful-patterns-for-shell-scriptsweekly0.5https://effective-shell.com/part-5-building-your-toolkit/weekly0.5https://effective-shell.com/part-5-building-your-toolkit/configuring-the-shellweekly0.5https://effective-shell.com/part-5-building-your-toolkit/controlling-changes-with-gitweekly0.5https://effective-shell.com/part-5-building-your-toolkit/customising-your-command-promptweekly0.5https://effective-shell.com/part-5-building-your-toolkit/managing-rempte-git-repositories/weekly0.5https://effective-shell.com/part-5-building-your-toolkit/managing-your-dotfilesweekly0.5https://effective-shell.com/part-6-advanced-techniques/weekly0.5https://effective-shell.com/part-6-advanced-techniques/a-vim-crash-course/weekly0.5https://effective-shell.com/part-6-advanced-techniques/how-to-avoid-scripting/weekly0.5https://effective-shell.com/part-6-advanced-techniques/master-the-multiplexer/weekly0.5https://effective-shell.com/part-6-advanced-techniques/the-secure-shell/weekly0.5https://effective-shell.com/part-6-advanced-techniques/understanding-shell-expansion/weekly0.5https://effective-shell.com/work-in-progressweekly0.5https://effective-shell.com/xx-appendices/essential-manpagesweekly0.5https://effective-shell.com/xx-appendices/exercisesweekly0.5https://effective-shell.com/xx-appendices/index-of-commandsweekly0.5https://effective-shell.com/xx-appendices/installing-samples/weekly0.5https://effective-shell.com/xx-appendices/posixweekly0.5https://effective-shell.com/xx-appendices/recommended-reading/weekly0.5https://effective-shell.com/xx-appendices/shell-parameter-expansionweekly0.5https://effective-shell.com/xx-appendices/the-futureweekly0.5https://effective-shell.com/zz-developer-guide/componentsweekly0.5https://effective-shell.com/zz-developer-guide/images-and-diagramsweekly0.5https://effective-shell.com/zz-developer-guide/markdown-guideweekly0.5https://effective-shell.com/zz-developer-guide/recording-terminal-sessionsweekly0.5 \ No newline at end of file diff --git a/pr-preview/pr-346/work-in-progress/index.html b/pr-preview/pr-346/work-in-progress/index.html new file mode 100644 index 00000000..d7cda176 --- /dev/null +++ b/pr-preview/pr-346/work-in-progress/index.html @@ -0,0 +1,26 @@ + + + + + +Work in Progress! | Effective Shell + + + + + + + + + + + + + + +
+

Work in Progress!

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!

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.

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).

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/essential-manpages/index.html b/pr-preview/pr-346/xx-appendices/essential-manpages/index.html new file mode 100644 index 00000000..156dc29e --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/essential-manpages/index.html @@ -0,0 +1,26 @@ + + + + + +essential-manpages | Effective Shell + + + + + + + + + + + + + + +
+

essential-manpages

The Most Important Manpages​

As an appendix, or printed reference, list of the top ten manpages?

  • man re_pattern - basic and extended regex patterns
  • man test is an excellent way to quickly check common tests (existence of a file etc)
  • man set is super useful when checking options like set -ex in scripts
  • man re_format
  • man getopt
  • man XXX show signal commands (Ctrl+V etc)
  • man bash search for ARITHMETIC\ EVALUATION to find how arithmetic operators work in bash
  • man bash search GRAMMAR for pipelines, if statements, conditionals, loops, lists and so on
  • man bash search for ^EXPANSION to see all shell expansion operators
  • man bash search for ^INVOCATION to find details on startup and the startup files that are read
  • man bash search for ^[ ]+shopt to find descriptions of shell options

Essential Bash Manpages

ManpageSearchDescription
man bash^PROMPTINGDetails on how to set the PS1 prompt.

Essential Z-Shell Manpages

ManpageSearchDescription
man zshmiscPROMPT\ SEQUENCESDetails on how to set the PS1 prompt.
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/exercises/index.html b/pr-preview/pr-346/xx-appendices/exercises/index.html new file mode 100644 index 00000000..4a82efb3 --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/exercises/index.html @@ -0,0 +1,26 @@ + + + + + +Good Scripts to write as exercises | Effective Shell + + + + + + + + + + + + + + +
+

Good Scripts to write as exercises

  • recent - a better version of history, which deduplicates and sorts based on the most commonly used items
  • quickman - a quick link to the most common man pages
  • options - show and interactively toggle options
  • phonetic - take a sequence of letters/numbers and spell it out using NATO phonetic alphabet
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/index-of-commands/index.html b/pr-preview/pr-346/xx-appendices/index-of-commands/index.html new file mode 100644 index 00000000..dff76ba7 --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/index-of-commands/index.html @@ -0,0 +1,26 @@ + + + + + +index-of-commands | Effective Shell + + + + + + + + + + + + + + +
+

index-of-commands

CommandDescription
cd
ls
pwd
mkdir
rm
rmdir
cd
cd
pstree -p $$Show the process tree for the current shell process.
Shell Configuration
shoptSet or unset a shell option.
chshChange the shell for a user.
User Management
useradd -m nameAdd user with the name name. -m creates a home directory name.
passwd nameSet the password for user name.
userdel nameRemove user with the name name.
usermod -aG sudo nameMake name a sudoer.
------------------------------------------------------------------------------------------------------------------------------
Git
git initInitialise a new Git Repository.
git statusShow the status of the working tree and index.
git add <files>Stage files - you can use patterns and wildcards.
git reset <files>Unstage files - you can use patterns and wildcards.
git rm --cached <files>Unstage files - you can use patterns and wildcards.
git commitCreate a commit from the current index - the shell editor will open for the commit message.
git commit -m 'message'Create a commit with message message.
git commit -aStage and commit all changes in the working tree.
git checkout <branch>Checkout a branch called branch.
git checkout -b branchCreate and checkout a new branch called branch.
git branch <name>Create a branch called name but do not check it out.
git branch -m <new_name>Change the current branch name to new_name.
git merge <branch>Merge the branch named branch into the current branch.
git logShow the log of commits.
git log --oneline --branchShow the log of commits, one line per commit, with the branch graph.
git rm <files>Stage the removal of files from the repostiry - you can use patterns and wildcards.
git mv <source> <destination>Stage the movement of source to destination.
git checkout 8342becCheckout a commit with SHA 834bec.
git checkout HEAD~1Move the current HEAD back one commit.
git checkout <branch>~3Checkout branch, move back three commits from the tip.
  • grep
  • mkdir
  • rm
  • rmdir
  • touch
  • cat
  • watch
  • tail
  • head
  • less
  • more
  • most
  • echo
  • timeout
  • until
  • pwd -P (physical, shows symlinks)
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/installing-samples/index.html b/pr-preview/pr-346/xx-appendices/installing-samples/index.html new file mode 100644 index 00000000..5b0ac4cb --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/installing-samples/index.html @@ -0,0 +1,26 @@ + + + + + +Installing the Samples | Effective Shell + + + + + + + + + + + + + + +
+

Installing the Samples

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.

The easiest way to install the samples is to run the following command:

curl effective.sh | sh

This will install the samples to the following location:

~/effective-shell

It will also allow you to overwrite the samples, update or recreate them if you run the command multiple times.

Manually Downloading the Samples​

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:

You can also download them as a tarball from:

Deleting the Samples​

If you have installed the samples to the default location, you can safely delete them with the following command:

rm -rf ~/effective-shell
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/posix/index.html b/pr-preview/pr-346/xx-appendices/posix/index.html new file mode 100644 index 00000000..3fd48896 --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/posix/index.html @@ -0,0 +1,26 @@ + + + + + +Posix | Effective Shell + + + + + + + + + + + + + + +
+
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/recommended-reading/index.html b/pr-preview/pr-346/xx-appendices/recommended-reading/index.html new file mode 100644 index 00000000..353e0915 --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/recommended-reading/index.html @@ -0,0 +1,27 @@ + + + + + +Recommended Reading | Effective Shell + + + + + + + + + + + + + + +
+

Recommended Reading

Work In Progress

This section is being expanded regularly, so check back often to find more recommendations!

Some great books and articles for specific topics are listed below.

The Future of the Shell​

General Shell Skills​

Fantastic Books​

Each of these books would make a great addition to your bookshelf if you are a technologist. Many of these books I have read multiple times and still go back to as a reference.

Book Cover Image: Applied Cryptography

Applied Cryptography: Protocols, Algorithms, and Source Code in C - Bruce Schneier

This is the absolute best book around on cryptography - from concepts, protocols all the way to advanced topics. There are code examples in C that allow you to really see how these concepts work in practice. This is an excellent book for someone who wants to learn about cryptography but also have the option to go deep into the topics that interest them.

Book Cover Image: Pro Git

Pro Git - Scott Chacon and Ben Straub

Superb book on Git, that is essential reading for the new user, but also goes into great depth on topics that will be relevant for expert readers. Should be on everyone's bookshelf.

Shell Scripting by Jason Cannon​

Short and sweet, this is a good book for absolute shell scripting beginners.

Wicked Cool Shell Scripts - Dave Taylor & Brandon Perry​

Ideal for systems administrators and power users who can benefit from automating processes across many platforms. This book contains a lot of tips on how to standardise the behaviour of programs across Linux and Unix systems. If you find yourself regularly shell scripting and want to start to build a library of your own scripts to run across machines, this is a great book to read. It will be particularly useful for anyone who faces challenges on incompatibilities and inconsistencies between programs on different systems.

Practical Vim: Edit Text at the Speed of Thought, Drew Niel​

Absolutely the best book I've read on Vim, perfect for users of all levels. Written by Drew Niel, who is the author of the amazing Vimcasts series.

Joshua Levy - The Art of the Command Line​

This is a wonderful repository, which aims to help you "Master the command line, in one page". This page is full of useful resources and is a superb reference for users from novice all the way to advanced!

Essential Online Resources​

These resources are available online and are particularly useful.

Command Line Interface Guidelines​

An open-source guide to help you write better command-line programs, taking traditional UNIX principles and updating them for the modern day.

This is an excellent online resource that describes the principles, but also practical patterns, that you can use to design CLI programs that interface well to other programs and make sense to human operators. This is great reading if you are building any kind of CLI app.

https://clig.dev/

The Missing Semester of Your CS Education​

The introduction in the site says it better than I could!

Classes teach you all about advanced topics within CS, from operating systems to machine learning, but there's one critical subject that's rarely covered, and is instead left to students to figure out on their own: proficiency with their tools. We’ll teach you how to master the command-line, use a powerful text editor, use fancy features of version control systems, and much more!

https://missing.csail.mit.edu/

Thanks to Lennart R. Wilke for sharing this with me!

Maarten Billemont - Bash Guide​

This is an excellent and very detailed resource on bash. It goes into a lot of detail on how things are implemented and is a great resource to find all of the low level details you might be interested in. There is also a more modern version being currently drafted at https://guide.bash.academy/.

Great Books​

Great Videos​

The UNIX Operating System​

This is a fascinating video from the late 60s - you might be amazed at how much of the stuff you see here is still fundamental to how we work with computers today. The shell, pipelines, the file system and more:

Screenshot: The Unix Operating System

The Mother of All Demos​

Another fascinating video from the late 60s - see the mouse, hypertext, word processing and more:

Screenshot: The Mother of all Demos

Where Grep Came From - Computerphile​

Professor Brian Kerninghan explains where the grep tool came from:

Screenshot: YouTube Where Grep Came From

Advanced​

Research​

The following articles and links were particularly useful when writing this book:

Great source of shell tricks and tips:

TODO​

Books I'm reading as part of the research for this book.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/shell-parameter-expansion/index.html b/pr-preview/pr-346/xx-appendices/shell-parameter-expansion/index.html new file mode 100644 index 00000000..f376b937 --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/shell-parameter-expansion/index.html @@ -0,0 +1,26 @@ + + + + + +shell-parameter-expansion | Effective Shell + + + + + + + + + + + + + + +
+

shell-parameter-expansion

VariableDescription
$$This is the process ID of the script itself.
$0The first parameter to the shell, which is typically the path of the shell itself. If this parameter starts with - then the shell is assumed to be a Login Shell.
$-The flags that were set for the shell, such as i for 'interactive'.
$0The first parameter to the shell, which is typically the path of the shell itself.
$1The first parameter
$2The second parameter
${11}The 11th parameter - if the parameter is more than one digit you must surround it with braces
$#The number of parameters
$@The full set of parameters as an array
$*The full set of parameters as a string separated by the first value in the $IFS variable
${@:start:count}A subset of 'count' parameters starting at parameter number 'start'
$?The status code of the most recently called command.

TODO​

Need to update Chapter 19 to link to this.

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/xx-appendices/the-future/index.html b/pr-preview/pr-346/xx-appendices/the-future/index.html new file mode 100644 index 00000000..27deb128 --- /dev/null +++ b/pr-preview/pr-346/xx-appendices/the-future/index.html @@ -0,0 +1,26 @@ + + + + + +the-future | Effective Shell + + + + + + + + + + + + + + +
+

the-future

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.

Scattered notes so far:

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/zz-developer-guide/components/index.html b/pr-preview/pr-346/zz-developer-guide/components/index.html new file mode 100644 index 00000000..6507dbd9 --- /dev/null +++ b/pr-preview/pr-346/zz-developer-guide/components/index.html @@ -0,0 +1,114 @@ + + + + + +Components | Effective Shell + + + + + + + + + + + + + + +
+

Components

This page shows the custom components that are available for Effective Shell. These are primarily used to improve the reading experience for code samples.

To add more components to this page, please check the src/theme/ReactLiveScope/index.js file, which has been swizzled as per the guide at Docusaurus - Code Blocks - Interactive code editor. Each custom component you want to use must be included in this file.

AsciinemaPlayer​

The AsciinemaPlayer component is renders an asciinema recording. Note that the recording file must be available to be served to the browser, so it will normally live somewhere in the static folder.

First, import the component:

import AsciinemaPlayer from '@site/src/components/AsciinemaPlayer/AsciinemaPlayer.tsc';

Use the component as below:

Result
Loading...
Live Editor

The bulk of the properties that are exposed are directly from the asciinema-player component - this page has a more detailed description of many of the properties listed below. The following properties are exposed:

PropertyUsage
srcThe location of the cast file, must be available from the browser.
styleAny additional CSS styles to apply.
colsThe number of columns in the player's terminal.
rowsThe number of rows in the player's terminal.
autoPlaySet this option to true if playback should start automatically.
preloadSet this option to true if the recording should be preloaded on player's initialization.
loopSet 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.
startAtStart playback at a given time.
speedPlayback speed. The value of 2 means 2x faster.
idleTimeLimitLimit terminal inactivity to a given number of seconds.
themeTerminal color theme.
posterPoster (a preview frame) to display until the playback is started.
fitControls the player's fitting (sizing) behaviour inside its container element.
fontSizeSize of the terminal font.

AnnotatedCommand​

The 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.

First, import the component:

import AnnotatedCommand from '@site/src/components/AnnotatedCommand/AnnotatedCommand.tsc';

Use the component as below:

Result
Loading...
Live Editor

The following properties are exposed:

PropertyUsage
annotationThe text to show beneath the command.

Caret​

The 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.

First, import the component:

import Caret from '@site/src/components/Caret/Caret.tsx';

Use the component as below:

Result
Loading...
Live Editor

The following properties are exposed:

PropertyUsage
caretStyleThe style of the cursor, block by default, or line for an 'insert mode' cursor.

Tips for Developing Components​

Check the following resources for useful tips on Component Development:

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/zz-developer-guide/images-and-diagrams/index.html b/pr-preview/pr-346/zz-developer-guide/images-and-diagrams/index.html new file mode 100644 index 00000000..4a577439 --- /dev/null +++ b/pr-preview/pr-346/zz-developer-guide/images-and-diagrams/index.html @@ -0,0 +1,26 @@ + + + + + +Images and Diagrams | Effective Shell + + + + + + + + + + + + + + +
+

Images and Diagrams

Images​

Not in Use

Ideal Image is not currently enabled as it appears to clash with native Docusaurus lazy-loading.

The @docusaurus/plugin-ideal-image is used to allow lazy loading and zoom of images.

Use the Image tag as shown:

import Image from '@theme/IdealImage';
<Image img={require('./images/vim-cheatsheet.png')} />

This will render an image as shown below:

::: warn Currently Disabled

<Image img={require('./images/vim-cheatsheet.png')} />

:::

Diagrams​

Render a Draw.io diagram like so:

import Drawio from '@theme/Drawio'
import asymmetricEncryption from '!!raw-loader!./images/asymmetric-encryption.drawio';

<Drawio content={asymmetricEncryption} />

This would render as below:

loading...
+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/zz-developer-guide/markdown-guide/index.html b/pr-preview/pr-346/zz-developer-guide/markdown-guide/index.html new file mode 100644 index 00000000..95d03123 --- /dev/null +++ b/pr-preview/pr-346/zz-developer-guide/markdown-guide/index.html @@ -0,0 +1,26 @@ + + + + + +Markdown | Effective Shell + + + + + + + + + + + + + + +
+

Markdown

The contents of this book are primarily written in MDX. Some key concepts to be aware of:

  • MDX is the language that most files in the ./doc folder are written in
  • Commonmark is the standard markdown spec that MDX is based on
  • GitHub Flavored Markdown is also based on Commonmark
  • Markua is used internally for the Leanpub distribution

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.

TODO: links should be explicit e.g. 00-index.md rather than index.md

Emphasis​

TODO: Use single stars for italics and double stars for bold - because Markua confusingly uses single underscores for underline.

Asides / Admonitions / Blurbs​

TODO

Markua Tags​

TODO

frontmatter marks content that is considered part of the introduction or preface. It is numbered in the manuscript with numerals such as i, ii, xii.

mainmatter indicates content that is part of the main book, it is numbered with numerals.

backmatter todo

Titles​

Frontmatter

Images​

TODO image names must be unique

Images​

Not in Use

Ideal Image is not currently enabled as it appears to clash with native Docusaurus lazy-loading.

The @docusaurus/plugin-ideal-image is used to allow lazy loading and zoom of images.

Use the Image tag as shown:

import Image from '@theme/IdealImage';
<Image img={require('./images/vim-cheatsheet.png')} />

This will render an image as shown below:

::: warn Currently Disabled

<Image img={require('./images/vim-cheatsheet.png')} />

:::

Diagrams​

Render a Draw.io diagram like so:

import Drawio from '@theme/Drawio'
import asymmetricEncryption from '!!raw-loader!./images/asymmetric-encryption.drawio';

<Drawio content={asymmetricEncryption} />

This would render as below:

+ + + + \ No newline at end of file diff --git a/pr-preview/pr-346/zz-developer-guide/recording-terminal-sessions/index.html b/pr-preview/pr-346/zz-developer-guide/recording-terminal-sessions/index.html new file mode 100644 index 00000000..d9d4b629 --- /dev/null +++ b/pr-preview/pr-346/zz-developer-guide/recording-terminal-sessions/index.html @@ -0,0 +1,26 @@ + + + + + +Recording Terminal Sessions | Effective Shell + + + + + + + + + + + + + + +
+

Recording Terminal Sessions

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.

Asciinema​

The asciinema tool can record the output of terminal sessions. You can see the results in action in pages like Chapter 33 - Master the Multiplexer.

Some tips for working with asciinema:

To record a Tmux session, you will need to start detached from Tmux and then attach. You can do this by hand, simply using tmux attach, but this adds some noise to the beginning of the recording. A better way is to use the command below:

asciinema rec --command "tmux attach [-t session-name]"

Script Recording​

Record a shell session by running:

# Start recording...
script recording.txt

# ...run your commands...

# Finish the recording.
exit

Once you have this recording, you can use it to rapidly record an asciinema file:

asciinema rec --command "tmux attach [-t session-name] && scriptreplay recording.txt"

It can be helpful to 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 scriptreplay_ng tool.

+ + + + \ No newline at end of file