The shell challenge test not follow posix rules?

#[allow(unused_imports)]
use std::io::{self, Write};

const BUILT_IN_COMMANDS: [&str;3] = ["echo","exit","type"];

enum Command{
    ExitCommand,
    EchoCommand {display_string:String},
    TypeCommand {command_name: String},
    CommandNotFound,
}

impl Command{
    fn from_input(input:&str) -> Self {
        let input=input.trim();
        if input == "exit" || input == "exit 0" {
            return Self::ExitCommand;
        };
        if let Some(pos) = input.find("echo") {
            if pos ==0{
                return Self::EchoCommand{
                    display_string: input["echo".len()..].trim().to_string(),
                };
            }
        }
        if let Some(pos) = input.find("type"){
            if pos==0 {
                return Self::TypeCommand{
                    command_name: input["type".len()..].trim().to_string(),
                };
            }
        }
        Self::CommandNotFound // we are returning this value
    }

}

fn main() {
    loop{
        print!("$ ");
        io::stdout().flush().unwrap();

        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();
        
        //implementing internal built_in_commands
        let command = Command::from_input(&input);
        match command{
            Command::ExitCommand => break,
            Command::EchoCommand {display_string} => println!("{}", display_string),
            Command::TypeCommand {command_name} => {
                if BUILT_IN_COMMANDS.contains(&command_name.as_str()){
                    println!("{} is a shell builtin",command_name);
                    continue;
                }
                let mut found = false;
                //finding the files using rust std library
                if let Some(path_var) = std::env::var_os("PATH"){
                    for dir in std::env::split_paths(&path_var){
                        let full_path = dir.join(&command_name);
                        
                        // skip if file/comand does not exist
                        if !full_path.exists(){
                            continue;
                        }else {
                            println!("{} is {}", command_name, full_path.display());
                            found=true;
                            break;
                        }
                    }
                }
                if !found{println!("{}: not found", command_name)};
            },
            Command::CommandNotFound => println!("{}: command not found", input.trim()),
        }
    }
}

this is my code so far

this gets stuck at this error

[compile] Compilation successful.

[tester::#MG5] Running tests for Stage #MG5 (Locate executable files)

[tester::#MG5] [setup] export PATH=/tmp/owl:$PATH

[tester::#MG5] [setup] export PATH=/tmp/rat:$PATH

[tester::#MG5] [setup] export PATH=/tmp/bee:$PATH

[tester::#MG5] [setup] PATH is now: /tmp/bee:/tmp/rat:/tmp/owl:…

[tester::#MG5] [setup] Available executables:

[tester::#MG5] [setup] - my_exe

[tester::#MG5] Running ./your_program.sh

[your-program] $ type cat

[your-program] cat is /usr/bin/cat

[tester::#MG5] âś“ Received expected response

[your-program] $ type cp

[your-program] cp is /usr/bin/cp

[tester::#MG5] âś“ Received expected response

[your-program] $ type mkdir

[your-program] mkdir is /usr/bin/mkdir

[tester::#MG5] âś“ Received expected response

[your-program] $ type my_exe

[your-program] my_exe is /tmp/bee/my_exe

[tester::#MG5] ^ Line does not match expected value.

[tester::#MG5] Expected: “my_exe is /tmp/rat/my_exe”

[tester::#MG5] Received: “my_exe is /tmp/bee/my_exe”

[your-program] $

[tester::#MG5] Assertion failed.

[tester::#MG5] Test failed

i tried reversing the path and checking for the first occuranc and still it didnt worked cause other test were failing
please help me with this

Hey @exilonium, could you double-check if your code implements the following logic?

Note that there are multiple files with the same name in different locations, but not all of them are executable. Only the first executable should be picked up.

Would you mind clarifying what you meant by “not following POSIX rules”? Happy to dig into that with you.

1 Like

tysm i was missing that
here’s the full code if anyones interested

#[allow(unused_imports)]
use std::io::{self, Write};
use std::path::PathBuf;

const BUILT_IN_COMMANDS: [&str;3] = ["echo","exit","type"];

enum CommandLocation {
    Builtin,
    Executable(PathBuf),
    NotFound,
}

impl CommandLocation{
    fn resolve(command_name:&str) -> Self{
        if BUILT_IN_COMMANDS.contains(&command_name){
            return Self::Builtin;
        }
        if let Some(path_var) = std::env::var_os("PATH"){
            for dir in std::env::split_paths(&path_var){
                let full_path = dir.join(command_name);
                if !full_path.exists() {continue};

                #[cfg(unix)]
                {
                    use std::os::unix::fs::PermissionsExt;
                    if let Ok(metadata) = std::fs::metadata(&full_path) {
                        if metadata.permissions().mode() & 0o111 == 0 {
                            continue;
                        }
                    } else {
                        continue;
                    }
                }
                return Self::Executable(full_path);
            }
        }
        Self::NotFound
    }
    fn describe(&self,name:&str) -> String{
        match self {
            CommandLocation::Builtin => format!("{} is a shell builtin", name),
            CommandLocation::Executable(path) => format!("{} is {}", name, path.display()),
            CommandLocation::NotFound => format!("{}: not found", name),
        }
    }
}

enum Command{
    ExitCommand,
    EchoCommand {display_string:String},
    TypeCommand {command_name: String},
    CommandNotFound,
}

impl Command{
    fn from_input(input:&str) -> Self {
        let input=input.trim();
        if input == "exit" || input == "exit 0" {
            return Self::ExitCommand;
        };
        if let Some(pos) = input.find("echo") {
            if pos ==0{
                return Self::EchoCommand{
                    display_string: input["echo".len()..].trim().to_string(),
                };
            }
        }
        if let Some(pos) = input.find("type"){
            if pos==0 {
                return Self::TypeCommand{
                    command_name: input["type".len()..].trim().to_string(),
                };
            }
        }
        Self::CommandNotFound // we are returning this value
    }

}

fn main() {
    loop{
        print!("$ ");
        io::stdout().flush().unwrap();

        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();
        
        //implementing internal built_in_commands
        let command = Command::from_input(&input);
        match command{
            Command::ExitCommand => break,
            Command::EchoCommand {display_string} => println!("{}", display_string),
            Command::TypeCommand {command_name} => {
                let location = CommandLocation::resolve(&command_name);
                println!("{}", location.describe(&command_name));
            },
            Command::CommandNotFound => println!("{}: command not found", input.trim()),
        }
    }
}

edit:- by not following posix rule i meant that its not chekcking the leftmost path but that was a coding oversight from me since i was not checking for executable permission

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.