SSH running sh script that works locally but not remotely - bash

I am trying to run a script that will be called by some other software to run some parameters to get out objective values.
The script run.sh is as follows:
#!/bin/bash
set -e
ssh id#somehost '
/path/to/folder/solver arg1 arg2 arg3
res=$(</path/to/folder/res_data.txt)
echo "Final Result:"
echo "1 $res"
'
Running this file results in the following:
$ sh run.sh
OpenNN Exception: NeuralNetwork class.
void load(const std::string&) method.
Cannot load XML file ../data/neural_network.xml.
Final Result:
1 -285361 3.22136
Connection to somehost closed.
The Final Result above is from a previous output
If I run a similar script without the ssh
set -e
/path/to/folder/solver arg1 arg2 arg3
res=$(</path/to/folder/res_data.txt)
echo "Final Result:"
echo "1 $res"
Results in
$ sh run.sh 7 26 100
Final Result:
1 -285361 3.22136
$ sh run.sh 7 26 150
Final Result:
1 -421429 5.16397
Does anyone have any idea how to fix this?

Based on the comments above the solution of the error I was getting,
#!/bin/bash
set -e
ssh id#somehost '
cd /path/to/folder/
./solver '$1' '$2' '$3'
res=$(<./res_data.txt)
echo "Final Result:"
echo "1 $res"
'
I was simple enough just to add cd /path/to/folder/ and run the script from the folder it seems to work, in addition I also fixed the issue with the arguments rather than ./solver $1 $2 $3, having ./solver '$arg1' '$arg2' '$arg3' as the way to pass the inputted arguments to run on the solver.
The following output is from the corrected file above
$ sh run.sh 7 26 100
Final Result:
1 -285361 3.22136
$sh run.sh 7 26 150
Final Result:
1 -421429 5.16397

Related

bash script to iterate from middle of a list

I have a bash script that iterates over a folder of sql scripts. I want it to run intelligently when the script encounters an error. I don't want to run a step twice.
for a in $files_list; do
command 1
command 2
.
.
done
If 1,2,3,4,5 are the files to be executed and script exits at step 3 with some error. In next execution, I want to run from step 3 only. I have a variable in place for detecting at which step the script exited. I am stuck at starting the iteration from that variable.
Is it something that can be done? Any suggestion is accepted. I'm open to change my logic as well.
Didn't really spend much time but Let's say all the commands you want to execute are stored in a file test.txt
test.txt
ls
cal
du
d
date
ls
The following bash script takes two arguments filename where the commands are stored and the checkpoint from where it needs to start.
test.sh
## Function to read a file line by line
checkpoint=$2
filename=$1
checkpointReached=false
read_file_line_by_line() {
local file="$1"
while IFS= read -r line
do
if [ $checkpointReached == "false" ] && [ "$line" != "$checkpoint" ] ; then
echo "...... Finding checkpoint....... current line is $line"
fi
if [ "$line" == "$checkpoint" ]; then
echo " *************************************************
Checkpoint reached. Commands after checkpoint
*****************************************************"
checkpointReached=true
continue
fi
if $checkpointReached; then
echo " $line"
execute_command $line
fi
done < "$file"
}
function execute_command() {
local command="$1"
echo " Executing .......... $command"
if eval $command; then
echo " Command executed successfully"
else
echo " Command failed. Exiting........"
echo " Please note the new checkpoint.... $command"
exit 1
fi
}
read_file_line_by_line "$filename"
Let's run it the first time, the checkpoint is the first command i.e. ls and its expected to fail at d which is invalid command
╰─ bash test.sh text.txt "ls"
*************************************************
Checkpoint reached. Commands after checkpoint
*****************************************************
Executing .......... ls
test.sh test.yaml text.txt
Command executed successfully
du
Executing .......... du
24 .
Command executed successfully
d
Executing .......... d
test.sh: line 33: d: command not found
Command failed. Exiting........
Please note the new checkpoint.... d
Now we know the checkpoint. So we pass it along. Assuming that d is fixed now, we change it with echo for the sake of the script. In your case the script d would be fixed.
╰─ bash test.sh text.txt "echo"
...... Finding checkpoint....... current line is ls
...... Finding checkpoint....... current line is du
*************************************************
Checkpoint reached. Commands after checkpoint
*****************************************************
Executing .......... echo
Command executed successfully
date
Executing .......... date
Mon Sep 12 14:50:44 +04 2022
Command executed successfully
Let me know if this helps.

Run/execute concatenated variable as command

word2018 FILE_2018 > FILE_2018.out &
this works OK for me in bin/bash, terminal, konsole, BASH
I have an text-file script in #!/bin/bash, where I run this variable CMD and it works in CSH, but not in BASH.
CMD="word${year} ${FILE_version.txt} > ${FILE_version.out} &"
word2018 - system alias defined/sourced from other location
word - static name
2018 - variable (can be any number 2015, 2022,....)
FILE_version - variable (can be - FILE_2015, FILE_2022,...)
csh -c "$CMD" ...works OK
bash -c "$CMD" ...NOT working, command word2018 not found
any advice, help? I would appreciate very well
Just call $CMD
testABC(){
echo "arg: $1"
}
alias_name=test
#....system alias
var1=ABC
var2=123
CMD="${alias_name}${var1} ${var2}"
echo CMD: $CMD
$CMD
result:
CMD: testABC 123
arg: 123

Get command used to start a script

How to get the command previously used to start a shell script?
for example:
nohup /script_name.sh &
Inside the script itself, how to check if "nohup" has been used?
Thanks.
You want to use the $_ parameter in your script.
Example: shell.sh
#!/bin/bash
echo $_;
user#server [~]# sh shell.sh
/usr/bin/sh
user#server [~]#
Additionally:
If you want to get rid of that full path - /usr/bin/sh - utilize basename command.
#!/bin/bash
echo `basename $_`;
user#server [~]# sh shell.sh
sh
user#server [~]#
well that depends on the script in question.There're many ways to execute a script like:
./<scriptname> #chmod 700 <scriptname> should be done before executing this script
bash <scriptname> # provided bash is used for executing the script.
or if you just want to get the name of script2 in script1, then use sed or awk for parsing the script1 with regular expression => /script2/.
Try this:
cat <script1> | awk '{ if( $0 ~ /^[^#].* \/scriptname.sh/ ){ print $1}}'
#codebaus thanks, doing something like this works but using strace definitely not.
#!/bin/bash
# echo $_
# echo $0
if grep "sh" $_ >/dev/null ; then
exit 1
fi ;
echo "string" ;
I believe you want to run this?:
#!/bin/bash
# echo $_
# echo $0
if grep "sh" $_ 2> /dev/null ; then
exit 1
fi ;
echo "string";
user#server [~]# sh shell.sh
Binary file /usr/bin/sh matches
user#server [~]#
Not sure what you are trying to accomplish in the end game. But $_ should give you what you need based on your initial question.
Additionally:
As I did not answer your strace comment, apologies. Based on the previous code above.
strace sh shell.sh
wait4(-1, Binary file /usr/bin/strace matches
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 874
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD (Child exited) # 0 (0) ---

Bash set -x echo redirects as well as commands

I am writing a Bash script where I want all commands to be echoed as they occur. I know that I need to use set -x and set +x to toggle this behaviour off and on, respectively (SOF post here). However, it doesn't echo everything, namely, I/O redirects.
For example, let's say I have this very simple Bash script:
set -x
./command1 > output1
./command2 arg1 arg2 > output2
This will be the output
+ ./command1
+ ./command2 arg1 arg2
Bash is not echoing my stdout redirect to output1 and output2. Why is this? How can I achieve this behaviour? Perhaps is there a shopt option that I must set in the script?
NOTE: I also noticed that pipes will not print as expected. For example, if I were to use this command:
set -x
./command3 | tee output3
I will get this output:
+ tee output3
+ ./command3
How do I make the commands be echoed exactly in the way they are written instead of having the pipe reordered by the script?
Neither set -x nor the DEBUG trap will provide full information when certain constructs are used.
Examples are:
ex1.sh
for i in a b c
do
echo $i # Where does the output go?
done > outfile # Here!
ex2.sh
c="this"
case "$c" in
this) echo sure ;; # Where does the output go?
that) echo okay ;;
esac > choice # Here!
ex3.sh
what="up"
while [ "$what" = "up" ]
do
echo "Going down!" # Where does the output go?
what="down"
echo "Newton rulezzz!" > thelaw
done > trying # Here!
and many more like these, let alone all kinds of nested variants. I don't know of an easy way to handle these, except to enter into the land of full Bash script parsing, which is a minefield...
EDIT: If Bash-specific features are not required, and backwards compatibility with the Bourne shell will do, the Korn shell (ksh, tested with version 93u+ 2012-08-01) does a bit better on showing information for redirects:
$ ksh -x ex1.sh
+ 1> outfile
+ echo a
+ echo b
+ echo c
$ ksh -x ex2.sh
+ c=this
+ 1> choice
+ echo sure
$ ksh -x ex3.sh
+ what=up
+ 1> trying
+ [ up '=' up ]
+ echo 'Going down!'
+ what=down
+ echo 'Newton rulezzz!'
+ 1> thelaw
+ [ down '=' up ]
You should rather use set -v.
-v Print shell input lines as they are read.
The output seems to meet your expectation.
$ set -v
$ ./command1 > output1
./command1 > output1
sh: ./command1: No such file or directory
$ ./command2 arg1 arg2 > output2
./command2 arg1 arg2 > output2
sh: ./command2: No such file or directory
$ ./command3 | tee output3
./command3 | tee output3
sh: ./command3: No such file or directory
It isn't possible with set -x. Your only option is to view the current command through a DEBUG trap.
trap 'printf %s\\n "$BASH_COMMAND" >&2' DEBUG
This won't show the precise arguments as set -x will. Combining them should give you the full picture, though it's a lot of debug output.

