使用 bash 脚本对文件进行排序

使用 bash 脚本对文件进行排序

非常感谢您阅读本文。我对 bash 很陌生,所以我需要您在以下方面的建议:

我想编写一个 bash 脚本来读取包含 2 列的文件

f  2
g  1
s  4
d  5
f  2
g  5
d  9
g  10
h  1
s  5
d  29

我的脚本实际上会根据第一列(字母表)对该文件进行排序,并生成一个名为alpha_sorted.txtand 的文件,然后我希望它对数字执行相同的操作并将其命名为numbers_sorted.txt.

我对此很陌生,所以如果可能的话,我想请求您的帮助,为我提供文档或链接,甚至帮助编写代码。

该脚本旨在用于入门级别,因此不建议使方法复杂化。

更新

使用 john1024 的答案,我遇到以下问题:

Hasan@HasanChr /cygdrive/c/users/Hasan/Desktop/Bash
$ chmod +x script.sh

Hasan@HasanChr /cygdrive/c/users/Hasan/Desktop/Bash
$ ./script.sh
cat: alpha_sorted.txt: No such file or directory

这是一个屏幕截图script.sh

在此输入图像描述

答案1

由于这是发布在UNIX.stackexchange.com,我假设您可以访问常用的 UNIX 工具。

第一列按字母顺序排序:

$ sort file.txt >alpha_sorted.txt
$ cat alpha_sorted.txt
d  29
d  5
d  9
f  2
f  2
g  1
g  10
g  5
h  1
s  4
s  5

数字排序:

$ sort -nk2,2 file.txt >numbers_sorted.txt
$ cat numbers_sorted.txt
g  1
h  1
f  2
f  2
s  4
d  5
g  5
s  5
d  9
g  10
d  29

-n指定数字排序。 -k2,2指定对第二列进行排序。

有关详细信息,请参阅man sort

使用记事本编辑 Unix 脚本时出现问题

我创建了一个带有 DOS 行结尾的脚本:

$ cat dos.sh
sort file.txt >alpha_sorted.txt
cat alpha_sorted.txt 

虽然不可见,但我在命令末尾添加了一个空格cat。使用此文件,我可以重现您看到的错误:

$ chmod +x dos.sh
$ dos.sh
cat: alpha_sorted.txt: No such file or directory
: No such file or directory

dos2unix我们可以使用或等实用程序来纠正此问题tr。使用tr

$ tr -d '\r' <dos.sh >fixed.sh
$ chmod +x fixed.sh

现在,我们可以成功运行命令:

$ fixed.sh
d  29
d  5
d  9
f  2
f  2
g  1
g  10
g  5
h  1
s  4
s  5

答案2

有更好的排序方法而不是纯粹在 bash 中完成。这不是对你的问题的一个很好的答案——它并不简单(因为它使用了 bash 的几个不常见的功能),而且它不做“Unix 方式”的事情,即使用是为做一件事并做好它而预先构建的(例如排序)。

我决定写下这个答案,以帮助阐明您帐户的默认 shell 是为了运行命令和重定向 I/O 而构建的。仅仅因为外壳有一个多种功能,就像 Bash 一样并不意味着它是特定工作的最佳工具。您经常会在这里看到建议使用 awk 或 perl (或 jq 或 sort ...)的答案,而不是尝试将其破解为仅 shell 脚本

话虽如此,bashsort——它只是不是内置的。我再说一遍:这仍然不是一个好主意。但你可以做到。下面是在 bash 中实现的四个函数,它们种类在两个字段中的每一个上都有两种不同的方式。

这些函数使用:

插入排序效率不高(O(n) 2),但对于小型数据集来说当然是合理的,例如 11 行示例。对于样本数据,这四个函数的运行时间为亚秒级,但对于随机生成的 1,000 行输入文件,“单独数组”排序花费了约 15 秒,而“就地”版本花费了约 60 秒,因为值的重新处理。将此与标准排序实用程序它以千分之一秒的时间对任一列上的 1,000 行文件进行排序。

这两个“就地”函数试图通过仅创建一个数组(以及一些用于循环和交换值的一次性变量)来节省一些字节;从好的方面来说,它使用一个简洁的 bash 函数将文件内容映射到数组中。 “键控”功能将谨慎抛诸脑后,创造单独的数组,一个用于排序所需的键,另一个用于实际值。

function sort_inplace_f1 {
  local array
  mapfile -t array < "$1"
  local i j tmp
  for ((i=0; i <= ${#array[@]} - 2; i++))
  do
    for ((j=i + 1; j <= ${#array[@]} - 1; j++))
    do
      local ivalue jvalue
      [[ ${array[i]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
      ivalue="${BASH_REMATCH[1]}"
      [[ ${array[j]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
      jvalue=${BASH_REMATCH[1]}
      if [[ $ivalue > $jvalue ]]
      then
        tmp=${array[i]}
        array[i]=${array[j]}
        array[j]=$tmp
      fi
    done
  done
  printf "%s\n" "${array[@]}"
}

function sort_inplace_f2 {
  local array
  mapfile -t array < "$1"
  local i j tmp
  for ((i=0; i <= ${#array[@]} - 2; i++))
  do
    for ((j=i + 1; j <= ${#array[@]} - 1; j++))
    do
      local ivalue jvalue
      [[ ${array[i]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
      ivalue="${BASH_REMATCH[2]}"
      [[ ${array[j]} =~ ([^[:space:]]+)[[:space:]]+(.*) ]]
      jvalue=${BASH_REMATCH[2]}
      if [[ $ivalue > $jvalue ]]
      then
        tmp=${array[i]}
        array[i]=${array[j]}
        array[j]=$tmp
      fi
    done
  done
  printf "%s\n" "${array[@]}"
}

function sort_keyed_f1 {
  local c1 c2 keys values
  while IFS=' ' read -r c1 c2
  do
    keys+=("$c1")
    values+=("$c1 $c2")
  done < "$1"

  local i j tmpk tmpv
  for ((i=0; i <= ${#keys[@]} - 2; i++))
  do
    for ((j=i + 1; j <= ${#keys[@]} - 1; j++))
    do
      if [[ ${keys[i]} > ${keys[j]} ]]
      then
        # swap keys
        tmpk=${keys[i]}
        keys[i]=${keys[j]}
        keys[j]=$tmpk
        # swap values
        tmpv=${values[i]}
        values[i]=${values[j]}
        values[j]=$tmpv
      fi
    done
  done
  printf "%s\n" "${values[@]}"
}

function sort_keyed_f2 {
  local c1 c2 keys values
  while IFS=' ' read -r c1 c2
  do
    keys+=("$c2")
    values+=("$c1 $c2")
  done < "$1"

  local i j tmpk tmpv
  for ((i=0; i <= ${#keys[@]} - 2; i++))
  do
    for ((j=i + 1; j <= ${#keys[@]} - 1; j++))
    do
      if [[ ${keys[i]} -gt ${keys[j]} ]]
      then
        # swap keys
        tmpk=${keys[i]}
        keys[i]=${keys[j]}
        keys[j]=$tmpk
        # swap values
        tmpv=${values[i]}
        values[i]=${values[j]}
        values[j]=$tmpv
      fi
    done
  done
  printf "%s\n" "${values[@]}"
}

即使经历了这一切之后,你仍然需要 shell 的核心“功能”之一,即将输出重定向到文件:

sort_keyed_f1 input-file > alpha_sorted.txt
sort_keyed_f2 input-file > numbers_sorted.txt

相关内容