3

I define a function in .bash_aliases file and include it in my .bashrc file.

My .bash_aliases file:

function dmidecode() {
        if [[ $1 == -t && $2 == 1 ]]; then
                cat ~/some_file
        else
                command dmidecode "$@"
        fi
}

The function is: when someone executes the command: dmidecode -t 1, bash reads the content from ~/some_file and return.

My .bashrc file:

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color|*-256color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
        # We have color support; assume it's compliant with Ecma-48
        # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
        # a case would tend to support setf rather than setaf.)
        color_prompt=yes
    else
        color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi

# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'

# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# Add an "alert" alias for long running commands.  Use like so:
#   sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

It works when I interactively execute the command by typing the command in the bash shell. However, it doesn't work when I tried to execute it remotely. It said dmidecode command not found (I didn't install dmidecode tool but I write a function to point it to somewhere.). The remote connection is good because I can remotely execute commands like "ls" or "pwd". It just doesn't work for the functions that I wrote.

And I tried to put shopt -s expand_aliases in the .bashrc file. It doesn't solve the problem also.

FYI, here is my test code for remote command execution:

from paramiko import client

class ssh:
    client = None

    def __init__(self, address, username, password, port=22):
        print("Connecting to server.")
        self.client = client.SSHClient()
        self.client.set_missing_host_key_policy(client.AutoAddPolicy())
        self.client.connect(address, port, username=username, password=password, look_for_keys=False)

    def sendCommand(self, command):
        if(self.client):
            stdin, stdout, stderr = self.client.exec_command(command)
            while not stdout.channel.exit_status_ready():
                # Print data when available
                if stdout.channel.recv_ready():
                    alldata = stdout.channel.recv(1024)
                    prevdata = b"1"
                    while prevdata:
                        prevdata = stdout.channel.recv(1024)
                        alldata += prevdata

                    print(str(alldata, "utf8"))
        else:
            print("Connection not opened.")

def main():
    connection = ssh("my_ip", "username", "password", 22)
    connection.sendCommand("dmidecode -t 1") # this doesn't work, "dmidecode command not found"
    connection.sendCommand("pwd") # this works


if __name__ == "__main__":
    main()

Any idea on this?

3
  • Progress: In Python's paramiko, the exec_command() behaves equivalent to ssh host_ip command which is a non-interactive shell. Commented Oct 5, 2018 at 18:50
  • Well the way I see it, the .bashrc starts by verifying if the shell is interactive or not, and if not do nothing. My understanding is that anything after that won't even be seen/execute. Then again, you might want to send this question in serverfault.com . Commented Oct 5, 2018 at 18:51
  • Functions defined locally aren't transferred to the remote host over the SSH connection. Commented Oct 5, 2018 at 18:59

1 Answer 1

5

Your title says it all, actually. When you do ssh login@remote 'my command', your shell is NOT interactive, by definition, since you provide a command. However, ~/.bashrc is only sourced by bash when you shell is interactive.

When you execute ssh login@remote 'my command', here is what happens:

  • your machine connects first to "remote" with user "login"
  • then sshd, the ssh server running on "remote", executes your shell with the parameters -c 'my command' (i.e. bash -c 'my command' since you are using bash)
  • since it is called with -c, bash executes directly your command without reading your startup files

The solution? Source your startup file before executing your command:

ssh login@remote 'source ~/.bash_aliases; my command'
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your answer. However, the problem still exists. I tried ssh login@remote 'source ~/.bashrc; my command' and it still said the command is not found.
@DiLuo I wrote source ~/.bash_aliases. That's where your function is defined. There is nothing in your bashrc that needs to be read in a non-interactive shell. Worse: there is a command at the beginning that prevents its execution (see the comment "If not running interactively, don't do anything")
You save my life man. This .bashrc file is not written by me but comes with the system. Thanks a lot and your solution of sourcing the .bash_aliases before executing the command is correct! Thanks!
You're welcome. It's really better to keep ~/.bashrc as is, without removing the "exit if not interactive" command, and populate .bash_aliases instead. If you have a lot of definitions in .bash_aliases, to speed up you remote command, you may also want to move the functions you need remotely to a special file (e.g.) ~/.ssh_functions and source that file in .bash_aliases. Your ssh command would then become ssh login@remote 'source ~/.ssh_functions; my command'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.