Bash variables expansion (possible use of eval) in for-do loop

I am studying the book "Beginning Linux Programming 4th ed" and chapter 2 is about shell programming. I was impressed by the example on page 53, and tried to develop a script to display more on that. Here is my code:
enter code here
#!/bin/bash
var1=10
var2=20
var3=30
var4=40
for i in 1 2 3 4 # This works as intended!
do
x=var$i
y=$(($x))
echo $x = $y # But we can avoid declaring extra parameters x and y, see next line
printf " %s \n" "var$i = $(($x))"
done
for j in 1 2 3 4 #This has problems!
do
psword=PS$j
#eval psval='$'PS$i # Produces the same output as the next line
eval psval='$'$psword
echo '$'$psword = $psval
#echo "\$$psword = $psval" #The same as previous line
#echo $(eval '$'PS${i}) #Futile attempts
#echo PS$i = $(($PS${i}))
#echo PS$i = $(($PS{i}))
done
#I can not make it work as I want : the output I expect is
#PS1 = \[\e]0;\u#\h: \w\a\]${debian_chroot:+($debian_chroot)}\u#\h:\w\$
#PS2 = >
#PS3 =
#PS4 = +
How can I get the intended output? When I run it as it is I only get
PS1 =
PS2 =
PS3 =
PS4 = +
What happened with PS1 and PS2 ?
Why do not I get the same value that I get with
echo $PS1
echo $PS2
echo $PS3
echo $PS4
because that was what I am trying to get.
Shell running a script is always non interactive shell. You may force to run the script in interactive mode using '-i' option:
Try to change:
#!/bin/bash
to:
#!/bin/bash -i
see INVOCATION section in 'man bash' (bash.bashrc is where your PS1 is defined):
When an interactive shell that is not a login shell is started, bash reads and executes commands from
/etc/bash.bashrc and ~/.bashrc, if these files exist. This may be inhibited by using the --norc option. The
--rcfile file option will force bash to read and execute commands from file instead of /etc/bash.bashrc and
~/.bashrc.
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in
the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read
and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the file name.
you can also read: http://tldp.org/LDP/abs/html/intandnonint.html
simple test:
$ cat > test.sh
echo "PS1: $PS1"
$ ./test.sh
PS1:
$ cat > test.sh
#!/bin/bash -i
echo "PS1: $PS1"
$ ./test.sh
PS1: ${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u#\h\[\033[01;34m\] \w \$\[\033[00m\]
Use indirect expansion:
for j in 0 1 2 3 4; do
psword="PS$j"
echo "$psword = ${!psword}"
done

Resources