Method gets with crystal

Hi guys, I have a specific request. How when I query the user input with gets, instead of echoing the input, print asterisk ? I would like basically to mask the user input, but print something instead.

It wouldn’t print asterisks, but would be pretty familiar to a Linux user to just use #noecho.

So I saw this already, but first it’s not exactly the result I would like, I really would like to have the possibility to print asterisk instead.

And the second issue I have is, if the user type straight away something, the echo take a bit to be disabled, is it normal too ?

Here’s my simple attempt:

print "Enter password: "
password = String.build do |str|
  loop do
    char = STDIN.raw &.read_char
    if char == '\r' || char == '\n'
      puts
      break
    end

    str << char
    print "*"
    # STDOUT.flush
  end
end
puts "Password is set as #{password}"

Additionally, I found a shard prompt that covers this use case. :smiling_face_with_sunglasses:

1 Like

Thanks a lot !

Is it normal when I press CTRL+C, I can’t still escape ?
And if I press backspace, I it still print asterisk :x

The following code adds a check to verify if the input characters are printable and handles backspaces.

print "Enter password: "
password = ""

loop do
  char = STDIN.raw &.read_char

  case char
  when '\u0003'
    abort "\nInput terminated with Ctrl+C"
  when '\r', '\n', '\u0004' # handle Ctrl+D as EOF
    puts
    break
  when '\u007F' # handle Backspace
    unless password.empty?
      password = password.rchop
      print "\b \b"
    end
  when .try &.printable?
    password += char.not_nil!
    print "*"
  else
    # next
    print "\r#{" " * (26 + password.size)}\r"
    print "Invalid input! Try again: "
    STDIN.rewind
    password = ""
  end
end

puts "Password is set as #{password}"

It should interrupt when detects Ctrl+C and treats as EOF when detects Ctrl+D from STDIN.

1 Like

Thank you so much Sunrise

I have one question. In my software, I would like to perform a rescue to ensure if the user press CTRL+C during execution time, I can perform an action before exit. How can I do this ?

It’s my Main.cr of my project:

require "./RequiredLibraries"

Ism = ISM::CommandLine.new

begin
    #We check first if the user try to perform right escalating access
    tryEscalating = false

    if ISM::Core::Security.ranAsSuperUser
        tryEscalating = true
        ISM::Core::Notification.needToBeRunAsNormalUserNotification
    elsif !ISM::Core::Security.ranAsMemberOfGroupIsm
        tryEscalating = true
        ISM::Core::Notification.needToBeRunAsMemberOfIsmGroupNotification
    end

    if tryEscalating
        ISM::Core.exitProgram
    end

    ISM::Core.hideTerminalCursor

    Ism.start

#We catch any raised error
rescue error
    ISM::Core::Error.show(  className: "None",
                            functionName: "None",
                            errorTitle: "Unexpected error occured",
                            error: "The program stopped due to an unknown error",
                            exception: error,
                            information: "This error occur when #{ISM::Default::CommandLine::Name.upcase} is unable to catch the error",
                            errorCode: 1)

#We ensure that the program exit securely
ensure
    #We ensure that the system is locked even we are facing an issue
    if ISM::Core::Security.systemHandleUserAccess
        ISM::Core::Security.lockSystemAccess
    end

    #To finish, we reset the initial terminal title, reset cursor and exit with the error code 1
    ISM::Core.showTerminalCursor
    ISM::Core.resetTerminalTitle
    ISM::Core.exitProgram(code: 1)
end
    raise "Terminated with Ctrl+C"

Use raise instead of abort so that interrupts caused by Ctrl-C can be caught with rescue.

But how I catch this interrupt? It’s this I don’t know how to do

In the case above, since the characters typed into STDIN need to be processed one by one, it is implemented by capturing the character '\u0003' from pressing Ctrl+C;
For other more general cases, on_terminate can be used.