Ẩn đầu vào của người dùng trên thiết bị đầu cuối trong tập lệnh Linux


121

Tôi có tập lệnh bash như sau:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

Tôi muốn rằng khi người dùng nhập mật khẩu trên thiết bị đầu cuối, nó sẽ không được hiển thị (hoặc một cái gì đó như *******) sẽ được hiển thị). Làm cách nào để đạt được điều này?


2
Lưu ý chung chỉ để tránh nhầm lẫn: tên người dùng / mật khẩu này không liên quan gì đến tên người dùng / mật khẩu linux - Tôi chỉ đang tìm cách ẩn dữ liệu mà người dùng nhập trong quá trình "đọc mật khẩu".

Tôi thêm một bản cập nhật cho nếu bạn muốn nhận được ưa thích bởi xuất ra *trong khi họ gõ vào mật khẩu
SiegeX

Cảm ơn nhiều. Một câu hỏi nếu ai đó biết - điều này có tự động ngăn nó đi vào .bash_history không?


2
Tôi không nghĩ nó sẽ làm được, bash_history chỉ nắm bắt lệnh của bạn, những gì sẽ xảy ra sau khi chạy lệnh của bạn, nó không bắt được.
Andreas Wong

Câu trả lời:


272

Chỉ cần cung cấp -s cho cuộc gọi đã đọc của bạn như vậy:

$ read -s PASSWORD
$ echo $PASSWORD

9
Tôi thích cách này hơn. Tôi sẽ bỏ phiếu cho bạn nếu tôi không đạt đến giới hạn hàng ngày của mình
SiegeX

4
Để cung cấp ngữ cảnh: không-s hiển thị khi đầu vào được nhập. ( -sLà một phần mở rộng không POSIX, vì vậy không phải tất cả vỏ hỗ trợ nó, chẳng hạn như ashvỏ mà đi kèm với BusyBox; sử dụng ssty -echocách tiếp cận trong vỏ như vậy)
mklement0

3
@electron Không có gì trong mantrang thực sự gợi ý rằng -sđặt màu văn bản giống với màu nền. Nó chỉ "âm thầm vang vọng". ss64.com/bash/read.html Silent mode. If input is coming from a terminal, characters are not echoed.
Andreas Wong

2
@electron Tôi đã thử nghiệm trên hộp của mình, không những nó không di chuyển con trỏ, khi tôi cố đánh dấu dòng nơi tôi đã nhập mật khẩu của mình, tôi dường như không thể dán nó sau đó. Cả hai điều sẽ xảy ra nếu những gì cuốn sách nói là đúng. Tôi đoán có thể là một hương vị khác của vỏ (tôi đã sử dụng bash 4.1.2 (1)).
Andreas Wong

1
@DominykasMostausk là đúng nhưng nhãn là dành cho bash, do đó, giải pháp. Có lẽ tấn vỏ biến thể nơi này không làm việc :)
Andreas Wong

25

Cập nhật

Trong trường hợp bạn muốn trở nên lạ mắt bằng cách xuất một *ký tự cho mỗi ký tự mà họ nhập, bạn có thể làm như sau (sử dụng read -sgiải pháp của andreas ):

unset password;
while IFS= read -r -s -n1 pass; do
  if [[ -z $pass ]]; then
     echo
     break
  else
     echo -n '*'
     password+=$pass
  fi
done

Không cần ưa thích

echo "Please enter your username";
read username;

echo "Please enter your password";
stty -echo
read password;
stty echo

Nó phải IFS=$'\n'như vậy để bạn thực sự có thể hoàn thành việc nhập mật khẩu. Nếu không thì tốt đẹp; rất thông minh.
sorpigal

2
Không cần đặt IFS=$'\n'vì dấu phân cách mặc định của read là -d $'\n'. Câu lệnh if để ngắt trên một chuỗi nul, xảy ra trên một dòng mới, là thứ cho phép chúng kết thúc mật khẩu.
SiegeX

Trong thử nghiệm trên FreeBSD với bash 3.x, tôi thấy điều này không đúng. Thử nghiệm trên Linux với 3.1.17 mang lại kết quả của bạn. Hiện tại tôi chưa có hộp BSD nhưng tôi sẽ cố gắng xác nhận điều này vào ngày mai, chỉ vì sự tò mò của riêng tôi.
sorpigal

@Sorpigal: thú vị, hãy cho tôi biết những gì bạn tìm ra. Tôi là tất cả về tính di động
SiegeX

2
Tôi đã hiểu. Bài kiểm tra FreeBSD của tôi dựa trên mã được sao chép và dán từ bản chỉnh sửa nhầm lẫn ban đầu của bạn về bài đăng của lesmana, có một điểm khác biệt quan trọng: bạn đã vượt qua readmột -d ''. Khi tôi thử lại sau trên Linux, tôi đã sử dụng phiên bản đã đăng lại. Nếu tôi cố gắng bỏ qua, -d ''tất nhiên nó hoạt động giống hệt nhau trên FreeBSD! Tôi thực sự khá nhẹ nhõm vì không có phép thuật nền tảng bí ẩn nào đang hoạt động ở đây.
sorpigal

17

cho một giải pháp hoạt động mà không có bash hoặc một số tính năng nhất định từ readbạn có thể sử dụng sttyđể tắt tiếng

stty_orig=$(stty -g)
stty -echo
read password
stty $stty_orig

9

Đây là một biến thể của *giải pháp in ấn tuyệt vời của @ SiegeX bash với hỗ trợ thêm vào khoảng trống; điều này cho phép người dùng sửa mục nhập của họ bằng backspacekhóa ( deletekhóa trên máy Mac) , như thường được hỗ trợ bởi lời nhắc mật khẩu:

#!/usr/bin/env bash

password=''
while IFS= read -r -s -n1 char; do
  [[ -z $char ]] && { printf '\n'; break; } # ENTER pressed; output \n and break.
  if [[ $char == $'\x7f' ]]; then # backspace was pressed
      # Remove last char from output variable.
      [[ -n $password ]] && password=${password%?}
      # Erase '*' to the left.
      printf '\b \b' 
  else
    # Add typed char to output variable.
    password+=$char
    # Print '*' in its stead.
    printf '*'
  fi
done

Ghi chú:

  • Về lý do tại sao nhấn phím xóa lùi lại ghi mã ký tự 0x7f: "Trong các hệ thống hiện đại, phím xóa lùi thường được ánh xạ tới ký tự xóa (0x7f trong ASCII hoặc Unicode)" https://en.wikipedia.org/wiki/Backspace
  • \b \blà cần thiết để cung cấp cho sự xuất hiện của việc xóa ký tự bên trái; chỉ sử dụng \bdi chuyển con trỏ sang trái, nhưng vẫn giữ nguyên ký tự ( xóa lùi không phá hủy ). Bằng cách in dấu cách và di chuyển trở lại, ký tự dường như đã bị xóa (cảm ơn, Ký tự thoát "xóa lùi" '\ b' trong C, hành vi không mong muốn? ).

Trong một POSIX chỉ vỏ (ví dụ, shtrên Debian và Ubuntu, nơi shdash), sử dụng các stty -echophương pháp (đó là tối ưu, bởi vì nó in không có gì ), vì readBUILTIN sẽ không hỗ trợ -s-ntùy chọn.


Cảm ơn, @Dylan. Tôi chỉ nhận ra rằng mã để xóa ký tự cuối cùng khỏi mật khẩu đã nhập cho đến nay có thể được đơn giản hóa - hãy xem bản cập nhật.
mklement0

3

Một chút khác với câu trả lời của @ lesmana

stty -echo
read password
stty echo

đơn giản là: ẩn tiếng vang để nội dung của bạn hiển thị tiếng vang


Bạn có thể giải thích tại sao câu trả lời này tốt hơn câu trả lời của @ lesmana không? Tôi thấy nó đơn giản hơn với ít chi phí hơn cho một sttycuộc gọi khác và một ít biến hơn trên ngăn xếp - chỉ loại bỏ tiếng vọng và chỉ khôi phục tiếng vang. Có cách nào khả thi điều này có thể làm đảo lộn các sttycài đặt khác không? Lesmana's bảo tồn tất cả các cài đặt.
Jeff Puckett

2

Đây là một biến thể của câu trả lời của @ SiegeX hoạt động với trình bao Bourne truyền thống (không hỗ trợ cho các +=bài tập).

password=''
while IFS= read -r -s -n1 pass; do
  if [ -z "$pass" ]; then
     echo
     break
  else
     printf '*'
     password="$password$pass"
  fi
done

Một trình bao Bourne truyền thống (hoặc một trình bao tương thích với POSIX) cũng sẽ không nhận ra [[ ... ]], cũng như readnội bộ của nó sẽ không có các tùy chọn -s-ntùy chọn. dashVí dụ, mã của bạn sẽ không hoạt động . (Nó sẽ làm việc trên nền tảng nơi shthực sự là bash, chẳng hạn như trên OSX, nơi bashkhi gọi là shchỉ sửa đổi một vài tùy chọn mặc định khi vẫn hỗ trợ hầu hết các bashisms.)
mklement0

2
Cảm ơn phản hồi, đã chuyển sang đơn lẻ [nhưng rõ ràng tôi không thể làm gì nhiều nếu bạn readthiếu các tùy chọn này.
tripleee

2

Tôi luôn thích sử dụng các ký tự thoát Ansi:

echo -e "Enter your password: \x1B[8m"
echo -e "\x1B[0m"

8mlàm cho văn bản ẩn và 0mđặt lại văn bản thành "bình thường". -E khiến Ansi có thể trốn thoát.

Cảnh báo duy nhất là bạn vẫn có thể sao chép và dán văn bản ở đó, vì vậy bạn có thể không nên sử dụng tính năng này nếu bạn thực sự muốn bảo mật.

Nó chỉ cho phép mọi người không nhìn vào mật khẩu của bạn khi bạn nhập chúng vào. Chỉ cần không để máy tính của bạn bật sau đó. :)


GHI CHÚ:

Ở trên là độc lập với nền tảng miễn là nó hỗ trợ trình tự thoát Ansi.

Tuy nhiên, đối với một giải pháp Unix khác, bạn có thể chỉ cần nói readđể không lặp lại các ký tự ...

printf "password: "
let pass $(read -s)
printf "\nhey everyone, the password the user just entered is $pass\n"

1

Nhận tên người dùng và mật khẩu

Làm cho nó rõ ràng hơn để đọc nhưng đặt nó ở vị trí tốt hơn trên màn hình

#!/bin/bash
clear
echo 
echo 
echo
counter=0
unset username
prompt="  Enter Username:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]; then
        break
    elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
        prompt=$'\b \b'
        username="${username%?}"
        counter=$((counter-1))
    elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
        prompt=''
        continue
    else
        counter=$((counter+1))
        prompt="$char"
        username+="$char"
    fi
done
echo
unset password
prompt="  Enter Password:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]; then
        break
    elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
        prompt=$'\b \b'
        password="${password%?}"
        counter=$((counter-1))
    elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
        echo
        prompt="  Enter Password:"
        continue
    else
        counter=$((counter+1))
        prompt='*'
        password+="$char"
    fi
done
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.