简体   繁体   中英

How to pass a bash variable to sqlplus and then pass that output to another variable

So what I'm trying to do is to clear the audit logs of the PDB in an Oracle database. The name of the PDB can be different each time, so I cannot use tnsnames to sqlplus directly into the PDB to do this. I'm passing commands into bash and then passing those into a SQLPLUS command. Each of these work except for one and I can't seem to figure out how to get it to work.

My code is

AUDIT="DELETE FROM SYS.AUD$ WHERE NTIMESTAMP# < sysdate-30;"
FINDPDB="select pdb_name from dba_pdbs where pdb_name != 'PDB\$SEED';"
ALTER="alter session set container=$FINDPDB;"


sqlplus -S /nolog <<EOF1
  connect / as sysdba
  set echo off feedback off head off pages 0
  set serveroutput on
  $FINDPDB
  $ALTER
  $AUDIT
  exit;
EOF1

The error I keep getting is

alter session set container=select pdb_name from dba_pdbs where pdb_name != 'PDB$SEED';
                              *
ERROR at line 1:
ORA-65015: missing or invalid container name

This tells me that it's not passing the output of the select statement to $FINDPDB, but rather the actual select statement itself.

Is there a way I can pass this value to the ALTER variable and have it alter the session and clear the sys.aud$ table?

I have no Oracle instance at hand but I see two ways to do this :

  1. Make many connections through SQL*Plus
    1. First, to retrieve pdb_name .
    2. Second, to set container and delete audits.
  2. Uses a single SQL*Plus but uses two named pipes
    1. One to send generated SQL commands
    2. Second to read SQL*Plus output

As alternative way I should have used a "real" programming language (Ruby, Python, JavaScript) which are better dedicated to deal with data read from database.

EDIT: After some search, it mays be done in PL/SQL

DECLARE
   v_pdb_name VARCHAR2(255);
BEGIN
   SELECT pdb_name INTO v_pdb_name FROM dba_pdbs WHERE pdb_name != 'PDB\$SEED';
   EXECUTE IMMEDIATE 'ALTER SESSION SET container='||v_pdb_name;
   DELETE FROM sys.aud$ WHERE ntimestamp# < sysdate-30;
END;
/

The error I keep getting is

 alter session set container=select pdb_name from dba_pdbs where pdb_name != 'PDB$SEED'; * ERROR at line 1: ORA-65015: missing or invalid container name 

This tells me that it's not passing the output of the select statement to $FINDPDB, but rather the actual select statement itself.

I don't see why you would expect this to pass the output of the SELECT query into $FINDPDB . You're putting together a big long string which bash passes to the standard input of sqlplus and then writes to stdout the output from sqlplus. At no point is bash picking out certain lines of the sqlplus output and putting them into shell variables.

In fact, try adding echo $ALTER to your bash script before you call sqlplus. You will quite probably find that the output is

alter session set container=select pdb_name from dba_pdbs where pdb_name != 'PDB$SEED';

If so, then bash has already done the substitution you didn't want before you've even started sqlplus .

You seem to want bash and sqlplus to have some kind of back-and-forth dialog. I would give up on this approach. Instead of trying to put the PDB name into a shell variable, I would put it into a sqlplus substitution variable. I would try something like the following (not tested):

sqlplus -S /nolog <<"EOF1"
  connect / as sysdba
  set echo off feedback off head off pages 0
  set serveroutput on
  column pdb_name new_value pdb
  select pdb_name from dba_pdbs where pdb_name != 'PDB\$SEED';
  alter session set container = &pdb.;
  delete from sys.aud$ where ntimestamp# < sysdate - 30;
  exit;
EOF1

We use column pdb_name new_value pdb to set the substitution variable pdb to the next value to be selected from a column named pdb_name . We then run a select query to fetch the PDB name and hence store it in pdb . Once we've got this value in a substitution variable, we can then issue the alter session statement to change the PDB and finally the delete statement to delete data from the PDB.

I'm tempted to avoid the use of a PL/SQL block for this, as has been suggested in another answer. I would prefer that the delete statement is parsed after the PDB is changed as I would want to be sure that the data from the 'correct' PDB is being deleted. My concern with using PL/SQL for this is that the PL/SQL compiler would determine which table to delete from when the block is parsed, which would be before it runs the block, and hence before it executes the alter session statement to change the PDB. However, I don't know PDBs and CDBs in Oracle 12c well enough to say whether this is a genuine problem or unfounded nonsense.

I don't have access to a pluggable Oracle 12c database to run something like this against, so I can't tell you whether this script works. If not, hopefully it should give you an idea of where to go.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM