I have this sentence that returns two values.

cat /ora$sid/dbs/listener.ora|grep PORT|sed 's/.*PORT.*\([0-9]\{4\}\)\(.*\)/\1/'


With this sentence I want to get only one value. This value I want to save it into a variable for to build a sqlplus string to automatily connect to oracle database and then execute a command.

How can I limit the number of results returned and save it into a variable To later use this value in another command?


I have added the sentence into a ssh call but when I execute it I don't get any value (without ssh call work fine with head -1)

read -p "Maquina : " maquina; read -p "SID : " sid; ssh ora$sid@`echo $maquina` "port=$(cat /ora$sid/dbs/listener.ora|grep PORT|sed 's/.*PORT.*\([0-9]\{4\}\)\(.*\)/\1/ ' | head -1)| echo $port"
  • 1
    which value do you want? first? largest? both as a string "1521 1522" ? Both in an array variable?
    – Jeff Schaller
    Jun 24 '16 at 11:45

Given the way you've constructed the command, by recommendation would be to use head, tail, or some combination of the two to get only one result. If you just want the first result, head -1 should do it. If you're looking for a specific line number, a combination of head and tail will likely be necessary.

  • 1
    Or sed -n 3p to get the third line, and similar Jun 24 '16 at 11:44

If you want the first returned value you can use |head -1 to get it.

You can then use the $() structure to return it as a value eg

port=$(cat /ora$sid/dbs/listener.ora|grep PORT|sed 's/.*PORT.*\([0-9]\{4\}\)\(.*\)/\1/' | head -1)

That's a lot of commands doing the same things only one command could do.

I don't know how "listener.ora" is formatted, but the below line should work fine, at most with some minor tweaking.

sed -rn '/PORT/ s/.*([0-9]{4}).*/\1/p' /ora$sid/dbs/listener.ora

-r for regex
-n for suppressing automatic printing
/PORT/ to find line containing PORT
s/ for replace
/p to print the edited lines

Use head or tail to get one or the other values.

$PORT0=(sed -rn '/PORT/ s/.*([0-9]{4}).*/\1/p' /ora$sid/dbs/listener.ora | head -1)

Or if you want both:

grab_port () { sed -rn '/PORT/ s/.*([0-9]{4}).*/\1/p' /ora$sid/dbs/listener.ora ; }
$PORT0=$(grab_port|head -1)
$PORT1=$(grab_port|tail -1)

Now, let's look at your line:

read -p "Maquina : " maquina; read -p "SID : " sid; ssh ora$sid@`echo $maquina` "port=$(cat /ora$sid/dbs/listener.ora|grep PORT|sed 's/.*PORT.*\([0-9]\{4\}\)\(.*\)/\1/ ' | head -1)| echo $port"

The part that starts with "port= ends with | echo $port", piping the result of its own variable to itself... not what you want to do here. Edit: Not to mention that it isn't even running the command or making the variable. It's just not constructed properly.

If you set the port variable before, it gets easier as well.

read -p "Maquina : " maquina; read -p "SID : " sid; ssh ora$sid@`echo $maquina` "$PORT0"

For the general question in your title, head -1 or sed -n 1p -- or sed 1q -- are ways to select the first line of anything.

But your case is to select the first substitution result by sed. You can get that from sed itself with a slight modification, and it can also do the equivalent of your grep and your useless cat, plus you only need one of your capture groups:

sed </ora$sid/dbs/listener.ora -n '/PORT/{s/.*PORT.*\([0-9]\{4\}\).*/\1/p;q}' 
# -n don't output input lines by default
# /PORT/ when an input line contains the string PORT
#    {s/.../\1/p; extract only the 4digits after PORT and print them
#    q} and then terminate sed processing, so we onloy get one value

ADDED: you can also avoid the backslashing in the regexp if you use -r, as Kaffe answered (while I was drafting), on MOST versions of sed.

Your ssh case has TWO (or three) problems. You do:

ssh user@machine "port=$( commands ) | echo port"

First, the $( commands ) are within a double-quoted string, so they run on YOUR machine, not the other machine. You are connecting to $maquina but trying (see next) to tell it to print the port number for YOUR machine, which is a waste of time. (And it's not easy to fix this, because you want to subsitute $sid plus use singlequotes for the sed command.)

Second, you do port=value | echo $port. That pipeline runs separate subshells (on the other machine); in the first subshell it sets the variable port to the port number (from your machine) but that variable is never used for anything; in the second subshell it prints the value of the variable port, which is not set in that subshell, so you get an empty line. To put a value in a variable and then use the variable, use ; or if you want to or can depend on its exit status && or ||.

But you don't need any variable. Except for mangling whitespace and expanding globs if present, which they aren't here, var=$(commands); echo $var is exactly the same as commands. Similarly, !echo $maquina! where the ! are actually backticks (I can't find a way to markdown in text) without whitespace or glob (which won't occur in a machine name) is exactly the same as $maquina.


ssh ora$sid@$maquina "sed </ora$sid/dbs/listener.ora -n '/PORT/{s/.*PORT.*\([0-9]\{4\}\).*/\1/p;q}' "
grep -m1 PORT /ora$sid/dbs/listener.ora | sed 's/.*PORT.*\([0-9]\{4\}\)\(.*\)/\1/'


Change the if($count > NUM) to restrict the number of returned matches, here it is 3 matches.

perl -e 'my $count = 0; while(<>) { if(/PORT (\d+)/) {print "$1\n"; $count+=1; if($count > 2) {exit}} }' /ora$sid/dbs/listener.ora

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.