Alterar parte específica do arquivo através de um script de shell

1

Qual é a maneira mais simples de procurar uma palavra em um arquivo fornecido na linha de comando, com este formato

./<file1> -f <file2> --edit <id> <column> <value>

Eu quero pesquisar as pessoas em <id> em <file2> e alterar a palavra em <column> fornecida com <value> .

Eu tentei

awk -F '|' -v ID="$4" -v Column="$5" \
           -v Value="$6" 'ID==$1 {$Column=Value ;}1' \
           OFS='|' $2>NewFile
mv NewFile $2 ;

Mas eu quero que isso seja feito sem um arquivo temporário

Por exemplo:

1000|text1|text2|text3
1001|text4|text5|text6
1002|text7|text8|text9

Depois de executar

./<file> -f file2 --edit 1001 2 Marios

deve ter essa alteração:

1000|text1|text2|text3
1001|Marios|text5|text6
1002|text7|text8|text9

Eu acho que é factível com perl ou sed, mas desde que eu sou um iniciante eu tenho apenas um pouco de conhecimento awk. Se você puder elaborar sua resposta, será apreciado.

    
por Μάριος Τσοκανάς 16.10.2016 / 02:24

4 respostas

2

Editar arquivos de texto sem um arquivo temporário é uma má idéia e geralmente não é feito em scripts Unix. Isso requer a regravação de todo o arquivo, ou pelo menos a parte do sufixo que é afetada por uma edição. Se a gravação for interrompida, o arquivo está corrompido.

Naturalmente, fazemos isso todos os dias com os editores de texto: armazenamos os arquivos na memória e os sobregravamos no disco quando salvamos. As diferenças são que todos os editores decentes mantêm pelo menos um backup, a menos que esse recurso seja explicitamente desabilitado (e que seja um arquivo extra, não aceitável para você, presumivelmente) e que os editores sejam interativos: se um save falhar por algum motivo completo, falha do sistema, qualquer que seja) uma pessoa sabe disso. Nos casos em que não é um travamento, o editor ainda está em execução e tem o arquivo na memória, apesar da falha no salvamento. O usuário pode executar comandos para salvar o arquivo em outro lugar ou tentar salvar novamente depois de executar algumas ações fora do programa para corrigir alguma situação.

Solução no TXR: substitua a cópia na memória sem nenhuma estratégia de backup ou recuperação:

#!/usr/local/bin/txr --lisp

(defvarl myname [*args-full* 2])

;; check for required arguments syntax
(unless (and (= (length *args*) 6)
             (equal [*args* 0] "-f")
             (equal [*args* 2] "--edit"))
  (put-line 'usage: @myname -f <file> --edit <col1-key> <col-num> <replace>')
  (exit 1))

;; do in-memory update and overwrite
(let ((file [*args* 1])
      (key [*args* 3])
      (col (pred (tointz [*args* 4]))) ;; pred, because [f #] is zero based
      (val [*args* 5])
      (ss (make-strlist-output-stream))) ;; in-memory string list stream

  ;; awk into memory
  (awk (:inputs file) ;; input from file
       (:output ss) ;; output stream is in-memory string list
       (:set fs "|") ;; field separator is pipe
       ((equal [f 0] key) (set [f col] val)) ;; do replacement
       (t)) ;; true condition with no action -> default print action

  ;; overwrite original file with string list
  (with-stream (out (open-file file "w"))
    (put-lines (get-list-from-stream ss) out)))

Sessão:

$ diff -u data.orig data
$ ./inplace 
usage: ./inplace -f <file> --edit <col1-key> <col-num> <replace>
$ ./inplace -f data --edit 1001 2
usage: ./inplace -f <file> --edit <col1-key> <col-num> <replace>
$ ./inplace -f data --edit 1001 2 Marios
$ diff -u data.orig data
--- data.orig   2016-10-16 08:05:03.233736781 -0700
+++ data    2016-10-16 08:15:57.412394022 -0700
@@ -1,3 +1,3 @@
 1000|text1|text2|text3
-1001|text4|text5|text6
+1001 Marios text5 text6
 1002|text7|text8|text9
    
por 16.10.2016 / 17:20
0

Uma coisa que parece estar procurando é a análise de linha de comando. Uma análise decente pode ser feita com um case em um shell POSIX.

O próximo AWK é completamente bom para realizar essa transformação. Para fazer isso no lugar você tem duas alternativas: use o GNU awk (com -i ) como John sugere, ou use um arquivo temporário. Abaixo está um exemplo usando mktemp , embora mktemp não seja POSIX está em praticamente qualquer * nix sistema

#!/bin/sh

while test $# -gt 0
do
    case "$1" in
      -f)
      file="$2"
      shift
      shift
      ;;
      --edit)
      id="$2"
      column="$3"
      value="$4"
      shift
      shift
      shift
      shift
      ;;
      *)
      echo "Usage:"
      echo "  $0 -f <file> --edit <id> <column> <value>"
      exit
      ;;
  esac
done
# debug
echo "edit [$file] in [$id] change column [$column] to [$value]"

tmpf='mktemp'
awk -v FS="|" -v OFS="|" "/^$id/ { \$$column = \"$value\" }1" "$file" > "$tmpf"
mv "$tmpf" "$file"

A idéia é escapar dos caracteres certos ao passar o programa para o awk. Supondo que o script acima é chamado script.sh , você pode simplesmente fazer:

./script.sh -f myfile --edit 1001 3 "It's a me Mario"

Isso ainda tem alguns problemas que eu abordei abaixo para não agrupá-lo. Primeiro, você também deve verificar um número vazio de argumentos:

if test $# -eq 0
then
    echo Usage
    exit
fi

E, em segundo lugar, usar um mv simples às vezes é perigoso. Notavelmente quando algo dá errado e o script não produz saída. É sempre bom adicionar algo assim em torno de um mv que sobrescreverá a entrada:

if test -s "$tmpf"
then
    mv "$tmpf" "$file"
else
    echo Something went wrong
fi
    
por 16.10.2016 / 04:00
0

Suponha que você tenha um arquivo como abaixo e precise obter a saída mencionada:

Arquivo de entrada:

1000|text1|text2|text3
1001|text4|text5|text6
1002|text7|text8|text9

O resultado deve ser:

1000|text1|text2|text3
1001|Marios|text5|text6
1002|text7|text8|text9

Por favor, tente isto:

grep -rn "1001" file1 | awk -F '|' '{export $2=<new value>;print $2}' \;
  1. Basicamente, grep -rn "1001" file1 fornecerá a linha:

    1001|text4|text5|text6

  2. Depois de obter a saída acima, use awk para alterar o valor da segunda coluna, (que é Campo separado por " | ") e imprima o valor.

Eu não tenho o ambiente para executá-lo no momento, mas tenho certeza de que a lógica será útil para o que você deseja alcançar.

Conclusão: sugiro não usar arquivos temporários em scripts, porque se usarmos mais dessas atribuições de arquivos temporários, o desempenho do servidor será reduzido, pois ocorrerá mais E / S no servidor, o que, por sua vez, prejudicará seu desempenho. e tornando o servidor lento.

    
por 16.10.2016 / 06:26
0

Um invólucro de função de shell de uma linha em torno de algum código sed simples:

# Usage: foo <file2> <id> <column> <value>
foo() { sed -i "/^$2|/s/[^|]*/$4/$3" "$1" ; }

Exemplo:

 foo file2 1001 2 Marios  ;  cat file2

Saída:

1000|text1|text2|text3
1001|Marios|text5|text6
1002|text7|text8|text9
    
por 16.10.2016 / 10:14