CMAKE suppress error output for custom command? - bash

I have a cmake command that looks like the following:
add_custom_command(
TARGET "uninstall"
POST_BUILD
COMMENT "Uninstall files within install_manifest.txt"
COMMAND sudo xargs rm -vf < install_manifest.txt || echo Nothing in install_manifest.txt to be uninstalled!
COMMAND cat install_manifest.txt | xargs -L1 dirname | sudo xargs rmdir --ignore-fail-on-non-empty -p
COMMAND echo
)
which has the goal of uninstalling files that were installed as part of the package.
The line
COMMAND sudo xargs rm -vf < install_manifest.txt || echo Nothing in install_manifest.txt to be uninstalled! is to remove the individual files that were installed, and COMMAND cat install_manifest.txt | xargs -L1 dirname | sudo xargs rmdir --ignore-fail-on-non-empty -p was to clear up the empty directories left behind.
Unfortunately, when I run the make uninstall I see the following near the end:
rmdir: failed to remove '/usr/local/include/palisade/pke': No such file or directory
rmdir: failed to remove '/usr/local/include/palisade/binfhe': No such file or directory
rmdir: failed to remove '/usr/local/include/palisade/binfhe': No such file or directory
rmdir: failed to remove '/usr/local/include/palisade/binfhe': No such file or directory
rmdir: failed to remove '/usr/local/include/palisade/binfhe': No such file or directory
rmdir: failed to remove '/usr/local/include/palisade/binfhe': No such file or directory
rmdir: failed to remove '/usr/local/include/palisade/binfhe': No such file or directory
make[3]: *** [CMakeFiles/uninstall.dir/build.make:72: uninstall] Error 123
make[2]: *** [CMakeFiles/Makefile2:334: CMakeFiles/uninstall.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:341: CMakeFiles/uninstall.dir/rule] Error 2
make: *** [Makefile:183: uninstall] Error 2
which I want to avoid. If I had to guess, the issue comes about because as rmdir is deleting folders, it is deleting their parents which means that for some other files which are along the same branch, it cannot access the empty folders there which is causing the errors. Does anyone know how I might address this? I don't want to propagate those errors to the user because for all intents and purposes it shouldn't matter to them

It will happen because the CMake command "add_custom_command" will halt execution if any of its COMMAND gives error (non-zero error code).
One workaround is to make sure that the command always succeeds by using "echo"
(some_shell_command || echo "")
For your example code :
add_custom_command(
TARGET "uninstall"
POST_BUILD
COMMENT "Uninstall files within install_manifest.txt"
COMMAND sudo xargs rm -vf < install_manifest.txt || echo Nothing in install_manifest.txt to be uninstalled!
COMMAND cat install_manifest.txt | xargs -L1 dirname | sudo xargs rmdir --ignore-fail-on-non-empty -p || echo ""
COMMAND echo
)
might work

Related

How to delete a file with a "Symbol for NULL" character (0x2400) in filename

ATTN: shell gods ;)
I can't seem to figure out how to delete a file with a unicode character 0x2400 in the filename on OSX (Example: ␀.test).
It's not a NULL character per-se, but a "symbol for null". (See: http://unicodelookup.com/#null/1)
Script - How to Reproduce
#!/usr/bin/env bash
dir="${HOME}/test_dir"
# Create Directory: ~/test_dir
if [ ! -d "${dir}" ]; then
printf "\nCreating Directory: ${dir}\n"
mkdir ${dir}
fi
# Create our character
char=$'\xE2\x90\x80'
# Create filename
file="${dir}/${char}.test"
# Create the File
printf "\nCreating File: ${file}\n"
touch ${file}
Delete the file... NOPE!
# Attempt 1 - Delete File
printf "\nDeleting File: ${file}\n"
rm -rf ${file}
Delete the whole directory... NOPE!
# Attempt 2 - Delete Directory
printf "\nDeleting Directory: ${file}\n"
rm -rf ${dir}
Delete the file via inode... NOPE!
# Attempt 3 - Delete File
inode=$(ls -i1 ${dir} | awk '{print $1}')
printf "\nDeleting via Inode: ${inode}\n"
find ${dir} -inum ${inode} -exec rm -i {} \;
The whole script should output something like this:
Creating File: /Users/bsmith/test_dir/␀.test
Deleting File: /Users/bsmith/test_dir/␀.test
rm: /Users/bsmith/test_dir/␀.test: Invalid argument
Deleting Directory: /Users/bsmith/test_dir/␀.test
rm: /Users/bsmith/test_dir/␀.test: Invalid argument
rm: /Users/bsmith/test_dir: Directory not empty
Deleting via Inode: 68592933
remove /Users/bsmith/test_dir/␀.test? y
rm: /Users/bsmith/test_dir/␀.test: Invalid argument
This command works for me:
rm ?.test
But sadly it is very probable that it will NOT work for you.
It is a known bug of osx:
Is it impossible to delete/move a file named “␀” on mac?
Rename folder with odd characters
The sure bet is to boot from a pen drive with some Linux OS, mount the file system in such Linux, and erase the file. It is sure that files with such names could be erased in Linux.

