シェルスクリプトを記載する時、bashで実行されることを意識する際、冒頭のシェバン (shebang) 行に
1 | #!/bin/bash |
と記載することが多いです。
Linuxではこれで全く問題ないのですが、近年の macOS の状況を見ていると、これはちょっと考え直した方がいいかなと思ってきました。
簡潔に言うと、
1 | #!/usr/bin/env bash |
とするのが汎用性の高い運用になると思います。
シェルスクリプトを記載する時、bashで実行されることを意識する際、冒頭のシェバン (shebang) 行に
1 | #!/bin/bash |
と記載することが多いです。
Linuxではこれで全く問題ないのですが、近年の macOS の状況を見ていると、これはちょっと考え直した方がいいかなと思ってきました。
簡潔に言うと、
1 | #!/usr/bin/env bash |
とするのが汎用性の高い運用になると思います。
Ubuntu 22.04 から、ターミナルでの変数を使ったディレクトリ移動が厄介になりました。
例を挙げます。
FSLのインストールパス は変数 $FSLDIR に入っています。私はこれまでは、$FSLDIR/standard にアクセスしたい場合
cd $FSLDIR までタイプしたら、その後、タブキーをタイプすると、シェルが自動で cd /usr/local/fsl と変数を展開してくれて、その後のディレクトリをタイプしていました。
しかし、Ubuntu 22.04 から、同じことをすると
1 | cd \$FSLDIR/ |
と変数がエスケープされてしまい展開されなくなってしまいました。
これは不便です。
調べたところ、shopt というコマンドがあることを知りました。
画像解析などをしていると、しばしば、「今の作業で新しくできたファイルは何だろう?」と思うことがあります。
Linuxでは、find コマンドに、-newer というオプションがあります。findのmanページには以下の記載があります。
-newer file
ファイルが file よりも最近に内容を更新されていれば、真を返す。
であるならば、あるファイルを作成して、作業をして、その後に、findをかければ列挙できるはずです。
いろいろ調べた結果、touchコマンドで -t オプションを使うとタイムスタンプを決められるので、それをスクリプトに組み込めるなと思いました。
以下のスクリプトで、新たに生成されたファイルの一覧が generated_from_タイムスタンプ.txt に作成されます。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | #!/bin/bash #generate timestamp ts=$( date +%Y%m%d%H%M.%S) #generate a temporary file with current timestamp for find touch -t $ts /tmp/_timestamp # BEGIN commands to generate new files # # END # list files which is generated after /tmp/_timestamp find . - type f -newer /tmp/_timestamp | tee -a generated_from_${ts}.txt |
先日、あるスクリプトを書いている中、.bashrcに追記する内容を書いた時に
1 | echo "something" >> ~/.bashrc |
と書くべきものを
1 | echo "something" > ~/.bashrc |
と誤って書いてしまい、テストでスクリプトを走らせて .bashrc を確認したところ、
something
の一語だけになっており、顔から血の気がひくという経験をしました。
Ubuntu上でFSLやFreeSurferを走らせる時、
1 | Syntax error: "(" unexpected. |
といったエラーが出ることがあります。
この原因を探っていたらわかったことがありました。
FSLのプログラムは、基本、bashで動くことを前提にしていますが、ファイルの冒頭のShebang行は、#!/bin/shとなっています。
RedHatやCentOSは、/bin/sh は、bashにシンボリックリンクが張られていますが、
Ubuntuでは、dashにシンボリックリンクが張られています。
ls -l で確認するとすぐに確認できます。
1 2 | $ ls -l /bin/sh lrwxrwxrwx 1 root root /bin/sh -> dash |
dashとbashは完全に互換性がないため、ときにbashの文法が通用しないことがあります。
解決策は以下の2つです。
自分でシェルスクリプトを書く場合などはこれがいいでしょう。
しかし、FSLでは、300以上のスクリプトがありますので、これを全部変えるのはめんどくさいですね。
Ubuntuでは、 dpkg-reconfigure dash というコマンドで、dashを使わずにbashを使うようにすることができます。
インタラクティブに行うには、以下のコマンドを叩きます。
1 | $ sudo dpkg-reconfigure dash |
すると、次の画面が出るので、「いいえ」を選びます。
再度、/bin/shのシンボリックリンク先を確認してみます。
1 2 | $ ls -l /bin/sh lrwxrwxrwx 1 root root /bin/sh -> bash |
今度はbashに変更されていることがわかります。
なお、インタラクティブでなく変更したい際は、以下で行います。
いくつかのサイトで情報が提供されていましたが、正しくないものもありましたので、試行錯誤した結果、以下が最適な解決法とわかりました。
1 2 | echo "dash dash/sh boolean false" | sudo debconf- set -selections sudo dpkg-reconfigure --frontend=noninteractive dash |
私はこんな作業をよくします。
この際
1 2 | $ cp something /path/to/A $ cd /path/to/A |
とするわけですが、この /path/to/A を2回うつのはめんどくさいので、簡単な方法がないかを調べてみると、
!$
という特殊変数があることがわかりました。
しかし、しょっちゅう忘れるので、覚え方を考えてみました。
bashでは、履歴を知りたいときに、!を使います。
たとえば
1 | $ history |
でコマンド履歴を調べ、その1457番目のコマンドを再度実行したいときは
1 | $ !1457 |
とすればいいわけですね。
で、$ですが、正規表現において、$は「行の最後」を意味します。
この2つを組み合わせれば
「履歴において一番最後のもの」
と考えれば、 !$
は覚えやすいなと思いました。
自分の思考過程を備忘録として書いておきます。
<29/July/2020 追記>
コメントに Esc + . でもいけるというご指摘をいただきました。
これは、viの動作をイメージすると覚えやすいですね。
vi は コマンドモードで、. は繰り返しになるわけですので、
bashでEscでviのコマンドモードに入ったような感じになって、.で前回の引数の繰り返しというイメージで覚えたらいけそうです。
先日、ある方と「BashからMatlabを呼び出せないだろうか」という話をしていました。もし、これができたら、シェルスクリプトから、Matlabを呼び出せるので、シェルとMatlabを完全に連携できるわけです。
結論としては、以下でできました。
Matlabのスクリプト名を sample_code.m とすると、以下でできます。
1 | $ matlab -nodesktop -nosplash -r 'sample_code; exit' |
コツは2つです。
最近、以下のようなことをやる必要がありました。
ファイル名が
201105_ID.jpg
20120608_ID.jpg
20130704_ID.jpg
201409_ID.jpg
となっているファイル(年月が必ずあり、日が時についている)に対して、
年の後にアンダースコアを入れたい、つまり、
2011_05_ID.jpg
2012_0608_ID.jpg
2013_0704_ID.jpg
2014_09_ID.jpg
としたいわけです。