Delete Everything in a Directory Except One FIle - Using SSH - bash

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"

!(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)'

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.

Related

How to delete all except a directory in Makefile?

The following command set is working on linux prompt.
%cd ${ADIR}/exe; shopt -s extglob; rm -rf !(BDIR)
But it is not working in Makefile
Linux command - works
%cd ${ADIR}/exe; shopt -s extglob; rm -rf !(BDIR)
Command in Makefile
#cd ${ADIR}/exe; shopt -s extglob; rm -rf !\(BDIR\)
Make file message
rm: cannot remove `!(BDIR)': No such file or directory
The problem with your Makefile is that it escapes ( and ), which makes the shell interpret them literally.
The second issue,
/bin/sh: -c: line 0: syntax error near unexpected token `('
is caused by make using sh to execute commands, not bash.
The !(...) wildcard syntax (and extglob) are only supported by bash, not sh.
You could call bash explicitly:
#bash -c 'cd ${ADIR}/exe; shopt -s extglob; rm -rf !(BDIR)'
But that doesn't work either, because extglob doesn't take effect until the next line of input has been read, so !( ) still throws a syntax error.
We need a way to run a multi-line command using a single invocation of the shell. Unfortunately make makes this unnecessarily complicated.
One possible solution:
SHELL = /bin/bash
...
#bash -c $$'cd ${ADIR}/exe; shopt -s extglob\nrm -rf !(BDIR)'
This tells make to use bash to execute all recipes (not /bin/sh). We then run bash again manually, but using $'...' to quote the command string. This lets us write \n to embed a literal newline, which makes extglob / !( ... ) work.
We need double $$ to escape the $ for make, so $'...' becomes $$'...'.
I'm not very happy with this solution.
Unfortunately there's a weird behavior of bash in that the shopt setting won't take effect until the newline, so any globbing on the same line won't recognize it. Try this at your shell prompt:
$ shopt -s extglob; echo !(BDIR)
bash: !: event not found
Then try it in two lines:
$ shopt -s extglob
$ echo !(BDIR)
...works
Unfortunately this means it's almost impossible to use this with make.
You should use the POSIX-compatible version suggested in triplee's comment and avoid the need for special shells altogether.
Oh, it seems the answer was deleted. Anyway, do something like this instead:
foo:
cd ${ADIR}/exe && for f in *; do \
case $$f in (BDIR) : ok ;; (*) rm -rf "$$f" ;; esac; \
done

Shell script run by Jenkins creates non-terminating process

I'm trying to use Jenkins to create a special git repository. I created a free-style project that just executes a shell script. When I execute this script by hand, without Jenkins, it works just fine.
From Jenkins, however, it behaves quite differently.
# this will remove all subtrees
git log | grep git-subtree-dir | tr -d ' ' | cut -d ":" -f2 | sort | uniq | xargs -I {} bash -c 'if [ -d $(git rev-parse --show-toplevel)/{} ] ; then rm -rf {}; fi'
rm -rf .git
If this part is executed by Jenkins, in console output I see this kind of errors:
rm: cannot remove '.git/objects/pack/pack-022eb85d38a41e66ad3f43a5f28809a5a3ee4a0f.pack': Device or resource busy
rm: cannot remove '.git/objects/pack/pack-05630eb059838f149ad30483bd48d37f9a629c70.pack': Device or resource busy
rm: cannot remove '.git/objects/pack/pack-26f510b5a2d15ba9372cf0a89628d743811e3bb2.pack': Device or resource busy
rm: cannot remove '.git/objects/pack/pack-33d276d82226c201eedd419e5fd24b6b906d4c03.pack': Device or resource busy
I modified this part of the script like this:
while true
do
if rm -rf .git ; then
break
else
continue
fi
done
But this doesn't help. In the task manager I see a git process that just doesn't terminate.
I conjured said script by a lot of googling and I do not understand very good what's going on.
Jenkins runs on Windows Server 2012 behind IIS; shell scripts are executed by bash shipped with git for Windows.
1/ Ensure your path is correct, and no quote/double quote escaping occurs in the process of jenkins job starting.
2/ Your command line is a bit too handy to be correctly and safely evaluated.
Put your commands in a regular script, starting with #!/bin/bash instead of thru the command line.
xargs -I {} bash -c 'if [ -d $(git rev-parse --show-toplevel)/{} ] ; then rm -rf {}; fi'
becames
xargs -I {} /path/myscript.sh {}
with
#!/bin/bash
rev-parse="$(git rev-parse --show-toplevel)"
wait
if [ -d ${rev-parse}/${1} ] ; then
rm -rf ${1}
fi
Please note that your script is really unsafe, as you rm -rf a parameter without even evaluate it before… !
3/ You can add a wait between the git and the rm to wait for the end of the git process
4/ log your git command into a log file, with a redirection >> /tmp/git-jenkins-log
5/ put all of those commands in a script (see #2)
Following is an infinite loop in case rm -rf fail
while true
do
if rm -rf .git ; then
break
else
continue
fi
done
indeed continue can be used in for or while loop to get the next entry but in this while loop it will run the same rm command forever.
Well, aparrently I was able to fix my issue by running the script from different user.
By default on Windows Jenkins executes all jobs from the user SYSTEM. I have no idea why it affects the behaviour of my script but running it with psexec from specially created user account worked.
In case anyoune is interested, I did something like this:
psexec -accepteula -h -user Jenkins -p _password_ "full/path/to/bash.exe" full/path/to/script.sh

Shell script isn't working

I can't find out what's happening. Is this regular expression not working for shell scripts?
sudo rm -R -f '/web/!(release)'
Any ideas?
thanks
Perhaps you forgot to enable extglob. Also you shouldn't quote your extended glob pattern:
shopt -s extglob
sudo rm -R -f '/web/'!(release)
Also if the shell you're calling sudo with is not able to access /web, you can wrap up your command with bash:
sudo bash -c "shopt -s extglob"$'\n'"rm -R -f '/web/'!(release)"
See Pattern Matching and Filename Expansion.
Script file
shopt -s extglob
sudo rm -R -f 'web/'!(release);
Result
script.sh: Syntax error: "(" unexpected
edit :
I was running with "sh" instead of bash
now with bash script.sh it works
thanks

Difference between alias rm and /bin/rm

What is the difference between using /bin/rm abc.txt and the times when sometimes you have to alias rm which is then performed with rm abc.txt
/bin/rm will always refer to the binary rm command on your system. If you just write rm abc.txt one of these may happen:
Your shell implements rm directly as a builtin function or there is a shell function called rm (no external command is run).
rm has previously been aliased (with alias rm=<substituted-command>) to mean something different. Usually the aliased command is similar in function but it does not have to be.
If none of the above is applicable, the shell looks up the external command in /bin and runs it.
You can use alias to see all defined aliases. Also check out the command -V shell builtin which can tell you if a given command is an external command, shell function, builtin or special builtin.
A typical reason to create an alias for rm is to add the -i or -I option. In "interactive" mode rm will ask for confirmation before deleting anything.
$ alias rm="/bin/rm -i"
$ rm myfile
rm: remove regular file ‘myfile’? _

glob pattern in bash don't recognize '('

When running the following command:
rm -rf !(file1|file2)
all files except file1 and file2 are removed; as intended.
When either placing this command in a bash script:
#!/bin/bash
rm -rf !(file1|file2)
or running it using bash -c:
bash -c "rm -rf !(file1|file2)"
I receive the following error:
syntax error ner unexpected token '('
I have tried setting the shell options using
shopt -s extglob
yeilding in:
bash -c "shopt -s extglob; rm -rf !(file1|file2)"
to enable glob according to:
https://superuser.com/questions/231718/remove-all-files-except-for-a-few-from-a-folder-in-unix and some other questions as well.
Still it doesn't work, and I'm at loss.
First of all, for safety, let's do our testing with echo !(file1|file2) instead of rm -rf !(file1|file2).
Anyway, bash does some parsing of the entire command line before executing the shopt -s extglob command. When bash encounters the (, the extglob option isn't set yet. That's why you get the error.
Try this instead:
bash -O extglob -c 'echo !(file1|file2)'
In your script, you just need to turn on the option as a separate command line before relying on it:
#!/bin/bash
shopt -s extglob
echo !(file1|file2)
You can actually do this with the -c flag also:
bash -c 'shopt -s extglob
echo !(file1|file2)'
Or even like this:
bash -c $'shopt -s extglob\necho !(file1|file2)'

Resources