<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wayne Smallman</title>
    <description>The latest articles on DEV Community by Wayne Smallman (@octaneinteractive).</description>
    <link>https://dev.to/octaneinteractive</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F188274%2Fd38b18a7-0494-459f-9652-01d56211bd4a.jpeg</url>
      <title>DEV Community: Wayne Smallman</title>
      <link>https://dev.to/octaneinteractive</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/octaneinteractive"/>
    <language>en</language>
    <item>
      <title>Saving web pages with Vue and Node via Newspaper3k</title>
      <dc:creator>Wayne Smallman</dc:creator>
      <pubDate>Tue, 01 Sep 2020 12:46:24 +0000</pubDate>
      <link>https://dev.to/octaneinteractive/saving-web-pages-with-vue-and-node-via-newspaper3k-5ahn</link>
      <guid>https://dev.to/octaneinteractive/saving-web-pages-with-vue-and-node-via-newspaper3k-5ahn</guid>
      <description>&lt;p&gt;&lt;strong&gt;While the Under Cloud has an extension for Google Chrome that allows us to save a selection of text from a web page, what’s been lacking is the option to automate the saving of the entire page. Obvious though it is, saving a web page is no trivial task, and it’s something I’ve been 7 parts preparing for, 2 parts avoiding, and 1 part dreading for ages!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yet, here we are — at last — and the Under Cloud now supports the saving of web pages via &lt;a href="https://newspaper.readthedocs.io/en/latest/"&gt;Newspaper3k&lt;/a&gt;, a versatile package written in Python. I am stretching the definition of &lt;em&gt;now&lt;/em&gt;, since I’m still running tests in the staging environment, but it’s almost complete and should be on production within the week.&lt;/p&gt;

&lt;p&gt;The documentation for Newspaper is sparse, and code samples were (are) few and far between. Worse, I had no idea how I would make Python talk to Node — the API is the obvious choice here, but I had no understanding of Python, the types of data it supported, or how I would get that data out of it.&lt;/p&gt;

&lt;p&gt;I’m writing this from the perspective of someone on the other side of the learning curve, having walked the long route to get here, but — given the time constraints I’m up against — would have preferred a path less cluttered with obstacles. So this article is from present me for the attention of past me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives to Newspaper3k
&lt;/h2&gt;

&lt;p&gt;There are powerful services out there, such as &lt;a href="https://www.diffbot.com/"&gt;DiffBot&lt;/a&gt;, but these are cost-prohibitive at this stage in the life of the Under Cloud, and — to be honest, and in spite of what I said a few paragraphs ago — I’d prefer to figure these things out myself first before delegating them, so I at least have a good technical understanding of what’s going on. However, there are some open source alternatives, such as &lt;a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/"&gt;BeautifulSoup&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Newspaper3k versus BeautifulSoup
&lt;/h3&gt;

&lt;p&gt;I imagine some are wondering why I chose Newspaper3k instead of BeautifulSoup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Newspaper appears to be focused on general purpose page scraping;&lt;/li&gt;
&lt;li&gt;while BeautifulSoup — with its wealth of options for parsing the DOM — is geared more towards data science.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You need to know the specific parts of a web page to get the most from BeautifulSoup. I could be wrong, so I look forward to someone stepping in with more information!&lt;/p&gt;

&lt;h2&gt;
  
  
  Scraping a web page with Newspaper3k
&lt;/h2&gt;

&lt;p&gt;I'm going to make a few assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you have an understanding of both Vue and Node;&lt;/li&gt;
&lt;li&gt;and don’t need me to go through the whole process of installing and configuring either;&lt;/li&gt;
&lt;li&gt;or instantiating a new project;&lt;/li&gt;
&lt;li&gt;you have Python installed, along with the Newspaper3k package;&lt;/li&gt;
&lt;li&gt;I’ll be providing concise examples of the code, rather than the complete versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an aside, I don’t like scraping as a description of what we’re doing here, given the horrible connotations attached to it. Please don’t use this article to create nefarious garbage for the purposes of plagiarising the work of others.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;Although the Under Cloud is written in JavaScript (or ECMAScript, as it’s now known), the first thing I had to do was learn some Python to create the script that would act as a bridge between the backend written in Node and Newspaper written in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import sys
import json
from datetime import datetime

