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
以获得(略有不同的)列输出。
我有一个 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
以获得(略有不同的)列输出。