How to Properly Escape Exclamation Marks (!) in Bash Passwords and MySQL Commands


2 views

When dealing with passwords containing exclamation marks in Bash, you're actually encountering Bash's history expansion feature. The ! character triggers this behavior, which attempts to substitute commands from your history rather than treating it as a literal character.

# This fails because Bash tries to find command 'two' in history
$ mysql -umyuser -pone_@&!two

The standard escaping approach using backslashes often fails in this context because:

  1. Bash performs history expansion before parsing quotes and escapes
  2. The shell interprets the backslash differently in interactive vs non-interactive modes
# This still fails because history expansion occurs first
$ mysql -umyuser -pone_@&\\!two

Option 1: Single Quotes for the Entire Password

The most reliable method is wrapping the entire password in single quotes:

$ mysql -umyuser -p'one_@&!two'

Option 2: Disable History Expansion Temporarily

For scripts or aliases where quotes aren't feasible:

# In .bashrc or .bash_aliases
alias mysqllogin='set +H; mysql -umyuser -pone_@&!two; set -H'

Option 3: Use Double Quotes with Careful Escaping

When variables are involved:

PASSWORD="one_@&\!two"
mysql -umyuser -p"$PASSWORD"

The most robust solution for your .bashrc would be:

alias mysqlprod='mysql -umyuser --password="one_@&!two"'

For better security, consider using MySQL option files:

[client]
user = myuser
password = one_@&!two

Then connect simply with:

$ mysql --defaults-file=/path/to/my.cnf

When dealing with exclamation marks in passwords within Bash commands, you're encountering Bash's history expansion feature. The shell interprets ! as a special character that references previous commands unless properly escaped.

$ mysql -umyuser -pone_@&!two
-bash: !two: event not found

The standard backslash escape fails because Bash performs history expansion before processing escapes. This explains why your attempt with \! still resulted in an error:

$ mysql -umyuser -pone_@&\\!two
[1] 22242
-bash: !two: command not found

1. Single Quotes for the Entire Password

The most reliable method is wrapping the entire password in single quotes:

$ mysql -umyuser -p'one_@&!two'

2. Disabling History Expansion Temporarily

For scripts or aliases where quoting isn't practical:

$ set +H
$ mysql -umyuser -pone_@&!two
$ set -H  # Re-enable if needed

3. Double Escaping in .bashrc Aliases

For your specific .bashrc case:

alias mysqltest='mysql -umyuser -p"one_@&"'\\''!two'"'"

Using mysql_config_editor

A more secure approach than storing passwords in aliases:

$ mysql_config_editor set --login-path=mypath \
  --host=localhost --user=myuser --password
# Then enter the password (including !) at prompt
$ mysql --login-path=mypath

Environment Variables

Store the password in an environment variable:

export MYSQL_PWD='one_@&!two'
mysql -umyuser

While solving the escaping issue is important, consider that:

  • Password visibility in process listings can be a security risk
  • .bashrc entries may be visible to other users
  • MySQL's --defaults-file option provides better security

To verify your escaping works correctly without connecting to MySQL:

$ echo 'password_with_!mark'  # Test echo first
$ echo 'one_@&!two'           # Should output exactly as shown