I'm writing my first bash script and having trouble assigning a file path to a variable:
$target="/etc/httpd/conf/httpd.conf"
It seems bash wants to interpret this with the "=" assignment operator resulting in the script throwing an error to the effect "No such file or directory."
Is there an easy way to do this? I've discovered I can assign a full path to a constant like this:
readonly TARGET=/etc/httpd/conf/httpd.conf
but that seems rather cumbersome. How would I perform string ops to modify/manipulate?
I've also discovered I can put full paths in an array like this:
declare -a cfile=('/root/.bashrc' '/etc/fstab')
All well and good, but how do I assign a file path to a variable?
== == == ==
finished! my first bash script - a basic config file manager
#!/bin/bash
# cfmgr.sh - configuration file manager bash script
# options: -get, -put
# '-get' creates SOURCEDIR/USERDIR and copies config files to USERDIR
# '-put' copies files in SOURCEDIR/USERDIR to system-defined locations on server
# purpose: helps with moving LAMP VMs to different hosts, bulk edits of
# of config files in editors like Notepad++, and backing up config files.
readonly SOURCEDIR=/usr/bin/_serverconfig
while [[ $# > 0 ]]
do
arg="$1"
shift
case $arg in
-put)
put=true
;;
-get)
get=true
;;
*)
badarg=true
;;
esac
done
clear
if [ $badarg ]; then
echo "Invalid argument. Use either 'scf.sh -put' or 'scf.sh -get' to put"\
"or get config files."
exit
elif [ $get ]; then
echo "Enter directory name to store files cfmgr will GET from this server:"
elif [ $put ]; then
echo "Enter directory name containing files cfmgr will PUT to this server:"
else
echo "Use either 'scf.sh -put' or 'scf.sh -get' to put or get config files."
exit
fi
read -e -i $SOURCEDIR"/" USERDIR
pattern=" |'"
if [[ $USERDIR =~ $pattern ]]; then
echo "Spaces not allowed. Please try again."
exit
fi
declare -a cfile=('/root/.bashrc' '/etc/fstab' '/etc/hosts' '/etc/networks'\
'/etc/php.ini' '/etc/nsswitch.conf' '/etc/ntp.conf' '/etc/resolv.conf'\
'/etc/sysctl.conf' '/etc/httpd/conf/httpd.conf' '/etc/selinux/config'\
'/etc/samba/smb.conf' '/etc/samba/smbusers' '/etc/security/limits.conf'\
'/etc/sysconfig/network' '/etc/sysconfig/network-scripts/ifcfg-eth0'\
'/etc/sysconfig/network-scripts/ifcfg-eth1')
if [ $get ]; then
if [[ -d "$USERDIR" ]]; then
echo $USERDIR "directory already exists. Please try again."
exit
else
mkdir -m 755 $USERDIR
fi
for file in ${cfile[#]}
do
if [ -e $file ]; then
rsync -q $file $USERDIR
if [ $? -eq 0 ]; then
sleep 0.1
printf "# "$file"\n"
fi
else
printf "not found: "$file"\n"
fi
done
elif [ $put ]; then
if [[ ! -d "$USERDIR" ]]; then
echo $USERDIR "directory does not exist. Please try again."
exit
fi
id=0
cd $USERDIR
for item in *
do
if [[ -f $item ]]; then
cdir[$id]=$item
id=$(($id+1))
fi
done
for file in ${cdir[#]}
do
case $file in
.bashrc)
idx=0
;;
fstab)
idx=1
;;
hosts)
idx=2
;;
networks)
idx=3
;;
php.ini)
idx=4
;;
nsswitch.conf)
idx=5
;;
ntp.conf)
idx=6
;;
resolv.conf)
idx=7
;;
sysctl.conf)
idx=8
;;
httpd.conf)
idx=9
;;
config)
idx=10
;;
smb.conf)
idx=11
;;
smbusers)
idx=12
;;
limits.conf)
idx=13
;;
network)
idx=14
;;
ifcfg-eth0)
idx=15
;;
ifcfg-eth1)
idx=16
;;
*)
printf "not found: "$file"\n"
continue
esac
target=${cfile[$idx]}
if [[ -e $target ]]; then
dtm=$(date +%Y-%m-%d)
mv $target $target"."$dtm
fi
source=$USERDIR"/"$file
dos2unix -q $source
rsync -q $source $target
if [ $? -eq 0 ]; then
sleep 0.1
printf "# "$target"\n"
fi
done
read -p "reboot now? (y|n)" selection
case $selection in
[Yy]*)
`reboot`
;;
*)
exit
;;
esac
fi
exit 0
Instead of
$target="/etc/httpd/conf/httpd.conf"
Use:
target="/etc/httpd/conf/httpd.conf"
When bash sees the former, it first substitutes in for "$target". If target was empty, then the line that bash tries to execute, after the variable substitution and quote removal steps, is:
=/etc/httpd/conf/httpd.conf
Since there is no file named "=/etc/httpd/conf/httpd.conf", bash returns with a "No such file or directory" error.
Related
Here's the code:
#!/bin/bash
function print_help() {
echo "Just a pile of echos; no other logic here"
}
die() {
printf '%s\n' "$1" >&2
exit 1
}
IpAddress=
SourceDir=
DestDir=
backup=0
restore=0
while :; do
case $1 in
''|-h|\?|--help) print_help
exit 0
;;
-b|--backup)
if [ $restore -eq 0 ]; then
backup=1
echo "Taking backup..."
else
die "ERROR: Can't perform backup and restore at the same time."
fi
;;
-r|--restore)
if [ $backup -eq 0 ]; then
restore=1
echo "Restoring backup..."
else
die "ERROR: Can't perform backup and restore at the same time."
fi
;;
--ip)
if [ "$2" ]; then
ip=$2
shift
else
die 'ERROR: "--ip" requires a non-empty option argument.'
fi
;;
--ip=?*)
IpAddress=${1#*=}
echo "IP Address: $IpAddress"
;;
--source-dir=?*)
SourceDir=${1#*=}
if [ -d "$SourceDir" ]; then
echo "Source Directory: $SourceDir indeed exists"
else
die "ERROR: $SourceDir doesn't exist."
fi
;;
--source-dir=)
die 'ERROR: "--source-dir" requires a non-empty argument.'
;;
--dest-dir=?*)
DestDir=${1#*=}
if [ -w "$DestDir" ]; then
echo "Destination Directory: $DestDir indeed exists and is writable."
else
die "ERROR: $DestDir doesn't exist or is not writable for current user."
fi
;;
--dest-dir=)
die 'ERROR: "--dest-dir" requires a non-empty argument.'
;;
--)
shift
break
;;
-?*)
printf 'Warning: Unknown option (ignoring): %s\n' "$1" >&2
;;
*)
break
esac
shift
done
So far, I've tested two use cases, with directories:
/tmp/back/ (in which user has no read permissions) and
/tmp/back2/ (which fully belongs to the user).
The 1st option launched correctly prints the error message:
$ ./backup.sh -b --source-dir=/opt/ --dest-dir=/tmp/back/
Taking backup...
Source Directory: /opt/ indeed exists
ERROR: /tmp/back/ doesn't exist or is not writable for current user.
Meanwhile the 2nd option confirms that data is OK, and seems to come back to the print_help case:
$ ./backup.sh -b --source-dir=/opt/ --dest-dir=/tmp/back2/
Taking backup...
Source Directory: /opt/ indeed exists
Destination Directory: /tmp/back2/ indeed exists and is writable.
backup.sh: backup.sh <-b|r> [-h] [server IP] <source dir> <dest dir>
Take or restore a backup of files.
What should be corrected here in order to exit the script after all options are checked one time?
The problem is that your while loop is an infinite loop, and you only break out of that loop in two of your 12 cases, or when an error occurs. So unless the code encounters those two cases, or an error, it will keep iterating even when there are no more parameters to read. When all of the parameters have been shifted, $1 will evaluate to an empty string, which is explicitly listed in your first case (''|-h|\?|--help) print_help), resulting in the help message.
The solution is to test for the existence of parameters in your while loop:
if [ $# -eq 0 ]; then
print_help
exit 1
fi
while [ $1 -gt 0 ]; do
case $1 in
-h|\?|--help) print_help
...
done
That way, when the last parameter has been shifted, the loop will end.
When all args ended $1 is '' that is why it comes back to the print_help
What should be corrected here in order to exit the script after all
options are checked one time?
Change print_help pattern to *) and '') should be exit 0
But better change while condition to:
while [[ $1 ]]; do
...
done
And this check could be added at the beginning of the script:
[[ $1 ]] || print_help
to print help message and exit if no args passed to the script.
Remove '' from the case statement and test if there are no arguments before the loop:
[[ $1 ]] || { print_help ; exit 0 ; }
while :; do
case $1 in
-h|\?|--help) print_help
exit 0
;;
I am working on a shell script that will allow for the user to input a file name(s) into the command prompt, and the script will locate the executable file and determine if they could have been executed. However, when I attempt to run it, I get a "line 45 `$#': not a valid identifier" error message.
This is my first script and can't figure out how to address this error. I am hopeful that the rest of the script is ok, but I haven't been able to get past this one error to test it out.
Any help is greatly appreciated!
#!/bin/bash
FINDALL=FALSE
while :
do
case $1 in
-a) FINDALL=TRUE
shift ;;
-h) echo “Usage $0 [-a] commands…”
exit 0 ;;
*) break ;;
esac
done
for $# in $file
do
case $file in
*/*) if [ -x “$file” ]
then
echo $file
elif [ -d “$file” ]
then
echo $file is NOT found
fi
shift ;;
*) FOUND=FALSE ;;
esac
done
case $PATH in
:*) PATH=”.:$PATH” ;;
*::*) PATH=`echo $PATH | sed -e ‘s/::/:.:/g’` ;;
*:) PATH=”$PATH:.” ;;
esac
command=”$1”
IFS=$OLDIFS
IFS=:
set -- $PATH
IFS=$OLDIFS
case $command in
*/*) echo $command ;;
*) FOUND=FALSE
for P
do
if [ ! -d “$P/$command” -a -x “$P/$command” ]
then
FOUND=TRUE
echo $command
break
fi
done
if [ “$FOUND” = FALSE ]
then
echo “Command $command not found in your search path”
fi ;;
esac
I have a very peculiar issue with a script that I have wrote today. I am trying to form an ip address from two variables namely url and port. I am getting the url value from a library script which echos 10.241.1.8 and the port number is 10000. Now if I concatenate both the url and the port into another variable ip, I get completely a strange result(:10000241.1.8). I have my code and its result below. Please help me with your suggestions to fix this.
clear
echo $(date +'%H:%M:%S')'>> "Sample Records" Script started...'
usage() {
echo ">> $ script.sh -ctoff 89 -env c -ns reporting -depPath /user/release/audit_prime_oozie"
echo "Usage: $ script.sh -ctoff <Cutoff number> -env <testing cluster. ex: s for staging,c,d,p and a> -ns <optional: hive namespace> -depPath <deployment path>"
}
# Function to validate if value of a parameter is not empty
validate () {
if [[ $flag != 1 ]]; then
if [[ $tmpVar == *"-"* ]] || [[ -z $tmpVar ]]; then
usage
exit 1
fi
fi
}
options=$#
if [[ -z $options ]]; then
usage
exit 1
fi
arguments=($options)
index=0
# Function to extract the parameter values
check (){
for x in $options
do
index=`expr $index + 1`
case $x in
-ctoff)
cutOff="${arguments[index]}"
tmpVar=$cutOff
validate $tmpVar
;;
-env)
env="${arguments[index]}"
tmpVar=$env
validate $tmpVar
;;
-ns)
ns="${arguments[index]}"
tmpVar=$ns
validate $tmpVar
;;
-depPath)
depPath="${arguments[index]}"
tmpVar=$depPath
validate $tmpVar
;;
esac
if [[ -z $ns ]];then
ns=reporting
fi
done
}
check $#
error_exit(){
echo "$1" 1>&2
exit 1
}
# Create the execution directory
user=$(id -u -n)
PWD=`pwd`
INSTALL_ROOT=$PWD
LOCAL_DIR="/tmp/$user/sample_duns"
if [[ ! -d $LOCAL_DIR ]]; then
mkdir -p $LOCAL_DIR
echo ">> Created local directory $LOCAL_DIR"
if [[ $? -ne 0 ]]; then
echo ">> Unable to create $LOCAL_DIR, writing to current folder $INSTALL_ROOT"
LOCAL_DIR=$INSTALL_ROOT
fi
fi
if [[ $(ls -A $LOCAL_DIR) ]]; then
echo ">> Removed the temp files from $LOCAL_DIR"
rm -r $LOCAL_DIR/*
fi
# create the file name
datestamp=$(date '+%Y%m%d%H')
outFile=sample_duns_$datestamp.txt
# Copy the contents from HDFS to Local directory
echo ">> Copying required files from HDFS"
hdfs dfs -copyToLocal $depPath/data-warehouse/config/server.properties $LOCAL_DIR || error_exit "Cannot copy files from HDFS! Exiting now.."
hdfs dfs -copyToLocal $depPath/data-warehouse/reporting/lib_getHiveServer2ip.sh $LOCAL_DIR || error_exit "Cannot copy files from HDFS! Exiting now.."
if [[ $? -ne 0 ]]; then
echo ">> Files missing. Exiting now.."
exit 1
fi
# Call the lib script to get appropriate hiveserver2 ip address from the supplied environment for beeline execution
echo ">> Reading the HiveServer2 ip"
chmod +x $LOCAL_DIR/lib_getHiveServer2ip.sh
url=$($LOCAL_DIR/lib_getHiveServer2ip.sh $env $LOCAL_DIR/server.properties)
echo url=$url
port=10000
echo ip=$url:$b
Here is my output from the terminal.
11:18:16>> "Sample Records" Script started...
>> Removed the temp files from /tmp/user/sample_duns
>> Copying required files from HDFS
>> Reading the HiveServer2 ip
url=10.241.1.8
:10000241.1.8
I am expecting the below result
ip=10.241.1.8:10000
Adding the lib_getHiveServer2ip.sh script below
. $2 # read properties file
if [[ $1 == "d" ]]; then
ip=$devHSer
elif [[ $1 == "c" ]]; then
ip=$crankHSer
elif [[ $1 == "s" ]]; then
ip=$stgHSer
elif [[ $1 == "p" ]]; then
ip=$prdHSer
elif [[ $1 == "a" ]]; then
ip=$alpHSer
else
echo ">> Invalid cluster ip encountered. Exiting now ..."
exit 1
fi
echo $ip
Your url variable contains a carriage return character for some reason. Check lib_getHiveServer2ip.sh for weirdness.
Pipe your echo output to hexdump to confirm.
Edit: looks like your properties file has bad line endings. Use the file utility to check.
I was fiddling around with bash last month and am trying to create a script.
I want the script to search through the folders for files with some kind of extension defined by the argument -e. The folders are defined without -option. The output is 2 columns where in the first it prints the found files, and in the second the respective folders.
Is this the most efficient and/or flexible way to go?
I also can't manage to let the -l command work. Any idea what's wrong? When I enter -name \${CHAR}*, it simply doesn't work. Also, how can I make it recognize a range being used? With an if-function looking for the "-" character or something?
I think I managed to mount a block device, but how can I add the path as a parameter so it can be used as a folder? Setting a number as a var doesn't work, it tells me it doesn't recognize the command.
For some reason the 'no recursion' tag works, but the 'no numbers' doesn't. I have no idea why this would be different.
When using the 'no recursion' (nn) and 'no numbers' (nr) tags I use a long tag --tag for the arguments. Is it possible to use only 1 -tag? This is possible with get opts, but then I can't manage to use the other tags after the get opts has been used. Someone a solution?
Finally, is it possible, when finding 2 files with the same file name, instead of printing the file twice, can it just show the file once. But for every file with the same name keep a white space, so it can still show all the folders in the second column?
#!/bin/bash
#FUNCTIONS
#Error
#Also written to stderr
err() {
echo 1>&2;
echo "Error, not enough arguments" 1>&2;
echo "Usage: $0 [-e <file extension>] [<folder>]";
echo "Please enter the argument -e and at least 1 folder.";
echo "More: Please chek Help by using -h or --help.";
echo 1>&2;
exit
}
#Help
help() {
echo
echo "--- Help ---"
echo
echo "This script will look for file extentions in 1 or more directories. The output shows the found files with the according folder where it's located."
echo
echo "Argument -e <ext> is required."
echo "Other arguments the to-look-trough folders."
echo
echo "These are also usable options:"
echo "-h or --help shows this."
echo "-l <character> looks for files starting with the character."
echo "-l <character1>-<character2> does the same, but looks trough a range of characters."
echo "-b <block-device> mounts a partition to /mnt and let it search through."
echo "--nn (no numbers) makes sure there are no numbers in the file name."
echo "--nr (no recursion) doesn't look trough subdirectories."
echo "-r of –-err <file> writes the errors (f.e. corrupted directory) to <file>."
echo "-s <word> searches the word through the files and only shows the files having that word."
echo
exit
}
#VARS
#execute getopt
OPTS=$(getopt -o e:hl:b:r:s: -l "help,nn,nr,err" -n "FileExtensionScript" -- "$#");
#Bad arguments
if [ $? -ne 0 ];
then
err;
exit
fi
#Rearrange arguments
eval set -- "$OPTS";
#echo "AFTER SET -- \$OPTS: $#";
while true; do
case "$1" in
-e)
shift;
if [ -n "$1" ]; then
EXT=$1;
shift;
fi
;;
-h|--help)
shift;
help;
;;
-l)
shift;
if [ -n "$1" ]; then
CHAR=$1;
shift;
fi
;;
-b)
shift;
if [ -n "$1" ]; then
sudo mkdir /mnt/$1;
sudo echo -e "/dev/$1 /mnt/$1 vfat defaults 0 0 " >> /etc/fstab;
sudo mount -a;
999=/mnt/$1;
shift;
fi
;;
--nn)
shift;
NONUM=" ! -name '*[0-9]*'";
;;
--nr)
shift;
NOREC="-maxdepth 1";
;;
-f|--err)
shift;
if [ -n "$1" ]; then
ERROR="| 2>filename | tee -a $1";
shift;
fi
;;
-s)
shift;
if [ -n "$1" ]; then
SEARCH="-name '*$1*'";
shift;
fi
;;
--)
shift;
break;
;;
esac
done
#No folder or arguments given
if [ $# -lt 1 ];
then
err;
exit
fi
#Debug
echo "Folder argumenten: $#" >2;
echo \# $# >2;
#Create arrays with found files and according folders
FILES=( $(find $# $NOREC $SEARCH $NONUM -name \*.${EXT} $ERROR | rev | cut -d/ -f 1 | rev) )
FOLDERS=( $(find $# $NOREC $SEARCH $NONUM -name \*.${EXT} $ERROR | rev | cut -d/ -f 1 --complement | rev) )
#Show arrays in 2 columns
for ((i = 0; i <= ${#FILES[#]}; i++));
do
printf '%s %s\n' "${FILES[i]}" "${FOLDERS[i]}"
done | column -t | sort -k1 #Make columns cleaner + sort on filename
I am not native English speaker and am hoping to get some tips to finish my script :) Thanks in advance!
I'm trying to adapt a bash script from "Sams' Teach Yourself Linux in 24 Hours" which is a safe delete command called rmv. The files are removed by calling rmv -d file1 file2 etc. In the original script a max of 4 files can by removed using the variables $1 $2 $3 $4.
I want to extend this to an unlimited number of files by using a wildcard.
So I do:
for i in $*
do
mv $i $HOME/.trash
done
The files are deleted okay but the option -d of the command rmv -d is also treated as an argument and bash objects that it cannot be found. Is there a better way to do this?
Thanks,
Peter
#!/bin/bash
# rmv - a safe delete program
# uses a trash directory under your home directory
mkdir $HOME/.trash 2>/dev/null
# four internal script variables are defined
cmdlnopts=false
delete=false
empty=false
list=false
# uses getopts command to look at command line for any options
while getopts "dehl" cmdlnopts; do
case "$cmdlnopts" in
d ) /bin/echo "deleting: \c" $2 $3 $4 $5 ; delete=true ;;
e ) /bin/echo "emptying the trash..." ; empty=true ;;
h ) /bin/echo "safe file delete v1.0"
/bin/echo "rmv -d[elete] -e[mpty] -h[elp] -l[ist] file1-4" ;;
l ) /bin/echo "your .trash directory contains:" ; list=true ;;
esac
done
if [ $delete = true ]
then
for i in $*
do
mv $i $HOME/.trash
done
/bin/echo "rmv finished."
fi
if [ $empty = true ]
then
/bin/echo "empty the trash? \c"
read answer
case "$answer" in
y) rm -i $HOME/.trash/* ;;
n) /bin/echo "trashcan delete aborted." ;;
esac
fi
if [ $list = true ]
then
ls -l $HOME/.trash
fi
You can make use of shift here.
Once you find -d is one of the options in the switch, you can shift and get rid of -d from the positional parameters. Next you can
until [ -z $1 ] ; do
mv $1 $HOME/.trash
shift
done
getopts sets OPTIND to the index of the first argument after the options. (#)
So after parsing the options you can do:
shift $OPTIND-1
to remove the options from the argument list.
Then use "$#" rather than $*, and you can handle files with spaces in them.
Thanks a lot!
I changed the code to read:
#!/bin/bash
# rmv - a safe delete program
# todo: add ability to handle wildcards
# uses a trash directory under your home directory
mkdir $HOME/.trash 2>/dev/null
# four internal script variables are defined
cmdlnopts=false
delete=false
empty=false
list=false
# uses getopts command to look at command line for any options
while getopts "dehl" cmdlnopts; do
case "$cmdlnopts" in
d ) echo -e "deleting: \n" "${#:2}" ; delete=true ;;
e ) echo -e "emptying the trash..." ; empty=true ;;
h ) echo -e "safe file delete v1.0"
echo -e "rmv -d[elete] -e[mpty] -h[elp] -l[ist] file [...]" ;;
l ) echo -e "your .trash directory contains:" ; list=true ;;
esac
done
shift $OPTIND-1
if [ $delete = true ]
then
for i in $#
do
mv $i $HOME/.trash
done
echo "rmv finished."
fi
then
/bin/echo "empty the trash? \c"
read answer
case "$answer" in
y) rm -i $HOME/.trash/* ;;
n) /bin/echo "trashcan delete aborted." ;;
esac
fi
if [ $list = true ]
then
ls -l $HOME/.trash
fi
This deletes the files as desired but I get this error:
/home/peter/rmv: line 21: shift: 2-1: numeric argument required
mv: invalid option -- 'd'
Try `mv --help' for more information.
You need to use
shift $(($OPTIND - 1))
to get red of the processed command line args. Try this version:
#!/bin/bash
# rmv - a safe delete program
# uses a trash directory under your home directory
mkdir -p $HOME/.trash
# uses getopts command to look at command line for any options
while getopts "dehl" cmdlnopts; do
case "$cmdlnopts" in
d ) delete=true;;
e ) echo "emptying the trash..." ; empty=true ;;
h ) echo "safe file delete v1.0"
echo "rmv -d[elete] -e[mpty] -h[elp] -l[ist] files" ;;
l ) echo "your .trash directory contains:" ; list=true ;;
esac
done
shift $(($OPTIND - 1))
if [ -n "${delete}" ]; then
echo "deleting: " "${#}"
mv ${#} $HOME/.trash
echo "rmv finished."
fi
if [ -n "${empty}" ]; then
read -p "empty the trash? " answer
case "$answer" in
y) rm -i $HOME/.trash/* ;;
n) echo "trashcan delete aborted." ;;
esac
fi
if [ -n "${list}" ]; then
ls -l $HOME/.trash
fi
Late to the party, but for Googlers, this will generate the error Peter describes:
shift $OPTIND-1
while the syntax in Jurgen's reply will not:
shift $(($OPTIND-1))
The problem is that $OPTIND-1 is interpreted as a string, and shift can't use a string as an argument. $(( )) is Bash's arithmetic expansion operator. You put a string inside it, the string is evaluated as an arithmetic expression, and the value returned.