web-dev-qa-db-ja.com

最後のx個の出現以外の文字を置き換える

次のようなIPに関連付けられた一連のホスト名を持つファイルがあります。

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int.test.example.com 59.2.86.3
super.awesome.machine 123.234.15.6

私はそれを次のように見せたいです:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int-test-example-com 59.2.86.3
super-awesome-machine 123.234.15.6

どうすれば交換できますか? (ドット)最初の列から-(ハイフン)を付けて、2番目の列によるソートを容易にしますか?最初のスペースまでドットを置き換えるためにsedを使用するか、最後の3つを除くすべてのドットを置き換えることを考えていましたが、正規表現とsedを理解できません。単純な置換を実行できますが、これは私の頭の上の方法です!

これは、私がbashで書いている大きなスクリプトの一部です。この部分で行き詰まっています。

9
Florin

AWKを使用できます

awk '{gsub(/-/,".",$1);print}' infile

説明

awkはデフォルトで空白で行を分割します。したがって、行の最初の列($1 in awk- ese)は、置換を実行する対象になります。この目的のために、以下を使用できます。

 gsub(regex,replacement,string)

必要な置換を実行します。

gsubgawknawkでのみサポートされていますが、最近の多くのディストリビューションではawkgawkへのソフトリンクです。

7
Rahul Patil

最初のフィールドで置換を行う必要がある場合、最適なのは Rahulのawkソリューション を使用することですが、間隔に影響を与える可能性があることに注意してください(フィールドの間に単一のスペースを入れてフィールドを書き換えます)。

代わりにそれを書くことでそれを避けることができます:

Perl -pe 's|\S+|$&=~tr/./-/r|e' file

-pフラグは、「-eで指定されたスクリプトを適用した後、入力ファイルを1行ずつ読み取り、各行を出力する」ことを意味します。次に、すべてのs|pattern|replacement|\S+で置き換えた後、スペース以外の文字の最初のシーケンス($&)を一致したパターン(.)で置き換えます(-)。 。コツは、s|||eを使用することです。ここで、e演算子は式を置換として評価します。そのため、前のもの(tr/./-/)の一致($&)に1つの置換(s|||e)を適用できます。

すべての.-で置き換える必要がある場合は、最後の3つを除いて、GNU sedを使用し、revコマンド:

rev file | sed 's/\./-/4g' | rev
6

Sedは、この仕事に最適なツールではありません。他の答えを参考にして、より優れたツールを入手してください。ただし、可能です。

交換する . 沿って -最初のスペースまでのみ、ループでsを使用します。

sed -e '
  : a                     # Label "a" for the branching command
  s/^\([^ .]*\)\./\1-/    # If there is a "." before the first space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

(一部のsed実装では、同じ行のコメントをサポートしていないことに注意してください。GNU sedはサポートします。)

代わりに、最後のスペースまで置換を実行するには、次のようにします。

sed -e '
  : a                     # Label "a" for the branching command
  s/\.\(.* \)/-\1/        # If there is a "." before the last space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

別の手法では、sedのホールドスペースを利用します。変更したくないビットをホールドスペースに保存し、作業を行ってから、ホールドスペースを呼び出します。ここでは、最後のスペースで行を分割し、最初の部分でドットをダッシュ​​に置き換えます。

sed -e '
  h           # Save the current line to the hold space
  s/.* / /    # Remove everything up to the last space
  x           # Swap the work space with the hold space
  s/[^ ]*$//  # Remove everything after the last space
  y/./-/      # Replace all "." by "-"
  G           # Append the content of the hold to the work space
  s/\n//      # Remove the newline introduced by G
'

これは、大きな厄介な正規表現よりも少し読みやすいと思います。基本的には、行を空白で2つのフィールドに分割し、最初の部分でsedを使用します。

while read -r Host ip; do
    echo "$(sed 's/\./-/g' <<< "$Host") $ip"
done < input_file

シェルによっては、sedコマンドの代わりに$ {Host //./-}を使用することもできます。

2
maedox

Rahulがあなたのユースケースに 標準的な回答 を提供したので、私は定型的な問題に答えるのに一生懸命かかるだろうと思いました:正規表現の最後のx個の出現以外をすべて置き換えます:

Perl -pe '
    $count = tr{.}{.}; # Count '.' on the current line
    $x = 3;
    next LINE if $count <= $x;
    while(s{\.}{-}){   # Substitute one '.' with a '-'
        last if ++$i == $count - $x # Quit the loop before the last x substitutions
    }
$i = 0
' your_file

上記のコード(テスト済み)は、スペースで区切られたフィールドがあることを想定していません。最後の3つのドットを除いて、線上のすべてのドットをダッシュ​​に置き換えます。 3お好みに合わせてコードで。

2
Joseph R.

これには、さまざまなツールを使用できます。 Rahul Patilがあなたにgawkをすでに与えているので、他にいくつかあります:

  • Perl

    Perl -lane  '$F[0]=~s/\./-/g; print "@F"' file
    

    -aスイッチを指定すると、Perlは入力行を空白で自動的に分割し、結果のフィールドを配列@Fに保存します。したがって、最初のフィールドは$F[0]になるため、最初のフィールドでs///のすべての出現箇所を(.)を-に置き換え、配列全体を出力します。

  • シェル

     while read -r a b; do printf "%s %s\n" "${a//./-}" "$b"; done < file 
    

    ここでは、whileループがファイルを読み取り、空白で自動的に分割されます。これにより、$first$restの2つのフィールドが作成されます。構造${first//pattern/replacement}は、出現するすべてのpatternreplacementに置き換えます。

2
terdon
sed 's/\./-/' <file name>

コマンドの最後にgを使用しなくても、これを行うことができます…これは、パターンの最初の出現を置き換えるだけです。

0
sunandan