Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save mrEckendonk/59e7172b2dba1362aac7455a5829c44c to your computer and use it in GitHub Desktop.

Select an option

Save mrEckendonk/59e7172b2dba1362aac7455a5829c44c to your computer and use it in GitHub Desktop.
Local Development Create Apache Site - WordPress - Remove - Fedora tested. (tailscale not yet completed)
#!/bin/bash
# Configuration
RECORD_FILE="/root/.site_manager_records"
DB_ROOT_USER="root"
DB_ROOT_PASS="" # Leave empty if no password
TAILSCALE_TAILNET="banded-arctic.ts.net"
SERVE_RECORD="/root/.tailscale_serve_status"
# Check for root privileges
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root. Use sudo." >&2
exit 1
fi
# Install dependencies if missing
install_dependencies() {
if ! command -v httpd &> /dev/null || ! command -v openssl &> /dev/null; then
echo "Installing required packages..."
dnf install -y httpd mod_ssl openssl policycoreutils-python-utils firewalld tailscale jq mod_proxy_html
systemctl enable --now httpd firewalld
firewall-cmd --permanent --add-service={http,https}
firewall-cmd --reload
# Enable Apache proxy modules
cat > /etc/httpd/conf.modules.d/00-proxy.conf <<EOF
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_html_module modules/mod_proxy_html.so
LoadModule headers_module modules/mod_headers.so
LoadModule ssl_module modules/mod_ssl.so
EOF
fi
}
# Initialize dependencies
install_dependencies
# Domain validation functions
domain_exists() {
local domain="$1"
[[ -f "/etc/httpd/conf.d/${domain}.conf" ]] && return 0
return 1
}
ssl_exists() {
local domain="$1"
[[ -f "/etc/pki/tls/certs/${domain}/${domain}.crt" && \
-f "/etc/pki/tls/private/${domain}/${domain}.key" ]] && return 0
return 1
}
wp_site_exists() {
local domain="$1"
grep -q "^${domain}:" "$RECORD_FILE" 2>/dev/null
return $?
}
print_divider() {
echo "============================================================"
}
# Database functions
create_db_user() {
local domain="$1"
local db_name="wp_${domain//./_}"
local db_user="${db_name}_user"
local db_pass=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16)
# Create database and user
mysql -u"$DB_ROOT_USER" ${DB_ROOT_PASS:+-p"$DB_ROOT_PASS"} <<EOF
CREATE DATABASE IF NOT EXISTS \`${db_name}\`;
CREATE USER '${db_user}'@'localhost' IDENTIFIED BY '${db_pass}';
GRANT ALL PRIVILEGES ON \`${db_name}\`.* TO '${db_user}'@'localhost';
FLUSH PRIVILEGES;
EOF
# Add to record file
echo "${domain}:${db_name}:${db_user}:${db_pass}" >> "$RECORD_FILE"
chmod 600 "$RECORD_FILE"
echo "$db_name:$db_user:$db_pass"
}
remove_db_user() {
local domain="$1"
if wp_site_exists "$domain"; then
local record=$(grep "^${domain}:" "$RECORD_FILE")
IFS=':' read -ra parts <<< "$record"
local db_name="${parts[1]}"
local db_user="${parts[2]}"
mysql -u"$DB_ROOT_USER" ${DB_ROOT_PASS:+-p"$DB_ROOT_PASS"} <<EOF
DROP DATABASE IF EXISTS \`${db_name}\`;
DROP USER IF EXISTS '${db_user}'@'localhost';
FLUSH PRIVILEGES;
EOF
# Remove from record file
sed -i "/^${domain}:/d" "$RECORD_FILE"
fi
}
# Site management functions
create_basic_site() {
local domain="$1"
# Setup directories
DOC_ROOT="/var/www/${domain}/public_html"
SSL_CERT_DIR="/etc/pki/tls/certs/${domain}"
SSL_KEY_DIR="/etc/pki/tls/private/${domain}"
mkdir -p "$DOC_ROOT" "$SSL_CERT_DIR" "$SSL_KEY_DIR"
chown -R apache:apache "/var/www/${domain}"
chmod -R 755 "/var/www/${domain}"
# Generate SSL certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "${SSL_KEY_DIR}/${domain}.key" \
-out "${SSL_CERT_DIR}/${domain}.crt" \
-subj "/CN=${domain}" \
-addext "subjectAltName=DNS:${domain},DNS:www.${domain}" 2>/dev/null
# Set permissions
chmod 400 "${SSL_KEY_DIR}/${domain}.key"
chmod 444 "${SSL_CERT_DIR}/${domain}.crt"
chown root:apache "${SSL_KEY_DIR}/${domain}.key"
# Configure SELinux
semanage fcontext -a -t httpd_sys_content_t "/var/www/${domain}(/.*)?" 2>/dev/null
semanage fcontext -a -t cert_t "/etc/pki/tls/certs/${domain}(/.*)?" 2>/dev/null
semanage fcontext -a -t cert_t "/etc/pki/tls/private/${domain}(/.*)?" 2>/dev/null
restorecon -Rv "/var/www/${domain}" /etc/pki/tls 2>/dev/null
# Create Apache config
CONF_FILE="/etc/httpd/conf.d/${domain}.conf"
cat > "$CONF_FILE" << EOF
<VirtualHost *:80>
ServerName ${domain}
ServerAlias www.${domain}
Redirect permanent / https://${domain}/
</VirtualHost>
<VirtualHost *:443>
ServerName ${domain}
ServerAlias www.${domain}
SSLEngine on
SSLCertificateFile ${SSL_CERT_DIR}/${domain}.crt
SSLCertificateKeyFile ${SSL_KEY_DIR}/${domain}.key
DocumentRoot ${DOC_ROOT}
ErrorLog /var/log/httpd/${domain}_error.log
CustomLog /var/log/httpd/${domain}_access.log combined
<Directory ${DOC_ROOT}>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
# Proxy settings for Tailscale
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
</VirtualHost>
EOF
# Update /etc/hosts
if ! grep -q "$domain" /etc/hosts; then
echo -e "127.0.0.1\t${domain} www.${domain}" >> /etc/hosts
fi
}
create_test_page() {
local domain="$1"
local doc_root="/var/www/${domain}/public_html"
cat > "${doc_root}/index.html" << EOF
<!DOCTYPE html>
<html>
<head>
<title>${domain} Works!</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding-top: 50px;
background-color: #f0f8ff;
}
.success {
color: #2ecc71;
font-size: 3em;
}
.info {
color: #3498db;
font-size: 1.2em;
}
</style>
</head>
<body>
<div class="success">&#10004;</div>
<h1>${domain} is working!</h1>
<p class="info">SSL/TLS is properly configured on this server.</p>
<p class="info">&#128640; Automagic HTTPS Setup Complete!</p>
</body>
</html>
EOF
}
add_site() {
while true; do
read -p "Enter domain name (e.g., example.local): " DOMAIN
if [ -z "$DOMAIN" ]; then
echo "Domain name cannot be empty!" >&2
continue
fi
if domain_exists "$DOMAIN"; then
echo -e "\033[1;31mERROR: Domain '$DOMAIN' already exists!\033[0m" >&2
return 1
fi
print_divider
echo "You are about to create:"
echo " • Domain: $DOMAIN"
echo " • Document root: /var/www/${DOMAIN}/public_html"
echo " • SSL certificate: /etc/pki/tls/certs/${DOMAIN}"
echo " • Apache config: /etc/httpd/conf.d/${DOMAIN}.conf"
print_divider
read -p "Continue? (Y/n) " confirm
confirm=${confirm:-Y}
if [[ "$confirm" =~ ^[Yy]$ ]]; then
break
else
echo "Operation cancelled."
return
fi
done
create_basic_site "$DOMAIN"
create_test_page "$DOMAIN"
# Validate and restart Apache
if httpd -t; then
systemctl restart httpd
print_divider
echo -e "\033[1;32mSUCCESS: Site ${DOMAIN} added!\033[0m"
echo " • Access: https://${DOMAIN}"
echo " • Document root: /var/www/${DOMAIN}/public_html"
echo " • SSL Cert: /etc/pki/tls/certs/${DOMAIN}/${DOMAIN}.crt"
print_divider
else
echo -e "\033[1;31mERROR: Apache configuration test failed. Site not activated.\033[0m" >&2
fi
}
add_wordpress_site() {
while true; do
read -p "Enter domain name for WordPress site (e.g., blog.local): " DOMAIN
if [ -z "$DOMAIN" ]; then
echo "Domain name cannot be empty!" >&2
continue
fi
if domain_exists "$DOMAIN"; then
echo -e "\033[1;31mERROR: Domain '$DOMAIN' already exists!\033[0m" >&2
return 1
fi
if wp_site_exists "$DOMAIN"; then
echo -e "\033[1;31mERROR: WordPress site '$DOMAIN' already exists!\033[0m" >&2
return 1
fi
print_divider
echo "You are about to create a WordPress site:"
echo " • Domain: $DOMAIN"
echo " • Document root: /var/www/${DOMAIN}/public_html"
echo " • SSL certificate: /etc/pki/tls/certs/${DOMAIN}"
echo " • Apache config: /etc/httpd/conf.d/${DOMAIN}.conf"
echo " • New database and MySQL user will be created"
print_divider
read -p "Continue? (Y/n) " confirm
confirm=${confirm:-Y}
if [[ "$confirm" =~ ^[Yy]$ ]]; then
break
else
echo "Operation cancelled."
return
fi
done
# Create basic site structure
create_basic_site "$DOMAIN"
# Download and install WordPress
DOC_ROOT="/var/www/${DOMAIN}/public_html"
WP_URL="https://wordpress.org/latest.tar.gz"
echo "Downloading WordPress..."
wget -q -O /tmp/wordpress.tar.gz "$WP_URL"
echo "Extracting WordPress..."
tar -xzf /tmp/wordpress.tar.gz -C /tmp
cp -a /tmp/wordpress/* "$DOC_ROOT"
chown -R apache:apache "$DOC_ROOT"
rm -rf /tmp/wordpress /tmp/wordpress.tar.gz
# Create database and user
echo "Creating database and user..."
db_creds=$(create_db_user "$DOMAIN")
IFS=':' read -ra CREDS <<< "$db_creds"
local db_name="${CREDS[0]}"
local db_user="${CREDS[1]}"
local db_pass="${CREDS[2]}"
# Create wp-config.php
cp "$DOC_ROOT/wp-config-sample.php" "$DOC_ROOT/wp-config.php"
sed -i "s/database_name_here/$db_name/" "$DOC_ROOT/wp-config.php"
sed -i "s/username_here/$db_user/" "$DOC_ROOT/wp-config.php"
sed -i "s/password_here/$db_pass/" "$DOC_ROOT/wp-config.php"
# Add security keys
SECRET_KEYS=$(wget -q -O - https://api.wordpress.org/secret-key/1.1/salt/)
sed -i "/AUTH_KEY/s/put your unique phrase here/$SECRET_KEYS/" "$DOC_ROOT/wp-config.php"
# Add proxy settings to wp-config.php
echo "Adding proxy configuration to WordPress..."
cat >> "$DOC_ROOT/wp-config.php" << EOF
/* Added by Site Manager for Tailscale */
if (isset(\$_SERVER['HTTP_X_FORWARDED_PROTO']) && \$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
\$_SERVER['HTTPS'] = 'on';
}
EOF
# Validate and restart Apache
if httpd -t; then
systemctl restart httpd
print_divider
echo -e "\033[1;32mSUCCESS: WordPress site ${DOMAIN} created!\033[0m"
echo " • Access: https://${DOMAIN}"
echo " • WordPress Admin: https://${DOMAIN}/wp-admin"
echo " • Database: $db_name"
echo " • Database User: $db_user"
echo -e " • Database Password: \033[1;34m$db_pass\033[0m"
echo " • Document root: $DOC_ROOT"
print_divider
echo "Note: Save these database credentials for future reference"
print_divider
else
echo -e "\033[1;31mERROR: Apache configuration test failed. Site not activated.\033[0m" >&2
fi
}
renew_ssl() {
while true; do
read -p "Enter domain name to renew SSL: " DOMAIN
if [ -z "$DOMAIN" ]; then
echo "Domain name cannot be empty!" >&2
continue
fi
if ! domain_exists "$DOMAIN"; then
echo -e "\033[1;31mERROR: Domain '$DOMAIN' does not exist!\033[0m" >&2
return 1
fi
if ! ssl_exists "$DOMAIN"; then
echo -e "\033[1;31mERROR: SSL certificate for '$DOMAIN' not found!\033[0m" >&2
return 1
fi
print_divider
echo "You are about to RENEW SSL for:"
echo " • Domain: $DOMAIN"
echo " • New expiration: $(date -d "+365 days" +"%Y-%m-%d")"
echo " • Old certificate will be backed up"
print_divider
read -p "Continue? (Y/n) " confirm
confirm=${confirm:-Y}
if [[ "$confirm" =~ ^[Yy]$ ]]; then
break
else
echo "Operation cancelled."
return
fi
done
SSL_CERT_DIR="/etc/pki/tls/certs/${DOMAIN}"
SSL_KEY_DIR="/etc/pki/tls/private/${DOMAIN}"
# Backup old certs
TIMESTAMP=$(date +%Y%m%d%H%M%S)
BACKUP_DIR="/etc/pki/tls/backups/${DOMAIN}"
mkdir -p "$BACKUP_DIR"
cp -v "${SSL_CERT_DIR}/${DOMAIN}.crt" "${BACKUP_DIR}/${DOMAIN}.crt.bak-${TIMESTAMP}"
cp -v "${SSL_KEY_DIR}/${DOMAIN}.key" "${BACKUP_DIR}/${DOMAIN}.key.bak-${TIMESTAMP}"
echo "Created backups:"
echo " • ${BACKUP_DIR}/${DOMAIN}.crt.bak-${TIMESTAMP}"
echo " • ${BACKUP_DIR}/${DOMAIN}.key.bak-${TIMESTAMP}"
# Generate new certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "${SSL_KEY_DIR}/${DOMAIN}.key" \
-out "${SSL_CERT_DIR}/${DOMAIN}.crt" \
-subj "/CN=${DOMAIN}" \
-addext "subjectAltName=DNS:${DOMAIN},DNS:www.${DOMAIN}" 2>/dev/null
# Set permissions
chmod 400 "${SSL_KEY_DIR}/${DOMAIN}.key"
chmod 444 "${SSL_CERT_DIR}/${DOMAIN}.crt"
chown root:apache "${SSL_KEY_DIR}/${DOMAIN}.key"
# Restart Apache
if httpd -t; then
systemctl restart httpd
print_divider
echo -e "\033[1;32mSUCCESS: SSL certificate renewed for ${DOMAIN}!\033[0m"
echo " • New expiration: $(openssl x509 -in ${SSL_CERT_DIR}/${DOMAIN}.crt -noout -enddate | cut -d= -f2)"
echo " • Backup location: ${BACKUP_DIR}"
print_divider
else
echo -e "\033[1;31mERROR: Apache configuration test failed. Reverting changes.\033[0m" >&2
cp -v "${BACKUP_DIR}/${DOMAIN}.crt.bak-${TIMESTAMP}" "${SSL_CERT_DIR}/${DOMAIN}.crt"
cp -v "${BACKUP_DIR}/${DOMAIN}.key.bak-${TIMESTAMP}" "${SSL_KEY_DIR}/${DOMAIN}.key"
systemctl restart httpd
fi
}
start_tailscale_serve() {
# Check Tailscale status
if ! tailscale status &> /dev/null; then
echo -e "\033[1;31mERROR: Tailscale not running. Start with 'tailscale up' first.\033[0m" >&2
return 1
fi
# Check for existing serve
if [ -f "$SERVE_RECORD" ]; then
echo -e "\033[1;33mWARNING: An active Tailscale Serve session already exists!\033[0m"
IFS=':' read -r ACTIVE_DOMAIN ACTIVE_PORT < "$SERVE_RECORD"
echo " • Currently serving: $ACTIVE_DOMAIN on port $ACTIVE_PORT"
read -p "Stop current session and start new one? (y/N) " confirm
confirm=${confirm:-N}
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "Operation cancelled."
return
fi
# Stop existing serve
tailscale serve --https=443 off
rm -f "$SERVE_RECORD"
fi
# Get site domain
read -p "Enter domain name to expose via Tailscale Serve: " DOMAIN
if [ -z "$DOMAIN" ]; then
echo "Domain name cannot be empty!" >&2
return 1
fi
if ! domain_exists "$DOMAIN"; then
echo -e "\033[1;31mERROR: Domain '$DOMAIN' does not exist!\033[0m" >&2
return 1
fi
# Get FQDN
NODE_NAME=$(tailscale status --json | jq -r '.Self.HostName')
if [ -z "$NODE_NAME" ]; then
echo -e "\033[1;31mERROR: Could not get Tailscale node name\033[0m" >&2
return 1
fi
FQDN="${NODE_NAME}.${TAILSCALE_TAILNET}"
print_divider
echo "You are about to expose:"
echo " • Local Site: https://${DOMAIN}"
echo " • Public URL: https://${FQDN} (via Tailscale Serve)"
echo " • This will make your site accessible to your tailnet"
print_divider
read -p "Continue? (y/N) " confirm
confirm=${confirm:-N}
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "Operation cancelled."
return
fi
# Start Tailscale Serve with HTTPS
echo "Starting Tailscale Serve with HTTPS..."
tailscale serve --bg --https=443 "https://${DOMAIN}"
# Record status
echo "${DOMAIN}:443" > "$SERVE_RECORD"
chmod 600 "$SERVE_RECORD"
print_divider
echo -e "\033[1;32mSUCCESS: Tailscale Serve started!\033[0m"
echo " • Local Site: https://${DOMAIN}"
echo " • Public URL: \033[1;34mhttps://${FQDN}\033[0m"
echo " • Tailscale Status: $(tailscale serve status)"
print_divider
echo "Note: All links will work correctly with HTTPS"
echo " Certificate is automatically managed by Tailscale"
print_divider
}
stop_tailscale_serve() {
if [ ! -f "$SERVE_RECORD" ]; then
echo -e "\033[1;31mERROR: No active Tailscale Serve session found!\033[0m" >&2
return 1
fi
# Get current serve info
IFS=':' read -r DOMAIN PORT < "$SERVE_RECORD"
NODE_NAME=$(tailscale status --json | jq -r '.Self.HostName')
FQDN="${NODE_NAME}.${TAILSCALE_TAILNET}"
print_divider
echo "Active Tailscale Serve:"
echo " • Domain: $DOMAIN"
echo " • Port: $PORT"
echo " • Public URL: https://${FQDN}"
print_divider
read -p "Stop this Tailscale Serve session? (y/N) " confirm
confirm=${confirm:-N}
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "Operation cancelled."
return
fi
# Stop Tailscale Serve
tailscale serve --https=443 off
rm -f "$SERVE_RECORD"
print_divider
echo -e "\033[1;32mSUCCESS: Tailscale Serve stopped!\033[0m"
echo " • Domain: $DOMAIN is no longer exposed"
print_divider
}
remove_site() {
while true; do
read -p "Enter domain name to remove: " DOMAIN
if [ -z "$DOMAIN" ]; then
echo "Domain name cannot be empty!" >&2
continue
fi
if ! domain_exists "$DOMAIN"; then
echo -e "\033[1;31mERROR: Domain '$DOMAIN' does not exist!\033[0m" >&2
return 1
fi
is_wordpress=false
if wp_site_exists "$DOMAIN"; then
is_wordpress=true
wp_info=$(grep "^${DOMAIN}:" "$RECORD_FILE")
IFS=':' read -ra parts <<< "$wp_info"
db_name="${parts[1]}"
db_user="${parts[2]}"
fi
print_divider
echo "WARNING: You are about to PERMANENTLY remove:"
echo " • Domain: $DOMAIN"
echo " • Document root: /var/www/${DOMAIN}"
echo " • SSL certificates"
echo " • Apache configuration"
echo " • Log files"
if $is_wordpress; then
echo " • WordPress database: $db_name"
echo " • Database user: $db_user"
fi
print_divider
read -p "ARE YOU SURE? This cannot be undone! (y/N) " confirm
confirm=${confirm:-N}
if [[ "$confirm" =~ ^[Yy]$ ]]; then
break
else
echo "Operation cancelled."
return
fi
done
# Remove WordPress database if exists
if wp_site_exists "$DOMAIN"; then
echo "Removing WordPress database..."
remove_db_user "$DOMAIN"
fi
# Remove Apache configs
rm -vf "/etc/httpd/conf.d/${DOMAIN}.conf"
# Remove document root
DOC_ROOT="/var/www/${DOMAIN}"
rm -rvf "$DOC_ROOT"
# Remove SSL certs
SSL_CERT_DIR="/etc/pki/tls/certs/${DOMAIN}"
SSL_KEY_DIR="/etc/pki/tls/private/${DOMAIN}"
rm -rvf "$SSL_CERT_DIR" "$SSL_KEY_DIR"
# Remove log files
rm -vf "/var/log/httpd/${DOMAIN}_access.log" "/var/log/httpd/${DOMAIN}_error.log"
# Remove hosts entry
sed -i "/${DOMAIN}/d" /etc/hosts
# Restart Apache
if httpd -t; then
systemctl restart httpd
print_divider
echo -e "\033[1;32mSUCCESS: Site ${DOMAIN} has been completely removed.\033[0m"
echo " • All related files and configurations deleted"
if $is_wordpress; then
echo " • Database '$db_name' and user '$db_user' removed"
fi
print_divider
# Disable Tailscale Serve if it was active
if [ -f "$SERVE_RECORD" ]; then
IFS=':' read -r SERVE_DOMAIN _ < "$SERVE_RECORD"
if [ "$SERVE_DOMAIN" == "$DOMAIN" ]; then
echo "Disabling Tailscale Serve for ${DOMAIN}..."
tailscale serve off
rm -f "$SERVE_RECORD"
echo "Tailscale Serve disabled"
print_divider
fi
fi
else
echo -e "\033[1;31mERROR: Apache configuration test failed. Manual cleanup needed.\033[0m" >&2
fi
}
# Main menu
while true; do
echo -e "\n\033[1;34mApache Site Management\033[0m"
print_divider
echo "1. Add a new site"
echo "2. Renew SSL certificate"
echo "3. Remove a site"
echo "4. Add a WordPress site"
echo "5. Start Tailscale Serve"
echo "6. Stop Tailscale Serve"
echo "7. Quit"
print_divider
# Show Tailscale status
if tailscale status &> /dev/null; then
TS_STATUS=$(tailscale status --json | jq -r '.BackendState')
TS_FQDN=$(tailscale status --json | jq -r '.Self.HostName + "." + .MagicDNSSuffix')
echo -e "Tailscale: \033[1;32m$TS_STATUS\033[0m | FQDN: \033[1;34m$TS_FQDN\033[0m"
# Show active serve status
if [ -f "$SERVE_RECORD" ]; then
IFS=':' read -r SERVE_DOMAIN SERVE_PORT < "$SERVE_RECORD"
echo -e "Active Serve: \033[1;33m$SERVE_DOMAIN\033[0m at \033[1;33mhttps://$TS_FQDN\033[0m"
else
echo -e "Active Serve: \033[1;31mNone\033[0m"
fi
print_divider
fi
read -p "Choose an option (1-7): " choice
case $choice in
1) add_site;;
2) renew_ssl;;
3) remove_site;;
4) add_wordpress_site;;
5) start_tailscale_serve;;
6) stop_tailscale_serve;;
7) echo "Exiting..."; exit 0;;
*) echo -e "\033[1;31mInvalid option. Please choose 1-7.\033[0m";;
esac
read -n 1 -s -r -p "Press any key to continue..."
clear
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment