Я использую zsh и хочу использовать написанную мной функцию для замены cd. Эта функция дает вам возможность перейти в родительский каталог:
$ pwd
/a/b/c/d
$ cl b
$ pwd
/a/b
Вы также можете перейти в подкаталог родительского каталога:
$ pwd
/a/b/c/d
$ cl b/e
$ pwd
/a/b/e
Если первая часть пути не является родительским каталогом, он будет работать как обычный cd. Я надеюсь, что в этом есть смысл.
Таким образом, находясь в /a/b/c/d, я хочу иметь возможность перемещаться в /a, /a/b, /a/b/c, все подкаталоги /a/b/c/d и любые абсолютный путь, начинающийся с /, ~/ или ../ (или ./). Я надеюсь, что в этом есть смысл.
Это функция, которую я написал:
cl () {
local first=$( echo $1 | cut -d/ -f1 )
if [ $# -eq 0 ]; then
# cl without any arguments moves back to the previous directory
cd - > /dev/null
elif [ -d $first ]; then
# If the first argument is an existing normal directory, move there
cd $1
else
# Otherwise, move to a parent directory
cd ${PWD%/$first/*}/$1
fi
}
Вероятно, есть лучший способ (советы приветствуются), но у меня пока не было с этим проблем.
Теперь я хочу добавить автозаполнение. Это то, что у меня есть до сих пор:
_cl() {
pth=${words[2]}
opts=""
new=${pth##*/}
[[ "$pth" != *"/"*"/"* ]] && middle="" || middle="${${pth%/*}#*/}/"
if [[ "$pth" != *"/"* ]]; then
# If this is the start of the path
# In this case we should also show the parent directories
opts+=" "
first=""
d="${${PWD#/}%/*}/"
opts+="${d//\/// }"
dir=$PWD
else
first=${pth%%/*}
if [[ "$first" == "" ]]; then
# path starts with "/"
dir="/$middle"
elif [[ "$first" == "~" ]]; then
# path starts with "~/"
dir="$HOME/$middle"
elif [ -d $first ]; then
# path starts with a directory in the current directory
dir="$PWD/$first/$middle"
else
# path starts with parent directory
dir=${PWD%/$first/*}/$first/$middle
fi
first=$first/
fi
# List al sub directories of the $dir directory
if [ -d "$dir" ]; then
for d in $(ls -a $dir); do
if [ -d $dir/$d ] && [[ "$d" != "." ]] && [[ "$d" != ".." ]]; then
opts+="$first$middle$d/ "
fi
done
fi
_multi_parts / "(${opts})"
return 0
}
compdef _cl cl
Опять же, возможно, это не лучший способ сделать это, но он работает... отчасти.
Одна из проблем заключается в том, что когда я набираю cl ~/, он заменяет его на cl ~/ и не предлагает никаких каталогов в моей домашней папке. Есть ли способ заставить это работать?
ИЗМЕНИТЬ
cl () {
local first=$( echo $1 | cut -d/ -f1 )
if [ $# -eq 0 ]; then
# cl without any arguments moves back to the previous directory
local pwd_bu=$PWD
[[ $(dirs) == "~" ]] && return 1
while [[ $PWD == $pwd_bu ]]; do
popd >/dev/null
done
local pwd_nw=$PWD
[[ $(dirs) != "~" ]] && popd >/dev/null
pushd $pwd_bu >/dev/null
pushd $pwd_nw >/dev/null
elif [ -d $first ]; then
pushd $1 >/dev/null # If the first argument is an existing normal directory, move there
else
pushd ${PWD%/$first/*}/$1 >/dev/null # Otherwise, move to a parent directory or a child of that parent directory
fi
}
_cl() {
_cd
pth=${words[2]}
opts=""
new=${pth##*/}
local expl
# Generate the visual formatting and store it in `$expl`
_description -V ancestor-directories expl 'ancestor directories'
[[ "$pth" != *"/"*"/"* ]] && middle="" || middle="${${pth%/*}#*/}/"
if [[ "$pth" != *"/"* ]]; then
# If this is the start of the path
# In this case we should also show the parent directories
local ancestor=$PWD:h
while (( $#ancestor > 1 )); do
# -f: Treat this as a file (incl. dirs), so you get proper highlighting.
# -Q: Don't quote (escape) any of the characters.
# -W: Specify the parent of the dir we're adding.
# ${ancestor:h}: The parent ("head") of $ancestor.
# ${ancestor:t}: The short name ("tail") of $ancestor.
compadd "$expl[@]" -fQ -W "${ancestor:h}/" - "${ancestor:t}"
# Move on to the next parent.
ancestor=$ancestor:h
done
else
# $first is the first part of the path the user typed in.
# it it is part of the current direoctory, we know the user is trying to go back to a directory
first=${pth%%/*}
# $middle is the rest of the provided path
if [ ! -d $first ]; then
# path starts with parent directory
dir=${PWD%/$first/*}/$first
first=$first/
# List all sub directories of the $dir/$middle directory
if [ -d "$dir/$middle" ]; then
for d in $(ls -a $dir/$middle); do
if [ -d $dir/$middle/$d ] && [[ "$d" != "." ]] && [[ "$d" != ".." ]]; then
compadd "$expl[@]" -fQ -W $dir/ - $first$middle$d
fi
done
fi
fi
fi
}
compdef _cl cl
Это то, что я получил самостоятельно. Это работает (вроде как), но есть пара проблем:
- При возврате в родительский каталог завершение в основном работает. Но когда вы переходите к дочернему каталогу paretn, предложения неверны (они отображают введенный вами полный путь, а не только дочерний каталог). Результат действительно работает
- Я использую подсветку синтаксиса, но путь, который я набираю, просто белый (при использовании перехода к родительскому каталогу обычные функции cd окрашены)
- В моем zshrc у меня есть строка:
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}' '+l:|=* r:|=*'
С компакт-диском это означает, что я могу набрать load, и он завершится загрузкой. С cl это не работает. Не событие при использовании обычной функциональности компакт-диска.
Есть ли способ исправить (некоторые из этих) проблем? Надеюсь, вы понимаете мои вопросы. Мне трудно объяснить проблему.
Спасибо за вашу помощь!