我想 grep 从给定字符或模式到另一个给定字符或模式的字符串,而不是整行。
例如:
$ > echo "The brown fox jumps over the lazy dog" | grep -option "b" "s"
brown fox jumps
$ > echo "The brown fox jumps over the lazy dog" | grep -option "the l" "g"
the lazy dog
我实际上需要它的目的是在使用时 grep 一个路径,dirs -v
以便我可以执行以下操作:
$ > ls -AF
file.txt github/ .vim/
$ > dirs -v
0 ~
1 ~/.vim/pack/themes
2 ~/.vim/colors
3 ~/github/dirname
4 ~/github/repo/
$ > mv -v file.txt $(dirs -v | grep 2 | grep -option "~" "colors")
renamed 'file.txt' -> '~/.vim/colors/file.txt'
我使用了 grep 两次,这样我只能将~
与包含 的行中的匹配2
。有没有办法在我的 .zshrc 中作为 shell 函数/别名来完成此任务?
答案:我只是使用了mv file.txt ~2
以下答案之一。我什至没想到我能做到这一点,哈哈。
我还将以下答案之一中的这些行放入我的 .zshrc 中。
function grepo () { grep -Po "${1}.*?${2}" }
alias -g GO='| grepo'
这样我就可以像这样使用它:
$ > echo "the brown fox jumps over the lazy dog" GO b s
brown fox jumps
$ > echo "the brown fox jumps over the lazy dog" GO "the l" g
the lazy dog
$ > echo "the brown fox jumps over the lazy dog" GO fox over
fox jumps over
对于我的问题,它不起作用,但由于某种我无法想到的原因。
$ > ls -AF
file.txt github/ .vim/
$ > dirs -v
0 ~
1 ~/.vim/pack/themes
2 ~/.vim/colors
3 ~/github/dirname
4 ~/github/repo/
$ > mv file.txt $(dirs -v GO "~/.v" "themes")
mv: cannot move 'file.txt' to '~/.vim/pack/themes': No such file or directory
答案1
在 中zsh
,您可以执行以下操作:
$ str='The brown fox jumps over the lazy dog'
$ print -r -- ${(SM)str#b*s}
brown fox jumps
ksh${str#pattern}
运算符删除最短pattern
从字符串开头匹配的字符串。 (S
对于子串)参数扩展标志扩展它,使模式不限于匹配在开始时,但从一开始就尽可能接近。旗帜M
(对于匹配的) 使这些扩展运算符扩展为匹配的内容,而不是删除匹配的部分。
使用##
而不是#
让你最长代替最短和%
/%%
从字符串末尾查找模式。
您还可以进行 PCRE 匹配并使用其*?
非贪婪运算符:
$ zmodload zsh/pcre
$ [[ $str -pcre-match 'b.*?s' ]] && print -r -- $MATCH
brown fox jumps
或者:
$ set -o rematchpcre
$ [[ $str =~ 'b.*?s' ]] && print -r -- $MATCH
brown fox jumps
不过,对于您的具体用例,我只会这样做:
mv file.txt ~2
将文件移动到目录堆栈中的第二个目录。
如果配置正确,按after 后,补全将显示这些内容~<number>
对应的内容:Tab~+
$ mv 文件 ~+Tab 完成目录堆栈 1 -- ~/install/cvs/zsh/Config 2 -- ~ 3 -- /usr 4 -- ~bin 5 -- ~ 6 -- /
~/.zshrc
产生该输出的最小值如下所示:
set -o autopushd
autoload -Uz compinit
compinit
zstyle ':completion:*' menu select=0
zstyle ':completion:*' verbose true
zstyle ':completion:*' format 'Completing %d'
(另请参阅compinstall
以更适合初学者的方式自定义完成)。
答案2
使用 PCRE 正则表达式中的惰性匹配 ( .*?
):
$ str='The brown fox jumps over the lazy dog'
$ grep -Po 'b.*?s' <<<"$str"
brown fox jumps
$ grep -Po 'the l.*?g' <<<"$str"
the lazy dog
对于更基本的正则表达式有一个类似的解决方案,但仅适用于单个字符(在右手):
$ grep -o 'b[^s]*s' <<<"$str"
brown fox jumps
$ grep -o 'the l[^g]*g' <<<"$str"
the lazy dog
以上所有内容都将匹配第一的初始文本,然后直到第一的最终文本(包含在内)。
答案3
有一种更简单、更可靠的方法可以做到这一点。
在zsh中, 使用dirstack
多变的。不需要引用,因为 zsh 不会对变量扩展做任何花哨的事情。
mv file.txt $dirstack[2]
如果您想要父目录,可以附加历史修改器 :h
。例如,要将文件移动到目录堆栈条目的父级,请使用mv file.txt $dirstack[2]:h
.要移动到父级的父级,请使用mv file.txt $dirstack[2]:h:h
或mv file.txt $dirstack[2]:h2
。
您还可以使用参数扩展构造来修改目录名称,但它变得更加复杂。
或者,要修改目标目录,请按Tabafter $dirstack[2]
,zsh 将用其值替换变量扩展。
在bash中, 使用DIRSTACK
多变的。需要引号和大括号。
mv file.txt "${DIRSTACK[2]}"
您可以使用一个参数扩展根据结果值进行构建。例如,要将文件移动到目录堆栈条目的父目录,请使用mv file.txt "${DIRSTACK[2]#/*}"
.
$ > mv -v file.txt $(dirs -v | grep 2 | grep -option "~" "colors")
这是脆弱的。如果目录名包含2
?或者如果堆栈上的目录超过 12 个?它甚至不起作用,因为代字号在命令替换的结果中没有扩展。你需要使用类似的东西
"$(HOME=/none dirs -v | grep '^ *2[^0-9]' | sed 's:^[^/]*::')"
- 命令替换周围用双引号引起来如果生成的目录名称包含空格。
2
仅在行首匹配(允许前导空格)。2
如果它是多位数字的前导数字,则不匹配。- 使用 sed 删除目录名称开头之前的所有内容。由于
HOME
在运行时设置为不存在的路径dirs
,因此目录名称保证以斜杠开头。 - 如果您只想选择目录名称的一部分,则只需要额外的 grep 。
awk 可能是一个更好的工具,但在某种程度上,是使用单个更通用的工具 (awk) 还是组合更专业的工具(grep、sed 等)是一个品味问题。
"$(HOME=/none dirs -v | awk '$1 == 2 {sub(/^[ \t0-9]+/, ""); print; exit}')"
1根据您的完成情况和按键绑定设置,您可能需要另一个按键。讨论可能性超出了本答案的范围。
²除了删除空字之外,但这并不重要,只要您确保目录堆栈足够深即可。
³如果目录名称不包含空格(或通配符),则不需要引号。