web-dev-qa-db-ja.com

ある文字またはパターンから別の文字またはパターンに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

私が実際に必要としているのは、dirs -vを使用するときにパスをgrepして、これを実行できるようにすることです。

$ > 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'

~2を含む行の1つとのみ一致させることができるように、grepを2回使用しました。 .zshrcのシェル関数/エイリアスとしてこれを実現する方法はありますか?


回答:以下のいずれかの回答からmv file.txt ~2を使用しました。そんなことができるとは思ってもみませんでした(笑)。

また、以下の回答の1つからこれらの行を.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
ntruter42

zshでは、次のことができます。

$ str='The brown fox jumps over the lazy dog'
$ print -r -- ${(SM)str#b*s}
brown fox jumps

${str#pattern} ksh演算子は、文字列の先頭からpatternに一致する最短文字列を削除します。 S部分文字列の場合)パラメータ拡張フラグは、パターンが一致するように制限されないように拡張します開始時、しかし、最初から可能な限り近い。 Mフラグ(matchedの場合)は、これらの展開演算子を、一致した部分を削除する代わりに、一致したものに展開します。

##の代わりに#を使用すると、shortestの代わりにlongestが取得され、%/%%がパターンを探します。文字列の終わりから。

また、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

ファイルをdirstackの2番目のディレクトリに移動します。

適切に構成されている場合、補完は、を押した後にそれらの~<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も参照してください)。

3

レイジーマッチを使用する(.*?)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

上記はすべて、firstの最初のテキストと一致し、その後firstの最後のテキスト(含まれる)まで一致します。

3
Isaac

これを行うためのより簡単で信頼性の高い方法があります。

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を使用します。

parameter Expansion 構文を使用してディレクトリ名を変更することもできますが、より複雑になります。

または、ターゲットディレクトリを変更するには、を押します。 Tab$dirstack[2]の後、zshは変数展開をその値¹に置き換えます。

bashで、 DIRSTACK 変数を使用します。引用符と中括弧が必要です。

mv file.txt "${DIRSTACK[2]}"

結果の値に対して1つの パラメーター展開 構成を使用できます。たとえば、ファイルをディレクトリスタックエントリの親ディレクトリに移動するには、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}')"

¹ 完了とキーバインドの設定によっては、別のキーが必要になる場合があります。可能性について議論することは、この回答の範囲を超えています。
² 空の単語を削除することを除いて、ディレクトリスタックが十分に深いことを確認する限り、ここでは問題ではありません。
³ ディレクトリ名に空白(またはワイルドカード文字)が含まれていない場合は、引用符は必要ありません。