This question already has answers here:
Why should there be spaces around '[' and ']' in Bash?
(5 answers)
using OR in shell script
(2 answers)
Closed 7 months ago.
here is my simple script prova.sh:
#!/bin/bash
echo "\$# = [$#]"
echo "\$1 = [$1]"
if [ $# -ge 2 || $1="-h" ]
then
echo "#########################################################################"
echo -e "usage: $0 \t launches the program only"
echo -e " $0 -s\t shows the command fired and launches the program"
echo -e " $0 -so\t shows only the command fired"
echo -e " $0 -h\t shows this guide"
echo "#########################################################################"
exit 0
fi
command="ls -la"
if test "$1"="-so" || "$1"="-s"
then
echo "Fired command:"
echo $command
fi
if test "$1"!="-so"
then
$command
fi
here is the output:
$> ./prova.sh -h
$# = [1]
$1 = [-h]
./prova.sh: row 6 : [: "]" missing
./prova.sh: row6: -h=-h: command not found
Fired command:
ls -l
totale 4
-rwxrw-r--. 1 user user 632 20 lug 16.13 prova.sh
I expected just the help
you need to close every test using single brackets, the shell way, if [ $# -ge 2 ] || [ $1 = "-h" ]; then ... this will fix it. If you still use single brackets way, is safe to enclose you variables with double quotes, so if they are empty, you are safe, like this if [ "$#" -ge 2 ] || [ "$1" = "-h" ]; then ... or you can just put them inside a double bracket, and bash will handle this for you if [[ $# -ge 2 || $1 = "-h" ]]; then ...
This question already has an answer here:
Why variable values are lost after terminating the loop in bash? [duplicate]
(1 answer)
Closed 2 years ago.
I would like to make a script which allow me to execute a command which inherit environment variables from any PID.
Here the script I made :
#!/bin/sh
VARS=$(cat -A /proc/1/environ | tr "^#" "\n")
COMMAND=""
# sh compatible loop on a variable containing multiple lines
printf %s "$VARS" | while IFS='\n' read -r var
do
if [ "$var" != "" ]; then
export "$var"
fi
done
exec "$#"
I though exported variables would be available for the child process (created by exec) but this is obviously not the case because sh my_script.sh printenv doesn't show environment variables which are in /proc/1/environ.
I also tried the following script :
#!/bin/sh
VARS=$(cat -A /proc/1/environ | tr "^#" "\n")
COMMAND=""
# sh compatible loop on a variable containing multiple lines
printf %s "$VARS" | while IFS='\n' read -r var
do
if [ "$var" != "" ]; then
# Replace 'VAR=var' by 'VAR="var"' for eval
# sed replace only the first occurence of the '=' due of the missing /g parameter
escaped=$(echo $var | sed -e 's/=/="/')\"
COMMAND="${COMMAND} ${escaped}"
fi
done
COMMAND="${COMMAND} $#"
eval $COMMAND
However, it looks like eval doesn't export variables even if the evaluated command looks like VAR=value my_command.
How I am supposed to achieve my needs ?
Thanks in advance
That one should work (tested on RHEL 7)
#!/bin/bash
locPROC=$1
locCMD=$2
if [[ -z $locPROC || -z $locCMD ]]; then
exit
fi
if [[ -r /proc/${locPROC}/environ ]]; then
while IFS= read -r -d '' line; do
#Making sure it's properly quoted
locVar="${line/=/=\"}\""
#You probably don't want to mess with those
if [[ ${locVar:0:1} != "_" && ${locVar} != A__z* ]]; then
eval "$locVar"
eval "export ${locVar%%=*}"
fi
done < "/proc/${locPROC}/environ"
$locCMD
else
echo "Environment file is either inexistant or unreadable"
fi
EDITED : According to comments (still use eval...got to read more :) )
I would like to put my getopt call into a function so I can make my script a bit more tidy. I've read a few guides Using getopts inside a Bash function but they seem to be for getopts not getopt and cannot get my head round it.
I have the following getopt call at the start of my script
#-------------------------------------------------------------------------------
# Main
#-------------------------------------------------------------------------------
getopt_results=$( getopt -s bash -o e:h --long ENVIRONMENT:,HELP:: -- "$#" )
if test $? != 0
then
echo "Failed to parse command line unrecognized option" >&2
Usage
exit 1
fi
eval set -- "$getopt_results"
while true
do
case "$1" in
-e | --ENVIRONMENT)
ENVIRONMENT="$2"
if [ ! -f "../properties/static/build_static.${ENVIRONMENT}.properties" -o ! -f "../properties/dynamic/build_dynamic.${ENVIRONMENT}.properties" ]; then
echo "ERROR: Unable to open properties file for ${ENVIRONMENT}"
echo "Please check they exist or supply a Correct Environment name"
Usage
exit 1
else
declare -A props
readpropsfile "../properties/dynamic/dynamic.${ENVIRONMENT}.properties"
readpropsfile "../properties/static/static.${ENVIRONMENT}.properties"
fi
shift 2
;;
-h | --HELP)
Usage
exit 1
;;
--)
shift
break
;;
*)
echo "$0: unparseable option $1"
Usage
exit 1
;;
esac
done
when I put the whole lot in function , say called parse_command_line ()
and call it with parse_command_line "$#"
my script dies because it cannot work out the parameters it was called with. I have tried making OPTIND local as per some of the guides. Any advice? Thanks.
getopt shouldn't be used, but the bash-aware GNU version works fine inside a function, as demonstrated below:
#!/usr/bin/env bash
main() {
local getopt_results
getopt_results=$(getopt -s bash -o e:h --long ENVIRONMENT:,HELP:: "$#")
eval "set -- $getopt_results" # this is less misleading than the original form
echo "Positional arguments remaining:"
if (( $# )); then
printf ' - %q\n' "$#"
else
echo " (none)"
fi
}
main "$#"
...when saved as getopt-test and run as:
./getopt-test -e foo=bar "first argument" "second argument"
...properly emits:
Positional arguments remaining:
- -e
- foo=bar
- --
- hello
- cruel
- world
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
Task 1:
If the Time is AM, print "It is Morning". If it is PM, print "It is not morning".
Task 2:
Given 2 arguments passed to the script.
Validate that 2 arguments have been submitted. If three arguments have not been provided, print "Must Supply 2 Arguments" and exit the script
Argument 1: Should be a directory (needs validated). If this does not exist, print "Directory: directory, does not exist" and exit the script
Argument 2: Should be a file (needs validated). If this does not exist, print "File: filename, does not exist" and exit the script.
If all arguments are valid, print "Given valid: filename and directory"
This is what I have so far
echo "James DuBois: 555555 - Task 1"
TIME=$(date "+%H")
if [ $TIME -lt 12 ]; then
echo "morning"
else
echo "not morning"
fi
echo "Task 2"
[ -d "$1" ] || exit
[ -d "$2" ] || exit
[ $# == 2 ] || exit
echo "arg1: $1"
echo "arg2: $2"
James, BASH is a wonderful, flexible shell. It has its warts, but if you need to do anything having to do with Linux system administration, etc.., you can do it in bash. Your tasks are to familiarize you with using conditional expressions (tests). There are tests for just about anything you need. That's why I pointed you to the CONDITIONAL EXPRESSIONS part of man bash.
Your second task requires the input of a filename so you can test it. I presume it is intended to be passed as an argument to your script (called positional parameters). Here is one way to approach the test. Note: I have interchanged output routines echo and printf intentionally for your benefit (printf being a bit more robust). Take a look at the following and let me know what questions you have:
#!/bin/bash
# My first script
#
# echo & printf are used at random below -- intentionally
#
[ -z $1 ] && { # validate at least 1 argument given on command line
printf "error: insufficient input. usage: %s filename\n" "${0##*/}"
exit 1
}
printf "\nJames DuBois: 5555555\n\n Task 1\n\n"
TIME=$(date "+%H")
## test for time of date: morning/not morning
if [ $TIME -lt 12 ]; then
printf " morning - time for coffee\n"
else
echo " not morning - time for scotch"
fi
echo -e "\n Task 2\n"
printf "Testing whether '%s' is a valid file.\n\n" "$1"
## test for file using compound commands
[ -f "$1" ] && echo -e " file found: '$1'\n" || printf " file not found: '%s'\n\n" "$1"
echo -e "Second test whether '$1' is a valid file.\n"
## test for file using if; then; else; fi
if [ -f "$1" ]; then
printf " file found: '%s'\n\n" "$1"
else
echo -e " file not found: '$1'\n"
fi
exit 0
Use/Output
$ bash ~/scr/tmp/stack/morningfile.sh
error: insufficient input. usage: morningfile.sh filename
$ bash ~/scr/tmp/stack/morningfile.sh mtrx_simple_dyn.c
James DuBois: 5555555
Task 1
not morning - time for scotch
Task 2
Testing whether 'mtrx_simple_dyn.c' is a valid file.
file found: 'mtrx_simple_dyn.c'
Second test whether 'mtrx_simple_dyn.c' is a valid file.
file found: 'mtrx_simple_dyn.c'
$ bash ~/scr/tmp/stack/morningfile.sh dog.c
James DuBois: 5555555
Task 1
not morning - time for scotch
Task 2
Testing whether 'dog.c' is a valid file.
file not found: 'dog.c'
Second test whether 'dog.c' is a valid file.
file not found: 'dog.c'
This will have some additional advantages while doing the same thing in task 2:
echo "Task 2"
[[ -d $1 && ! -L $1 && -f $2 && ! -L $2 ]] || exit
printf "%s\n" "arg1: $1" "arg2: $2"
It checks for symbolic links with the -L option.
[[ ]] has some advantages over [ ] like dealing with white spaces without the need for quoting variables.
If you want to print messages while checking for directory and file:
[[ ! -d $1 || -L $1 ]] && echo "Directory doesn't exist" && exit
[[ ! -f $2 || -L $2 ]] && echo "File doesn't exist" && exit
printf "%s\n" "arg1: $1" "arg2: $2"
How can I determine the name of the Bash script file inside the script itself?
Like if my script is in file runme.sh, then how would I make it to display "You are running runme.sh" message without hardcoding that?
me=`basename "$0"`
For reading through a symlink1, which is usually not what you want (you usually don't want to confuse the user this way), try:
me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"
IMO, that'll produce confusing output. "I ran foo.sh, but it's saying I'm running bar.sh!? Must be a bug!" Besides, one of the purposes of having differently-named symlinks is to provide different functionality based on the name it's called as (think gzip and gunzip on some platforms).
1 That is, to resolve symlinks such that when the user executes foo.sh which is actually a symlink to bar.sh, you wish to use the resolved name bar.sh rather than foo.sh.
# ------------- SCRIPT ------------- #
#!/bin/bash
echo
echo "# arguments called with ----> ${#} "
echo "# \$1 ----------------------> $1 "
echo "# \$2 ----------------------> $2 "
echo "# path to me ---------------> ${0} "
echo "# parent path --------------> ${0%/*} "
echo "# my name ------------------> ${0##*/} "
echo
exit
# ------------- CALLED ------------- #
# Notice on the next line, the first argument is called within double,
# and single quotes, since it contains two words
$ /misc/shell_scripts/check_root/show_parms.sh "'hello there'" "'william'"
# ------------- RESULTS ------------- #
# arguments called with ---> 'hello there' 'william'
# $1 ----------------------> 'hello there'
# $2 ----------------------> 'william'
# path to me --------------> /misc/shell_scripts/check_root/show_parms.sh
# parent path -------------> /misc/shell_scripts/check_root
# my name -----------------> show_parms.sh
# ------------- END ------------- #
With bash >= 3 the following works:
$ ./s
0 is: ./s
BASH_SOURCE is: ./s
$ . ./s
0 is: bash
BASH_SOURCE is: ./s
$ cat s
#!/bin/bash
printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE"
$BASH_SOURCE gives the correct answer when sourcing the script.
This however includes the path so to get the scripts filename only, use:
$(basename $BASH_SOURCE)
If the script name has spaces in it, a more robust way is to use "$0" or "$(basename "$0")" - or on MacOS: "$(basename \"$0\")". This prevents the name from getting mangled or interpreted in any way. In general, it is good practice to always double-quote variable names in the shell.
If you want it without the path then you would use ${0##*/}
To answer Chris Conway, on Linux (at least) you would do this:
echo $(basename $(readlink -nf $0))
readlink prints out the value of a symbolic link. If it isn't a symbolic link, it prints the file name. -n tells it to not print a newline. -f tells it to follow the link completely (if a symbolic link was a link to another link, it would resolve that one as well).
I've found this line to always work, regardless of whether the file is being sourced or run as a script.
echo "${BASH_SOURCE[${#BASH_SOURCE[#]} - 1]}"
If you want to follow symlinks use readlink on the path you get above, recursively or non-recursively.
The reason the one-liner works is explained by the use of the BASH_SOURCE environment variable and its associate FUNCNAME.
BASH_SOURCE
An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME array variable are defined. The shell function ${FUNCNAME[$i]} is defined in the file ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}.
FUNCNAME
An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.
This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.
[Source: Bash manual]
Since some comments asked about the filename without extension, here's an example how to accomplish that:
FileName=${0##*/}
FileNameWithoutExtension=${FileName%.*}
Enjoy!
These answers are correct for the cases they state but there is a still a problem if you run the script from another script using the 'source' keyword (so that it runs in the same shell). In this case, you get the $0 of the calling script. And in this case, I don't think it is possible to get the name of the script itself.
This is an edge case and should not be taken TOO seriously. If you run the script from another script directly (without 'source'), using $0 will work.
Re: Tanktalus's (accepted) answer above, a slightly cleaner way is to use:
me=$(readlink --canonicalize --no-newline $0)
If your script has been sourced from another bash script, you can use:
me=$(readlink --canonicalize --no-newline $BASH_SOURCE)
I agree that it would be confusing to dereference symlinks if your objective is to provide feedback to the user, but there are occasions when you do need to get the canonical name to a script or other file, and this is the best way, imo.
this="$(dirname "$(realpath "$BASH_SOURCE")")"
This resolves symbolic links (realpath does that), handles spaces (double quotes do this), and will find the current script name even when sourced (. ./myscript) or called by other scripts ($BASH_SOURCE handles that). After all that, it is good to save this in a environment variable for re-use or for easy copy elsewhere (this=)...
You can use $0 to determine your script name (with full path) - to get the script name only you can trim that variable with
basename $0
if your invoke shell script like
/home/mike/runme.sh
$0 is full name
/home/mike/runme.sh
basename $0 will get the base file name
runme.sh
and you need to put this basic name into a variable like
filename=$(basename $0)
and add your additional text
echo "You are running $filename"
so your scripts like
/home/mike/runme.sh
#!/bin/bash
filename=$(basename $0)
echo "You are running $filename"
This works fine with ./self.sh, ~/self.sh, source self.sh, source ~/self.sh:
#!/usr/bin/env bash
self=$(readlink -f "${BASH_SOURCE[0]}")
basename=$(basename "$self")
echo "$self"
echo "$basename"
Credits: I combined multiple answers to get this one.
echo "$(basename "`test -L ${BASH_SOURCE[0]} \
&& readlink ${BASH_SOURCE[0]} \
|| echo ${BASH_SOURCE[0]}`")"
In bash you can get the script file name using $0. Generally $1, $2 etc are to access CLI arguments. Similarly $0 is to access the name which triggers the script(script file name).
#!/bin/bash
echo "You are running $0"
...
...
If you invoke the script with path like /path/to/script.sh then $0 also will give the filename with path. In that case need to use $(basename $0) to get only script file name.
Short, clear and simple, in my_script.sh
#!/bin/bash
running_file_name=$(basename "$0")
echo "You are running '$running_file_name' file."
Out put:
./my_script.sh
You are running 'my_script.sh' file.
Info thanks to Bill Hernandez. I added some preferences I'm adopting.
#!/bin/bash
function Usage(){
echo " Usage: show_parameters [ arg1 ][ arg2 ]"
}
[[ ${#2} -eq 0 ]] && Usage || {
echo
echo "# arguments called with ----> ${#} "
echo "# \$1 -----------------------> $1 "
echo "# \$2 -----------------------> $2 "
echo "# path to me ---------------> ${0} " | sed "s/$USER/\$USER/g"
echo "# parent path --------------> ${0%/*} " | sed "s/$USER/\$USER/g"
echo "# my name ------------------> ${0##*/} "
echo
}
Cheers
DIRECTORY=$(cd `dirname $0` && pwd)
I got the above from another Stack Overflow question, Can a Bash script tell what directory it's stored in?, but I think it's useful for this topic as well.
Here is what I came up with, inspired by Dimitre Radoulov's answer (which I upvoted, by the way).
script="$BASH_SOURCE"
[ -z "$BASH_SOURCE" ] && script="$0"
echo "Called $script with $# argument(s)"
regardless of the way you call your script
. path/to/script.sh
or
./path/to/script.sh
$0 will give the name of the script you are running. Create a script file and add following code
#!/bin/bash
echo "Name of the file is $0"
then run from terminal like this
./file_name.sh
To get the "realpath" of script or sourced scripts in all cases :
fullname=$(readlink $0) # Take care of symbolic links
dirname=${fullname%/*} # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}
Here is the bash script to generate (in a newly created "workdir" subdir and in "mytest" in current dir), a bash script which in turn will source another script, which in turm will call a bash defined function .... tested with many ways to launch them :
#!/bin/bash
##############################################################
ret=0
fullname=$(readlink $0) # Take care of symbolic links
dirname=${fullname%/*} # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}
fullname_withoutextension=${fullname%.*}
mkdir -p workdir
cat <<'EOD' > workdir/_script_.sh
#!/bin/bash
##############################################################
ret=0
fullname=$(readlink $0) # Take care of symbolic links
dirname=${fullname%/*} # Get (most of the time) the dirname
realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts
[ "$realpath" = '.' ] && realpath=${dirname:-.}
fullname_withoutextension=${fullname%.*}
echo
echo "# ------------- RESULTS ------------- #"
echo "# path to me (\$0)-----------> ${0} "
echo "# arguments called with ----> ${#} "
echo "# \$1 -----------------------> $1 "
echo "# \$2 -----------------------> $2 "
echo "# path to me (\$fullname)----> ${fullname} "
echo "# parent path(\${0%/*})------> ${0%/*} "
echo "# parent path(\$dirname)-----> ${dirname} "
echo "# my name ----\${0##*/}------> ${0##*/} "
echo "# my source -\${BASH_SOURCE}-> ${BASH_SOURCE} "
echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)"
echo "# my function name -\${FUNCNAME[0]}------> ${FUNCNAME[0]}"
echo "# my source or script real path (realpath)------------------> $realpath"
echo
[ "$realpath" = "workdir" ] || ret=1
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
show_params () {
echo
echo "# --- RESULTS FROM show_params() ---- #"
echo "# path to me (\$0)-----------> ${0} "
echo "# arguments called with ----> ${#} "
echo "# \$1 -----------------------> $1 "
echo "# \$2 -----------------------> $2 "
echo "# path to me (\$fullname)----> ${fullname} "
echo "# parent path(\${0%/*})------> ${0%/*} "
echo "# parent path(\$dirname)-----> ${dirname} "
echo "# my name ----\${0##*/}------> ${0##*/} "
echo "# my source -\${BASH_SOURCE}-> ${BASH_SOURCE} "
echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)"
echo "# my function name -\${FUNCNAME[0]}------> ${FUNCNAME[0]}"
echo "# my source or script real path (realpath)------------------> $realpath"
echo
[ "$realpath" = "workdir" ] || ret=1
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
}
show_params "$#"
EOD
cat workdir/_script_.sh > workdir/_side_by_side_script_sourced.inc
cat <<'EOD' >> workdir/_script_.sh
echo "# . $realpath/_side_by_side_script_sourced.inc 'hello there' 'william'"
. $realpath/_side_by_side_script_sourced.inc 'hello there' 'william'
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
EOD
chmod +x workdir/_script_.sh
[ -L _mytest_ ] && rm _mytest_
ln -s workdir/_script_.sh _mytest_
# ------------- CALLED ------------- #
called_by () {
echo '=========================================================================='
echo " Called by : " "$#"
echo '=========================================================================='
eval "$#"
}
called_by bash _mytest_
called_by ./_mytest_
called_by bash workdir/_script_.sh
called_by workdir/_script_.sh
called_by . workdir/_script_.sh
# ------------- RESULTS ------------- #
echo
echo
[ $ret = 0 ] || echo "*******************************************************"
[ $ret = 0 ] || echo "*********** ERROR **********************************"
[ $ret = 0 ] || echo "*******************************************************"
echo
[ $ret = 0 ] && echo ".... location of scripts (\$realpath) should always be equal to $realpath, for all test cases at date".
echo
# ------------- END ------------- #
echo "You are running $0"
somthing like this?
export LC_ALL=en_US.UTF-8
#!/bin/bash
#!/bin/sh
#----------------------------------------------------------------------
start_trash(){
ver="htrash.sh v0.0.4"
$TRASH_DIR # url to trash $MY_USER
$TRASH_SIZE # Show Trash Folder Size
echo "Would you like to empty Trash [y/n]?"
read ans
if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ]
then
echo "'yes'"
cd $TRASH_DIR && $EMPTY_TRASH
fi
if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ]
then
echo "'no'"
fi
return $TRUE
}
#-----------------------------------------------------------------------
start_help(){
echo "HELP COMMANDS-----------------------------"
echo "htest www open a homepage "
echo "htest trash empty trash "
return $TRUE
} #end Help
#-----------------------------------------------#
homepage=""
return $TRUE
} #end cpdebtemp
# -Case start
# if no command line arg given
# set val to Unknown
if [ -z $1 ]
then
val="*** Unknown ***"
elif [ -n $1 ]
then
# otherwise make first arg as val
val=$1
fi
# use case statement to make decision for rental
case $val in
"trash") start_trash ;;
"help") start_help ;;
"www") firefox $homepage ;;
*) echo "Sorry, I can not get a $val for you!";;
esac
# Case stop