bash script with parameters in a while loop [duplicate] - bash

This question already has answers here:
Read user input inside a loop
(6 answers)
Closed 4 years ago.
the script is:
#!/bin/bash
SCRIPTLOG=~/tmp/logfile
[ ! -d ~/tmp ] && mkdir ~/tmp
while getopts ":u:g:y" options; do
case $options in
u )
user=$OPTARG;;
g )
group=$OPTARG;;
* )
echo "wrong option: ${OPTARG}"
exit 1;;
esac
done
set -e
add_etx_members ()
{
user="${1}"
group="${2}"
set -u
echo "Users to add to ${group}:"
echo
echo "--- BEGIN ---"
printf "%-10s\n" $user
echo "--- END ---"
echo
while true; do
read -r -p "Continue? [y/n]: " REPLY
case $REPLY in
[yY]) echo;break ;;
[nNqQ]) echo;exit ;;
*) printf "\033[31m%s\033[0m\n" " invalid input: ${REPLY}"
esac
done
echo "here commands"
}
add_etx_members "${user}" "${group}" 2>&1 | tee -a ${SCRIPTLOG}
when I run single execution from command line it works (reaches "echo here commands"):
$ myscpt1 -u test -g ABC
Users to add to ABC:
--- BEGIN ---
test
--- END ---
Continue? [y/n]: y
here commands
$
but when I run it in while loop, it fails:
$ echo -e "ABC\nSDE"|while read a;do myscpt1 -u test -g ${a};done
Users to add to ABC:
--- BEGIN ---
test
--- END ---
invalid input: SDE
$
Although, the commands are the same as from single run:
$ echo -e "ABC\nSDE"|while read a;do echo myscpt1 -u test -g ${a};done
myscpt1 -u test -g ABC
myscpt1 -u test -g SDE

because the read inside the script is consuming the same input (the standard input is inherited from caller) as the while read calling the script.
may be resolved redirecting input inside script
exec < /dev/tty
or just for the while read
while ...;
done < /dev/tty
Note that while true could be changed by while read
or from outside depending on what you need
echo -e "ABC\nSDE"|while read a;do myscpt1 -u test -g ${a} < /dev/null;done

Related

Delete first line/chars on Windows Pipe

