bash variable expansion for scp tab completion - bash

I use ssh at work and recently switched to Linux on my local work machine. As a result, I now have to fully-qualify the hostname (i.e. instead of dev1 I need to use dev1.megacorpnetworkdomain.com). This gets rather annoying for scp. I already have auto-completion set up for remote paths. What I tried to do is create a variable alias for the path:
WORK='user#dev1.megacorpnetworkdomain.com:/home/user'
The problem is now scp fails to do remote completion when I do the following:
scp $WORK/<tab>
Whereas the following performs remote completion correctly:
scp user#dev1.megacorpnetworkdomain.com:/home/user/<tab>
I tried to naively work around this by using complete:
_scp_expand_vars () {
COMPREPLY=()
local cur=${COMP_WORDS[COMP_CWORD]}
eval cur=$cur
COMPREPLY=( $cur )
return 0
}
complete -F _scp_expand_vars scp
This now correctly expands variables for scp, but breaks the original functionality of remote directory expansion. How could I achieve both? I'm assuming the original functionality is handled by _scp_remote_files function based on a bit of online research?

For one, if you regularly access hosts under megacorpnetworkdomain.com, you should put it in /etc/resolv.conf; there should be a line:
search megacorpnetworkdomain.com
Then you can use all hosts by just referencing the short hostname, i.e. dev1 in any program.
For ssh you can follow the advice of Etan Reiser, an example looks like this:
Host dev1
HostName dev1.megacorpnetworkdomain.com
User user
When you use ssh or scp they will offer you the choices defined there with tab completion as usual.

Related

Run a unix shell script present on another server from a differnt server

I have server 1 where shell script1 is placed. I want to execute shell script 2 present on another server from the shell script1 itself which is on server 1.
This is what I am trying to do.
ftp -nv $hostname << END
quote user $UID
quote pass $pwd
binary
put $source_file
quit
END
rc=$?
echo "Return Code:$rc"
if [ $rc == 0 ]
then
**execute My_port script (present on remote server) which works on the ftpd file and do something**.
else
exit 1
fi
hope I make the requirement clear.
Does anyone have any idea on that?
To run any program remotely, you need (on the remote machine) some server program capable of that (and on the local machine, some client program compatible with it).
Usually, you'll use ssh (read its man page ssh(1)). Perhaps you'll have some openssh server. You'll find many SSH tutorials on the web (you probably want to use a public key to avoid needing to type a password).
There is also rsh, but without encryption so insecure. You probably don't want it, and it would be foolish to use it on anything else than a trusted local network.
So your shell script could contain something like ssh remotehost My_port script arguments...
Be also aware of the PATH variable (it is different on the local machine and on the remote ones, in general). It does matter, notably its remote settings, for commands (and remote commands). You might need to give the full path of scripts (e.g. ssh remotehost /home/remoteuser/bin/myport.sh ...) If the remote PATH setting don't have the directory containing the script. You could use ssh remotehost 'echo $PATH' to query the remote PATH setting.

How to make a bash function to run a bidirectional rsync

