Como faço para mover uma linha em um arquivo de texto para cima ou para baixo em uma linha?

3

Eu tenho alguns arquivos de texto, e gostaria de poder mover uma linha arbitrária em qualquer um dos arquivos para cima ou para baixo em uma linha (linhas no início ou no final do arquivo fique onde estão). Eu tenho algum código de trabalho, mas parece kludgy e eu não estou convencido de ter todos os casos de borda cobertos, então eu estou querendo saber se há alguma ferramenta ou paradigma que faz isso melhor (por exemplo, mais fácil de entender o código (para outros leitores ou me em 6 meses), mais fácil de depurar e mais fácil de manter, "mais eficiente" não é muito importante).

move_up() {
  # fetch line with head -<line number> | tail -1
  # insert that one line higher
  # delete the old line
  sed -i -e "$((line_number-1))i$(head -$line_number $file | tail -1)" -e "${line_number}d" "$file"
}

move_down() {
  file_length=$(wc -l < "$file")
  if [[ "$line_number" -ge $((file_length - 1)) ]]; then
    # sed can't insert past the end of the file, so append the line
    # then delete the old line
    echo $(head -$line_number "$file" | tail -1) >> "$file"
    sed -i "${line_number}d" "$file"
  else
    # get the line, and insert it after the next line, and delete the original
    sed -i -e "$((line_number+2))i$(head -$line_number $file | tail -1)" -e "${line_number}d" "$file"
  fi
}

Eu posso fazer a verificação de erros de entradas dentro ou fora dessas funções, mas bônus pontos se entrada ruim (como não-inteiros, arquivos inexistentes ou números de linha maiores que o comprimento do arquivo) são tratados de forma adequada.

Eu quero que ele seja executado em um script Bash em sistemas Debian / Ubuntu modernos. Eu nem sempre tenho acesso root, mas posso esperar que ferramentas "padrão" sejam instaladas (pense em um servidor web compartilhado), e pode ser capaz de solicitar a instalação de outras ferramentas se eu puder justificar a requisição ( embora menos dependências externas seja sempre melhor).

Exemplo:

$ cat b
1
2
3
4
$ file=b line_number=3 move_up
$ cat b
1
3
2
4
$ file=b line_number=3 move_down
$ cat b
1
3
4
2
$ 
    
por drewbenn 04.03.2015 / 09:13

3 respostas

12

Semelhante à sugestão do Archemar , você pode fazer o script com ed :

printf %s\n ${linenr}m${addr} w q | ed -s infile

ou seja,

linenr                      #  is the line number
m                           #  command that moves the line
addr=$(( linenr + 1 ))      #  if you move the line down
addr=$(( linenr - 2 ))      #  if you move the line up
w                           #  write changes to file
q                           #  quit editor

por exemplo. para mover a linha não. 21 uma linha:

printf %s\n 21m19 w q | ed -s infile

para mover a linha no. 21 uma linha abaixo:

printf %s\n 21m22 w q | ed -s infile

Mas como você só precisa mover uma determinada linha para cima ou para baixo em uma linha, pode também dizer que praticamente deseja trocar duas linhas consecutivas. Conheça sed :

sed -i -n 'addr{h;n;G};p' infile

i.e.

addr=${linenr}           # if you move the line down
addr=$(( linenr - 1 ))   # if you move the line up
h                        # replace content of the hold  buffer with a copy of the pattern space
n                        # read a new line replacing the current line in the pattern space  
G                        # append the content of the hold buffer to the pattern space
p                        # print the entire pattern space

por exemplo. para mover a linha não. 21 uma linha:

sed -i -n '20{h;n;G};p' infile

para mover a linha no. 21 uma linha abaixo:

sed -i -n '21{h;n;G};p' infile

Eu usei a sintaxe gnu sed acima. Se portabilidade é uma preocupação:

sed -n 'addr{
h
n
G
}
p' infile

Além disso, as verificações usuais: arquivo existe e é gravável; %código%; %código%; file_length > 2 ;

    
por 04.03.2015 / 11:55
2

existe um comando do vi chamado move m

você pode usar o vi no modo de texto: ex

  $line_number=7
  $line_up=$(($line_number + 1 ))
  (echo ${line_number}m${line_up} ; echo wq ) | ex foo

onde

  • foo é seu arquivo
por 04.03.2015 / 09:30
0

Com vims (use o vim no modo sed): link

Você pode:

cat file.txt | vims "$NUMBERm.-1"

Para mover a linha para baixo em um.

    
por 30.06.2017 / 10:53