I'm trying to capture a live tcpdump from a custom designed linux system. The command I'm using so far is:
plink.exe -ssh user#IP -pw PW "shell nstcpdump.sh -s0 -U -w - not port 22 and not host 127.0.0.1" | "C:\Program Files\Wireshark\wireshark" -i -
This will fail since when executing the command on the remote system, this (custom) shell will output "Done" before sending data. I tried to find out a way to remove the "Done" message from the shell but doesn't appear to be any.
So I came up with this (added findstr -V):
plink.exe -ssh user#IP -pw PW "shell nstcpdump.sh -s0 -U -w - not port 22 and not host 127.0.0.1" | findstr -V "Done" | "C:\Program Files\Wireshark\wireshark" -i -
This works more or less fine since I will get some errors and the live capture will stop. I believe it might have something to do with the buffers, but I'm not sure.
Does anyone know of any other method of removing/bypassing the first bytes/chars of the output from plink/remote shell?
[edit]
as asked, nstcpdump.sh is a wrapper for tcpdump. as mentioned before, this system is highly customized. nstcpdump.sh code:
root#hostname# cat /netscaler/nstcpdump.sh
#!/bin/sh
# piping the packet trace to the tcpdump
#
# FILE: $Id: //depot/main/rs_120_56_14_RTM/usr.src/netscaler/scripts/nstcpdump.sh#1 $
# LAST CHECKIN: $Author: build $
# $DateTime: 2017/11/30 02:14:38 $
#
#
# Options: any TCPDUMP options
#
TCPDUMP_PIPE=/var/tmp/tcpdump_pipe
NETSCALER=${NETSCALER:-`cat /var/run/.NETSCALER`}
[ -r ${NETSCALER}/netscaler.conf ] && . ${NETSCALER}/netscaler.conf
TIME=${TIME:-3600}
MODE=${MODE:-6}
STARTCMD="start nstrace -size 0 -traceformat PCAP -merge ONTHEFLY -filetype PIPE -skipLocalSSH ENABLED"
STOPCMD="stop nstrace "
SHOWCMD="show nstrace "
NSCLI_FILE_EXEC=/netscaler/nscli
NSTRACE_OUT_FILE=/tmp/nstrace.out
NS_STARTTRACE_PIDFILE=/tmp/nstcpdump.pid
TRACESTATE=$(nsapimgr -d allvariables | grep tracestate | awk '{ print $2}')
trap nstcpdump_exit 1 2 15
nstcpdump_init()
{
echo "##### WARNING #####"
echo "This command has been deprecated."
echo "Please use 'start nstrace...' command from CLI to capture nstrace."
echo "trace will now start with all default options"
echo "###################"
if [ ! -d $NSTRACE_DIR ]
then
echo "$NSTRACE_DIR directory doesn't exist."
echo "Possible reason: partition is not mounted."
echo "Check partitions using mount program and try again."
exit 1
fi
if [ ! -x $NSCLI_FILE_EXEC ]
then
echo "$NSCLI_FILE_EXEC binary doesn't exist"
exit 1
fi
if [ -e $NSTRACE_OUT_FILE ]
then
rm $NSTRACE_OUT_FILE
echo "" >> $NSTRACE_OUT_FILE
fi
}
nstcpdump_start_petrace()
{
sleep 0.5;
$NSCLI_FILE_EXEC -U %%:.:. $STARTCMD >/tmp/nstcpdump.sh.out
rm -f ${NS_STARTTRACE_PIDFILE}
}
nstcpdump_start()
{
# exit if trace is already running
if [ $TRACESTATE -ne 0 ]
then
echo "Error: one instance of nstrace is already running"
exit 2
fi
nstcpdump_start_petrace &
echo $! > ${NS_STARTTRACE_PIDFILE}
tcpdump -n -r - $TCPDUMPOPTIONS < ${TCPDUMP_PIPE}
nstcpdump_exit
exit 1
}
nstcpdump_exit()
{
if [ -f ${NS_STARTTRACE_PIDFILE} ]
then
kill `cat ${NS_STARTTRACE_PIDFILE}`
rm ${NS_STARTTRACE_PIDFILE}
fi
$NSCLI_FILE_EXEC -U %%:.:. $STOPCMD >> /dev/null
exit 1
}
nstcpdump_usage()
{
echo `basename $0`: utility to view/save/sniff LIVE packet capture on NETSCALER box
tcpdump -h
echo
echo NOTE: tcpdump options -i, -r and -F are NOT SUPPORTED by this utility
exit 0
}
########################################################################
while [ $# -gt 0 ]
do
case "$1" in
-h )
nstcpdump_usage
;;
-i )
nstcpdump_usage
;;
-r )
nstcpdump_usage
;;
-F )
nstcpdump_usage
;;
esac
break;
done
TCPDUMPOPTIONS="$#"
check_ns nstcpdump
#nstcpdump_init
#set -e
if [ ! -e ${TCPDUMP_PIPE} ]
then
mkfifo $TCPDUMP_PIPE
if [ $? -ne 0 ]
then
echo "Failed creating pipe [$TCPDUMP_PIPE]"
exit 1;
fi
fi
nstcpdump_start
Regards

Prompt For User Input Within A Function/Piped If Statement - Bash

