rm adding extra backslash to variable - linux

I'm writing a very simple script, that will interact with my git repository, but I've reached a point, where I can't figure out why the following is happening.
Having:
destPath='~/Dropbox/Shared/Alex\&Stuff'
destFile='file.zip'
#git archive --format zip --output $destFile master
echo $destPath/$destFile
rm $destPath/$destFile
The echo outputs the correct path:
~/Dropbox/Shared/Alex\&Stuff/file.zip
But the rm fails with the following:
rm: cannot remove ‘~/Dropbox/Shared/Alex\\&Stuff/file.zip’: No such file or directory
So, why the extra backslash is added when rm is executed? Alex\\$Stuff instead of Alex\$Stuff ?

Tilde character needs to be outside quote to be expnded:
destPath=~/Dropbox/Shared/Alex\&Stuff
destFile='file.zip'
#git archive --format zip --output $destFile master
echo "$destPath/$destFile"
rm "$destPath/$destFile"

Try
destPath="$HOME/Dropbox/Shared/Alex\&Stuff"
Tildas do not always expand to $HOME. And the ampersand doesn't need the backslash, unless you want an actual backslash there.
As for the double backslash, I would guess that that's how rm quotes its internal strings (i.e., backslashes have special meaning and a single backslash needs to be written as '\'--C does it this way, for example)

Related

Delete files in a variable - bash

i have a variable of filenames that end with a vowel. I need to delete all of these files at once. I have tried using
rm "$vowels"
but that only seems to return the files within the variable and state that there is "No such file or Directory"
Its your use of quotes: they tell rm that your variables contents are to be interpreted as a single argument (filename). Without quotes the contents will be broken into multiple arguments using the shell rules in effect.
Be aware that this can be risky if your filenames contain spaces - as theres no way to tell the difference between spaces between filenames, and spaces IN filenames.
You can get around this by using an array instead and using quoted array expansion (which I cant remember the syntax of, but might look something like rm "${array[#]}" - where each element in the array will be output as a quoted string).
SOLUTION
assigning the variable
vowel=$(find . -type f | grep "[aeiou]$")
removing all files within variable
echo $vowel | xargs rm -v

How to remove a file called * (asterisk) without using quotations?

I implemented the following command to create a file called * (asterisk):
echo > '*'
Now I'm supposed to remove this file without using any quotations.
I know how to remove this by using quotations, but not sure how without using quotations.
I tried the following commands which I was sure that they won't work because of command line expansion:
rm ./*
rm /*
If someone can help me with this, I would greatly appreciate it.
I think you're supposed to work this out yourself :-)
The simplest solution not involving quoting is to use the pattern [*]. Bracket expressions in a shell work much like character classes in regular expressions so that will match a file whose name is the single character *. Thus, you can delete your file with
rm [*]
Note that you cannot use that pattern to create a file named * because the shell substitutes words containing patterns with the name(s) of the files which match the pattern; if no such file exists, then the pattern is not matched and no substitution is performed. So if there is no file named *, then touch [*] will create a file named [*].
You could use history expansion. If the rm command directly follows the echo command, you can use !$:
echo > '*'
rm !$
!$ is shorthand for !!:$: repeat the last word ($) of the last command (!!).
If there are commands between the echo and the rm command, you can find the history number using fc -l:
$ echo > '*'
$ cmd1
$ cmd2
$ cmd3
$ fc -l
[...]
27628 echo > '*'
27629 cmd1
27630 cmd2
27631 cmd3
$ rm !27628:$
!27628 expands to the command with that number in the history, and $ is again the last word of that command.
If you have to run this in a script, you can't really look up the command number and insert it, but you can count the number of commands between the echo and the rm and use a relative event designator:
echo > '*'
cmd1
rm !-2:$
where !-2 refers to the command two lines back. Notice that history expansion is by default disabled in non-interactive shells; use
shopt -o history
to enable it.
You could use rm -i * if the number of files is not too big. This will ask for confirmation for every single file. Confirm deletion only for the file * and reject it for all others.

BASH find and replace in all files in directory using FIND and SED

I need to look for and replace certain strings for all files in a directory, including sub-directories. I think I'm nearly there using the following method which illustrates my general approach. I do much more inside the -exec than just this replace, but have removed this for clarity.
#!/bin/bash
#call with params: in_directory out_directory
in_directory=$1
out_directory=$2
export in_directory
export out_directory
#Duplicate the in_directory folder structure in out_directory
cd "$in_directory" &&
find . -type d -exec mkdir -p -- "$out_directory"/{} \;
find $in_directory -type f -name '*' -exec sh -c '
for file do
#Quite a lot of other stuff, including some fiddling with $file to
#get rel_file, the part of the path to a file from
#in_directory. E.g if in_directory is ./ then file ./ABC/123.txt
#will have rel_file ABC/123.txt
cat $file|tr -d '|' |sed -e 's/,/|/g' > $out_directory/$rel_file
done
' sh {} +
One issue is likely how I've tried to write the file to pipe the output to. However, this isn't the main/only issue as when I replace it with an explicit test path I still get the error
|sed -e 's/,/|/g' |No such file or directory
which makes me think the cat $file part is the problem?
Any help is massively appreciated as always - this is only the second BASH script I've ever had to write so I expect I've made a fairly basic mistake!
Your "inner" single quotes are being seen as "outer" single quotes and causing you problems. You think you are quoting the | in the tr command but what you are actually doing is ending the initial single-quoted string having an unquoted | and then starting a new single-quoted string. That second single-quoted string then ends at the single-quote that you believe is starting the sed script but is instead ending the previous single-quoted string, etc.
Use double quotes for those embedded single quotes if you can. Where you can't do that you have to use the '\'' sequence to get a literal single-quote in the single-quoted string.

Linux command to remove multiple files with two dot in the file name

Example file names is
abc.edf.xdc
pqe.ide.xdc
rm -rf "*.\*.xdc" is not working
Drop the quotes and it works:
/tmp/a$ touch abc.edf.xdc pqe.ide.xdc
/tmp/a$ ls
abc.edf.xdc pqe.ide.xdc
/tmp/a$ rm -f *.*.xdc
/tmp/a$ ls
/tmp/a$
rm -rf *.xdc should match all of those files. There's no need to put the extra "*.".
Source man bash
Enclosing characters in double quotes preserves the literal value of
all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !. The characters $ and `
retain their special meaning within double quotes.
So you don't need to use double quotes, just give rm -vf *.*.xdc

How to delete multiple files at once in Bash on Linux?

I have this list of files on a Linux server:
abc.log.2012-03-14
abc.log.2012-03-27
abc.log.2012-03-28
abc.log.2012-03-29
abc.log.2012-03-30
abc.log.2012-04-02
abc.log.2012-04-04
abc.log.2012-04-05
abc.log.2012-04-09
abc.log.2012-04-10
I've been deleting selected log files one by one, using the command rm -rf see below:
rm -rf abc.log.2012-03-14
rm -rf abc.log.2012-03-27
rm -rf abc.log.2012-03-28
Is there another way, so that I can delete the selected files at once?
Bash supports all sorts of wildcards and expansions.
Your exact case would be handled by brace expansion, like so:
$ rm -rf abc.log.2012-03-{14,27,28}
The above would expand to a single command with all three arguments, and be equivalent to typing:
$ rm -rf abc.log.2012-03-14 abc.log.2012-03-27 abc.log.2012-03-28
It's important to note that this expansion is done by the shell, before rm is even loaded.
Use a wildcard (*) to match multiple files.
For example, the command below will delete all files with names beginning with abc.log.2012-03-.
rm -f abc.log.2012-03-*
I'd recommend running ls abc.log.2012-03-* to list the files so that you can see what you are going to delete before running the rm command.
For more details see the Bash man page on filename expansion.
If you want to delete all files whose names match a particular form, a wildcard (glob pattern) is the most straightforward solution. Some examples:
$ rm -f abc.log.* # Remove them all
$ rm -f abc.log.2012* # Remove all logs from 2012
$ rm -f abc.log.2012-0[123]* # Remove all files from the first quarter of 2012
Regular expressions are more powerful than wildcards; you can feed the output of grep to rm -f. For example, if some of the file names start with "abc.log" and some with "ABC.log", grep lets you do a case-insensitive match:
$ rm -f $(ls | grep -i '^abc\.log\.')
This will cause problems if any of the file names contain funny characters, including spaces. Be careful.
When I do this, I run the ls | grep ... command first and check that it produces the output I want -- especially if I'm using rm -f:
$ ls | grep -i '^abc\.log\.'
(check that the list is correct)
$ rm -f $(!!)
where !! expands to the previous command. Or I can type up-arrow or Ctrl-P and edit the previous line to add the rm -f command.
This assumes you're using the bash shell. Some other shells, particularly csh and tcsh and some older sh-derived shells, may not support the $(...) syntax. You can use the equivalent backtick syntax:
$ rm -f `ls | grep -i '^abc\.log\.'`
The $(...) syntax is easier to read, and if you're really ambitious it can be nested.
Finally, if the subset of files you want to delete can't be easily expressed with a regular expression, a trick I often use is to list the files to a temporary text file, then edit it:
$ ls > list
$ vi list # Use your favorite text editor
I can then edit the list file manually, leaving only the files I want to remove, and then:
$ rm -f $(<list)
or
$ rm -f `cat list`
(Again, this assumes none of the file names contain funny characters, particularly spaces.)
Or, when editing the list file, I can add rm -f to the beginning of each line and then:
$ . ./list
or
$ source ./list
Editing the file is also an opportunity to add quotes where necessary, for example changing rm -f foo bar to rm -f 'foo bar' .
Just use multiline selection in sublime to combine all of the files into a single line and add a space between each file name and then add rm at the beginning of the list. This is mostly useful when there isn't a pattern in the filenames you want to delete.
[$]> rm abc.log.2012-03-14 abc.log.2012-03-27 abc.log.2012-03-28 abc.log.2012-03-29 abc.log.2012-03-30 abc.log.2012-04-02 abc.log.2012-04-04 abc.log.2012-04-05 abc.log.2012-04-09 abc.log.2012-04-10
A wild card would work nicely for this, although to be safe it would be best to make the use of the wild card as minimal as possible, so something along the lines of this:
rm -rf abc.log.2012-*
Although from the looks of it, are those just single files? The recursive option should not be necessary if none of those items are directories, so best to not use that, just for safety.
I am not a linux guru, but I believe you want to pipe your list of output files to xargs rm -rf. I have used something like this in the past with good results. Test on a sample directory first!
EDIT - I might have misunderstood, based on the other answers that are appearing. If you can use wildcards, great. I assumed that your original list that you displayed was generated by a program to give you your "selection", so I thought piping to xargs would be the way to go.
if you want to delete all files that belong to a directory at once.
For example:
your Directory name is "log" and "log" directory include abc.log.2012-03-14, abc.log.2012-03-15,... etc files. You have to be above the log directory and:
rm -rf /log/*

Resources