from newspaper import Article

# Here, the `url` value should be something like: https://www.bbc.co.uk/sport/football/53944598
url = sys.argv[1]

template_for_exceptions = "An exception of type {0} occurred. Arguments:\n{1!r}"

def get_web_page(url):

  try:

    if url and len(url) &amp;gt; 0:

      article = Article(url, keep_article_html = True)
      article.download()
      article.parse()

      dataForBookmarkAsJSON = json.dumps({
        'publicationDate': article.publish_date if article.publish_date is None else article.publish_date.strftime("%Y-%m-%d %H:%M:%S"),
        'title': article.title,
        'note': article.article_html,
        'authors': article.authors
      })

      try:

        sys.stdout.write(dataForBookmarkAsJSON)
        sys.stdout.flush()
        os._exit(0)

      except Exception as ex:

        message_for_exception = template_for_exceptions.format(type(ex).__name__, ex.args)
        print(message_for_exception)
        sys.exit(1)

  except Exception as ex:

    message_for_exception = template_for_exceptions.format(type(ex).__name__, ex.args)
    print(message_for_exception)
    sys.exit(1)

if __name__ == '__main__':
  get_web_page(url)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A few things to point out here, such as the &lt;code&gt;article.publish_date&lt;/code&gt; variable, which is either a date string that I format, or is a null, that I handle when populating the JSON object. Yes, I could have done that upstream in Node, but I took the moment to learn a few things about and in Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vue
&lt;/h3&gt;

&lt;p&gt;At the frontend, I’m using a component with the following method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getWebPage () {
  this.$axios.get(`/newspaper`, {
    params: {
      // Params.
    }
  }).then(function(response) {
    // Handle the response.
    }
  }).catch(function(error) {
    // Handle the error.
  })
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Node
&lt;/h3&gt;

