Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# sshcd

### This is a fork of fraction/sshcd with support for remote command execution

Tired of having to type this janky command to [ssh](http://manpages.ubuntu.com/manpages/saucy/en/man1/ssh.1.html) and [cd](http://manpages.ubuntu.com/manpages/saucy/en/man1/cd.1posix.html) into unfamiliar remote servers?

```sh
Expand All @@ -12,19 +14,26 @@ Stop it. Connect with SSH and cd (change directory) with one command.
sshcd [email protected]:/foo/bar
```

## Requirements

- Bash shell (the script requires bash, not sh or zsh)
- curl (for installation via provided commands)

## Installation

Use Homebrew to download and install the executable.
Add the executable into your PATH and give it execute permissions.

Mac example:

```sh
brew tap fraction/homebrew-formulae
brew install sshcd
sudo curl -Lo ~/.local/bin/sshcd https://raw.githubusercontent.com/romanesko/sshcd/refs/heads/master/sshcd
sudo chmod +x ~/.local/bin/sshcd
```

Add the executable into your path and give it permissions.
Linux example:

```sh
sudo curl -Lo /usr/local/bin/sshcd http://git.io/wfAXEQ
sudo curl -Lo /usr/local/bin/sshcd https://raw.githubusercontent.com/romanesko/sshcd/refs/heads/master/sshcd
sudo chmod +x /usr/local/bin/sshcd
```

Expand All @@ -36,15 +45,36 @@ The default usage is pretty simple.
sshcd [email protected]:/foo/bar
```

The tool supports prepended flags, too!
Execute a remote command:

```sh
sshcd [email protected]:/foo/bar ls -la
```

Execute a complex command (multiple commands):

```sh
sshcd [email protected]:/foo/bar bash -c 'sudo df -h ~'
```

The tool supports prepended ssh flags as well:

```sh
sshcd -v [email protected]:/foo/bar
```

### Spaces and special characters

Paths and remote commands with spaces or special characters are supported and properly escaped.

### Error handling

If you do not provide a target in the form [user@]host:/path, the script will print usage instructions and exit with an error.

## Support

Please [open an issue](https://github.com/fraction/sshcd/issues/new) for questions and concerns.
Please [open an issue](https://github.com/romanesko/sshcd/issues/new) for questions and concerns.

## Contributing

Fork the project, commit your changes, and [open a pull request](https://github.com/fraction/sshcd/compare/).
Fork the project, commit your changes, and [open a pull request](https://github.com/romanesko/sshcd/compare/).
62 changes: 36 additions & 26 deletions sshcd
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
#!/bin/bash -e
options=("${@:1:$(($# - 1))}") # Everything in "$@" except the last argument
target="${!#}" # The last argument from "$@"

# Get user
usertest="${target%%:*}"
if [ ! "${usertest/@/}" = "${usertest}" ]; then # user is included in the target
user="${target%%@*}" # Get user
target="${target#*@}" # Strip out user
fi
options=()
target=""
remote_cmd=()
user=""
path=""

# Get hostname and path
if [ "${target::1}" = "[" ]; then # Syntax: [user@]\[hostname\]:path
remote="${target%\]*}]"
path="${target#*\]}"
if [ -z "${path}" ]; then
exec ssh "$@"
fi
path="${path:1}"
else # Syntax: [user@]hostname:path
remote="${target%%:*}"
path="${target#*:}"
if [ "${path}" = "${target}" ]; then
exec ssh "$@"
for arg in "$@"; do
if [[ -z "$target" && "$arg" == *:* ]]; then
target="$arg"
elif [[ -z "$target" ]]; then
options+=("$arg")
else
remote_cmd+=("$arg")
fi
done

if [[ -z "$target" ]]; then
echo "Usage: $0 [ssh options] [user@]host:/path [command]" >&2
exit 1
fi

usertest="${target%%:*}"
path="${target#*:}"

if [[ "$usertest" == *"@"* ]]; then
user="${usertest%%@*}"
host="${usertest#*@}"
else
host="$usertest"
fi

# Construct target argument for ssh
if [ ! "${usertest/@/}" = "${usertest}" ]; then # user is included in the target
remote="${user}@${remote}" # Reattach user
[[ -n "$user" ]] && remote="${user}@${host}" || remote="${host}"

path_escaped=$(printf "%q" "$path")

if [[ ${#remote_cmd[@]} -gt 0 ]]; then
cmd=$(printf "%q " "${remote_cmd[@]}")
exec ssh "${options[@]}" "${remote}" "cd $path_escaped && exec $cmd"
else
exec ssh -t "${options[@]}" "${remote}" "cd $path_escaped; exec \$SHELL -l"
fi

path="${path/\'/\'\\\'\'}"
exec ssh -t "${options[@]}" "${remote}" "cd '${path}'; exec \$SHELL -l"