Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have created a bash script for use with a graphical bar in Linux, however the script seems to spawn too many processes.

The script:

#!/usr/bin/bash

executable="/usr/local/bin/bar"
monitor=${1:-0}
monitor_geometry=($(hc monitor_rect) $monitor)
x=${monitor_geometry[0]}
y=${monitor_geometry[1]}
panel_width=${monitor_geometry[2]}
panel_height=20
line_height=2
font="-*-terminesspowerline-medium-*-*-*-12-*-*-*-*-*-*-*"
bgcolor="#ff073642"
selbgcolor="#fffdf6e3"
selfgcolor="#ffeee8d5"
normfgcolor="#ff586e75"
urgentcolor="#ffdc322f"
separator="   "
icon_signal_max="|||"
icon_signal_med="||-"
icon_signal_min="|--"
icon_battery_charging="-"
icon_battery_discharging="x"
icon_arrow_up=""


# function declarations

hc() {
    "${herbstclient_command[@]:-herbstclient}" "$@";
    }

unique() {
    awk '$0 != l { print; l=$0; fflush(); }' "$@"
}

get_datetime() {
    {
        while true; do
            date +"datetime %a %d-%m-%Y (%V) %H:%M"
            sleep 60
        done
    } | unique
}

get_power_status() {
    {
        while true; do
            acpi_status=$(acpi --battery | cut -d' ' -f3 | sed 's/,//')
            charge_percentage=$(acpi --battery | cut -d' ' -f4 | sed 's/%.*//')

            if [[ $acpi_status ]]; then
                if [[ "$acpi_status" == "Charging" ]]; then
                    status="$charge_percentage%%"
                elif [[ "$acpi_status" == "Discharging" ]]; then
                    status="$charge_percentage%%"
                elif [[ "$acpi_status" == "Unknown" ]]; then
                    status=""
                fi
            fi

            if [[ "$charge_percentage" -le "20" ]]; then
                status="%{F$urgentcolor}$status{F-}"
            fi

            echo "power_status $status"
            sleep 1
        done
    } | unique
}

get_network_status() {
    {
        while true; do
            wlan_ssid=$(iwgetid -r)

            if [ -n $wlan_ssid ]; then
                signal_strength=$(cat /proc/net/wireless | awk 'NR==3 {print $3}' | sed 's/\.//')
                if [ "$signal_strength" -ge 65 ]; then
                    status="$wlan_ssid $icon_signal_max"
                elif [ "$signal_strength" -lt 65 -a "$signal_strength" -ge 40 ]; then
                    status="$wlan_ssid $icon_signal_med"
                else
                    status="$wlan_ssid $icon_signal_min"
                fi
            else
                status=""
            fi

            echo "network_status $status"
            sleep 1
        done
    } | unique
}

get_cpu_status() {
    {
        while true; do
            no_cores=$(nproc)
            loadavg=$(cat /proc/loadavg | awk '{print $1}')

            if [ $(echo "$loadavg >= $no_cores" | bc) -ne 0 ]; then
                status="%{F${urgentcolor}}${loadavg}%{F${normfgcolor}}"
            else
                status="$loadavg"
            fi

            echo "cpu_status $status"
            sleep 1
        done
    } | unique
}

# register panel
hc pad $monitor $panel_height


# event multiplexer
{
    get_datetime &
    children[1]=$!

    get_power_status &
    children[2]=$!

    get_network_status &
    children[3]=$!

    get_cpu_status &
    children[4]=$!

    hc --idle

    for pid in ${children[@]}; do
        kill $pid
    done
} 2> /dev/null | {
    tags=$(hc tag_status $monitor)
    unset tags[${#tags[@]}-1]
    visible=true

    while true ; do
        echo -n "%{c}"
        for i in ${tags[@]}; do
            case ${i:0:1} in
                '.') # empty tag
                    echo -n "%{-uF${normfgcolor}}"
                    ;;
                '#') # current tag
                    echo -n "%{+u U${selfgcolor} F${selfgcolor}}"
                    ;;
                '+') # active on other monitor
                    echo -n "%{-uF$selfgcolor}"
                    ;;
                ':') # tag with window(s)
                    echo -n "%{-uF$selfgcolor}"
                    ;;
                '!') # urgent tag
                    echo -n "%{-uF${urgentcolor}}"
                    ;;
                *)
                    echo -n "%{-uF${normfgcolor}}"
                    ;;
            esac
            echo -n "  ${i:1}  "
        done

        # align left
        echo -n "%{lF$selfgcolor}"
        echo -n " "
        echo -n "$power_status"
        echo -n "$network_status"
        echo -n "$cpu_status"

        # align right
        echo -n "%{r}"
        echo -n "$datetime"
        echo -n " "
        echo

        # wait for next event
        read line || break
        cmd=( $line )

        # find out event origin
        case ${cmd[0]} in
            tag*)
                tags=$(hc tag_status $monitor)
                unset tags[${#tags[@]}-1]
                ;;
            datetime)
                datetime="${cmd[@]:1}"
                ;;
            power_status)
                power_status="${cmd[@]:1}"
                ;;
            network_status)
                network_status="${cmd[@]:1}"
                ;;
            cpu_status)
                cpu_status="${cmd[@]:1}"
                ;;
            reload)
                exit
                ;;
            quit_panel)
                exit
                ;;
        esac
    done
} 2> /dev/null | $executable -g ${panel_width}x${panel_height}+${x}+${y} -f $font -u $line_height -B $bgcolor -F $selfgcolor

