前の行に行を追加する方法は?


9

解析と分析が必要なログファイルがあります。ファイルには、次のようなものが含まれています。

ファイル:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

jwdjamc ksadkek} ssfjddkc * kdlsdl
sddsfd jfkdfk 
20141101 server contain dump

上記のシナリオに基づいて、開始行に日付または前の行に追加する必要がある数値が含まれていないかどうかを確認する必要があります。

出力ファイル:

20141101 server contain dump
20141101 server contain nothing {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdl sddsfd jfkdfk 
20141101 server contain dump

回答:


11

perl先読みを使用したバージョン:

$ perl -0pe 's/\n(?!([0-9]{8}|$))//g' test.txt
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk
20141101 server contain dump

-0正規表現をファイル全体で照合できるようにします。これ\n(?!([0-9]{8}|$))は負の先読みです。つまり、改行の後に8桁が続かないか、行の終わりです(これ-0により、がファイルの終わりになります)。


@terdon、最後の改行を保存するように更新されました。
muru 2014年

良いですね!私はあなたに
賛成票を投じ

いいえ、-0NUL区切りのレコードの場合。-0777メモリ内のファイル全体を丸呑みするために使用します(ここでは必要ありません)。
ステファンChazelas

@StéphaneChazelasでは、ファイル全体を読み取る以外に、Perlを改行に一致させる最良の方法は何でしょうか。
ムル

ファイルを1行ずつ処理する他の回答を参照してください。
ステファンChazelas

5

少し簡単かもしれません sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • 最初の部分:1;N;$!b1は、ファイル内のすべての行\nを1つの長い行に分割して収集します

  • 2番目の部分は、数字以外の記号の後にスペースがある可能性がある場合、改行記号を削除します。

メモリの制限を回避するには(特に大きなファイルの場合)、以下を使用できます。

sed -e '1{h;d}' -e '1!{/^[0-9]/!{H;d};/^[0-9]/x;$G}' -e 's/\n\+\( *[^0-9]\)/\1/g'

または難しいsedスクリプトを忘れて、その年が始まることを覚えておいてください2

tr '\n2' ' \n' | sed -e '1!s/^/2/' -e 1{/^$/d} -e $a

いいですね、+ 1。どのように機能するかについての説明を追加していただけませんか?
terdon

1
ああ。いいね。私はいつもtr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'自分でやります。
mirabilos 2014年

申し訳ありませんが、sed(1)POSIX BASIC REGULAR EXPRESSION Sではないものを使用する場合は、GNUismですので、反対票を投じる必要があります。
mirabilos 2014年

1
@Costas、それはGNU grepのmanページです。POSIX BRE仕様があります。EREに相当するBRE +\{1,\}です。[\n]ポータブルでもありません。\n\{1,\}POSIXになります。
ステファンChazelas

1
また、ラベルの後に別のコマンドを置くことはできません。POSIX sedsでラベル: 1;xを定義すること1;xです。したがって、次のものが必要ですsed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'。また、多くのsed実装ではパターンスペースのサイズに小さな制限があることに注意してください(POSIXは10 x LINE_MAX IIRCのみを保証します)。
ステファンChazelas

5

1つの方法は次のとおりです。

 $ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file
 20141101 server contain dump
 20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
 20141101 server contain dump

ただし、それによって最後の改行も削除されます。再度追加するには、次を使用します。

$ { perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file; echo; } > new

説明

-l末尾の改行を削除(ともそれぞれに1が追加されますprint私が使用する理由であるコールprintfの代わりに。その後、現在の行は、(数字で始まる場合/^\d+/()と現在の行数が1以上である$.>1、これは余分なを追加することを避けるために必要とされ、行の先頭に空行)、行の先頭にa \nを追加します。は各行をprintf印刷します。


または、すべての\n文字を\0に変更してから、\0数字の文字列の直前にある文字を\n再びに変更できます。

$ tr '\n' '\0' < file | perl -pe 's/\0\d+ |$/\n$&/g' | tr -d '\0'
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
20141101 server contain dump

8桁の文字列のみに一致させるには、代わりに次のコードを使用します。

$ tr '\n' '\0' < file | perl -pe 's/\0\d{8} |$/\n$&/g' | tr -d '\0'

の最初の引数printfフォーマットです。使用printf "%s", $_
ステファンChazelas

@StéphaneChazelasなんで?つまり、私はそれがよりクリーンでおそらく理解しやすいことを知っていますが、それから保護する危険性はありますか?
terdon

はい、入力に%文字が含まれている可能性がある場合は、誤りであり、潜在的に危険です。%10000000000sたとえば、入力で試してください。
ステファンChazelas

Cでは、これは非常によく知られている非常に悪い習慣と脆弱性の原因です。でperlecho %.10000000000f | perl -ne printf私のマシンをひざまずかせます。
ステファンChazelas

@StéphaneChazelasうわー、はい。あまりにも私のもの。その後、十分に公正な、編集された回答と感謝します。
terdon

3

を使用してこれを試してください:

#!/usr/bin/awk -f

{
    # if the current line begins with 8 digits followed by
    # 'nothing' OR the current line doesn't start with 8 digits
    if (/^[0-9]{8}.*nothing/ || !/^[0-9]{8}/) {
        # print current line without newline
        printf "%s", $0
        # feeding a 'state' variable
        weird=1
    }
    else {
        # if last line was treated in the 'if' statement
        if (weird==1) {
            printf "\n%s", $0
            weird=0
        }
        else {
            print # print the current line
        }
    }
}
END{
    print # add a newline when there's no more line to treat
}

それを使用するには:

chmod +x script.awk
./script.awk file.txt

2

terdonのアルゴリズムを使用する別の最も簡単な方法(私の他の回答よりも):

awk 'NR>1 && /^[0-9]{8}/{printf "%s","\n"$0;next}{printf "%s",$0}END{print}' file

ITYM END{print ""}。オルタナティブ:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
ステファンChazelas


0

Le program en bash:

while read LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo -ne "\n${LINE} "
    else
        echo -n "${LINE} "
    fi
done < file.txt

1行形式で:

while read L; do if [[ $L =~ ^[0-9]{8} ]]; then echo -ne "\n${L} "; else echo -n "${L} "; fi done < file.txt

保存バックスラッシュとソリューション(read -r)と先頭のスペース(ジャストIFS=while):

while IFS= read -r LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo
        echo -nE "\n${LINE} "
    else
        echo -nE "${LINE} "
    fi
done < file.txt

1行のフォーム:

while IFS= read -r L; do if [[ $L =~ ^[0-9]{8} ]]; then echo; echo -nE "${L} "; else echo -nE "${L} "; fi done < file.text

これは、たとえば、バックスラッシュとn。空白も取り除きます。しかし、mkshこれを行うために使用することができます:while IFS= read -r L; do [[ $L = [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]* ]] && print; print -nr -- "$L"; done; print
mirabilos

もちろん、すべてのアルゴリズムを対象とするのではなく、タスクが提供する要件を解決します。もちろん、最終的な解決策は、実際には通常発生するので、一見するとより複雑で読みにくくなります:)
2014年

私は同意しますが、特に実際のテキストをダミーテキストで置き換える場合は、OP☺についてあまり想定しないようにする難しい方法を学びました。
mirabilos 2014年

0
[shyam@localhost ~]$ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' appendDateText.txt

それはうまくいく

i/p:
##06/12/2016 20:30 Test Test Test
##TestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test test
##i123312331233123312331233123312331233123312331233Test
## 06/12/2016 20:30 abc

o/p:
##06/12/2016 20:30 Test Test TestTestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test ##testi123312331233123312331233123312331233123312331233Test
06/12/2016 20:30 abc vi appendDateText.txt 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.