Is it possible to prompt for user input when calling a function in bash?
Take this for example:
#!/bin/bash
test1(){
echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
case $yn in
Yes ) make install; break;;
No ) exit;;
esac
done
}
pip list 2>/dev/null | if grep httplib2; then echo 2>/dev/null; else test1; fi
Disregard the fact I'm checking for httplib2 because I know that works fine if you else echo "Test". I've tried this with examples from stackoverflow and tldp, so I'm kind of confused now.
Can you not catch user input from a piped if statement?
Just tested with
pip list 2>/dev/null | if grep httplib2; then
echo 2>/dev/null;
else
echo "Type the year that you want to check (4 digits), followed by [ENTER]:"
read year
echo $year
fi
as well and had the same effect.
If test1 is in a pipeline but you need input from the terminal, use:
test1 </dev/tty
For example:
pip list 2>/dev/null | if grep httplib2; then echo 2>/dev/null; else test1 </dev/tty; fi
test1 gets its input from stdin. If you want to interact with it, its stdin must come from the terminal, /dev/tty, not from the pipeline.
An alternate method is to capture the handle for stdin before the pipeline starts. For example:
exec 3<&0; echo http | test1 <&3; exec 3<&-
Or,
exec 3<&0
pip list 2>/dev/null | if grep httplib2; then echo 2>/dev/null; else test1 <&3; fi
exec 3<&-

Write and read from a fifo from two different script

I have two bash script.
One script write in a fifo. The second one read from the fifo, but AFTER the first one end to write.
But something does not work. I do not understand where the problem is. Here the code.
The first script is (the writer):
#!/bin/bash
fifo_name="myfifo";
# Se non esiste, crea la fifo;
[ -p $fifo_name ] || mkfifo $fifo_name;
exec 3<> $fifo_name;
echo "foo" > $fifo_name;
echo "bar" > $fifo_name;
The second script is (the reader):
#!/bin/bash
fifo_name="myfifo";
while true
do
if read line <$fifo_name; then
# if [[ "$line" == 'ar' ]]; then
# break
#fi
echo $line
fi
done
Can anyone help me please?
Thank you
Replace the second script with:
#!/bin/bash
fifo_name="myfifo"
while true
do
if read line; then
echo $line
fi
done <"$fifo_name"
This opens the fifo only once and reads every line from it.
The problem with your setup is that you have fifo creation in the wrong script if you wish to control fifo access to time when the reader is actually running. In order to correct the problem you will need to do something like this:
reader: fifo_read.sh
#!/bin/bash
fifo_name="/tmp/myfifo" # fifo name
trap "rm -f $fifo_name" EXIT # set trap to rm fifo_name at exit
[ -p "$fifo_name" ] || mkfifo "$fifo_name" # if fifo not found, create
exec 3< $fifo_name # redirect fifo_name to fd 3
# (not required, but makes read clearer)
while :; do
if read -r -u 3 line; then # read line from fifo_name
if [ "$line" = 'quit' ]; then # if line is quit, quit
printf "%s: 'quit' command received\n" "$fifo_name"
break
fi
printf "%s: %s\n" "$fifo_name" "$line" # print line read
fi
done
exec 3<&- # reset fd 3 redirection
exit 0
writer: fifo_write.sh
#!/bin/bash
fifo_name="/tmp/myfifo"
# Se non esiste, exit :);
[ -p "$fifo_name" ] || {
printf "\n Error fifo '%s' not found.\n\n" "$fifo_name"
exit 1
}
[ -n "$1" ] &&
printf "%s\n" "$1" > "$fifo_name" ||
printf "pid: '%s' writing to fifo\n" "$$" > "$fifo_name"
exit 0
operation: (start reader in 1st terminal)
$ ./fifo_read.sh # you can background with & at end
(launch writer in second terminal)
$ ./fifo_write.sh "message from writer" # second terminal
$ ./fifo_write.sh
$ ./fifo_write.sh quit
output in 1st terminal:
$ ./fifo_read.sh
/tmp/myfifo: message from writer
/tmp/myfifo: pid: '28698' writing to fifo
/tmp/myfifo: 'quit' command received
The following script should do the job:
#!/bin/bash
FIFO="/tmp/fifo"
if [ ! -e "$FIFO" ]; then
mkfifo "$FIFO"
fi
for script in "$#"; do
echo $script > $FIFO &
done
while read script; do
/bin/bash -c $script
done < $FIFO
Given two script a.sh and b.sh where both scripts pass "a" and "b" to stdout, respectively, one will get the following result (given that the script above is called test.sh):
./test.sh /tmp/a.sh /tmp/b.sh
a
b
Best,
Julian

