Unterminated quoted string error while running 'kubectl exec' using shell - shell

I am trying to run a kubectl exec command to run the command in the respective containers and transfer their outputs to a file using a shell script. I have the data in a YAML file with name of container, pod name, command and the file name where it is to be stored. I have parsed the YAML file using yq package and am trying to execute the commands. The commands having no quotes are executed successfully but the ones containing quotes result in an error. Collect execs contain name of the file and command.
I have tried running the commands normally on the command line and they seem to execute without any error. The error comes when I store them in a variable and then execute them.
Also doesn't work if I use " or ' or change ' to ".
FUNCTION
get_execs() {
mkdir ${EXECDIR}
for con in $(yq '.containers[] | .name' ${YFILE})
do
# echo $con
x=$(i=$con yq '.containers[] | select(.name == env(i)) | .collect_execs[] | .name' ${YFILE})
# printf "%s\n" "$x"
mkdir ${EXECDIR}/$con
for j in $x
do
c=$(i=$con p=$j yq '.containers[] | select(.name == env(i)) | .collect_execs[] | select(.name == env(p)) | .cmd' ${YFILE})
pod=$(i=$con yq '.containers[] | select(.name == env(i)) | .pod' ${YFILE})
# printf "%s abc\n" "$c"
kubectl exec -n ${NAMESPACE} $pod -c $con -- $c > ${EXECDIR}/$con/$j
done
done
}
YAML FILE STRUCTURE:
containers:
- name: otg-port-eth1-protocol-engine
pod: otg-port-eth1
collect_execs:
- name: resource-usage
cmd: top -c -n 2 -b -H -w 120
- name : disk-space
cmd : df -H
- name: cpu-info
cmd: cat /proc/cpuinfo
- name: interface-manager-threads-iter1
cmd : sh -c 'gdb --eval-command "set pagination 0" --eval-command "thread apply all bt" --batch --pid $(pidof InterfaceManager)'
- name: interface-manager-threads-iter2
cmd : sh -c 'gdb --eval-command "set pagination 0" --eval-command "thread apply all bt" --batch --pid $(pidof InterfaceManager)'
- name: interface-manager-shared-sos
cmd: sh -c 'cat /proc/$(pidof InterfaceManager)/maps'
- name: netstat
cmd: netstat -an
- name: dmesg
cmd : dmesg
- name : ifconfig
cmd : ifconfig
ERROR
--eval-command: 1: Syntax error: Unterminated quoted string
command terminated with exit code 2
--eval-command: 1: Syntax error: Unterminated quoted string
command terminated with exit code 2
/proc/$(pidof: 1: Syntax error: Unterminated quoted string
command terminated with exit code 2
--eval-command: 1: Syntax error: Unterminated quoted string
command terminated with exit code 2
--eval-command: 1: Syntax error: Unterminated quoted string
command terminated with exit code 2
/proc/$(pidof: 1: Syntax error: Unterminated quoted string
command terminated with exit code 2

The commands running with sh -c are causing the errors. To fix this, I changed the YAML structure and broke up the commands into 2 parts: precmd and cmd.
- name: resource-usage
cmd: top -c -n 2 -b -H -w 120
- name : disk-space
cmd : df -H
- name: cpu-info
cmd: cat /proc/cpuinfo
- name: interface-manager-threads-iter1
precmd : sh -c
cmd : 'gdb --eval-command "set pagination 0" --eval-command "thread apply all bt" --batch --pid $(pidof InterfaceManager)'
- name: interface-manager-threads-iter2
precmd : sh -c
cmd : 'gdb --eval-command "set pagination 0" --eval-command "thread apply all bt" --batch --pid $(pidof InterfaceManager)'
- name: interface-manager-shared-sos
precmd : sh -c
cmd: 'cat /proc/$(pidof InterfaceManager)/maps'
And then run the check:
if [ ! "$pc" = null ]
then
kubectl exec -n ${NAMESPACE} $pod -c $con -- $pc "$c" > ${EXECDIR}/$con/$j
else
kubectl exec -n ${NAMESPACE} $pod -c $con -- $c > ${EXECDIR}/$con/$j
fi
This seems to be working for me.

Related

command execute with error when use expect in shell script while work fine in pure shell

I try to start all the exited docker containers deployed in separated servers, so basically i should execute the essential command below
[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)
It worked fine like in pure linux shell , but then error occured(show below) when i try to use expect to "send" the "essential" command. (local ip is 241,remote end is 209)
[root#localhost start_shell_dir]# spawn ssh root#192.168.1.209
root#192.168.1.209's password:
Last login: Fri Oct 15 22:23:25 2021 from 192.168.1.241
[root#localhost ~]# invalid command name "0"
while executing
"0 -ne 0 "
invoked from within
"send "[ 0 -ne 0 ] && docker start ""
The error log shows i have already log in the remote machine, and something wrong when i execute the docker command.
Glenn jackman from the comment area shows me the basic rule for tcl,then i realize expect does command substitutions before sending real command. We may see it from execute bash -x script.sh .
[root#localhost start_shell_dir]# bash -x startContainer.sh
+ read ip pass
+ read ip pass
+ /usr/bin/expect
[root#localhost start_shell_dir]# ++ docker ps -a
++ grep Exited
++ wc -l
++ docker ps -a
++ grep Exited
++ cut '-d ' -f1
spawn ssh root#192.168.1.209
root#192.168.1.209's password:
Last login: Fri Oct 15 22:37:56 2021 from 192.168.1.241
[root#localhost ~]# invalid command name "0"
while executing
"0 -ne 0 "
invoked from within
"send "[ 0 -ne 0 ] && docker start ""
Anyway, the final command that work for me is the command showed below(replace double quotes with braces and with backslash before $() to keep it as an ordinary character rather than pre-parse it).
send {[ \$(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start \$(docker ps -a | grep Exited | cut -d' ' -f1)}
#!/bin/bash
# my original script with error
while read ip pass
do
{
/usr/bin/expect <<-END
spawn ssh root#$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
expect "#"
send "[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)"
expect eof
END
}&
done<apps_ip.txt
Like the shell, Tcl (and expect) allows interpolation with double quotes. Tcl uses square brackets for command substitution (in the same way that the shell uses $(...))
Use curly braces to protect the contents of that string (analogous to the shell's single quotes):
send {[ $(docker ps -a | grep Exited | wc -l) -ne 0 ] && docker start $(docker ps -a | grep Exited | cut -d' ' -f1)}
#....^.............................................................................................................^
# and don't forget to hit Enter
send "\r"
See https://www.tcl-lang.org/man/tcl8.6/TclCmd/Tcl.htm for the few syntax rules of Tcl.

how to execute kubectl from bash script

Team,
I have a basic function and am getting error. basically, am trying to run kubectl via bash script. i even tried switching to its directory but still not woring.
node_cordon_info() {
kgn='kubectl get node'
if [ -z "$total_dgx_nodes_NotReady_state" ]; then
echo "No dgx_nodes_Ready_state found"
else
cd /usr/bin/
for dgx_node in "${total_dgx_nodes_Ready_state}"
kubectl get node $dgx_node --no-headers -o json | jq '.metadata.name,.metadata.labels."ns.com/cordon-reason"' | paste - - | xargs -l1 -- sh -c 'echo $1 https://jirasw.test.com/browse/$2' --
fi
}
output
./dgx-node-status-local.sh: line 94: syntax error near unexpected token `kubectl'
which kubectl
/usr/bin/kubectl
echo $PATH
/usr/bin/kubectl:/home/dtlu/deployments/:/usr/lib/go-1.10/bin:/usr/local/bin/kubectl:/home/dtlu/deployments/:/usr/lib/go-1.10/bin:/home/dtlu/deployments/:/usr/lib/go-1.10/bin:/home/dtlu/deployments/:/usr/lib/go-1.10/bin:/home/dtlu/deployments/:/usr/lib/go-1.10/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/dtlu/backup/nvidia/nsv/maglev-validations:/usr/local/bin:/usr/local/bin:/usr/local/bin:/usr/local/bin:/usr/local/bin
My bad: did not close the for loop.
node_cordon_info() {
kgn='kubectl get node'
if [ -z "$total_dgx_nodes_NotReady_state" ]; then
echo "No dgx_nodes_Ready_state found"
else
cd /usr/bin/
for dgx_node in "${total_dgx_nodes_Ready_state}"; do
kubectl get node $dgx_node --no-headers -o json | jq '.metadata.name,.metadata.labels."ns.com/cordon-reason"' | paste - - | xargs -l1 -- sh -c 'echo $1 https://jirasw.test.com/browse/$2' --
done
fi
}

how to escape a shell command in bash script written by another bash script

Can anybody show me how to escape a shell command in bash script written by another bash script ?
For example my script looks like:
sudo sh -c "echo \"if who | grep tty | grep \`whoami\` > /dev/null\" > test.sh"
sudo sh -c "echo \"then\" >> test.sh"
sudo sh -c "echo \" echo ' log in '\" >> test.sh"
sudo sh -c "echo \"else\" >> test.sh"
sudo sh -c "echo \" exit\" >> test.sh"
sudo sh -c "echo \"fi\" >> test.sh"
I want that the script test.sh contains
if who | grep tty | grep `whoami`> /dev/null
then
echo 'user is log in '
else
exit
fi
Actually the command whoami is replaced by root.
Solution:
sudo tee /usr/local/bin/test.sh << 'EOF'
if who | grep tty | grep `whoami`> /dev/null
then
echo 'user is log in '
else
exit
fi
EOF
Complex quotes are most easily handled with a heredoc:
cat > test.sh << 'EOF'
if who | grep tty | grep `whoami`> /dev/null
then
echo 'user is log in '
else
exit
fi
EOF

Set a command to a variable in bash script problem

Trying to run a command as a variable but I am getting strange results
Expected result "1" :
grep -i nosuid /etc/fstab | grep -iq nfs
echo $?
1
Unexpected result as a variable command:
cmd="grep -i nosuid /etc/fstab | grep -iq nfs"
$cmd
echo $?
0
It seems it returns 0 as the command was correct not actual outcome. How to do this better ?
You can only execute exactly one command stored in a variable. The pipe is passed as an argument to the first grep.
Example
$ printArgs() { printf %s\\n "$#"; }
# Two commands. The 1st command has parameters "a" and "b".
# The 2nd command prints stdin from the first command.
$ printArgs a b | cat
a
b
$ cmd='printArgs a b | cat'
# Only one command with parameters "a", "b", "|", and "cat".
$ $cmd
a
b
|
cat
How to do this better?
Don't execute the command using variables.
Use a function.
$ cmd() { grep -i nosuid /etc/fstab | grep -iq nfs; }
$ cmd
$ echo $?
1
Solution to the actual problem
I see three options to your actual problem:
Use a DEBUG trap and the BASH_COMMAND variable inside the trap.
Enable bash's history feature for your script and use the hist command.
Use a function which takes a command string and executes it using eval.
Regarding your comment on the last approach: You only need one function. Something like
execAndLog() {
description="$1"
shift
if eval "$*"; then
info="PASSED: $description: $*"
passed+=("${FUNCNAME[1]}")
else
info="FAILED: $description: $*"
failed+=("${FUNCNAME[1]}")
done
}
You can use this function as follows
execAndLog 'Scanned system' 'grep -i nfs /etc/fstab | grep -iq noexec'
The first argument is the description for the log, the remaining arguments are the command to be executed.
using bash -x or set -x will allow you to see what bash executes:
> cmd="grep -i nosuid /etc/fstab | grep -iq nfs"
> set -x
> $cmd
+ grep -i nosuid /etc/fstab '|' grep -iq nfs
as you can see your pipe | is passed as an argument to the first grep command.

IBM runmqsc parameters with ssh getting null

SERVER="192.1.1.1"
QUEUE="QUSERV"
CHANNEL="CHN001"
COMMAND="display chstatus($CHANNEL) where(status eq RUNNING)"
RESULT=`ssh -n -i /home/admusr/.ssh/id_rsa -o ConnectTimeout=15 admusr#$SERVER "echo \"$COMMAND\" | /opt/mqm/bin/runmqsc $QUEUE | grep 'STATUS(RUNNING)' "`
echo $RESULT (comes blank)
Bash Script : the result is empty because the $QUEUE is assigned with "" when executing the script
Does anyone have some tip pls ?
This is the output for: ssh -n -i /home/admusr/.ssh/id_rsa -o ConnectTimeout=15 admusr#$SERVER "echo \"$COMMAND\" /opt/mqm/bin/runmqsc $QUEUE grep 'STATUS(RUNNING)' "
The output for above command is :
`grep STATUS(RUNNING)lay chstatus(QMBLUE1.QMRED1) where(status eq RUNNING)` /opt/mqm/bin/runmqsc QMBLUE1

Resources