{"id":752,"date":"2025-08-20T13:19:04","date_gmt":"2025-08-20T05:19:04","guid":{"rendered":"https:\/\/play.datalude.com\/blog\/?p=752"},"modified":"2025-08-20T13:19:18","modified_gmt":"2025-08-20T05:19:18","slug":"bash-script-for-managing-htaccess-files","status":"publish","type":"post","link":"https:\/\/play.datalude.com\/blog\/2025\/08\/bash-script-for-managing-htaccess-files\/","title":{"rendered":"Bash script for managing htaccess files"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Had to create this for a less technical user to manage htaccess, so thought I'd share. Your mileage may vary. <br><br>Change the location of the default htpass file in the config. You can supply another location in mid script if you have more than one. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/bash<br><br># Configuration<br>HTACCESS_FILE_DEFAULT=\"htpass_test\"<br><br># Function to display a list of users<br>display_users() {<br>  echo \"--- Existing Users ---\"<br>  # Grep for lines that don't start with # and aren't empty<br>  # Then use cut to show only the username before the colon, and number the lines<br>  grep -vE '^(#|$)' \"$HTACCESS_FILE\" | cut -d':' -f1 | cat -n<br>  echo \"----------------------\"<br>}<br><br># Function to perform a backup<br>backup_file() {<br>  if [ -f \"$HTACCESS_FILE\" ]; then<br>    cp \"$HTACCESS_FILE\" \"$HTACCESS_FILE.bak\"<br>    echo \"Backup created at $HTACCESS_FILE.bak\"<br>  else<br>    echo \"No .htpasswd file to back up.\"<br>  fi<br>}<br><br># Function to generate a random password<br>generate_password() {<br>    tr -cd '[:alnum:]' &lt; \/dev\/urandom | head -c 12<br>}<br><br># --- Main Script ---<br><br># Select the .htpasswd file<br>read -p \"Enter .htpasswd file (default: $HTACCESS_FILE_DEFAULT): \" HTACCESS_FILE<br>HTACCESS_FILE=${HTACCESS_FILE:-$HTACCESS_FILE_DEFAULT}<br><br># Ensure the file exists, exit if not<br>if [ ! -f \"$HTACCESS_FILE\" ]; then<br>  echo \"Password file doesn't exist\"<br>  exit 1<br>fi<br><br># Main menu loop<br>while true; do<br>  echo \"Do you want to:\"<br>  echo \"a) Add a user\"<br>  echo \"r) Remove a user\"<br>  echo \"u) Update a user's password\"<br>  echo \"l) List users\"<br>  echo \"q) Quit\"<br>  read -p \"Enter your choice: \" choice<br><br>  case \"$choice\" in<br>    a)<br>      read -p \"Enter username to add: \" username<br>      # Check if the username already exists<br>      if grep -q \"^$username:\" \"$HTACCESS_FILE\"; then<br>        echo \"Error: User '$username' already exists. Use the 'u' option to update their password.\"<br>      else<br>        suggested_password=$(generate_password)<br>        read -p \"Enter password for '$username' (or press Enter to use suggested: $suggested_password): \" password<br>        password=${password:-$suggested_password}<br>        <br>        backup_file<br>        htpasswd -b \"$HTACCESS_FILE\" \"$username\" \"$password\"<br>        echo \"User '$username' added.\"<br>      fi<br>      ;;<br>    r)<br>      display_users<br>      read -p \"Enter reference number of user to remove: \" ref<br>      # Use grep and sed to find the line number and get the username<br>      username_to_remove=$(grep -vE '^(#|$)' \"$HTACCESS_FILE\" | sed -n \"${ref}p\" | cut -d':' -f1)<br><br>      if [ -z \"$username_to_remove\" ]; then<br>        echo \"Invalid reference number.\"<br>      else<br>        backup_file<br>        # Create a temp file without the user and then replace the original<br>        grep -v \"^$username_to_remove:\" \"$HTACCESS_FILE\" > \"$HTACCESS_FILE.tmp\" &amp;&amp; mv \"$HTACCESS_FILE.tmp\" \"$HTACCESS_FILE\"<br>        echo \"User '$username_to_remove' removed.\"<br>      fi<br>      ;;<br>    u)<br>      display_users<br>      read -p \"Enter reference number of user to update: \" ref<br>      username_to_update=$(grep -vE '^(#|$)' \"$HTACCESS_FILE\" | sed -n \"${ref}p\" | cut -d':' -f1)<br><br>      if [ -z \"$username_to_update\" ]; then<br>        echo \"Invalid reference number.\"<br>      else<br>        suggested_password=$(generate_password)<br>        read -p \"Enter new password for '$username_to_update' (or press Enter to use suggested: $suggested_password): \" password<br>        password=${password:-$suggested_password}<br><br>        backup_file<br>        htpasswd -b \"$HTACCESS_FILE\" \"$username_to_update\" \"$password\"<br>        echo \"Password for user '$username_to_update' updated.\"<br>      fi<br>      ;;<br>    l)<br>      display_users<br>      ;;<br>    q)<br>      echo \"Exiting.\"<br>      exit 0<br>      ;;<br>    *)<br>      echo \"Invalid option. Please try again.\"<br>      ;;<br>  esac<br><br>  echo # Add a newline for spacing<br>done<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Had to create this for a less technical user to manage htaccess, so thought I'd share. Your mileage may vary. Change the location of the default htpass file in the config. You can supply another location in mid script if you have more than one. #!\/bin\/bash# ConfigurationHTACCESS_FILE_DEFAULT=\"htpass_test\"# Function to display a list of usersdisplay_users() { &#8230; <a title=\"Bash script for managing htaccess files\" class=\"read-more\" href=\"https:\/\/play.datalude.com\/blog\/2025\/08\/bash-script-for-managing-htaccess-files\/\" aria-label=\"Read more about Bash script for managing htaccess files\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","footnotes":""},"categories":[144,4,5],"tags":[],"class_list":["post-752","post","type-post","status-publish","format-standard","hentry","category-bash-script","category-linux","category-security"],"_links":{"self":[{"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/posts\/752","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/comments?post=752"}],"version-history":[{"count":1,"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/posts\/752\/revisions"}],"predecessor-version":[{"id":753,"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/posts\/752\/revisions\/753"}],"wp:attachment":[{"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/media?parent=752"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/categories?post=752"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/play.datalude.com\/blog\/wp-json\/wp\/v2\/tags?post=752"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}