Skip to content
View in the app

A better way to browse. Learn more.

Unraid

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Need help with this for loop

Featured Replies

I have two ip's in a variable.

pingIPs="192.168.178.150 192.168.178.153"

 

my loop starts with:

for i in "${pingIPs}"

 

Something is wrong with the loop.

I suppose the variable is not resolved into 2 strings so that  there is no valid ip in the next steps.

How is the loop supposed to look like?

 

I also tried

for i in "$pingIPs"

 

Doesn't work either.

 

Thank you for your help.

 

remove the quotes

 

for i in ${pingIPs};do echo $i;done

 

so it "sees" multiple values to loop over, quoting it makes it into a single string

  • Author

Ahh...finally.

I copied the solution with quotes somewhere from a forum...

 

But without it works now.

 

Thank you!

 

And here's some food for thought example using BASH arrays, here documents and manual assignments.

#!/bin/bash

typeset -ax LINES
while read LINE
do [ -z "${LINE%\#*}" ] && continue
   LINES=("${LINES[@]}" "${LINE}" )     #  Append element to array
done <<-EOF
192.168.1.1
192.168.1.2
192.168.1.3
# comment ignored
# 192.168.1.99
EOF

echo "array assigned from here document"
set | grep LINES

echo "array assigned manually"
LINES[4]="192.168.1.4"
LINES[5]="192.168.1.5"

set | grep LINES

for IP in ${LINES[@]}
do  echo ping ${IP}
done

 

 

here's the respective output

array assigned from here document
LINES=([0]="192.168.1.1" [1]="192.168.1.2" [2]="192.168.1.3")
array assigned manually
LINES=([0]="192.168.1.1" [1]="192.168.1.2" [2]="192.168.1.3" [4]="192.168.1.4" [5]="192.168.1.5")
ping 192.168.1.1
ping 192.168.1.2
ping 192.168.1.3
ping 192.168.1.4
ping 192.168.1.5

 

  • Author

"You can't teach an old dog new tricks."

 

I had some basic programming lessons during my studies in Turbo Pascal, C and html,

but that's literally the "tip of the iceberg".

I never got that deep into scripting and then they launched Windows95 and took the last

bit of motivation away from me.  ;D

 

Although I'm really willing to learn those things, It's very, very time consuming.

A full time job, 2 kids and some additional real life activities don't leave much room...

 

Considering how often I will need the knowledge in the event, I must regrettably admit that "it's not worth it".

Like every other language, without practice and/or regular use it's meaningless to learn.

And while speaking a language is forgiving "typos", coding is not - that keeps the learning curve pretty flat.

So, the only thing that remains for me to do is to ask silly questions and be patient... :(

 

Forums are a blessing imho.

"...And while speaking a language is forgiving "typos", coding is not - that keeps the learning curve pretty flat...

Forums are a blessing imho.

[me=DaleWilliams]leaps to his feet, cheers, and gives a standing ovation![/me]

  • 3 weeks later...
  • Author

Opening up this once again...I'm stuck in the mud.

 

I wanted to do some improvements on this script but after rewriting I can't get it working again.

I wasted an hour yesterday night and still don't see the problem - it's really f****** disappointing!

 

So far these are the bugs:

./auto_s3_sleep.sh: line 317: syntax error near unexpected token `('
./auto_s3_sleep.sh: line 317: `                 log_message "TCP activity (30 second average): $TCPact"'

 

I don't know why it's not working anymore! The line of code is exactly the same as in the working script but now it throws this error...

 

When I remove the brackets it's running further to this error.

root@tower:/boot# auto_s3_sleep.sh
./auto_s3_sleep.sh: line 384: unexpected EOF while looking for matching `"'
./auto_s3_sleep.sh: line 395: syntax error: unexpected end of file

 

I compared the files with total commander.

The lines are exactly the same.

Although I edited with notepad+ I I fromdos'ed the file ... nope.

 

Looks like he can't parse the   

hourMatch=$(echo "$noCountdownHours" | grep "$hour" | wc -l)

 

?????????

 

Please have a look at it!

auto_s3_sleep.sh_buggy.txt

auto_s3_sleep.sh_ok.txt

Toward the end of line 123, at the end of a long series of hyphens, there is a very strange character where there should be a quote mark.  I don't know if that's the only problem.

  • Author

True, that's a weird char.

Funny, the parser did not complain about that...

 

Thanks for looking into.

I will check it when I'm back home.

 

  • Author
Toward the end of line 123, at the end of a long series of hyphens, there is a very strange character where there should be a quote mark.  I don't know if that's the only problem.

That was the only problem.

The script runs fine now but some changes don't work like intended.

 

This expression doesn't seem to work:

log_message () { 
if [ "$debug" -eq 2 ]
    then
        logger –t$program_name "$1"
        #logger "$1"
    fi

the log entry is like this:

Mar  5 21:16:43 tower logger: \226tauto_s3_sleep.sh Start checking conditions for standby... 
Mar  5 21:16:43 tower logger: \226tauto_s3_sleep.sh Not sleeping due to time of day.

 

when called:

log_message "Start checking conditions for standby..."

 

Whereas this one does:

.... | logger -t$program_name

 

logs like this:

Mar  5 21:16:43 tower auto_s3_sleep.sh: Renew DHCP: no 
Mar  5 21:16:43 tower auto_s3_sleep.sh: Force GB LAN: yes 
Mar  5 21:16:43 tower auto_s3_sleep.sh: -----

 

I'm probably calling the internal function log_message in a wrong way?

 

Next thing is, I'm calling the script in the go file like this:

/boot/auto_s3_sleep.sh | at now +45 minutes

 

It is not starting with 45 minutes delay though...???

I use these definitions at the top of all shell scripts.

#!/bin/bash

[ ${DEBUG:=0} -gt 0 ] && set -x -v

B=${0##*/}              # basename of program
D=${0%%/$B}             # dirname of program
E=${B#*.}               # Extension
P=${B%.*}               # strip off after last . character

# echo "D=${D} B=${B} P=${P} E=${E}";exit

 

then when I call logger it's with

 

logger -t${P}[${BASHPID}]

 

also I would suggest changing some of this code with the redundant pipes.

 

check_hour() {
    echo $(date +%H)
}

hour=`check_hour`

noCountdownHours="18 19 20 21 22 23"
hourMatch=$(echo "$noCountdownHours" | grep "$hour" | wc -l)

 

when querying hour you end up forking and running a pipe to the internal function check_hour

then there is another fork and pipe to $(date +%H), so the program is run, it's output via stdout is fed to echo. which takes the command line an puts it on stdout. There's all sorts of forking and piping for no reason.

 

doing this hour=$(date +%H) is plenty and reduces the need to echo something on stdout that already was printed on stdout.

The other code complicates things and causes all sorts of process splitting for no real benefit.

 

I have more to say, but let me save this post first.

See the following code snippets for ideas on simplifying the hour list check logic.

#!/bin/bash
check_hour() {
    # this is a waste of CPU resources
    echo $(date +%H)
}
check_minute() { 
    # this is a waste of CPU resources
    echo $(date +%m)
}

funca() {
  echo $BASHPID
}

hour=`check_hour`
noCountdownHours="18 19 20 21 22 23"


echo $BASHPID
funca

check_hour_list()
{
  HOUR=$(date +%H)

  for CountdownHour in ${noCountdownHours}
  do  if [ ${HOUR} = ${CountdownHour} ]
         then return 0
      fi
  done

  return 1
}

hour=$(date +%H)


# this is a waste of CPU resources
hourMatch=$(echo "$noCountdownHours" | grep "$hour" | wc -l)

echo $hourMatch


check_hour_list
if [ $? -eq 0 ]
   then echo "check_hour_list current hour matched"
   else echo "check_hour_list current hour DID NOT MATCH"
fi

if check_hour_list
   then echo "check_hour_list current hour matched"
   else echo "check_hour_list current hour DID NOT MATCH"
fi

  • Author

OK Sir!

Thank you for the pointer.  :-\

First of all, let me illustrate how I see our (your's and mine) skill levels in these things:

 

You = Ph.D vs. me = primary school

You = concerned about runtime, load, efficiency vs. me = happy when it runs

 

From my questions in the former posts you probably noticed this already... ;)

Also I'm "just" building on top of something that existed - without knowing if it's good or not.

At this point I'm just happy it runs somehow...

 

Now, I would like to implement your advice but to be honest, I probably understand 50% of that code.

Either you help me in understanding your script-fu by commenting more in detail or you hack it in that

script on your own (which probably takes less of your time).

 

Find attached my working edition.

 

...now I'm p****** off to further study your code snippets... :o

 

Edit:

I'm back with some questions...see comments within the code.

 

#!/bin/bash

[ ${DEBUG:=0} -gt 0 ] && set -x -v #this does exactly what (set -x -v)?

#next lines look like chinese to me but I will take them as given...
B=${0##*/}              # basename of program  
D=${0%%/$B}             # dirname of program
E=${B#*.}               # Extension
P=${B%.*}               # strip off after last . character

# echo "D=${D} B=${B} P=${P} E=${E}";exit # when do you use this?

then when I call logger it's with

 

logger -t${P}[${BASHPID}]

Clear so far, but the expression log_message "..." $.. is easier to write and looks more comprehensible (at least to my noob brain).

Is there a way to enhance the log_message function to do the equivalent of your line of code?

e.g.

log_message () {
logger -t${P}[${BASHPID}] "$1"

 

 

Another shitload of noob questions - sorry:

#!/bin/bash
check_hour() {
    # this is a waste of CPU resources
    echo $(date +%H)
}
check_minute() { 
    # this is a waste of CPU resources
    echo $(date +%m)
}

funca() {
    # what is the use of this function
  echo $BASHPID #returns PID in order to do what???
}


hour=`check_hour`
noCountdownHours="18 19 20 21 22 23"


echo $BASHPID #what's the good of those 2 lines?
funca        



check_hour_list()
#this function can replace the hourMatch expression in the current script
{
  HOUR=$(date +%H)

  for CountdownHour in ${noCountdownHours}
    do
        if [ ${HOUR} = ${CountdownHour} ]
            then return 0
        fi
    done
    
    return 1
}


hour=$(date +%H)

# this is a waste of CPU resources
hourMatch=$(echo "$noCountdownHours" | grep "$hour" | wc -l)

echo $hourMatch


check_hour_list
if [ $? -eq 0 ] #$? refers to what variable? the return value from check_hour_list?
   then echo "check_hour_list current hour matched"
   else echo "check_hour_list current hour DID NOT MATCH"
fi

if check_hour_list # the necessity of this if construction is not clear to me
   then echo "check_hour_list current hour matched"
   else echo "check_hour_list current hour DID NOT MATCH"
fi

auto_s3_sleep.sh_working_but_ugly_logging.txt

OK Sir!

Thank you for the pointer.  :-\

First of all, let me illustrate how I see our (your's and mine) skill levels in these things:

 

You = Ph.D vs. me = primary school

You = concerned about runtime, load, efficiency vs. me = happy when it runs

From my questions in the former posts you probably noticed this already... ;)Also I'm "just" building on top of something that existed - without knowing if it's good or not.At this point I'm just happy it runs somehow...

 

I hear ya. No Ph.D here though.  This is my trade. I've been doing this since childhood.

usually I'll take the quick get it to run approach when prototyping.

The issue later becomes, how does it run over time when looped a million times or so.

Shell is easy to get working one way or another. It's when you have to run it over and over that things get more complex.

In this case, it may not matter.  quick and easy bad habits tend to propagate until one day you say.. Oh no!!!

 

#!/bin/bash

[ ${DEBUG:=0} -gt 0 ] && set -x -v #this does exactly what (set -x -v)?

#next lines look like chinese to me but I will take them as given...
B=${0##*/}              # basename of program  
D=${0%%/$B}             # dirname of program
E=${B#*.}               # Extension
P=${B%.*}               # strip off after last . character

# echo "D=${D} B=${B} P=${P} E=${E}";exit # when do you use this?

 

While the echo is commented out, it's there in case I need to refresh myself how the program name is breaking down.

I do the same thing in my C programs. Break down the program name. put it in globals so error messages are properly tagged.

 

then when I call logger it's with

logger -t${P}[${BASHPID}]

Clear so far, but the expression log_message "..." $.. is easier to write and looks more comprehensible (at least to my noob brain).

Is there a way to enhance the log_message function to do the equivalent of your line of code?

e.g.

log_message () {
logger -t${P}[${BASHPID}] "$1"

 

Yes, you got it, change the logger message inside your function so it is tagged properly in the syslog.

 

I did not get a chance to go through the other code. In the  meantime consider that every shell function can return a value

in shell 0 is always success, any other number is a failure.

 

check_hour_list()
{
  HOUR=$(date +%H)

  for CountdownHour in ${noCountdownHours}
  do  if [ ${HOUR} = ${CountdownHour} ]
         then return 0
      fi
  done

  return 1
}

 

So in this snippet we are running one pipe to the date command and storing it in HOUR.

Then looping across each element in the countdownhour string. Looking for a match.

When a match occurs return 0 which is true. otherwise fall through and return a false.

This could even be condensed with.

 

check_hour_list()
{
  HOUR=$(date +%H)

  for CountdownHour in ${noCountdownHours}
  do  [ ${HOUR} = ${CountdownHour} ] && then return 0
  done

  return 1
}

 

here is how you check for an explicit return code.

check_hour_list
if [ $? -eq 0 ]
   then echo "check_hour_list current hour matched"
   else echo "check_hour_list current hour DID NOT MATCH"
fi

 

here is how you check for an implicit return code.

if check_hour_list
   then echo "check_hour_list current hour matched"
   else echo "check_hour_list current hour DID NOT MATCH"
fi

 

I learned most of what I do from this book and various texts around the internet.

The New KornShell Command And Programming Language

http://www.amazon.com/o/ASIN/0131827006?tag=adapas02-20

 

These days I would suggest an O'Reilly book on bash.

#!/bin/bash

[ ${DEBUG:=0} -gt 0 ] && set -x -v

 

#this does exactly what (set -x -v)?

 

in bash and ksh set -x -v turn on an option for verbosity and tracing of your script.

This particular line says, if DEBUG is not 0, assign 0 to DEBUG.

the it says, if DEBUG is assigned and > 0 turn on verbose and tracing in the shell.

 

You can use it in various ways. here's an example of doing it on the command line assigning it in the environment beforehand.

 

DEBUG=1 ./scriptname

 

 

 

  • Author

Thanks for taking the time!

 

Well, based on my experience, a Ph.D probably wouldn't be capable of scripting like that.

He probably would tell me a story of what's in principle is possible and talk my ear off...

 

This is probably the most essential passage:

I've been doing this since childhood.

There is nothing like practice.

&

You can't teach an old dog new tricks.

 

I think the debug routine which is installed right now is OK.

I can adapt your variables but the different logging options are welcome.

This is not done in your version.

 

There is only one problem when I call it like this:

 

log_message () {
    logger –t$program_name "$1"

 

It will log like this:

Mar  5 21:16:43 tower logger: \226tauto_s3_sleep.sh Start checking conditions for standby... 
Mar  5 21:16:43 tower logger: \226tauto_s3_sleep.sh Not sleeping due to time of day.

 

This will most likely be the same with your code, don't you think?

Where is the "\226t" coming from?

And why can't I pass the code in lines 109 to 124 with this command?

 

 

Additionally, can you elaborate on why the next line in the go file is not working?

 

/boot/auto_s3_sleep.sh | at now +45 minutes

 

There is no delay. The script is starting right away.

 

 

 

#!/bin/bash

[ ${DEBUG:=0} -gt 0 ] && set -x -v

program_name=`basename $0`

log_message () {
    logger -t$program_name "$1"
}

log_message "testing" 

 

root@unRAID:/tmp# DEBUG=1 ./auto_s3_sleep.sh 

program_name=`basename $0`
basename $0
++ basename ./auto_s3_sleep.sh
+ program_name=auto_s3_sleep.sh

log_message () {
    logger -t$program_name "$1"
}

log_message "testing" 
+ log_message testing
+ logger -tauto_s3_sleep.sh testing

 

root@unRAID:/tmp# tail /var/log/syslog
Mar  6 10:37:23 unRAID auto_s3_sleep.sh: testing

 

You can see from my code example, it works.

You have a bad character in the line and logger is seeing it, interpreting it and escaping it.

When I tried to cut and paste the logger line into vi, it complained and truncated the line.

Additionally, can you elaborate on why the next line in the go file is not working?

 

/boot/auto_s3_sleep.sh | at now +45 minutes

 

There is no delay. The script is starting right away.

 

echo /boot/auto_s3_sleep.sh | at now +45 minutes

  • Author

Much useful information here  8)

 

Now that you got me on the "return code - trip" I realize that I could save some variable declaration.

e.g.

check_LockFile () {
    if [ "$checkLockFile" = $yes ]
    then
        if [ -e "/tmp/nos3sleep.lock" ]
        then
        LockFile=1
        else
        LockFile=0
        fi
    fi

I could simply use the return code.

 

But will this work, if I want to chain the return codes of two functions?

Is this recommended? If the variable is needed often it may cause more CPU load to generate the

return code every time. Can I mix return code and variables?

 

What would be the right syntax?

Like this (to check both return codes)?

if [[ hour_match && check_LockFile ]]
    then

 

Syntax to check one return code and a variable?

 

    if [[ check_hour && "$LockFile" -eq 0 ]]
    then

 

Edit:

According to my findings this should be correct:

(command expansion for check_hour)

if [[ $(check_hour) -eq 0 && "$LockFile" -eq 0 ]]

 

 

Why are you using the variables like ${P}.

I looked-up and found it is used when the variable is mixed with a string.

 

In which line do you expect the misspelling?

I can't see any alien character so far.

 

Edit:

I've been thinking of this

    for CountdownHour in ${noCountdownHours}
        do  [ ${hour} = ${CountdownHour} ] && then return 0
    done
    return 1

Won't return 1 overwrite the previous expression?

Will the for loop close after the done?

Edit:

I've been thinking of this

    for CountdownHour in ${noCountdownHours}
        do  [ ${hour} = ${CountdownHour} ] && then return 0
    done
    return 1

Won't return 1 overwrite the previous expression?

Will the for loop close after the done?

 

The for loop will return 0 (exit the shell) function as soon as hour = anything in the noCOuntdownHours list.

Otherwise it will fall through and return 1.

Why are you using the variables like ${P}.

I looked-up and found it is used when the variable is mixed with a string.

 

P=PROGRAM
PETER=PIPER 

echo $PETER

rcotrone@rgclws:~ $ echo $PETER
piper
rcotrone@rgclws:~ $ echo ${P}ETER
PROGRAMETER

 

It's just being very specific about what it is you want to be returned.  I've been bitten by the wrong variable returned.

Which do you think will be returned?

Much useful information here  8)

 

Now that you got me on the "return code - trip" I realize that I could save some variable declaration.

e.g.

check_LockFile () {
    if [ "$checkLockFile" = $yes ]
    then
        if [ -e "/tmp/nos3sleep.lock" ]
        then
        LockFile=1
        else
        LockFile=0
        fi
    fi

I could simply use the return code.

 

But will this work, if I want to chain the return codes of two functions?

Is this recommended? If the variable is needed often it may cause more CPU load to generate the

return code every time. Can I mix return code and variables?

 

What would be the right syntax?

Like this (to check both return codes)?

if [[ hour_match && check_LockFile ]]
    then

 

Syntax to check one return code and a variable?

 

    if [[ check_hour && "$LockFile" -eq 0 ]]
    then

 

Edit:

According to my findings this should be correct:

(command expansion for check_hour)

if [[ $(check_hour) -eq 0 && "$LockFile" -eq 0 ]]

 

I wouldn't do command expansion like that. The process has to split via fork, a pipe occurs and then you are just using the return value anyway.

 

The reason to use return codes instead of variables is readability and being concise when appropriate.

The reason to use a variable is when you are doing many test comparisons in a loop.

 

You can have the check_hour function set a global variable and ALSO return that via the return command.

Then you can have the best of both worlds.

A variable to check the last status of the call and a return code when needed.

As far as proper syntax for the two shell functions, I have not explored that yet. It's worthy of a test yourself.

In which line do you expect the misspelling?

I can't see any alien character so far.

 

I would suggest re-typing this line from scratch. There's some weird character between logger and -t I believe.

When I cut and pasted the line from notepad into a terminal session running vi, it truncated the line with an error.

 

log_message () {
    logger –t$program_name "$1"

 

  • Author
I wouldn't do command expansion like that. The process has to split via fork, a pipe occurs and then you are just using the return value anyway.

 

The reason to use return codes instead of variables is readability and being concise when appropriate.

The reason to use a variable is when you are doing many test comparisons in a loop.

 

You can have the check_hour function set a global variable and ALSO return that via the return command.

Then you can have the best of both worlds.

A variable to check the last status of the call and a return code when needed.

 

I'm sorry but you lost me with this one.

Where is the fork and the pipe?

 

 

Conciseness is OK if you know what you're doing.

For example this:

if check_hour_list
   then echo "check_hour_list current hour matched"
   else echo "check_hour_list current hour DID NOT MATCH"
fi

Not very clear at first sight (at least to me) because it implies many things I did not know.

 

I wouldn't do command expansion like that. The process has to split via fork, a pipe occurs and then you are just using the return value anyway.

 

I'm sorry but you lost me with this one.

Where is the fork and the pipe?

 

Anytime you do a `command` or $(command)

 

The process sets up a pipe, the process splits with a fork, The child does the command inside the ` ` or $().

the parent reads/processes the data, the child ends.

 

For one or two commands it's not a big deal.

When you do something in a loop over and over many many times rapidly, it adds up to allot of wasted CPU cycles.

Archived

This topic is now archived and is closed to further replies.

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.