Bash,如何修改占位符后文件的所有行?

Bash, how to modify all the rows of a file after a placeholder?

我有一个 table 文件,我想这样修改:w当我找到一个特定的占位符时,我希望在会议之前根据它修改下一行新的占位符,之后下一行将以另一种方式修改,等等

这是我的样本table:

chr2.0  46580   46730   A_206699
chr2    46580   46730   A_206949
chr2    47020   47170   A_206699
chr2    47020   47170   A_206949
chr2.1  1680380 1680530 A_206997
chr2    1697600 1697750 A_206956
chr2    1697600 1697750 A_206963
chr2    1697600 1697750 A_206966
chr2.2  1697600 1697750 A_206980
chr2    1697600 1697750 A_206981
chr2    1697600 1697750 A_206982
chr2    1697600 1697750 A_206983
 ..................................
chr2.9  1748280 1748430 A_206982
chr2    1748280 1748430 A_206983
chr2    1748280 1748430 A_206984
chr2    1748280 1748430 A_206986
chr2    1748280 1748430 A_206987

我想要一个 bash 脚本来接受这个输入并产生以下输出:

chr2.0  46580   46730   A_206699
chr2.0  46580   46730   A_206949
chr2.0  47020   47170   A_206699
chr2.0  47020   47170   A_206949
chr2.1  1680380 1680530 A_206997
chr2.1  1697600 1697750 A_206956
chr2.1  1697600 1697750 A_206963
chr2.1  1697600 1697750 A_206966
chr2.2  1697600 1697750 A_206980
chr2.2  1697600 1697750 A_206981
chr2.2  1697600 1697750 A_206982
chr2.2  1697600 1697750 A_206983
 ...................................
chr2.9  1748280 1748430 A_206982
chr2.9  1748280 1748430 A_206983
chr2.9  1748280 1748430 A_206984
chr2.9  1748280 1748430 A_206986
chr2.9  1748280 1748430 A_206987

我该怎么办? 有什么建议么?谢谢!

编辑: 占位符是行中第一个元素中的点。

如果你喜欢正则表达式,awk 或 sed 都可以(awk 会更罗嗦一些,sed 精简且难以理解;)

虽然你没有问他们,但我猜他们可能对你来说很陌生,你当然可以在 bash:

key=''
cat $input_file | while read first rest
do
    [[ "$first" != chr?.? ]] || key="$first"
    printf '%s  %s\n' "$key" "$rest"
done > $output_file

可以 while 循环结束时重定向输入 输出,但是更容易这样读。您可能还想更详细地了解 printf 中的格式,但这应该能为您提供基本概念。

这是我的解决方案:

#!/bin/bash

if [[ $# -ne 1 ]]; then echo 'usage: require one argument.' >&2; exit 1; fi;
file="";

widths=(8 8 8 8);

suffix='';
while read -r; do

    ## parse
    line="$REPLY";
    i=0;
    fields=();
    for width in "${widths[@]}"; do
        field="${line:$i:$width}"; ## extract
        field="${field%"${field##*[![:space:]]}"}"; ## rtrim
        fields+=("$field");
        let i+="$width";
    done;

    ## save new suffix, or apply last known suffix
    if [[ "${fields[0]}" =~ (\.[0-9])$ ]]; then
        suffix="${BASH_REMATCH[1]}";
    else
        fields[0]="${fields[0]}$suffix";
    fi;

    ## print
    for ((i = 0; i < ${#widths[@]}; ++i)); do
        printf '%-*s' "${widths[$i]}" "${fields[$i]}";
    done;
    printf '\n';

done <"$file";

exit 0;

您的数据是 fixed-width table,因此正确解析它需要知道字段的宽度。我在脚本中 hard-coded 它们,但如果你不想这样做,你可以通过另一个渠道传递它们,也许通过文件中的 header 行,或者在单独的文件中.或者,如果已知文件被分隔(例如由空格分隔),则可以修改脚本以在分隔符上进行解析。但是,跟踪字段宽度可以保留对齐方式,您的问题似乎暗示您想要这样做。

你可以用它来做你想做的事(尽管不幸的是它不会保持列间距)。

awk ' ~ /\./ {f=} {=f; print}' input

将其通过管道传输到 column -t 以获得(略有不同的)列输出。