scope of variable in pipe

2020-02-11 07:00发布

The following shell scrip will check the disk space and change the variable diskfull to 1 if the usage is more than 10% The last echo always shows 0 I tried the global diskfull=1 in the if clause but it did not work. How do I change the variable to 1 if the disk consumed is more than 10%?

#!/bin/sh
diskfull=0

ALERT=10
df -HP | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print $5 " " $1 }' | while read output;
do
  #echo $output
  usep=$(echo $output | awk '{ print $1}' | cut -d'%' -f1  )
  partition=$(echo $output | awk '{ print $2 }' )
  if [ $usep -ge $ALERT ]; then
     diskfull=1
     exit
  fi
done

echo $diskfull

标签: shell pipe
6条回答
该账号已被封号
2楼-- · 2020-02-11 07:07

@OP, use an outer brace or ()

count=0
max=10
diskfull=0
df -HP | { while read disk b c d used e
do    
    if [ "$count" -gt 1 ];then
        used=${used%?}
        if [ "$used" -gt "$max" ];then
            echo "overload: $disk, used: $used%"
            diskfull=1
        fi
    fi
    count=$(( count+1 ))
done 
echo "diskfull: $diskfull" 
}
查看更多
Root(大扎)
3楼-- · 2020-02-11 07:09

This is a side-effect of using while in a pipeline. There are two workarounds:

1) put the while loop and all the variables it uses in a separate scope as demonstrated by levislevis86

some | complicated | pipeline | {
    while read line; do
        foo=$( some calculation )
    done
    do_something_with $foo
}
# $foo not available here

2) if your shell allows it, use process substitution and you can redirect the output of your pipeline to the input of the while loop

while read line; do
    foo=$( some calculation )}
done < <(some | complicated | pipeline)
do_something_with $foo
查看更多
爱情/是我丢掉的垃圾
4楼-- · 2020-02-11 07:09

In this line:

usep=$(echo $output | awk '{ print $1}' | cut -d'%' -f1  )

it's not necessary to use cut. You can do this:

usep=$(echo $output | awk -F% '{ print $1}' )
查看更多
5楼-- · 2020-02-11 07:10

you can do it this way with gawk(no need to use grep). for alerts you can send email to root.

threshold=10
df -HP | awk -v t="$threshold" -v msg="" 'NR>1 && $5+0 > t{ 
    msg=msg $1" is "$5"\n"
}END{print msg}' | mail root 

or check whether there is "msg" or not first

threshold=10
result=$(df -HP | awk -v t="$threshold" -v msg="" 'NR>1 && $5+0 > t{ 
    msg=msg $1" is "$5"\n"
}END{print msg}')
if [ -n "$result" ];then
  echo "Overload"
  echo "$result" | mail root

fi
查看更多
Luminary・发光体
6楼-- · 2020-02-11 07:13

I think you must not be getting to the diskfull=1 line, because if you were, you would get no output at all-- the following exit line would exit the script.

I don't know why this isn't working, but note that awk can handle the rest of the work:

diskfull=$(df -HP | grep -vE '^Filesystem|tmpfs|cdrom' | awk 'BEGIN { x = 0 } { if ($5 + 0 >= '$ALERT') { x = 1 } } END { print x }')

This way you don't need the while loop.

查看更多
Summer. ? 凉城
7楼-- · 2020-02-11 07:24

When using pipes the shell seams to use sub-shells to do the work. As $diskfull is not known to these sub-shells the value is never changed.

See: http://www.nucleardonkey.net/blog/2007/08/variable_scope_in_bash.html

I modified your script as follows. It works for me and should work on your system too.

#!/bin/sh
diskfull=0

ALERT=10
stats=`df -HP | grep -vE '^Filesystem|tmpfs|cdrom|none|udev' | awk '{ print $5 "_" $1 }'`
for output in $stats
do
  usep=$(echo $output | awk '{ print $1}' | cut -d'%' -f1  )
  partition=$(echo $output | sed s/.*_// )
  #echo $partition -  $usep
  if [ $usep -le $ALERT ]; then
     diskfull=1
     break
  fi
done
echo $diskfull
查看更多
登录 后发表回答