Bash 读取文本文件,更改某些行的列顺序

Bash 读取文本文件,更改某些行的列顺序,bash,shell,awk,sed,Bash,Shell,Awk,Sed,我的输入文件的格式如下: 0 1 0 0 0 1 1 0 0 0 / 1 0 / 1 0 / 1 0 1 0 3/4 1 0 0 1/4 0 0 -1 1/2 0 -1 0 1/4 -1 0 0 3/4 0 0 1 1/2 我想重新排列包含分数的行的顺序。目前我有: #!bi

我的输入文件的格式如下:

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0 3/4     1     0     0 1/4     0     0    -1 1/2
   0    -1     0 1/4    -1     0     0 3/4     0     0     1 1/2
我想重新排列包含分数的行的顺序。目前我有:

#!bin/bash
filename="input.txt"
while ((i++)); read -r line; do
  re='[0-9][/][0-9]';
  if [[ $line =~ $re ]]
    then
      echo $line
  fi
done < "$filename"
这会使我的文件看起来像

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0     1     0     0     0     0    -1 3/4 1/4 1/2
   0    -1     0    -1     0     0     0     0     1 1/4 3/4 1/2

最好使用
awk

awk -v OFS='\t' '/[0-9]\/[0-9]/{print $1,$2,$3,$5,$6,$7,$9,$10,$11,$4,$8,$12; next} 1' file

0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
0   1   0   1   0   0   0   0   -1  3/4 1/4 1/2
0   -1  0   -1  0   0   0   0   1   1/4 3/4 1/2

最好使用
awk

awk -v OFS='\t' '/[0-9]\/[0-9]/{print $1,$2,$3,$5,$6,$7,$9,$10,$11,$4,$8,$12; next} 1' file

0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
0   1   0   1   0   0   0   0   -1  3/4 1/4 1/2
0   -1  0   -1  0   0   0   0   1   1/4 3/4 1/2

@阿努巴瓦是一个比我更好的解决方案。既然我写了另一段代码,我也会介意的

#!/bin/bash

filename="input.txt"

