Bash Script - Passing command string between variables
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Bash Script - Passing command string between variables
I'm writing a script to quickly mount/unmount specific/all usb devices I plug into my laptop. I know this is a buggy way to do essentially reinvent the wheel, but I'm just practicing.
What I'd like to do: Initially, I wanted to do what I thought was a simple thing. I wanted to attach a string to a command executed further down in the script. For example appending "MNTOPT_NTFS='-o "umask=002,utf8" -t ntfs3g -L'" to a mount command executed in a function. It ends up looking like "mount $MNTOPT_NTFS $2 $MNTDIR/$2" or something to that effect. Mount did not seem to parse the appended string correctly.
Next, I tried to store the entire mount command in a variable to be parsed later in my mount function. The problem is that the variables present within the $MNTCMD variable are not correctly parsed when $MNTCMD is called. Have a look at the script following the "vfat" condition.
Anyway, here is the script:
Code:
########################################
#!/bin/bash
#Quick Unmount Script
#Mount or Unmount USB drives by label
########################################
#Variables
#############
PROG="qmnt"
MNTDIR="/media"
DLIST=$(find $MNTDIR -mindepth 1 -maxdepth 1 -type d | awk -F"/" '{print$3}' 2> /dev/null)
MNTOPT_EXT2="-L"
MNTOPT_EXT3="-L"
MNTOPT_EXT4="-L"
MNTOPT_VFAT="-o umask=000 -L"
MNTOPT_NTFS="-o umask=000,utf8 -t ntfs-3g -L"
# Make sure only root can run our script
#########################################
if [[ $EUID -ne 0 ]]; then
echo -ne "This script must be run as root. \n" 1>&2
exit 1
fi
#Help Function
####################
function help() {
echo -ne "\n----------------------------------------------------------------\n"
echo -ne " QUICK (U)MOUNT \n"
echo -ne "----------------------------------------------------------------\n"
echo -ne "Usage: $PROG [-OPTION] [DEVICE LABEL]\n"
echo -ne "----------------------------------------------------------------\n"
echo -ne "Options: |\n"
echo -ne " | [-a], [--all] -> Unmount ALL Mounted USB Drives\n"
echo -ne " | [-l], [--list] -> List Connected USB Devices\n"
echo -ne " | [-m], [--mount] -> Mount Drive to $MNTDIR By Label\n"
echo -ne " | [-u], [--unmount] -> Unmount Drive By Label\n"
echo -ne "----------------------------------------------------------------\n"
echo -ne "*Simply input the device's label to mount/unmount it.\n\n"
exit
}
#Determine Mount Options
#############################
function get_opts() {
ID=$(blkid -L $1)
FS=$(blkid -o value -s TYPE $ID)
if [ "$FS" == "ext2" ]; then
MNTOPT=("$MNTOPT_EXT2")
elif [ "$FS" == "ext3" ]; then
MNTOPT=("$MNTOPT_EXT3")
elif [ "$FS" == "ext4" ]; then
MNTOPT=("$MNTOPT_EXT4")
elif [ "$FS" == "vfat" ]; then
MNTOPT=("$MNTOPT_VFAT")
elif [ "$FS" == "ntfs" ]; then
MNTOPT=("$MNTOPT_NTFS")
elif [ "$FS" ]; then
echo -ne "Mount options undefined. Please edit the script."
MNTOPT=("")
fi
}
#List Connected USB Devices
#############################
function list() {
for udi in $(/usr/bin/hal-find-by-capability --capability storage)
do
device=$(hal-get-property --udi $udi --key block.device)
vendor=$(hal-get-property --udi $udi --key storage.vendor)
model=$(hal-get-property --udi $udi --key storage.model)
if [[ $(hal-get-property --udi $udi --key storage.bus) = "usb" ]]
then
parent_udi=$(hal-find-by-property --key block.storage_device --string $udi)
mount=$(hal-get-property --udi $parent_udi --key volume.mount_point)
label=$(hal-get-property --udi $parent_udi --key volume.label)
media_size=$(hal-get-property --udi $udi --key storage.removable.media_size)
size=$(expr $media_size / \( 1024 \* 1024 \* 1024 \))
printf "$label $mount "${size}GB" $device \n\n"
fi
done
exit 0
}
#List Mounted USB Devices
##############################
function list_mounted() {
echo -ne "---------------------\n"
echo -ne "Mounted USB Devices:\n"
echo -ne "---------------------\n\n"
echo -ne "$DLIST\n\n"
}
#On device label error
############################
function wrongdir() {
echo -ne "\n---------------------\n"
echo -ne " QUICK (U)MOUNT \n"
echo -ne "---------------------\n\n"
echo -ne "Device name doesn't exist.\n"
echo -ne "Please verify your device is mounted and/or using that label.\n\n"
#Print Mounted Devices
list_mounted
exit $E_WDIR
}
#Unmount Function
##############################
function unmount() {
umount "$MNTDIR/$1" > /dev/null 2>&1
ST="$?"
if [ "$ST" == "0" ]; then
rmdir "$MNTDIR/$1"
echo -ne "\n---------------------\n"
echo -ne "<- Unmounted $1\n"
echo -ne "---------------------\n\n"
fi
}
#Mount Function
##############################
function qmount() {
CKDEV=$(blkid -L $1)
if [ ! -d "$MNTDIR/$1" ] && [ -n "$CKDEV" ]; then
mkdir "$MNTDIR/$1"
get_opts "$1"
mount ${MNTOPT[@]} "$1" "$MNTDIR/$1" > /dev/null 2>&1
ST="$?"
if [ "$ST" == "0" ]; then
echo -ne "\n-------------------------\n"
echo -ne "-> Mounted $1\n"
echo -ne "-------------------------\n\n"
else
echo -ne "\n-------------------------\n"
echo -ne "Error, Code $ST. Exiting.\n"
echo -ne "-------------------------\n\n"
rmdir "$MNTDIR/$1"
exit 1
fi
else
echo -ne "\nLabel $1 already mounted or does not exist.\n Please '$PROG -l' for a list of possible values\n\n"
fi
}
#Arguments & Conditional Actions
##################################
if [[ $# -eq 0 ]]; then
help
else
while [ $# -ne 0 ]; do
#Display Help
##############
if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
help
#List Connected Devices
#######################
elif [ "$1" == "-l" ] || [ "$1" == "--list" ]; then
echo -ne "\nListing Connected USB Devices...\n"
echo -ne "----------------------------------\n\n"
list
exit 0
#Unmount All Devices in $MNTDIR
###############################
elif [ "$1" == "-a" ] || [ "$1" == "--all" ]; then
for d in $DLIST; do
unmount "$d"
while [ "$ST" == "1" ]; do
echo -ne "\nError, Code $ST. The device "$d" is busy with the following process(es):\n"
echo -ne "--------------------------------------------------------------------\n\n"
fuser -vkim "$MNTDIR/$d"
echo -ne
unmount "$d"
done
if [ "$ST" != "0" ] && [ "$ST" != "1" ]; then
echo -ne "\nError, Code $ST. Exiting.\n"
exit 1
fi
sleep 2
done
exit 0
#Unmount Specified Device
#########################
elif [ "$1" == "-u" ] || [ "$1" == "--unmount" ]; then
if [ ! -d "$MNTDIR/$2" ]; then
wrongdir
else
unmount "$2"
while [ "$ST" == "1" ]; do
echo -ne "\nError, Code $ST. The device is busy with the following process(es):\n"
echo -ne "--------------------------------------------------------------------\n\n"
fuser -vkim "$MNTDIR/$2"
echo -ne
unmount "$2"
done
if [ "$ST" -gt "1" ]; then
echo -ne "\nError, Code $ST. Exiting.\n"
exit 1
fi
exit 0
fi
#Mount Specified Device
#######################
elif [ "$1" == "-m" ] || [ "$1" == "--mount" ]; then
qmount "$2"
exit 0
#Display Help if no args
########################
elif [ -n "$1" ]; then
help
fi
done
fi
exit 0
Here single quotes prevent shell expansion, hence $1 and $MNTDIR/$1 are interpreted literally. Actually you don't really need double quotes around the various option values. Something like
Here single quotes prevent shell expansion, hence $1 and $MNTDIR/$1 are interpreted literally. Actually you don't really need double quotes around the various option values. Something like
Code:
MNTCMD_VFAT="mount -o umask=002 -L $1 $MNTDIR/$1"
should be enough for your task.
That is how I had it initially. True, the single quotes prevent shell expansion. My reasoning for this, is I do not want bash to interpret the "$1" variable at that point in the script. If it does, it returns the option (in this case -m) and a resulting mount error. So, I wanted to carry the literal string to another part of the script where it could then be expanded. Is there a way to do this? I know I could just put all my code in the function, but I wanted to make it easier to edit by having all important variables at the top.
And the double quotes I just put around variables by habit to avoid white space problems.
Ok. I got the point. In function get_opts you can try to substitute:
Code:
MNTCMD=$MNTCMD_VFAT
with
Code:
MNTCMD=$(eval echo $MNTCMD_VFAT)
eval forces expansion, whereas the literal value of MNTCMD_VFAT would be preserved both by direct assignment and by a simple echo.
I had read about the 'eval echo' command. It comes very close, with a couple small issues. My complete static variable is:
Code:
MNTCMD_VFAT='mount -o umask=002 -L "$1" "$MNTDIR/$1" > /dev/null 2>&1'
##I then eval echo the variable in the get_opts function with:
MNTCMD=$(eval echo "$MNTCMD_VFAT")
##Finally, I call it in function qmount() with:
$MNTCMD
The problem is with the "> /dev/null 2>&1", the entire variable gets sucked into the null void and not just the output from mount as intended.
Second, I grab the return code from the mount command with a #? variable in the mount function. The return code ends up always being zero as it is that of the eval echo command and not mount.
Excellent use of quotes, there's a few missing here and there, but it's not like most scripts I see. There's also some usage of [ that could easily be replaced by [[ just in case.
While I switch things over to arrays (nice wiki btw), I'll ask another question. Anyone know of a creative way to aggregate a list of all mounted "usb" devices by label?
I am currently fiddling with a combination of "mount -l", "blkid", and the little hal script written above. Hal can print all devices on the usb bus (mounted or not and by block device, not partition by partition). Is the only way to do this by using some fancy awk commands in comparing all 3 outputs?
EDIT: Beautiful... Using a simple array, I was able to make it work as i originally intended, appending mount options to the mount command. So that seems to be working well. I've posted the updated script in my original post. Now the only thing that remains is trying to make a list of mounted usb devices.
The variable ST is never set so while is never executed and if is always true
The sigterm variable is actually set within the "unmount" function. It proceeds to be passed in the loop. It works well in tests, so I think it is good.
Quote:
Best as I can tell, this option is an infinite loop:
True. As you can see, every conceivable branch in the loop ends with an exit command. The loop never needs to be terminated. Perhaps this is bad practice, but I don't see an alternative w/ my limited experience.
Anyway, I whipped up another script and had a couple questions:
1. This time I used a case statement to handle parameters. My original intention was to use shift to cycle through a list of inputs by looping through the case statement. Instead I had to nest another loop for each applicable situation. The problem is the script failing if a text argument is shifted to $1 (as the case statement needs -a,-r,-l, etc). Other than my nested until loop, is there another solution to have $1 always remain the same, then have $3 become $2, $4 become $3, etc.? So I only want shift to work from $2 to $n.
2. I know this isn't exactly secure (plaintext default pass) but I'm not too worried about that. I just don't like having to call openssl. Is there an alternative built in?
VSFTP User Script
Code:
#!/bin/bash
#####################################
#VSFTPD User Add/Remove/List Script
#Requires:
# OpenSSL
#####################################
#Variables
##############
PROG="vsftp-users"
PUB_FTP="/home/ftp/public"
FTP_ROOT="/home/ftp"
PUB_MESSAGE=""$FTP_ROOT/.message""
PRIV_MESSAGE=""$FTP_ROOT/.priv_message""
PUB_PASS="public"
PRIV_PASS="privftp"
#Error Checking On
###################
set -e
#Root Test
##############
if [[ $EUID -ne 0 ]]; then
echo -ne "This script must be run as root. \n" 1>&2
exit 1
fi
#Check Requirements
#####################
REQCK=$(type -P openssl)
[[ -z "$REQCK" ]] && exit $ER_REQ
#Check Message Files
#####################
[[ ! -f "$PRIV_MESSAGE" ]] && echo "Welcome to my FTP." > "$PRIV_MESSAGE"
[[ ! -f "$PUB_MESSAGE" ]] && echo "Welcome to my FTP." > "$PUB_MESSAGE"
#Help Function
######################
function help() {
echo -ne "\n------------------------------------------------------------------\n"
echo -ne " VSFTP USER MANAGER \n"
echo -ne "------------------------------------------------------------------\n"
echo -ne "Usage: $PROG [-OPTION] [USER(S)]\n"
echo -ne "------------------------------------------------------------------\n"
echo -ne "Options: |\n"
echo -ne " | [-a], [--add] -> Add VSFTPD User(s)\n"
echo -ne " | [-ap], [--addpriv] -> Add Private VSFTPD User(s)\n"
echo -ne " | [-l], [--list] -> List VSFTPD Users\n"
echo -ne " | [-h], [--help] -> Print This Help Dialogue\n"
echo -ne " | [-r], [--remove] -> Remove VSFTPD User(s)\n"
echo -ne "------------------------------------------------------------------\n"
echo -ne "Description: Add/Remove User(s) to VSFTPD. You can list multiple \n"
echo -ne " users with the add and remove options separated by \n"
echo -ne " white space.\n\n"
exit 0
}
#Add User Function
######################
function add_user() {
if [[ "$PRIV" == "1" ]]; then
USER_HOME="$FTP_ROOT/$1"
mkdir "$USER_HOME"
useradd -d "$USER_HOME" -c "added as vsftpd user" -s "/sbin/nologin" -p $(openssl passwd -crypt "$PRIV_PASS") "$1"
cp "$PRIV_MESSAGE" "$USER_HOME"
chown -R "$1".ftp "$USER_HOME"
find "$USER_HOME" -type d -exec chmod 775 [] \;
find "$USER_HOME" -type f -exec chmod 664 [] \;
else
useradd -d "$PUB_FTP" -c "added as vsftpd user" -s "/sbin/nologin" -p $(openssl passwd -crypt "$PUB_PASS") "$1"
fi
}
#Remove User Function
######################
function remove_user() {
USERCK=$(cat /etc/passwd | grep $1 | awk -F":" '{print$6}')
if [[ "$USERCK" == "$FTP_ROOT/$1" ]]; then
userdel -fr "$1"
else
userdel -f "$1"
fi
}
#List User Function
######################
function list_users() {
echo -ne "\n------------------------------\n"
echo -ne " Current Users: \n"
echo -ne "------------------------------\n"
echo -ne " User | Home \n"
echo -ne "------------------------------\n"
cat /etc/passwd | grep vsftpd | awk -F":" '{print$1" | "$6}'
echo -ne "------------------------------\n\n"
exit 0
}
#Arguments & Conditional Actions
##################################
[[ -z "$1" ]] && help
while [ ! -z "$1" ]; do
case "$1" in
--add|-a)
until [ -z "$2" ]; do
add_user "$2"
shift
done
exit 0
;;
--addpriv|-ap)
until [ -z "$2" ]; do
PRIV="1"
add_user "$2"
shift
done
exit 0
;;
--help|-h)
help
;;
--list|-l)
list_users
;;
--remove|-r)
until [ -z "$2" ]; do
remove_user "$2"
shift
done
exit 0
;;
*)
echo -ne "\n--> Parameter Undefined! Verify your syntax and try again.\n"
echo -ne "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
help
;;
esac
done
exit 0
1. This time I used a case statement to handle parameters. My original intention was to use shift to cycle through a list of inputs by looping through the case statement. Instead I had to nest another loop for each applicable situation. The problem is the script failing if a text argument is shifted to $1 (as the case statement needs -a,-r,-l, etc). Other than my nested until loop, is there another solution to have $1 always remain the same, then have $3 become $2, $4 become $3, etc.? So I only want shift to work from $2 to $n.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.