Skip to content

Instantly share code, notes, and snippets.

@pasela
Last active May 10, 2016 11:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pasela/9643be6205d2d00cd874c956b4490595 to your computer and use it in GitHub Desktop.
Save pasela/9643be6205d2d00cd874c956b4490595 to your computer and use it in GitHub Desktop.
[shell] interactive file selection and interactive change directory
# specify your favorite filtering tool
if [[ -z "$INTERACTIVE_SELECTOR" ]]; then
if type fzy >/dev/null 2>&1; then
export INTERACTIVE_SELECTOR=fzy
elif type peco >/dev/null 2>&1; then
export INTERACTIVE_SELECTOR=peco
elif type fzf >/dev/null 2>&1; then
export INTERACTIVE_SELECTOR=fzf
fi
fi
alias isel=interactive-select
function icd()
{
cd "$(interactive-select -dh "$@")"
}
#!/bin/bash
#
# select file and/or directory interactively using $INTERACTIVE_SELECTOR.
#
# USAGE:
# interactive-select [-dfhH] [BASE_DIR]
#
# OPTIONS:
# -d directory only
# -f file only
# -h hide dot-files
# -H include dot-files
#
# ARGS
# BASE_DIR base directory (default: .)
#
# ENV
# INTERACTIVE_SELECTOR interactive filtering tool such as peco, fzf, fzy...
if [[ -z "$INTERACTIVE_SELECTOR" ]]; then
echo "INTERACTIVE_SELECTOR not defined" >&2
exit 1
fi
while getopts dfhH OPT
do
case $OPT in
d) DIR_ONLY=1 ;;
f) FILE_ONLY=1 ;;
h) HIDE_DOT_FILES=1 ;;
H) HIDE_DOT_FILES= ;;
esac
done
shift $((OPTIND - 1))
base=${1:-.}
FIND_OPT=(-print)
if [[ -n "$DIR_ONLY" && -n "$FILE_ONLY" ]]; then
: # do nothing
elif [[ -n "$DIR_ONLY" ]]; then
FIND_OPT=(-type d "${FIND_OPT[@]}")
elif [[ -n "$FILE_ONLY" ]]; then
FIND_OPT=(-type f "${FIND_OPT[@]}")
fi
if [[ -n "$HIDE_DOT_FILES" ]]; then
FIND_OPT=(-type d -name . -or -name ".*" -prune -or "${FIND_OPT[@]}")
fi
selected=$(cd "$base" && find . "${FIND_OPT[@]}" | \
sed -e 's#^./##' | $INTERACTIVE_SELECTOR) && echo "$base/$selected"
@pasela
Copy link
Author

pasela commented May 10, 2016

小難しいことしている理由

  • 指定パスが含まれると絞り込みの時にうざい(下記参照)
  • .gitとか邪魔(-hで除外、-Hで含める(-h上書き用))
  • ファイルだけ(-f)、ディレクトリだけ(-d)とか簡単に指定したい

findが複雑になった過程

普通にfindすると指定したパスも付いた状態で出力されるので絞り込むときに余計にマッチしたりして邪魔になる。

$ find /usr/local/lib -type d -print
/usr/local/lib
/usr/local/lib/zsh
/usr/local/lib/guilt
/usr/local/lib/pkgconfig
/usr/local/lib/java

-printf%Pを使うとちょうど指定したパス以下のみ出力してくれる……が、-printfはGNU版にしかなかった。

$ find /usr/local/lib -type d -printf "%P\n"

zsh
guilt
pkgconfig
java

仕方ないのでsedで先頭だけ削ろうとしたら~/fooやシンボリックリンクの時にうまくいかなかったのでcdしてからfind .するようにした。

$ cd /usr/local/lib && find . -name ".*" -prune -or -type d -print

そうしたら、ドットファイルを除外する-name ".*" -pruneがカレントの.に引っかかるせいなのか何も出てこなくなった。
なので先頭に-name . -orを入れることになった。

$ cd /usr/local/lib && find . -name . -or -name ".*" -prune -or -type d -print
./zsh
./guilt
./pkgconfig
./java

で、./sedで削って…という感じでこんなに長くなった。

$ cd /usr/local/lib && find . -name . -or -name ".*" -prune -or -type d -print | sed -e 's#^./##'
zsh
guilt
pkgconfig
java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment