Bashスクリプトとプロセス

Bashスクリプトとプロセス

Linuxでグラフバー用のbashスクリプトを作成しましたが、スクリプトがあまりにも多くのプロセスを生成しているようです。

スクリプト:

#!/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出力:

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:~%

デーモン化できる場所が4つしかないのに、なぜプロセスがそんなに多いのですか?このスクリプトがあまりにも多くのリソースを使用している場合は、それを修正したいと思います。

ベストアンサー1

これはどこでもサブシェルを起動するために発生します:-)

使用中の構文(使用中の場所{ some_stuff } 2>/dev/null | other_stuff)は、中かっこ間のすべてのコードビットのサブシェルを生成します。これは、次のスクリプトを使用してかなり簡単に説明できます。

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

これにより、次のような出力が生成されます。

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

その理由は、パイプライン内のすべてのコマンドを同時に実行する必要があるためです。したがって、シェルをフォークする必要があります。

Linuxはフォーク時に書き込み時にコピーメモリ割り当てを使用するため、これは大きなリソースの無駄ではありません。これは、プロセスがフォークされたときにメモリ使用量が2倍にならないことを意味します。両方のプロセスのいずれかがそのメモリを変更するまで、両方のプロセスは同じメモリを使用します。変更が発生すると、特定のメモリページがコピーされます。

唯一の解決策は、同じパイプラインでスクリプトの複数の部分を実行しないことです。これを達成する方法は完全にあなた次第です。 1つのオプションは、サブシェルで最後のコマンドを使用する
ことです。execこのように実行されているコマンドは、シェルのPIDを引き継ぎます。

おすすめ記事