それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

ruby の URI で日本語を含むURIに対応する。

日本語URIを扱うという戦い

rubyURIで日本語を含む文字列が扱えなくなったので、何とかしないといけないとアレコレ模索した。

>> URI.parse  'http://example.com/あああ'
URI::InvalidURIError: URI must be ascii only "http://example.com/\u{3042}\u{3042}\u{3042}"
from /Users/takuya/.rbenv/versions/2.2.3/lib/ruby/2.2.0/uri/rfc3986_parser.rb:20:in `split'

URI.encode が使えることを知った。

>> URI.encode 'http://example.com/あああ'
=> "http://example.com/%E3%81%82%E3%81%82%E3%81%

parse encode と合わせて使うことでほとんどの問題は解決する。

>> URI.parse URI.encode 'http://example.com/あああ'
=> #<URI::HTTP http://example.com/%E3%81%82%E3%81%82%E3%81%82>

これで、エラーなく扱うことができる。

が、、、、encode を毎回指定するのはめんどくさくないかな。

参考資料

http://d.hatena.ne.jp/haroperi/20110405/1301959688

renameコマンドでぱぱっと連番付きファイルを作成する。

連番を付記したファイルを作るのが、めんどくさくなってきた

for ループと sprintf を組み合わせたり、ruby ワンライナーでヤってたけど、だんだんめんどくさくなってきた

rename Commandでぱぱっと作ることにした

rename -N 01 -X -e '$_ = "京都旅行-2016-08-27_$N"' *.jpg

これでぱぱっと、ファイルを連番にできて便利すぎてヤバイ。

-N : 引数のフォーマットと開始番号を指定する。例えば 0001 なら4桁0埋め、 100 なら開始番号が100
-X : 拡張子を置換対象にしない
-e : 出力するフォーマット $N が番号が入る所

先頭に連番を付ける場合

もとのファイル名をそのままにして 先頭に連番を付ける場合

./rename -n -N 001 -X  -e '$_="$N\_$_"' *

2017/01/11追記: 辞書順に並んでしまう時

すでに連番が振っていて、辞書順に並んでしまうときは、ちょっと工夫が必要かもしれない。

rename -N 0001 -X -e '$_ = "img-$N"' $( ls -v  *.jpg )

ls -v が使えないときはもうけっこう大変だと思う。何かいい方法あるのかな

2022-05-30 追加

インストールする方法。

wget http://plasmasturm.org/code/rename/rename
chmod +x rename
./rename -h

apt で入るものが変わってたのでインストールし直した。

rubyの置換でコールバックを使う。

JavaScript の String#replace はコールバックが使えて便利。

置換でコールバックの関数使えると便利なんですよね。

> a.replace(/a/, function(e){ console.log(e); return ''})
a
'bc'
>

js のこの仕様が結構過ぎで多用しまくっててクセになってた

ruby でもブロック渡せることを知った。

'abc'.gsub(/a/){ 'abc' } #=> "abcbc"

ブロック渡せるの便利だよね。大雑把な正規表現でも細かい部分を微調節できるし。正規表現をアレコレいじくり回して直感的じゃなくなったり、複雑になりすぎた正規表現を調整できる。

match でもブロック使える

>> 'abc'.match(/(.)/){|e|  p e ; nil   }
#<MatchData "a" 1:"a">
=> nil

正規表現が複雑になり過ぎないように、ブロックを使うのも悪くないよね。

参考資料

gsub, gsub! (String) - Rubyリファレンス

ruby のURI.parse が日本語GET引数や日本語UTF-8のパスに対応しなくなった

対応しなくなったんですよ。マジで。

日本語を含むとエラー

>> URI.parse 'http://example.com/あああ'
URI::InvalidURIError: URI must be ascii only "http://example.com/\u{3042}\u{3042}\u{3042}"
from /Users/takuya/.rbenv/versions/2.2.3/lib/ruby/2.2.0/uri/rfc3986_parser.rb:20:in `split'
>>

ruby まじどうしようもないな。

