Bash pass argument for array selection - bash

I'm trying to pass the second argument to get an array and loop trough but im getting this error: ${$2[#]}: bad substitution
my code is:
/etc/init.d/displaycameras start c1
#!/bin/bash
dis1cam1="screen -dmS dis1cam1 sh -c 'omxplayer --avdict rtsp_transport:tcp --win \"0 0 640 428\" rtsp://myvideo --live -n -1'";
camera_feeds=('c1=(dis1cam1 dis1cam2 dis1cam3 dis1cam4 dis1cam5 dis1cam6 dis1cam8 dis1cam9)' 'c2=(dis2cam1 dis2cam2 dis2cam3 dis2cam4)')
for elt in "${camera_feeds[#]}";do eval $elt;done
# Start displaying camera feeds
case "$1" in
start)
for i in "${$2[#]}"
do
eval eval '$'$i
done
echo "Camera Display 1 Started"
;;
Is there a way to pass the 2nd argument to call the c2 set ?
in this way is working perfect:
#!/bin/bash
dis1cam1="screen -dmS dis1cam1 sh -c 'omxplayer --avdict rtsp_transport:tcp --win \"0 0 640 428\" rtsp://myvideo --live -n -1'";
camera_feeds=('c1=(dis1cam1 dis1cam2 dis1cam3 dis1cam4 dis1cam5 dis1cam6 dis1cam8 dis1cam9)' 'c2=(dis2cam1 dis2cam2 dis2cam3 dis2cam4)')
for elt in "${camera_feeds[#]}";do eval $elt;done
# Start displaying camera feeds
case "$1" in
start)
for i in "${c1[#]}"
do
eval eval '$'$i
done
echo "Camera Display 1 Started"
;;

I would strongly advise implementing this differently.
#!/usr/bin/env bash
die() { echo "$*" >&2; exit 1; }
[[ $BASH_VERSION = [0-3]* ]] && die "Bash 4.3 or newer needed"
[[ $BASH_VERSION = 4.[0-2].* ]] && die "Bash 4.3 or newer needed"
dis1cam1() { : "code to start camera dis1cam1 here"; )
dis1cam2() { : "code to start camera dis1cam2 here"; )
# ...etc...
camera_feeds__c1=(dis1cam1 dis1cam2 dis1cam3 dis1cam4 dis1cam5 dis1cam6 dis1cam8 dis1cam9)
camera_feeds__c2=(dis2cam1 dis2cam2 dis2cam3 dis2cam4)
# here, we're showing the iterate-over-all-feeds case
# you can just set var=camera_feeds__c1 yourself if you prefer
for var in "${!camera_feeds__#}"; do # var will be camera_feeds__c1 or camera_feeds__c2
feed_name=${var#camera_feeds__} # feed_name will be "c1" or "c2"
declare -n camera_feeds=$var
for i in "${camera_feeds[#]}"; do
echo "Starting $i in feed $feed_name" >&2
"$i" # look up and run code in variable named in $i
done
unset -n camera_feeds
done
"${camera_feeds__#}" expands to the list of shell variables whose names start with camera_feeds__; this is thus the name of our two arrays.
declare -n camera_feeds=$var then makes camera_feeds an alias for the array presently being iterated over, such that for i in "${camera_feeds[#]}" iterates over that array.
unset -n camera_feeds clears this association.

Related

How can I invoke another program's bash completion handler for a single subcommand of my program?

Context
I have an arduino-cli wrapper script named ino that reads target/build configuration from:
JSON files located in the sketch directory
Command-line flags/arguments
It then constructs and exec's the corresponding arduino-cli command-line.
Problem
As a convenience wrapper script, ino isn't intended to support every feature of arduino-cli. So for those tasks that ino doesn't automate, the user can instead invoke arduino-cli indirectly using the cli subcommand of ino.
For example, if the user types the following commands:
% ino cli update
% ino cli core list --all
The ino script will take everything following cli and simply append them to the arduino-cli executable. So they would be equivalent to the following commands:
% arduino-cli update
% arduino-cli core list --all
Since arduino-cli has nice bash completion for all of its subcommands and flags, I would like to hijack the same completion functionality for my ino cli subcommand.
What I've tried
The accepted answers here:
How do I autocomplete nested, multi-level subcommands?
Multi Level Bash Completion
These helped me understand how to identify the current subcommand and discriminate the completion results based upon it.
However, I couldn't figure out how to then invoke the arduino-cli completion handler using the remaining args.
Completion handler derived from accepted answer here:
How do I get bash completion for command aliases?
See my ino completion handler based on that answer below (Reference 1).
This question/answer isn't quite the same, because they can basically just install a completion handler on their alias. I'm needing to "install" one on an argument to a command/alias.
This almost seems to work. Try it with xtrace option enabled (set -x), and you can see the arduino-cli command-line is appearing in the args ... but following ino at position $0.
E.g., given ino cli core list --all to the wrapper handler, the arduino-cli handler receives ino arduino-cli core list --all. Not sure how to get rid of $0!
Reference
ino completion wrapper derived from alias-based wrappers
joinstr() {
local d=${1-} f=${2-}
shift 2 && printf %s "$f" "${#/#/$d}"
}
complete-subcmd() {
[[ ${#} -gt 2 ]] || {
printf "usage:\n\tcomplete-subcmd src-command... -- comp-func dst-command...\n"
return 1
}
# parse the command-line by splitting it into two command-lines
# of variable length, src-command and dst-command:
# 1. src-command is the trigger that invokes the real completion
# handler, comp-func.
# 2. dst-command is the leading args of the command-line passed
# to the real completion handler, comp-func, to produce the
# resulting completion choices.
unset -v dstparse
local -a srccmd dstcmd
local func
while [[ ${#} -gt 0 ]]; do
case "${1}" in
--)
# when we reach the delimiter, also shift in comp-func as
# the next argument (the real completion handler).
dstparse=1
shift
func=${1:-}
;;
*)
# if we aren't processing the delimiter, then all other
# args are appended to either src-command or dst-command.
if [[ -z ${dstparse} ]]; then
srccmd+=( "${1}" )
else
dstcmd+=( "${1}" )
fi
;;
esac
shift
done
# if the completer is dynamic and not yet loaded, try to load it
# automatically using the given command
if [[ $( type -t "${func}" ) != function ]]; then
type -p _completion_loader &> /dev/null &&
_completion_loader "${dstcmd[#]}"
fi
local wrap=$( joinstr _ "${srccmd[#]}" | tr -d -c '[A-Za-z_]' )
# replace our args with dst-command followed by whatever remains
# from the invoking command-line.
eval "
function _${wrap} {
(( COMP_CWORD+=$(( ${#dstcmd[#]} )) ))
COMP_WORDS=( "${dstcmd[#]}" \${COMP_WORDS[#]:1} )
"${func}"
return 0
}
"
# install this wrapper handler on the first word in src-command
complete -F "_${wrap}" "${srccmd[0]}"
}
complete-subcmd ino cli -- __start_arduino-cli arduino-cli
completion.bash from arduino-cli
# bash completion V2 for arduino-cli -*- shell-script -*-
__arduino-cli_debug()
{
if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
fi
}
# Macs have bash3 for which the bash-completion package doesn't include
# _init_completion. This is a minimal version of that function.
__arduino-cli_init_completion()
{
COMPREPLY=()
_get_comp_words_by_ref "$#" cur prev words cword
}
# This function calls the arduino-cli program to obtain the completion
# results and the directive. It fills the 'out' and 'directive' vars.
__arduino-cli_get_completion_results() {
local requestComp lastParam lastChar args
# Prepare the command to request completions for the program.
# Calling ${words[0]} instead of directly arduino-cli allows to handle aliases
args=("${words[#]:1}")
requestComp="${words[0]} __completeNoDesc ${args[*]}"
lastParam=${words[$((${#words[#]}-1))]}
lastChar=${lastParam:$((${#lastParam}-1)):1}
__arduino-cli_debug "lastParam ${lastParam}, lastChar ${lastChar}"
if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then
# If the last parameter is complete (there is a space following it)
# We add an extra empty parameter so we can indicate this to the go method.
__arduino-cli_debug "Adding extra empty parameter"
requestComp="${requestComp} ''"
fi
# When completing a flag with an = (e.g., arduino-cli -n=<TAB>)
# bash focuses on the part after the =, so we need to remove
# the flag part from $cur
if [[ "${cur}" == -*=* ]]; then
cur="${cur#*=}"
fi
__arduino-cli_debug "Calling ${requestComp}"
# Use eval to handle any environment variables and such
out=$(eval "${requestComp}" 2>/dev/null)
# Extract the directive integer at the very end of the output following a colon (:)
directive=${out##*:}
# Remove the directive
out=${out%:*}
if [ "${directive}" = "${out}" ]; then
# There is not directive specified
directive=0
fi
__arduino-cli_debug "The completion directive is: ${directive}"
__arduino-cli_debug "The completions are: ${out[*]}"
}
__arduino-cli_process_completion_results() {
local shellCompDirectiveError=1
local shellCompDirectiveNoSpace=2
local shellCompDirectiveNoFileComp=4
local shellCompDirectiveFilterFileExt=8
local shellCompDirectiveFilterDirs=16
if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
# Error code. No completion.
__arduino-cli_debug "Received error from custom completion go code"
return
else
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
if [[ $(type -t compopt) = "builtin" ]]; then
__arduino-cli_debug "Activating no space"
compopt -o nospace
else
__arduino-cli_debug "No space directive not supported in this version of bash"
fi
fi
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
if [[ $(type -t compopt) = "builtin" ]]; then
__arduino-cli_debug "Activating no file completion"
compopt +o default
else
__arduino-cli_debug "No file completion directive not supported in this version of bash"
fi
fi
fi
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
# File extension filtering
local fullFilter filter filteringCmd
# Do not use quotes around the $out variable or else newline
# characters will be kept.
for filter in ${out[*]}; do
fullFilter+="$filter|"
done
filteringCmd="_filedir $fullFilter"
__arduino-cli_debug "File filtering command: $filteringCmd"
$filteringCmd
elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
# File completion for directories only
# Use printf to strip any trailing newline
local subdir
subdir=$(printf "%s" "${out[0]}")
if [ -n "$subdir" ]; then
__arduino-cli_debug "Listing directories in $subdir"
pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
else
__arduino-cli_debug "Listing directories in ."
_filedir -d
fi
else
__arduino-cli_handle_standard_completion_case
fi
__arduino-cli_handle_special_char "$cur" :
__arduino-cli_handle_special_char "$cur" =
}
__arduino-cli_handle_standard_completion_case() {
local tab comp
tab=$(printf '\t')
local longest=0
# Look for the longest completion so that we can format things nicely
while IFS='' read -r comp; do
# Strip any description before checking the length
comp=${comp%%$tab*}
# Only consider the completions that match
comp=$(compgen -W "$comp" -- "$cur")
if ((${#comp}>longest)); then
longest=${#comp}
fi
done < <(printf "%s\n" "${out[#]}")
local completions=()
while IFS='' read -r comp; do
if [ -z "$comp" ]; then
continue
fi
__arduino-cli_debug "Original comp: $comp"
comp="$(__arduino-cli_format_comp_descriptions "$comp" "$longest")"
__arduino-cli_debug "Final comp: $comp"
completions+=("$comp")
done < <(printf "%s\n" "${out[#]}")
while IFS='' read -r comp; do
COMPREPLY+=("$comp")
done < <(compgen -W "${completions[*]}" -- "$cur")
# If there is a single completion left, remove the description text
if [ ${#COMPREPLY[*]} -eq 1 ]; then
__arduino-cli_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
comp="${COMPREPLY[0]%% *}"
__arduino-cli_debug "Removed description from single completion, which is now: ${comp}"
COMPREPLY=()
COMPREPLY+=("$comp")
fi
}
__arduino-cli_handle_special_char()
{
local comp="$1"
local char=$2
if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then
local word=${comp%"${comp##*${char}}"}
local idx=${#COMPREPLY[*]}
while [[ $((--idx)) -ge 0 ]]; do
COMPREPLY[$idx]=${COMPREPLY[$idx]#"$word"}
done
fi
}
__arduino-cli_format_comp_descriptions()
{
local tab
tab=$(printf '\t')
local comp="$1"
local longest=$2
# Properly format the description string which follows a tab character if there is one
if [[ "$comp" == *$tab* ]]; then
desc=${comp#*$tab}
comp=${comp%%$tab*}
# $COLUMNS stores the current shell width.
# Remove an extra 4 because we add 2 spaces and 2 parentheses.
maxdesclength=$(( COLUMNS - longest - 4 ))
# Make sure we can fit a description of at least 8 characters
# if we are to align the descriptions.
if [[ $maxdesclength -gt 8 ]]; then
# Add the proper number of spaces to align the descriptions
for ((i = ${#comp} ; i < longest ; i++)); do
comp+=" "
done
else
# Don't pad the descriptions so we can fit more text after the completion
maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
fi
# If there is enough space for any description text,
# truncate the descriptions that are too long for the shell width
if [ $maxdesclength -gt 0 ]; then
if [ ${#desc} -gt $maxdesclength ]; then
desc=${desc:0:$(( maxdesclength - 1 ))}
desc+="…"
fi
comp+=" ($desc)"
fi
fi
# Must use printf to escape all special characters
printf "%q" "${comp}"
}
__start_arduino-cli()
{
local cur prev words cword split
COMPREPLY=()
# Call _init_completion from the bash-completion package
# to prepare the arguments properly
if declare -F _init_completion >/dev/null 2>&1; then
_init_completion -n "=:" || return
else
__arduino-cli_init_completion -n "=:" || return
fi
__arduino-cli_debug
__arduino-cli_debug "========= starting completion logic =========="
__arduino-cli_debug "cur is ${cur}, words[*] is ${words[*]}, #words[#] is ${#words[#]}, cword is $cword"
# The user could have moved the cursor backwards on the command-line.
# We need to trigger completion from the $cword location, so we need
# to truncate the command-line ($words) up to the $cword location.
words=("${words[#]:0:$cword+1}")
__arduino-cli_debug "Truncated words[*]: ${words[*]},"
local out directive
__arduino-cli_get_completion_results
__arduino-cli_process_completion_results
}
if [[ $(type -t compopt) = "builtin" ]]; then
complete -o default -F __start_arduino-cli arduino-cli
else
complete -o default -o nospace -F __start_arduino-cli arduino-cli
fi
# ex: ts=4 sw=4 et filetype=sh
UPDATE2:
After I posted this I checked your links and after seeing the accepted answer here, I was thinking I am just old and forget that I just copied this code from that link, and it wasn't me who wrote it. Even the example used there is the same, but investigating the code further, it looks I did write this and used a different approach, and maybe this will help you understand what's going on. As I mentioned at the bottom UPDATE1 section: you need to tune the COMP variables then call the original function
Original:
I wrote an 'alias wrapper' script a couple of years ago.
The idea is to use the original bash completion with aliases even with parameters.
For example:
alias apti='apt-get install'
source alias-completion-wrapper _apt_get apti apt-get install
#here _apt_get is the original completion function
Now you can use tab to complete the package name after apti just like after apt-get install
#alias-completion-wrapper
#Example: . alias-completion-wrapper _apt_get apti apt-get install
comp_function_name="$1"
ali="$2"
shift 2
x="$#"
function_name=`echo _$# |tr ' ' _`
function="
function $function_name {
_completion_loader $1
(( COMP_CWORD += $# - 1 ))
COMP_WORDS=( $# \"\${COMP_WORDS[#]:1}\")
COMP_LINE=\"\${COMP_WORDS[#]}\"
let COMP_POINT=\${COMP_POINT}-${#ali}+${#x}
$comp_function_name
return 0
}"
eval "$function"
complete -F $function_name $ali
unset function function_name ali x
To be honest, I can't remember how it works and I didn't commented the script :)
But I think you will be able to tune this for your needs.
UPDATE1:
As I investigated the code a bit, it looks like the idea is to tune the COMP variables, then call the original function :)
UPDATE3:
I had some time, so the modification you need are:
This ${COMP_WORDS[#]} contains the current command line. ${COMP_WORDS[#]:1} cuts off the first word, which is originally the alias/command. As you want to use it after a parameter you have to cut off the parameter too.
COMP_WORDS=( $# \"\${COMP_WORDS[#]:2}\")
I don't see COMP_LINE and COMP_POINT in the other solution, but as I can recall without those, it doesn't worked well in certain circumstances. So I suppose you need:
COMP_LINE=\"\${COMP_WORDS[#]:1}\"
And here ${#ali} is the length of the command. You need to replace this with the length of your command with the parameter. eg,:"xcmd prm" -> 8 (count the space too)
let COMP_POINT=\${COMP_POINT}-${#ali}+${#x}
Not sure about (( COMP_CWORD += $# - 1 )) either remove the -1 or use -2 or leave it as it is :)
After the modifications, just change the eval to echo and remove the complete -F line. And source the script as described. This way it will echo the function what you can insert into your completion script.

Running two loops simultaneously in bash

I am reading file in which two different types of key-value pairs are there and I have to pass those key-value pairs to another file. For that I have to run two loops which doesn't seems me the optimized approach. Below the code I am trying and it will also clarify you what I am trying to do :
#!/bin/bash
set -e
# Prints command usage
function usage() {
cat <<EOF
Usage: $0 [--in-file <infile>] [--env <env>]
EOF
}
env=""
# Parse Args
POSITIONAL=()
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-i|--in-file)
in_file=$2
shift 2 # past argument and value
;;
-e|--env)
env="$2"
shift 2 # past argument and value
;;
*) # unknown option
echo "unknown option"
shift # past argument
;;
esac
done
set -- "${POSITIONAL[#]}" # restore positional parameters
if [[ ${in_file} == "" ]]; then
echo "ERROR: Missing in-file argument"
usage
exit 1
fi
cat ${in_file}
echo "Unsilencing alarms"
declare -A summary
overall_status_failed="false"
silence_ids=`yq read -j ${in_file} alarm_ids | jq -M -r .[]`
while read -r silence_id; do
echo "Unsilencing ${silence_id} silence id"
if [env != ""]; then
./unsilence_alarm.sh --alarm-id ${silence_id} --env ${env}|| script_failed="true"
else
./unsilence_alarm.sh --alarm-id ${silence_id}|| script_failed="true"
fi
if [[ "${script_failed}" == "true" ]]; then
summary["silence id ${silence_id} "]='Fail'
overall_status_failed="true"
script_failed="false"
else
summary["silence id ${silence_id} "]='Pass'
fi
done <<< "${silence_ids}"
echo "Unsilencing moody alarms"
silence_ids=`yq read -j ${in_file} moody_alarm_ids | jq -M -r .[]`
while read -r silence_id; do
echo "Unsilencing ${silence_id} moody silence id"
if [env != ""]; then
./unsilence_alarm.sh --moody-alarm-id ${silence_id} --env ${env}|| script_failed="true"
else
./unsilence_alarm.sh --moody-alarm-id ${silence_id}|| script_failed="true"
fi
if [[ "${script_failed}" == "true" ]]; then
summary["moody silence id ${silence_id}"]="Fail"
overall_status_failed="true"
script_failed="false"
else
summary["moody silence id ${silence_id}"]="Pass"
fi
done <<< "${silence_ids}"
set +x
echo
echo
echo
echo +------------------------- Unsilence summary --------------------------+
for status in "${!summary[#]}"; do
printf "${status} - ${summary[$status]}\n"
done
echo +----------------------------------------------------------------------+
if [[ "${overall_status_failed}" == "true" ]]; then
exit 1
fi
exit
Can anyone tell me the optimized approach for merging these two while loops? Another file accepts the parameters like:
[--alarm-id <id>] [--moody-alarm-id <id>] [--env <env>]
Combine both while blocks with the help of associative arrays and wrap it in for
Does this work as expected?
...
declare -A flags
flags[alarm_ids]="--alarm-id"
flags[moody_alarm_ids]="--moody-alarm-id"
for alarm_id in alarm_ids moody_alarm_ids; do
silence_ids=`yq read -j ${in_file} ${alarm_id} | jq -M -r .[]`
while read -r silence_id; do
echo "Unsilencing ${silence_id} silence id"
./unsilence_alarm.sh ${flags[$alarm_id]} ${silence_id} ${env:+"--env ${env}"} \
|| script_failed="true"
if [[ "${script_failed}" == "true" ]]; then
summary["silence ${flag[alarm_id]//-/ } ${silence_id} "]='Fail'
overall_status_failed="true"
script_failed="false"
else
summary["silence ${flag[alarm_id]//-/ } ${silence_id} "]='Pass'
fi
done <<< "${silence_ids}"
done
...
Simultaneous would not be easy. If you do need it, wrap the above code snippet in a function without the for loop; and use a parameter to pass the "key" to the the while loop. And then you could start them as background activities with foo &. But that would not make it easier for summary. Since you need to wait for it to complete anyway - synchronous is the way to go.
Also ${env:+"--env ${env}"} expands to the rhs of + if lhs (env) is not null. Using 4-statement long if-else block seemed tedious. I am not sure if this method is frowned upon, but this is how I personally would have done it.

set -e is shadowing signal function

I am running bash 4.4.19 on MacOS. I found the signal function doesn't work when if "set -e" is set. is it expected behavior? Here is my sample code:
#!/usr/local/bin/bash
set -e
declare -A Array1
Array1=([index1]="abc" [index2]="def" [index3]="dfkdjkfjdkdjfdk")
trap ReactSignal USR1
fun() {
PPid=$1
NUM=0
Array1[index4]="insidefunction"
while [ $NUM -le 5 ]
do
((NUM++))
echo "inside number is $NUM"
sleep 1
done
kill -USR1 $PPid
}
ReactSignal() {
IFS= read -r -d '' -u 3 checkOutput
echo "function output is ${checkOutput}"
}
Ppid="$$"
echo "start...."
coproc funfd { fun $Ppid; }
exec 3>&${funfd[0]}
echo "end...."
sleep 7
echo array value is ${Array1[#]}
It's because ((expression)) actually has a return code, as per the bash documentation:
((expression))
The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1.
So, of course, if NUM is zero going in to the expression, the return code will be one and set -e will pick this up as an error that needs termination.
There are any number of ways to solve this, from using the (rather ugly, in my opinion):
set +e ; ((NUM++)); set -e
to not using ((expression)) at all:
for i in {0..4} ; do
doSomethingWith $i
done

Create a script that makes two touch command in the right order always

For performing one task, I need to make two touch commands in a precise order:
touch aaaa-ref-bbb.done
touch cccc-grp-dddd.done
Beinng aaaa .. dddd any kind of strings. The first string contains "-ref-" and the second string contains "-done-"
I want to make a script that applies both touch commands, independently of the orders that the parameters are passed.
For instance (parameters in the wrong order)
./script.sh bla-grp-bla bleh-ref-bleh
Will produce an output of
touch bleh-ref-bleh
touch bla-grp-bla
If the parameters are written in the right order, the touch commands follow the right order.
I have done several tries and each change goes closer to the goal, but now I'm stuck.
Could you help with this?
#### tool for touch debug mode (set -x / set +x)
#!/bin/bash
#
#### USAGE
##### Constants
#start debug code
exec 5> >(logger -t $0)
BASH_XTRACEFD="5"
PS4='$LINENO: '
set -x
FIRSTPARAM=$1
SECONDPARAM=$2
echo $FIRSTPARAM
echo $SECONDPARAM
dotouch()
{
if [[ "$FIRSTPARAM" =~ 'ref' ]]; then
echo 'correct order, processing...'
sleep 3
firsttouch = $FIRSTPARAM'.done'
secondtouch = $SECONDPARAM'.done'
echo $firsttouch
touch $firsttouch
sleep 1
touch $secondtouch
echo "touch was" $1 $2
else
secondtouch = $FIRSTPARAM'.done'
firstouch = $SECONDPARAM'.done'
touch $firsttouch
sleep 1
touch $secondtouch
echo "touch was" $2 $1
fi
}
if [ "$FIRSTPARAM" =~ "ref" ] || [ "$FIRSTPARAM" =~ "grp" ]; then
dotouch()
echo "touch commands executed"
exit 0
else
echo "Usage: $0 [xxxx_ref_xxxx.tar] [xxxx_grp_yyyy.tar] "
exit 1
fi
exit 0
#end debug code
set +x
You are defining 2sttouch and 1ndtouch variables and using firsttouch and secondtouch. You should use the same variable names.
Let's start by putting your shebang line in the right place, and drastically simplifying the code;
#!/bin/bash
#### tool for touch debug mode (set -x / set +x)
exec 5> >(logger -t $0)
BASH_XTRACEFD="5"
PS4='$LINENO: '
set -x
FIRSTPARAM=$1
SECONDPARAM=$2
echo $FIRSTPARAM
echo $SECONDPARAM
dotouch() {
touch "$1"
echo "$Just touched $1"
return
}
case $FIRSTPARAM in
*"ref"*) dotouch $FIRSTPARAM'.done' ; dotouch $SECONDPARAM'.done' ;;
*"grp"*) dotouch $SECONDPARAM'.done' ; dotouch $FIRSTPARAM'.done' ;;
*) echo "Usage: $0 [xxxx_ref_xxxx.tar] [xxxx_grp_yyyy.tar] " ; exit 1 ;;
esac
exit 0
#end debug code
set +x
There was no need for most of that.
The problem is you are not considering the different cases on the main if before entering to the dotouch function. In your expression you are only evaluating the first parameter so you don't really know the content of the second parameter.
My suggestion is:
Create a doTouch function that simply touches 2 received parameters in the order they are received.
Add the different cases on the main code (if there are more, add more elif statements).
Here is the code (without the debug annotations):
#!/bin/bash
#######################################
# Script function helpers
#######################################
doTouch() {
local ref=$1
local grp=$2
echo "Touching $ref"
touch "$ref"
echo "Touching $grp"
touch "$grp"
echo "Touching order was: $ref $grp"
}
usage() {
echo "Usage: $0 [xxxx_ref_xxxx.tar] [xxxx_grp_yyyy.tar]"
}
#######################################
# Main
#######################################
# Retrieve parameters
FIRSTPARAM=$1
SECONDPARAM=$2
echo $FIRSTPARAM
echo $SECONDPARAM
# Check parameter order and touch
if [[ $FIRSTPARAM == *"ref"* ]] && [[ $SECONDPARAM == *"grp"* ]]; then
doTouch $FIRSTPARAM $SECONDPARM
elif [[ $SECONDPARAM == *"ref"* ]] && [[ $FIRSTPARAM == *"grp"* ]]; then
doTouch $SECONDPARM $FIRSTPARAM
else
usage
exit 1
fi
# Regular exit
exit 0

Run current bash script in background

Usually I add "&" character to start my process in backgroud, exemple :
user#pc:~$ my_script &
But how can I make it in background without "&" character ?
#!/bin/bash
#What can I add here to hide current process ($$) and to release focus ?
start_server()
{
#my script here with infinite loop ...
}
Thanks guys.
#!/bin/bash
if [[ "$1" != "--nodaemon" ]]; then
( "$0" --nodaemon "$#" </dev/null &>/dev/null & )
else
shift
fi
#...rest of script
What this does is check to see if its first argument is "--nodaemon", and if so fire itself ("$0") off in the background with the argument "--nodaemon", which'll prevent it from trying to re-background itself in a sort of infinite loop.
Note that putting this as the first thing in the script will make it always run itself in the background. If it only needs to drop into the background under certain conditions (e.g. when run with the argument "start"), you'd have to adjust this accordingly. Maybe something like this:
#!/bin/bash
start_server()
{
#my script here with infinite loop ...
}
if [[ "$1" = "start" ]]; then
( "$0" start-nodaemon </dev/null &>/dev/null & )
elif [[ "$1" = "start-nodaemon" ]]; then
start_server
elif #.....

Resources