rm -f won't handle all arguments in bash script - bash

Got a bit of a peculiar issue in bash. I want to author a script that takes a prefix of a file name and deletes all files that begin with that prefix and end in some given suffixes. To do this, I have the following line of code:
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Please provide an argument";
exit 1
fi
# Ending dot already included in $1
rm -f $1"aux" $1"bbl" $1"blg" $1"synctex.gz" $1"log"
# But even if you call it wrong...
rm -f $1".aux" $1".bbl" $1".blg" $1".synctex.gz" $1 ".log"
exit 0
Unfortunately, when I call my script (which is called cleanlatex), as intended:
cmd$ cleanlatex lpa_aaai.
only the .aux file is deleted. Clearly rm -f doesn't expand its application to all arguments, which is what would've been done if I explicitly ran rm -f lpa_aaai.aux lpa_aaai.bbl .... What am I doing wrong here?
Edit: To answer #Etan's question, this is what I see when I add those commands:
+ '[' 1 -lt 1 ']'
+ rm -v lpa_aaai.aux lpa_aaai.bbl lpa_aaai.blg lpa_aaai.synctex.gz lpa_aaai.log
removed ‘lpa_aaai.aux’
removed ‘lpa_aaai.bbl’
removed ‘lpa_aaai.blg’
removed ‘lpa_aaai.synctex.gz’
removed ‘lpa_aaai.log’
+ rm -v lpa_aaai..aux lpa_aaai..bbl lpa_aaai..blg lpa_aaai..synctex.gz lpa_aaai. .log
rm: cannot remove ‘lpa_aaai..aux’: No such file or directory
rm: cannot remove ‘lpa_aaai..bbl’: No such file or directory
rm: cannot remove ‘lpa_aaai..blg’: No such file or directory
rm: cannot remove ‘lpa_aaai..synctex.gz’: No such file or directory
rm: cannot remove ‘lpa_aaai.’: No such file or directory
rm: cannot remove ‘.log’: No such file or directory
The second set, consisting of the cannot remove messages, does not concern me: I only have those removals as a fail-safe anyway. This did exactly what I needed. Thank you.

It's not clear what your problem actually is; however, you can reduce your script to two lines of bash:
: ${1?Please provide an argument}
rm -f "$1".{aux,bbl,log,synctex.gz,log}
The first line has the same effect as your if statement, using standard POSIX parameter expansion.
The second line properly quotes $1 and uses brace expansion to produce the sequence of file names with the desired list of suffixes. It makes the simplifying assumption that the user typed the basename of the files without the trailing period: who would type foo. when foo would suffice? You're already making the assumption that there are not files named (for instance) foo.aux and foo..aux, so you might as well make an assumption that makes for less work.
I removed exit 0 because either rm succeeds and your script will exit with status 0 anyway, or rm fails, in which case you shouldn't be exiting with 0 status, but whatever status rm exits with.

Related

mv: cannot stat [DIRECTORY/FILE]: no such file or directory

EDIT: DIR_trash="trash"
I wrote a function to move a file to current directory.
if [ "$1" == "-u" ]
then
if [ $# == 1 ]
then
echo "Something went wrong. Please make sure you're passing the name of the file/directory after '-u'."
else
if [ -f $DIR_trash/$2.zip ]
then
echo "$2.zip has been found in the trash."
cd
cd $DIR_trash
sed -i "/$2/d" $file7
mv -i /$DIR_trash/$2.zip .
unzip $2.zip
\rm $2.zip
cd
else
echo "$2.zip has not been found in the trash."
fi
fi
fi
As you can see, there is a line of code which says:
mv -i /DIR_trash/$2.zip .
So basically I'm trying to move a file that I passed in argument 2 to current directory, from trash. I always run this script from home directory, which does have trash directory. This is what I get when I run this:
Whenever I manually write this is in the Konsole (from home direcotry) it does work:
rm -u trash/d1 .
I'm out of ideas. Could anyone please help?
Let's say you run the script with the current directory being /some/where, and with the arguments -u and d1. I'll also assume that your home directory is /home/ninini. Let's look at where your script looks for files.
DIR_trash="trash"
if [ -f $DIR_trash/$2.zip ]
You check if /some/where/trash/d1.zip exists.
cd
cd $DIR_trash
Assuming both cd commands succeed, the current directory is now /home/ninini/trash.
mv -i /$DIR_trash/$2.zip .
You're saying to move /trash/d1.zip to the current directory, which is /home/ninini/trash.
Neither the source nor the destination make sense. The source /$DIR_trash doesn't make sense: why would you be looking for a directory called trash under the root directory? And the destination doesn't make sense since you just attempted to change to the trash directory, and now you're attempting to move a file out of the trash directory… into the trash directory.
I can't tell what the correct code is because you didn't say what the script is meant to do. You do say that you want to “to move a file to current directory”; then you must not change the current directory midway through the script! Assuming that the path $DIR_trash/$2.zip from the test command is the correct one, remove the cd commands and write
mv -i -- "$DIR_trash/$2.zip" .
Note that this moves the file from a directory called trash under the current directory. If this isn't what you wanted, you need to change the definition of DIR_trash. It should probably be an absolute path, perhaps
DIR_trash=~/trash
Note also that your script breaks on files containing whitespace and other special characters. Always put double quotes around variable substitutions: "$VAR", not $VAR. (Exception: when you know you need some effect that the double quotes prevent, and you understand why it's safe to leave them out.)

How to check if a file exists or not and create/delete if does/does not exist in shell

In shell, I want to check if a file exists or not then create if it doesn't exist or delete if it exists. For this I need a one liner and am trying to do something like:
ls | awk '\filename\' <if exist delete else create>
I need the ls as my problem has some command that outputs a list of strings that need to be pipelined to awk then possibly touch/mkdir.
#!/bin/bash
if [ -z "$1" ] || [ ! -f "$1" ] # $1 is input filename and -f check if $1 is a regular file
then
rm "$1" #delete the file
else
touch "$1" #create the file
fi
save the file as filecreator.sh
change the permission to allow execution with sudo chmod a+rx
while running the script use ./filecreator.sh yourfile.extension
You can see the file in your directory.
Using oc projects and oc new-project instad of ls and touch as indicated in a comment.
oc projects |
while read -r proj; do
if [ -d "$proj" ]; then
rm -rf "$proj"
else
oc new-project "$proj"
fi
done
I don't think there is a useful way to write this as a one-liner. If you like, you can replace the newlines with semicolons, except after then and else.
You really should put your actual requirements in the question itself. ls is a superbly useless example because it cannot list a file which doesn't already exist, and you should not use ls in scripts at all.
rm yourfile 2>/dev/null || touch yourfile
If the file existed before, rm will succeed and erase the file, and the touch won't be executed. You end up with no file afterwards.
If the file did not exist before, rm will fail (but the error message is not visible, since it is directed to the bitbucket), and due to the non-zero exit code of rm, the touch will be executed. You end up with an empty file afterwards.
Caveat: If the file exists, but you don't have permissions to remove it, you won't notice this error, due to the redirection of stderr. Hence, for debugging and later diagnosis, it might be better to redirect stderr to some file instead.

How can I delete a file only if it exists?

I have a shell script and I want to add a line or two where it would remove a log file only if it exists. Currently my script simply does:
rm filename.log
However if the filename doesn't exist I get a message saying filename.log does not exist, cannot remove. This makes sense but I don't want to keep seeing that every time I run the script. Is there a smarter way with an IF statement I can get this done?
Pass the -f argument to rm, which will cause it to treat the situation where the named file does not exist as success, and will suppress any error message in that case:
rm -f -- filename.log
What you literally asked for would be more like:
[ -e filename.log ] && rm -- filename.log
but it's more to type and adds extra failure modes. (If something else deleted the file after [ tests for it but before rm deletes it, then you're back at having a failure again).
As an aside, the --s cause the filename to be treated as literal even if it starts with a leading dash; you should use these habitually if your names are coming from variables or otherwise not strictly controlled.
Touch the file first, which will create it if it's not present, but only change the timestamp if it is present.
touch filename && rm filename
Less efficient, but easy to remember.
if [ ! -f 'absolute path of file' ]
then
echo "File does not exist. Skipping..."
else
rm 'absolute path of file'
fi
If you use the following then it should work.
You can use
rm -rf my/dir || true

Error handling in shell script

I have the following the shell script. Which throws the following error if there is no file exist in the folder. So, how we do handle this so that script doesn't stop executing?
Error:
mv: cannot stat `*': No such file or directory
Script:
for file in *
do
fl=$(basename "$file")
flname="${fl%%.*}"
gunzip "$file"
mv "$flname" "$flname-NxDWeb2"
tar -zcf "$flname-NxDWeb2".tar.gz "$flname-NxDWeb2"
rm "$flname-NxDWeb2"
done;
If the shell is bash, you can allow * to expand to the null string: shopt -s nullglob before your loop.
BTW, you might want to explicitly specify the uncompressed filename to produce, in case your logic doesn't completely agree with gunzip's (which it probably won't, if there's more than one dot in the name, or the file ends with .tgz or .taz):
gunzip -c "$file" >"$flname"
(you will need to remove the original yourself in this case, though)
You can avoid the need to move, too:
flname="${fl%%.*}-NxDWeb2"
And you might want to use trap to ensure your temporaries are cleaned up in the failure case (possible make your temporaries in $TMPDIR, too).

bash: passing entire command (with arguments) to a function

I am essentially trying to implement a function which asserts the failure (non-zero exit code) of another command, and prints a message when it fails.
Here is my function:
function assert_fail () {
COMMAND=$#
if [ `$COMMAND; echo $?` -ne 0 ]; then
echo "$COMMAND failed as expected."
else
echo "$COMMAND didn't fail"
fi
}
# This works as expected
assert_fail rm nonexistent
# This works too
assert_fail rm nonexistent nonexistent2
# This one doesn't work
assert_fail rm -f nonexixtent
As soon as I add options to the command, it doesn't work. Here is the output of the above:
rm: cannot remove `nonexistent': No such file or directory
rm nonexistent failed as expected.
rm: cannot remove `nonexistent': No such file or directory
rm: cannot remove `nonexistent2': No such file or directory
rm nonexistent nonexistent2 failed as expected.
rm -f nonexistent didn't fail
I have tried putting double quotes around the commands, to no avail. I would expect the third invocation in the above to produce similar output to the other two.
I appreciate any/all help!
#rici correctly pointed out the issue you're seeing, but there are a couple of real problems with your wrapper function. First, it doesn't correctly preserve spaces (and some other funny characters) in arguments. COMMAND=$# (or COMMAND="$#") merges all of the arguments into a single string, losing the distinction between spaces between arguments and spaces within arguments. To keep them straight, either use "$#" directly without storing it in a variable, or store it as an array (COMMAND=("$#"), then execute it as "${COMMAND[#]}"). Second, if the command prints anything to stdout, it'll wreak havoc with your exit status check; just test it directly, as #chepner said. Here's my suggested rewrite:
function assert_fail () {
if "$#"; then
echo "$* didn't fail"
else
echo "$* failed as expected."
fi
}
Note that the way I did the echo commands does lose the distinction of spaces within arguments. If that's a problem, replace the echo commands with this:
printf "%q " "$#"
echo "didn't fail"
and
printf "%q " "$#"
echo "failed as expected."
rm -f never fails on non-existent files. It has nothing to do with your wrapper. See man rm:
OPTIONS
-f, --force
ignore nonexistent files, never prompt

Resources