【问题标题】:How do I use bash to selectively remove newlines?如何使用 bash 选择性地删除换行符?
【发布时间】:2014-05-30 01:29:48
【问题描述】:

我有一个 server.log 文件,其中包含这样编写的堆栈跟踪:

timestamp texttexttexttext \n
  at texttexttexttext\n
  at texttexttexttext\n
  at texttexttexttext\n
caused by texttexttexttext\n
  at texttexttexttext\n
  at texttexttexttext\n
  at texttexttexttext\n
  at texttexttexttext\n

以及像这样写的警告/信息,所有信息都在一行中:

timestamp texttexttexttexttext \n

我想解析日志文件,以便唯一的换行符是时间戳之前的换行符;这样堆栈跟踪都是一行,我可以稍后循环遍历它,只需要回显一次即可显示整个堆栈跟踪。基本上我希望大的堆栈跟踪看起来像这样:

timestamp texttexttexttext
  at texttexttexttext
  at texttexttexttext
  at texttexttexttext
caused by texttexttexttext
  at texttexttexttext
  at texttexttexttext
  at texttexttexttext
  at texttexttexttext\n

我尝试遍历这些行,如果它以时间戳开头,我在它之前添加一个换行符,如果它不以时间戳开头,我使用 sed 将换行符替换为空格。下面是循环:cmets 显示我试图将它全部放在一个字符串中以便稍后循环,但我也尝试将它全部放在一个文件中以便稍后通过。

for x in "$(cat tempFile.txt)" 
do
    if [[ ! $x =~ [0-2][0-9](:+).* ]]
    then
        #STRINGBUILDER+=`printf "%s" $x | sed 's/"$NL"/" "/'`
        printf "%s" $x | sed 's/$'\n'/" "/' > parsed.txt
    else
        #STRINGBUILDER+=`printf "\n%s" $x`
        printf "\n%s" $x > parsed.txt
    fi
done

但是,无论我以后如何遍历文件,换行符似乎仍然存在。我该如何摆脱它们?

【问题讨论】:

  • 我认为您的消息与您帖子中出现的\ns 混淆了。我希望您的文件中没有明确包含这些字符。
  • @RSahu 你说得对,它们并没有明确地在文件中,我只是用它们来帮助可视化换行符的确切位置。
  • 你能发布你想从输入中得到的输出吗?
  • 是的;我编辑了帖子。
  • 时间戳是什么格式的?

标签: bash file-io sed newline


【解决方案1】:

您遇到的具体问题是循环中只有一个参数:整个文件内容。那是因为"$(cat tempFile.txt)" 被引用了,所以它被完全分配给变量$x。

这里有一些(未经测试的)代码应该可以工作,虽然我也不得不质疑正则表达式......它没有锚定,所以尾随 .* 毫无意义......

if read line; then
  while read next_line; do
    if [[ $next_line =~ [0-2][0-9]: ]]; then
        echo "$line"
    else
        echo -n "$line "
    fi
    line=$next_line
  done
  echo "$line"
fi < tempFile.txt > parsed.txt

【讨论】:

  • 有道理;问题是我对报价在那里的运作方式感到困惑。
  • @user3688447 - 你也对重定向感到困惑。查看&gt; 与&gt;&gt;。
【解决方案2】:

Awk 语句应该可以工作。

awk 'BEGIN {ORS=""};!/[1-2][1-9]/ {print} /[1-2][1-9]/{printf("\n%s",$0)}' tempFile.txt > parsed.txt;

它是如何工作的:

BEGIN {ORS=""}

将输出记录分隔符设置为空,因此不打印换行符。

!/[1-2][1-9]/ {print}

如果它不是时间戳行(如果需要,您可以添加更多正则表达式。我不知道格式)然后打印该行。

/[1-2][1-9]/{printf("\n%s",$0)}'

如果是时间戳行,则打印一个换行符,然后是该行

【讨论】:

    【解决方案3】:

    如果你想从文件中构造一个字符串,并且echo该字符串具有所需的行格式,你可以这样做:

    str=""
    while IFS= read -r line; do
        if [[ $line =~ ^[0-2][0-9]:+ ]]; then
            string+="$line"
        else
            string+="${line/\\n/\ }"
        fi
    done < file
    echo -en "$string\n"
    

    如果您知道时间戳将位于行首,则不需要.*。你可以在你的正则表达式前面加上^。如果您希望访问 BASH_REMATCH 内置数组变量,则仅需要括号将 Bash 中的正则表达式括起来。

    【讨论】:

    • 我也试试这个方法;感谢您阐明如何锚定正则表达式。这是我第一次使用它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多