How to autocomplete or use abbreviations in bash command - bash

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.

Related

Concatenating a local file with a remote one

These three lines of code require authentication twice. I don't yet have password-less authentication set up on this server. In fact, these lines of code are to copy my public key to the server and concatenate it with the existing file.
How can I re-write this process with a single ssh command that requires authentication only once?
scp ~/local.txt user#server.com:~/remote.txt
ssh -l user user#server.com
cat ~/remote.txt >> ~/otherRemote.txt
I've looked into the following possibilities:
command sed
operator ||
operator &&
shared session: Can I use an existing SSH connection and execute SCP over that tunnel without re-authenticating?
I also considered placing local.txt at an openly accessible location, for example, with a public dropbox link. Then if cat could accept this as an input, the scp line wouldn't be necessary. But this would also require an additional step and wouldn't work in cases where local.txt cannot be made public.
Other references:
Using a variable's value as password for scp, ssh etc. instead of prompting for user input every time
https://superuser.com/questions/400714/how-to-remotely-write-to-a-file-using-ssh
You can redirect the content to the remote, and then use commands on the remote to do something with it. Like this:
ssh user#server.com 'cat >> otherRemote.txt' < ~/local.txt
The remote cat command will receive as its input the content of ~/local.txt, passed to the ssh command by input redirection.
Btw, as #Barmar pointed out, specifying the username with both -l user and user# was also redundant in your example.

Bash script to ssh to particular server

I'm wondering how I would go about creating my own bash script to ssh to a server. I know it's lazy, but I would ideally want not to have to type out:
ssh username#server
And just have my own two letter command instead (i.e. no file extension, and executable from any directory).
Any help would be much appreciated. If it helps with specifying file paths etc, I am using Mac OS X.
You can set configs for ssh in file ~/.ssh/config:
Host dev
HostName mydom.example.com
User myname
Then, just type
$> ssh dev
And you're done. Also, you can add your public key to the file ~/.ssh/authorized_keys so you won't get prompted for your password every time you want to connect via ssh.
Use an alias.
For example: alias sv='ssh user#hostname', then you can simply type sv.
Be sure to put a copy of the aliases in your profile, otherwise they will disappear at the end of your session.
you could create an alias like this:
alias ss="ssh username#server" and write it into your .bash_profile. ".bash_profile" is a hidden file is located in your home directory. If .bash_profile doesn't exist yet (check by typing ls -a in your home directory), you can create it yourself.
The bash_profile file will be read and executed every time you open a new shell.
You can use ssh-argv0 to avoid typing ssh.
To do this, you need to create a link to ssh-argv0 with the name of the host you want to connect, including the user if needed. Then you can execute that link, and ssh will connect you to the host of the link name.
Example
Setup the link:
ln -s /usr/bin/ssh-argv0 ~/bin/my-server
/usr/bin/ssh-argv0 is the path of ssh-argv0 on my system, yours could be different, check with which ssh-argv0
I have put it in ~/bin/ to be able to execute it from any directory (in OS X you may need to add ~/bin/ manually to your path on .bash_profile)
my-server is the name of my server, and if needed to set the user, it would be user#my-server
Execute it:
my-server
Even more
You can also combine this with mogeb answer to configure your server connection, so that you can call it with a shorter name, and avoid to include the user even if it is different than on the local system.
Host serv
HostName my-server
User my-user
Port 22
then set a link to ssh-argv0 with the name serv, and connect to it with
serv

How to automate password entry?

I want to install a software library (SWIG) on a list of computers (Jenkins nodes). I'm using the following script to automate this somewhat:
NODES="10.8.255.70 10.8.255.85 10.8.255.88 10.8.255.86 10.8.255.65 10.8.255.64 10.8.255.97 10.8.255.69"
for node in $NODES; do
scp InstallSWIG.sh root#$node:/root/InstallSWIG.sh
ssh root#$node sh InstallSWIG.sh
done
This way it's automated, except for the password request that occur for both the scp and ssh commands.
Is there a way to enter the passwords programmatically?
Security is not an issue. I’m looking for solutions that don’t involve SSH keys.
Here’s an expect example that sshs in to Stripe’s Capture The Flag server and enters the password automatically.
expect <<< 'spawn ssh level01#ctf.stri.pe; expect "password:"; send "e9gx26YEb2\r";'
With SSH the right way to do it is to use keys instead.
# ssh-keygen
and then copy the *~/.ssh/id_rsa.pub* file to the remote machine (root#$node) into the remote user's .ssh/authorized_keys file.
You can perform the task using empty, a small utility from sourceforge. It's similar to expect but probably more convenient in this case. Once you have installed it, your first scp will be accomplished by following two commands:
./empty -f scp InstallSWIG.sh root#$node:/root/InstallSWIG.sh
echo YOUR_SECRET_PASSWORD | ./empty -s -c
The first one starts your command in the background, tricking it into thinking it's running in interactive mode on a terminal. The other one sends it data from stdin. Of course, putting your password anywhere on command line is risky due to shell history being preserved, users being able to see it in ps results etc. Not secure either, but a bit better thing would be to store the password in a file and redirect the second command's input from that file instead of using echo and a pipe.
After copying to the server, you can run the script in a similar manner:
./empty -f ssh root#$node sh InstallSWIG.sh
echo YOUR_SECRET_PASSWORD | ./empty -s -c
You could look into setting up passwordless ssh keys for that. Establishing Batch Mode Connections between OpenSSH and SSH2 is a starting point, you'll find lots of information on this topic on the web.
Wes' answer is the correct one but if you're keen on something dirty and slow, you can use expect to automate this.

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

How do you use ssh in a shell script?

When I try to use an ssh command in a shell script, the command just sits there. Do you have an example of how to use ssh in a shell script?
Depends on what you want to do, and how you use it. If you just want to execute a command remotely and safely on another machine, just use
ssh user#host command
for example
ssh user#host ls
In order to do this safely you need to either ask the user for the password during runtime, or set up keys on the remote host.
First, you need to make sure you've set up password-less (public key login). There are at least two flavors of ssh with slightly different configuration file formats. Check the ssh manpage on your system, consult you local sysadmin or head over to How do I setup Public-Key Authentication?.
To run ssh in batch mode (such as within a shell script), you need to pass a command you want to be run. The syntax is:
ssh host command
If you want to run more than one command at the same time, use quotes and semicolons:
ssh host "command1; command2"
The quotes are needed to protect the semicolons from the shell interpreter. If you left them out, only the first command would be run remotely and all the rest would be run on the local machine.
You need to put your SSH public key into the ~/.ssh/authorized_keys file on the remote host. Then you'll be able to SSH to that host password-less.
Alternatively you can use ssh-agent. I would recommend against storing the password in the script.
You can use expect command to populate the username/password info.
The easiest way is using a certificate for the user that runs the script.
A more complex one implies adding to stdin the password when the shell command asks for it. Expect, perl libraries, show to the user the prompt asking the password (if is interactive, at least), there are a lot of choices.

Resources