-2

In BASH, the following command removes everything in a directory except one file:

rm -rf !(filename.txt)

However, in SSH the same command changes nothing in the directory and it returns the following error: -jailshell: !: event not found

So, I escaped the ! with \, (the parentheses also require escaping) but it still doesn't work:

rm -rf \!\(filename.txt\)

It returns no error and nothing in the directory changed.

Is it even possible to run this command in SSH? I found a workaround but if this is possible it would expedite things considerably.

I connect to the ssh server using the alias below:

alias devssh="ssh -p 2222 -i ~/.ssh/private_key user@host"
Thomas
  • 117
  • 1
  • 10

2 Answers2

1

!(filename.txt) is an extglob, a bash future that might have to be enabled. Make sure that your ssh server runs bash and that extglob is enabled:

ssh user@host "bash -O extglob -c 'rm -rf !(filename.txt)'"

Or by using your alias:

devssh "bash -O extglob -c 'rm -rf !(filename.txt)'"

If you are sure that the remote system uses bash by default, you can also drop the bash -c part. But your error message indicates that the ssh server runs jailshell.

ssh user@host 'shopt -s extglob; rm -rf !(filename.txt)'
devssh 'shopt -s extglob; rm -rf !(filename.txt)'
Socowi
  • 25,550
  • 3
  • 32
  • 54
  • I tried to set the extglob using `bash -O extglob -c 'rm -rf !(filename.txt)` and it blew away all the contents in the entire public_html directory. – Thomas Aug 06 '21 at 20:50
  • Seems about right if there was no `filename.txt` in that directory. Are you sure such a file was there before? – Socowi Aug 06 '21 at 20:53
  • @Thomas: I suggest to replace `rm` with `echo rm` first. – Cyrus Aug 06 '21 at 20:54
  • [Here is a local test](https://onlinegdb.com/n9lPRynYs), showing that the command works as expected. – Socowi Aug 06 '21 at 20:59
  • I'll just be quite glad it's a dev server. – Thomas Aug 06 '21 at 21:03
  • @Socowi, as I said in the question, I know it works fine in BASH. It does not work when I'm logged in to the server via SSH. – Thomas Aug 07 '21 at 11:35
  • You said you executed `bash -O extglob -c 'rm -rf !(filename.txt)'` **through ssh (?)**, and that this command deleted everything. That means that you (1) ran bash **on the remote system** (2) the command worked. Therefore, I don't understand why this question is still open. What else do you need? *(Only if you confirmed that `bash -O extglob -c 'touch filename.txt; rm -rf !(filename.txt); ls'` deleted `filename.txt` too I would see a problem. But then I would need more information, because I cannot reproduce such a problem, as shown by my prev. comment)* – Socowi Aug 07 '21 at 11:48
0

I wouldn't do it that way. I wouldn't rely on bash being on the remote, and I wouldn't rely on any bashisms. I would use:

$ ssh user@host 'rm $(ls | grep -v "^filename.txt$")'

If I wanted to protect against the possibility that the directory might be empty, I'd assign the output of $(...) to a variable, and test it for emptiness. If I was concerned the command might get too long, I'd write the names to a file, and send the grep output to rm with xargs.

If it got too elaborate, I'd copy a script to the remote and execute it.

James K. Lowden
  • 7,574
  • 1
  • 16
  • 31
  • 1
    I would be concerned for more than a long command. If you had the files `important.txt` and `not important.txt` and you ran `rm $(ls | grep -v "^important.txt$")` then you would be in for a [surprise](https://onlinegdb.com/l4Kd8FYf7). – Socowi Aug 06 '21 at 21:11
  • **ls** written to a pipe produces one line per name. `^important.txt$` matches the whole line, anchored at the front by `^`. It doesn't match `not important.txt`, which `grep -v` will print because the sense is inverted. – James K. Lowden Aug 08 '21 at 14:55
  • Yes, this is all correct. But did you try it for yourself (see link in my previous comment). The problem is the unquoted `$()` which undergoes **word**splitting. `not important.txt` then turns into `not` and `important.txt`. Also, globs in filenames will expand. – Socowi Aug 08 '21 at 15:15
  • @Socowi, thanks, I see your point now. I would still follow my advice, to use standard syntax, and yours, to test carefully, especially with **rm**(1). – James K. Lowden Aug 08 '21 at 16:41