I am executing this command

ar=($( ssh -i id_rsa -T -y [email protected] sh -c "id;whoami;ps aux")) 

I need to save the output of each command executed into the array and call the array, but i am not able to get the desired result as ps aux or other command when calling the array as

echo ${ar[2]}

its just showing me the first line of ps aux that is


and not the full result of ps aux

  • Use declare -p ar to see the full contents of the array. You might want to redirect that to a file and view it with an editor. Commented Jul 3, 2020 at 9:25
  • Refinement of previous comment. declare -p does not show the exact contents of an array. It show what you would have to type into the shell to create it in the first place. So it includes explicit indexes, quoting and backslash escapes, to conform to shell input rules. Very helpful example, but you need to think what the actual assigned values would be if you intend to process it further. Commented Jul 3, 2020 at 10:54

3 Answers 3


Try (bash4.4+)

readarray -td '' a < <(
  ssh -i id_rsa -T -y [email protected] '
    id;     printf "\0"
    whoami; printf "\0"
    ps aux; printf "\0"'

ssh already invokes a shell (the login shell of that root user there) to interpret the command line. To do that it concatenates the arguments. So what ends up being run in your approach is that shell with -c and sh -c id;whoami;ps aux as arguments, and that shell will then run sh -c id, whoami and ps aux. The sh -c is superfluous here and can only cause confusion.

Beware the array elements will contain the full output of each command including the trailing newline character.

If you want to print them, use:

printf %s "$id_output"

To remove the newline character, you can do:


Your command is storing each whitespace-separated string of the ssh command in an array. So, since you are sshing and then running id, whoami and ps aux, all of their output is added to the array, splitting on whitespace (the default value of the $IFS variable). You can see this with declare -p ar:

$ ar=($( ssh localhost sh -c "id;whoami;ps aux")) 
$ declare -p ar | head -c500
declare -a ar=([0]="uid=1000(terdon)" [1]="gid=1000(terdon)" [2]="groups=1000(terdon),3(sys),7(lp),10(wheel),14(uucp),56(bumblebee),84(avahi),96(scanner),209(cups),995(plugdev)" [3]="terdon" [4]="USER" [5]="PID" [6]="%CPU" [7]="%MEM" [8]="VSZ" [9]="RSS" [10]="TTY" [11]="STAT" [12]="START" [13]="TIME" [14]="COMMAND" [15]="root" [16]="1" [17]="0.0" [18]="0.0" [19]="174456" [20]="11996" [21]="a" [22]="b" [23]="f" [24]="R" [25]="Ss" [26]="Jun23" [27]="7:06" [28]="/sbin/init" [29]="root" [30]="2" 

As you can see there, each whitespace-delimited string of the output of each of the commands run is stored in its own array element.

If you want to have an array with only three elements, one per command, you need to use a different character to split on. One way to do this is to edit your commands so that they print a unique character after executing and then use mapfile to read the array, telling it to split on that unique character. For example, \0:

$ mapfile -d '' < <( ssh localhost sh -c "id; printf '\0'; whoami; printf '\0'; ps aux") ar

$ for i in 0 1 2; do echo "$i: ${ar[i]}"; done | head
0: uid=1000(terdon) gid=1000(terdon) groups=1000(terdon),3(sys),7(lp),10(wheel),14(uucp),56(bumblebee),84(avahi),96(scanner),209(cups),995(plugdev)

1: terdon

root           1  0.0  0.0 174456 11996 ?        Ss   Jun23   7:07 /sbin/init
root           2  0.0  0.0      0     0 ?        S    Jun23   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<   Jun23   0:00 [rcu_gp]
root           4  0.0  0.0      0     0 ?        I<   Jun23   0:00 [rcu_par_gp]
root           6  0.0  0.0      0     0 ?        I<   Jun23   0:00 [kworker/0:0H-kblockd]
  • It's not storing each line, it's storing the strings resulting of split+glob on ssh's output stripped of the trailing newlines. So with the default value of $IFS, that's the SPC/TAB/NL separated words, potentially expanded to filenames on the local system if any of those have wildcards (and they will likely have wildcards, see those [...] in your sample output for instance). Having said that given the output of the OP's echo ${a[2]}, it seems they have set IFS=$'\n' which would mean split+glob would give you filename expansion of each non-empty line. Commented Jul 3, 2020 at 10:06
  • @StéphaneChazelas thanks, I know. I thought I explain all that in the rest of the answer. I just forgot to correct that first line before posting.
    – terdon
    Commented Jul 3, 2020 at 10:12
  • I don't understand that. Clearly, the op shows that ${ar[2]} is the first line of the ps output, and contains whitespace, so IFS must be set properly. But OP shows the line remains correctly multi-spaced, even though the echo argument is not quoted. Commented Jul 3, 2020 at 10:49
  • @Paul_Pedant I am assuming the OP is setting IFS to \n as Stéphane suggested. I am also assuming they aren't showing us the exact commands since those wouldn't produce this output, as you say. Probably best to ask the OP though, not me.
    – terdon
    Commented Jul 3, 2020 at 10:59
  • It seems to me that all that's missing from the question to get that output is that IFS=$'\n'.
    – ilkkachu
    Commented Jul 3, 2020 at 19:31

The expensive part of ssh is the authentication process. I would use the ControlMaster option to allow several separate ssh sessions to share a single authenticated connection.

ssh_get () {
  ssh -o ControlMaster=auto -o ControlPersist=5 -i id_rsa -T -y [email protected] "$@"

for cmd in id whoami "ps aux"; do
  ar+=("$(ssh_get "$cmd")")

The first call will do the authentication, but after it exits, the ssh connection will remain in the background for up to 5 seconds (per the ControlPersist option), allowing the next call to use it without reauthenticating. The overhead of running ssh itself is minimal.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .