How to execute alias command from Rust

Hey everyone,

I’ve been working on a small project, called dalia, to learn Rust over the past few weeks. You can review the code on GitHub here. Everything’s gone well so far, but I seem to have gotten stuck getting a call to the alias shell command working from within the Rust code.

To provide a little more background, dalia is a CLI tool where the user provides a config file consisting of directory locations on disk, and an optional name, and dalia creates aliases for cd’ing to them dynamically. The bulk of the logic for that is in main.rs and copied below:

fn run() -> Result<(), String> {
    let mut config = load_configuration()?;
    config.process_input()?;
    let mut alias_cmd = Command::new(ALIAS_PROGRAM);
    for (alias, path) in config.aliases() {
        alias_cmd.arg(format!("{}='cd {}'", alias, path));
    }
    let output = alias_cmd
        .output()
        .expect("alias process failed to create aliases");
    if !output.status.success() {
        return Err(format!("couldn't create aliases: {:?}", output));
    }
    Ok(())
}

If I print the command as a String I see it’s formatted correctly, but none of the aliases work. Am I doing something obviously wrong in code? Or am I missing some subtle nuance about the shell that doesn’t have anything to do with Rust.

Thank you for your time and any help would be appreciated.

Probably alias is a shell builtin and thus is not available without invoking whatever your shell is. That is, there's not actually an alias executable, it's just a word that's interpreted specially by the shell, so to register an alias you need to launch a shell process and tell it to run the alias command with those arguments.

(And welcome to the forum!)

ETA: Another thing to watch out for is whether aliases are globally shared or local to a shell process and its child processes. (I can't tell from the Bash manual's section on aliases how this is handled, and it might vary between shells.) If it's the second one, this approach won't work even with the modification I described, because the shell process where each alias is defined will exit immediately afterward, taking the alias with it.

3 Likes

I think you might be right about this because there’s an example in the Command docs that describes what you suggest (the code snippet’s below):

use std::process::Command;

let mut echo_hello = Command::new("sh");
echo_hello.arg("-c")
          .arg("echo hello");
let hello_1 = echo_hello.output().expect("failed to execute process");
let hello_2 = echo_hello.output().expect("failed to execute process");

I did something similar and the aliases still didn’t work. I may have to figure out an alternative instead of creating the aliases directly by doing something like writing them out to a file and sourcing it).

That's because users' custom aliases are usually defined in a shell startup script. If user has used .bash_rc, then non-bash shells won't know about it. If user uses zsh, then aliases could be in .zshrc, etc. It depends on the shell, and knowing which shell is correct depends on the operating system. There's also .profile and some rules (which I don't remember) that differentiate between login shells and non-login shells that run slightly different startup scripts.

If you tried Command::new("bash").arg("-c").arg(". ~/.profile; alias") it might give you slightly less wrong result.

In general it's a very system-specific problem, and it's not unique to Rust. If you copy a solution from any other language, it will work for Rust too.

3 Likes