I have a C program that I want to run without having to manually type commands into. I have 4 commands (5 if you count the one to exit the program) that I want given to the program and I don't know where to start. I have seen some stuff like
./a.out <<<'name'
to pass in a single string but that doesn't quite work for me.
Other issues I have that make this more difficult are that one of the commands will give an output and that output needs to be a part of a later command. If I had access to the source code I could just brute force in some loops and counters so I am trying to get a hold of it but for now I am stuck working without it. I was thinking there was a way to do this with bash scripts but I don't know what that would be.
In simple cases, bash script is a possibility: run the executable in coproc (requires version 4). A short example:
#!/bin/bash
coproc ./parrot
echo aaa >&${COPROC[1]}
read result <&${COPROC[0]}
echo $result
echo exit >&${COPROC[1]}
with parrot (a test executable):
#!/bin/bash
while [ true ]; do
read var
if [ "$var" = "exit" ]; then exit 0; fi
echo $var
done
For a more serious scenarios, use expect.
Related
I am trying to loop through an array of directories using a bash script so I can list directories with their timestamp, ownership etc using ls -arlt. I am reviewing bash so would like some feedback.
It works with declare -a for those indirect references but for each directory it outputs and extra directory from the /home/user.
I tried to use declare -n and declare -r for each directory and doesn't work.
#!/bin/bash
# Bash variables
acpi=/etc/acpi
apm=/etc/apm
xml=/etc/xml
array=( acpi apm xml )
# Function to display timestamp, ownership ...
displayInfo()
{
for i in "${array[#]}"; do
declare -n curArray=$i
if [[ -d ${curArray} ]]; then
declare -a _acpi=${curArray[0]} _apm=${curArray[1]} _xml=${curArray[2]}
echo "Displaying folder apci: "
cd $_acpi
ls -alrt
read -p "Press enter to continue"
echo "Displaying folder apm: "
cd $_apm
ls -alrt
read -p "Press enter to continue"
echo "Displaying folder xml: "
cd $_xml
ls -alrt
read -p "Press enter to continue"
else
echo "Displayed Failed" >&2
exit 1
fi
done
}
displayInfo
exit 0
It outputs an extra directory listing the /home/user and don't want that output.
There are a lot of complex and powerful shell features being used here, but in ways that don't fit together or make sense. I'll go over the mistakes in a minute, first let me just give how I'd do it. One thing I will use that you might not be familiar with is indirect variable references with ${!var} -- this is like using a nameref variable, but IMO it's clearer what's going on.
acpi=/etc/acpi
apm=/etc/apm
xml=/etc/xml
array=( acpi apm xml )
displayInfo()
{
for curDirectory in "${array[#]}"; do
if [[ -d ${!curDirectory} ]]; then
echo "Displaying folder $curDirectory:"
ls -alrt "${!curDirectory}"
read -p "Press enter to continue"
else
echo "Error: ${!curDirectory} does not exist or is not a directory" >&2
exit 1
fi
done
}
displayInfo
(One problem with this is that it does the "Press enter to continue" thing after each directory, rather than just between them. This can be fixed, but it's a little more work.)
Ok, now for what went wrong with the original. My main recommendation for you would be to try mentally stepping through your code to see what it's doing. It can help to put set -x before it, so the shell will print its interpretation of what it's doing as it runs, and see how it compares to what you expected. Let's do a short walkthrough of the displayInfo function:
for i in "${array[#]}"; do
This will loop over the contents of array, so on the first pass through the loop i will be set to "acpi". Good so far.
declare -n curArray=$i
This creates a nameref variable pointing to the other variable acpi -- this is similar to what I did with ${! }, and basically reasonable so far. Well, with one exception: the name suggests it's an array, but acpi is a plain variable, not an array.
if [[ -d ${curArray} ]]; then
This checks whether the contents of the acpi variable, "/etc/acpi" is the path of an existing directory (which it is). Still doing good.
declare -a _acpi=${curArray[0]} _apm=${curArray[1]} _xml=${curArray[2]}
Here's where things go completely off the rails. curArray points to the variable acpi, so ${curArray[0]} etc are equivalent to ${acpi[0]} etc. But acpi isn't an array, it's a plain variable, so ${acpi[0]} gets its value, and ${acpi[1]} and ${acpi[2]} get nothing. Furthermore, you're using declare -a (declare arrays), but you're just assigning single values to _acpi, _apm, and _xml. They're declared as arrays, but you're just using them as plain variables (basically the reverse of how you're using curArray -> acpi).
There's a deeper confusion here as well. The for loop above is iterating over "acpi", "apm", and "xml", and we're currently working on "acpi". During this pass through the loop, you should only be working on acpi, not also trying to work on apm and xml. That's the point of having a for loop there.
Ok, that's the main problem here, but let me just point out a couple of other things I'd consider bad practice:
cd $_apm
ls -alrt
Using a variable reference without double-quotes around it like this invites parsing confusion; you should almost always put double-quotes, like cd "$_apm". Also, using cd in a script is dangerous because if it fails the rest of the script will execute in the wrong place. In this case, _apm is empty, so without double-quotes it's equivalent to just cd, which moves to your home directory. This is why you're getting that result. If you used cd "$_apm" it would get an error instead... but since you don't check for that it'll go ahead and still list an irrelevant location.
It's almost always better to avoid cd and its complications entirely, and just use explicit paths, like ls -alrt "$_apm".
echo "Displayed Failed" >&2
exit 1
Do you actually want to exit the entire script if one of the directories doesn't exist? It'd make more sense to me to just return 1 (which exits just the function, not the entire script), or better yet continue (which just goes on to the next iteration of the loop -- i.e. the next directory on the list). I left the exit in my version, but I'd recommend changing it.
One more similar thing:
acpi=/etc/acpi
apm=/etc/apm
xml=/etc/xml
array=( acpi apm xml )
Is there any actual reason to use this array -> variable name -> actual directory path system (and resulting indirect expansion or nameref complications), rather than just having an array of directory paths, like this?
array=( /etc/acpi /etc/apm /etc/xml )
I left the indirection in my version above, but really if there's no reason for it I'd remove the complication.
I am using slurm scripts to run arrays for Matlab computing on a cluster. Each script uses an array to loop over a matlab parameter.
1) Is it possible to create a shell script to loop over another variable?
2) Can I pass variables to a slurm script?
For example, my slurm files currently look like
#!/bin/bash
#SBATCH --array=1-128
...
matlab -nodesktop r "frame=[${SLURM_ARRAY_TASK_ID}]; filename=['Person24']; myfunction(frame, filename);";
I frequently need to run this array to process a number of different files. This means I will submit the job (sbatch exampleScript.slurm), edit the file, update 'Person24' to 'Person25', and then resubmit the job. This is pretty inefficient when I have a large number of files to process.
Could I make a shell script that would pass a variable to the slurm script? For example, something like this:
Shell Script (myshell.sh)
#!/bin/bash
for ((FNUM=24; FNUM<=30; FNUM+=1));
do
sbatch myscript.slurm >> SOMEHOW PASS ${FNUM} HERE (?)
done
Slurm script (myscript.slurm)
#!/bin/bash
#SBATCH --array=1-128
...
matlab -nodesktop -nodisplay r "frame=[${SLURM_ARRAY_TASK_ID}]; filename=[${FNUM}]; myfunction(frame, filename);";
where I could efficiently submit all of the jobs using something like
sbatch myshell.sh
Thank you!
In order to avoid possible name collisions with shell and anvironment variables, it is a good habit to always use lowercase or mixed case variables in your Bash scripts.
You were almost there. You just need to pass the variable as an argument to the second script and then pick it up there based on the positional parameters. In this case, it looks like you're only passing one argument, so $1 is OK to use. In other cases, with multiple parameters of a fixed number you could also use $2,$3, etc. With a variable number of arguments "$#" would be more appropriate.
Shell Script (myshell.sh)
#!/bin/bash
for ((fnum=24; fnum<=30; fnum+=1))
do
sbatch myscript.slurm "$fnum"
done
Slurm script (myscript.slurm)
#!/bin/bash
#SBATCH --array=1-128
fnum=$1
...
matlab -nodesktop -nodisplay r "frame=[${slurm_array_task_ID}]; filename=[${fnum}]; myfunction(frame, filename);";
For handling various timeout conditions this might work:
A=$(sbatch --parsable a.slurm)
case $? in
9|64|130|131|137|140)
echo "some sort of timeout occurred"
B=$(sbatch --parsable --dependency=afternotok:$A a.slurm)
;;
*)
echo "some other exit condition occurred"
;;
esac
You will just need to decide what conditions you want to handle and how you want to handle them. I have listed all the ones that seem to involve timeouts.
Given this code:
L_count=12
echo "hi hello $l_count"
The expected output is:
hi hello 12
If I run the code it gives the result as desired.
However, I have problems working in databases:
Now I am inserting the same string 'hi hello $l_count' in col1 of test table:
Insert into test(col1) values ('hi hello $l_count');
In UNIX:
l_count=12
var1=sqlconnect
Select col1 from test
echo $var1
It is not replacing l_count with 12 even if I do:
echo $(echo $var1)
How can I handle this?
If I understand your problem correctly, you have a code fragment in your database that you want to evaluate as a shell script. You don't mention what shell you are using, I am guessing bash.
Bash contains a builtin function called eval to help you do that. I guess other shells probably have similar functionality.
So basically, try eval echo $var1
Thanks peder.Below command worked fine
eval "echo //$var1"
I try to transfer the excellent example docker-haproxy from centos to alpine.
A shell script is used to process a list of values given as parameters to the script into an array, then write these values plus their index to some file.
The following construction works in bash:
ServerArray=${SERVERS:=$1}
...
for i in ${ServerArray[#]}
do
echo " " server SERVER_$COUNT $i >> /haproxy/haproxy.cfg
let "COUNT += 1"
done
but not in ash (or sh):
syntax error: bad substitution
The error refers to line
for i in ${ServerArray[#]}
What is the correct syntax here? I guess the line
ServerArray=${SERVERS:=$1}
does not define an array as intended, but googling for long did not help me.
bash to sh (ash) spoofing says
sh apparently has no arrays.
If so, how to solve the problem then?
I guess I can do with this construction:
#!/bin/sh
# test.sh
while [ $# -gt 0 ]
do
echo $1
shift
done
delivers
/ # ./test 172.17.0.2:3306 172.17.0.3:3306
172.17.0.2:3306
172.17.0.3:3306
which is what I need to proceed
Pretty simple, I need to test a C program by inputting a large number of integers into it. To do this, for this particularly case, I need to run the program, then when it waits for user input (scanf("%d, integer)), I need to give it the integer from the loop I'm currently at.
right now I have
for i in {1..5};
do
(echo -n "$i" && ./a2 $i)
done
but it still just waits for user input, then after user input is given, prints the integer of the loop I'm on. I've tried a few examples from similar problems I've found on stack exchange and elsewhere but so far no luck. I haven't ever really needed to mess about with shell scripting before so it's probably something simple I'm doing backasswordsly but no one else has done wrong before.
Try this!
for i in `seq 1 1000`; do echo $i | ./a2; done
Your solution
echo -n "$i" && ./a2 $i
would pass $i as argument for a2 not input.
I think what you need is not usually done using shell script.
You can write a c code to generate your input, which in this case is numbers from 1-10000. Let that file be testGenerator.c. Then, run this on your terminal:
gcc testGenerator.c
./a.out >input
This will create a file, named input which will have numbers from 1 to 10000, which is of course the o/p of testGenerator.c.
Then, if your program, in which you want input is compiled into a2, as I can see, you can run it as:
./a2 <input >output
and you will get the required output in the file output. If you don't write >output here, you will see o/p on terminal.
Also, when you are running this script, you are actually running the a2 10000 times and giving a number as command line argument, which is very different from taking input from stdin which of course, scanf does.
for i in {1..1000}; do echo $i | ./a2; done