Truncate a directory in bash

Is there some elegant/simple way to delete the folder's contents in such a way there's no error output if it is empty?
The following command
$ rm -r $dir/*
doesn't work if the directory is empty, since in such a case, the wilcard * is not expanded and you get an error saying that rm cannot find file *.
Of course, the standard way is check if it is empty (with ls $dir | wc -w or find $dir -link 2 or any other related command), and deleting its contents otherwise.
Is there an alternative way not to check folder contents and only "truncate" the directory instead?
Bash
Simply,
$ rm -rf dir/*
(By default I believe) Bash doesn't complain about not finding anything with the glob. It just passes your literal glob through to your command:
$ echo dir/*
dir/*
When rm doesn't find a filename that has the literal glob character, it complains about not finding the file it's been asked to delete:
$ rm "dir/*"
rm: cannot remove ‘dir/*’: No such file or directory
$ echo $?
1
But if you force it, it won't complain:
$ rm -f "dir/*"
$ echo $?
0
I don't know if that refrain-from-complain is POSIX.
Do note, however, that if you don't have the shell option "dotglob" set, you'll miss files that start with a dot, AKA "hidden" files.
Generally
Zsh doesn't pass the literal glob through by default. You have to ask for it with "set -o nonomatch".
$ echo dir/*
zsh: no matches found: dir/*
$ echo $?
1
$ set -o nonomatch
$ echo dir/*
dir/*
For compatibility, I wouldn't use the above modern-Bash-specific "rm -rf dir/*", but would use the more general, widely-compatible solution:
$ find dir -mindepth 1 -delete
Find all files in "dir" at a minimum depth of 1 ("dir" itself is at depth 0), and delete them.
You can use rm -rf:
rm -rf "$dir"/*
As per man bash:
-f, --force
ignore nonexistent files and arguments, never prompt
rm -rf dir/*
does not delete hidden files which name starts with dot.
This is quite weird, when bash glob the *, it does not include .* files.
mkdir -p dir
touch dir/.a
rm -fr dir/*
ls dir/.a && echo I am not deleted
output is
dir/.a
I am not deleted
Besides, the rm -fr dir/* has another disadvantage: when there are too many files in the dir, the rm command will get too many arguments and results in error too many arguments. Also, it is very slow in that case.
Seems that the most reliable and fastest way is
find dir -mindepth 1 -delete

Makefile: removing files

On Windows I am trying to add a Makefile target to remove all files from a specific directory (NOT including subdirectories)...
clean_files:
rm -f Build/*.*
But I get the error: /bin/sh: rm: command not found
Running it from the command line works and running it without the *'s works.
clean_files:
- rm -f Build/*
putting a '-' before a make command will ignore any errors from that command, like
rm: cannot remove `Build/subdir': Is a directory
For removing all files from directory (NOT including sub-directories) consider:
clean_files:
find Build/ -type f -maxdepth 1 -delete

Parallel processing of untar/remove in unix shell script

Question:
I want to untar a tarfile which has many tar files within itself and remove the files in all the tar files and I want all of these processes to run in parallel in Unix bash scripting.
Conditions:
The script should return an error if any untar/remove process has any error.
It should only return success after all N (untar and remove) processes complete successfully.
Proposed solution:
mkdir a
tar -C a -xvf b.tar
cd a
for i in *
do
rm -r $i &
done
If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you can do this:
tar xvf foo.tgz | perl -ne 'print $l;$l=$_;END{print $l}' | parallel rm
It is useful if you do not have space to extract the full tar.gz file, but you need to process files as you unpack them:
tar xvf foo.tgz | perl -ne 'print $l;$l=$_;END{print $l}' | parallel do_stuff {}\; rm {}
You can install GNU Parallel simply by:
wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
Watch the intro videos for GNU Parallel to learn more:
https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
mkdir a
tar -C a -xvf b.tar
cd a
success=$(for i in *
do
rm -r $i || echo failed & # if a job fails false will be echoed
done
wait)
# if any of the jobs failed, success will be set to a value other than ""
[[ -z "$success" ]] && exit 0 || exit 1
The answer tar xvf a.tar | tac | xargs -P 4 rm -rv is inspired from Burton Samograd's comment about xargs -P
$ mkdir -p a/b/c/d
mkdir: created directory `a'
mkdir: created directory `a/b'
mkdir: created directory `a/b/c'
mkdir: created directory `a/b/c/d'
$ touch a/1 a/2 a/3 a/b/4 a/b/5
$ tar cf a.tar a
$ rm -rfv a
removed directory: `a/b/c/d'
removed directory: `a/b/c'
removed `a/b/4'
removed `a/b/5'
removed directory: `a/b'
removed `a/3'
removed `a/1'
removed `a/2'
removed directory: `a'
$ tar xvf a.tar | tac | xargs -P 4 rm -rv
removed `a/2'
removed `a/1'
removed `a/3'
removed `a/b/5'
removed `a/b/4'
removed directory: `a/b/c/d'
removed directory: `a/b/c'
removed directory: `a/b'
removed directory: `a'

How do I write a shell script to remove the unzipped files in a wrong directory?

I accidentally unzipped files into a wrong directory, actually there are hundreds of files... now the directory is messed up with the original files and the wrongly unzip files. I want to pick the unzipped files and remove them using shell script, e.g.
$unzip foo.zip -d test_dir
$cd target_dir
$ls test_dir | rm -rf
nothing happened, no files were deleted, what's wrong with my command ? Thanks !
The following script has two main benefits over the other answers thus far:
It does not require you to unzip a whole 2nd copy to a temp dir (I just list the file names)
It works on files that may contain spaces (parsing ls will break on spaces)
while read -r _ _ _ file; do
arr+=("$file")
done < <(unzip -qql foo.zip)
rm -f "${arr[#]}"
Right way to do this is with xargs:
$find ./test_dir -print | xargs rm -rf
Edited Thanks SiegeX to explain to me OP question.
This 'read' wrong files from test dir and remove its from target dir.
$unzip foo.zip -d /path_to/test_dir
$cd target_dir
(cd /path_to/test_dir ; find ./ -type f -print0 ) | xargs -0 rm
I use find -0 because filenames can contain blanks and newlines. But if not is your case, you can run with ls:
$unzip foo.zip -d /path_to/test_dir
$cd target_dir
(cd /path_to/test_dir ; ls ) | xargs rm -rf
before to execute you should test script changing rm by echo
Try
for file in $( unzip -qql FILE.zip | awk '{ print $4 }'); do
rm -rf DIR/YOU/MESSED/UP/$file
done
unzip -l list the content with a bunch of information about the zipped files. You just have to grep the file name out of it.
EDIT: using -qql as suggested by SiegeX
The following worked for me (bash)
unzip -l filename.zip | awk '{print $NF}' | xargs rm -Rf
Do this:
$ ls test_dir | xargs rm -rf
You need ls test_dir | xargs rm -rf as your last command
Why:
rm doesn't take input from stdin so you can't pipe the list of files to it. xargs takes the output of ls command and presents it to rm as input so that it can delete it.
Compacting the previous one. Run this command in the /DIR/YOU/MESSED/UP
unzip -qql FILE.zip | awk '{print "rm -rf " $4 }' | sh
enjoy

Resources