从 .bashrc 或 .zshrc 修改现有函数
modify an existing function from .bashrc or .zshrc
/usr/share/zsh/functions/Completion/Unix/_git
中定义了一个函数
(( $+functions[_git-diff] )) ||
_git-diff () {
local curcontext=$curcontext state line ret=1
declare -A opt_args
local -a diff_options diff_stage_options
__git_setup_diff_options
__git_setup_diff_stage_options
_arguments -C -s $endopt \
$* \
$diff_options \
'(--exit-code)--quiet[disable all output]' \
$diff_stage_options \
'(--cached --staged)--no-index[show diff between two paths on the filesystem]' \
'(--cached --staged --no-index)'{--cached,--staged}'[show diff between index and named commit]' \
'(-)--[start file arguments]' \
'*:: :->from-to-file' && ret=0
case $state in
(from-to-file)
# If "--" is part of $opt_args, this means it was specified before any
# $words arguments. This means that no heads are specified in front, so
# we need to complete *changed* files only.
if [[ -n ${opt_args[(I)--]} ]]; then
if [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
__git_changed-in-index_files && ret=0
else
__git_changed-in-working-tree_files && ret=0
fi
return ret
fi
# If "--no-index" was given, only file paths need to be completed.
if [[ -n ${opt_args[(I)--no-index]} ]]; then
_alternative 'files::_files' && ret=0
return ret
fi
# Otherwise, more complex conditions need to be checked.
case $CURRENT in
(1)
local files_alt='files::__git_changed-in-working-tree_files'
if [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
files_alt='files::__git_changed-in-index_files'
fi
_alternative \
'commit-ranges::__git_commit_ranges' \
'blobs-and-trees-in-treeish::__git_blobs_and_trees_in_treeish' \
$files_alt \
'blobs::__git_blobs ' && ret=0
;;
(2)
# Check if first argument is something special. In case of committish ranges and committishs offer a full list compatible completions.
if __git_is_committish_range $line[1]; then
# Example: git diff branch1..branch2 <tab>
__git_tree_files ${PREFIX:-.} $(__git_committish_range_last $line[1]) && ret=0
elif __git_is_committish $line[1] || __git_is_treeish $line[1]; then
# Example: git diff branch1 <tab>
_alternative \
'commits::__git_commits' \
'blobs-and-trees-in-treeish::__git_blobs_and_trees_in_treeish' \
'files::__git_tree_files ${PREFIX:-.} HEAD' && ret=0
elif __git_is_blob $line[1]; then
_alternative \
'files::__git_cached_files' \
'blobs::__git_blobs' && ret=0
elif [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
# Example: git diff --cached file1 <tab>
__git_changed-in-index_files && ret=0
else
# Example: git diff file1 <tab>
__git_changed-in-working-tree_files && ret=0
fi
;;
(*)
if __git_is_committish_range $line[1]; then
# Example: git diff branch1..branch2 file1 <tab>
__git_tree_files ${PREFIX:-.} $(__git_committish_range_last $line[1]) && ret=0
elif { __git_is_committish $line[1] && __git_is_committish $line[2] } ||
__git_is_treeish $line[2]; then
# Example: git diff branch1 branch2 <tab>
__git_tree_files ${PREFIX:-.} $line[2] && ret=0
elif __git_is_committish $line[1] || __git_is_treeish $line[1]; then
# Example: git diff branch file1 <tab>
# Example: git diff branch -- f<tab>
__git_tree_files ${PREFIX:-.} HEAD && ret=0
elif __git_is_blob $line[1] && __git_is_blob $line[2]; then
_nothing
elif [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
# Example: git diff --cached file1 file2 <tab>
__git_changed-in-index_files && ret=0
else
# Example: git diff file1 file2 <tab>
__git_changed-in-working-tree_files && ret=0
fi
;;
esac
;;
esac
return ret
}
我只需要追加
'files::__git_changed_files ${PREFIX:-.} HEAD' \
以上
'files::__git_tree_files ${PREFIX:-.} HEAD' && ret=0
目前我已将新函数复制粘贴到我的 .zshrc
中,它正在运行。
但是,我认为有一种更简洁的方法可以使用 .bashrc
或 .zshrc
覆盖现有函数,如下所示:
_git-diff 2>/dev/null
functions[_git-diff-orig]=$functions[_git-diff]
_git-diff() {
_git-diff-orig "$@"
...
}
但我不知道该怎么做。谁能帮忙。明确一点,我想使用 .bashrc
或 .zshrc
覆盖该函数,以便它更便携。
在zsh
中,functions
关联数组中的值为规范化代码文本。这意味着您可以使用任何常用的文本操作方法来修改它:
foo() {
echo before
echo and after
}
foo
# => before
# => and after
functions[foo]=${functions[foo]/echo before/echo before;echo during}
foo
# => before
# => during
# => and after
functions foo
# => foo () {
# => echo before
# => echo during
# => echo and after
# => }
有趣的是,zsh 将解析、验证和规范化分配给 functions
数组的代码——这与正常声明函数本质上是相同的过程。这就是为什么 functions foo
的输出有一个换行符,即使字符串替换使用了分号。
将这些行添加到 ~/.zshrc
应该适用于您的示例:
current="'files::__git_tree_files ${PREFIX:-.} HEAD'"
replacement="'files::__git_changed_files ${PREFIX:-.} HEAD' $current"
functions[_git-diff]=${functions[_git-diff]/$current/$replacement}
修改函数的其他方法
有多种方法可以更改 shell 语言的文本,其中一些
对于更复杂的功能更改可能会更好。注意更多变化
会使过程更加脆弱,因为对基本代码的更新可能会失败
修补程序。
另请注意,更改已应用于存储在
functions
数组;可能与原始源文件中的内容不匹配。
使用patch
显而易见的选择,因为我们正在修补代码。 patch
实用程序修改
基于 diff
输出的文本(通常是 diff -u
)。该实用程序可以处理
一些空格差异,以及一些错误,例如不正确的行号:
plan() {
forecast=
if [[ $forecast == sun ]]; then
print "take sunscreen"
print "wear hat"
elif [[ $forecast == rain ]]; then
print "take umbrella"
print "carry raincoat"
else
print "stay home"
fi
}
plan sun
#=> take sunscreen
#=> wear hat
plan hail
#=> stay home
patchDiff='
--- plan1
+++ plan2
@@ -4,3 +4,5 @@
print "take sunscreen"
+ print "apply sunscreen"
print "wear hat"
+ print "use sunglasses"
elif [[ $forecast == rain ]]
@@ -11,2 +13,3 @@
print "stay home"
+ print "and relax"
fi'
functions[plan]=$(print -- $patchDiff \
| patch -ls -o >(cat) =(print -- $functions[plan]))
plan sun
#=> take sunscreen
#=> apply sunscreen
#=> wear hat
#=> use sunglasses
plan hail
#=> stay home
#=> and relax
使用sed
使用 sed
流编辑器,并在前面的示例的基础上构建:
sedscript='s/print *\(.*\)/print ${(C):-}/'
functions[plan]=$(print $functions[plan] | sed -e $sedscript)
functions plan
#=> plan () {
#=> forecast=
#=> if [[ $forecast == sun ]]
#=> then
#=> print ${(C):-"take sunscreen"}
#=> print ${(C):-"apply sunscreen"}
#=> print ${(C):-"wear hat"}
#=> print ${(C):-"use sunglasses"}
#=> elif [[ $forecast == rain ]]
#=> then
#=> print ${(C):-"take umbrella"}
#=> print ${(C):-"carry raincoat"}
#=> else
#=> print ${(C):-"stay home"}
#=> print ${(C):-"and relax"}
#=> fi
#=> }
plan rain
#=> Take Umbrella
#=> Carry Raincoat
按行
这不需要匹配函数中的任何特定内容。两者都是
好的和坏的 - 它很容易实现,但它可以打破甚至简单
基本功能的更改:
# split the code into an array of lines (f)
lines=("${(f)functions[plan]}")
# add code in the middle
newCode=(
${lines[1]}
'if [[ $forecast == tornado ]]; then
print "find shelter"
print "go quickly"
return
fi'
${lines[2,-1]})
# rejoin array with newlines (F)
functions[plan]=${(F)newCode}
functions plan
#=> plan () {
#=> forecast=
#=> if [[ $forecast == tornado ]]
#=> then
#=> print "find shelter"
#=> print "go quickly"
#=> return
#=> fi
#=> if [[ $forecast == sun ]]
#=> then
#=> print ${(C):-"take sunscreen"}
#=> print ${(C):-"apply sunscreen"}
#=> print ${(C):-"wear hat"}
#=> print ${(C):-"use sunglasses"}
#=> elif [[ $forecast == rain ]]
#=> then
#=> print ${(C):-"take umbrella"}
#=> print ${(C):-"carry raincoat"}
#=> else
#=> print ${(C):-"stay home"}
#=> print ${(C):-"and relax"}
#=> fi
#=> }
plan tornado
#=> find shelter
#=> go quickly
/usr/share/zsh/functions/Completion/Unix/_git
(( $+functions[_git-diff] )) ||
_git-diff () {
local curcontext=$curcontext state line ret=1
declare -A opt_args
local -a diff_options diff_stage_options
__git_setup_diff_options
__git_setup_diff_stage_options
_arguments -C -s $endopt \
$* \
$diff_options \
'(--exit-code)--quiet[disable all output]' \
$diff_stage_options \
'(--cached --staged)--no-index[show diff between two paths on the filesystem]' \
'(--cached --staged --no-index)'{--cached,--staged}'[show diff between index and named commit]' \
'(-)--[start file arguments]' \
'*:: :->from-to-file' && ret=0
case $state in
(from-to-file)
# If "--" is part of $opt_args, this means it was specified before any
# $words arguments. This means that no heads are specified in front, so
# we need to complete *changed* files only.
if [[ -n ${opt_args[(I)--]} ]]; then
if [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
__git_changed-in-index_files && ret=0
else
__git_changed-in-working-tree_files && ret=0
fi
return ret
fi
# If "--no-index" was given, only file paths need to be completed.
if [[ -n ${opt_args[(I)--no-index]} ]]; then
_alternative 'files::_files' && ret=0
return ret
fi
# Otherwise, more complex conditions need to be checked.
case $CURRENT in
(1)
local files_alt='files::__git_changed-in-working-tree_files'
if [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
files_alt='files::__git_changed-in-index_files'
fi
_alternative \
'commit-ranges::__git_commit_ranges' \
'blobs-and-trees-in-treeish::__git_blobs_and_trees_in_treeish' \
$files_alt \
'blobs::__git_blobs ' && ret=0
;;
(2)
# Check if first argument is something special. In case of committish ranges and committishs offer a full list compatible completions.
if __git_is_committish_range $line[1]; then
# Example: git diff branch1..branch2 <tab>
__git_tree_files ${PREFIX:-.} $(__git_committish_range_last $line[1]) && ret=0
elif __git_is_committish $line[1] || __git_is_treeish $line[1]; then
# Example: git diff branch1 <tab>
_alternative \
'commits::__git_commits' \
'blobs-and-trees-in-treeish::__git_blobs_and_trees_in_treeish' \
'files::__git_tree_files ${PREFIX:-.} HEAD' && ret=0
elif __git_is_blob $line[1]; then
_alternative \
'files::__git_cached_files' \
'blobs::__git_blobs' && ret=0
elif [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
# Example: git diff --cached file1 <tab>
__git_changed-in-index_files && ret=0
else
# Example: git diff file1 <tab>
__git_changed-in-working-tree_files && ret=0
fi
;;
(*)
if __git_is_committish_range $line[1]; then
# Example: git diff branch1..branch2 file1 <tab>
__git_tree_files ${PREFIX:-.} $(__git_committish_range_last $line[1]) && ret=0
elif { __git_is_committish $line[1] && __git_is_committish $line[2] } ||
__git_is_treeish $line[2]; then
# Example: git diff branch1 branch2 <tab>
__git_tree_files ${PREFIX:-.} $line[2] && ret=0
elif __git_is_committish $line[1] || __git_is_treeish $line[1]; then
# Example: git diff branch file1 <tab>
# Example: git diff branch -- f<tab>
__git_tree_files ${PREFIX:-.} HEAD && ret=0
elif __git_is_blob $line[1] && __git_is_blob $line[2]; then
_nothing
elif [[ -n ${opt_args[(I)--cached|--staged]} ]]; then
# Example: git diff --cached file1 file2 <tab>
__git_changed-in-index_files && ret=0
else
# Example: git diff file1 file2 <tab>
__git_changed-in-working-tree_files && ret=0
fi
;;
esac
;;
esac
return ret
}
我只需要追加
'files::__git_changed_files ${PREFIX:-.} HEAD' \
以上
'files::__git_tree_files ${PREFIX:-.} HEAD' && ret=0
目前我已将新函数复制粘贴到我的 .zshrc
中,它正在运行。
但是,我认为有一种更简洁的方法可以使用 .bashrc
或 .zshrc
覆盖现有函数,如下所示:
_git-diff 2>/dev/null
functions[_git-diff-orig]=$functions[_git-diff]
_git-diff() {
_git-diff-orig "$@"
...
}
但我不知道该怎么做。谁能帮忙。明确一点,我想使用 .bashrc
或 .zshrc
覆盖该函数,以便它更便携。
在zsh
中,functions
关联数组中的值为规范化代码文本。这意味着您可以使用任何常用的文本操作方法来修改它:
foo() {
echo before
echo and after
}
foo
# => before
# => and after
functions[foo]=${functions[foo]/echo before/echo before;echo during}
foo
# => before
# => during
# => and after
functions foo
# => foo () {
# => echo before
# => echo during
# => echo and after
# => }
有趣的是,zsh 将解析、验证和规范化分配给 functions
数组的代码——这与正常声明函数本质上是相同的过程。这就是为什么 functions foo
的输出有一个换行符,即使字符串替换使用了分号。
将这些行添加到 ~/.zshrc
应该适用于您的示例:
current="'files::__git_tree_files ${PREFIX:-.} HEAD'"
replacement="'files::__git_changed_files ${PREFIX:-.} HEAD' $current"
functions[_git-diff]=${functions[_git-diff]/$current/$replacement}
修改函数的其他方法
有多种方法可以更改 shell 语言的文本,其中一些 对于更复杂的功能更改可能会更好。注意更多变化 会使过程更加脆弱,因为对基本代码的更新可能会失败 修补程序。
另请注意,更改已应用于存储在
functions
数组;可能与原始源文件中的内容不匹配。
使用patch
显而易见的选择,因为我们正在修补代码。 patch
实用程序修改
基于 diff
输出的文本(通常是 diff -u
)。该实用程序可以处理
一些空格差异,以及一些错误,例如不正确的行号:
plan() {
forecast=
if [[ $forecast == sun ]]; then
print "take sunscreen"
print "wear hat"
elif [[ $forecast == rain ]]; then
print "take umbrella"
print "carry raincoat"
else
print "stay home"
fi
}
plan sun
#=> take sunscreen
#=> wear hat
plan hail
#=> stay home
patchDiff='
--- plan1
+++ plan2
@@ -4,3 +4,5 @@
print "take sunscreen"
+ print "apply sunscreen"
print "wear hat"
+ print "use sunglasses"
elif [[ $forecast == rain ]]
@@ -11,2 +13,3 @@
print "stay home"
+ print "and relax"
fi'
functions[plan]=$(print -- $patchDiff \
| patch -ls -o >(cat) =(print -- $functions[plan]))
plan sun
#=> take sunscreen
#=> apply sunscreen
#=> wear hat
#=> use sunglasses
plan hail
#=> stay home
#=> and relax
使用sed
使用 sed
流编辑器,并在前面的示例的基础上构建:
sedscript='s/print *\(.*\)/print ${(C):-}/'
functions[plan]=$(print $functions[plan] | sed -e $sedscript)
functions plan
#=> plan () {
#=> forecast=
#=> if [[ $forecast == sun ]]
#=> then
#=> print ${(C):-"take sunscreen"}
#=> print ${(C):-"apply sunscreen"}
#=> print ${(C):-"wear hat"}
#=> print ${(C):-"use sunglasses"}
#=> elif [[ $forecast == rain ]]
#=> then
#=> print ${(C):-"take umbrella"}
#=> print ${(C):-"carry raincoat"}
#=> else
#=> print ${(C):-"stay home"}
#=> print ${(C):-"and relax"}
#=> fi
#=> }
plan rain
#=> Take Umbrella
#=> Carry Raincoat
按行
这不需要匹配函数中的任何特定内容。两者都是 好的和坏的 - 它很容易实现,但它可以打破甚至简单 基本功能的更改:
# split the code into an array of lines (f)
lines=("${(f)functions[plan]}")
# add code in the middle
newCode=(
${lines[1]}
'if [[ $forecast == tornado ]]; then
print "find shelter"
print "go quickly"
return
fi'
${lines[2,-1]})
# rejoin array with newlines (F)
functions[plan]=${(F)newCode}
functions plan
#=> plan () {
#=> forecast=
#=> if [[ $forecast == tornado ]]
#=> then
#=> print "find shelter"
#=> print "go quickly"
#=> return
#=> fi
#=> if [[ $forecast == sun ]]
#=> then
#=> print ${(C):-"take sunscreen"}
#=> print ${(C):-"apply sunscreen"}
#=> print ${(C):-"wear hat"}
#=> print ${(C):-"use sunglasses"}
#=> elif [[ $forecast == rain ]]
#=> then
#=> print ${(C):-"take umbrella"}
#=> print ${(C):-"carry raincoat"}
#=> else
#=> print ${(C):-"stay home"}
#=> print ${(C):-"and relax"}
#=> fi
#=> }
plan tornado
#=> find shelter
#=> go quickly