Приховування вводу користувача на термінал у сценарії Linux


121

У мене є bash-скрипт:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

Я хочу, щоб, коли користувач вводить пароль на терміналі, він не повинен відображатися (або щось на зразок *******). Як я цього досягти?


2
Загальна примітка лише для запобігання плутанини: це ім’я користувача / пароль не має нічого спільного з ім'ям користувача / паролем Linux - я просто шукаю спосіб приховати дані, які користувач вводить під час "читання пароля".

Я додав оновлення, якщо ви хочете пофантазувати, *
вивівши при введенні

Велике спасибі Одне запитання, якщо хтось знає - чи це автоматично не дозволить йому зайти в .bash_history?


2
Я не думаю, що це буде, bash_history лише фіксує вашу команду, що відбувається після запуску вашої команди, вона не захоплює.
Андреас Вонг

Відповіді:


272

Просто надішліть-в ваш дзвінок для читання так:

$ read -s PASSWORD
$ echo $PASSWORD

9
Мені так подобається краще. Я б проголосував за вас, якби я не досягав своєї щоденної межі
SiegeX

4
Для забезпечення контексту: нічого не-s відображається, коли вводиться вхід. ( це розширення, яке не є POSIX, тому не всі оболонки підтримують його, наприклад, оболонка, яка постачається з BusyBox; використовуйте підхід у таких оболонках)-sashssty -echo
mklement0

3
@electron Ніщо на manсторінці насправді не припускає, що -sвстановлює колір тексту таким же, як колір фону. Це просто «перегукується мовчки». ss64.com/bash/read.html Silent mode. If input is coming from a terminal, characters are not echoed.
Андреас Вонг

2
@electron Я перевірив свою скриньку, не тільки вона не переміщує курсор, коли я намагаюся виділити рядок, де я вводив свій пароль, я не можу, здається, вставити його пізніше. І те й інше має відбутися, якщо те, що йдеться у книзі, є правдивим. Я здогадуюсь, можливо, інший аромат шкаралупи (я використовував bash 4.1.2 (1)).
Андреас Вонг

1
@DominykasMostauskis Так, але етикетка - це баш, тому рішення. Напевно, існує багато варіантів оболонок, де це не працює :)
Андреас Вонг

25

Оновлення

Якщо ви хочете пофантазувати, видавши *для кожного символу, який вони вводять, ви можете зробити щось подібне (використовуючи read -sрішення andreas ):

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

Без фантазії

echo "Please enter your username";
read username;

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

Це повинно бути IFS=$'\n'таким, щоб ви могли фактично закінчити введення пароля. Інакше приємно; дуже розумний.
сорпігал

2
Не потрібно встановлювати, IFS=$'\n'тому що роздільник лічильника за замовчуванням є -d $'\n'. Перевірка if-перерви на нульовий рядок, що відбувається в новому рядку, - це те, що дозволяє їм закінчити пароль.
SiegeX

Під час тестування на FreeBSD з bash 3.x я вважаю, що це не так. Тестування на Linux з 3.1.17 дає ваші результати. На даний момент я не маю під рукою коробку BSD, але завтра спробую це підтвердити, лише заради власної цікавості.
сорпігал

@Sorpigal: цікаво, дайте мені знати, що ви дізнаєтесь. Я все про портативність
SiegeX

2
Я отримав його. Мій FreeBSD тест заснований на коді скопіювати і вставити з вихідного помилкового редагують пост lesmana, який містить одна важлива відмінність: ви були проходження . Коли я повторно перепробував його в Linux, я використав репостовану версію. Якщо я спробую пропустити, звичайно, це працює однаково на FreeBSD! Мені насправді досить полегшено, що тут немає якоїсь загадкової платформної магії. read-d ''-d ''
сорпігал

17

для рішення, яке працює без bash або певних функцій, readви можете використовувати sttyдля відключення луни

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

9

Ось варіант відмінного *рішення для друку @ SiegeX bash з підтримкою зворотного простору ; це дозволяє користувачеві виправити введення за допомогою backspaceключа ( deleteклавіші на Mac) , як це зазвичай підтримується підказками пароля:

#!/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

Примітка:

  • Що стосується того, чому натискання backspace записує код символів 0x7f: "У сучасних системах клавіша backspace часто відображається на символ видалення (0x7f в ASCII або Unicode)" https://en.wikipedia.org/wiki/Backspace
  • \b \bпотрібен для надання вигляду видалення символу зліва; просто за допомогою \bпереміщення курсору вліво, але залишає персонажа недоторканим ( неруйнівна задня область). Надрукувавши пробіл і знову повернувшись назад, символ, здається, був стертий (спасибі, " Зворотний простір ", символ втечі "\ b" в C, несподівана поведінка? ).

У оболонці, призначеній лише для POSIX (наприклад, shна Debian та Ubuntu, де shє dash), використовуйте stty -echoпідхід (який є неоптимальним, тому що він нічого не друкує ), оскільки readвбудований не підтримуватиме -sта -nпараметри.


Дякую, @Dylan Я щойно зрозумів, що код для видалення останнього символу з набраного паролем поки що можна спростити - див. Оновлення.
mklement0

3

Трохи відмінна від (але в основному подобається) відповіді @ lesmana

stty -echo
read password
stty echo

просто: приховати відлуння, щоб ваші речі показували відлуння


Чи можете ви пояснити, чому це краще, ніж відповідь @ lesmana ? Я бачу, що це простіше з меншими накладними витратами на черговий sttyдзвінок і з однією менш змінною на стеці - лише забираючи відлуння і лише відновлюючи відлуння. Чи можливий спосіб це може порушити інші sttyналаштування? Лесмана зберігає всі налаштування.
Джефф Пукетт

2

Ось варіант відповіді @ SiegeX, який працює з традиційною оболонкою Bourne (яка не підтримує +=завдання).

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

Традиційна оболонка Борна (або сумісна з POSIX) також не розпізнає [[ ... ]], а також readвбудований модуль не має -sта -nваріанти. dashНаприклад, ваш код не працюватиме . (Він буде працювати на платформах, де shнасправді є bash, наприклад, на OSX, де bashпри посиланні на них shпросто змінюється декілька варіантів за замовчуванням, але все ще підтримується більшість башизмів.)
mklement0,

2
Дякую за відгук, перейшов на одиночний, [але явно не дуже багато, що я можу зробити, якщо ваших readнедоліків немає.
трійчатка

2

Мені завжди подобається використовувати символи втечі Ansi:

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

8m робить текст невидимим і 0m скидає текст на "нормальний". -Е робить можливими втечі Ансі.

Єдине застереження полягає в тому, що ви все ще можете скопіювати та вставити текст, який там є, тому вам, мабуть, не слід використовувати це, якщо ви дійсно хочете захистити.

Він просто дозволяє людям не дивитися на ваші паролі, коли ви вводите їх. Просто не залишайте комп'ютер згодом. :)


ПРИМІТКА:

Вищезазначене є платформою незалежною, доки вона підтримує послідовності відходу Ansi.

Однак для іншого рішення Unix ви можете просто сказати, readщоб не повторювати персонажів ...

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

1

Отримати ім’я користувача та пароль

Зробіть більш зрозумілим для читання, але поставте його на кращу позицію над екраном

#!/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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.