diff --git a/Readme.md b/Readme.md index f944b384f..d0c061dbe 100644 --- a/Readme.md +++ b/Readme.md @@ -1 +1,15 @@ :) + +--- + +## Notes + +H2's support these: + + {: .-two-column} + {: .-three-column} + {: .-left-reference} + +Tables support these: + + {: .-shortcuts} diff --git a/_includes/2017/head.html b/_includes/2017/head.html index 837771124..421133a20 100644 --- a/_includes/2017/head.html +++ b/_includes/2017/head.html @@ -9,6 +9,7 @@ + diff --git a/_sass/2017/base/base.scss b/_sass/2017/base/base.scss new file mode 100644 index 000000000..192375cc8 --- /dev/null +++ b/_sass/2017/base/base.scss @@ -0,0 +1,21 @@ +/* + * Base + */ + +html, body { + background: #fcfcfc; + font-family: $body-font; + font-size: 14px; + line-height: 1.6; + color: $text-color; +} + +body { + padding: 16px; + max-width: $column * 3 + 32px; + margin: 0 auto; +} + +pre, code { + font-family: $monospace-font; +} diff --git a/_sass/2017/components/h2-section.scss b/_sass/2017/components/h2-section.scss new file mode 100644 index 000000000..46d5f5fbb --- /dev/null +++ b/_sass/2017/components/h2-section.scss @@ -0,0 +1,14 @@ +/* + * h2 section + */ + +/* Hide the first h2 heading */ +.h2-section { + & > h2 { + margin-top: 64px; + } + + &:first-child > h2 { + display: none; + } +} diff --git a/_sass/2017/components/h3-section-list.scss b/_sass/2017/components/h3-section-list.scss new file mode 100644 index 000000000..f9452b523 --- /dev/null +++ b/_sass/2017/components/h3-section-list.scss @@ -0,0 +1,116 @@ +/* + * H3 section list: + * The body that is isotoped. + */ + +.h3-section-list { + & { + margin: 0 auto; + } + + // Clearfix + &::after { + content: ''; + display: table; + clear: both; + zoom: 1; + } + + // Each section + & > .h3-section { + float: left; + padding: $gutter / 2; + width: 100%; + } + + @media (min-width: 768px) { + & > .h3-section { + width: 50%; + } + + & > .h3-section.-wide { + width: 100%; + } + + & > .h3-section.-halfwide { + width: 100%; + } + } + + @media (min-width: 960px) { + & > .h3-section { + width: 33.33%; + } + + & > .h3-section.-wide { + width: 66.67%; + } + + & > .h3-section.-halfwide { + width: 50%; + } + } +} + +/* + * Two column (default) + */ + +.h3-section-list, +.h3-section-list.-two-column { + @media (min-width: 768px) { + & > .h3-section { + width: 50%; + } + } +} + +/* + * One column + */ + +.h3-section-list.-one-column { + & > .h3-section { + width: 100%; + } +} + +/* + * Three column + */ + +.h3-section-list.-three-column { + @media (min-width: 768px) { + & > .h3-section { + width: 50%; + } + } + + @media (min-width: 960px) { + & > .h3-section { + width: 33.33%; + } + } +} + +/* + * Three column, left reference + */ + +.h3-section-list.-left-reference { + @media (min-width: 768px) { + & > .h3-section { + width: 50%; + } + } + + @media (min-width: 960px) { + & > .h3-section { + width: 66.67%; + } + + & > .h3-section:first-child { + width: 33.33%; + } + } +} diff --git a/_sass/2017/components/h3-section.scss b/_sass/2017/components/h3-section.scss new file mode 100644 index 000000000..1ec9711dd --- /dev/null +++ b/_sass/2017/components/h3-section.scss @@ -0,0 +1,80 @@ +/* + * H3 section + */ + +.h3-section > .body { + & > pre { + margin: 0; + padding: 16px; + font-size: 12px; + overflow: auto; + background: white; + } + + & { + background: white; + box-shadow: + 0 4px 5px rgba(80, 100, 150, 0.05), + 0 2px 3px rgba(80, 100, 150, 0.1); + } + + & > ul { + margin: 0; + padding: 0; + list-style-type: none; + } + + & > ul > li { + padding: 8px 16px; + padding-left: 32px; + position: relative; + } + + & > ul > li::before { + content: ''; + position: absolute; + display: inline-block; + width: 8px; + height: 8px; + background: #666; + border-radius: 50%; + left: 16px; + top: 16px; + } + + & > ul > li + li { + border-top: solid 1px $line-color; + } + + /* Paragraphs */ + & > p { + padding: 16px; + margin: 0; + } + + /* Description paragraphs */ + & > pre ~ p, + & > ul ~ p, + & > iframe ~ p, + & > table ~ p { + background: $gray-bg; + color: $gray-text; + + /* Links */ + & a, + & a:visited { + color: $text-color; + text-decoration: none; + border-bottom: solid 1px $line-color; + } + + & a:hover { + color: $baseB-400; + } + } + + /* Line divisions */ + & > *:not(:first-child) { + border-top: solid 1px $line-color; + } +} diff --git a/_sass/2017/markdown/headings.scss b/_sass/2017/markdown/headings.scss new file mode 100644 index 000000000..e778e629f --- /dev/null +++ b/_sass/2017/markdown/headings.scss @@ -0,0 +1,57 @@ +/* + * MarkdownBody context + */ + +.main-heading, +.MarkdownBody h1, +.MarkdownBody h2 { + font-weight: 300; + font-family: $body-font; + margin: $gutter / 2; + padding: 0; + margin-bottom: 16px; + padding-bottom: 16px; + border-bottom: solid 1px $line-color; +} + +.main-heading, +.MarkdownBody h1 { + font-size: 3.2em; +} + +.MarkdownBody h2 { + font-size: 2.4em; +} + +.MarkdownBody h3 { + margin: 0; + padding: 0; + margin-bottom: 16px; + font-family: $body-font; + font-size: 1.66em; + font-weight: 300; + color: $baseA-400; +} + +.MarkdownBody { + a, + a:visited { + color: $baseB-400; + text-decoration: none; + } + + a:hover { + text-decoration: underline; + } + + em { + font-style: normal; + color: $gray-text; + } + + iframe { + border: 0; + margin: 0; + width: 100%; + } +} diff --git a/_sass/2017/markdown/table.scss b/_sass/2017/markdown/table.scss new file mode 100644 index 000000000..557b9c052 --- /dev/null +++ b/_sass/2017/markdown/table.scss @@ -0,0 +1,64 @@ +/* + * Table + */ + +.MarkdownBody table { + & { + width: 100%; + } + + & tr + tr { + border-top: solid 1px $line-color; + } + + /* Horizontal lines */ + & tbody + tbody { + border-top: solid 1px $dark-line-color; + } + + & td, + & th { + padding: 8px 16px; + } + + & tr td:last-child { + text-align: right; + } + + & td:first-child > code { + font-size: 0.86em; + color: #35a; + } + + & a, + & a:visited { + color: #35a; + text-decoration: none; + } + + & td:first-child > code ~ em { + font-style: normal; + font-size: 0.86em; + color: $gray-text; + } + + & thead { + display: none; + } +} + +.MarkdownBody table.-shortcuts { + code { + font-size: 1em; + padding: 3px 8px; + background: #fafafa; + box-shadow: + 0 2px 0 rgba(0, 0, 0, 0.2), + inset 0 0 0 1px rgba(0, 0, 0, 0.05); + border-radius: 3px; + margin-right: 2px; + letter-spacing: 0.2em; + font-family: roboto; + color: $text-color; + } +} diff --git a/_sass/2017/style.scss b/_sass/2017/style.scss new file mode 100644 index 000000000..6f4f16648 --- /dev/null +++ b/_sass/2017/style.scss @@ -0,0 +1,22 @@ +$gutter: 32px; +$column: 400px; + +$body-font: roboto, sans-serif; +$monospace-font: menlo, monospace; + +$gray-bg: #fcfcfc; +$gray-text: #678; +$text-color: #333; +$baseA-400: #53a; +$baseB-400: #35a; +$line-color: #f5f5f5; +$dark-line-color: #ccc; + +@import url('https://unpkg.com/sanitize.css@5.0.0/sanitize.css'); + +@import './base/base'; +@import './markdown/headings'; +@import './markdown/table'; +@import './components/h2-section'; +@import './components/h3-section-list'; +@import './components/h3-section'; diff --git a/assets/2017/style.css b/assets/2017/style.css deleted file mode 100644 index bd73e6e8f..000000000 --- a/assets/2017/style.css +++ /dev/null @@ -1,248 +0,0 @@ -@import url('https://unpkg.com/sanitize.css@5.0.0/sanitize.css'); - -:root { - --gutter: 32px; - --column: 400px; - - --body-font: roboto, sans-serif; - --monospace-font: menlo, monospace; - - --gray-bg: #fcfcfc; - --gray-text: #678; - --text-color: #333; - --baseA-400: #53a; - --line-color: #f5f5f5; - --dark-line-color: #ccc; -} - -/* - * Base - */ - -html, body { - background: #fcfcfc; - font-family: var(--body-font); - font-size: 14px; - line-height: 1.6; - color: var(--text-color); -} - -body { - padding: 16px; - max-width: calc(var(--column) * 3 + 32px); - margin: 0 auto; -} - -pre, code { - font-family: var(--monospace-font); -} - -/* - * MarkdownBody context - */ - -.main-heading, -.MarkdownBody h1, -.MarkdownBody h2 { - font-weight: 300; - font-family: var(--body-font); - margin: calc(var(--gutter) / 2); - padding: 0; - margin-bottom: 16px; - padding-bottom: 16px; - border-bottom: solid 1px var(--line-color); -} - -.main-heading, -.MarkdownBody h1 { - font-size: 3.2em; -} - -.MarkdownBody h2 { - font-size: 2.4em; -} - -.MarkdownBody h3 { - margin: 0; - padding: 0; - margin-bottom: 16px; - font-family: var(--body-font); - font-size: 1.66em; - font-weight: 300; - color: var(--baseA-400); -} - -/* - * h2 section - */ - -/* Hide the first h2 heading */ -.h2-section > h2 { - margin-top: 64px; -} - -.h2-section:first-child > h2 { - display: none; -} - -/* - * H3 section list: - * The body that is isotoped. - */ - -.h3-section-list { - margin: 0 auto; -} - -.h3-section-list::after { - content: ''; - display: table; - clear: both; - zoom: 1; -} -.h3-section-list > .h3-section { - float: left; - padding: calc(var(--gutter) / 2); - width: 100%; -} - -@media (min-width: 768px) { - .h3-section-list > .h3-section { - width: 50%; - } - - .h3-section-list > .h3-section.-wide { - width: 100%; - } - - .h3-section-list > .h3-section.-halfwide { - width: 100%; - } -} - -@media (min-width: 960px) { - .h3-section-list > .h3-section { - width: 33.33%; - } - - .h3-section-list > .h3-section.-wide { - width: 66.67%; - } - - .h3-section-list > .h3-section.-halfwide { - width: 50%; - } -} - -/* - * H3 section - */ - -.h3-section > .body > pre { - margin: 0; - padding: 16px; - font-size: 12px; - overflow: auto; - background: white; -} - -.h3-section > .body { - background: white; - box-shadow: 0 4px 5px rgba(80, 100, 150, 0.05), 0 2px 3px rgba(80, 100, 150, 0.1); -} - -.h3-section > .body > ul { - margin: 0; - padding: 0; - list-style-type: none; -} - -.h3-section > .body > ul > li { - padding: 8px 16px; - padding-left: 32px; - position: relative; -} - -.h3-section > .body > ul > li::before { - content: ''; - position: absolute; - display: inline-block; - width: 8px; - height: 8px; - background: #666; - border-radius: 50%; - left: 16px; - top: 16px; -} - -.h3-section > .body > ul > li + li { - border-top: solid 1px var(--line-color); -} - -/* Description paragraphs */ -.h3-section > .body > pre ~ p, -.h3-section > .body > ul ~ p, -.h3-section > .body > table ~ p { - padding: 16px; - margin: 0; - background: var(--gray-bg); - color: var(--gray-text); -} - -.h3-section > .body > p > a, -.h3-section > .body > p > a:visited { - color: var(--text-color); - text-decoration: none; - border-bottom: solid 1px var(--line-color); -} - -.h3-section > .body > *:not(:first-child) { - border-top: solid 1px var(--line-color); -} - -/* - * Table - */ - -table { - width: 100%; -} - -table tr + tr { - border-top: solid 1px var(--line-color); -} - -/* Horizontal lines */ -table tbody + tbody { - border-top: solid 1px var(--dark-line-color); -} - -table td, -table th { - padding: 8px 16px; -} - -table tr td:last-child { - text-align: right; -} - -table code { - font-size: 0.86em; - color: #35a; -} - -table a, -table a:visited { - color: #35a; - text-decoration: none; -} - -table code ~ em { - font-style: normal; - font-size: 0.86em; - color: var(--gray-text); -} - -table thead { - display: none; -} diff --git a/assets/2017/style.scss b/assets/2017/style.scss new file mode 100644 index 000000000..45cd10867 --- /dev/null +++ b/assets/2017/style.scss @@ -0,0 +1,3 @@ +--- +--- +@import '2017/style.scss'; diff --git a/react.md b/react.md index f5e8443d2..d75cb3c70 100644 --- a/react.md +++ b/react.md @@ -6,14 +6,41 @@ layout: 2017/sheet {%raw%} -Getting started ---------------- +Example +------- +{: .-left-reference} -{:.-three-column} +### Basic example - +```jsx +class Hello extends React.Component { + render () { + return
+ Hello {this.props.name} +
+ } +} +``` -### Getting started +```jsx +const el = document.body +ReactDOM.render(, el) +``` + +Use the [React.js jsfiddle](http://jsfiddle.net/reactjs/69z2wepo/) to start hacking. (or the unofficial [jsbin](http://jsbin.com/yafixat/edit?js,output)). + +### Try it + + + +[Open in jsbin](http://jsbin.com/yafixat/edit?js,output) +{: target="_blank"} + +Components +---------- +{: .-three-column} + +### Class components ```jsx class MyComponent extends React.Component { @@ -30,8 +57,6 @@ const el = document.body ReactDOM.render(, el) ``` -Use the [React.js jsfiddle](http://jsfiddle.net/reactjs/69z2wepo/) to start hacking. (or the unofficial [jsbin](http://jsbin.com/yafixat/edit?js,output)). - ### Functional components ```jsx @@ -66,34 +91,19 @@ Nest components to separate concerns. See: [multiple components](http://facebook ```jsx this.forceUpdate() -this.isMounted() +``` +```jsx this.setState({ ... }) this.replaceState({ ... }) +``` +```jsx this.state this.props ``` -These are methods available for `Component` instances. See [Component API](http://facebook.github.io/react/docs/component-api.html). - - -### Component specs - -| Method | What | -| ---- | ---- | -| [`render()`](http://facebook.github.io/react/docs/component-specs.html#render) | | -| ---- | ---- | -| [`getInitialState()`](http://facebook.github.io/react/docs/component-specs.html#getinitialstate) | | -| [`getDefaultProps()`](http://facebook.github.io/react/docs/component-specs.html#getdefaultprops) | | -| ---- | ---- | -| [`mixins: [ ... ]`](http://facebook.github.io/react/docs/component-specs.html#mixins) | Mixins ... [more](#mixins) | -| [`propTypes: { ... }`](http://facebook.github.io/react/docs/component-specs.html#proptypes) | Validation ... [more](#property-validation) | -| [`statics: { ... }`](http://facebook.github.io/react/docs/component-specs.html#statics) | Static methods | -| [`displayName: "..."`](http://facebook.github.io/react/docs/component-specs.html#displayname) | Automatically filled by JSX | -{:.greycode.no-head} - -Methods and properties you can override. See [component specs](http://facebook.github.io/react/docs/component-specs.html). +These methods and properies are available for `Component` instances. See [Component API](http://facebook.github.io/react/docs/component-api.html). ### Properties @@ -116,7 +126,6 @@ Use [props](https://facebook.github.io/react/docs/tutorial.html#using-props) (`t ```jsx this.setState({ username: 'rstacruz' }) -this.replaceState({ ... }) ``` ```jsx @@ -126,18 +135,19 @@ render () { } ``` -Use [states](https://facebook.github.io/react/docs/tutorial.html#reactive-state) (`this.state`) to manage dynamic data. +Use states (`this.state`) to manage dynamic data. +See [States](https://facebook.github.io/react/docs/tutorial.html#reactive-state). ### Setting default props ```jsx -class Hello extends React.Component { - constructor (props) { - super({ shown: true, ...props }) - } +Hello.defaultProps = { + color: 'blue' } ``` +See [defaultProps](https://facebook.github.io/react/docs/react-component.html#defaultprops). + ### Setting default state ```jsx @@ -151,32 +161,32 @@ class Hello extends React.Component { Lifecycle --------- - -{:.-two-column} +{: .-two-column} ### Mounting -| `componentWillMount()` | Before rendering (no DOM yet) | -| `componentDidMount()` | After rendering | -| `componentWillUnmount()` | Invoked before DOM removal | +| `constructor` _(props)_ | Before rendering [#](https://facebook.github.io/react/docs/react-component.html#constructor) | +| `componentWillMount()` | _Don't use this_ [#](https://facebook.github.io/react/docs/react-component.html#componentwillmount) | +| `render()` | Render [#](https://facebook.github.io/react/docs/react-component.html#render) | +| `componentDidMount()` | After rendering (DOM available) [#](https://facebook.github.io/react/docs/react-component.html#componentdidmount) | +| `componentWillUnmount()` | Before DOM removal [#](https://facebook.github.io/react/docs/react-component.html#componentwillunmount) | -Before initial rendering occurs. Add your DOM stuff on didMount (events, timers, etc). See [reference](http://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount). - -Clear your DOM stuff in componentWillMount (probably done on didMount). See [reference](http://facebook.github.io/react/docs/component-specs.html#unmounting-componentwillunmount). +Set initial the state on `constructor()`. +Add DOM event handlers, timers (etc) on `componentDidMount()`, then remove them on `componentWillUnmount()`. ### Updating -| `componentWillReceiveProps`*(newProps={})* | Use `setState()` here | -| `shouldComponentUpdate`*(newProps={}, newState={})* | Skips `render()` if returns false | -| `componentWillUpdate`*(newProps={}, newState={})* | Can't use `setState()` here | -| `componentDidUpdate`*(prevProps={}, prevState={})* | Operate on the DOM here | +| `componentWillReceiveProps` *(newProps)* | Use `setState()` here | +| `shouldComponentUpdate` *(newProps, newState)* | Skips `render()` if returns false | +| `componentWillUpdate` *(newProps, newState)* | Can't use `setState()` here | +| `render()` | Render | +| `componentDidUpdate` *(prevProps, prevState)* | Operate on the DOM here | Called when parents change properties and `.setState()`. These are not called for initial renders. See [reference](http://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops). DOM nodes --------- - -{:.-two-column} +{: .-two-column} ### References @@ -195,7 +205,7 @@ this.input.focus() this.input.value() ``` -Allows access to DOM nodes. See [References](http://facebook.github.io/react/docs/more-about-refs.html). +Allows access to DOM nodes. See [Refs and the DOM](https://facebook.github.io/react/docs/refs-and-the-dom.html). ### DOM Events @@ -204,7 +214,6 @@ Allows access to DOM nodes. See [References](http://facebook.github.io/react/doc value={this.state.value} onChange={this.handleChange} /> ``` -{:.light} ```jsx handleChange: function(event) { @@ -216,6 +225,7 @@ Add attributes like `onChange`. See [events](https://facebook.github.io/react/do Property validation ------------------- +{: .-three-column} ### React.PropTypes @@ -258,60 +268,51 @@ MyComponent.propTypes = { } ``` -See [propTypes](http://facebook.github.io/react/docs/reusable-components.html#prop-validation). - ### Required types ```jsx -MyComponent.propTypes = { - requiredFunc: React.PropTypes.func.isRequired, - requiredAny: React.PropTypes.any.isRequired +MyCo.propTypes = { + name: React.PropTypes.string.isRequired } ``` -Add `.isRequired`. - -### React elements +### Elements ```jsx -MyComponent.propTypes = { +MyCo.propTypes = { // React element - element: React.PropTypes.element, + element: React.PropTypes.element, // num, string, element, or an array of those - node: React.PropTypes.node + node: React.PropTypes.node } ``` -Use `.element`, `.node`. - -### Enumerables +### Enumerables (oneOf) +```jsx +MyCo.propTypes = { + direction: React.PropTypes.oneOf([ + 'left', 'right' + ]) +} ``` -propTypes: { - enum: React.PropTypes.oneOf(['M','F']), // enum - union: React.PropTypes.oneOfType([ // any - React.PropTypes.string, - React.PropTypes.number ]), -``` - -Use `.oneOf`, `.oneOfType`. ### Arrays and objects ```jsx -propTypes: { +MyCo.propTypes = { array: React.PropTypes.array, arrayOf: React.PropTypes.arrayOf(React.PropTypes.number), object: React.PropTypes.object, objectOf: React.PropTypes.objectOf(React.PropTypes.number), - message: React.PropTypes.instanceOf(Message), object2: React.PropTypes.shape({ color: React.PropTypes.string, size: React.PropTypes.number - }), + }) +} ``` Use `.array[Of]`, `.object[Of]`, `.instanceOf`, `.shape`. @@ -319,20 +320,18 @@ Use `.array[Of]`, `.object[Of]`, `.instanceOf`, `.shape`. ### Custom validation ```jsx -propTypes: { - customProp: function(props, propName, componentName) { - if (!/matchme/.test(props[propName])) { - return new Error('Validation failed!'); +MyCo.propTypes = { + customProp: (props, key, componentName) => { + if (!/matchme/.test(props[key])) { + return new Error('Validation failed!') } } } ``` -Supply your own function. - ## Other features -### Propagating properties +### Transfering props ```html @@ -347,32 +346,34 @@ class VideoPlayer extends React.Component { ``` Propagates `src="..."` down to the sub-component. - See [Transferring props](http://facebook.github.io/react/docs/transferring-props.html). ### Top-level API ```jsx React.createClass({ ... }) - React.isValidElement(c) +``` -ReactDOM.findDOMNode(c) // 0.14+ -ReactDOM.render(, domnode, [callback]) // 0.14+ -ReactDOM.unmountComponentAtNode(domnode) // 0.14+ +```jsx +ReactDOM.render(, domnode, [callback]) +ReactDOM.unmountComponentAtNode(domnode) +``` -ReactDOMServer.renderToString() // 0.14+ -ReactDOMServer.renderToStaticMarkup() // 0.14+ +```jsx +ReactDOMServer.renderToString() +ReactDOMServer.renderToStaticMarkup() ``` JSX patterns ------------ +{: .-two-column} ### Style shorthand ```jsx -var style = { backgroundImage: 'url(x.jpg)', height: 10 }; -return
; +var style = { height: 10 } +return
``` See [inline styles](https://facebook.github.io/react/tips/inline-styles.html). @@ -389,23 +390,37 @@ See [dangerously set innerHTML](https://facebook.github.io/react/tips/dangerousl ### Lists ```jsx -var TodoList = React.createClass({ - render: function() { - function item(itemText) { - return
  • {itemText}
  • ; - }; - return
      {this.props.items.map(item)}
    ; +class TodoList extends React.Component { + render () { + const { items } = this.props + + return
      + {items.map(item => + )} +
    } -}); +} ``` -See also +Always supply a `key` property. + +### Conditionals + +```jsx +
    +{showPopup + ? + : null} +
    +``` + +Also see -------- +{: .-one-column} -{:.-two-column} +This reference was made for React v15. -### Also see - -* [Animations](http://facebook.github.io/react/docs/animation.html) +* [React website](http://facebook.github.io/react) _(facebook.github.io)_ +* [Animations](http://facebook.github.io/react/docs/animation.html) _(facebook.github.io)_ {%endraw%} diff --git a/sh.md b/sh.md index 011c71e30..ad1d58514 100644 --- a/sh.md +++ b/sh.md @@ -1,261 +1,572 @@ --- title: Bash scripting category: CLI +layout: 2017/sheet --- +Getting started +--------------- +{: .-three-column} + +### Example + +```bash +#!/usr/bin/env bash + +NAME="John" +echo "Hello $NAME!" +``` + +### Variables + +```bash +NAME="John" +echo $NAME +echo "$NAME" +echo "${NAME}!" +``` + +### String quotes + +```bash +NAME="John" +echo "Hi $NAME" #=> Hi John +echo 'Hi $NAME' #=> Hi $NAME +``` + +### Shell execution + +```bash +echo "I'm in $(pwd)" +echo "I'm in `pwd`" +# Same +``` + +See [Command substitution](http://wiki.bash-hackers.org/syntax/expansion/cmdsubst) + +### Conditional execution + +```bash +git commit && git push +git commit || echo "Commit failed" +``` + +### Functions +{: id='functions-example'} + +```bash +get_name() { + echo "John" +} + +echo "You are $(get_name)" +``` + +See: [Functions](#functions) + +### Conditionals +{: id='conditionals-example'} + +```bash +if [ -z "$string" ]; then + echo "String is empty" +elsif [ -n "$string" ]; then + echo "String is not empty" +fi +``` + +See: [Conditionals](#conditionals) + +### Strict mode + +```bash +set -euo pipefail +IFS=$'\n\t' +``` + +See: [Unofficial bash strict mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/) + +### Brace expansion + +```bash +echo {A,B}.js +``` + +| `{A,B}` | Same as `A B` | +| `{A,B}.js` | Same as `A.js B.js` | +| `{1..5}` | Same as `1 2 3 4 5` | + +See: [Brace expansion](http://wiki.bash-hackers.org/syntax/expansion/brace) + + +Parameter expansions +-------------------- +{: .-three-column} + +### Basics + +```bash +name="John" +echo ${name} +echo ${name/J/j} #=> "john" (substitution) +echo ${name:0:2} #=> "jo" (slicing) +echo ${food:-Cake} #=> $food or "Cake" +``` + +```bash +length=2 +echo ${name:0:length} #=> "jo" +``` + +See: [Parameter expansion](http://wiki.bash-hackers.org/syntax/pe) + ### Pattern substitution - STR=/path/to/foo.c +```bash +STR="/path/to/foo.cpp" +``` - echo ${STR%.c} #=> "/path/to/foo" - echo ${STR%.c}.o #=> "/path/to/foo.o" - echo ${STR##*.} #=> "c" (extension) +| `${STR%.cpp}` | `/path/to/foo` | +| `${STR%.cpp}.o` | `/path/to/foo.o` | +| `${STR##*.}` | `cpp` _(extension)_ | +| `${STR##*/}` | `foo.cpp` _(basepath)_ | - BASE=${STR##*/} #=> "foo.c" (basepath) - DIR=${SRC%$BASE} #=> "/path/to" +### Directory name -### Substitutions by regex +```bash +SRC="/path/to/foo.cpp" +BASE=${STR##*/} #=> "foo.cpp" (basepath) +DIR=${SRC%$BASE} #=> "/path/to" (dirpath) +``` - echo ${STR/hi/hello} # Replace first match - echo ${STR//hi/hello} # Replace all matches - echo ${STR/#hi/hello} # ^hi - echo ${STR/%hi/hello} # hi$ +### Regexp-like substitution - echo "${STR:0:3}" # .substr(0, 3) -- position, length - echo "${STR:-3:3}" # Negative position = from the right +```bash +STR="Hey world" +``` - echo ${#line} # Length of $line +| `${STR/Hey/Hello}` | Replace first match | +| `${STR//Hey/Hello}` | Replace all | +| `${STR/#Hey/Hello}` | Like `^Hey` | +| `${STR/%world/mundo}` | Like `world$` | - [ -z "$CC" ] && CC=gcc # CC ||= "gcc" assignment - ${CC:=gcc} # $CC || "gcc" - ${CC:-gcc} # same as above +### Substrings -### Reading input +| `${STR:0:3}` | Substring: `"Hey"` _(position, length)_ | +| `${STR:-3:3}` | Substring from the right | - echo -n "Proceed? [y/n]: " - read ans - echo $ans +```bash +STR="Hello world" +echo ${STR:6:5} # "world" +echo ${STR:-5:5} # "world" +``` - read -n 1 ans # Just one character +### Length -Loops ------ +| `${#STR}` | Length of `$STR` | -### Basic for loop +### Default values - for i in /etc/rc.*; do - echo $i - done - -### Ranges - - for i in {1..5}; do - echo "Welcome $i" - done - -### Reading lines - - cat file.txt | while read line; do - echo $line - done - -Functions ---------- - -### Defining functions - - myfunc() { ... } - fuction myfunc { ... } - fuction myfunc() { ... } - -### Returning strings - - myfunc() { - local myresult='some value' - echo $myresult - } - - result=$(myfunc) - -### Errors - - myfunc() { return 1; } - -### Arguments - - $# # Number of arguments - $* # All args - $1 # First argument - -Ifs - files ------------ - - # File conditions - if [ -a FILE ]; then # -e exists -d directory -f file - fi # -r readable -w writeable -x executable - # -h symlink -s size > 0 - - # File comparisons - if [ FILE1 -nt FILE2 ] # -nt 1 more recent than 2 - # -ot 2 more recent than 1 - # -ef same files - -Ifs ---- - - # String - if [ -z STRING ] # empty? - if [ -n STRING ] # not empty? - - # Numeric - if [ $? -eq 0 ] # -eq -ne -lt -le -gt -ge - # $? is exit status by the way - - # Etc - if [ -o noclobber ] # if OPTIONNAME is enabled - if [ ! EXPR ] # not - if [ ONE -a TWO ] # and - if [ ONE -o TWO ] # or - - # Regex - if [[ "A" =~ "." ]] - -### Numeric comparisons - - if (( $a < $b )) +| `${CC:-gcc}` | `$CC || "gcc"` | ### Unset variables -Assume `$FOO` is not set. Doing *this* will result in *that*: +Assuming `$FOO` is not set: - ${FOO:-word} # Returns word - ${FOO:+word} # Returns empty, or word if set - ${FOO:=word} # Sets parameter to word, returns word - ${FOO:?message} # Echoes message and exits +| `${FOO:-word}` | Returns `word` | +| `${FOO:+word}` | Returns empty (or `word` if set) | +| `${FOO:=word}` | Sets `$FOO` to `word`, returns `word` | +| `${FOO:?message}` | Echoes message and exits | - ${FOO=word} # : is optional in all of the above +The `:` is optional (eg, `${FOO=word}` works) + +Loops +----- +{: .-three-column} + +### Basic for loop + +```bash +for i in /etc/rc.*; do + echo $i +done +``` + +### Ranges + +```bash +for i in {1..5}; do + echo "Welcome $i" +done +``` + +### Reading lines + +```bash +cat file.txt | while read line; do + echo $line +done +``` + +### Forever + +```bash +while true; do + ··· +done +``` + +Functions +--------- +{: .-three-column} + +### Defining functions + +```bash +myfunc() { + echo "hello $1" +} +``` + +```bash +# Same as above (alternate syntax) +function myfunc() { + echo "hello $1" +} +``` + +```bash +myfunc "John" +``` + +### Returning values + +```bash +myfunc() { + local myresult='some value' + echo $myresult +} +``` + +```bash +result=$(myfunc) +``` + +### Raising errors + +```bash +myfunc() { + return 1 +} +``` + +```bash +if myfunc; then + echo "success" +else + echo "failure" +fi +``` + +### Arguments + +| Expression | Description | +| --- | --- | +| `$#` | Number of arguments | +| `$*` | All arguments | +| `$@` | All arguments, starting from first | +| `$1` | First argument | + +See [Special parameters](http://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables). + +Conditionals +------------ +{: .-three-column} + +### Conditions + +| Condition | Description | +| --- | --- | +| `[ -z STRING ]` | Empty string | +| `[ -n STRING ]` | Not empty string | +| --- | --- | +| `[ NUM -eq NUM ]` | Equal | +| `[ NUM -ne NUM ]` | Not equal | +| `[ NUM -lt NUM ]` | Less than | +| `[ NUM -le NUM ]` | Less than or equal | +| `[ NUM -gt NUM ]` | Greater than | +| `[ NUM -ge NUM ]` | Greater than or equal | +| --- | --- | +| `[[ STRING =~ STRING ]]` | Regexp | +| --- | --- | +| `(( NUM < NUM ))` | Numeric conditions | + +| Condition | Description | +| --- | --- | +| `[ -o noclobber ]` | If OPTIONNAME is enabled | +| --- | --- | +| `[ ! EXPR ]` | Not | +| `[ X ] && [ Y ]` | And | +| `[ X ] || [ Y ]` | Or | + +### File conditions + +| Condition | Description | +| --- | --- | +| `[ -e FILE ]` | Exists | +| `[ -r FILE ]` | Readable | +| `[ -h FILE ]` | Symlink | +| `[ -d FILE ]` | Directory | +| `[ -w FILE ]` | Writable | +| `[ -s FILE ]` | Size is > 0 bytes | +| `[ -f FILE ]` | File | +| `[ -x FILE ]` | Executable | +| --- | --- | +| `[ FILE1 -nt FILE2 ]` | 1 is more recent than 2 | +| `[ FILE1 -ot FILE2 ]` | 2 is more recent than 1 | +| `[ FILE1 -ef FILE2 ]` | Same files | + +### Example + +```bash +# String +if [ -z "$string" ]; then + echo "String is empty" +elsif [ -n "$string" ]; then + echo "String is not empty" +fi +``` + +```bash +# Combinations +if [ X ] && [ Y ]; then + ... +fi +``` + +```bash +# Regex +if [[ "A" =~ "." ]] +``` + +```bash +if (( $a < $b )) +``` + +```bash +if [ -e "file.txt" ]; then + echo "file exists" +fi +``` Numeric calculations -------------------- - $((RANDOM%=200)) # Random number 0..200 - $((a + 200)) # $ is optional +```bash +$((RANDOM%=200)) # Random number 0..200 +$((a + 200)) # $ is optional +``` Arrays ------ - # Declaring using declare -a - declare -a Fruits=('Apple' 'Banana' 'Orange') +### Defining arrays - Fruits[0]="Apple" - Fruits[1]="Banana" - Fruits[2]="Orange" +```bash +Fruits=('Apple' 'Banana' 'Orange') +``` - echo ${Fruits[0]} # Element #0 - echo ${Fruits[@]} # All elements, space-separated - echo ${#Fruits[@]} # Number of elements - echo ${#Fruits} # String length of the 1st element - echo ${#Fruits[3]} # String length of the Nth element - echo ${Fruits[@]:3:2} # Range (from position 3, length 2) +```bash +Fruits[0]="Apple" +Fruits[1]="Banana" +Fruits[2]="Orange" +``` + +### Working with arrays + +```bash +echo ${Fruits[0]} # Element #0 +echo ${Fruits[@]} # All elements, space-separated +echo ${#Fruits[@]} # Number of elements +echo ${#Fruits} # String length of the 1st element +echo ${#Fruits[3]} # String length of the Nth element +echo ${Fruits[@]:3:2} # Range (from position 3, length 2) +``` ### Operations - Fruits=("${Fruits[@]}" "Watermelon") # Push - Fruits=( ${Fruits[@]/Ap*/} ) # Remove by regex match - unset Fruits[2] # Remove one item - Fruits=("${Fruits[@]}") # Duplicate - Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate - lines=(`cat "logfile"`) # Read from file +```bash +Fruits=("${Fruits[@]}" "Watermelon") # Push +Fruits=( ${Fruits[@]/Ap*/} ) # Remove by regex match +unset Fruits[2] # Remove one item +Fruits=("${Fruits[@]}") # Duplicate +Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate +lines=(`cat "logfile"`) # Read from file +``` ### Iteration - for i in "${arrayName[@]}"; do - echo $i - done +```bash +for i in "${arrayName[@]}"; do + echo $i +done +``` -Misc crap ---------- - - command -V cd #=> "cd is a function/alias/whatever" +Options +------- ### Options - set -o noclobber # Avoid overlay files (echo "hi" > foo) - set -o errexit # Used to exit upon error, avoiding cascading errors - set -o pipefail # Unveils hidden failures - set -o nounset # Exposes unset variables +```bash +set -o noclobber # Avoid overlay files (echo "hi" > foo) +set -o errexit # Used to exit upon error, avoiding cascading errors +set -o pipefail # Unveils hidden failures +set -o nounset # Exposes unset variables +``` ### Glob options - set -o nullglob # Non-matching globs are removed ('*.foo' => '') - set -o failglob # Non-matching globs throw errors - set -o nocaseglob # Case insensitive globs - set -o globdots # Wildcards match dotfiles ("*.sh" => ".foo.sh") - set -o globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb') +```bash +set -o nullglob # Non-matching globs are removed ('*.foo' => '') +set -o failglob # Non-matching globs throw errors +set -o nocaseglob # Case insensitive globs +set -o globdots # Wildcards match dotfiles ("*.sh" => ".foo.sh") +set -o globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb') +``` -set GLOBIGNORE as a colon-separated list of patterns to be removed from glob +Set `GLOBIGNORE` as a colon-separated list of patterns to be removed from glob matches. +Miscellaneous +------------- + +### Subshells + +```bash +(cd somedir; echo "I'm now in $PWD") +pwd # still in first directory +``` + +### Redirection + +```bash +python hello.py > output.txt # stdout to (file) +python hello.py >> output.txt # stdout to (file), append +python hello.py 2> error.log # stderr to (file) +python hello.py 2>&1 # stdout to stderr +python hello.py 2>/dev/null # stderr to (null) +``` + +```bash +python hello.py < foo.txt +``` + +### Inspecting commands + +```bash +command -V cd +#=> "cd is a function/alias/whatever" +``` + ### Trap errors - trap 'echo Error at about $LINENO' ERR +```bash +trap 'echo Error at about $LINENO' ERR +``` or - traperr() { - echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}" - } +```bash +traperr() { + echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}" +} - set -o errtrace - trap traperr ERR +set -o errtrace +trap traperr ERR +``` ### Case/switch - case $1 in - start | up) - vagrant up - ;; +```bash +case "$1" in + start | up) + vagrant up + ;; - *) - echo "Usage: $0 {start|stop|ssh}" - ;; - esac + *) + echo "Usage: $0 {start|stop|ssh}" + ;; +esac +``` ### Source relative - source "${0%/*}/../share/foo.sh" +```bash +source "${0%/*}/../share/foo.sh" +``` ### printf - printf "Hello %s, I'm %s" Sven Olga +```bash +printf "Hello %s, I'm %s" Sven Olga +#=> "Hello Sven, I'm Olga +``` ### Directory of script - DIR="${0%/*}" +```bash +DIR="${0%/*}" +``` ### Getting options - while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in - -V | --version ) - echo $version - exit - ;; - -s | --string ) - shift; string=$1 - ;; - -f | --flag ) - flag=1 - ;; - esac; shift; done - if [[ "$1" == '--' ]]; then shift; fi +```bash +while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in + -V | --version ) + echo $version + exit + ;; + -s | --string ) + shift; string=$1 + ;; + -f | --flag ) + flag=1 + ;; +esac; shift; done +if [[ "$1" == '--' ]]; then shift; fi +``` ### Heredoc - cat <