#!/bin/sh cat << 'EEE' > /dev/null /* fnf .... find file, find parser * Copyright (C) 2017-2019 Momi-g * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ EEE # cmd='-h bool 0 # -r int -1 # -o bool 0' # buf=`prsopt "$cmd"` # eval "$buf" # if [ $? -ne 0 ] ; then echo "$0: optErr. $OPTARG" >/dev/stderr ; exit 1; fi # func_rdopt() { ( rdopt_fmt="$1" # ":ab:c" etc. format. shift #ck quiet mode rdopt_quiet=1 rdopt_buf=${rdopt_fmt%%[!:]*} if [ "$rdopt_buf" != ':' ] ; then rdopt_quiet=0 rdopt_fmt=':'"$rdopt_fmt" fi #--inicialize make list opt_a='', ... #ab:c -> 'a' \n 'b' \n 'c' \n rdopt_buf='s/://g s/./&\ /g' rdopt_zerolist=`echo "$rdopt_fmt" | sed -e "$rdopt_buf" | sed -e " /^$/d s/^/opt_/g s/$/=''/g " ` #--- local vars # rdopt_fmt="$1" # ":ab:c" opt format # rdopt_quiet=1 # rdopt_zerolist=`echo "$rdopt_fmt" | sed -e "$rdopt_buf" | sed -e " # rdopt_buf='s/://g rdopt_out="" rdopt_opt="" rdopt_skip="" rdopt_err="" rdopt_safe=" s#'#'\"'\"'#g 1 s/^/'/g $ s/$/'/g" #---getopts loop. break if all args read or detect '--' while : do if [ 0 -eq "$#" ] || [ "$rdopt_err" = "1" ] ; then break fi if [ "$1" = "--" ] ; then shift ; break fi getopts "$rdopt_fmt" rdopt_opt "$@" # ":a:bc:f:" etc... # err detect@silent mode # $?=1 ... detect optend or '--'. '--' is removed at previous line. # ':' ... detect option, but dont have subargs (OPTARG="factor"). # '?' ... detect unsupported option char (OPTARG="factor") or args end (OPTARG="blank"). # OPTARG ... "" is optend. "a/b/c..." is invalid option # OPTIND ... if err, OPTIND indicates next (new) arg pos. if [ "$?" = "1" ] ; then # normal end (detect normal args). save general args. shift $((OPTIND - 1)) if [ "$#" -eq "0" ] ; then break fi # new set make rdopt_buf=`printf '%s' "$1" | sed -e "$rdopt_safe"` rdopt_skip="$rdopt_skip $rdopt_buf" shift OPTIND=1 continue fi if [ "$rdopt_opt" = "?" ] || [ "$rdopt_opt" = ":" ] ; then # detect invalid opt rdopt_err=1 #OPTARG has err option char. continue fi # normal. detect option. if [ "$OPTARG" = "" ] ; then # no subargs rdopt_out="$rdopt_out opt_$rdopt_opt"'=1' else # exist subargs rdopt_buf=`printf "%s\n" "$OPTARG" | sed -e "$rdopt_safe" ` rdopt_out="$rdopt_out opt_$rdopt_opt"'='"$rdopt_buf" fi done # exit parse if [ "$rdopt_err" = "1" ] ; then if [ "$rdopt_quiet" = "0" ] ; then rdopt_buf=${0##*/} echo "$rdopt_buf: invalid option. sleep. ( $OPTARG )" >/dev/stderr while : do sleep 1000 done else rdopt_out="OPTARG=$OPTARG OPTIND=1 test 1 = 0" fi else # '--' end if [ "$#" != "0" ] ; then rdopt_buf=`cat << 'EEE' for ii do printf '%s' "$ii" | sed -e " s#'#'\"'\"'#g 1 s/^/'/g $ s/$/' /g" done EEE ` rdopt_buf=`eval "$rdopt_buf"` rdopt_skip="$rdopt_skip $rdopt_buf" fi rdopt_out="OPTIND=1 $rdopt_zerolist $rdopt_out set -- $rdopt_skip " fi printf '%s\n' "$rdopt_out" # clean OPTIND=1 rdopt_fmt="" rdopt_quiet="" rdopt_zerolist="" rdopt_buf="" rdopt_out="" rdopt_opt="" rdopt_skip="" rdopt_err="" rdopt_safe="" ) OPTIND=1 } buf=`func_rdopt "hr:oP" "$@"` eval "$buf" if [ $? -ne 0 ] ; then echo "$0: optErr. $OPTARG" >/dev/stderr ; exit 1; fi if [ "$opt_h" = '1' ] ; then cat << 'EEE' HowTo (find file/directry) option: -h(elp), -r(ecurse -1(dfl),0,1,2..), -o(ctal output) ------ ex.) ~$ fnf txt unko (-r -1) #search files from ./ >>> ./unko___uh.txt ./unko.txt ./unkotxt/ ...show include 'txt' + 'unko' (fixed)string in filepath (not file name) ...recursive search (-1:non stop 0:pwd 1:pwd+next) ...directry(or directry link) is end with '/'. ex.) ~$ fnf '/u' unko //uh >>> ./unko.txt >>> '//' means 'not'. '/u' && 'unko' && !'uh' ... normal filepath probably should not contain '//'. (./aaa//bbb/c.txt etc) ex.) ~$ fnf txt unko -o >>> \001\004\123... >>> printf '\001\004\123...' -> ./unko___uh.txt \111\333... ... output in octal number. You can safely handle filenames containing newlines and unicode characters. ("./aaa/bb\n\n\123 b/ccc.txt" etc) EEE exit 0 fi # -Pのデフォで普通にmaxつけて検索。type lでシンボリックリンクを探し出して頭に//0みたいな # フラグをつけて再検索させるとか。maxdepthが使えねー。微妙だ。第一階層、第二階層って感じに # 検索させてスラッシュカウントpurneでぶっちぎるとか。loop対策がどーよ? # findはパターンがシェルパターンなので、そのへんも。-pathでパス健作になる # find . -path ./src/emacs -prune -o -print # findは確固,-o -aと否定のみ規程。 # find . -path '*/*/*' -prune find -print0 --version >/dev/null 2>&1 if [ "$?" = "0" ] && [ "$opt_P" != "1" ]; then echo "// (stderr info) detect gnu-find. use fastmode." >/dev/stderr gnumode=1 fi if [ "$opt_r" != "-1" ] && [ "$opt_r" != "" ] ; then ptn='*/*/*' #>> pwdは/一つだけ。//はサブディレクトリ。///はサブサブ。 for ii in `seq 1 $opt_r ` do ptn="$ptn"'/*' done else ptn='*//*' # 基本的に//を含むファイルは存在し得ない。ファイル名に\000と/は使えないため。 # null_hitで枝刈りを防ぐ。 fi # maxdepthがfindで使えないので代替。posix. outstr='cat -' if [ "$opt_o" != "1" ] ; then outstr='while read -r aa do printf "$aa" echo done' fi # syscallオーバーヘッドが重いけど、awkとかeval使えないし。%bもシェルだけだし。 # printf自体はbuilt-inだから、致命的に遅い # ってほどじゃない。awkに比べたら500%プラスってとこ。 awk 10ms -> while print 50ms # 20ms # 固定文字検索のみに絞る。検索文字も生だと扱いにくいので8進にしとく。 buf=`for ii in "$@" do printf '%s\000' "$ii" done | od -An -to1 -w16 -v | tr -d '\n' | sed -e 's# 000#@#g' | tr ' ' 'o' | tr '@' '\n' ` filter="" for ii in $buf do buf=${ii#o057o057} # //bbb みたいな'//'付き文字列は否定。not. if [ "$buf" != "$ii" ] ; then filter="$filter /$buf/ d" else filter="$filter /$buf/ ! d" fi done filter="sed -e '$filter'" # echo "$filter" # exit # sed -e ' # /o141o141/ ! d # /o163o163/ ! d # /o144o144/ d' # sed -e '' # ---main # -Lはtypeでフォルダ系の表示の尻尾に/を判別するとき、dirリンク時に情報が欲しいので。 # . と./で混在してる。変。lsは--indiで/指定できるけど、findにはない。 # -L ./ と -L .で違う。バグってほどじゃないけど注意が必要。 # echo "$ptn" # exit # -execが遅いみたい。20ms -> 800ms # 20ms time find -L ./ -path "@@" -prune -o -print # 700ms time find -L ./ -path "@@" -prune -o -exec printf '%s\n' '{}' ';' # syscallが遅いから//を使って何とかできんか。...無理。出力する方法がない。 # execでまとめられてしまってる。gnuとスイッチかな。 # gnu can use -printf ... fast output(40ms) if [ "$gnumode" = "1" ] ; then cmd=`cat << 'EEE' find -L ./ -path "$ptn" -prune \ -o -type d -a '!' -path '*/' -a -printf '%p/\000' \ -o -printf '%p\000' EEE ` else # posix & syscall. slow. 800ms cmd=`cat << 'EEE' find -L ./ -path "$ptn" -prune \ -o -type d -a '!' -path '*/' -a -exec printf '%s/\000' '{}' ';' \ -o -exec printf '%s\000' '{}' ';' EEE ` fi eval "$cmd" | od -An -to1 -w16 -v | sed -e 's# 000#@#g' | tr -d '\n' | tr ' ' 'o' | tr '@' '\n' | eval "$filter" | tr 'o' '\134' | eval "$outstr" # . パスから探し出した一つ一つのファイルに大して # pathnameがptnに一致したら握りつぶす。その先のフォルダも見なかったことにされる。(prune) # -o or. 握りつぶされなかった生き残りに対して # -o type d 対象ファイル名がディレクトリかつ(-a) path名が/で終わってない(!...否定) # のならば、{}/\000を出力。返り値はここでtrueなので後ろの評価はスキップされる。 # -o 上が失敗、つまりディレクトリとかじゃないのならば{}\000を出力。 # -oはelseに近い。 # # 等価コード # loop: # # mode=read_linkend_data(); (-L) # obj=search(dir); # # if (pathname == $ptn ){ ptnはシェルパターン. */とか??123*/とか。 # next # } else if (obj==dir && pathname != '*/') { # printf obj + '/' + '\000' # next # } else { # printf obj + '\000' # next # } # loopend #