AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 778780
Accepted
Tortoise Pirate
Tortoise Pirate
Asked: 2024-06-22 06:08:47 +0800 CST2024-06-22 06:08:47 +0800 CST 2024-06-22 06:08:47 +0800 CST

Como o UserA pode substituir programaticamente uma string de texto em um arquivo de sua propriedade localizado em/etc?

  • 772

Um usuário Linux não root possui um arquivo de texto localizado em /etc, onde o usuário Linux não root não tem permissão para criar arquivos. O usuário Linux não root pode editar o arquivo manualmente via vi sem problemas. Tentativas programáticas e manuais de substituir uma string de texto dentro do arquivo usando sed -i, perl -ie outros métodos de arquivo temporário no diretório estão falhando devido a problemas de permissão quando o usuário Linux não-root executa o script de substituição de texto. Encontrei uma solução no Perl Cookbook (anotado mais abaixo), mas ela contém avisos preocupantes, além de parecer um pouco além de mim no momento. Alguém pode sugerir uma opção mais simples?

Informações do servidor:

  • cat /etc/os-release: Servidor Oracle Linux 8.9

  • uname -a: Linux server01.domain.com 5.4.17-2136.322.6.4.el8uek.x86_64

  • perl:(v5.26.3) built for x86_64-linux-thread-multi

  • sed:(GNU sed) 4.5

  • estranho:GNU Awk 4.2.1, API: 2.0 (GNU MPFR 3.1.6-p2, GNU MP 6.1.2)

  • vi:VIM - Vi IMproved 8.0 (2016 Sep 12, compiled Aug 5 2022 07:42:15)

  • Localização do arquivo a ser editado:/etc

  • Permissões em /etc:

    drwxr-xr-x  130 root   root        12288 Jun 21 11:50 etc
    
  • Arquivo de texto para editar:/etc/targetfile

    -rw-rw-r-- 1 justauser group1 1864 Jun 19 10:52 targetfile
    
  • O comprimento do arquivo /etc/targetfilepode chegar a 200 linhas com cerca de 50 caracteres cada.

Exemplo/ /etc/targetfileconteúdo abreviado:

f1112:/dir1/dir2/59.35:N            # Comment
f3332:/dir1/dir2/59.35:N            # Comment
f4442:/dir1/dir2/59.35:N            # Comment
f555:/dir1/dir2/59.35:N             # Comment
f666s2:/dir1/dir2/59.35:N           # Comment
f777s2:/dir1/dir2/59.35:N           # Comment

Objetivo: Alterar programaticamente /dir1/dir2/59.35 para /dir1/dir2/59.77 no arquivo de destino localizado em /etc, onde o usuário não root que executa o script é "justauser" que não tem permissão para criar arquivos em /etc.

Restrições:

  • As permissões em /etc não podem ser alteradas.
  • Os programas/utilitários de servidor existentes não podem ser atualizados.
  • Todos os programas/comandos devem ser chamados de dentro de um script shell bash.
  • O script shell bash deve ser executado como "justauser", não como root.
  • Não usar o sudo é fortemente preferido.

Diversos:

Enquanto estiver logado como usuário Linux "justauser", a edição da linha de comando /etc/targetfileusando vi funciona bem. Comandos como sed -ie perl -ifalham, pois "justauser" não possui permissões de gravação em /etc para criar arquivos temporários.

perl -i -p -e 's/f4442:\/dir1\/dir2\/59.35:N/f4442:\/dir1\/dir2\/59.77:N/g' /etc/targetfile 
--Can't remove /etc/targetfile: Permission denied, skipping file.

sed -i 's/f4442:\/dir1\/dir2\/59.35:N/f4442:\/dir1\/dir2\/59.77:N/' /etc/targetfile
--sed:  sed: couldn't open temporary file /etc/sedO2SLSF: Permission denied
Rough Intended Usage Example (static values replacing variables to come):
#!/bin/bash
...

function _editConfig {
echo "Editing targetfile..."
perl -i -p -e 's/f4442:\/dir1\/dir2\/59.35:N/f4442:\/dir1\/dir2\/59.77:N/g' /etc/targetfile
if [ $? -ne 0 ]
        then
                echo "Error on _editConfig function.\n Terminating program"
                EXITCODE=1
fi
}
...

Opções vistas:

Perl Cookbook 7.10 Modificando um arquivo no local sem um arquivo temporário:

open(F, "+< $infile")       or die "can't read $infile: $!";
$out = '';
while (<F>) {
    s/DATE/localtime/eg;
    $out .= $_;
}
seek(F, 0, 0)               or die "can't seek to start of $infile: $!";
print F $out                or die "can't print to $infile: $!";
truncate(F, tell(F))        or die "can't truncate $infile: $!";
close(F)                    or die "can't close $infile: $!";
...
"This approach is for the truly determined. 
It's harder to write, takes more memory (potentially a lot more),
doesn't keep a backup file, 
and may confuse other processes trying 
to read from the file you're updating. 
For most purposes, therefore, we suggest it's probably not worth it."

Comentários finais:

Existe uma opção de edição local que não usa um arquivo temporário no diretório que seja mais simples que o exemplo do Perl Cookbook?

O exemplo do Perl Cookbook é um pouco difícil de seguir para um novato em Perl e também requer algum tipo de modificação/conversão... eu acho... para trabalhar no corpo do script bash.

linux
  • 5 5 respostas
  • 1392 Views

5 respostas

  • Voted
  1. Barmar
    2024-06-22T22:35:32+08:002024-06-22T22:35:32+08:00

    Use edem vez de sed. Normalmente este é um editor interativo, mas você pode alimentar comandos para sua entrada padrão usando um documento aqui.

    ed /etc/filename <<EOF
    1,$s/old/new/g
    w
    EOF
    
    • 16
  2. Best Answer
    waltinator
    2024-06-22T06:26:49+08:002024-06-22T06:26:49+08:00

    O problema com perl -ie sed -ié que eles tentam criar um arquivo de backup/temporário em /etce $USERnão têm wpermissão de rito em /etc. Você poderia:

    cp /etc/myfile  $HOME/myfile
    sed -i ... $HOME/myfile
    cp $HOME/myfile /etc/myfile
    

    Isso deixará o arquivo de backup no formato $HOME. Os arquivos de backup (configurações antigas) não pertencem ao /etc.

    • 14
  3. kos
    2024-06-22T10:43:09+08:002024-06-22T10:43:09+08:00

    Esta não é a maneira mais segura, pois um erro no sedscript resultará no truncamento do arquivo /etc/targetfile.

    Para contornar a incapacidade de gravar arquivos temporários em /etc, é claro que a maneira ingênua seria tentar e sed [...] /etc/targetfile >/etc/targetfile, que seria truncado /etc/targetfileantes de /etc/targetfileter a chance de ser lido por sed; uma maneira de contornar isso seria usar sponge, que não substituirá o arquivo de destino até que o STDIN tenha sido totalmente lido, evitando efetivamente o truncamento do arquivo de origem antes de ele ter sido totalmente processado, ao mesmo tempo permitindo contornar o limitação imposta pelas permissões em /etc.

    Por sua manpágina :

    esponja lê a entrada padrão e a grava no arquivo especificado. Ao contrário de um redirecionamento de shell, o Sponge absorve toda a sua entrada antes de abrir o arquivo de saída. Isso permite restringir pipelines que leem e gravam no mesmo arquivo.

    sed [...] /etc/targetfile | sponge /etc/targetfile
    
    • 11
  4. Ed Morton
    2024-06-22T23:01:16+08:002024-06-22T23:01:16+08:00

    Eu apenas usaria o awk e armazenaria tudo na memória e depois escreveria no final (depois que o awk fechasse o arquivo de entrada), por exemplo, usando qualquer awk:

    awk '
        { sub("^f4442:/dir1/dir2/59.35:N", "f4442:/dir1/dir2/59.77:N"); rec=rec $0 ORS }
        END { printf "%s", rec > FILENAME }
    ' file
    

    Com a entrada de amostra postada, observe a mudança na terceira linha a partir do topo:

    $ cat file
    f1112:/dir1/dir2/59.35:N            # Comment
    f3332:/dir1/dir2/59.35:N            # Comment
    f4442:/dir1/dir2/59.35:N            # Comment
    f555:/dir1/dir2/59.35:N             # Comment
    f666s2:/dir1/dir2/59.35:N           # Comment
    f777s2:/dir1/dir2/59.35:N           # Comment
    

    $ awk '
        { sub("^f4442:/dir1/dir2/59.35:N", "f4442:/dir1/dir2/59.77:N"); rec=rec $0 ORS }
        END { printf "%s", rec > FILENAME }
    ' file
    

    $ cat file
    f1112:/dir1/dir2/59.35:N            # Comment
    f3332:/dir1/dir2/59.35:N            # Comment
    f4442:/dir1/dir2/59.77:N            # Comment
    f555:/dir1/dir2/59.35:N             # Comment
    f666s2:/dir1/dir2/59.35:N           # Comment
    f777s2:/dir1/dir2/59.35:N           # Comment
    

    Se o arquivo for grande, sugiro usar um arquivo temporário como:

    tmp=$(mktemp) &&
    awk '{sub("^f4442:/dir1/dir2/59.35:N", "f4442:/dir1/dir2/59.77:N")} 1' file > "$tmp" &&
    cat "$tmp" > file
    rm -f "$tmp"
    

    Dessa forma, você está novamente alterando apenas o conteúdo de file, não substituindo-o.

    PS Pessoalmente, eu não usaria uma pesquisa regexp e substituiria isso, pois apresenta muitas dicas sobre o que poderia estar presente na entrada e/ou na string de pesquisa. Em vez disso, usaria strings literais, por exemplo:

    awk -v old='f4442:/dir1/dir2/59.35:N' -v new='f4442:/dir1/dir2/59.77:N' '
        index($0,old) == 1 { $0 = new substr($0,length(old)+1) }
        { rec = rec $0 ORS }
        END { printf "%s", rec > FILENAME }
    ' file
    

    Isso ainda teria que ser ajustado se o caminho do diretório pudesse conter barras invertidas, mas isso é fácil de lidar, se necessário, por exemplo, veja ENVIRON[]em how-do-i-use-shell-variables-in-an-awk-script .

    • 6
  5. tchrist
    2024-06-24T23:40:53+08:002024-06-24T23:40:53+08:00

    Eu não mencionei esse caso no Perl Cookbook porque é muito raro, mas como sua string de substituição tem exatamente o mesmo comprimento da string original, na verdade é possível atualizar o arquivo do disco no lugar - simplesmente redefinindo o ponteiro de busca de volta para onde estava quando aquela linha foi lida.

    O código a seguir funciona no seu arquivo, conforme testado em um sistema Ubuntu:

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    ###my $filename   = "$ENV{HOME}/tmp/targetfile"; # for testing
    my $filename   = "/etc/targetfile";
    my $old_string = "f4442:/dir1/dir2/59.35:N";
    my $new_string = "f4442:/dir1/dir2/59.77:N";
    
    length($old_string) == length($new_string)
        || die "old and new string must be the same length";
    
    open(my $fh, "+<", $filename) || die "can't open $filename for update: $!";
    $fh->autoflush(1);
    
    my $filepos = 0;
    
    while (<$fh>) {
        next unless s/\Q$old_string\E/$new_string/g;
        warn "updating line of $filename at position $filepos"; # or comment out if you're confident :)
        seek($fh, $filepos, 0) || die "can't seek to pos $filepos in $filename: $!";
        print($fh $_)          || die "can't write new line to $filename as pos $filepos: $!";
    }
    continue {
        $filepos = tell($fh);
    }
    
    close($fh) || die "can't close $filename: $!";
    

    A moral da história é que, surpreendentemente, você realmente pode usar o mesmo identificador para leitura e escrita, se for muito, muito , muito cuidadoso - e somente se houver um seekidentificador de arquivo no stdio entre a alteração de E/ S "instruções" (da leitura para a escrita ou vice-versa), como existe aqui. Isso é necessário para manter o buffer stdio em ordem e, embora o perl use seu próprio sistema de buffer de E/S , as mesmas regras POSIX herdadas se aplicam.

    Essa abordagem é especialmente atraente porque não ocupa mais memória proporcional ao tamanho do arquivo, como faz a desagradável abordagem "leia tudo na memória primeiro". Isso significa que você pode executar isso em um arquivo com cem milhões de linhas, se desejar, e não consumirá mais memória do que consumiria para um arquivo minúsculo.

    É extremamente corajoso, dada a falta de apoio.

    • 4

relate perguntas

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • Inicie/pare o serviço systemd usando o atalho de teclado [fechado]

  • Necessidade de algumas chamadas de sistema

  • astyle não altera a formatação do arquivo de origem

  • Passe o sistema de arquivos raiz por rótulo para o kernel do Linux

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve