Classificando arquivos usando um script bash

1

Muito obrigado por ler isto. Eu sou muito novo para bater, então eu preciso de seu conselho no seguinte:

Eu quero escrever um script bash que leia um arquivo com 2 colunas

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

Meu script, na verdade, classificava esse arquivo com base na primeira coluna (alfabeto) e produzia um arquivo chamado alpha_sorted.txt e, em seguida, eu queria que ele fizesse a mesma coisa com os números e nomeasse numbers_sorted.txt .

Sou muito novo nisso, então gostaria de pedir sua ajuda, se possível, fornecendo-me documentos ou links ou até mesmo ajudando com o código.

O script deve ser para o nível introdutório, portanto não é aconselhável complicar os métodos.

Atualizar

Usando a resposta do john1024, tenho o seguinte problema:

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

Aqui está uma captura de tela de script.sh

    
por JavaFreak 12.06.2016 / 03:15

2 respostas

3

Existem maneiras melhores de classificar do que fazê-lo apenas no bash. Esta não é uma boa resposta à sua pergunta - não é simples (porque usa vários recursos do bash que não são comuns), e não faz as coisas "The Unix Way", que é usar ferramentas que são pré-construídos para fazer uma coisa e fazer isso bem (como ordenar).

Eu decidi escrever esta resposta para ajudar a destacar que o shell padrão da sua conta foi criado para executar comandos e redirecionar E / S. Só porque um shell tem uma grande quantidade de recursos, como o Bash não significa que é o melhor ferramenta para um trabalho específico. Muitas vezes você verá respostas que sugerem o uso de awk ou perl (ou jq ou sort ...) em vez de tentar cortá-lo em uma concha apenas script .

Dito isto, o bash pode ordenar - não é apenas interno. Vou me repetir: ainda não é uma boa ideia. Mas você consegue fazer isso. Abaixo estão quatro funções, implementadas no bash, que ordena duas maneiras diferentes em cada um dos dois campos.

As funções usam:

A classificação de inserção não é eficiente ( O (n) 2 ), mas certamente razoável para conjuntos de dados pequenos, como o exemplo de 11 linhas. As quatro funções foram executadas em segundos para os dados de amostra, mas para um arquivo de entrada de 1.000 linhas geradas aleatoriamente, as ordenações de "matriz separada" levaram ~ 15 segundos enquanto as versões "no local" levaram ~ 60 segundos por causa de todas do reprocessamento dos valores. Compare isso com o utilitário de classificação padrão que classificou o arquivo de 1.000 linhas em cada coluna em sub-milésimos -uma segunda vez.

As duas funções "inplace" tentam economizar alguns bytes criando apenas uma matriz (e algumas variáveis únicas para valores de loop e troca); no lado positivo, ele usa uma função bash para mapear o conteúdo do arquivo em matrizes. As funções "chaveadas" são cautelosas e criam duas matrizes separadas, uma para ordenar as chaves desejadas e outra para os valores reais.

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[@]}"
}

Mesmo depois de tudo isso, você ainda precisa de uma das principais "funções" do seu shell, ou seja, redirecionar a saída para um arquivo:

sort_keyed_f1 input-file > alpha_sorted.txt
sort_keyed_f2 input-file > numbers_sorted.txt
    
por 13.06.2016 / 04:05
7

Como isso é postado no unix .stackexchange.com, presumo que você tenha acesso às ferramentas usuais do unix.

Classificação alfabética na primeira coluna:

$ 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

Classificação numérica:

$ 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 especifica a classificação numérica. -k2,2 especifica a classificação na segunda coluna.

Para mais informações, consulte man sort .

Problemas ao editar um script Unix com o Notepad

Eu criei um script com finais de linha do DOS:

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

Embora não seja visível, adicionei um espaço no final do comando cat . Com este arquivo, posso reproduzir o erro que você viu:

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

Podemos corrigir este problema com um utilitário como dos2unix ou tr . Usando tr :

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

Agora, podemos executar o comando com sucesso:

$ fixed.sh
d  29
d  5
d  9
f  2
f  2
g  1
g  10
g  5
h  1
s  4
s  5
    
por 12.06.2016 / 03:58

Tags