Writing a Nested shell script- Unable to pass argument - bash

I am trying to create a shell script using another script.
Following is the code.
#!/bin/bash
count=$#
cat << EOF > /tmp/kill_loop.sh
#!/bin/bash
while true;
do
for i in "$#"
do
echo $i
done
done
EOF
When I see kill_loop.sh , "$i" is empty.
#!/bin/bash
while true;
do
for i in "one two three"
do
echo
done
done
I want "$i" to be printed as such in kill_loop.sh file so that if i execute kill_loop.sh, it echoes the value 'one','two' and 'three'

Your "outer" shell script is interpreting $i as if it were one of its own variables, which isn't set, thus it evaluates to nothing. Try escaping the $ so the outer shell doesn't expand it:
echo \$i

here is the "foreach" function:
function foreach
{
typeset cmd=$1;
shift;
for arg in "$#";
do
$cmd $arg;
done
}

Related

For Loop for Arguments Collects Spaces Instead of Arguments

I've created a shell script that has the SSH address as the first argument, then has one or more arguments afterwards. The script is capable of working with two arguments or less (the address and the single argument), but it is not able to work properly after. Below is my code to get a better idea.
ssh.sh $1 << EOF
$(typeset -f sr_single "$#")
if [ "$#" -eq 2 ]; then
echo $2
sr_single $2
elif [ "$#" -lt 2 ]; then
echo "Needs at least two arguments: serial number and argument(s)"
else
echo "${#:2}"
for i in "${#:2}"; do
echo "'" $i "'"
sr_single $i
done
fi
EOF
Below is what it returns if I call the function "sr.sh test#ssh.com -l"
-l
And below is what it returns when I call "sr.sh test#ssh.com -l -v"
-l -v
' '
My question is how is this function not getting the second variable and the ones after on this one, where it seems to be working properly in the rest of the program? Thanks
To do this with less hair loss, define all your code in functions, like so:
rmt_main() {
if (( $# == 2 )); then
printf 'Exactly arguments received; $2 is: %q\n' "$2" >&2
elif (( $# < 2 )); then
echo 'Error: Needs at least two arguments' >&2
else
otherfunc "${#:2}"
fi
}
otherfunc() {
echo "Otherfunc called with arguments:" >&2
printf '%q\n' "$#"
}
...after which, calling can look like:
# generate an eval-safe string containing your arguments
printf -v args_str '%q ' "$#"
# explicitly invoke bash, so we don't need to worry about whether our escaping is POSIX-y
ssh "$1" 'bash -s' <<EOF
$(typeset -f rmt_main otherfunc) # emit function definitions
rmt_main $args_str # and call rmt_main with the eval-safe argument list
EOF
Note that the only contents we're expanding inside the heredoc are generated by the local shell in a form guaranteed to be correctly escaped to parse as code (by the remote shell). We are not under any circumstances expanding data (like command-line arguments) into the heredoc in unescaped form, and all our functions use completely conventional quoting (which is to say that all parameter expansions inside the function definitions are quoted).

How do I pass an array of strings to a Bash script and join that array?

I want to join an array of strings on the string "%2C+". My shell script launch looks like this.
#!/bin/bash
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${#/#/$d}"; }
selectQuery=$(join_by "%2C+" $1)
echo selectQuery
But when I run ./download-data $("state_code" "county_code"), I get this error in the terminal: bash: state_code: command not found.
I need to pass the argument as an array since I plan to pass more arrays later on. Something like ./download-data $("state_code" "county_code") $("more" "string").
Make your script accept multiple strings in separate arguments:
#!/bin/bash
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${#/#/$d}"; }
selectQuery=$(join_by "%2C+" "$#")
echo "$selectQuery"
and then run it with multiple arguments:
./download-data "state_code" "county_code"

how to pass parameter to a function when it's called inside another function in shell

I have shell script with 2 functions like below:
lines(){
while IFS="" read -l
do
line=$(wc -l < "something.txt")
if [ "$line" = "$1" ] ; then
do something...
echo "lines are: "$l""
else
#calling files function here
files
do something...
fi
done<something.txt
}
files(){
do something....
echo "something...\n""$(lines "$1")"
}
####Main
case "$1" in
lines)
shift
lines "$1"
;;
*)
esac
I am trying to run the script like this on an ubuntu machine:
sh files.sh line 3
I have some if operations where
In files I am trying to call lines function. When it's called and goes back to perform the actions in lines(), the argument I am trying to pass from the command line 3 i.e., "$1" is being passed as null (empty)
Can someone help me how I can have lines function read the parameter I am passing from the command line
Thanks
The easy thing to do is to just create a global array variable that preserves your original command-line arguments:
#!/usr/bin/env bash
[ "$BASH_VERSION" ] || { echo "ERROR: This script requires bash" >&2; exit 1; }
args=( "$0" "$#" )
func1() { func2 "local-arg1" "local-arg2"; }
func2() { echo "Function argument 1 is $1; original argument 2 is ${args[2]}"; }
func1
...will, if called as ./scriptname global-arg1 global-arg2, emit as output:
Function argument 1 is local-arg1; original argument 2 is global-arg2
Positional parameters are local to each function. So files can't access the $1 variable of lines by itself, you need to pass it explicitly:
files "$1"

Loop inside "heredoc" in shell scripting

I need to execute series of commands inside an interactive program/utility with parameterized values. Is there a way to loop inside heredoc ? Like below .. Not sure if eval can be of any help here. Below example doesn't seem to work as the interactive doesn't seem to recognize system commands.
#!/bin/sh
list="OBJECT1 OBJECT2 OBJECT3"
utilityExecutable << EOF
for i in $list ; do
utilityCommand $i
done
EOF
Instead of passing a here-document to utilityExecutable,
the equivalent is to pipe the required text to it. You can create the desired text using echo statements in a for-loop, and pipe the entire loop output to utilityExecutable:
#!/bin/sh
list="OBJECT1 OBJECT2 OBJECT3"
for i in $list; do
echo "utilityCommand $i"
done | utilityExecutable
Yes, this is tricky and can be confusing! You have to modify your codes as follow.
#!/bin/sh
list="OBJECT1 OBJECT2 OBJECT3"
utilityExecutable << EOF
list="$list"
for i in \$list ; do
utilityCommand \$i
done
EOF
This is because heredoc uses its own variables, which are completely separate from the shell. When you are inside heredoc, you have to use and modify heredoc's own variables. So the \$ is needed to reference heredoc's own variables instead of shell variables when inside heredoc.
cat << EOF
$(
for i in {1..10}; do
echo $i;
done
)
EOF
commandxyz -noenv<<EOF
echo "INFO - Inside eof"
t_files=("${p_files[#]}")
#copy array
#echo \${t_files[*]}
#all elements from array
#echo \${#t_files[#]}
#array length
for i in \${t_files[#]} ; do
echo -e \$i;
do other stuff \$i;
done
cat $patch_file
git apply $patch_file
EOF
myVar=$(
for i in {1..5}; do
echo hello;
echo world;
done;
); cat <<< $myVar

Make use of variable from while read loop

In my bash script I use while read loop and a helper function fv():
fv() {
case "$1" in
out) echo $VAR
;;
* ) VAR="$VAR $1"
;;
esac
}
cat "$1" | while read line
do
...some processings...
fv some-str-value
done
echo "`fv out`"
in a hope that I can distil value from while read loop in a variable accessible in rest of the script.
But above snippet is no good, as I get no output.
Is there easy way to solve this - output string from this loop in a variable that would be accessible in rest of the script - without reformatting my script?
As no one has explained to you why your code didn't work, I will.
When you use cat "$1" |, you are making that loop execute in a subshell. The VAR variable used in that subshell starts as a copy of VAR from the main script, and any changes to it are limited to that copy (the subshell's scope), they don't affect the script's original VAR. By removing the useless use of cat, you remove the pipeline and so the loop is executed in the main shell, so it can (and does) alter the correct copy of VAR.
Replace your while loop by while read line ; do ... ; done < $1:
#!/bin/bash
function fv
{
case "$1" in
out) echo $VAR
;;
* ) VAR="$VAR $1"
;;
esac
}
while read line
do
fv "$line\n"
done < "$1"
echo "$(fv out)"
Stop piping to read.
done < "$1"

Resources