如何从一个字符或模式 grep 到另一个字符或模式?

如何从一个字符或模式 grep 到另一个字符或模式?

我想 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:hmv 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根据您的完成情况和按键绑定设置,您可能需要另一个按键。讨论可能性超出了本答案的范围。
²除了删除空字之外,但这并不重要,只要您确保目录堆栈足够深即可。
³如果目录名称不包含空格(或通配符),则不需要引号。

相关内容