Today I Learned

A Hashrocket project

Proc Composition Operator in Ruby #functionalruby

Function composition is really nice, and having a clean way to do it is important. Ruby 2.6 introduced a really neat way of handling it with procs:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self << mult_self
add_and_mult[3] # 18

If you want the application to be reversed:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self >> mult_self
add_and_mult[3] # 36

This can be made into a way of building up pipelines for single argument procs by declaring them in an Array and using #inject with an identity proc:

calculations = [
->x { x / 3 },
->x { x + 5 },
->x { x * 2 }
]
pipeline = calculations.inject(->x {x}) do |built, func|
build << func
end
pipeline[3] # 8

This can be super cool for things like ActiveRecord objects where you want to chain certain operations without necessarily making them explicit methods via scopes or similar.

Print stacktrace in Elixir #ElixirLang

When you are trying to debug something it is usually helpful to see the stacktrace of what called a function.

If you’re inside of a try expression, and within the catch or rescue block, that’s easy - use the __STACKTRACE__ Special Form.

try do
    # NOPE
rescue
  ArgumentError ->
    IO.puts("Stacktrace #{inspect(__STACKTRACE__)}")
catch
  value ->
    IO.puts("Stacktrace #{inspect(__STACKTRACE__)}")
else
  # NOPE
after
  # NOPE
end

For all other cases the incantation is a little more complicated (and if you don’t think you can remember it, perhaps set up a snippet):

self()
|> Process.info(:current_stacktrace)
|> IO.inspect(label: "---------> stacktrace")

Change the CodeLens annotation color in CoC

CoC is the fanciest Language Server Protocol client for (neo)vim. It even implements Code Lens annotations, like reference counts in Javascript:

code lens reference counts

Ok neat. But by default, virtual text in neovim is in the normal highlight group. That means it looks just like code, and I’m already confused enough. After much source diving, I found that the highlight group used by CoC for this is CocCodeLens, and used like so:

:hi CocCodeLens guifg=White

code lens reference counts with better color

My code is much more readable.

Smooth animations with Math.sin()

Remember geometry class? Well, I don’t.

But I do know that when you’re making a synthesizer, a sine wave produces a nice smooth sound.

And as it turns out, a sine wave has a smooth curve. So, you can use Math.sin() to animate smooth motions like so:

const domNode = document.querySelector('#waveEmoji')
function animate() {
  const a = Math.sin(Date.now() / speed) * range + offset;
  domNode.style = `transform: rotate(${a}deg);`;
  requestAnimationFrame(animate);
}
animate();

Edit Sine Wave

Tmux Shortcuts I use

Create a new window

<tmux-leader>c

Think ‘c’ as in create. Rename window

<tmux-leader>,

That’s a comma. I don’t have anything clever for this one. Create a new pane

<tmux-leader>%

Maybe you’re clever enough to come up with something for this? I just memorized it. Kill a window ( like with a hung terminal )

<tmux-leader>&

You can also create a new session without calling new-session in full, you just use

tmux new -s <session-name>

Which is a bit quicker to type. Unless you like to type, you know, you do you.

Variable arguments and map in JS can hurt

Given some array of numbers as strings:

const someNumStrings = ['1', '2', '05', '68'];

If you want them to be numbers, then you might be tempted to do something like:

someNumStrings.map(parseInt)

Which would be fine if parseInt didn’t allow multiple arguments, and if map only sent in the single element. But that’s not how it works, so what you end up getting is a bit of a mess.

[1, NaN, 0, NaN]

The parseInt function takes a radix as the second argument (and realistically anything else you want to pass to it won’t cause it to explode). The Array.map method takes a callback (in this case parseInt) and gives that little sucker all the data you could want! In this case, the index is being passed as the radix, and parseInt doesn’t care about the others.

TL;DR: map(el => parseInt(el)) >>>>>>>>>> map(parseInt) and if you ever intentionally encode the radix as the index of the element you’re parsing… may god have mercy on your soul.

Vim Mark That Test! 🔖

This week Chris Erin taught me a technique that has changed my testing game: Vim mark your current Ruby test with mA.

When writing TDD-style code in a test-crazy language like Ruby, I tend to follow a predictable workflow: create a test, watch it fail, bounce around the code making changes, and run/edit the test again. This loop can repeat for hours on a complex feature.