awk '
{
for (i=1; i <= NF; i++)
  if ( $(i+1) == "/" || $i == "/" || $(i-1) == "/") {
    printf "MM%sMM",$i" "$(i+1)" "$(i+2)
    i = i+2
  } else if ( match ($i, /^[[:digit:]]\/[[:digit:]]/) ) {
    printf "MM%sMM",$i
  } else {
    printf "MM%sMM",$i
  }
  printf "\n"
}' $filename | sed -e 's/MMMM/MM/g;s/^MM//;s/MM/\t/g' 
#/bin/bash
filename=“input.txt”
awk'
{

因为(i=1;i@anubhava是一个比我更好的解决方案。因为我写了其他代码,所以请记住

#!/bin/bash

filename="input.txt"

awk '
{
for (i=1; i <= NF; i++)
  if ( $(i+1) == "/" || $i == "/" || $(i-1) == "/") {
    printf "MM%sMM",$i" "$(i+1)" "$(i+2)
    i = i+2
  } else if ( match ($i, /^[[:digit:]]\/[[:digit:]]/) ) {
    printf "MM%sMM",$i
  } else {
    printf "MM%sMM",$i
  }
  printf "\n"
}' $filename | sed -e 's/MMMM/MM/g;s/^MM//;s/MM/\t/g' 
!/bin/bash
filename=“input.txt”
awk'
{

对于(i=1;i你可以很容易地用awk做到这一点,尽管如此,我认为稍微定义一下游戏规则是很重要的。 在下列假设下:

  • 分数是以下形式的任意一种:
    A/b
    A/b
    A/b
  • 如果分数出现在第4列或第8列中,请重新排列这些列
  • 您希望保持格式正确
考虑到这一点,您可以使用以下awk代码

awk 'BEGIN{format="%4s%6s%6s%6s%6s%6s%6s%6s%6s%7s%7s%7s\n"}
     { gsub(/[[:blank:]]*\/[[:blank:]]*/,"/",$0); $0=$0 }
     ($4 ~ /\//) || ($8 ~ /\//) { 
        $12=$4" "$8" "$12
        $4=""; $8=""
        $0=$0
     }                                           
     { printf format,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12 }
    ' file.txt
这样做的目的如下:

  • 用单个
    /
    替换所有
    “/”
    或其上的任何变体

  • $0=$0
    重新定义字段,即在前两行中 将从18个字段移动到12个字段

  • 如果在字段4或8中出现分数(即a
    /
    ),则重新定义字段12,删除字段4和8,然后再次执行
    $0=$0

  • 用正确的格式打印

注:在上述示例中,分数具有不同的输出(无空格)

以上内容将为您提供以下输出:

   0     1     0     0     0     1     1     0     0    0/1    0/1    0/1
   0     1     0     1     0     0     0     0    -1    3/4    1/4    1/2
   0    -1     0    -1     0     0     0     0     1    1/4    3/4    1/2
如果你不想改变第一行中的分数,那么你可以像这样做很容易

awk 'BEGIN{format="%4s%6s%6s%6s%6s%6s%6s%6s%6s%7s%7s%7s\n"}
     (NF>12) { print; next }
     { 
        $12=$4" "$8" "$12
        $4=""; $8=""
        $0=$0
        printf format,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12
     }
    ' file.txt
在这里,你假设

  • 如果一行有超过12个字段,只需打印即可

  • 否则,洗牌列

然而,这是不太可靠的,因为一切都取决于分数在第4、第8和第12列中的类型。也就是说,它们必须在没有空格的情况下键入。输出如下所示:

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0     1     0     0     0     0    -1    3/4    1/4    1/2
   0    -1     0    -1     0     0     0     0     1    1/4    3/4    1/2

你可以用awk轻松做到这一点,尽管如此,我认为定义一点游戏规则是很重要的。 在下列假设下:

  • 分数是以下形式的任意一种:
    A/b
    A/b
    A/b
  • 如果分数出现在第4列或第8列中,请重新排列这些列
  • 您希望保持格式正确
考虑到这一点,您可以使用以下awk代码

awk 'BEGIN{format="%4s%6s%6s%6s%6s%6s%6s%6s%6s%7s%7s%7s\n"}
     { gsub(/[[:blank:]]*\/[[:blank:]]*/,"/",$0); $0=$0 }
     ($4 ~ /\//) || ($8 ~ /\//) { 
        $12=$4" "$8" "$12
        $4=""; $8=""
        $0=$0
     }                                           
     { printf format,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12 }
    ' file.txt
这样做的目的如下:

  • 用单个
    /
    替换所有
    “/”
    或其上的任何变体

  • $0=$0
    重新定义字段,即在前两行中 将从18个字段移动到12个字段

  • 如果在字段4或8中出现分数(即a
    /
    ),则重新定义字段12,删除字段4和8,然后再次执行
    $0=$0

  • 用正确的格式打印

注:在上述示例中,分数具有不同的输出(无空格)

以上内容将为您提供以下输出:

   0     1     0     0     0     1     1     0     0    0/1    0/1    0/1
   0     1     0     1     0     0     0     0    -1    3/4    1/4    1/2
   0    -1     0    -1     0     0     0     0     1    1/4    3/4    1/2
如果你不想改变第一行中的分数,那么你可以像这样做很容易

awk 'BEGIN{format="%4s%6s%6s%6s%6s%6s%6s%6s%6s%7s%7s%7s\n"}
     (NF>12) { print; next }
     { 
        $12=$4" "$8" "$12
        $4=""; $8=""
        $0=$0
        printf format,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12
     }
    ' file.txt
在这里,你假设

  • 如果一行有超过12个字段,只需打印即可

  • 否则,洗牌列

然而,这是不太可靠的,因为一切都取决于分数在第4、第8和第12列中的类型。也就是说,它们必须在没有空格的情况下键入。输出如下所示:

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0     1     0     0     0     0    -1    3/4    1/4    1/2
   0    -1     0    -1     0     0     0     0     1    1/4    3/4    1/2

@阿努巴瓦编辑了哈!:-)我知道,我只是想用一种更好的方式来描述我的问题,这可能会让事情变得更糟。我开始认为我无法解决这个问题,只能手动更改所有行,用哪个字符分隔列?这些行是否包含前导分隔符?@anubhava editedHah!:-)我知道,我只是想用一种更好的方式来描述我的问题,这可能会让事情变得更糟。我开始认为我无法解决这个问题,只能手动更改所有行,用哪个字符分隔列?行中是否包含前导分隔符?@RobS。如果你有现代版的gawk yo你可以就地更换(参考)。只需将
awk
替换为
gawk-i inplace
。如果您的awk版本不支持inplace,那么您可以执行
awk'…'file.txt>file.new.txt;mv file.new.txt file.txt
。基本上是创建一个新文件并重命名它。gawk不起作用,但简单地将输出写入一个新文件并移动该文件即可。大大提高了感谢您的帮助!@RobS。如果您有现代版的gawk,您可以就地更换(参考)。只需将
awk
替换为
gawk-i inplace
。如果您的awk版本不支持inplace,那么您可以执行
awk'…'file.txt>file.new.txt;mv file.new.txt file.txt
。基本上是创建一个新文件并重命名它。gawk不起作用,但简单地将输出写入一个新文件并移动该文件即可。大大提高了感谢帮助!它没有,分数保留在行内。但是找到了一个解决方案。它是否与所示的预期输出匹配?它没有,分数保留在行内。但是找到了一个解决方案。它是否与所示的预期输出匹配?