multiple sed operations creating empty file

When as part of shell script only one line is operating on a file using sed command the redirected file contains the updated data, as below
cat ${PROP_PATH}/${PROP_FILE} | sed "s!${ISTR_KEY}=.*!${ISTR_KEY}=${SIM_ISTR_KEY_VAL}!" > ${UPDATEDPROPS_DIR}/${PROP_FILE}
whereas when it is executed as part of a shell script, where after this another sed command updates the same file as in the below script at the end what i get is an empty file, why ? ..... ideas please.
(check 'switchAll2Sim()' function below)
#!/bin/ksh
#
SIM_ICR_KEY_VAL="http://www.example.com/sim/http/icr"
SIM_ISTR_KEY_VAL="http://www.example.com/sim/http/istr"
SIM_GT_KEY_VAL="http://www.example.com/sim/http/gtr"
#
ICR_KEY="interface.url.icr"
ISTR_KEY="interface.url.istr"
GT_KEY="interface.ws.url.gt"
## Property Files
PROP_PATH=""
PROP_FILE="properties"
##
DATE=`date +%m%d%Y`
DATETIME=`date +%m%d%Y-%T`
BCKUP_DIR=_bckup
UPDATEDPROPS_DIR=_updatedprops
# ----------------------------------
pause(){
echo "Press [Enter] key to continue..."
read fackEnterKey
}
permissions(){
chmod 777 ${UPDATEDPROPS_DIR}
}
backup(){
if [ ! -d "${BCKUP_DIR}" ]; then
mkdir ${BCKUP_DIR}
fi
if [ ! -d "${UPDATEDPROPS_DIR}" ]; then
mkdir ${UPDATEDPROPS_DIR}
fi
permissions
## keep backup of properties
cp ${PROP_PATH}/${PROP_FILE} ${BCKUP_DIR}/${PROP_FILE}_${DATETIME}
echo "Backup of property files completed at: " ${DATETIME}
}
#-------------------------------------------------------------
# switch all properties to SIM
#-------------------------------------------------------------
switchAll2Sim(){
backup
#
# update files
cat ${PROP_PATH}/${PROP_FILE} | sed "s!${ISTR_KEY}=.*!${ISTR_KEY}=${SIM_ISTR_KEY_VAL}!" > ${UPDATEDPROPS_DIR}/${PROP_FILE}
cat ${UPDATEDPROPS_DIR}/${PROP_FILE} | sed "s!${ICR_KEY}=.*!${ICR_KEY}=${SIM_ICR_KEY_VAL}!" > ${UPDATEDPROPS_DIR}/${PROP_FILE}
cat ${UPDATEDPROPS_DIR}/${PROP_FILE} | sed "s!${GT_KEY}=.*!${GT_KEY}=${SIM_GT_KEY_VAL}!" > ${UPDATEDPROPS_DIR}/${PROP_FILE}
echo "Switch all to SIM completed at: " ${DATETIME}
pause
}
# switch all properties to real
#-------------------------------------------------------------
switchAll2Real(){
pause
}
#-------------------------------------------------------------
dispCurrentStats(){
echo "Displaying current properties..."
echo "*********************************"
echo " File: " ${PROP_PATH}/${PROP_FILE}
grep ${ICR_KEY} ${PROP_PATH}/${PROP_FILE}
grep ${ISTR_KEY} ${PROP_PATH}/${PROP_FILE}
grep ${GT_KEY} ${PROP_PATH}/${PROP_FILE}
#
echo "*********************************"
pause
}
show_menus() {
clear
echo "~~~~~~~~~~~~~~~~~~~~~"
echo " M E N U"
echo "~~~~~~~~~~~~~~~~~~~~~"
echo "1. Display current properties"
echo "2. Switch all to real"
echo "3. Switch all to simulator"
echo "4. Exit"
}
# read input from the keyboard and take a action
read_options(){
read option
case $option in
1) dispCurrentStats ;;
2) switchAll2Real ;;
3) switchAll2Sim ;;
4) exit 0;;
*) echo "Please insert options 1 ~ 4";;
esac
}
# -----------------------------------
# Main - infinite loop
# ------------------------------------
while true
do
show_menus
read_options
done
Thanks, using '-i, says [sed: illegal option -- i]
Then you have to work with tmp files.
cp foo foo.tmp
sed "s/x/y/" foo.tmp > foo
/bin/rm foo.tmp
OR
sed "s/x/y/" foo > foo.tmp
/bin/mv -f foo.tmp foo
is probably more efficient.
I hope this helps.
Your problem is that cat is reading from the same file that sed is writing to.
cat foo | sed "s/x/y/" > foo
Will not work because cat and sed run at the same time, not one after the other.
To fix this try the -i option to sed.
sed -i "s/x/y/" foo

