質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.63%

グローバル変数をうまく更新できない

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 112

cat_tattu

score 1

シェルスクリプトを書いていたところ、つまづいた

このスクリプトは引数で指定したディレクトリパスからそのディレクトリツリーとファイルを表示します。
最終目標としては子ディレクトリごとにあるファイルがパターンにマッチしたものであればそれぞれスコアリングしていき、そのディレクトリにおけるファイルの総スコアによって実行させるプログラムを変える、というようなことを考えています。

以下にスクリプトを載せます。

#!/bin/bash

# check.sh [path]
#
# path ... dir path (default: current dir)

regex_genome='^GC[AF]_[0-9]{9}\.[0-9]_[A-Za-z0-9_\.]*_genomic\.fna$'
checkdir=0
score=0

search () {
    /bin/ls | while read dir;
    do
    [ $dir = "." -o $dir = ".." ] && continue

    if [ -d $dir ]; then

        [ $(echo $dir | grep -v "idae$") ] && checkdir=$dir

        for ((i=0;i<$deep;i++))
        do
        echo -n "|  ";
        done
        echo -n "+--$dir/"

        [ -L $dir ] && echo `ls -l $dir | sed 's/^.*'$dir' //'`

        echo

        cd $dir && ((++deep)) && search
    elif [ -f $dir ]; then

        for((i=0;i<$deep;i++))
        do
        echo -n "|  ";
        done
        echo -n "+ $dir"

        [[ $dir =~ ^timeless_${checkdir}_aa\.fa$ ]] && filescore=1
        [[ $dir =~ ^timeless_${checkdir}_nt\.fa$ ]] && filescore=2
        [[ $dir =~ ^cytb_aa_${checkdir}\.fa$ ]] && filescore=4
        [[ $dir =~ ^cytb_nt_${checkdir}\.fa$ ]] && filescore=8
        [[ $dir =~ $regex_genome ]] && filescore=16

        echo -e "\t$filescore"
        score=$((filescore+score))
    fi

    done

    (($deep == 0)) && exit 0

    cd ..
    ((deep--))
    [[ ${checkdir} != 0 && ${score} != 0 ]] && echo -e "|  \ttotal\t$score"
}

