Bindsym fails on a rofi script but bash sh works. How differently does bindsym exec and bash sh work? - i3

So the script itself works quite well but when I try to open it by bindsym then it doesn open and rofi itself just doesnt work anymore and I have to kill it.
bindsym $mod+c exec /home/scripts/gpaste.sh
and nothing happens
Then if I try to run the script get this manually
(process:24274): Helper-WARNING **: 21:59:52.297: Failed to set lock on pidfile: Rofi already running?
(process:24274): Helper-WARNING **: 21:59:52.297: Got error: -1 Socket operation on non-socket
So I need to kill rofi by htop.
If I run the script in terminal I get it work and rofi opens up a correct menu.
The script (that I found and tried)
#!/usr/bin/env bash
line=gpaste-client --oneline | \ rofi -theme Adapta-Nokto -dmenu -i -p gpaste -kb-custom-1 Ctrl+x $#
exit_code=$?
index=echo $line | cut -d ':' -f1
if ! [[ $index =~ ^[0-9]+$ ]]; then
exit 0
fi
if [ $exit_code == 0 ]; then
gpaste-client select $index
elif [ $exit_code == 10 ]; then
gpaste-client delete $index
fi
Why might it be working like this?

Related

Why does autostart script on Linux not work

I got an auto start bash script, to start fluidsyth and aconnect command automatically after rebooting my linux system (RPI-3):
#!/bin/bash
(STOP=$((SECONDS+5))
until [[ $SECONDS -ge $STOP || $(ps -C fluidsynth -o stat=) =~ S ]]; do:; done &&
aconnect 20:0 128:0 &)
fluidsynth -a alsa -g 5 /usr/share/sounds/sf2/FluidR3_GM.sf2
When I run this script, it popped up with the following error:
./piano4.sh: line 4: syntax error near unexpected token `done'
./piano4.sh: line 4: `until [[ $SECONDS -ge $STOP || $(ps -C fluidsynth -o stat=) =~ S ]]; do:; done &&'
Till now I cannot solve this error. There are no ^M in the script (I already checked).
Can anyone help me to get it up and running?
Thanks
There are no ^M in the script (I already checked)
I found the answer by changing the script into:
#!/bin/bash (STOP=$((SECONDS+15)) until [[ $SECONDS -ge $STOP || $(ps -C fluidsynth -o stat=) =~ S ]]; do echo "" > /dev/null; done && aconnect 20:0 128:0 &) fluidsynth -a alsa -g 5 /usr/share/sounds/sf2/FluidR3_GM.sf2
Thanks

systemd service works different then running the .sh file from user shell trying run a VNC startup script with noVNC

I want to run a .sh file with a systemd service. Let's explain it a little bit. If I go to cd /home/ubuntu I can run ./vnc_startup.sh. This file creates a VNC connection and starts noVNC. Then I go to the browser open the address and login. I can run every command like as example rosrun rviz rviz because I have installed ROS.
If I use this service, it will not work:
cat /etc/systemd/system/novnc.service
[Unit]
After=NetworkManager.service time-sync.target
[Service]
Type=forking
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu
TimeoutStartSec=infinity
TimeoutStopSec=infinity
ExecStartPre=/bin/rm -f /home/ubuntu/no_vnc_startup.log
ExecStartPre=/bin/rm -f /home/ubuntu/vnc_startup.log
ExecStartPre=/bin/rm -f /home/ubuntu/wm.log
ExecStartPre=/bin/rm -f /home/ubuntu/wm_startup.log
ExecStart=/bin/bash -c "source /etc/environment; /home/ubuntu/vnc_startup.sh"
ExecStopPost=/bin/rm -f /home/ubuntu/no_vnc_startup.log
ExecStopPost=/bin/rm -f /home/ubuntu/vnc_startup.log
ExecStopPost=/bin/rm -f /home/ubuntu/wm.log
ExecStopPost=/bin/rm -f /home/ubuntu/wm_startup.log
[Install]
WantedBy=multi-user.target
I also tried it with systemctl --user start novnc.service and put the file inside /usr/lib/systemd/user instead of sudo systemctl start novnc.service and /etc/systemd/system/novnc.service.
Following workaround will work in the noVNC environment: I can open a Terminal. I can see ubuntu#hostname:~$. So it seems to be the right user and I am in the right working directory. Before running as example rosrun rviz rviz I have to run sudo su ubuntu. And then it works. If I had run ./vnc_startup.sh instead of running this script with systemd it works directly without the workaround with sudo su ubuntu.
Hard to explain. I hope you can understand me.
systemctl --user show-environment
HOME=/home/ubuntu
LANG=de_DE
LOGNAME=ubuntu
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
SHELL=/bin/bash
USER=ubuntu
XDG_RUNTIME_DIR=/run/user/1001
The command printenv makes clear that maybe the DISPLAY=:0 is missing, so I tried it with adding
export DISPLAY=:0
xset q
And I also added Environment=XAUTHORITY=/home/ubuntu/.Xauthority:
[Unit]
After=NetworkManager.service time-sync.target
[Service]
Type=forking
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu
Environment=XAUTHORITY=/home/ubuntu/.Xauthority
TimeoutStartSec=infinity
TimeoutStopSec=infinity
ExecStartPre=/bin/rm -f /home/ubuntu/no_vnc_startup.log
ExecStartPre=/bin/rm -f /home/ubuntu/vnc_startup.log
ExecStartPre=/bin/rm -f /home/ubuntu/wm.log
ExecStartPre=/bin/rm -f /home/ubuntu/wm_startup.log
ExecStart=/bin/bash -c "source /etc/environment; export DISPLAY=:0; xset q; /home/ubuntu/vnc_startup.sh"
ExecStopPost=/bin/rm -f /home/ubuntu/no_vnc_startup.log
ExecStopPost=/bin/rm -f /home/ubuntu/vnc_startup.log
ExecStopPost=/bin/rm -f /home/ubuntu/wm.log
ExecStopPost=/bin/rm -f /home/ubuntu/wm_startup.log
[Install]
WantedBy=multi-user.target
Here my log files:
cat no_vnc_startup.log
New 'shlServer01:1 (ubuntu)' desktop is shlServer01:1
Starting applications specified in /home/ubuntu/.vnc/xstartup
Log file is /home/ubuntu/.vnc/shlServer01:1.log
r settings:
- Listen on :6901
- Flash security policy server
- Web server. Web root: /home/ubuntu/noVNC
- No SSL/TLS support (no cert file)
- proxying from :6901 to localhost:5901
Navigate to this URL:
http://shlServer01:6901/vnc.html?host=shlServer01&port=6901
Press Ctrl-C to exit
192.168.0.6 - - [15/Dec/2021 15:16:56] 192.168.0.6: Plain non-SSL (ws://) WebSocket connection
192.168.0.6 - - [15/Dec/2021 15:16:56] 192.168.0.6: Version hybi-13, base64: 'False'
192.168.0.6 - - [15/Dec/2021 15:16:56] 192.168.0.6: Path: '/websockify'
192.168.0.6 - - [15/Dec/2021 15:16:56] connecting to: localhost:5901
cat vnc_startup.log
Killing Xvnc process ID 63164
Xvnc process ID 63164 already killed
cat wm.log
/usr/bin/startxfce4: X server already running on display :0
xfce4-session: Cannot open display: .
▒xfce4-session --help▒ eingeben, um mehr ▒ber die Verwendung zu erfahren.
cat wm_startup.log
------------------ startup of Xfce4 window manager ------------------
No protocol specified
xset: unable to open display ":0"
No protocol specified
xset: unable to open display ":0"
No protocol specified
xset: unable to open display ":0"
No protocol specified
xrdb: Resource temporarily unavailable
xrdb: Can't open display ':0'
No protocol specified
No protocol specified
/usr/bin/startxfce4: X server already running on display :0
xfce4-session: Cannot open display: .
▒xfce4-session --help▒ eingeben, um mehr ▒ber die Verwendung zu erfahren.
What surprises me is that this is not the error. The error messages also come when I start the script from the terminal and then it works.
The vnc_startup.sh looks like following:
#!/bin/bash
### every exit != 0 fails the script
set -e
## print out help
help (){
echo "
OPTIONS:
-w, --wait (default) keeps the UI and the vncserver up until SIGINT or SIGTERM will received
-s, --skip skip the vnc startup and just execute the assigned command.
example: docker run consol/centos-xfce-vnc --skip bash
-d, --debug enables more detailed startup output
e.g. 'docker run consol/centos-xfce-vnc --debug bash'
-h, --help print out this help
Fore more information see: https://github.com/ConSol/docker-headless-vnc-container
"
}
if [[ $1 =~ -h|--help ]]; then
help
exit 0
fi
# should also source /home/ubuntu/generate_container_user
source /home/ubuntu/.bashrc
# add `--skip` to startup args, to skip the VNC startup procedure
if [[ $1 =~ -s|--skip ]]; then
echo -e "\n\n------------------ SKIP VNC STARTUP -----------------"
echo -e "\n\n------------------ EXECUTE COMMAND ------------------"
echo "Executing command: '${#:2}'"
exec "${#:2}"
fi
if [[ $1 =~ -d|--debug ]]; then
echo -e "\n\n------------------ DEBUG VNC STARTUP -----------------"
export DEBUG=true
fi
## correct forwarding of shutdown signal
cleanup () {
kill -s SIGTERM $!
exit 0
}
trap cleanup SIGINT SIGTERM
## write correct window size to chrome properties
/home/ubuntu/chrome-init.sh
## resolve_vnc_connection
VNC_IP=$(hostname -i)
## change vnc password
echo -e "\n------------------ change VNC password ------------------"
# first entry is control, second is view (if only one is valid for both)
mkdir -p "/home/ubuntu/.vnc"
PASSWD_PATH="/home/ubuntu/.vnc/passwd"
if [[ -f $PASSWD_PATH ]]; then
echo -e "\n--------- purging existing VNC password settings ---------"
rm -f $PASSWD_PATH
fi
if [[ $VNC_VIEW_ONLY == "true" ]]; then
echo "start VNC server in VIEW ONLY mode!"
#create random pw to prevent access
echo $(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 20) | vncpasswd -f > $PASSWD_PATH
fi
echo "ubuntu" | vncpasswd -f >> $PASSWD_PATH
chmod 600 $PASSWD_PATH
## start vncserver and noVNC webclient
echo -e "\n------------------ start noVNC ----------------------------"
if [[ $DEBUG == true ]]; then echo "/home/ubuntu/noVNC/utils/launch.sh --vnc localhost:5901 --listen 6901"; fi
/home/ubuntu/noVNC/utils/launch.sh --vnc localhost:5901 --listen 6901 &> /home/ubuntu/no_vnc_startup.log &
PID_SUB=$!
echo -e "\n------------------ start VNC server ------------------------"
echo "remove old vnc locks to be a reattachable container"
vncserver -kill :1 &> /home/ubuntu/vnc_startup.log \
|| rm -rfv /tmp/.X*-lock /tmp/.X11-unix &> /home/ubuntu/vnc_startup.log \
|| echo "no locks present"
echo -e "start vncserver with param: VNC_COL_DEPTH=24, VNC_RESOLUTION=1280x1024\n..."
if [[ $DEBUG == true ]]; then echo "vncserver :1 -depth 24 -geometry 1280x1024"; fi
vncserver :1 -depth 24 -geometry 1280x1024 &> /home/ubuntu/no_vnc_startup.log
echo -e "start window manager\n..."
/home/ubuntu/wm_startup.sh &> /home/ubuntu/wm_startup.log
## log connect options
echo -e "\n\n------------------ VNC environment started ------------------"
echo -e "\nVNCSERVER started on DISPLAY= :1 \n\t=> connect via VNC viewer with $VNC_IP:5901"
echo -e "\nnoVNC HTML client started:\n\t=> connect via http://$VNC_IP:6901/?password=...\n"
if [[ $DEBUG == true ]] || [[ $1 =~ -t|--tail-log ]]; then
echo -e "\n------------------ /home/ubuntu/.vnc/*:1.log ------------------"
# if option `-t` or `--tail-log` block the execution and tail the VNC log
tail -f /home/ubuntu/*.log /home/ubuntu/.vnc/*:1.log
fi
if [ -z "$1" ] || [[ $1 =~ -w|--wait ]]; then
wait $PID_SUB
else
# unknown option ==> call command
echo -e "\n\n------------------ EXECUTE COMMAND ------------------"
echo "Executing command: '$#'"
exec "$#"
fi
The wm_startup.sh looks like this:
#!/usr/bin/env bash
### every exit != 0 fails the script
set -e
echo -e "\n------------------ startup of Xfce4 window manager ------------------"
### disable screensaver and power management
xset -dpms &
xset s noblank &
xset s off &
/usr/bin/startxfce4 --replace > /home/ubuntu/wm.log &
sleep 1
cat /home/ubuntu/wm.log
And it should not be important but the launch.sh file fom noVNC looks like this:
#!/usr/bin/env bash
# Copyright 2016 Joel Martin
# Copyright 2016 Solly Ross
# Licensed under MPL 2.0 or any later version (see LICENSE.txt)
usage() {
if [ "$*" ]; then
echo "$*"
echo
fi
echo "Usage: ${NAME} [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]"
echo
echo "Starts the WebSockets proxy and a mini-webserver and "
echo "provides a cut-and-paste URL to go to."
echo
echo " --listen PORT Port for proxy/webserver to listen on"
echo " Default: 6080"
echo " --vnc VNC_HOST:PORT VNC server host:port proxy target"
echo " Default: localhost:5900"
echo " --cert CERT Path to combined cert/key file"
echo " Default: self.pem"
echo " --web WEB Path to web files (e.g. vnc.html)"
echo " Default: ./"
echo " --ssl-only Disable non-https connections."
echo " "
exit 2
}
NAME="$(basename $0)"
REAL_NAME="$(readlink -f $0)"
HERE="$(cd "$(dirname "$REAL_NAME")" && pwd)"
PORT="6080"
VNC_DEST="localhost:5900"
CERT=""
WEB=""
proxy_pid=""
SSLONLY=""
die() {
echo "$*"
exit 1
}
cleanup() {
trap - TERM QUIT INT EXIT
trap "true" CHLD # Ignore cleanup messages
echo
if [ -n "${proxy_pid}" ]; then
echo "Terminating WebSockets proxy (${proxy_pid})"
kill ${proxy_pid}
fi
}
# Process Arguments
# Arguments that only apply to chrooter itself
while [ "$*" ]; do
param=$1; shift; OPTARG=$1
case $param in
--listen) PORT="${OPTARG}"; shift ;;
--vnc) VNC_DEST="${OPTARG}"; shift ;;
--cert) CERT="${OPTARG}"; shift ;;
--web) WEB="${OPTARG}"; shift ;;
--ssl-only) SSLONLY="--ssl-only" ;;
-h|--help) usage ;;
-*) usage "Unknown chrooter option: ${param}" ;;
*) break ;;
esac
done
# Sanity checks
which netstat >/dev/null 2>&1 \
|| die "Must have netstat installed"
netstat -ltn | grep -qs ":${PORT} .*LISTEN" \
&& die "Port ${PORT} in use. Try --listen PORT"
trap "cleanup" TERM QUIT INT EXIT
# Find vnc.html
if [ -n "${WEB}" ]; then
if [ ! -e "${WEB}/vnc.html" ]; then
die "Could not find ${WEB}/vnc.html"
fi
elif [ -e "$(pwd)/vnc.html" ]; then
WEB=$(pwd)
elif [ -e "${HERE}/../vnc.html" ]; then
WEB=${HERE}/../
elif [ -e "${HERE}/vnc.html" ]; then
WEB=${HERE}
elif [ -e "${HERE}/../share/novnc/vnc.html" ]; then
WEB=${HERE}/../share/novnc/
else
die "Could not find vnc.html"
fi
# Find self.pem
if [ -n "${CERT}" ]; then
if [ ! -e "${CERT}" ]; then
die "Could not find ${CERT}"
fi
elif [ -e "$(pwd)/self.pem" ]; then
CERT="$(pwd)/self.pem"
elif [ -e "${HERE}/../self.pem" ]; then
CERT="${HERE}/../self.pem"
elif [ -e "${HERE}/self.pem" ]; then
CERT="${HERE}/self.pem"
else
echo "Warning: could not find self.pem"
fi
# try to find websockify (prefer local, try global, then download local)
if [[ -e ${HERE}/websockify ]]; then
WEBSOCKIFY=${HERE}/websockify/run
if [[ ! -x $WEBSOCKIFY ]]; then
echo "The path ${HERE}/websockify exists, but $WEBSOCKIFY either does not exist or is not executable."
echo "If you intended to use an installed websockify package, please remove ${HERE}/websockify."
exit 1
fi
echo "Using local websockify at $WEBSOCKIFY"
else
WEBSOCKIFY=$(which websockify 2>/dev/null)
if [[ $? -ne 0 ]]; then
echo "No installed websockify, attempting to clone websockify..."
WEBSOCKIFY=${HERE}/websockify/run
git clone https://github.com/novnc/websockify ${HERE}/websockify
if [[ ! -e $WEBSOCKIFY ]]; then
echo "Unable to locate ${HERE}/websockify/run after downloading"
exit 1
fi
echo "Using local websockify at $WEBSOCKIFY"
else
echo "Using installed websockify at $WEBSOCKIFY"
fi
fi
echo "Starting webserver and WebSockets proxy on port ${PORT}"
#${HERE}/websockify --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
${WEBSOCKIFY} ${SSLONLY} --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
proxy_pid="$!"
sleep 1
if ! ps -p ${proxy_pid} >/dev/null; then
proxy_pid=
echo "Failed to start WebSockets proxy"
exit 1
fi
echo -e "\n\nNavigate to this URL:\n"
if [ "x$SSLONLY" == "x" ]; then
echo -e " http://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
else
echo -e " https://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
fi
echo -e "Press Ctrl-C to exit\n\n"
wait ${proxy_pid}
Suggesting to simply your /etc/systemd/system/novnc.service service unit with a single script for ExecStart command and a single script for ExecStop
/etc/systemd/system/novnc.service
[Unit]
After=NetworkManager.service time-sync.target
[Service]
Type=forking
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu
Environment=XAUTHORITY=/home/ubuntu/.Xauthority
TimeoutStartSec=infinity
TimeoutStopSec=infinity
ExecStart=/bin/bash -c "/home/ubuntu/servic_vnc_startup.sh"
ExecStop=/bin/bash -c "/home/ubuntu/servic_vnc_shutdown.sh"
[Install]
WantedBy=multi-user.target
/home/ubuntu/servic_vnc_startup.sh
#!\bin\bash
source /home/ubuntu/.bash_profile
source /etc/environment
export DISPLAY=:0
xset q;
rm -f /home/ubuntu/{no_vnc_startup.log,vnc_startup.log,wm.log,wm_startup.log}
/home/ubuntu/vnc_startup.sh
/home/ubuntu/servic_vnc_shutdown.sh
#!\bin\bash
source /home/ubuntu/.bash_profile
source /etc/environment
export DISPLAY=:0
xset q;
pkill -9 -f "/home/ubuntu/vnc_startup.sh"
rm -f /home/ubuntu/{no_vnc_startup.log,vnc_startup.log,wm.log,wm_startup.log}
Debugging
Login as user ubuntu.
Run /home/ubuntu/servic_vnc_startup.sh from command line.
If fails, fix it till it is successful.
Then try running /home/ubuntu/servic_vnc_startup.sh as user noboby:
sudo -u nobody "/home/ubuntu/servic_vnc_startup.sh"
User nobody has no shell and no environment context, as is the systemd service /etc/systemd/system/novnc.service.
If user nobody can run /home/ubuntu/servic_vnc_startup.sh then the /etc/systemd/system/novnc.service service unit can do as well.
Do same testing pattern with /home/ubuntu/servic_vnc_shutdown.sh
Lessons to learn:
Simplify service unit as much as possible.
Pull all scripting to a single shell script. Debug single shell script.
Avoid debugging handling service unit once deployed. Instead debug and modify called script.
Use user nobody user, to debug script to run without environment context and without shell.

Detecting when Mongod's port is open inside a script

I'm trying to write a bash script that starts a mongod process, waits for it to start (i.e. have it's default port open) and then pipe some commands into it through the mongo shell. I'd like some way to wait for the mongod process to be completely up that's more deterministic than just sleep 5.
This is the script so far:
set_up_authorization() {
echo "Setting up access control"
/path/to/mongo < configure_access_controls.js
}
wait_for_mongod_to_start() {
RETRIES=1000
CONNECTED="false"
echo "Waiting for mongod to start"
while [[ $RETRIES -ge 0 && $CONNECTED == "false" ]] ; do
RESPONSE=$(exec 6<>/dev/tcp/127.0.0.1/27017 || echo "1")
if [[ $RESPONSE == "" ]] # which should happen if the exec is successful
CONNECTED="true"
fi
RETRIES=$((RETRIES - 1))
done
if [[ $RETRIES -eq 0 ]] ; then
echo "Max retries reached waiting for mongod to start. Exiting."
exit 1
fi
echo "Mongod started"
}
./start_mongod_instance.sh
wait_for_mongod_to_start
set_up_authorization
While this script works, it produces a ton of output on the terminal while the exec is failing:
./initialize_cluster.sh: connect: Connection refused
./initialize_cluster.sh: line xx: /dev/tcp/127.0.0.1/27017: Connection refused
...which repeats for all ~900 failed attempts.
Neither of the following seems to get rid of the terminal logging either:
exec 6<>/dev/tcp/127.0.0.1/27017 >/dev/null
OR
exec 6<>/dev/tcp/127.0.0.1/27017 2>/dev/null
I've also tried using the following:
ps -aux | grep "mongod" | wc -l
but the process having a pid that ps lists isn't equivalent to it's port being open or it accepting connections.
Any ideas on either front would be appreciated - a more elegant way to wait for the process to start completely or a way to get rid of the excessive logging to the terminal.
Note: I don't have access to nmap or nc to check the port (this is on a client's machine).
exec is a bit special. It affects the output of the current shell. Meaning you need to redirect stderr of the current shell before running the port check:
host="localhost"
port="9000"
exec 2>/dev/null # redirect error here
while ! exec 3<>"/dev/tcp/${host}/${port}" ; do
echo "Waiting ..."
sleep 1
done
Furthermore you might have noticed that I check the exit status of exec rather than some output to decide whether the port is open or not.
If you want to reset it afterwards:
host="localhost"
port="9000"
# Copy fd 2 into fd 3 and redirect fd 2 to /dev/null
exec 3<&2 2>/dev/null
while ! exec 3<>"/dev/tcp/${host}/${port}" ; do
echo "Waiting ..."
sleep 1
done
# Copy back fd 3 into fd 2
exec 2<&3
echo "EE oops!" >&2

How to check if ssh-agent is already running in bash?

I have a sample sh script on my Linux environment, which basically run's the ssh-agent for the current shell, adds a key to it and runs two git commands:
#!/bin/bash
eval "$(ssh-agent -s)"
ssh-add /home/duvdevan/.ssh/id_rsa
git -C /var/www/duvdevan/ reset --hard origin/master
git -C /var/www/duvdevan/ pull origin master
Script actually works fine, but every time I run it I get a new process so I think it might become a performance issue and I might end up having useless processes out there.
An example of the output:
Agent pid 12109
Identity added: /home/duvdevan/.ssh/custom_rsa (rsa w/o comment)
Also, along with all this, is it possible to find an existing ssh-agent process and add my keys into it?
No, really, how to check if ssh-agent is already running in bash?
Answers so far don't appear to answer the original question...
Here's what works for me:
if ps -p $SSH_AGENT_PID > /dev/null
then
echo "ssh-agent is already running"
# Do something knowing the pid exists, i.e. the process with $PID is running
else
eval `ssh-agent -s`
fi
This was taken from here
Also, along with all this, is it possible to find an existing ssh-agent process and add my keys into it?
Yes. We can store the connection info in a file:
# Ensure agent is running
ssh-add -l &>/dev/null
if [ "$?" == 2 ]; then
# Could not open a connection to your authentication agent.
# Load stored agent connection info.
test -r ~/.ssh-agent && \
eval "$(<~/.ssh-agent)" >/dev/null
ssh-add -l &>/dev/null
if [ "$?" == 2 ]; then
# Start agent and store agent connection info.
(umask 066; ssh-agent > ~/.ssh-agent)
eval "$(<~/.ssh-agent)" >/dev/null
fi
fi
# Load identities
ssh-add -l &>/dev/null
if [ "$?" == 1 ]; then
# The agent has no identities.
# Time to add one.
ssh-add -t 4h
fi
This code is from pitfalls of ssh agents which describes both the pitfalls of what you're currently doing, of this approach, and how you should use ssh-ident to do this for you.
If you only want to run ssh-agent if it's not running and do nothing otherwise:
if [ $(ps ax | grep [s]sh-agent | wc -l) -gt 0 ] ; then
echo "ssh-agent is already running"
else
eval $(ssh-agent -s)
if [ "$(ssh-add -l)" == "The agent has no identities." ] ; then
ssh-add ~/.ssh/id_rsa
fi
# Don't leave extra agents around: kill it on exit. You may not want this part.
trap "ssh-agent -k" exit
fi
However, this doesn't ensure ssh-agent will be accessible (just because it's running doesn't mean we have $SSH_AGENT_PID for ssh-add to connect to).
If you want it to be killed right after the script exits, you can just add this after the eval line:
trap "kill $SSH_AGENT_PID" exit
Or:
trap "ssh-agent -k" exit
$SSH_AGENT_PID gets set in the eval of ssh-agent -s.
You should be able to find running ssh-agents by scanning through /tmp/ssh-* and reconstruct the SSH_AGENT variables from it (SSH_AUTH_SOCK and SSH_AGENT_PID).
ps -p $SSH_AGENT_PID > /dev/null || eval "$(ssh-agent -s)"
Single line command. Run for the first time will start ssh-agent. Run for the second time will not start the ssh-agent. Simple and Elegant Mate !!!
Using $SSH_AGENT_PID can only test the ssh-agent but miss identities when it is not yet added
$ eval `ssh-agent`
Agent pid 9906
$ echo $SSH_AGENT_PID
9906
$ ssh-add -l
The agent has no identities.
So it would be save to check it with ssh-add -l with an expect script like example below:
$ eval `ssh-agent -k`
Agent pid 9906 killed
$ ssh-add -l
Could not open a connection to your authentication agent.
$ ssh-add -l &>/dev/null
$ [[ "$?" == 2 ]] && eval `ssh-agent`
Agent pid 9547
$ ssh-add -l &>/dev/null
$ [[ "$?" == 1 ]] && expect $HOME/.ssh/agent
spawn ssh-add /home/user/.ssh/id_rsa
Enter passphrase for /home/user/.ssh/id_rsa:
Identity added: /home/user/.ssh/id_rsa (/home/user/.ssh/id_rsa)
$ ssh-add -l
4096 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /home/user/.ssh/id_rsa (RSA)
So when both ssh-agent and ssh-add -l are put to run on a bash script:
#!/bin/bash
ssh-add -l &>/dev/null
[[ "$?" == 2 ]] && eval `ssh-agent`
ssh-add -l &>/dev/null
[[ "$?" == 1 ]] && expect $HOME/.ssh/agent
then it would always check and assuring that the connection is running:
$ ssh-add -l
4096 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /home/user/.ssh/id_rsa (RSA)
You can also emulate the repeating of commands on above script with do while
The accepted answer did not work for me under Ubuntu 14.04.
The test to check if the ssh-agent is running I have to use is:
[[ ! -z ${SSH_AGENT_PID+x} ]]
And I am starting the ssh-agent with:
exec ssh-agent bash
Otherwise the SSH_AGENT_PID is not set.
The following seems to work under both Ubuntu 14.04 and 18.04.
#!/bin/bash
sshkey=id_rsa
# Check ssh-agent
if [[ ! -z ${SSH_AGENT_PID+x} ]]
then
echo "[OK] ssh-agent is already running with pid: "${SSH_AGENT_PID}
else
echo "Starting new ssh-agent..."
`exec ssh-agent bash`
echo "Started agent with pid: "${SSH_AGENT_PID}
fi
# Check ssh-key
if [[ $(ssh-add -L | grep ${sshkey} | wc -l) -gt 0 ]]
then
echo "[OK] SSH key already added to ssh-agent"
else
echo "Need to add SSH key to ssh-agent..."
# This should prompt for your passphrase
ssh-add ~/.ssh/${sshkey}
fi
Thanks to all the answers here. I've used this thread a few times over the years to tweak my approach. Wanted to share my current ssh-agent.sh checker/launcher script that works for me on Linux and OSX.
The following block is my $HOME/.bash.d/ssh-agent.sh
function check_ssh_agent() {
if [ -f $HOME/.ssh-agent ]; then
source $HOME/.ssh-agent > /dev/null
else
# no agent file
return 1
fi
if [[ ${OSTYPE//[0-9.]/} == 'darwin' ]]; then
ps -p $SSH_AGENT_PID > /dev/null
# gotcha: does not verify the PID is actually an ssh-agent
# just that the PID is running
return $?
fi
if [ -d /proc/$SSH_AGENT_PID/ ]; then
# verify PID dir is actually an agent
grep ssh-agent /proc/$SSH_AGENT_PID/cmdline > /dev/null 2> /dev/null;
if [ $? -eq 0 ]; then
# yep - that is an agent
return 0
else
# nope - that is something else reusing the PID
return 1
fi
else
# agent PID dir does not exist - dead agent
return 1
fi
}
function launch_ssh_agent() {
ssh-agent > $HOME/.ssh-agent
source $HOME/.ssh-agent
# load up all the pub keys
for I in $HOME/.ssh/*.pub ; do
echo adding ${I/.pub/}
ssh-add ${I/.pub/}
done
}
check_ssh_agent
if [ $? -eq 1 ];then
launch_ssh_agent
fi
I launch the above from my .bashrc using:
if [ -d $HOME/.bash.d ]; then
for I in $HOME/.bash.d/*.sh; do
source $I
done
fi
Hope this helps others get up and going quickly.
Created a public gist if you want to hack/improve this with me: https://gist.github.com/dayne/a97a258b487ed4d5e9777b61917f0a72
cat /usr/local/bin/ssh-agent-pro << 'EOF'
#!/usr/bin/env bash
SSH_AUTH_CONST_SOCK="/var/run/ssh-agent.sock"
if [[ x$(wc -w <<< $(pidof ssh-agent)) != x1 ]] || [[ ! -e ${SSH_AUTH_CONST_SOCK} ]]; then
kill -9 $(pidof ssh-agent) 2>/dev/null
rm -rf ${SSH_AUTH_CONST_SOCK}
ssh-agent -s -a ${SSH_AUTH_CONST_SOCK} 1>/dev/null
fi
echo "export SSH_AUTH_SOCK=${SSH_AUTH_CONST_SOCK}"
echo "export SSH_AGENT_PID=$(pidof ssh-agent)"
EOF
echo "eval \$(/usr/local/bin/ssh-agent-pro)" >> /etc/profile
. /etc/profile
then you can ssh-add xxxx once, you can use ssh-agent everytime when you login.
I've noticed that having a running agent is not enough because sometimes, the SSH_AUTH_SOCK variable is set or pointing to a socket file that does not exist anymore.
Therefore, to connect to an already running ssh-agent on your machine, you can do this :
$ pgrep -u $USER -n ssh-agent -a
1906647 ssh-agent -s
$ ssh-add -l
Could not open a connection to your authentication agent.
$ test -z "$SSH_AGENT_PID" && export SSH_AGENT_PID=$(pgrep -u $USER -n ssh-agent)
$ test -z "$SSH_AUTH_SOCK" && export SSH_AUTH_SOCK=$(ls /tmp/ssh-*/agent.$(($SSH_AGENT_PID-1)))
$ ssh-add -l
The agent has no identities.
Regarding finding running ssh-agents, previous answers either don't work or rely on a magic file like $HOME/.ssh_agent. These approaches require us to believe that user never run agents without saving their output to this file.
My approach instead relies on a rarely changed default UNIX domain socket template to find an accessible ssh-agent among available possibilities.
# (Paste the below code to your ~/.bash_profile and ~/.bashrc files)
C=$SSH_AUTH_SOCK
R=n/a
unset SSH_AUTH_SOCK
for s in $(ls $C /tmp/ssh-*/agent.* 2>/dev/null | sort -u) ; do
if SSH_AUTH_SOCK=$s ssh-add -l >/dev/null ; then R=$? ; else R=$? ; fi
case "$R" in
0|1) export SSH_AUTH_SOCK=$s ; break ;;
esac
done
if ! test -S "$SSH_AUTH_SOCK" ; then
eval $(ssh-agent -s)
unset SSH_AGENT_PID
R=1
fi
echo "Using $SSH_AUTH_SOCK"
if test "$R" = "1" ; then
ssh-add
fi
In this approach, SSH_AGENT_PID remains unknown, since it is hard to deduce it for non-roots. I assume it is actually not required for users since they don't normally want to stop agents. On my system, setting SSH_AUTH_SOCK is enough to communicate with agent for e.g. passwordless authentication.
The code should work with any shell-compatible shell.
You can modify line #1 to:
PID_SSH_AGENT=`eval ssh-agent -s | grep -Po "(?<=pid\ ).*(?=\;)"`
And then at the end of the script you can do:
kill -9 $PID_SSH_AGENT
I made this bash function to count and return the number of running ssh-agent processes... it searches ssh-agent process using procfs instead of using $ ps -p $SSH_AGENT_PID:cmd or $SSH_AUTH_SOCK:var ... (these ENV-var. can still be set with old values while ssh-agent's process is already killed: if $ ssh-agent -k or $ $(ssh-agent -k) instead of $ eval $(ssh-agent -k))
function count_agent_procfs(){
declare -a agent_list=( )
for folders in $(ls -d /proc/*[[:digit:]] | grep -v /proc/1$);do
fichier="${folders}/stat"
pid=${folders/\/proc\//}
[[ -f ${fichier} ]] && [[ $(cat ${fichier} | cut -d " " -f2) == "(ssh-agent)" ]] && agent_list+=(${pid})
done
return ${#agent_list[#]}
}
..and then if there is a lot of ssh-agent process running you get their PID with this list..."${agent_list[#]}"
Very simple command to check how many processes are running for ssh-agent (or any other program): pidof ssh-agent
or:
pgrep ssh-agent
And very simple command to kill all processes of ssh-agent (or any program):
kill $(pidof ssh-agent)

Bash - Update terminal title by running a second command

On my terminal in Ubuntu, I often run programs which keep running for a long time. And since there are a lot of these programs, I keep forgetting which terminal is for which program, unless I tab through all of those. So I wanted to find a way to update my terminal title to the program name, whenever I run a command. I don't want to do it manually.
I use gnome-terminal, but answer shouldn't really depend on that. Basically, If I'm able to run a second command, then I can simply use gconftool command to update the title. So I was hoping to find a way to capture the command in bash and update the title after every command. How do I do that?
I have some answers for you :) You're right that it shouldn't matter that you're using gnome-terminal, but it does matter what command shell you're using. This is a lot easier in zsh, but in what follows I'm going to assume you're using bash, and that it's a fairly recent version (> 3.1).
First of all:
Which environment variable would
contain the current 'command'?
There is an environment variable which has more-or-less what you want - $BASH_COMMAND. There's only one small hitch, which is that it will only show you the last command in a pipe. I'm not 100% sure what it will do with combinations of subshells, either :)
So I was hoping to find a way to
capture the command in bash and update
the title after every command.
I've been thinking about this, and now that I understand what you want to do, I realized the real problem is that you need to update the title before every command. This means that the $PROMPT_COMMAND and $PS1 environment variables are out as possible solutions, since they're only executed after the command returns.
In bash, the only way I can think of to achieve what you want is to (ab)use the DEBUG SIGNAL. So here's a solution -- stick this at the end of your .bashrc:
trap 'printf "\033]0;%s\007" "${BASH_COMMAND//[^[:print:]]/}"' DEBUG
To get around the problem with pipes, I've been messing around with this:
function settitle () {
export PREV_COMMAND=${PREV_COMMAND}${#}
printf "\033]0;%s\007" "${BASH_COMMAND//[^[:print:]]/}"
export PREV_COMMAND=${PREV_COMMAND}' | '
}
export PROMPT_COMMAND=${PROMPT_COMMAND}';export PREV_COMMAND=""'
trap 'settitle "$BASH_COMMAND"' DEBUG
but I don't promise it's perfect!
Try this:
trap 'echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"' DEBUG
Thanks to the history 1 it works even with complicated expressions like:
true && (false); echo $? | cat
For which approaches relying on $BASH_COMMAND or $# fail. For example simon's displays:
true | echo $? | cat
Thanks to Gilles and simon for providing inspiration.
I see what stoutie is trying to do, except it's a lot more work than needed. And doesn't cause all sorts of other potentially bad things that can occur as a result of redefining 'cd' and putting in all of that testing just to change directories. Bash has built in support for most of this.
You can put this in your .bashrc anywhere after you set your current PS1 prompt (this way it just prepends it)
# If this is an xterm set the titlebar to user#host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;\u#\h: \w\a\]$PS1"
;;
*)
;;
esac
The OP asked for bash, but others might be interested to learn that (as mentioned above) this is indeed a lot easier using the zsh shell. Example:
# Set window title to command just before running it.
preexec() { printf "\x1b]0;%s\x07" "$1"; }
# Set window title to current working directory after returning from a command.
precmd() { printf "\x1b]0;%s\x07" "$PWD" }
In preexec, $1 contains the command as typed (requires shell history to be enabled, which seems to be a fair assumption), $2 the expanded command (shell aliases etc.) and $3 the "very expanded" command (shell function bodies). (more)
I'm doing something like this, to show my pwd in the title, which could be modified to do whatever you want to do with the title:
function title { echo -en "\033]2;$1\007"; }
function cd { dir=$1; if [ -z "$dir" ]; then dir=~; fi; builtin cd "$dir" && title `pwd`; }
I just threw this in my ~/.bash_aliases.
Update
I ran into strange bugs with my original answer. I ended up picking apart the default Ubuntu PS1 and breaking it into parts only to realize one of the parts was the title:
# simple prompt
COLOR_YELLOW_BOLD="\[\033[1;33m\]"
COLOR_DEFAULT="\[\033[0m\]"
TITLE="\[\e]0;\u#\h:\w\a\]"
PROMPT="\w\n$ "
HUH="${debian_chroot:+($debian_chroot)}"
PS1="${COLOR_YELLOW_BOLD}${TITLE}${HUH}${PROMPT}${COLOR_DEFAULT}"
Without breaking into variables, it would look like this:
PS1="\[\033[1;33m\]\[\e]0;\u#\h:\w\a\]${debian_chroot:+($debian_chroot)}\w\n$ \[\033[0m\]"
I have tested three method, all is OK, use any one for your pleasure.
export PROMPT_COMMAND='echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"'
trap 'echo -ne "\033]2;$(history 1 | sed "s/^[ ]*[0-9]*[ ]*//g")\007"' DEBUG
trap 'echo -ne "\e]0;"; echo -n $BASH_COMMAND; echo -ne "\a"' DEBUG
please note if use $BASH_COMMAND, it don't recognize bash alias, and use PROMPT_COMMAND show finished command, but use trap show running command.
Based on the the need to auto position putty windows I have modified my /etc/bash.bashrc file on a Debian/Ubuntu system. I have posted the full contents for completeness but the relevant bit to starts on the # Display command ... comment line.
# System-wide .bashrc file for interactive bash(1) shells.
# To enable the settings / commands in this file for login shells as well,
# this file has to be sourced in /etc/profile.
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# 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, overwrite the one in /etc/profile)
PS1='${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
# Display command run in title which allows us to distinguish Kitty/Putty
# windows and re-position easily using AutoSizer window utility. Based on a
# post here: http://mg.pov.lt/blog/bash-prompt.html
case "$TERM" in
xterm*|rxvt*)
# Show the currently running command in the terminal title:
# http://www.davidpashley.com/articles/xterm-titles-with-bash.html
show_command_in_title_bar()
{
case "$BASH_COMMAND" in
*\033]0*)
# The command is trying to set the title bar as well;
# this is most likely the execution of $PROMPT_COMMAND.
# In any case nested escapes confuse the terminal, so don't
# output them.
;;
*)
echo -ne "\033]0;${USER}#${HOSTNAME}: ${BASH_COMMAND}\007"
;;
esac
}
trap show_command_in_title_bar DEBUG
;;
*)
;;
esac
# Commented out, don't overwrite xterm -T "title" -n "icontitle" by default.
# If this is an xterm set the title to user#host:dir
#case "$TERM" in
#xterm*|rxvt*)
# PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME}: ${PWD}\007"'
# ;;
#*)
# ;;
#esac
# enable bash completion in interactive shells
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
# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
function command_not_found_handle {
# check because c-n-f could've been removed in the meantime
if [ -x /usr/lib/command-not-found ]; then
/usr/bin/python /usr/lib/command-not-found -- "$1"
return $?
elif [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/bin/python /usr/share/command-not-found/command-not-found -- "$1"
return $?
else
printf "%s: command not found\n" "$1" >&2
return 127
fi
}
fi
You can set up bash such that it sends a certain escape sequence to the terminal every time it starts an external program. If you use the escape sequence that terminals use to update their titles, your problem should be solved.
I have used that before, so I know it is possible. but I cannot remember it off the top of my head and do not have time to research the details right now, though.
Some of the old methods were removed from gnome-terminal 3.14 due to these two bugs (724110 and 740188).
In Ubuntu 20.04
PS1=$PS1"\[\e]0;New_Terminal_Name\a\]"
\[ begin a sequence of non-printing characters
\e]0; is the char sequence for setting the terminal title. Bash identifies this sequence and set the tile with the following characters. Number 0 turns out to be the value to reference the title property.
New_Terminal_Name is the tile we gave
\a is the ASCII bell character, also in this case, it marks the end of the tile to read from Bash.
\] end a sequence of non-printing characters
We can create a function for future use
function set_title(){
if [ -z "$PS1_BACK" ]; # set backup if it is empty
then
PS1_BACK="$PS1"
fi
TITLE="\[\e]0;$*\a\]"
PS1="${PS1_BACK}${TITLE}"
}
Open the ~/.bashrc file in your home directory with a text editor and append the above function at the end of it. Save and close.
To use it immediately source it to the current terminal.
source ~/.bashrc
We can use it then like this
set_title <New terminal tab title>
My terminal window titler script
This dynamic backgrounded script show all running command with pid number and elapsed time in seconds, like if I run du -h | less, this will build title looking like:
204640 6 du -h | 204641 6 less
Then when no command (other than himself) are running, don't change the terminal title, so standard behaviours works normaly.
First run start backgroud task. Second run in same terminal ask for kill previous backgrounded task.
Save this into a file, set execute flag then run it without argument:
cat <<"EOF" >titleWin.sh
#!/bin/bash
## Ask for kill process if already started
mapfile -t pids < <(ps -C ${0##*/} ho pid)
for pid in ${pids[#]} ;do
if [[ $pid != $$ ]] && [ -d /proc/$pid ]; then
echo -n "STARTED: [$pid]: ${0##*/}. Kill them (Y/n)? "
read -rsn 1 act
case $act in
n|N ) echo No;;
* ) echo Yes;kill $pid ;;
esac
exit
fi
done
## Title win for xterm or screen (or tmux).
case $TERM in
xterm*|rxvt* ) titleFmt='\e];%s\a';;
screen* ) titleFmt='\ek%s\e\\';;
* ) echo "Unable to title window.";exit 1;;
esac
tty=$(tty)
## Date to epochseconds converter
exec {dateout}<> <(:)
exec {datein}> >(exec stdbuf -o0 date -f - +%s >&$dateout)
DPID=$!
trap "echo TRAP;kill $DPID" 1 2 3 6 9 15
# Main loop
while :;do
string=""
while read -r pid wday mon day time year cmd; do
if [[ $pid != $$ ]] && [[ $pid != $PPID ]] && [[ $pid != $BASHPID ]] &&
[[ $pid != $DPID ]] && [ "${cmd#*pid,lstart,cmd}" ] &&
[ -d /proc/$pid ] ;then
echo >&${datein} $wday $mon $day $time $year
read -ru $dateout date
string+="$pid $((EPOCHSECONDS-date)) $cmd | "
fi
done < <(exec ps --tty ${tty#*/dev/} ho pid,lstart,cmd)
[[ "$string" ]] && printf "$titleFmt" "${string% | }"
sleep .333
done &
EOF
chmod +x titleWin.sh
./titleWin.sh

Resources