ファイルまたはディレクトリのみへの chmod コマンド

ファイルまたはディレクトリのみへの chmod コマンド

chmod で再帰的にパーミッションを設定するときにいつも思うのが、
「ディレクトリだけとかできないかなぁ・・・」
普通にやるとファイルにも x が付いちゃって実行ファイルになっちゃいますよね。

で、探してみたらありました。
chmod - ファイルのアクセス権の変更

find を利用してファイルまたはディレクトリだけにして、chmod するという手法。

ファイルの場合

find . -type f -print | xargs chmod 644

ディレクトリの場合
find . -type d -print | xargs chmod 755

こりゃ便利だ。

シェルで、自身のスクリプトファイル名を取得する

シェルスクリプト内ではパラメータを使用して、自身のスクリプトファイル名を取得できます。

例えば

#!/bin/sh

FILE=${0}

とかすると、変数:FILE にはスクリプトのファイル名が入ります。
要はパラメータの最初(インデックス0)には自身のファイル名が渡されるってことです。

これ結構使えるんですけど、シェルスクリプトの実行の仕方でパラメータの中身が変わってしまうのがネックでした。
フルパス指定で実行すればフルパスで、相対パスで実行すれば相対パスで渡されてくる。
つまり、"自身のスクリプトファイル名がパラメータで渡される"んじゃなくて、"コマンドラインがそのままパラメータで渡される"ってこと。

私はスクリプトの実行内容のログを取るときに、

LOG_DIR=/var/log
LOG_FILE=${LOG_DIR}/${0}.log

echo `date` > ${LOG_FILE}

みたいなことしてコマンドの実行結果を変数:LOG_FILE で示されるファイルにリダイレクトしたりしてます。
これおわかりの通り、カレントからの相対パスでの実行でしか通用しません。

なんとかならないかなーって探してたら見つけましたよ。
シェルのパラメータ展開でスクリプト自身のファイル名を取得する

私の例だと、

LOG_DIR=/var/log
LOG_FILE=${LOG_DIR}/${0##*/}.log
で、スクリプトファイル名だけがゲットできます。

詳しい原理はリンク先で解説してますので割愛。
こりゃ便利だ。

わかりずらい Java の参照渡し (3/3)

参照に対する勘違いを起こす理由として、Java のメソッドには「オブジェクトを操作するメソッド」と「オブジェクトを生成するメソッド」の 2 タイプあり、最もよく使われる String クラスに「オブジェクトを生成するメソッド」しかないことが挙げられると思います。

結論から言ってしまうと、String クラスに用意されているメソッドは、どのメソッドを使ってもオブジェクトそのものの値は変わりません。
言い換えると、オブジェクトそのものの値を変えることができません。そのようなメソッドが存在しないからです。
String クラスにはオブジェクトの値を元にした新しいオブジェクトを生成するメソッドしか存在しません。

先のサンプルAの test1 メソッドの内容を変えたサンプルCを例とします。

サンプルC
public class SampleC
{
 public static void main(String[] args)
 {
  String s1 = "main メソッド実行";

  SampleC sampleC = new SampleC();
  sampleC.test1(s1);

  System.out.println(s1);     ・・・4
 }

 public void test1(String s2)
 {
  s2.toUpperCase();        ・・・1
  s2.replaceAll("main", "test1"); ・・・2
  s2.concat("?");         ・・・3
 }
}

このサンプルCを実行した時、の部分でコンソールには
> java SampleC
main メソッド実行
と出力されます。

test1 メソッド内では変数:s2 の String オブジェクトのメソッドを実行しています。
の toUpperCase() は文字列を大文字に変換するメソッド、 の replaceAll(String, String) は文字列内にある第1引数の文字を第2引数の文字に置き換えるメソッド、 の concat(String) は引数に指定した文字列を末尾に追加するメソッドです。
しかし、このどれを実行しても、変数:s2 が参照している String オブジェクト(これは変数:s1 が参照しているオブジェクトと同じ)は変わりません。
それはこれらのメソッドが「オブジェクトを生成する」タイプのメソッドだからです。

これに対しサンプルBで使用した Vector#add(Object) メソッドは「オブジェクトを操作するメソッド」なので、元のオブジェクトの内容が変更されます。

メソッドの結果として生成されたオブジェクトは戻り値として返りますので、変数で受けてやる必要があります。

 public void test1(String s2)
 {
  String s3 = s2.toUpperCase();
  String s4 = s2.replaceAll("main", "test1"");
  String s5 = s2.concat("?");
 }
こうすると、変数:s3, s4, s5 のそれぞれに結果オブジェクト(の参照)が格納されます。
具体的には変数:s3 には "MAIN メソッド実行"、変数:s4 には "test1 メソッド実行"、変数:s5 には "main メソッド実行?" が入ります。
この時、もちろん変数:s2 が参照しているオブジェクトは変更されません。

この String クラスの「オブジェクトを生成するメソッド」に慣れてしまい、
"メソッドを実行しても元のオブジェクトは変わらない"
= "渡した元のオブジェクトは操作されない"
= "参照渡しってどういうこと?"
という混乱に繋がっているのだと思います。
そして、たまに使用する「オブジェクトを操作するメソッド」のせいで、渡したオブジェクトが変わる時と変わらない時があり、さらに混乱してしまいます。

Java には「オブジェクトを操作するメソッド」や、渡されたバッファに値を格納するようなメソッドが少ないです。
ほとんどがこの「オブジェクトを生成するメソッド」です。
ポインタ=参照渡しを意識させないための工夫でしょうが、これだけでは限界があったのか結果的に「オブジェクトを操作するメソッド」が存在することによって混乱を招いていると思います。

使用するクラスのメソッドが「オブジェクトを生成するメソッド」なのか、「オブジェクトを操作するメソッド」なのか − 意識して使用していくことで、混乱は防げると思います。

私は Sun の人間ではありませんし、VM やメモリをハックしたわけでもありませんので、記載された内容は厳密には事実と異なる部分があるかもしれません。 ですが、「納得できて理解できればいいな」という考えで公開しています。この点はご了承ください。

カテゴリー

ブログの一覧

RSS Atom