I want to use capistrano for a custom sets of tasks on a remote server not directly related to deployment, it would be useful for me if I can start vim using capistrano, I've tried with this:
set :pty, true
execute "vim #{shared_path}/my_file.txt"
But I receive this (for obvious reasons)
01 stdin: is not a tty
01 Vim: Warning: Output is not to a terminal
01 Vim: Warning: Input is not from a terminal
It's there anyway to make it work?
As far as I know you can't start vim without a terminal. You could start a terminal with vim in it, here are a few ways to do this:
Start a terminal like st, xterm or similar. Examples:
x-terminal-emulator -e vim
st -e vim
xterm -e vim
This solution is not the best, as terminal-emulators can have different switches for executing commands on call. -e is working for st and xterm.
A better solution is to start a shell like zsh, bash or similar, because almost every shell works with the same switch, which is -c to start a program directly in it. Example:
zsh -c vim
bash -c vim
Related
I have a script where I need to start a command, then pass some additional commands as commands to that command. I tried
su
echo I should be root now:
who am I
exit
echo done.
... but it doesn't work: The su succeeds, but then the command prompt is just staring at me. If I type exit at the prompt, the echo and who am i etc start executing! And the echo done. doesn't get executed at all.
Similarly, I need for this to work over ssh:
ssh remotehost
# this should run under my account on remotehost
su
## this should run as root on remotehost
whoami
exit
## back
exit
# back
How do I solve this?
I am looking for answers which solve this in a general fashion, and which are not specific to su or ssh in particular. The intent is for this question to become a canonical for this particular pattern.
Adding to tripleee's answer:
It is important to remember that the section of the script formatted as a here-document for another shell is executed in a different shell with its own environment (and maybe even on a different machine).
If that block of your script contains parameter expansion, command substitution, and/or arithmetic expansion, then you must use the here-document facility of the shell slightly differently, depending on where you want those expansions to be performed.
1. All expansions must be performed within the scope of the parent shell.
Then the delimiter of the here document must be unquoted.
command <<DELIMITER
...
DELIMITER
Example:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<END
a=1
mylogin=$(whoami)
echo a=$a
echo mylogin=$mylogin
END
echo a=$a
echo mylogin=$mylogin
Output:
a=0
mylogin=leon
a=0
mylogin=leon
2. All expansions must be performed within the scope of the child shell.
Then the delimiter of the here document must be quoted.
command <<'DELIMITER'
...
DELIMITER
Example:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<'END'
a=1
mylogin=$(whoami)
echo a=$a
echo mylogin=$mylogin
END
echo a=$a
echo mylogin=$mylogin
Output:
a=1
mylogin=root
a=0
mylogin=leon
3. Some expansions must be performed in the child shell, some - in the parent.
Then the delimiter of the here document must be unquoted and you must escape those expansion expressions that must be performed in the child shell.
Example:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<END
a=1
mylogin=\$(whoami)
echo a=$a
echo mylogin=\$mylogin
END
echo a=$a
echo mylogin=$mylogin
Output:
a=0
mylogin=root
a=0
mylogin=leon
A shell script is a sequence of commands. The shell will read the script file, and execute those commands one after the other.
In the usual case, there are no surprises here; but a frequent beginner error is assuming that some commands will take over from the shell, and start executing the following commands in the script file instead of the shell which is currently running this script. But that's not how it works.
Basically, scripts work exactly like interactive commands, but how exactly they work needs to be properly understood. Interactively, the shell reads a command (from standard input), runs that command (with input from standard input), and when it's done, it reads another command (from standard input).
Now, when executing a script, standard input is still the terminal (unless you used a redirection) but the commands are read from the script file, not from standard input. (The opposite would be very cumbersome indeed - any read would consume the next line of the script, cat would slurp all the rest of the script, and there would be no way to interact with it!) The script file only contains commands for the shell instance which executes it (though you can of course still use a here document etc to embed inputs as command arguments).
In other words, these "misunderstood" commands (su, ssh, sh, sudo, bash etc) when run alone (without arguments) will start an interactive shell, and in an interactive session, that's obviously fine; but when run from a script, that's very often not what you want.
All of these commands have ways to accept commands by ways other than in an interactive terminal session. Typically, each command supports a way to pass it commands as options or arguments:
su root -c 'who am i'
ssh user#remote uname -a
sh -c 'who am i; echo success'
Many of these commands will also accept commands on standard input:
printf 'uname -a; who am i; uptime' | su
printf 'uname -a; who am i; uptime' | ssh user#remote
printf 'uname -a; who am i; uptime' | sh
which also conveniently allows you to use here documents:
ssh user#remote <<'____HERE'
uname -a
who am i
uptime
____HERE
sh <<'____HERE'
uname -a
who am i
uptime
____HERE
For commands which accept a single command argument, that command can be sh or bash with multiple commands:
sudo sh -c 'uname -a; who am i; uptime'
As an aside, you generally don't need an explicit exit because the command will terminate anyway when it has executed the script (sequence of commands) you passed in for execution.
If you want a generic solution which will work for any kind of program, you can use the expect command.
Extract from the manual page:
Expect is a program that "talks" to other interactive programs according to a script. Following the script, Expect knows what can be expected from a program and what the correct response should be. An interpreted language provides branching and high-level control structures to direct the dialogue. In addition, the user can take control and interact directly when desired, afterward returning control to the script.
Here is a working example using expect:
set timeout 60
spawn sudo su -
expect "*?assword" { send "*secretpassword*\r" }
send_user "I should be root now:"
expect "#" { send "whoami\r" }
expect "#" { send "exit\r" }
send_user "Done.\n"
exit
The script can then be launched with a simple command:
$ expect -f custom.script
You can view a full example in the following page: http://www.journaldev.com/1405/expect-script-example-for-ssh-and-su-login-and-running-commands
Note: The answer proposed by #tripleee would only work if standard input could be read once at the start of the command, or if a tty had been allocated, and won't work for any interactive program.
Example of errors if you use a pipe
echo "su whoami" |ssh remotehost
--> su: must be run from a terminal
echo "sudo whoami" |ssh remotehost
--> sudo: no tty present and no askpass program specified
In SSH, you might force a TTY allocation with multiple -t parameters, but when sudo will ask for the password, it will fail.
Without the use of a program like expect any call to a function/program which might get information from stdin will make the next command fail:
ssh use#host <<'____HERE'
echo "Enter your name:"
read name
echo "ok."
____HERE
--> The `echo "ok."` string will be passed to the "read" command
I have an little shell script (named "run") which redirects all output of a program to /dev/null:
#!/bin/bash
$# &> /dev/null &
disown +
How can I say zsh that the whole autocompletion shall work for this?
I mean
$ run git com<TAB>
autocomplete to
$ run git commit
I was able to make that work by adding:
compdef _command run
to my .zshrc file.
I've based my answer on this bash question. It was worth giving it a try with compdef - surprisingly it worked.
As I'm still zsh/autocompletion newbie I cannot explain the inner workings and you should probably go through the documentation or other sources to find more on the topic.
I am trying to use Emacs Client. I am on OSX. so:
alias ec="/Applications/Emacs.app/Contents/MacOS/bin/emacsclient -n -c -a -nw /Applications/Emacs.app/Contents/MacOS/Emacs"
Then I put (start-server) on my init.el file. A\and:
e --daemon
So when I run ec. And a clean Emacs instance is fired up.
Whats is wrong here?
Thanks in advance
cat "${pos}" | /usr/bin/iconv -f CP1251 -t UTF-8 | uniq | sed -En "/^CLIENT_ID.*/!p" | while read line
do
.....
......
cat >> "$TMPFILE" << EOF
INSERT INTO ......;
EOF
done
As you can see each iteration writes a SQL statement to a tmp-file.
I launched this script from a regular interactive shell and got the expected output. Launched from a cron job - nothing.
After investigating I found a problem. When I use "$TMPFILE" without "" the script works ok. Why does this happen?
OS: FreeBSD, bourne shell.
IIRC, cron doesn't source all the files that a login shell does, so you will end up with different settings for environment variables. Could be the path $TMPFILE is pointing to contains spaces when run from cron for example.
Also, on some systems (depending on setup), cron uses a different shell. So if you start your script from command line, for example /usr/bin/sh might be used, whereas when started by cron, /bin/sh is used. (I have no experience with *BSD, but I have observed this on linux.)
It is so confusing that emacsclient said it can't find socket just after executing emacs --daemon in bash:
$ ps aux | grep emacs
shiangro 1744 0.0 0.0 2432784 604 s000 S+ 1:03下午 0:00.00 grep emacs
$ /usr/local/bin/emacs --daemon
("emacs")
Starting Emacs daemon.
Restarting server
$ /usr/local/bin/emacsclient -t
emacsclient: can't find socket; have you started the server?
To start the server in Emacs, type "M-x server-start".
emacsclient: No socket or alternate editor. Please use:
--socket-name
--server-file (or environment variable EMACS_SERVER_FILE)
--alternate-editor (or environment variable ALTERNATE_EDITOR)
I have this settings in my .emacs:
(server-start)
(setq server-socket-dir "~/.emacs.d/server")
and it works,the server file ~/.emacs.d/server/server was just there,but emacsclient say it can't find socket,so annoying that I have to tell him the socket file using the -s option.
I find this thorny problem while I want let emacs runing as a daemon after everytime rebooting(start) systerm by using crontab's ◎reboot special strings.
In this case ,cron successfully started the emacs server and the server file ~/.emacs.d/server/server was also there, but later when I started a terminal and tried to emacsclient -t ,it failed and complained can't find socket file!
Although I can bypass this problem by using -s ~/.emacs.d/server/server everytime I excute emacsclient,or alias emacsclient as emacsclient -s ~/.emacs.d/server/server ,but is ther a better way to comfort my heart?
Backgroud:
system: Mac OS X 10.9.2
emacs: GNU Emacs 24.3.1 installed by homebrew
Finding the server socket file is the tricky bit, you can use lsof to find it, and then a bit of grep-ing to extract the socket path/filename.
lsof -c emacs | grep server | grep -E -o '[^[:blank:]]*$'
Or on OSX when you expect to be running /Application/Emacs you'd change the command name lsof is looking for with -c Emacs. ie.
lsof -c Emacs | grep server | grep -E -o '[^[:blank:]]*$'
You could use cut instead of the messy filtering grep (searching for non-blanks until the line end [^[:blank:]]*$)
lsof -c Emacs | grep server | cut -c70-
Better yet, squish the interspacing and use cut's field chopping.
lsof -c Emacs | grep server | tr -s " " | cut -d' ' -f8
Now that you have the socket (or it's empty) you can do a conditional start on emacsclient, ie.
#!/bin/bash
socket_file=$(lsof -c Emacs | grep server | tr -s " " | cut -d' ' -f8)
if [[ $socket_file == "" ]]; then
# Just run Emacs (with any arguments passed to the script)
# It would be a good idea to parse the arguments and clean/remove
# anything emacsclient specific.
# (ie. -e should be --eval for emacs)
# note that emacsclient doesn't fix these args for you either
# when using -a / --alternate-editor
emacs $# &
# or on OSX
/Application/Emacs.app/Contents/MacOS/Emacs $# &
else
emacsclient $# -n -s $socket_file
fi
Since you've done:
/usr/local/bin/emacs --daemon
the server is already started. So, you don't actually need the:
(server-start)
(setq server-socket-dir "~/.emacs.d/server")
in your .emacs. When you follow that approach, the server is placed in /tmp/emacs502 (or maybe some other number). On linux, emacsclient doesn't seem to have trouble finding it there (in that case I'm seeing /tmp/emacs1920), and so "emacsclient -nw" works. I'm trying it on OSX using HomeBrew, as you are, and I find I have to connect using:
emacsclient -nw -s /tmp/emacs502/server
(If you used --deamon=name, then you would use "name" instead of "server" in that last line.)
emacsclient only finds the emacs server if I run emacs from the command line. If I run emacs from the Ubuntu launcher then emacsclient fails to connect to the server.
If you want to use the Emacs daemon instead of the server, define the two environment variables
export ALTERNATE_EDITOR=""
export EDITOR=emacsclient
You can add these environment variables in either ~/.bashrc or ~/.profile.
If the ALTERNATE_EDITOR environment variable is empty, then Emacs will run its daemon and connect to it.
I think emacsclient can look for special file server in standard path only, e.g. in /tmp/emacs1000. If you change this parameter server-socket-dir, then you should tell about it to emacsclient by key -s.