I have one shell script.
#
. ./shprofile
if [ "$1" != "INSERT_ALL_ITEMS" -a "$1" != "INSERT_ONE_ITEM" ]
then
echo "Usage: $0 [INSERT_ALL_ITEMS|INSERT_ONE_ITEM]"
exit
fi
#
echo "Start Date & Time is.." `date`
case "$1" in
INSERT_ALL_ITEMS)
echo "Executing all Items"
;;
INSERT_ONE_ITEM)
#
echo "Executing one Item"
;;
*)
echo "Invalid Options!!"
;;
esac
#
echo "Stop Date & Time is.." `date`
If I execute the above script with below command
./runItemsBatch.sh INSERT_ALL_ITEMS
Then it is showing echo message, instead of executing the script
Usage: ./runItemsBatch.sh [INSERT_ALL_ITEMS|INSERT_ONE_ITEM]
What is the wrong in script? Why it is not executing the script, even though I give correct option.
Need help.
Given that your script ran fine on my CentOS 7 virtual machine (commenting the reference to the other file), I think it may be that you didn't set up the interpreter properly.
The first line (#) should be a shabang (#!) followed by the interpreter. e.g.: #!/bin/sh.
You could also try to remove the quotes for the arguments in your script because the interpreter could ...interpret them as literals.
I mean that you should try to replace the line
if [ "$1" != "INSERT_ALL_ITEMS" -a "$1" != "INSERT_ONE_ITEM" ]
with
if [ $1 != "INSERT_ALL_ITEMS" -a $1 != "INSERT_ONE_ITEM" ]
Try with either this two things. In my case however, the script worked with every combination of "shabang / no shabang and quotes / no quotes"
Hope I helped.
EDIT: Try also to delete the spaces between variables and operators in the if (Again, these are rules depending by the interpreter you are using, or defaulted to use)
Related
when I read the book linux shell scripting cookbook
they say when you wanna print !,you shouldn't put it in double quote,or you can add \ before ! to escape it.
e.g.
$echo "Hello,world!"
bash: !:event not found error
$echo "Hello,world\\!"
Hello,world!
but in my situation(ubuntu14.04), I get the answer like that:
$echo "Hello,world!"
Hello,world!
$echo "Hello,world\\!"
Hello,world\!
So, why in my machine can't get the same answer?
Why the escape symbol \ was printed as a normal symbol?
When you're typing interactively to the shell, ! has special meaning, it's the history expansion character. To prevent this special meaning, you need to put it in single quotes or escape it.
echo 'Hello, world!'
echo "Hello, world\!'
The reason it's not happening on Ubuntu may be because it's running a newer version of bash, which is apparently more selective about when history expansion occurs. It seems to require ! to be followed by alphanumerics, not punctuation.
You don't need to do this in scripts, because history is not normally enabled there. It's just for interactive shells.
Create a shell script called file.sh:
#!/bin/bash
# file.sh: a sample shell script to demonstrate the concept of Bash shell functions
# define usage function
usage(){
echo "Usage: $0 filename"
exit 1
}
# define is_file_exits function
# $f -> store argument passed to the script
is_file_exits(){
local f="$1"
[[ -f "$f" ]] && return 0 || return 1
}
# invoke usage
# call usage() function if filename not supplied
[[ $# -eq 0 ]] && usage
# Invoke is_file_exits
if ( is_file_exits "$1" )
then
echo "File found"
else
echo "File not found"
fi
Run it as follows:
chmod +x file.sh
./file.sh
./file.sh /etc/resolv.conf
In an attempt to stay consistent i have tried to use double brackets [[ ]] in all my if statements. I did however get into a problem when i was going to check the return value for a command i wanted to run. After testing several ways of creating an if statement i found that only without brackets could i execute a command.
The following does not work:
if [[ $command ]] ; then
echo "something"
fi
if [[ $(command) ]] ; then
echo "something"
fi
if [[ ${command} ]] ; then
echo "something"
fi
and the code above makes the if loop true even when the command was not run.
since the code above doesnt work with braces it doesnt work to use this either:
[[ $command ]] || echo "failed"
and it doesnt work in a subshell either.
The following works:
if $command ; then
echo "something"
fi
if $(command) ; then
echo "something"
fi
Why doesnt it work to place a command in an if loop with brackets, and why does the if loops above report true when it didnt even run the command ? I'm using bash version 4.1.9. Ive tried this many times and the if loops are just as simple as the ones i typed above, it just checks if a command was run successfully and exits if it wasnt.
The short answer is:
[ and [[ expect an expression.
if expects a command.
Saying:
[[ $(command) ]]
would essentially execute:
[[ -n <command_output> ]]
which may or may not be what you want. On the other hand, saying:
$command && echo something || echo other
would echo something or other based on the return code of the command (0 and non-zero respectively).
Double braces are a shortcut for test. In your examples, what's happening is that you're testing the shell variable $command for existence.
if [[ $PWD ]]; then
echo PWD is set to a value
fi
if [[ $NOT_A_REAL_VAR ]]; then
echo Nope, its not set
fi
In your second example, you're using command substitution to check that command output something on standard output.
if [[ $(echo hi) ]]; then
echo "echo said hi'
fi
if [[ $(true) ]]; then #true is a program that just quits with successful exit status
echo "This shouldn't execute"
fi
Your third example is the same as your first, pretty much. You use the curly braces if you want to group your variables. for example if you want to put an 's' after something.
WORD=Bike
echo "$WORDS" #won't work because "WORDS" isn't a variable
echo "${WORD}S" # will output "BikeS"
Then in your fifth example, you are running the program that is sitting inside command.
So, if you want to test some strings, use [[ ]] or [ ]. If you just want to test the exit status of a program, then don't use those, just use a bare if.
Check man test for details on the braces.
If you're just checking the return value of the command, drop the double brackets.
if $command
then
echo "Command succeeded"
else
echo "Command failed: $!"
fi
The double brackets are a test command. (Well, not really, but their a takeoff of the single square brackets that were an alias to the test command.) In early Bourne shell, you would see things like:
if test -z "$string"
then
echo "This is an empty string"
fi
The square brackets were syntactic sugar:
if [ -z "$string" ]
then
echo "This is an empty string"
fi
So, if you're not doing an actual test, you can eliminate the double or single square brackets.
If you're using square brackets, you should use the double ones and not the single ones because the double ones are a bit more forgiving and can do a bit more:
if [ -z $string ] # No quotes: This will actually fail if string is zero bytes!
if [[ -z $string ]] # This will work despite the lack of quotes
I have the following bash script file callee.sh which is being called from another script file caller.sh.
The callee.sh is as follows:
if [ $1 -eq 1 ];
then
echo inside $1
source ~/MYPROGRAMSRC/standAloneWordCount.sh $2
#echo "inside standalone branch"
#echo $1
elif [ $1 -eq 2 ];
then
#echo "inside distributed branch"
#echo $1
else
echo invalid option for first argument-\n Options:\n "distributed"\n or\n "standalone"\n
fi
As most people might be able to tell, this is a script I use to decide whether to run hadoop in distributed or standAlone mode depending on the arguments.
This script is called from caller.sh as follows
source callee.sh $2 $counterGlobal
where $2 is a number either 1 or 2 and $counterGlobal is some integer.
My problem is that the if condition in callee.sh never evaluates to True and hence my script standAloneWordCount.sh which I call from within callee.sh is never called. I am running with bash shell and have tried many variants of the if statement like:
if [ $(($1 == 1 )) ] -- (1)
In an echo statement just above the line -- (1) , the expression $(($1 == 1)) evaluates to 1 so I am baffled as to why I am unable to satisfy the if condition.
Also I keep getting the error where it says:
syntax error near unexpected token `else'
if anyone could help me out with these two errors, it would be much appreciated. As I've run out of ideas.
Thanks in advance!
have tried many variants of the if statement like:
if [ $(($1 == 1 )) ]
You should instead be saying:
if (($1 == 1)); then
...
fi
Regarding the Syntax error near unexpected tokenelse'`, it's not because of any code that you've shown above. It seems to originate from some other portion of your script.
If you're using bash, try using double square brackets:
if [[ $1 -eq 1 ]]; then
echo "inside 1"
fi
As for the syntax error, you need quotes around your text (which also means escaping the existing quotes or use single quotes):
echo -e "invalid option for first argument-\n Options:\n \"distributed\"\n or\n \"standalone\"\n"
The -e flag is there to let bash know you want the \n to evaluate to a newline.
I have a script, which is essentially a wrapper around an executable by the same name on a different machine. For the sake of example, i'll wrap printf here. My current script looks like this:
#!/bin/bash
ssh user#hostname.tld. printf "$#"
Unfortunately, this breaks when one of the arguments contains a space, e.g. i'd expect the following commands to give identical outputs.:
~$ ./wrap_printf "%s_%s" "hello world" "1"
hello_world1_
~$ printf "%s_%s" "hello world" "1"
hello world_1
The problem gets even worse when (escaped) newlines are involved. How would I properly escape my arguments here?
Based on the answer from Peter Lyons, but also allow quotes inside arguments:
#!/bin/bash
QUOTE_ARGS=''
for ARG in "$#"
do
ARG=$(printf "%q" "$ARG")
QUOTE_ARGS="${QUOTE_ARGS} $ARG"
done
ssh user#hostname.tld. "printf ${QUOTE_ARGS}"
This works for everything i've tested so far, except newlines:
$ /tmp/wrap_printf "[-%s-]" "hello'\$t\""
[-hello'$t"-]
#!/bin/sh
QUOTE_ARGS=''
for ARG in "$#"
do
QUOTE_ARGS="${QUOTE_ARGS} '${ARG}'"
done
ssh user#hostname.tld. "${QUOTE_ARGS}"
This works for spaces. It doesn't work if the argument has an embedded single quote.
Getting quoting right is pretty hard and doing it in bash (in a general and robust way) almost impossible.
Use Perl:
#!/usr/bin/perl
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new('user#hostname');
$ssh->system('printf', #ARGV);
Based on the answers from Koert and Peter Lyons, here a wrapper for ssh; i call it "sshsystem". (also available at https://gist.github.com/4672115)
#!/bin/bash
# quote command in ssh call to prevent remote side from expanding any arguments
# uses bash printf %q for quoting - no idea how compatible this is with other shells.
# http://stackoverflow.com/questions/6592376/prevent-ssh-from-breaking-up-shell-script-parameters
sshargs=()
while (( $# > 0 )); do
case "$1" in
-[1246AaCfgKkMNnqsTtVvXxYy])
# simple argument
sshargs+=("$1")
shift
;;
-[bcDeFIiLlmOopRSWw])
# argument with parameter
sshargs+=("$1")
shift
if (( $# == 0 )); then
echo "missing second part of long argument" >&2
exit 99
fi
sshargs+=("$1")
shift
;;
-[bcDeFIiLlmOopRSWw]*)
# argument with parameter appended without space
sshargs+=("$1")
shift
;;
--)
# end of arguments
sshargs+=("$1")
shift
break
;;
-*)
echo "unrecognized argument: '$1'" >&2
exit 99
;;
*)
# end of arguments
break
;;
esac
done
# user#host
sshargs+=("$1")
shift
# command - quote
if (( $# > 0 )); then
# no need to make COMMAND an array - ssh will merge it anyway
COMMAND=
while (( $# > 0 )); do
arg=$(printf "%q" "$1")
COMMAND="${COMMAND} ${arg}"
shift
done
sshargs+=("${COMMAND}")
fi
exec ssh "${sshargs[#]}"
The easiest and quickest is to just use Bash's Quoting Parameter Transformation: ${parameter#Q}. This can automatically applied during array expansion with ${array[#]#Q}, but when using the builtin argument array, the name and the brackets are dropped, so it becomes ${##Q}. Therefore the original script only needs 4 characters added to it to work.
#!/bin/bash
ssh user#hostname.tld. printf "${##Q}"
Now any escaping will work, even terminal colors like this:
./wrap_printf "%s\e[39m\e[49m\n" $'\e[30m\e[42mBlack on Green' "Just Normal Text"
On my terminal in Ubuntu, I often run programs which keep running for a long time. And since there are a lot of these programs, I keep forgetting which terminal is for which program, unless I tab through all of those. So I wanted to find a way to update my terminal title to the program name, whenever I run a command. I don't want to do it manually.
I use gnome-terminal, but answer shouldn't really depend on that. Basically, If I'm able to run a second command, then I can simply use gconftool command to update the title. So I was hoping to find a way to capture the command in bash and update the title after every command. How do I do that?
I have some answers for you :) You're right that it shouldn't matter that you're using gnome-terminal, but it does matter what command shell you're using. This is a lot easier in zsh, but in what follows I'm going to assume you're using bash, and that it's a fairly recent version (> 3.1).
First of all:
Which environment variable would
contain the current 'command'?
There is an environment variable which has more-or-less what you want - $BASH_COMMAND. There's only one small hitch, which is that it will only show you the last command in a pipe. I'm not 100% sure what it will do with combinations of subshells, either :)
So I was hoping to find a way to
capture the command in bash and update
the title after every command.
I've been thinking about this, and now that I understand what you want to do, I realized the real problem is that you need to update the title before every command. This means that the $PROMPT_COMMAND and $PS1 environment variables are out as possible solutions, since they're only executed after the command returns.
In bash, the only way I can think of to achieve what you want is to (ab)use the DEBUG SIGNAL. So here's a solution -- stick this at the end of your .bashrc:
trap 'printf "\033]0;%s\007" "${BASH_COMMAND//[^[:print:]]/}"' DEBUG
To get around the problem with pipes, I've been messing around with this:
function settitle () {
export PREV_COMMAND=${PREV_COMMAND}${#}
printf "\033]0;%s\007" "${BASH_COMMAND//[^[:print:]]/}"
export PREV_COMMAND=${PREV_COMMAND}' | '
}
export PROMPT_COMMAND=${PROMPT_COMMAND}';export PREV_COMMAND=""'
trap 'settitle "$BASH_COMMAND"' DEBUG
but I don't promise it's perfect!
Try this:
trap 'echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"' DEBUG
Thanks to the history 1 it works even with complicated expressions like:
true && (false); echo $? | cat
For which approaches relying on $BASH_COMMAND or $# fail. For example simon's displays:
true | echo $? | cat
Thanks to Gilles and simon for providing inspiration.
I see what stoutie is trying to do, except it's a lot more work than needed. And doesn't cause all sorts of other potentially bad things that can occur as a result of redefining 'cd' and putting in all of that testing just to change directories. Bash has built in support for most of this.
You can put this in your .bashrc anywhere after you set your current PS1 prompt (this way it just prepends it)
# If this is an xterm set the titlebar to user#host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;\u#\h: \w\a\]$PS1"
;;
*)
;;
esac
The OP asked for bash, but others might be interested to learn that (as mentioned above) this is indeed a lot easier using the zsh shell. Example:
# Set window title to command just before running it.
preexec() { printf "\x1b]0;%s\x07" "$1"; }
# Set window title to current working directory after returning from a command.
precmd() { printf "\x1b]0;%s\x07" "$PWD" }
In preexec, $1 contains the command as typed (requires shell history to be enabled, which seems to be a fair assumption), $2 the expanded command (shell aliases etc.) and $3 the "very expanded" command (shell function bodies). (more)
I'm doing something like this, to show my pwd in the title, which could be modified to do whatever you want to do with the title:
function title { echo -en "\033]2;$1\007"; }
function cd { dir=$1; if [ -z "$dir" ]; then dir=~; fi; builtin cd "$dir" && title `pwd`; }
I just threw this in my ~/.bash_aliases.
Update
I ran into strange bugs with my original answer. I ended up picking apart the default Ubuntu PS1 and breaking it into parts only to realize one of the parts was the title:
# simple prompt
COLOR_YELLOW_BOLD="\[\033[1;33m\]"
COLOR_DEFAULT="\[\033[0m\]"
TITLE="\[\e]0;\u#\h:\w\a\]"
PROMPT="\w\n$ "
HUH="${debian_chroot:+($debian_chroot)}"
PS1="${COLOR_YELLOW_BOLD}${TITLE}${HUH}${PROMPT}${COLOR_DEFAULT}"
Without breaking into variables, it would look like this:
PS1="\[\033[1;33m\]\[\e]0;\u#\h:\w\a\]${debian_chroot:+($debian_chroot)}\w\n$ \[\033[0m\]"
I have tested three method, all is OK, use any one for your pleasure.
export PROMPT_COMMAND='echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"'
trap 'echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"' DEBUG
trap 'echo -ne "\e]0;"; echo -n $BASH_COMMAND; echo -ne "\a"' DEBUG
please note if use $BASH_COMMAND, it don't recognize bash alias, and use PROMPT_COMMAND show finished command, but use trap show running command.
Based on the the need to auto position putty windows I have modified my /etc/bash.bashrc file on a Debian/Ubuntu system. I have posted the full contents for completeness but the relevant bit to starts on the # Display command ... comment line.
# System-wide .bashrc file for interactive bash(1) shells.
# To enable the settings / commands in this file for login shells as well,
# this file has to be sourced in /etc/profile.
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, overwrite the one in /etc/profile)
PS1='${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
# Display command run in title which allows us to distinguish Kitty/Putty
# windows and re-position easily using AutoSizer window utility. Based on a
# post here: http://mg.pov.lt/blog/bash-prompt.html
case "$TERM" in
xterm*|rxvt*)
# Show the currently running command in the terminal title:
# http://www.davidpashley.com/articles/xterm-titles-with-bash.html
show_command_in_title_bar()
{
case "$BASH_COMMAND" in
*\033]0*)
# The command is trying to set the title bar as well;
# this is most likely the execution of $PROMPT_COMMAND.
# In any case nested escapes confuse the terminal, so don't
# output them.
;;
*)
echo -ne "\033]0;${USER}#${HOSTNAME}: ${BASH_COMMAND}\007"
;;
esac
}
trap show_command_in_title_bar DEBUG
;;
*)
;;
esac
# Commented out, don't overwrite xterm -T "title" -n "icontitle" by default.
# If this is an xterm set the title to user#host:dir
#case "$TERM" in
#xterm*|rxvt*)
# PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME}: ${PWD}\007"'
# ;;
#*)
# ;;
#esac
# enable bash completion in interactive shells
if ! shopt -oq posix; then
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
fi
# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
function command_not_found_handle {
# check because c-n-f could've been removed in the meantime
if [ -x /usr/lib/command-not-found ]; then
/usr/bin/python /usr/lib/command-not-found -- "$1"
return $?
elif [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/bin/python /usr/share/command-not-found/command-not-found -- "$1"
return $?
else
printf "%s: command not found\n" "$1" >&2
return 127
fi
}
fi
You can set up bash such that it sends a certain escape sequence to the terminal every time it starts an external program. If you use the escape sequence that terminals use to update their titles, your problem should be solved.
I have used that before, so I know it is possible. but I cannot remember it off the top of my head and do not have time to research the details right now, though.
Some of the old methods were removed from gnome-terminal 3.14 due to these two bugs (724110 and 740188).
In Ubuntu 20.04
PS1=$PS1"\[\e]0;New_Terminal_Name\a\]"
\[ begin a sequence of non-printing characters
\e]0; is the char sequence for setting the terminal title. Bash identifies this sequence and set the tile with the following characters. Number 0 turns out to be the value to reference the title property.
New_Terminal_Name is the tile we gave
\a is the ASCII bell character, also in this case, it marks the end of the tile to read from Bash.
\] end a sequence of non-printing characters
We can create a function for future use
function set_title(){
if [ -z "$PS1_BACK" ]; # set backup if it is empty
then
PS1_BACK="$PS1"
fi
TITLE="\[\e]0;$*\a\]"
PS1="${PS1_BACK}${TITLE}"
}
Open the ~/.bashrc file in your home directory with a text editor and append the above function at the end of it. Save and close.
To use it immediately source it to the current terminal.
source ~/.bashrc
We can use it then like this
set_title <New terminal tab title>
My terminal window titler script
This dynamic backgrounded script show all running command with pid number and elapsed time in seconds, like if I run du -h | less, this will build title looking like:
204640 6 du -h | 204641 6 less
Then when no command (other than himself) are running, don't change the terminal title, so standard behaviours works normaly.
First run start backgroud task. Second run in same terminal ask for kill previous backgrounded task.
Save this into a file, set execute flag then run it without argument:
cat <<"EOF" >titleWin.sh
#!/bin/bash
## Ask for kill process if already started
mapfile -t pids < <(ps -C ${0##*/} ho pid)
for pid in ${pids[#]} ;do
if [[ $pid != $$ ]] && [ -d /proc/$pid ]; then
echo -n "STARTED: [$pid]: ${0##*/}. Kill them (Y/n)? "
read -rsn 1 act
case $act in
n|N ) echo No;;
* ) echo Yes;kill $pid ;;
esac
exit
fi
done
## Title win for xterm or screen (or tmux).
case $TERM in
xterm*|rxvt* ) titleFmt='\e];%s\a';;
screen* ) titleFmt='\ek%s\e\\';;
* ) echo "Unable to title window.";exit 1;;
esac
tty=$(tty)
## Date to epochseconds converter
exec {dateout}<> <(:)
exec {datein}> >(exec stdbuf -o0 date -f - +%s >&$dateout)
DPID=$!
trap "echo TRAP;kill $DPID" 1 2 3 6 9 15
# Main loop
while :;do
string=""
while read -r pid wday mon day time year cmd; do
if [[ $pid != $$ ]] && [[ $pid != $PPID ]] && [[ $pid != $BASHPID ]] &&
[[ $pid != $DPID ]] && [ "${cmd#*pid,lstart,cmd}" ] &&
[ -d /proc/$pid ] ;then
echo >&${datein} $wday $mon $day $time $year
read -ru $dateout date
string+="$pid $((EPOCHSECONDS-date)) $cmd | "
fi
done < <(exec ps --tty ${tty#*/dev/} ho pid,lstart,cmd)
[[ "$string" ]] && printf "$titleFmt" "${string% | }"
sleep .333
done &
EOF
chmod +x titleWin.sh
./titleWin.sh