Amber icon indicating copy to clipboard operation
Amber copied to clipboard

read user input when script is passed through a pipe

Open gbourant opened this issue 9 months ago • 17 comments

ref: #687

gbourant avatar Mar 17 '25 14:03 gbourant

By adding < /dev/tty we make it work over pipes but it won't echo the prompt correctly if it contains an array.

    const STEPS  = ["STEP_1", "STEP_2", "STEP_3", "STEP_4"]
    const STEP    = input_prompt("Select a step: [{STEPS}]")

With my change it prints Select a step: [STEP_1

gbourant avatar Mar 18 '25 18:03 gbourant

Well, this has nothing to do with the < /dev/tty. It has to do how Amber transpils the code to bash.

__AMBER_ARRAY_3=("STEP_1" "STEP_2" "STEP_3" "STEP_4")
local STEPS=("${__AMBER_ARRAY_3[@]}")
input_prompt__162_v0 "Select a step: ${STEPS[@]}"

If I change local STEPS=("${__AMBER_ARRAY_3[@]}") to local STEPS=("${__AMBER_ARRAY_3[*]}") (with an *) it will print as expected. So maybe when input_prompt ("Select a step: [{STEPS}]") contains an array (STEPS), the * should be used instead of @.

gbourant avatar Mar 18 '25 19:03 gbourant

We need to see if #678 fix that compilation, @Ph0enixKM what do you think?

Mte90 avatar Mar 19 '25 12:03 Mte90

@gbourant could you provide tests for this example?

Ph0enixKM avatar Apr 10 '25 09:04 Ph0enixKM

@Ph0enixKM, I'll check the comments and add tests this or next week.

gbourant avatar Apr 17 '25 06:04 gbourant

@gbourant sure thing. Take your time

Ph0enixKM avatar Apr 17 '25 10:04 Ph0enixKM

I do think that the suggestion to move the < /dev/tty outside of the quotes is correct.

Also, using read -p "$prompt" < /dev/tty forces input to come directly from the terminal, bypassing the stdin redirection (e.g., exec 0</tmp/test_input), which breaks automated testing that relies on feeding input via stdin.

If you have any suggestion let me know.

input_prompt__94_v0() {
    local prompt=$1
    read -p "$prompt" < /dev/tty
    __AS=$?
    __AF_input_prompt94_v0="$REPLY"
    return 0
}
echo "Amber" >/tmp/test_input
__AS=$?
exec 0</tmp/test_input
__AS=$?
input_prompt__94_v0 "Please enter your name:"
__AF_input_prompt94_v0__5_12="${__AF_input_prompt94_v0}"
__0_name="${__AF_input_prompt94_v0__5_12}"
echo "Hello, ""${__0_name}"
rm /tmp/test_input
__AS=$?

gbourant avatar Apr 19 '25 17:04 gbourant

@Ph0enixKM we need u to approve to merge

b1ek avatar May 10 '25 10:05 b1ek

Right, sorry. I took my time too.

Ph0enixKM avatar May 10 '25 10:05 Ph0enixKM

@gbourant the checks are currently failing, you need to fix that before merging

b1ek avatar May 12 '25 15:05 b1ek

Hello @b1ek ,

I have noticed that that's why I made the following comment.

If anyone has any suggestion, it's highly welcomed :-)

Maybe we need to use something like (it's not working)

pub fn eval_bash_pipe<T: Into<String>>(code: T) -> (String, String) {

    let mut cmd = Command::new("bash");
    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());

    let mut child = cmd.spawn().unwrap();
    let code_str = code.into();

    // if let Some(stdin) = child.stdin.as_mut() {
        // stdin.write_all(code_str.as_bytes()).unwrap();
    // }

    child.stdin.as_mut().unwrap().write_all(code_str.as_bytes()).unwrap();

    let output = child.wait_with_output().unwrap();

    (
        String::from_utf8(output.stdout).unwrap().trim_end().into(),
        String::from_utf8(output.stderr).unwrap().trim_end().into(),
    )
}

and

#[test]
fn pipe_test() {
    let hello = fs::read_to_string("src/tests/stdlib/no_output/pipe.ab")
    .expect("Failed to read the script file");
    let hello = compile_code(hello);
    let (stdout, _) = eval_bash_pipe(hello);
    assert_eq!(stdout, "Hello, Amber");
}
import * from "std/env"

trust $ echo "Amber" >> /tmp/test_input $
trust $ exec 0< /tmp/test_input $
let name = input_prompt("Please enter your name:")
echo "Hello, " + name
trust $ rm /tmp/test_input $

// echo "Hello, Amber"

Also note, I'm not familiar with Rust :-)

gbourant avatar May 14 '25 08:05 gbourant

I think that in the meantime you can improve the test for input_prompt that is in amber and for the rust stuff we can see later (there is another PR that fixes some stuff with the CI awaiting) @gbourant

Mte90 avatar May 19 '25 09:05 Mte90

@gbourant can you share updates on this PR?

Ph0enixKM avatar Jul 09 '25 10:07 Ph0enixKM

@Ph0enixKM, I cannot find a way to make the test to pass. It expects the input from the TTY but it reads from the file exec 0< /tmp/test_input. As far as I did my research, there isn't any built-in way to make the test read predefined data from the TTY. Maybe we need to run those test differently like I said at https://github.com/amber-lang/amber/pull/688#issuecomment-2879267183.

Also a small note, my original PR changed only the input_prompt but other input_ methods exist.

I won’t be continuing this, but anyone who wants to pick it up is more than welcome!


pub fun input_prompt(prompt: Text): Text {
    trust $ read -p "\${nameof prompt}" < /dev/tty $
    return trust $ echo \$REPLY $
}

import * from "std/env"

// Output
// Hello, Amber

main {
    trust $ echo "Amber" >> /tmp/test_input $
    trust $ exec 0< /tmp/test_input $
    let name = input_prompt("Please enter your name:")
    echo "Hello, " + name
    trust $ rm /tmp/test_input $
}

gbourant avatar Jul 09 '25 12:07 gbourant

ok, locally this change works. check it when you can @Ph0enixKM

gbourant avatar Jul 10 '25 01:07 gbourant

I'll check this PR when I come back from holidays

Ph0enixKM avatar Jul 16 '25 09:07 Ph0enixKM

@Ph0enixKM can you check this one?

Mte90 avatar Sep 22 '25 10:09 Mte90