I have a programme, ./a that I run in a loop in shell.
for((i=1 ; i<=30; i++)); do
./a arg1 5+i &//arg2 that I need to pass in which is the addition with the loop variables
done
How could I passed in the arg2 which is the addition with loop variables?
Also, I has another programme which is ./b which I need to run once and takes in all the 5 +i arguments. How could I do that without hardcoded it.
./b arg1 6\
7\
8\
9\.....
Thanks.
Addition is performed with the same (()) you are already using, while concatenation is done simply with "":
for((i=1 ; i<=30; i++)); do
let j=$((5+i))
list="$list $j"
./a arg1 $j
done
./b $list
This should work:
( for((i=5 ; i<=30; i++)); do ./a $((5+i)); echo $((5+i)); done ) | xargs ./b
In current bash versions you can use the {a..b} range notation. E.g.
for i in {1..30}; do
./a arg1 $i
done
./b arg1 {6..35}
For your second part I would do it like this
./b arg1 $(seq 6 35)
Or if you really require the addition within a loop
declare -a list
for n in $(seq 1 30) ; do
list=("${list[#]}" $((5+n)))
done
./b arg1 ${list[#]}
Related
Here is my code:
#!/bin/bash
if [[ $1 = "" ]]; then
exit 0
fi
array=($(cat $1))
let b=${#array[#]}-1
count=0
for i in {1..7}; do
for j in {30..37}; do
for n in {40..47}; do
if [[ $count -gt $b ]]; then
printf '\n'
printf '\e[0m'
exit 1
fi
printf '\e[%s;%s;%sm%-5s' "$i" "$j" "$n" "${array[$count]}"
printf '\e[0m'
let count=$count+1
done
printf '\n'
done
done
#printf '\n'
printf '\e[0m'
exit 0
The problem is that when I start it like this
. color.sh arg
or without argument, it just closes. I know that the reason for that is exit. Is there any way correct my code so I could start a script with dot at start and terminal wouldn't close after execution? I don't want to start it like this: ./script
Replace all exit with return.
return inside a sourced script will even work with exit codes:
$ . <(echo "echo before; return 0; echo after")
before
$ echo $?
0
$ . <(echo "echo before; return 7; echo after")
before
$ echo $?
7
When you use the dot to run a script you are "sourcing" it, which means the interpreter reads and executes all the commands in that script in the context of the current environment without spawning a subshell, as if you had typed each yourself.
That's why if you source it you can set variables in a script that will remain after it has run, whereas running it in a subshell would encapsulate them, and they would go away when the script ends.
Accordingly, if you source a script that hits an exit, it causes the calling environment to exit. Use return as Socowi suggested.
I understand I can:
ssh archive_server -l user -n "cat text.csv"|tee -a text1.csv|tee -a text2.csv|tee....|tee -a text10.csv
Is there a way to do it a a loop?
for i in `seq 1 10`; do
echo $i
tee ???
done
Assuming your shell is really bash (not /bin/sh), you can build an array (and use a C-style for loop, which unlike the nonstandard external command seq, is guaranteed to be available everywhere bash is):
#!/bin/bash
filenames=( )
for ((i=1; i<=10; i++)); do # note that the "10" here can be a variable name
filenames+=( "file$i.txt" )
done
tee -a -- "${filenames[#]}" <text.csv
If you need compatibility with /bin/sh, it gets a little bit more verbose:
#!/bin/sh
tee_between() (
prefix=$1; suffix=$2; start=$3; end=$4
set --
i=$(( $start - 1 )); while [ $(( ( i += 1 ) <= end )) -ne 0 ]; do
set -- "$#" "file$i.txt"
done
exec tee -a -- "$#"
)
tee_between "file" ".txt" 1 10 <text.csv
Note:
set -- modifies the current process's (or, in this case, the current function's) argument list, using that as an array we can dynamically modify.
tee_between() ( ) instead of tee_between() { } means that the function runs in a subshell -- a completely separate forked-off process. In consequence of this choice, the exec command will replace only that subshell with a copy of tee, and not the parent process.
You don't need a loop. tee can be given multiple filename arguments, so just give all the output files at once:
cat text.csv | tee -a text{1..10}.csv
If the number of files is dynamic, you can use a loop in $() to insert the filenames into the command line:
cat text.csv | tee -a $(
for i in $(seq 1 $filecount); do
echo text$i;
done)
Just make sure that you don't have any whitespace in the output filename prefix, as the spaces will be treated as argument delimiters.
Using $# you can do things to a list of files in bash. Example:
script.sh:
#!/bin/bash
list=$#
for file in $list; do _commands_; done
Then i can call this program with
~/path/to/./script dir1/{subdir1/*.dat,subdir2/*}
This argument will expand to a number of arguments that becomes $list. But now i want other arguments, say $1, $2, and this listing to be $3. So i want the expansion of dir1/{subdir1/*.dat,subdir2/*} to occur inside the script, instead of becoming many arguments. On the command line you can do this:
find dir1/{subdir1/*.dat,subdir2/*}
And get the desired output, i.e. a list if files. So I tried things like this:
arg1=$1
arg2=$2
list=$(find $3)
for file in $list; do _commands_; done
...
calling:
~/path/to/./script arg_1 arg_2 'dir1/{subdir1/*.dat,subdir2/*}'
But without success. Some help on how to make this list expand into a variable inside the script would be well appreciated!:)
EDIT: So answers below gave the solution using these commands:
arg1="$1"
arg2="$2"
shift 2
for f in "$#"; do echo "processing $f"; done;
But out of curiosity, is it still possible to pass the string dir1/{subdir1/*.dat,subdir2/*} to a find command (or whichever means to the same end) inside the script, without using $#, and obtain the list that way? This could be useful e.g. if it is preferable to have the listing as not the first or last argument, or maybe in some other cases, even if it requires escaping characters or quoting the argument.
You can have this code in your script:
arg1="$1"
arg2="$2"
shift 2
for f in "$#"; do echo "processing $f"; done;
Then call it as:
~/path/to/script arg_1 arg_2 dir1/{subdir1/*.dat,subdir2/*}
Using shift 2 will move positional parameters 2 places thus making $3 as $1 and $4 as $2 etc. You can then directly invoke $# to iterate the rest of the arguments.
As per help shift:
shift: shift [n]
Shift positional parameters.
Rename the positional parameters $N+1,$N+2 ... to $1,$2 ... If N is
The shell expansion is performed by the shell, before your script is even called. That means you'll have to quote/escape the parameters. In the script, you can use eval to perform the expansion.
#!/bin/bash
arg1="$1" ; shift
arg2="$2" ; shift
eval "list=($#)"
for q in "${list[#]}" ; do echo "$q" ; done
$ ./a 123 456 'a{b,c}' 'd*'
ab ac d.pl docs
I don't see the point of doing the expansion inside the script in your example.
#!/bin/bash
arg1="$1" ; shift
arg2="$2" ; shift
list=("$#")
for q in "${list[#]}" ; do echo "$q" ; done
or just
#!/bin/bash
arg1="$1" ; shift
arg2="$2" ; shift
for q in "$#" ; do echo "$q" ; done
$ ./a 123 456 a{b,c} d*
ab
ac
d.pl
docs
I have a bash that should be run in this way:
./script.sh <arg1> <arg2> <arg3>...<argn>
I want to show these args in my bash:
<arg3> <arg4> ... <argn>
So I wrote this bash:
for (( i=1; i<=$#-3; i++ ))
do
echo $((3+i))
done
but it shows me number of args.
How can I put # in order to see my real args?
Thanks
If you want to show arguments starting from arg3, you can simply use
echo "${#:3}" # OR
printf "%s\n" "${#:3}"
If you really want to show argument indices, use
for (( i=3; i < $#; i++)); do
echo $i
done
You can store all arguments in a BASH array and then use them for processing later:
args=( "$#" )
for (( i=2; i<${#args[#]}; i++ ))
do
echo "arg # $((i+1)) :: ${args[$i]}"
done
A minimal solution that displays the desired arguments without the math:
shift 2
for word
do
echo ${word}
done
I prefer #anubhava's solution of storing the arguments in an array, but to make your original code work, you could use eval:
for ((i=1;i<=$#;i++)); do
eval echo "\$$i"
done
After your all good answers I found this solution that works well for my thread:
ARG=( $(echo "${#:3}") )
for (( i=1; i<=$#-3; i++ ))
do
echo ${ARG[i]}
done
I need to get three arguments by test.ksh script
as the following
./test.ksh 12 34 AN
is it possible to set the argument by counter for example ?
for get_arg 1 2 3
do
my_array[get_arg]=$$get_arg
print ${my_array[get_arg]}
done
in this example I want to get three arguments from the user by loop counter "$$get_arg"
in place of $1 $2 $3
is it possible? and how ?
my_array=("$#")
for i in 0 1 2
do
echo "${my_array[$i]}"
done
This assigns all the arguments to array my_array; the loop then selects the first three arguments for echoing.
If you're sure you want the first three arguments in the array, you could use:
my_array=("$1" "$2" "$3")
If you want the 3 arguments at positions 1, 2, 3 in the array (rather than 0, 1, 2), then use:
# One or the other but not both of the two assignments
my_array=("dummy" "$#")
my_array=("dummy" "$1" "$2" "$3")
for i in 1 2 3
do
echo "${my_array[$i]}"
done
bash has a special variable
$#
which contains the arguments of the script it currently executes. I think this is what your'e looking for:
for arg in $# ; do
# code
done
Edit:
My bad ksh:
for arg;do
print $arg
done
Original Post:
Use shift to iterate through shell script parameters:
# cat test.sh
#!/bin/bash
while [ "$1" != "" ]; do
echo $1
shift
done
test run:
# ./test.sh arg1 monkey arg3
arg1
monkey
arg3
source
Even you don't need in $#, this would work the same:
#!/bin/bash
i=0
for arg; do
my_array[i]="$arg"
echo "${my_array[i]}"
(( i++ ))
done
That is,
if in words is not present, the for command executes the commands
once for each positional parameter that is set, as if in $# had been
specified.