日本語を含むURIはASCIIにEncodeしろってことだろうけどさ、ブラウザからURIをコピペしたら日本語含まれるじゃん?

日本語が含まれたまま使えないと不便だよね。

モンキーパッチで対応する

require 'uri'


module URI
  class << self
    alias :_parse :parse

    def parse a, original=false
      return self._parse a if original

      r=/[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+/
      a = a.gsub(r){|m|  URI.encode_www_form_component(m) }

      u = self._parse a
      return u
    end
  end
end

とりあえず対応することが出来る。大抵のURIはコレで対応できそう。

npm のモジュール地獄見てて、gem の同じような地獄に成りつつあるし。イチイチ依存Gem増やす必要はないかもしれない。

関連資料

ruby で URL をパースしてGET引数をHashに展開する。(日本語URI対応) - それマグで!

ruby の暗黙的文字列変換 toString 的なものをするには

ruby って to_s が自動的に呼ばれない。

たとえば、Hashと文字列を結合しようとした時

h = { name:'takuya' }

str = 'this obj is  ' + h

puts str

Hash を String にする方法がないからエラー

takuya@~/Desktop$ ruby  test.rb
test.rb:5:in `+': no implicit conversion of Hash into String (TypeError)
        from test.rb:5:in `<main>'

他の言語では?

Java とかだと、toString 定義しておけば読んでくれるじゃん?

class Man {
     public String toString(){  return " I'm man "; }
}

public class Main{
    public static void main(String[] args){
      Man m = new Man();
      String s = "Hello "+ m;
      System.out.println(  s );
    }
}

こういう、暗黙的な型変換を呼び出すにはどうするのか

to_s ではなく to_str を使う

to_str を使うと、目的を達成することが出来る。

よく見るとエラーメッセージもno implicit conversion(暗黙的変換がない)と言ってる

to_str を追加する

h = { name:'takuya' }

def h.to_str
  self.to_s
end

str = 'this obj is  ' + h

puts str

実行結果

takuya@~/Desktop$ ruby  test.rb
this obj is  {:name=>"takuya"}

何でこんなことになってるのか。

コーディングにおける意図しない変換を防ぐためだと。

たとえば、数字だって、暗黙的変換がないので、明示的に to_s する必要がある。

'this is ' +  1 +  ' th place  my family  traveled' ## ダメ
'this is ' +  1.to_s +  ' th place  my family traveled'  ## おっけ

そういえば、明示的にto_s つけるか、#{number}で埋め込んでたわ。

Javaって暗黙型変換が便利なようで実は不便だからな。Rubyはこういう変なところで厳格な言語だよね。

ちなみに、覚えておくべき英語は次の通り。

  • explicit 明示的
  • implicit 暗黙的

参考資料

http://qiita.com/tbpgr/items/19b42765a7b9cb292f44

無線LANの設定(SSIDの並び順と一覧)が保存されてるplist の場所

OSX無線LANの順番が面倒くさい。

SSIDが10超えたあたりでもう管理できなくなってきて、現在100近くになってる。

f:id:takuya_1st:20160822022644j:plain:w300

無線LANの設定ファイルはドコになるの。

もう PreferencePane から設定変更してるレベルじゃなくなってきた。 plist さがして直接触ろう。。。

設定の保存される場所

下記のファイルに保存されます。

/Library/Preferences/SystemConfiguration/com.apple.airport.preferences

read で読んでみる。

defaults read /Library/Preferences/SystemConfiguration/com.apple.airport.preferences

SSIDの優先度の設定はドコか

defaults read /Library/Preferences/SystemConfiguration/com.apple.airport.preferences PreferredOrder

PreferredOrder が該当の優先度の設定ですね。

ここで問題点

ssid は ガチのSSIDで入っていて、SSIDStringと SSIDが別管理になっている。

/usr/libexec/PlistBuddy    /Users/takuya/.Desktops/2016-04-24/com.apple.airport.preferences.plist   -c 'print KnownNetworks:wifi.ssid.<27667265\ 61737377\ 6f726428\ 41455329>'

などとして、該当キーを探す必要がある。

並び順のファイルはドコか

defaults read    /Users/takuya/.Desktops/2016-04-24/com.apple.airport.preferences.plist   PreferredOrder
(
    "wifi.ssid.<6f63752e 612e6a70>",
    "wifi.ssid.<74616b75 6f6e6535>",

これで見られるんだけど、キーが。。。SSIDStringじゃなくて別のキーになってるので、手作業で並び直すには、ちょっとCFBundleから触れる奴作ったほうがややそう

そのうちやる

Cocoa から、ドラッグドロップで並べ替えられるや作ります。

参考資料

http://osxdaily.com/2012/12/21/list-wi-fi-networks-mac-has-connected-to-before/

ヤフオク違反通報の自動化。ヤフオクを快適に使うために

yahoo オークションで物を買おうと思うと邪魔な連中がいる。

堂々とした、規約違反。とくに関係のないカテゴリに、商品を出品しまくってる連中。

こういうのは、百害あって一利なし。

エアコンの取り外しや家電の回収など。こいつら電化製品コーナーに出品してくるからたちが悪い。 スマホ買おうと思うと、本体のカテゴリにアクセサリ出品する奴ら

もうまとめてヤフオクに通報することにする。

特定キーワードを除外する検索作ろうと思ったが

特定キーワードを除外するヤフオク検索を作ろうと思ったけど、そういうのより、ガッツリ通報してヤフオクの削除パターンに含めてもらったほうが有益な気がしてきたのです。

通報の自動化

といっても、プログラムを書く人なので、通報は完全に自動化しようと思います。

使い方

yahooオークション通報.rb http://検索結果ページ

通報のコツ

鬱陶しい出品者を見つけたら、その出品者の出品一覧ページに移動する。

出品一覧URLをスクリプトにに突っ込んで、まとめて通報

仕組み

chrome のクッキーを利用し、ヤフオクにログインして、通報する。

準備

git clone https://github.com/takuya/chrome-storage
mv chrome-storage $RUBYLIB

ソースコード

#!/usr/bin/env ruby
#coding: utf-8
require 'mechanize'
require 'pry'
require 'json'

require 'chrome-storage'

class Mechanize
  def load_chrome_cookie(cookie_domain, cookie_path="/")


    data = ::ChromeStorage.dump_cookie_data('yahoo')

    data.each{|domain,values|
      values.each{|e|
        cookie_params = {:name=>e[:name],:value=> e[:value], :domain => domain, :expires => Time.now + 60*60*24*30,:created_at=> Time.now - 60*60*24*30, :path => e[:path] }
        cookie = Mechanize::Cookie.new(cookie_params)
        self.cookie_jar.add(cookie)
      }
    }

    self.cookies.map{|e| e.cookie_value + ";"}.join(" ")

  end
end

m = Mechanize.new
m.user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.117 Safari/537.36 Vivaldi/1.3.551.30'
m.load_chrome_cookie("yahoo.co.jp")
# m.cookies.map{|e| e.cookie_value + ";"}.join(" ")
m.get("http://auctions.yahoo.co.jp/")
raise "ログイン失敗してる" unless m.page.body =~/ようこそ/

# 通報したいカテゴリとか検索結果のURL
m.get ARGV.shift
# オークションID取り出し
auction_id_list = m.page.search("h3 a[href*=page]").map{|e| File.basename(URI.parse(e.attr("href")).path)}

# 通報
auction_id_list.each{|e|
  m.get "http://pageinfo.auctions.yahoo.co.jp/jp/show/reviews?aID=#{e}"
  print "#{e} #{m.page.title }"
  f =  m.page.forms[0]
  f.radiobutton_with(:value=>/1018/).click
  f.submit
  puts " -- 通報しますた"
}

通報ツールがもっと出回ればイイ。

違反業者が、ライバル業者を陥れるために、通報しまくって、通報合戦になればいいと思うんですよ。

クソ業者同士がお互いを通報しまくって、カテゴリが浄化されればいいと思う。

iPhone5/5s の発売後の転売厨は通報合戦だったので、たぶん業者同士を殴りあいさせたほうが、浄化が進むと思うんだよね。

違反見つけたら、すぐ通報

鬱陶しい連中を通報するしか私たちが取れるベストな手段だと思う。

通報していくのに疲れるので、自動する。

とくに、出品者の出品情報をみて通報がものすごく捗る、同様の商品を複数個出品してたり、工事だの送料だのでボッてる連中を一網打尽にする。

面倒くさい業者なら数日以内に消えてるよ。

tail -f でログ閲覧中に更新がわかるように画面をクリアしたい

tail -f してログをみるとき、ここまで見た!をやりたい

リクエストログを追いかけるときに、失敗リクエストを、ターミナルの表示から消したいことがある。

tail 側でなく、ターミナルアプリ側でやる。

f:id:takuya_1st:20160820010837p:plain:w200

クリアバッファする

tailf 側でなく、アプリ側でバッファクリアすることで、実現が可能。

参考資料

suzutan に教えてもらいましたありがとうございます!

http://askubuntu.com/questions/416304/is-there-a-way-to-clear-the-terminal-screen-during-a-running-process

2018-12-10

tail 更新 でマッチしなかったのでキーワード追加

マクドナルドのマクドwifi に自動接続

マクドWifiが導入されましたね。

マクドWifiは、Freeらしいです。ただし通信内容は監視されてるので注意。

ssid

00_MCD-FREE-WIFI

になります。

接続

メールアドレスで繋いだほうが無難。

接続スクリプト

    def mcd_login(user=nil, pass=nil)
      # マクドのWifiは適当なメアドでログインできた。
      # この仕様はいつまで継続するかわからないので注意が必要
      ## 'http://mcd.intplus-freewifi.com/mcd/oauth/contract.html?lang=jp' へリダイレクトさ
      #  れるときにJSPSessionIDが発行されるのでそれを使わないとログインできない

      m.get 'http://google.com'
      m.get "/mdj/en/welcome"
      m.get "/mdj/en/register"
      f = m.page.forms[0]
      f.submit 
      ## oauth できるが、メールのみがカンタンだった。
      f = m.page.form_with :action => /mail/
      f.field_with( :name => /mail/i).value = 'tekitou@gmail.com'
      f.submit 

ものすごく簡単。数分でログイン処理が掛ける。やさしいね!

愚痴

SSIDになんでもかんでも、00 つけりゃいいってもんじゃないだろうと思うんですけどね。

基本的にはOAuth経由。ようやく世の中がOauth経由になってきて嬉しい。

ただ、OAuthは個人情報を渡しすぎる。楽でいいんだけどね。

2017-05-07

コードの若干修正

モスバーガーの無線LANに自動ログイン。

MOS BURGER Free Wi-Fi

モスバーガーWifiサービスを始めました。

対応させました。

require 'mechanize'


   def mos_login( user, pass,force=false)
        m = Mechanize.new

        m.user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4"


        f = m.page.forms[0]
        f.field_with(:type=>/tex/i).value = user
        f.field_with(:type=>/pass/i).value = pass
        f.submit

        ## JS でリダイレクト処理1
        f = m.page.forms[0]
        f.submit
        ## JS でリダイレクト処理2
        f = m.page.forms[0]
        f.action = (URI.join(m.page.uri , 'reus-login!checkLogin.action')).to_s
        f.encoding = 'UTF-8' ## bad hack for avoiding mechanize bug
        f.submit
        ## 24時間経つと再度の規約同意が必要 で一からやり直し
        if m.page.body.to_s.toutf8 =~ /今すぐ利用/ then
          f = m.page.forms[0]
          f.checkbox_with(  :name => /agree/i ).check
          f.action = (URI.join(m.page.uri , 'renew-confirm!renewAdd.action')).to_s
          btn = f.button_with :value => /今すぐ利用/
          f.encoding = 'UTF-8' ## bad hack for avoiding mechanize bug
          m.submit( f , btn )

          mos_login(user,pass)

        end



    end

JSのリダイレクト処理だけど、Cookieもナニも発行しない。

ここは愚痴

こんな実装がまかり通っていいのか。

WISPのXMLは提示しないし、無線LANにAES鍵をつけなオープンだわ、HTTPでしかリダイレクトしない。OAuthするから、Twitterなどのの通信が全て素通り。もうね、こんなのインターネットじゃない。

というか、先週くらいから、数店舗で発生しているのですが、ログインしても通信が不可能のまま、ログインにリダイレクトされ続ける事象が頻繁に起きます。何度ログインしても通信が不可能ですね。

さすがソフトバンク

ソフトバンク系の公衆無線LANはいい加減、総務省から指導が入ってもいいと思う。

インターネットをなのるのをやめてほしい。。。。

追記 2016-08-21

モスバーガーソフトバンクのSB-Wifiを使っているのですが、コレが全くつながらない。

モスバーガーWiFiは、つながらないことのほうが多いと思ったほうが良い

ログインを何度試行しても、ログイン厨の表示を確認してもつながらないのだから、これは業が深いと。

ソフトバンク系列はまじ通信サービスを扱える会社じゃないわ。

/dev/tty.usbserial が作られなくなってた。SIPのせいSIPが全部悪い

/dev/tty.usbserial が消えた。

USB-Serial(rs232) のケーブルを使って、通信をしようとしたら、/dev/tty.usb* が見つからない。

システムレポートを見ると

このMacについて → システムレポート → USB を見てみると、ちゃんとデバイスは存在している。USBーSerialケーブルが存在する。

ドライバは存在スルのか?

/system/library/extensions

ここを見ると、該当ドライバ osx-pl2303.kext は確かに存在してる。存在するけど感知せず。

SIP が疑わしい。

SIPとは、System Integrity Protection のこと。kext を読んでくれない

Kernel ext が保護対象に含まれているか確認する。

takuya@~/Desktop$ cat /System/Library/Sandbox/rootless.conf | grep extensions
54:*                /System/Library/Extensions
55:                 /System/Library/Extensions/*

ああ、コレだわ。

SIP をオフにする

Command + R を押して再起動して、リカバリモードで起動する。ユーティリティからターミナルを選ぶ

csrutil disable

これで再起動すれば、SIPはオフになる。

SIP オフを確認

takuya@~/Desktop$ csrutil status
System Integrity Protection status: disabled.

disabled になったら OK

再起動して確認

ls /dev/tty.usbserial 
screen /dev/tty.usbserial 115200

うん、ちゃんと通信できたわ。安心した。

もうとっても不自由なOSX

野良ビルドのkext を一切追加できなくなったのはほんとに不便だと思う。

セキュリティ対策は理解できるが、自分で自分のコンピュータにドライバを突っ込むことが出来なくなった。 ドライバを開発する人も減るだろう。これは由々しき事態ですね。

ソースが公開されていたら、自分でビルドして自分の開発者IDで署名すればいいだろうけど、それも面倒くさい。

kext 無いと不便なのだが。。。どうするのがベターな解決方法なのでしょうか・・・

2016-03-11の段階では存在した。

3月ごろにルーターを弄くってた時には、間違いなく存在してました。いまは存在してない。では、その間に何が起きたか。

macOS X 起動時にShiftをした のキャッシュ削除・ディレクトリ修正の適用。

3月以降のOSXのアップデート時に外れたんだろう。

安いUSB-Serialが動かなくなったのは、困った。思わぬところで追加コストを払う羽目になった。

参考資料

http://rcmdnk.github.io/blog/2015/10/10/computer-mac/

正規表現で一番最後に出現したものを取得する

正規表現で最後に出てきたものを取りたい

たとえば、次のようなファイル名のファイルがあるとして、ここから、数字を取り出したい。

しかも欲しい数字は(2)の2が欲しい。

2016-08-07の売上報告のコピー(2).xls # 欲しい数字2、それも(2) の中の2が欲しい。

どうやるか。

通常であれば、数字にマッチさせ、マッチ個数を見て、最後を取り出せば、いいんだろうけど。

正規表現だけで実現できないかなと思いついたので調べみました。

正規表現で、最後にマッチしたものだけを取り出せないか、考えてみました。

考えた結果がコレ

/([\d]+)[^\d]+$/

実際の結果はこちら

'2016-08-07の売上報告のコピー(13).xls'.match /([\d]+)[^\d]+$/ #=> #<MatchData "13).xls" 1:"13">

ポイント

最初に、数字にマッチさせてみた。

>> '2016-08-07の売上報告のコピー(13).xls'.scan /([0-9]+)/
=> [["2016"], ["08"], ["07"], ["13"]]

数字にマッチさせてみると、それっぽいのが取れるんで。

つぎに、数字+数字以外+行末にマッチさせる。

>> '2016-08-07の売上報告のコピー(13).xls'.scan /([\d]+)[^\d]*$/
=> [["13"]]

これでいける。つまり、ココから言えることは、

最後の出現を取りたいときは、欲しい文字+欲しい文字以外+末尾とすればいいとわかる。

最後の出現=(欲しい文字+欲しい文字以外)の組み合わせ。

英単語(小文字)で一番最後に出現したものを取る場合

>> 'iPhone は iOS を使った Apple の製品です。'.scan /([A-Za-z]+)[^A-Za-z]*$/
=> [["Apple"]]

N番目の出現は、流石にしんどいけど、何個か組み合わせば、実現できそう

最後から二つ目を取るには

>> 'iPhone は iOS を使った Apple の製品です。'.scan /([A-Za-z]+)[^A-Za-z]*[A-Za-z]*[^A-Za-z]*$/
=> [["iOS"]]
>>

正規表現(欲しい文字)+欲しい文字以+外欲しい文字+欲しい文字以外 と組み合わせで実現できそうですね。

先頭から2番めに出てきた数字列にマッチさせる。

この方法を考えていけば、先頭から2回めに出てきた文字列にマッチさせることが出来ると思いました。

>> '2016-08-07の売上報告のコピー(13).xls'.scan /^[^\d]*[\d]+[^\d]*([\d]+)/
=> [["08"]]
>> '2016-08-07の売上報告のコピー(13).xls'.scan /^[^\d]*[\d]+[^\d]*[\d]+[^\d]*([\d]+)/
=> [["07"]]

あとはコレを複数個組み合わせれば、先頭からN番目も一回の正規表現で取り出せそうですね。

まぁ実際には、マッチ配列から取出すことのほうが多いんですけど。

find でカレントディレクトリの ” . " ドットを対象外にするには

find コマンドで一括処理をすると困るのが ドット

find コマンドで、ディレクトリを列挙すると、カレントディレクトリも表示される。

takuya@:~/letsencrypt$ find -type d | head
. #←これ邪魔。
./letshelp-letsencrypt
./letshelp-letsencrypt/docs
./letshelp-letsencrypt/docs/_static
./letshelp-letsencrypt/docs/_templates
./letshelp-letsencrypt/docs/api
./letshelp-letsencrypt/letshelp_letsencrypt
./letshelp-letsencrypt/letshelp_letsencrypt/testdata
./letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-available
./letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-enabled

一括処理するときに、だいたい不要なのがカレントディレクト

カレントディレクトリって、ほとんど場合、不要じゃないですか?違います?私は不要なことが多いです。

カレントディレクトリを消す

find -mindepth 1 

カレントディレクトリは深さ(depth) が 0なので、 mindepth =1とすれば、消すことが出来ます。

もうデフォルトでいいじゃんコレ。

私は、find のデフォルトをmindepth 1 にすることにしました。

ちなみに、find は引数を最初に取るので、alias 出来ないから、function を作ってalias 化のかわりとします。

function find { $( which find ) "$@" -mindepth 1 -not -iwholename '*/.git/*'  ; }

.git も使わないのでデフォルト無視としてる

参考資料

http://stackoverflow.com/questions/13525004/how-to-exclude-this-current-dot-folder-from-find-type-d

find コマンドで指定条件を除外(exclude)して、特定の拡張子を対象から消すには

find コマンドで指定条件のファイルを<除外>したい時

ずっとgrep 使ってたんですけど、find だけで出来ないのかなと気になって調べました。

-not -name pattern のように出来る

除外するときは、-not を最初につけてから上限を書けばいいことがわかった。

次の例は、拡張子 jpg を除外する場合

 find -type f -not -name '*jpg'

条件として記述するので name 以外にもファイル名やパーミッションなど find コマンドのオプションと -notを組み合わせて、◯◯以外の条件にマッチしたものを除く、といったファイルの検索できることがわかる。

複数条件書いたらどうなるのか

-not があれば、気になるのが複数条件

 find -type f -not -name '*jpg' -not -name '*.png'

このように、複数続けて書いたら、( not A and not B ) になる。つまり、jpg ファイルと png ファイルを除外できる。ふむ。-not による条件の重ねがけは絞り込みじゃなく、not の 和集合になるようです

結論:除外したい条件を書くときは

-not つけて並べておけば大丈夫!

コレが一番わかり易いですよね。

もっと細い not expr not expr の話。

カンタンな結論だと、not つけて並べておけば大丈夫ってことなんですが否定入ると、論理の世界なので、and/or をちゃんと意識しておいたほうがイイ感じた。 なので、and/or をちゃんと把握するためにももう少し調べてみる。

気になったので man を調べました。

条件に関する man の記述は以下のとおり

       -not expr
              Same as ! expr, but not POSIX compliant.

       expr1 expr2
              Two expressions in a row are taken to be joined with an implied "and"; expr2 is not evaluated if expr1 is false.

       expr1 -a expr2
              Same as expr1 expr2.

       expr1 -and expr2
              Same as expr1 expr2, but not POSIX compliant.

       expr1 -o expr2
              Or; expr2 is not evaluated if expr1 is true.

       expr1 -or expr2
              Same as expr1 -o expr2, but not POSIX compliant.

       expr1 , expr2
              List; both expr1 and expr2 are always evaluated.  The value of expr1 is discarded; the value of the list is the value of expr2. The comma operator can be useful for searching for several different types of thing, but  travers‐
              ing the filesystem hierarchy only once.  The -fprintf action can be used to list the various matched items into several different output files.

条件を並べて書くのは 暗黙の and

expr1 expr2 は expr1 -and expr2 と同じ

このように、条件を並べて書くのは、暗黙のand として扱われる。したがってexpr1 が false のときは、expr2 は評価されない。

つまり、expr1 が評価され合致したら、expr2 が評価される。 更に言うと、expr1 で絞り込んだ後、expr2 で更に絞り込まれる。

-not expr は普通の expr の扱いになる

-not expr は not の付いた1つのexpr と解釈される

-not -name '*.txt' -not -name '*.php'

このように書いた場合、-not -name '*.txt' and -not -name '*.php' になる 。

つまり、このように解釈する。

  1. not expr1 が評価され合致したら、not で否定され、not expr2 は評価されない。
  2. not expr1 が評価され不一致なら、not で否定され、not expr2 が評価される。

表にするとこんな感じ

not expr1 expr1 not expr2
false マッチする evalされない
true マッチしない eval される

具体例 find -not -name '*.jpg' -not -name '*.png' の場合

具体例を書くとこんな感じ。

ファイル名 -name '*.jpg' -not -name '*.jpg' -name '*.png' -not -name '*.png' 結果
hoge.jpg true false skip skip false / 出力されない
hoge.png false true true false false / 出力されない
hoge.txt false true false true true / 結果に出る

このようになる。

find 奥が深い

find って単純に、条件を列挙してたけど、実は and 結合になっていて not と or も使えるんですね。奥が深い、でもここまで使い込むかなぁ。複数回コマンド実行して結果を結合しちゃうよね・・・

find を使っていくうえで憶えることは、たくさんですね。

条件に関することも、exec に関すること、 POSIX互換部分、GNU追加機能、日付、パーミッション....etc findには知っておくと便利なことがたくさん。 たかが find されど find 、これらをぱぱっと使いこなすのは、熟練の技みたいになってますねぇ。。

関連資料

find コマンド関連の他の記事はこちら

参考資料

man find