I have a local folder and a remote one on a server with ssh connection. I don't have admin privileges so installing new packages are not possible to use unison for example. I have to sync these two folders quite often and they are also big. From here I know that to sync in both sides I have to rsync twice. once from server to local:
rsync -Przzuve ssh user#server:/path/to/remote/folder/* /path/to/local/folder
and then the other way around, from local to server
rsync -Przzuve ssh /path/to/local/folder/* user#server:/path/to/remote/folder
What I want to have is a single command like:
rsyncb /path/to/local/folder user#server:/path/to/remote/folder
To just sync the content of two folders in both directions in one command without worrying about the -* options and /* at the end of the first path...
I found this about making a bash function with given arguments but I do not understand how to implement what I want. I would appreciate if you could help me with this.
Just define a function.
rsyncb() {
rsync -Przzuve ssh "$1"/* "$2"
rsync -Przzuve ssh "$2"/* "$1"
}
Then use it like this:
rsyncb user#server:/path/to/remote/dir /path/to/local/dir

How to autocomplete or use abbreviations in bash command

Everyday I have to transfer files to many (6) AWS EC2 instances via SCP, from my bash terminal, on Linux Debian 8. I have to hand write:
i) files to transfer;
ii) destination folder;
iii) url to my .pem certificate;
iv) openning urls on the servers: ec2-user#XX.XX.XXX....
As a lazy programmer I want to use some kind of abbreviation in order to type less, having in mind that iii) and iv) are the same generally.
First I wrote a bash script which asks me for files to be transferred and folder on my server, however it takes more time due to the fact that prompts for an answer every time I transfer. Instead, I can just use arrow to repeat the bash command previously introduced.
Here is an example of the current command I use to run:
scp -i /pem/aws.pem file_to_upload.txt ec2-user#XX-XX-XX-XX.amazonwas.com:/var/www/html/folder/
the following strings require repeated typing:
scp -i /pem/aws.pem
ec2-user#..... // this is a large string of 64 characters.
I'd love some sort of placeholder, where I just type the path to files to upload and the remote folder.
How can I autocomplete or use abbreviations, like VIM to insert the same recurrent text in a bash command?
All the solutions already posted work, but they are really ugly and not the way how it should be. Remember, we still have ~/.ssh/config?
scp -i /pem/aws.pem file_to_upload.txt ec2-user#XX-XX-XX-XX.amazonwas.com:/var/www/html/folder/
will boil down to
scp file_to_upload.txt am:/var/www/html/folder/
if you set up your ~/.ssh/config:
Host am:
Hostname XX-XX-XX-XX.amazonwas.com
User ec2-user
IdentityFile /pem/aws.pem
There your local file gets auto-completed and remote directory also if you don't use passphrase (or set up ControlMaster and ControlPersist options - this will even be fast!).
Sounds like you could take advantage of using some bash aliases in your bashrc or bash_profile.
Take a look at my .bashrc file on githut
I often need to know what is my ip address. One example of a bash alias is my showip alias.
alias showip='ifconfig | grep "inet" | grep -v 127.0.0.1'
I have also created aliases to ssh into my Linux boxes. You could create a bash alias that should solve your problem.
Copying directory recursively from host:
scp -r user#host:/directory/SourceFolder TargetFolder
NOTE: If the host is using a port other than port 22, you can specify it with the -P option:
scp -P 2222 user#host:/directory/SourceFile TargetFile
When you are connecting to your bash terminal with a ssh program, see what keyboard mappings your ssh supports.
Other options, based on
echo "This is a line I do not want to type twice"
Look how you can use the bash history
!!
^twice^two times^
You can put your abbreviations in shell variables
no2="This is a line I do not want to type twice"
echo "${no2}"
And you can use an alias
my2='echo "This is a line I do not want to type twice"'
my2
The shell variables and aliases can be put in ${HOME}/.bashrc.

Get the local user during ssh session

Is it possible to display the local user on remote host?
eg.:
local_user#ownpc$ ssh remote_user#server
then
echo $some_variable_containing_local_user
that results local_user
Purpose: I have a very limited read access to a UNIX server, i'd like to perform scp from remote to local over the current ssh session. Performed via a shell script, regardless of who logged in. (without any additional installs or local variable modifications)
Ps.: I am not looking for scp ssh:user#host/folder/file /home/user/folder
Thank you!
Using an unquoted here-doc allows local variables to be expanded before passing the commands to ssh
local_user#ownpc$ ssh remote_user#server <<END
echo you are $USER
END
should output
you are local_user
That may give you a basis to script your remote actions in the here-doc.
I'm not 100% clear on your situation but both of these methods work for me:
ssh remote_user#remote_host whoami
and
ssh remote_user#remote_host 'echo $USER'
The first executes "whoami" on the remote host while the second echos out what $USER is set to in the remote shell. I'm a little surprised the second one works because often ssh remote executing does not start a shell but it appears to work anyway.

How can I ssh directly to a particular directory?

I often have to login to one of several servers and go to one of several directories on those machines. Currently I do something of this sort:
localhost ~]$ ssh somehost
Welcome to somehost!
somehost ~]$ cd /some/directory/somewhere/named/Foo
somehost Foo]$
I have scripts that can determine which host and which directory I need to get into but I cannot figure out a way to do this:
localhost ~]$ go_to_dir Foo
Welcome to somehost!
somehost Foo]$
Is there an easy, clever or any way to do this?
You can do the following:
ssh -t xxx.xxx.xxx.xxx "cd /directory_wanted ; bash --login"
This way, you will get a login shell right on the directory_wanted.
Explanation
-t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services.
Multiple -t options force tty allocation, even if ssh has no local tty.
If you don't use -t then no prompt will appear.
If you don't add ; bash then the connection will get closed and return control to your local machine
If you don't add bash --login then it will not use your configs because its not a login shell
You could add
cd /some/directory/somewhere/named/Foo
to your .bashrc file (or .profile or whatever you call it) at the other host. That way, no matter what you do or where you ssh from, whenever you log onto that server, it will cd to the proper directory for you, and all you have to do is use ssh like normal.
Of curse, rogeriopvl's solution works too, but it's a tad bit more verbose, and you have to remember to do it every time (unless you make an alias) so it seems a bit less "fun".
My preferred approach is using the SSH config file (described below), but there are a few possible solutions depending on your usages.
Command Line Arguments
I think the best answer for this approach is christianbundy's reply to the accepted answer:
ssh -t example.com "cd /foo/bar; exec \$SHELL -l"
Using double quotes will allow you to use variables from your local machine, unless they are escaped (as $SHELL is here). Alternatively, you can use single quotes, and all of the variables you use will be the ones from the target machine:
ssh -t example.com 'cd /foo/bar; exec $SHELL -l'
Bash Function
You can simplify the command by wrapping it in a bash function. Let's say you just want to type this:
sshcd example.com /foo/bar
You can make this work by adding this to your ~/.bashrc:
sshcd () { ssh -t "$1" "cd \"$2\"; exec \$SHELL -l"; }
If you are using a variable that exists on the remote machine for the directory, be sure to escape it or put it in single quotes. For example, this will cd to the directory that is stored in the JBOSS_HOME variable on the remote machine:
sshcd example.com \$JBOSS_HOME
SSH Config File
If you'd like to see this behavior all the time for specific (or any) hosts with the normal ssh command without having to use extra command line arguments, you can set the RequestTTY and RemoteCommand options in your ssh config file.
For example, I'd like to type only this command:
ssh qaapps18
but want it to always behave like this command:
ssh -t qaapps18 'cd $JBOSS_HOME; exec $SHELL'
So I added this to my ~/.ssh/config file:
Host *apps*
RequestTTY yes
RemoteCommand cd $JBOSS_HOME; exec $SHELL
Now this rule applies to any host with "apps" in its hostname.
For more information, see http://man7.org/linux/man-pages/man5/ssh_config.5.html
I've created a tool to SSH and CD into a server consecutively – aptly named sshcd. For the example you've given, you'd simply use:
sshcd somehost:/some/directory/somewhere/named/Foo
Let me know if you have any questions or problems!
Based on additions to #rogeriopvl's answer, I suggest the following:
ssh -t xxx.xxx.xxx.xxx "cd /directory_wanted && bash"
Chaining commands by && will make the next command run only when the previous one was successful (as opposed to using ;, which executes commands sequentially). This is particularly useful when needing to cd to a directory performing the command.
Imagine doing the following:
/home/me$ cd /usr/share/teminal; rm -R *
The directory teminal doesn't exist, which causes you to stay in the home directory and remove all the files in there with the following command.
If you use &&:
/home/me$ cd /usr/share/teminal && rm -R *
The command will fail after not finding the directory.
In my very specific case, I just wanted to execute a command in a remote host, inside a specific directory from a Jenkins slave machine:
ssh myuser#mydomain
cd /home/myuser/somedir
./commandThatMustBeRunInside_somedir
exit
But my machine couldn't perform the ssh (it couldn't allocate a pseudo-tty I suppose) and kept me giving the following error:
Pseudo-terminal will not be allocated because stdin is not a terminal
I could get around this issue passing "cd to dir + my command" as a parameter of the ssh command (to not have to allocate a Pseudo-terminal) and by passing the option -T to explicitly tell to the ssh command that I didn't need pseudo-terminal allocation.
ssh -T myuser#mydomain "cd /home/myuser/somedir; ./commandThatMustBeRunInside_somedir"
I use the environment variable CDPATH
going one step further with the -t idea. I keep a set of scripts calling the one below to go to specific places in my frequently visited hosts. I keep them all in ~/bin and keep that directory in my path.
#!/bin/bash
# does ssh session switching to particular directory
# $1, hostname from config file
# $2, directory to move to after login
# can save this as say 'con' then
# make another script calling this one, e.g.
# con myhost repos/i2c
ssh -t $1 "cd $2; exec \$SHELL --login"
My answer may differ from what you really want, but I write here as may be useful for some people. In my solution you have to enter into the directory once and then every new ssh session goes to the same dir (after the first logout).
How to ssh to the same directory you have been in your last login.
(I assume you use bash on the remote node.)
Add this line to your ~/.bash_logout on the remote node(!):
echo $PWD > ~/.bash_lastpwd
and these lines to the ~/.bashrc file (still on the remote node!)
if [ -f ~/.bash_lastpwd ]; then
cd $(cat ~/.bash_lastpwd)
fi
This way you save your current path on every logout and .bashrc put you into that directory after login.
ps: You can tweak it further like using the SSH_CLIENT variable to decide to go into that directory or not, so you can differentiate between local logins and ssh or even between different ssh clients.
Another way of going to directly after logging in is create "Alias". When you login into your system just type that alias and you will be in that directory.
Example : Alias = myfolder '/var/www/Folder'
After you log in to your system type that alias (this works from any part of the system)
this command if not in bashrc will work for current session. So you can also add this alias to bashrc to use that in future
$ myfolder => takes you to that folder
I know this has been answered ages ago but I found the question while trying to incorporate an ssh login in a bash script and once logged in run a few commands and log back out and continue with the bash script. The simplest way I found which hasnt been mentioned elsewhere because it is so trivial is to do this.
#!/bin/bash
sshpass -p "password" ssh user#server 'cd /path/to/dir;somecommand;someothercommand;exit;'
Connect With User
In case if you don't know this, you can use this to connect by specifying both user and host
ssh -t <user>#<Host domain / IP> "cd /path/to/directory; bash --login"
Example: ssh -t admin#test.com "cd public_html; bash --login"
You can also append the commands to be executed on every login by appending it in the double quotes with a ; before each command
Unfortunately, the suggested solution (of #rogeriopvl) doesn't work when you use multiple hops, so I found another one.
On remote machine add into ~/.bashrc the following:
[ "x$CDTO" != "x" ] && cd $CDTO
This allows you to specify the desired target directory on command line in this way:
ssh -t host1 ssh -t host2 "CDTO=/desired_directory exec bash --login"
Sure, this way can be used for a single hop too.
This solution can be combined with the usefull tip of #redseven for greater flexibilty (if no $CDTO, go to saved directory, if exists).
SSH itself provides a means of communication, it does not know anything about directories. Since you can specify which remote command to execute (this is - by default - your shell), I'd start there.
simply modify your home with the command:
usermod -d /newhome username

Resources