ps output:

jakob@jw-laptop:~%  ps faux | grep panel.sh
jakob    26906  0.0  0.0  12908  2324 pts/1    S+   02:34   0:00      \_ grep --color panel.sh
jakob    26616  0.0  0.0  15780  3220 ?        S    02:34   0:00 /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26619  0.0  0.0  15780  1880 ?        S    02:34   0:00  \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26622  0.0  0.0  15780  1624 ?        S    02:34   0:00  |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26625  0.0  0.0  15780  1752 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26627  0.0  0.0  15780  1752 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26624  0.0  0.0  15780  1628 ?        S    02:34   0:00  |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26633  0.0  0.0  15912  2496 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26634  0.0  0.0  15780   480 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26626  0.0  0.0  15780  1628 ?        S    02:34   0:00  |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26631  0.0  0.0  15912  2560 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26632  0.0  0.0  15780   480 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26628  0.0  0.0  15780  1624 ?        S    02:34   0:00  |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26637  0.0  0.0  15784  2556 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26638  0.0  0.0  15780   476 ?        S    02:34   0:00  |   |   \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob    26620  0.0  0.0  15912  2556 ?        S    02:34   0:00  \_ /usr/bin/bash /home/jakob/.config/herbstluftwm/panel.sh 0
jakob@jw-laptop:~%

Why so many processes, when there's only four places where I daemonize? If this script is using too many resources I would like to fix it.

share|improve this question
5  
This question appears to be off-topic because it belongs on Code Review... –  jasonwryan Jul 8 at 1:32
    
1  
If you want us to read your script, you must post it in your question, not on an external site. However a 210-line script is too much. Shrink it to about 50 lines top that demonstrate the problem. Edit your question to add a working script (not necessarily with all the bells and whistles of the original) and explain why you think that script spawns too many processes. –  Gilles Jul 8 at 2:19
3  
@jasonwryan “Because it belongs elsewhere” is not a reason why a question would be off-topic here. This site's topic is not defined in terms of what's on-topic or not on other sites. –  Gilles Jul 8 at 2:20
1  
@jasonwryan I would have been happy to explain where these extra processes were coming from. That to me seems what the OP is asking, and is factual. –  Patrick Jul 8 at 3:02

1 Answer 1

up vote 3 down vote accepted

This is happening because you are launching subshells all over the place :-)

The syntax you are using, where you do { some_stuff } 2>/dev/null | other_stuff, creates a subshell for each bit of code between the curly braces. This can be demonstrated fairly easily with the following script:

#!/bin/bash
{ sleep 1; } | { sleep 2; } | { sleep 3; } & ps axf

Which results in the following output:

phemmer  26014  19   0  0.0  0.0 S+         00:00      \_ bash /tmp/test.sh
phemmer  26015  19   0  0.0  0.0 S+         00:00          \_ bash /tmp/test.sh
phemmer  26018  19   0  0.0  0.0 S+         00:00          |   \_ sleep 1
phemmer  26016  19   0  0.0  0.0 S+         00:00          \_ bash /tmp/test.sh
phemmer  26020  19   0  0.0  0.0 S+         00:00          |   \_ sleep 2
phemmer  26017  19   0  0.0  0.0 S+         00:00          \_ bash /tmp/test.sh
phemmer  26021  19   0  0.0  0.0 S+         00:00          |   \_ sleep 3
phemmer  26019  19   0  0.0  0.0 R+         00:00          \_ ps axf

The reason for this is because all the commands in a pipeline have to run at the same time. So the shell has to fork.

This isn't a huge resource waste as linux uses copy-on-write memory allocation when forking. Meaning that when a process forks, it's memory usage isn't doubled. Both processes will use the same memory until one of the processes changes that memory. Once it's changed that specific memory page is copied.

The only solution to this is to not execute multiple parts of the script in the same pipeline. How you accomplish this is entirely up to you.
One option might be to use an exec for the last command in the subshell. This way the command being run takes over the PID of the shell.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.