There are many ways you could navigate back to your test from elsewhere; a very efficient method is to mark it in Vim with mA. Like any mark, you can return to it with `A. Why ‘A’? Capitalized marks are file marks and are valid between files. With this command, you can mindlessly jump back to your test from anywhere in the jumplist stack.

Using zsh functions with xargs

I want to call a zsh function with xargs, but the arguments passed to xargs don’t run in your environment.

$ function hi() { echo "hello world $@" }
$ hi person!
hello world person!
$ seq 3 | xargs hi
xargs: hi: No such file or directory

No such file or directory!? hi is a function, but xargs doesn’t see it. With a combination of environment variables, function output and zsh command execution, we can use that function with xargs.

First let’s read the definition of our function into an environment variable.

$ FUNCS=$(functions hi); echo $FUNCS
hi () {
  echo "hello world $@"
}

Now we can use that in combination with zsh -c to execute the function with xargs.

$ FUNCS=$(functions hi); seq 3 | xargs -I{} zsh -c "eval $FUNCS; hi {}"
hello world 1
hello world 2
hello world 3

This solution is messy but workable.

Multiline matches with ripgrep (rg)

Ripgrep has become the default file search tool in my development environment. It’s fast! It can also do multiline searches if given the correct set of flags.

First, let me introduce you to the dataset:

$ echo 'apple\norange\nbanana\nkiwi'
apple
orange
banana
kiwi

So what if I want all the lines from orange to kiwi?

$ echo 'apple\norange\nbanana\nkiwi' | rg 'orange.*kiwi'

This finds nothing! Never fear, there is a --multiline flag.

$ echo 'apple\norange\nbanana\nkiwi' | rg --multiline 'orange.*kiwi'

This also finds nothing! The problem is that . does not match \n in regex. You can change that behaviour however by using the dot all modifier which looks like (?s).

$ echo 'apple\norange\nbanana\nkiwi' | rg --multiline '(?s)orange.*kiwi'
orange
banana
kiwi

We did it! Alternately, you can use the --multiline-dotall flag to allow . to match \n.

$ echo 'apple\norange\nbanana\nkiwi' | rg --multiline --multiline-dotall 'orange.*kiwi'
orange
banana
kiwi

I prefer short incantations however, and we can shorten it by using -U instead of --multiline.

$ echo 'apple\norange\nbanana\nkiwi' | rg -U '(?s)orange.*kiwi'
orange
banana
kiwi

Access a shadowDOM node from Capybara

Using selenium_headless you can access a node from the shadowDOM using evaluate_script. The trick is knowing where in the showdowRoot.childNodes it’s going to be. If you have a web component called my-dope-component then you could do something like this:

shadow_host = find('my-dope-component')
shadow_child = evaluate_script('arguments[0].shadowRoot.childNodes[<magic-index>]', shadow_host)

The value for <magic-index> depends on the structure of the template that you’re using, as well as the library (if any) that you’re using. You could also write a find style script that iterates the children and locates the correct one based on the tagName property or similar.

For example:

FIND_NODE = <<-SCRIPT
Array.from(arguments[0].shadowRoot.childNodes).map(element => {
  if (element.tagName === arguments[1].toUpperCase()) return element;
}).filter( value => value != undefined)[0]
SCRIPT

Git Diff Matches Paths 📁

Today I learned that the git-diff command accepts a <path> argument. I used this knowledge to construct a command that runs only the RSpec test files on my branch that differ from master.

(other-branch)$ git diff master --name-only spec | xargs rspec

This is handy on a big test suite; when you can’t run every test, make sure you run the ones that you have changed.

See man git-diff for more info.

Turn on coordinates in Minecraft Realm

In a Minecraft Realm the option to turn on the coordinate system is not available without turning on cheats. That is not good because then you’ll lose achievements.

Many tutorials recommend downloading the realm locally, flipping the switch for coordinates, and then uploading that copy of your world back to the realm. Turns out that some server commands will work in the Realm… coordinates being one of them!

Enter this into your chat window to enable coordinates:

/gamerule showcoordinates true

You can, of course, turn them back off via:

/gamerule showcoordinates false

Auth with gh on remote machine

gh is a new github cli in the alpha phase right now hosted at https://github.com/cli/cli. It’s auth right now is through the browser which doesn’t work great on a remote machine.

When you try to do something with gh that should connect to github you’ll get a bunch of errors and this:

$ gh pr create
Please open the following URL manually:
https://github.com/login/oauth/authorize?client_id=XXXXXXXXXXXXXXXXXX&redirect_uri=http%3A%2F%2Flocalhost%3A34869%2Fcallback&scope=repo&state=XXXXXXXXXXXXXXXXXXXX

This is running a server in the background that is waiting for a request, don’t kill the process until the authentication is complete.

You can copy that url to your browser, authenticate with github and then you are redirected to a localhost url where the server is hosted

Copy the localhost url that you were redirected to, open a new window and then curl the localhost url.

$ curl http://localhost:34869/callback&scope=repo&state=XXXXXXXXXXXXXXXXXXXX

Now you should be authenticated and the gh process should continue.

Search in dotfiles with ripgrep

ripgrep is a very fast searching file system searching utility written in Rust. General usage is like this:

rg something directory-name

This is great, but when searching in my dotfiles for a configuration I don’t find what I’m looking for. Any file whose filename starts with . is considered hidden and these files are not searched by default with ripgrep.

To search hidden files (or dot files) use the --hidden flag.

rg --hidden "alias git" ~

Now, if there is a configuration that is overriding git in my dot files, I’ll find it!

Mac Notes Default Text Size 🔎

I use Mac Notes a lot during pairing and brainstorming. It works, and it’s right there on the Mac!

One issue I’ve had is that the default text size on Mac Notes is unreadably small on a big monitor or iMac. I always end up creating a note, then bumping the text size with CMD-SHIFT ..

Of course, there’s a better way. Set the default font size in preferences.

image

Expo PushNotifications with pop-ups on Android

Expo has an amazing push notification service. In order to display push notifications in a pop-up style (the default on iOS) on Android, you must do two things:

  1. Create an Android push notification channel
  2. Set the priority and channelId in the push notification payload to expo.

Create channel example:

import React from "react";
import { Platform } from "react-native";
import { Notifications } from "expo";

class App extends React.Component<Props> {
  componentDidMount() {
    if (Platform.OS === "android") {
      Notifications.createChannelAndroidAsync("chat-messages", {
        name: "Messages",
        priority: "max",
        sound: true,
        vibrate: [0, 250, 500, 250]
      });
    }
  }

  render() {
    // ...
  }
}

Send PN with priority and channel example:

curl -H "Content-Type: application/json" -X POST "https://exp.host/--/api/v2/push/send" -d '{
  "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  "title":"New Message",
  "body": "Are you there?",
  "channelId": "chat-messages",
  "priority": "max"
}'

Byline Class and Time Tag 📰

I’ve been hacking on TIL to optimize our site for Safari Reader mode. A lot of this is black box, because Apple doesn’t have easy-to-find documentation about how the feature works.

One thing that seems to matter is a class called byline for your author information, and an HTML tag time for the publication date. Here’s an example, borrowed from this site:

<p class="byline">
  <%= name %>
  <time datetime="<%= inserted_at %>">
    <%= inserted_at %>
  </time>
</p>

It gives us relevant reader information, shown here in Safari:

image

Here’s the PR:

https://github.com/hashrocket/tilex/pull/650

Ergodox vim input for numpad keys can be wonky

I use the ergodox ez keyboard. When you’re setting it up you can select either the numpad value of a key, or the shifted/regular value. These will behave slightly differently depending on your settings for application keypad mode. This is something that is set at the application layer, so you may end up dealing with vim recieing <esc>Ok if you are trying to type +. You can either try to find the way to get your application (in my case Manjaro’s terminal emulator) to change the keypad mode (I couldn’t find that), or you can setup an innoremap section in your vimrc to cover the cases where you definitely don’t want the escape sequence.

:inoremap <Esc>Oq 1
:inoremap <Esc>Or 2
:inoremap <Esc>Os 3
:inoremap <Esc>Ot 4
:inoremap <Esc>Ou 5
:inoremap <Esc>Ov 6
:inoremap <Esc>Ow 7
:inoremap <Esc>Ox 8
:inoremap <Esc>Oy 9
:inoremap <Esc>Op 0
:inoremap <Esc>On .
:inoremap <Esc>OQ /
:inoremap <Esc>OR *
:inoremap <Esc>Ol +
:inoremap <Esc>OS -
:inoremap <Esc>OM <Enter>

This sequence is for PuTTY, but you can see which key to put after the <Esc>O by looking at what registers on keydown events. Alternatively, for ergodox users, only use the shifted/regular values for keys.

Meta Charset Top That! 🔝

When writing HTML, make sure that your meta charset tag is in the first 1024 bytes on the page.

Per MDN:

The <meta> element declaring the encoding must be inside the <head> element and within the first 1024 bytes of the HTML as some browsers only look at those bytes before choosing an encoding.

What does this mean practically? Keep this tag near the top of your document, and use a website validator like https://validator.w3.org to check your tag placement.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta

Prefetch images in ReactNative

When a react native app boots and there is a known set of user/group avatar urls received from an API, it may be a better user experience to load them before hand.

React Native’s Image component has a prefetch method that will save the image into the device’s image cache:

import { Image } from "react-native";

const App = () => {
  const { users, loading } = useUserFetcher();

  users.forEach(u => Image.prefetch(u.avatarURL));

  return (
    <View>
      <UserList users={users} loading={loading} />
    </View>
  );
};

Type "text/javascript" is Redundant

Have a script tag in your HTML annotated with <script type="text/javascript">? Per the HTML5 specification, the MIME type of a script tag, when omitted, is text/javascript. We are encouraged to omit it.

Delete that code and go green 💚.

Per MDN:

Omitted or a JavaScript MIME type: This indicates the script is JavaScript. The HTML5 specification urges authors to omit the attribute rather than provide a redundant MIME type. In earlier browsers, this identified the scripting language of the embedded or imported (via the src attribute) code. JavaScript MIME types are listed in the specification.

Source:

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

Swap between STI classes in Rails

Rails allows you to swap between single table inheritance classes with the becomes method. This method creates a new instance of the desired class, and passes the attributes from the original record to the new instance.

So if I have two classes, User and Admin, where Admin inherits from User:

class User
  # ...
end

class Admin < User
  # ...
end

I can change between the two classes using becomes:

user = User.first # returns an instance of the User class
user.class # => User
user.id # => 1
user.name # => Joe Hashrocket

admin = user.becomes(Admin) # returns an instance of the Admin class
admin.class # => Admin
admin.id # => 1
admin.name # => Joe Hashrocket

RN fetch on Android requires mime type

ReactNative on Android requires a mime type when uploading files with FormData. But the only way to get the mime type of a user-chosen file is to read the file or files into memory after a user select one or more. In all of my use cases, my server didn’t care if the mimetype was in the FormData.

The easy solution is to just set the mimetype to a binary file type (e.g. application/octet-stream):

let data = new FormData();
data.append("file", {
  name: "cool.pdf",
  uri: "///files/cool.pdf",
  // MIME-type required on Android
  type: "application/octet-stream"
});

fetch("http://example.com/upload-file", data);

Disable Spring in Rails with `DISABLE_SPRING`

This is fairly self explanatory. If you don’t want spring running, because maybe you forget it’s on and then you’re confused when you’re making changes to configuration and you don’t see those changes manifest, then you can disable spring with:

DISABLE_SPRING=true

But be careful about where you put this. If you are using dotenv and you place it in your .env file it will not take effect. dotenv sets its environment after spring has started.

This is something that you should put in bashrc or zshrc.

export DISABLE_SPRING=true

Ecto queries can be combined

Queries can take other queries to build on top of, which is very useful for conditional query logic:

def find_posts(group_id, title) do
  posts =
    from(posts in Phoenix.Post,
      where: posts.group_id == ^group_id,
    )
    |> title_search(title)
    |> Phoenix.Repo.all()
end

defp title_search(query, title) do
  query
    |> where([posts], posts.title == ^title)
end

defp title_search(query, nil) do
  query
end

💅 styled-components limit prop names

💅 styled-components is one of my absolute favorite libraries! ❤️ I just have to be careful not use prop names for my components that could be interpreted as styles.

For example:

interface Props {
  position: "left-side" | "right-side";
  item: Item;
}

const ListItem= styled.View<{position: "left-side" | "right-side"}>(({position}) => ({
  backgroundColor: position === "left-side" ? "burlywood" : "skyblue";
}));

const ListItemContainer: React.FC<Props> = ({position, item}) => {
  return (
    <ListItem position={position}>
      <Text>{item.name}</Text>
    </ListItem>
  );
}

<ListItemContainer item={item} position="left-side" />

This will return an error because in StyleSheet, position must be one of absolute or relative

Do float division with `fdiv`

Usually in ruby, when I need to perform floating point division I call .to_f on either the numerator or denominator, like this:

1 / 5.to_f
# 0.2

While many people might read this as floating point division instead of integer division, reading it that way gets trickier when those numbers are variables and the .to_f happens somewhere else.

You can be more explicit about the operation you are performing with .fdiv.

1.fdiv(5)
# 0.2

This works with floats as well, so that you can use fdiv in any context.

1.to_f.fdiv(5.to_f)
# 0.2

Remove newlines from strings in Rails

Rails has a method called squish that will remove newlines from strings. It works very nicely with heredocs, where you may want readability but don’t really want the newlines.

Without squish:

<<~SQL
  update posts
  set status = 'public'
  where status is null;
SQL
# "update posts\nset status = 'public'\nwhere status is null;\n"

With squish:

<<~SQL.squish
  update posts
  set status = 'public'
  where status is null;
SQL
# "update posts set status = 'public' where status is null;"

Couldn't decrypt `config/credentials.yml.enc`

Rails 5.2 introduced the credentials system for managing secure credentials in your application. You can edit the file with rails credentials:edit, which is pretty dope. But if you do like I did and you start your project on one machine and go to try and edit that file on a different machine after you pull down the repository from Github, it will generate a new master.key file for you. This will not work to decrypt the file, and you’ll get the error above. You need to make sure that you grab the file from wherever you started your application from. In my case it was as simple as using scp to grab the file from a different machine.

How to manually edit ufw rules

Usually you don’t want to manually edit ufw, but in this case I just needed to update an ip address across multiple ip4 and ip6 rules. Turns out there is config file that can be very carefully edited:

$ sudo vim /lib/ufw/user.rules
$ sudo vim /lib/ufw/user6.rules

Very carefully update the ip addresses and then reload ufw:

$ sudo ufw reload

Constrain a route to a specific host

If you have two hosts that point to one rails app, like apple.myapp.com and orange.myapp.com and you’d like a specific route to only be availabe on orange then you can constrain the route based on the host like this:

constaints(host: 'orange.myapp.com') do
  get '/thing/:id', to: 'thing#show'
end

And if you had additional host for banana that should also get this route:

constaints(host: ['orange.myapp.com', 'banana.myapp.com']) do
  get '/thing/:id', to: 'thing#show'
end

But in this case only the subdomain differs so we can do this:

constaints(subdomain: ['orange', 'banana']) do
  get '/thing/:id', to: 'thing#show'
end

Git Interactive Single Key 👇

Today I became familiar with the Git configuration interactive.singleKey. In the Hashrocket dotfiles this is enabled by default, and I’m not sure how common it is out in the wild. To quote the docs:

In interactive commands, allow the user to provide one-letter input with a single key (i.e., without hitting enter).

This allows git add --patch to be very powerful by enabling a quick vote up or down on a patch with a single keystroke.

Make sure you have the Perl module Term::ReadKey installed; it is a dependency.

What's my public IP? #automation #linux #terminal

Ever wonder what your public IP looks like? When I do, I usually search duckduckgo.com for “what’s my ip” and it is nice enough to tell me that at the top above the search results.

But what if I want to do that at the terminal, and perhaps use that information in a script?

TIL about ipinfo.io! curl it and you get your ip, hostname, city, state, zipcode, country, ISP, timezone, even lat & lng coordinates of the rough location!

curl ipinfo.io
{
  "ip": "111.111.111.111",
  "hostname": "redacted.comcast.com",
  "city": "Chicago",
  "region": "Illinois",
  "country": "US",
  "loc": "41.8500,-87.6500",
  "org": "AS7922 Comcast Cable Communications, LLC",
  "postal": "60666",
  "timezone": "America/Chicago",
  "readme": "https://ipinfo.io/missingauth"
}

This information is not fully accurate, but it’s good enough for scripts, and because it comes back in json form you can pipe it to jq to extract info or read it directly into your scripts.

See git history of a renamed file

If you rename a file, git won’t show history of the previous name:

$ git log --pretty=oneline things/text.txt
8567d... Move file into things directory

However, the --follow flag will allow you to see the history of commits beyond the rename:

$ git log --follow --pretty=oneline things/text.txt
8567d... Move file into things directory
1458a... Fix something
0aac5... Add something

Docs:

--follow
  Continue listing the history of a file beyond renames (works only for a single file).

Import Absolute Paths in Typescript Jest Tests

In order to avoid this:

// project/__tests__/stuff/someDistantCousin.test.ts
import { thing } from '../../src/stuff/someDistantCousin'
import { wrapFunction } from '../testUtils/firebase'

And to write this instead:

import { thing } from 'src/stuff/someDistantCousin'
import { wrapFunction } from 'tests/testUtils/firebase'

There are 2 things to configure:

  1. Jest Config (i.e. jest.config.js, package.json, etc…)
  2. Typscript Config: tsconfig.json

jest.config.js

module.exports = {
  moduleNameMapper: {
    'src/(.*)': '<rootDir>/src/$1',
    'tests/(.*)': '<rootDir>/__tests__/$1',
  },
}

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "src/*": ["src/*"],
      "tests/*": ["__tests__/*"]
    }
  }
}

Show All Docker Containers (Running & Stopped)

docker ps was a confusing command for me, because I thought that it would show all containers by default. But trying to run a container that has been stopped will give you a an error that there is a container in use with that name. Running docker ps will be fruitless because that container will not be listed by default. You can easily view all your containers, running & stopped, with:

docker ps -a

H/T to the docs lol - https://docs.docker.com/engine/reference/commandline/ps/

How to typescript react native list refs

VirtualLists (i.e. SectionList, FlatList) have always been dificult for me to describe in typescript, but the following seems to work:

interface Props {
  list: MutableRefObject<SectionList<any> | undefined>;
}

Example:

import React, { MutableRefObject } from "react";
import { SectionList, View, TouchableOpacity, Text } from "react-native";

interface Props {
  listRef: MutableRefObject<SectionList<any> | undefined>;
}

const Button = (props: Props) => {
  return (
    <TouchableOpacity
      onPress={() => {
        props.listRef.current?.scrollToLocation({
          itemIndex: 0,
          sectionIndex: 0,
          animated: true
        });
      }}
    >
      <Text>Scroll Up</Text>
    </TouchableOpacity>
  );
};

const Screen = () => {
  const listRef = useRef<SectionList<any>>();

  return (
    <View>
      <SectionList ref={listRef} />
      <Button listRef={listRef} />
    </View>
  );
};

Search and Replace Control Characters In Vim

In vim, if you want to search and replace non-printable control characters (for example you have ^M scattered throughout your file and it’s messing up an import) you can use ctrl+v+<char>. The ctrl+v allows you to type the control character, but you must hold down control for both of the key presses.

:%s/ctrl+v+M/

This would search and replace all non-printable ^M characters in your text.

Slack Hot-Keys Rock

Do you like slack? Chances are it doesn’t really matter whether you do or don’t. It’s a matter of course at this point that you will probably work with it. Because that’s the case, you should at least strive to be as aware of neat features as possible!

To that end, you should check out the built-in hot-key help using:

MacOS: Command + / Linux/Windows: Ctrl + /

I’m particularly fond of the “Navigation” and “Channels & Direct Messages”!

No ifconfig on Linux?

If you are on a Linux machine and trying to run ifconfig you may get Command not found: ifconfig. That is because some Linux distros don’t come with it bundled by default.

On Manjaro Linux which is an Arch Linux based distro you can use pacman to install the package containing ifconfig called net-tools:

sudo pacman -S net-tools

On Debian based distros you can use:

sudo apt-get install net-tools

If you don’t want to install net-tools you can try using the ip command:

ip addr

This will return the interfaces on your machine and their IP bindings.

How to redirect standard error

2> Allows us to redirect standard error.

Taking advantage of rm’s ability to not delete files recursively can come in handy, especially when writing clean up scripts, but it can be noisy when you don’t care.

Give this file structure:

~/
  tmpfile1.txt
  tmpfile2.txt
  tmpfile3.txt
  tmpfile4.txt
  do-not-delete/
    secrets.yml

I may want to delete all the files, but not touch the directories (to keep file removal simple)

$ rm *
rm: cannot remove 'do-not-delete': Is a directory

$ ls
~/
  do-not-delete/
    secrets.yml

Because the directory error message comes over stderr, we can simply redirect it to /dev/null to ignore it:

rm * 2> /dev/null