[ $# != 0 ] && cd $1 || exit $?


deep=0
search


スコアはファイルごとのスコアは思った通りに出ますが、総スコアが出力されません。

$ ./script/check.sh $HOME/research/db/
+--Apidae/
|  +--Amel/
|  |  + GCF_003254395.2_Amel_HAv3.1_genomic.fna    16
|  |  + cytb_aa_Amel.fa    4
|  |  + cytb_nt_Amel.fa    8
+--Drosophilidae/
|  +--Dmelanogaster/
|  |  + GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna    16
|  |  + cytb_aa_Dmelanogaster.fa    4
|  |  + cytb_nt_Dmelanogaster.fa    8
|  |  + timeless_Dmelanogaster_aa.fa    1
|  +--Dsim/
|  |  + GCF_000754195.2_ASM75419v2_genomic.fna    16
|  |  + cytb_aa_Dsim.fa    4
|  |  + cytb_nt_Dsim.fa    8
|  |  + timeless_Dsim_aa.fa    1
|  +--Dvir/
|  |  + GCF_000005245.1_dvir_caf1_genomic.fna    16
|  |  + cytb_aa_Dvir.fa    4
|  +--Gmel/
|  |  + GCF_003640425.1_ASM364042v1_genomic.fna    16
|  |  + cytb_aa_Gmel.fa    4
|  |  + cytb_nt_Gmel.fa    8


次に表示しているのはbashのデバッグ機能によるスクリプトの動きの一部です。

...
+ echo -e '\t8'
+ score=28
+ read dir
+ (( 2 == 0 ))
+ cd ..
+ (( deep-- ))
+ [[ Amel != 0 ]]
+ [[ 0 != 0 ]]
...


再帰によりscoreの値が変わっているためscoreが0になっている気がしますがいまいちわかりません。

スクリプトの55行目の条件文の${score} != 0の算術演算子を==に変えると次のような実行結果になります。

$ ./script/check.sh $HOME/research/db/
+--Apidae/
|  +--Amel/
|  |  + GCF_003254395.2_Amel_HAv3.1_genomic.fna    16
|  |  + cytb_aa_Amel.fa    4
|  |  + cytb_nt_Amel.fa    8
|      total    0
+--Drosophilidae/
|  +--Dmelanogaster/
|  |  + GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna    16
|  |  + cytb_aa_Dmelanogaster.fa    4
|  |  + cytb_nt_Dmelanogaster.fa    8
|  |  + timeless_Dmelanogaster_aa.fa    1
|      total    0
|  +--Dsim/
|  |  + GCF_000754195.2_ASM75419v2_genomic.fna    16
|  |  + cytb_aa_Dsim.fa    4
|  |  + cytb_nt_Dsim.fa    8
|  |  + timeless_Dsim_aa.fa    1
|      total    0
|  +--Dvir/
|  |  + GCF_000005245.1_dvir_caf1_genomic.fna    16
|  |  + cytb_aa_Dvir.fa    4
|      total    0
|  +--Gmel/
|  |  + GCF_003640425.1_ASM364042v1_genomic.fna    16
|  |  + cytb_aa_Gmel.fa    4
|  |  + cytb_nt_Gmel.fa    8
|      total    0


総スコアはすべて0になっています。
elif内でscoreを表示すると総スコアが表示はされますが、更新されていくscoreが表示されて煩わしいです。 

総スコアが0になる原因として、前述したように再帰を用いているためscoreがあやふやな値に更新されていることが考えられますがうまく理解できません。
どうすれば解決できるかを教えていただきたいです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

仕様が不明でごちゃごちゃしているので、全部は見ていませんが、変数の値が共有できていないのは、再帰呼び出しのせいではなくて、パイプを使っているためにパイプの両側がサブシェルで実行され、そこでの変数が親シェルと異なるためです。

参考:
・再帰では変数が共有される(同じシェルプロセスなので)

foo(){
    let X++; echo $X
    let A=$1-1
    [ $A != 0 ] && foo $A
}
X=0
foo 5


・パイプを使うと変数が共有されない

A=1
date | while read X; do A="$X"; echo $A; done
echo $A

/bin/ls | while read dirfor dir in *に変えればパイプが無くなるので、変数が共有できると思います。

あと、caseを使うとすっきりする部分もあります。

        case "$dir" in
        timeless_${checkdir}_aa.fa) filescore=1 ;;
        timeless_${checkdir}_nt.fa) filescore=2 ;;
        cytb_aa_${checkdir}.fa)     filescore=4 ;;
        cytb_nt_${checkdir}.fa)     filescore=8 ;;
        *) [[ $dir =~ $regex_genome ]] && filescore=16 ;;
        esac


と思ったけど、最後がその正規表現必須ならいまいちですね。ワイルドカードでのチェックでよければ他と揃えられます。
シンボリックリンクのところは、readlinkコマンドを使うとシンプルになると思います。

他に、気になる点としては、[[ =~ ]]を使った正規表現テストを知っているのに、[ $(echo $dir | grep -v "idae$") ] && checkdir=$dirのようなコマンド起動をしている点。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/08 13:51

    パイプによって変数が共有されなくなることは初めて知りました。
    また、スコアリングする際に毎回echoしてたのも冗長だと思っていて、switchみたいな書き方があると便利だなと思っていたところ、caseで同様にできるということを知ることができました。
    詳細に解説してくださってありがとうございます。

    キャンセル

  • 2020/01/08 14:11

    これくらい書ける人だと、mam bashを通読するといろいろ発見があると思います。
    ・READLINE ライブラリ
    ・履歴
    のあたりは飛ばし読みで。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる