Como faço para executar algum comando editando seu arquivo (argumento) "no lugar" usando o bash?

StackOverflow https://stackoverflow.com/questions/146435

  •  02-07-2019
  •  | 
  •  

Pergunta

Eu tenho um arquivo temp.txt, que quero classificar com o sort comando em bash.

Eu quero os resultados classificados para substituir o arquivo original.

Isso não funciona, por exemplo (eu recebo um arquivo vazio):

sortx temp.txt > temp.txt

Isso pode ser feito em uma linha sem recorrer a copiar para arquivos temporários?


Editar: o -o a opção é muito legal para sort. eu usei sort na minha pergunta como exemplo. Eu encontro o mesmo problema com outros comandos:

uniq temp.txt > temp.txt.

Existe uma solução geral melhor?

Foi útil?

Solução

sort temp.txt -o temp.txt

Outras dicas

UMA sort Precisa ver toda a entrada antes de começar a sair. Por esse motivo, o sort O programa pode oferecer facilmente uma opção para modificar um arquivo no local:

sort temp.txt -o temp.txt

Especificamente, o Documentação de GNU sort diz:

Normalmente, Sort lê todas as entradas antes de abrir o arquivo de saída, para que você possa classificar com segurança um arquivo no lugar usando comandos como sort -o F F e cat F | sort -o F. No entanto, sort com --merge (-m) pode abrir o arquivo de saída antes de ler todas as entradas, então um comando como cat F | sort -m -o F - G não é seguro, pois o tipo pode começar a escrever F antes da cat acabou de ler.

Enquanto a documentação do BSD sort diz:

Se [o] arquivo de saída for um dos arquivos de entrada, classifique-o para um arquivo temporário antes de classificar e gravar a saída para [o] arquivo de saída.

Comandos como uniq pode começar a escrever a saída antes que eles terminem de ler a entrada. Esses comandos normalmente não suportam a edição no local (e seria mais difícil para eles apoiarem esse recurso).

Você normalmente contornou isso com um arquivo temporário ou, se deseja evitar um arquivo intermediário, pode usar um buffer para armazenar o resultado completo antes de escrevê -lo. Por exemplo, com perl:

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

Aqui, a parte Perl lê a saída completa de uniq em variável $_ e depois substitui o arquivo original com esses dados. Você pode fazer o mesmo na linguagem de script de sua escolha, talvez até em Bash. Mas observe que ele precisará de memória suficiente para armazenar o arquivo inteiro, isso não é aconselhável ao trabalhar com arquivos grandes.

Aqui está uma abordagem mais geral, trabalha com a Uniq, classificação e outros enfeites.

{ rm file && uniq > file; } < file

Comentário de Tobu sobre esponja Garam sendo uma resposta por si só.

Para citar do MoreUtils pagina inicial:

Provavelmente, a ferramenta de uso mais geral em MoreUtils até agora é a esponja (1), o que permite fazer coisas assim:

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

No entanto, sponge sofre do mesmo problema Steve Jessop comenta aqui. Se algum dos comandos no pipeline antes sponge Falha, então o arquivo original será escrito.

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

Uh-oh, my-important-file se foi.

Aqui está, uma linha:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

Tecnicamente, não há cópia para um arquivo temporário, e o comando 'MV' deve ser instantâneo.

eu gosto de sort file -o file Responda, mas não quero digitar o mesmo nome de arquivo duas vezes.

Usando Bash Expansão de história:

$ sort file -o !#^

Agarra o primeiro arg da linha atual quando você pressiona digitar.

Um tipo único no local:

$ sort -u -o file !#$

Agarra o último arg na linha atual.

Muitos mencionaram o -o opção. Aqui está a parte da página do homem.

Da página do homem:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.

Isso seria altamente restrito à memória, mas você poderia usar o AWK para armazenar os dados intermediários na memória e, em seguida, escrevê -los de volta.

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

Uma alternativa a sponge com o mais comum sed:

sed -ni r<(command file) file

Funciona para qualquer comando (sort, uniq, tac, ...) e usa o muito conhecido sed's -i opção (Editar arquivos no local).

Aviso: Tentar command file Primeiro porque a edição de arquivos no local não é segura por natureza.


Explicação

Em primeiro lugar, você está dizendo sed não imprimir as linhas (originais) (-n opção), e com a ajuda do sed's r comando e bash's Substituição do processo, o conteúdo gerado por <(command file) será a saída salva no lugar.


Tornando as coisas ainda mais fáceis

Você pode envolver esta solução em uma função:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

Exemplo

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file

Use o argumento --output= ou -o

Acabei de tentar no FreeBSD:

sort temp.txt -otemp.txt

Para adicionar o uniq Capacidade, quais são as desvantagens de:

sort inputfile | uniq | sort -o inputfile

Leia o editor não interativo, ex.

Se você insistir em usar o sort programa, você tem que usar um arquivo intermediário - eu não acho sort tem uma opção para classificar na memória. Qualquer outro truque com o stdin/stdout falhará, a menos que você possa garantir que o tamanho do buffer para o STDIN da Sort seja grande o suficiente para ajustar o arquivo inteiro.

EDIT: Que vergonha. sort temp.txt -o temp.txt Funciona excelente.

Outra solução:

uniq file 1<> file
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top