&lt;p&gt;At the backend, I have the route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;router.get('/newspaper', async (req, res) =&amp;gt; {
  const getNewspaper = await controllerNewspaper.getWebPage(data)
  res.json(getNewspaper)
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;… and in the controller, I have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.getWebPage = async (params) =&amp;gt; {

  let { spawn } = require('child_process')
  let processForPython = spawn(process.env.PYTHON_VERSION, [
    `${process.env.PYTHON_PATH}/get_web_page.py`, 
    params.url
  ], {
    maxBuffer: 10240000
  })

  let dataForBookmarkStream = []

  return new Promise ((resolve, reject) =&amp;gt; {
    processForPython.stdout.on('data', (response) =&amp;gt; {
      dataForBookmarkStream.push(response)
    })

    processForPython.stderr.on('data', (error) =&amp;gt; {
      reject({
        error: `An error occurred while attempting to parse the web page: ${error.toString()}`
      })
    })

    processForPython.on('exit', (code) =&amp;gt; {
      switch (code) {
        case 0:
          if ( dataForBookmarkStream ) {
            if ( dataForBookmarkStream.length &amp;gt; 0 ) {
              try {
                try {
                  dataForBookmark = JSON.parse(dataForBookmarkStream.join().toString())
                } catch (exception) {
                  reject({
                    error: "JSON object supplied by Newspaper is invalid."
                  })
                }
                if (typeof dataForBookmark === 'object') {
                  const paramsForBookmark = new URLSearchParams()
                  paramsForBookmark.append('userID', params.userID)
                  // Additional parameters, using dataForBookmark...
                  instanceOfAxios.post('/assets', paramsForBookmark)
                  .then(function (response) {
                    resolve(response)
                  })
                  .catch(function (error) {
                    reject(error)
                  })
                }
              } catch (exception) {
                reject({
                  error: "An error occurred while attempting to save the web page."
                })
              }
            } else {
              reject()
            }
          } else {
            reject()
          }
          break
        case 1:
          reject({
            error: "Web page couldn't be saved."
          })
          break
      }
    })

  }).catch(error =&amp;gt; {
    return {
      error: "Web page couldn't be saved."
    }
  })

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Yeah, it’s a lot to take in, so let’s look at some specifics…&lt;/p&gt;

&lt;p&gt;First, figure out what the version of Python is and create an equivalent environmental variable to &lt;code&gt;process.env.PYTHON_VERSION&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Second, figure out what the path to Python is and create an equivalent environmental variable to &lt;code&gt;process.env.PYTHON_PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, feel free to tweak &lt;code&gt;maxBuffer&lt;/code&gt; to fit. As an aside, I did attempt a version of the code using &lt;code&gt;maxBuffer&lt;/code&gt; alone, but some web pages were too big, at which point the JSON object failed to parse and then everything went to crap.&lt;/p&gt;

&lt;p&gt;Once the Python script is called, it begins to stream the JSON object to &lt;code&gt;processForPython.stdout.on('data')&lt;/code&gt;, which I’m grabbing in chunks via the &lt;code&gt;dataForBookmarkStream&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Assuming the process was a success, we hit the switch block in &lt;code&gt;processForPython.on('exit')&lt;/code&gt; and exit when the code is 0. Here’s where we convert the encoded data in &lt;code&gt;dataForBookmarkStream&lt;/code&gt; into something useful, using:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dataForBookmark = JSON.parse(dataForBookmarkStream.join().toString())&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;… before sending the data via the API to somewhere else in the application.&lt;/p&gt;

&lt;p&gt;Do we have some Node and Python people shaking their collective heads wearing an avuncular expression with a hint of disappointment? If so, share and let’s learn what could be improved!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Our brains aren't hard drives, and how we remember things and make connections between them is personal — the &lt;a href="https://app.theundercloud.com/"&gt;Under Cloud&lt;/a&gt; is the missing link in the evolution of doing research.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>node</category>
      <category>vue</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Side projects as an employee KPI</title>
      <dc:creator>Wayne Smallman</dc:creator>
      <pubDate>Wed, 05 Aug 2020 12:01:57 +0000</pubDate>
      <link>https://dev.to/octaneinteractive/side-projects-as-an-employee-kpi-f8e</link>
      <guid>https://dev.to/octaneinteractive/side-projects-as-an-employee-kpi-f8e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Working long hours — at the office, a coffee house, or at home — is one of the more obvious indicators that an employee is dedicated to their job, but is it indicative of how productive or how good they are?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's possible the long hours are a sign that someone is struggling to accomplish in 5 hours what could or should have been done in 1 or 2, and if that's the case, the job is encroaching on their personal life, which in turn would have a commensurate negative affect on their work like — and that's when the downward cycle begins. Unchecked, these long hours would have negative consequences for both employer and and employee alike.&lt;/p&gt;

&lt;p&gt;Gmail, Google Maps, Twitter, Slack, and Groupon started started as side projects, as did Under Cloud, the digital research assistant, which I've since spun out as a startup of sorts.&lt;/p&gt;

&lt;p&gt;Side projects should be considered key performance indicator of an employee, something I imagine Google figured out a long time ago, giving rise to their famous 20% time set aside for their employees to focus on side projects. The powers at Google are no fools, and the fruits of that 20% me time become Google products, but that needn't be the case for everyone else.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Should I tell my boss I have a side project?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;… is a question I see asked a lot while on a number of groups for entrepreneurs, developers, and designers to talk, share ideas, and learn from their peers. If it were me, I’d want to know if an employee had a side project, not because I’d feel threatened, but because I’d like to help, where possible. As an employer, I see side projects as evidence of some notable qualities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proactive, forward thinking, and a willingness to look beyond their immediate environment;&lt;/li&gt;
&lt;li&gt;inventiveness, and a mind for both identifying and fixing problems, or seeking out new and novel niches;&lt;/li&gt;
&lt;li&gt;and a demonstration of a commitment to self improvement, and a desire to learn.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are commendable qualities that are beneficial to the employer, too — more so if these side projects are work-related activities. It's feasible an employee could be working on a side project without me knowing — and that's not a problem in and of itself — but it's inconceivable that their out-of-hours dedication to their craft wouldn't be of benefit Octane to some extent, since they'd either be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;learning new or honing existing skills;&lt;/li&gt;
&lt;li&gt;collaborating with others and improving their own project management and communication skills;&lt;/li&gt;
&lt;li&gt;and perhaps learning to make more effective use of their time, and — through trial and error — create a productive work-life balance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm sure there are other benefits, so what'd I not include, and what do you think of side projects?&lt;/p&gt;

&lt;p&gt;Image by &lt;a href="https://pixabay.com/photos/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=731198"&gt;Free-Photos&lt;/a&gt; from &lt;a href="https://pixabay.com/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=731198"&gt;Pixabay&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>discuss</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Share functions between Vue and Node</title>
      <dc:creator>Wayne Smallman</dc:creator>
      <pubDate>Fri, 24 Jul 2020 08:52:31 +0000</pubDate>
      <link>https://dev.to/octaneinteractive/share-functions-between-vue-and-node-am1</link>
      <guid>https://dev.to/octaneinteractive/share-functions-between-vue-and-node-am1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Vue and Node make a powerful and versatile team, but aside from the API — via Express for me — Vue doesn't &lt;em&gt;talk&lt;/em&gt; to Node, or vice versa, and I had to fix that.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So there I was, building the front end, writing the code for a &lt;em&gt;utilities&lt;/em&gt; mixin to store functions to handle common string and number operations. But then I realized that I had also written much the same code in a controller for Node.&lt;/p&gt;

&lt;p&gt;What if I could use the same functions in Node &lt;em&gt;and&lt;/em&gt; Vue?&lt;/p&gt;

&lt;p&gt;So, first I created a file in Node that would contain the &lt;em&gt;core&lt;/em&gt; functions I'd be using in the mixin in Vue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// "utilities.js" in Node.

let utilities = {}

utilities.stripTags = (note, hellip = false) =&amp;gt; {
  let removeHTML = note.replace(/(&amp;lt;([^&amp;gt;]+)&amp;gt;)/ig, "")
  let removeHTMLEntities = removeHTML.replace(/&amp;amp;#{0,1}[a-z0-9]+;/ig, "")
  let removeLineBreaks = removeHTMLEntities.replace(/\r?\n|\r/g, " ")
  let removeNonbreakingSpaces = removeLineBreaks.replace(/&amp;amp;nbsp;/ig, " ")
  let removeEmojies = removeNonbreakingSpaces.replace(/[\u1000-\uFFFF]+/g, " ")
  let string = removeEmojies
  return (hellip) ? string.concat('&amp;amp;hellip;') : string
}

module.exports = utilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;… then, I returned to the mixin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// "utilities.js" in Vue.

'use strict'

// Core modules.
import CoreUtilities from 'path_to/utilities'

export const Utilities = {
  data () {
    return {
      // Reactive variables...
    }
  },
  methods: {
    stripTags (note, hellip = false) {
      // Utilizing a method in core utilities.
      return CoreUtilities.stripTags(note, hellip)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I have one definitive source of functions that both Vue and Node are able to access.&lt;/p&gt;

&lt;p&gt;If anyone knows a more efficient method of replacing HTML, please share!&lt;/p&gt;

&lt;p&gt;Image by &lt;a href="https://pixabay.com/users/Dyanap-1633410/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1039483" rel="noopener noreferrer"&gt;Dyanap&lt;/a&gt; from &lt;a href="https://pixabay.com/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=1039483" rel="noopener noreferrer"&gt;Pixabay&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>node</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Fantastic experts, and where to find them!</title>
      <dc:creator>Wayne Smallman</dc:creator>
      <pubDate>Thu, 23 Jul 2020 14:23:17 +0000</pubDate>
      <link>https://dev.to/octaneinteractive/fantastic-experts-and-where-to-find-them-5d5g</link>
      <guid>https://dev.to/octaneinteractive/fantastic-experts-and-where-to-find-them-5d5g</guid>
      <description>&lt;p&gt;A friend is encouraging me to start a podcast, and the immediate task is to build a list of at least 20 experts in the field of research — in the public or private sector — to discuss their methods, experiences, successes, and failures among other things.&lt;/p&gt;

&lt;p&gt;If you could listen to anyone: who would it be; and what would you ask them?&lt;/p&gt;

&lt;p&gt;As a bit of background, the purpose is to support the product I'm working on, the &lt;a href="https://app.theundercloud.com/"&gt;Under Cloud&lt;/a&gt;, with a range of resources from experts designed for the target audience of student and professional journalists.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>How do you maintain momentum?</title>
      <dc:creator>Wayne Smallman</dc:creator>
      <pubDate>Sun, 19 Jul 2020 09:28:08 +0000</pubDate>
      <link>https://dev.to/octaneinteractive/how-do-you-maintain-momentum-g68</link>
      <guid>https://dev.to/octaneinteractive/how-do-you-maintain-momentum-g68</guid>
      <description>&lt;p&gt;&lt;strong&gt;If planning is a science, then timing is an art, and the more complex a plan is, the more critical time — and timing — becomes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So it's vital I maintain momentum, or risk the slump, the sag, or the flop! Low places that are difficult to climb out from, and are places I hate to be.&lt;/p&gt;

&lt;p&gt;This is a Sunday morning here in the north of England, and I've been flat out, cramming as much work as is feasible before I go for a 9-10k walk at 11am.&lt;/p&gt;

&lt;p&gt;I run multiple threads of things, where each delivers something on short times scales — like improving the UX of a feature, refactoring some code to test a new thing I've learned and so on — but contribute to that greater whole.&lt;/p&gt;

&lt;p&gt;Where the slump, the sag, and the flop come into things is when I have to reach out, ask for help, need authorization, require some essential piece of data or information, and I wait — then wait, and do some more waiting.&lt;/p&gt;

&lt;p&gt;I mitigate against this by switching to some other thread, to keep the momentum going.&lt;/p&gt;

&lt;p&gt;I get it, no-one but me works on Smallman Standard Time!&lt;/p&gt;

&lt;p&gt;So, how do you maintain momentum?&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Useful functions and commands for Bash</title>
      <dc:creator>Wayne Smallman</dc:creator>
      <pubDate>Tue, 07 Jul 2020 12:28:07 +0000</pubDate>
      <link>https://dev.to/octaneinteractive/useful-functions-and-commands-for-bash-2hpf</link>
      <guid>https://dev.to/octaneinteractive/useful-functions-and-commands-for-bash-2hpf</guid>
      <description>&lt;p&gt;I'll be honest, the command line was once a terrifying place. I remember when Mac OS X launched, and there it was, the Terminal — the name encapsulated the fear I had towards it.&lt;/p&gt;

&lt;p&gt;But I persisted, and what was once fear is now a friendship (though not reciprocal).&lt;/p&gt;

&lt;p&gt;Taming the &lt;em&gt;Beast&lt;/em&gt; took a while, for sure, but I'm at a point where I'm able to do useful things in Bash, like create aliases and functions.&lt;/p&gt;

&lt;p&gt;So here are two functions I use &lt;em&gt;a lot&lt;/em&gt;, which I've decided to share in the hope someone finds them useful, too!&lt;/p&gt;

&lt;p&gt;Before taking the plunge, be sure to create a copy of ".bash_profile" first!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nano ~/.bash_profile&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Also, feel free to rename the functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit a file with a native application
&lt;/h2&gt;

&lt;p&gt;I'm no command line hero, so when it comes to editing big files and making multi-line edits, I like to use something native:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsef () {
    # edit file.
    # Usage: wsef file_name
    # Default application.
    APP="BBEdit"
    if [ "$1" ]; then
        open -a $APP $1
    else
        echo "A file name is required."
        return
    fi
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If required, swap out "BBEdit" for something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find a command
&lt;/h2&gt;

&lt;p&gt;The up arrow key is perfect for the first ten previous commands, but then I need something else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsfc () {
    # find command.
    # Usage: wsfc term
    if [ "$1" ]; then
        history | grep -i $1
    else
        echo "A search term is required."
        return
    fi
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also, as an added a bonus:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;export HISTTIMEFORMAT="%d/%m/%y %T "&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;... which adds a timestamp to the search results, or whenever you run the &lt;code&gt;history&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extras
&lt;/h2&gt;

&lt;p&gt;If you're a cat lover like me:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cat -n name_of_file&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;... and you'll see line numbers.&lt;/p&gt;

&lt;p&gt;And for those who've got themselves stuck in vi as I have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;press "i"&lt;/li&gt;
&lt;li&gt;make an edit, if you dare&lt;/li&gt;
&lt;li&gt;press "esc"&lt;/li&gt;
&lt;li&gt;write ":wq"&lt;/li&gt;
&lt;li&gt;then press enter&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Have fun!&lt;/p&gt;

</description>
      <category>bash</category>
      <category>macos</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
