haspar.us

Writing `rm` because of Windows

Writing rm because of Windows

Problem

I happen to have a Windows laptop in my possession, and I’m stubborn enough to use it for coding. Fortunately, I am also stubborn enough to get it to work.

rm -rf doesn’t work on Windows.

  1. Powershell on it has Remove-Item aliased to rm.

    This would almost solve the problem, but…

    Remove-Item : A parameter cannot be found that matches parameter name 'rf'.
    At line:1 char:4
    + rm -rf dist

    Powershell, unlike getopt, doesn’t allow specifying multiple options together.

  2. rm -r -f doesn’t work too, because -f is ambiguous.

    Remove-Item : Parameter cannot be processed because the parameter name 'f' is ambiguous.
    Possible matches include: -Filter -Force.
    At line:1 char:7
    + rm -r -f dist

    All right, for a few months I’ve been writing rm -r -fo whenever I had to remove node_modules.

    I aliased[1] it to rmrf for convenience and with the following

    function rmrf {
      param(
            [parameter(Position=0)][string]$directory
      )
      process {
         # rm -r -fo $directory
         Remove-Item -Recurse -Force $directory
      }
    }
[1]

Powershell aliases can’t be used to partially apply arguments. Docs for Set-Alias basically tell you to write a function instead.

Alias was not enough

When I started my career in Polish companies, we often had somebody on MacOS, somebody on Linux and somebody on Windows in the team. Mac was slightly more popular as a standard company issued notebook for developers, but I remember we often had a mix.

When I work on open source, the projects are usually system agnostic (it’s not super hard in web ecosystem), using rimraf instead of rm -rf, using dotenv, writing scripts in JavaScript or Python etc.

Surprisingly, 41.2% StackOverflow Survey respondents use Windows and only 30% use MacOS.

StackOverflow Developer Survey

My anecdata looks more like 75% MacOS, 15% Linux, 10% Windows, but from a perspective of open source guy, I wouldn’t want to break the build for 10% of users or lose 10% of contributors.

However, in a startup setting, the situation is totally different.

The company offered to buy me a Mac, but the orders take about 7 weeks. So, I had a choice. I could either bother my teammates to make the project system-agnostic, or deal with it on my side.

A smarter man might have gone to the store, get any MacBook they have, and be done with it.

Writing my own rm in Rust

Googling “rm for Windows” didn’t get me any useful results, so I quickly wrote my own, very dirty, totally not production-grade, rm to stick in my bin directory.

Disclaimer: Writing the code took me less time than I’ve been writing this note, so I provide no guarantees to the quality and correctness of the code. I didn’t even write any error messages, because exposing the error from std::fs is totally fine for my use case.

main.rs
use clap::Parser;

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
  #[clap(short, long)]
  recursive: bool,

  #[clap(short, long)]
  force: bool,

  #[clap()]
  files: Vec<String>,
}

fn rm(args: Args) -> std::io::Result<()> {
  for file in args.files {
      if args.recursive {
          if args.force {
              std::fs::remove_dir_all(file)?;
          } else {
              std::fs::remove_dir(file)?;
          }
      } else {
          std::fs::remove_file(file)?;
      }
  }

  return std::io::Result::Ok(());
}

fn main() {
  let args = Args::parse();
  Result::unwrap(rm(args))
}
Cargo.toml
[package]
name = "rust-rm"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "3.1.18", features = ["derive"] }

I removed the alias by adding the following to my $PROFILE.

Remove-Item -Path Alias:rm

I promptly ran cargo build --release and I added my newest toy to a directory I already had in PATH.

❯ ls

    Directory: D:\tools\bin

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        04/05/2022     16:24           1128 colormode.ps1
-a----        11/05/2022     17:00         654848 rm.exe
-a----        12/07/2021     16:21              8 sh.cmd
-a----        07/07/2021     15:01         782336 tr.exe
-a----        20/07/2021     13:16            637 xargs.cmd