bash - how to pass arg values to shell script within in a cronjob

I have cron entry that looks like this which works: (passing in 5 inputs)
30 10 * * 5 sh /home/test.sh hostnm101.abc /mypath/dir test foobar F008AR >> /logs/mytst.log 2>&1
I want to change it so I store inputs (4,5) foobar and F008AR in a separate file and read in by
script test.sh ($4,$5)
test.sh
#!/bin/bash
if [ $# != 5 ]; then
exit 1
fi
DIRDT=`date '+%Y%m%d'`
TSTDIR=$2/test/$DIRDATE
[ ! -d "$TSTDIR" ] && ( mkdir "$TSTDIR" || { echo 'mkdir command failed'; exit 1; } )
perl /home/dev/tstextr.pl -n $1 -b $2 -d $TSTDIR/ -s $3 -u $4 -p $5 -f $DIRDT
Is there any easy way to do this within the cron for those (2) input values? Thanks.
Alright try this way.
1) Create a separate file /mypath/dir/login.info with content like this (username/password in separate lines):
foobar
F008AR
2) Modify your test.sh like this:
#!/bin/bash
if [ $# != 4 ]; then
exit 1
fi
DIRDT=`date '+%Y%m%d'`
TSTDIR=$2/test/$DIRDATE
[ ! -d "$TSTDIR" ] && ( mkdir "$TSTDIR" || { echo 'mkdir command failed'; exit 1; } )
IFS="
"
arr=( $(<$2/$4) )
#echo "username=${arr[0]} password=${arr[1]}"
perl /home/dev/tstextr.pl -n $1 -b $2 -d $TSTDIR/ -s $3 -u ${arr[0]} -p ${arr[1]} -f $DIRDT
3) Have your cron command like this:
30 10 * * 5 sh /home/test.sh hostnm101.abc /mypath/dir test login.info >> /logs/mytst.log 2>&1
Summary
IFS stands for Internal Field Separator (IFS) in bash
I am using it like this:
IFS="
"
Which means make new line character as field separator (since we are storing username and password in 2 separate lines). And then this line to read file /mypath/dir/login.info into an array:
arr=( $(<$2/$4) )
First line (username) is read into $arr[0]
Second line (password) is read into $arr[1]
You can echo it to check read content:
echo "username=${arr[0]}"
echo "password=${arr[1]}"
cron executes with sh -c so:
sh /home/test.sh hostnm101.abc /mypath/dir test `cat /mypath/dir/foobar_file` `cat /mypath/dir/F008AR_file` >> /logs/mytst.log 2>&

Resources