
Eu tenho um script que usa sed em um loop para excluir linhas contendo palavras de uma lista:
for i in $list; do
sed -i "/$i/d" file
done
$list
é criado a partir de uma consulta SQL:
list=$(psql -d dbname -t -c "SELECT foo from table ORDER BY column DESC")
O problema é que minha empresa implementou recentemente regras de auditoria mais rígidas que incluem todas fchown
ou fchmod
syscalls. Usando strace
, vejo que cada sed -i
inclui um fchown
e fchmod
syscall.
Com uma variável de lista grande, iowait
dispara e o sistema fica paralisado porque o auditd está escrevendo uma quantidade ridícula de linhas em seu log.
Estou tentando descobrir se existe uma maneira de passar a variável de lista para sed em uma linha para que o arquivo seja aberto e fechado apenas uma vez.
Eu já perguntei, qualquer alteração nas regras do auditd é um fracasso.
Obrigado.
Responder1
Executando apenas sed
uma vez e usando um único sed
script em vez de invocar um zilhão de comandos em linha, um por um:
sedscript=$(mktemp)
for i in $list; do
echo "/$i/d" >> "$sedscript"
done
sed -f "$sedscript" -i /path/to/file
rm -f "$sedscript"
Responder2
Se você puder atribuir cuidadosamente as palavras em list
, poderá usar a expansão do parâmetro bash para substituir cada espaço pelo alternador regex |
e pedir grep
para fazer o trabalho para você:
list='auditd the to'
cp file temp &&
grep -Ev "${list// /|}" temp > file &&
rm temp
Responder3
Se o seu shell suportar matrizes, você poderia fazer algo como
mapfile -t arr < <(psql -d dbname -t -c "SELECT foo from table ORDER BY column DESC")
para colocar a saída do comando em uma matriz de shell, então
printf '/%s/d\n' "${arr[@]}" | sed -i.bak -f - somefile
para aplicar a sequência de exclusões a somefile
, no lugar. As advertências usuais se aplicam se os campos retornados do seu psql
comando contiverem caracteres especiais para sed
.
Isso é semelhante à solução do @DopeGhoti, exceto que evita o arquivo de script temporário (e deve lidar com espaços em branco nos campos da tabela, caso seja necessário).
Responder4
Se isto: psql -d dbname -t -c "SELECT foo from table ORDER BY column DESC"
fornecer uma lista de palavras delimitadas por espaços em branco, tente isto:
$ psql -d dbname -t -c "SELECT foo from table ORDER BY column DESC" \
| awk -v FS=" " -v OFS="|" '{$1=$1; print "("$0")"}' \
| xargs -I {} sed -i -E '/{}/d' file