6

SSH allows a remote user to issue some command without interactively logging in to the server, as the last line of ssh usage indicates:

usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
           [-D [bind_address:]port] [-E log_file] [-e escape_char]
           [-F configfile] [-I pkcs11] [-i identity_file]
           [-L [bind_address:]port:host:hostport] [-Q protocol_feature]
           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
           [-R [bind_address:]port:host:hostport] [-S ctl_path]
           [-W host:port] [-w local_tun[:remote_tun]]
           [user@]hostname [command]

When ssh is invoked with a remote command, the .bash_history file does not get updated (i.e., the remote command is not added to .bash_history). I managed to "simulate" this effect by adding the following command to /etc/ssh/sshd_config:

ForceCommand if [[ -z $SSH_ORIGINAL_COMMAND ]]; then bash; else printf "$SSH_ORIGINAL_COMMAND\n" >> .bash_history; bash -c "$SSH_ORIGINAL_COMMAND"; fi

The above command checks whether the environment variable $SSH_ORIGINAL_COMMAND is empty:

  • If so, no remote command is issued, and we simply run bash.
  • Otherwise, $SSH_ORIGINAL_COMMAND is added to .bash_history, and the remote command within $SSH_ORIGINAL_COMMAND is executed.

It works as expected, but I need a bit more: I want the current timestamp to be added to .bash_history as well. To this end, I added the following command to /etc/ssh/sshd_config:

ForceCommand if [[ -z $SSH_ORIGINAL_COMMAND ]]; then bash; else printf "#`date +%s`\n$SSH_ORIGINAL_COMMAND\n" >> .bash_history; bash -c "$SSH_ORIGINAL_COMMAND"; fi

But when I try to ssh to the server, I receive the following error:

bash: -c: line 0: unexpected EOF while looking for matching `"'
bash: -c: line 1: syntax error: unexpected end of file
Connection to 127.0.0.1 closed.

If I remove # before date +%s, it works correctly. But I need # to be printed before the timestamp, as it is the correct format for the .bash_history file.

  • I do not understand the problem but putting a \ before the # might help. As the whole command is passed to bash -c, "$SSH_ORIGINAL_COMMAND" should be enough as second command. – Hauke Laging Apr 1 '14 at 22:47
  • @HaukeLaging: Sorry, I still get the same error. Maybe there's another way for escaping #, which is possibly commenting out the rest of line? – M.S. Dousti Apr 2 '14 at 1:48
  • @HaukeLaging: Using printf "\x23..." instead of printf "#..." solved the problem. But I'm still eager to know how to escape # in the latter command. – M.S. Dousti Apr 2 '14 at 1:58
3

A # in sshd_config is interpreted as the beginning of a comment and everything following it is ignored. Although (according to sshd_config(5)) "" may be used to quote arguments containing spaces, they do not quote #.

That also explains the error you get. sshd only passes the following to bash:

if [[ -z $SSH_ORIGINAL_COMMAND ]]; then bash; else printf "

The second " cannot be found as the command line ends just after the first one.

To prevent this behaviour the literal # must not be used:

  • As printf is used here anyway, using its capabilities to use backslash-escaped characters comes in handy. # can be written as \x23 (hexadecimal), \43 (octal), \u23 (Unicode, hexadecimal up to 4 hex digits) or even \U23 (Unicode, hexadecimal up to 8 hex digits). The same works for echo -e. Note that the \ has to be quoted, so use either "\43", '\43' or \\43.

  • In cases where you do not need echo or printf, you can get bash (works on zsh, too) to do the replacement by using $'string'. For example: to do touch foo#bar, you could write touch $'foo\x23bar'. If the value has less than the maximum amount of allowed digits (3 for octal, 2 for hexadecimal and 4 or 8 respectively for Unicode) you should use leading zeroes to avoid misinterpretations. For example: $'foo\u23bar' evaluates to foo⎺r while $'foo\u0023bar' gives the expected foo#bar.

  • Avoid using # (literal or otherwise) alltogether by putting all functionality into a script and then just put ForceCommand /path/to/script in your configuration.

The second option also allows you to forgo printf by using the format option of date more extensively. Instead of

printf "\x23`date +%s`\n$SSH_ORIGINAL_COMMAND\n" >> .bash_history

You can write

date $'+\x23%s'"${SSH_ORIGINAL_COMMAND//%/%%}" >> .bash_history
  • Thanks. As I said in a comment above, "I'm still eager to know how to escape #." I used printf "\x23..." instead of printf "#...", and it worked. But is there another way for escaping #? – M.S. Dousti Apr 4 '14 at 9:52
  • @SadeqDousti I included a few ways to escape/avoid # in my answer. There are probably many more, but I think I covered the most practical ones. – Adaephon Apr 4 '14 at 13:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.