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 -i
e 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.9uname -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/targetfile
pode chegar a 200 linhas com cerca de 50 caracteres cada.
Exemplo/ /etc/targetfile
conteú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/targetfile
usando vi funciona bem. Comandos como sed -i
e perl -i
falham, 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.
Use
ed
em vez desed
. Normalmente este é um editor interativo, mas você pode alimentar comandos para sua entrada padrão usando um documento aqui.O problema com
perl -i
esed -i
é que eles tentam criar um arquivo de backup/temporário em/etc
e$USER
não têmw
permissão de rito em/etc
. Você poderia:Isso deixará o arquivo de backup no formato
$HOME
. Os arquivos de backup (configurações antigas) não pertencem ao/etc
.Esta não é a maneira mais segura, pois um erro no
sed
script 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 esed [...] /etc/targetfile >/etc/targetfile
, que seria truncado/etc/targetfile
antes de/etc/targetfile
ter a chance de ser lido porsed
; uma maneira de contornar isso seria usarsponge
, 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
man
página :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:
Com a entrada de amostra postada, observe a mudança na terceira linha a partir do topo:
Se o arquivo for grande, sugiro usar um arquivo temporário como:
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:
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 .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:
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
seek
identificador 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.