🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

sh

shは、UNIX系OSのシェル操作の1つであり、最も基本的なシェルのことです。

Q&A

解決済

1回答

3282閲覧

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

cat_tattu

総合スコア5

bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

sh

shは、UNIX系OSのシェル操作の1つであり、最も基本的なシェルのことです。

0グッド

0クリップ

投稿2020/01/07 10:00

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

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

bash

1#!/bin/bash 2 3# check.sh [path] 4# 5# path ... dir path (default: current dir) 6 7regex_genome='^GC[AF]_[0-9]{9}.[0-9]_[A-Za-z0-9_.]*_genomic.fna$' 8checkdir=0 9score=0 10 11search () { 12 /bin/ls | while read dir; 13 do 14 [ $dir = "." -o $dir = ".." ] && continue 15 16 if [ -d $dir ]; then 17 18 [ $(echo $dir | grep -v "idae$") ] && checkdir=$dir 19 20 for ((i=0;i<$deep;i++)) 21 do 22 echo -n "| "; 23 done 24 echo -n "+--$dir/" 25 26 [ -L $dir ] && echo `ls -l $dir | sed 's/^.*'$dir' //'` 27 28 echo 29 30 cd $dir && ((++deep)) && search 31 elif [ -f $dir ]; then 32 33 for((i=0;i<$deep;i++)) 34 do 35 echo -n "| "; 36 done 37 echo -n "+ $dir" 38 39 [[ $dir =~ ^timeless_${checkdir}_aa.fa$ ]] && filescore=1 40 [[ $dir =~ ^timeless_${checkdir}_nt.fa$ ]] && filescore=2 41 [[ $dir =~ ^cytb_aa_${checkdir}.fa$ ]] && filescore=4 42 [[ $dir =~ ^cytb_nt_${checkdir}.fa$ ]] && filescore=8 43 [[ $dir =~ $regex_genome ]] && filescore=16 44 45 echo -e "\t$filescore" 46 score=$((filescore+score)) 47 fi 48 49 done 50 51 (($deep == 0)) && exit 0 52 53 cd .. 54 ((deep--)) 55 [[ ${checkdir} != 0 && ${score} != 0 ]] && echo -e "| \ttotal\t$score" 56} 57 58[ $# != 0 ] && cd $1 || exit $? 59 60 61deep=0 62search

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

result

1$ ./script/check.sh $HOME/research/db/ 2+--Apidae/ 3| +--Amel/ 4| | + GCF_003254395.2_Amel_HAv3.1_genomic.fna 16 5| | + cytb_aa_Amel.fa 4 6| | + cytb_nt_Amel.fa 8 7+--Drosophilidae/ 8| +--Dmelanogaster/ 9| | + GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna 16 10| | + cytb_aa_Dmelanogaster.fa 4 11| | + cytb_nt_Dmelanogaster.fa 8 12| | + timeless_Dmelanogaster_aa.fa 1 13| +--Dsim/ 14| | + GCF_000754195.2_ASM75419v2_genomic.fna 16 15| | + cytb_aa_Dsim.fa 4 16| | + cytb_nt_Dsim.fa 8 17| | + timeless_Dsim_aa.fa 1 18| +--Dvir/ 19| | + GCF_000005245.1_dvir_caf1_genomic.fna 16 20| | + cytb_aa_Dvir.fa 4 21| +--Gmel/ 22| | + GCF_003640425.1_ASM364042v1_genomic.fna 16 23| | + cytb_aa_Gmel.fa 4 24| | + 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があやふやな値に更新されていることが考えられますがうまく理解できません。
どうすれば解決できるかを教えていただきたいです。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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

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

Bash

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

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

Bash

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

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

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

Bash

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

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

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

投稿2020/01/07 12:22

otn

総合スコア85886

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cat_tattu

2020/01/08 04:51

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

2020/01/08 05:11

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問