Using wildcards to exclude files with a certain suffix - linux

I am experimenting with wildcards in bash and tried to list all the files that start with "xyz" but does not end with ".TXT" but getting incorrect results.
Here is the command that I tried:
$ ls -l xyz*[!\.TXT]
It is not listing the files with names "xyz" and "xyzTXT" that I have in my directory. However, it lists "xyz1", "xyz123".
It seems like adding [!\.TXT] after "xyz*" made the shell look for something that start with "xyz" and has at least one character after it.
Any ideas why it is happening and how to correct this command? I know it can be achieved using other commands but I am especially interested in knowing why it is failing and if it can done just using wildcards.

These commands will do what you want
shopt -s extglob
ls -l xyz!(*.TXT)
shopt -u extglob
The reason why your command doesn't work is beacause xyz*[!\.TXT] which is equivalent to xyz*[!\.TX] means xyz followed by any sequence of character (*) and finally a character in set {!,\,.,T,X} so matches 'xyzwhateveryouwant!' 'xyzwhateveryouwant\' 'xyzwhateveryouwant.' 'xyzwhateveryouwantT' 'xyzwhateveryouwantX'
EDIT: where whateveryouwant does not contain any of !\.TX

I don't think this is doable with only wildcards.
Your command isn't working because it means:
Match everything that has xyz followed by whatever you want and it must not end with sequent character: \, .,T and X. The second T doesn't count as far as what you have inside [] is read as a family of character and not as a string as you thought.
You don't either need to 'escape' . as long as it has no special meaning inside a wildcard.
At least, this is my knowledge of wildcards.

Related

How do I exclude a character in Linux

Write a wildcard to match all files (does not matter the files are in which directory, just ask for the wildcard) named in the following rule: starts with a string “image”, immediately followed by a one-digit number (in the range of 0-9), then a non-digit char plus anything else, and ends with either “.jpg” or “.png”. For example, image7.jpg and image0abc.png should be matched by your wildcard while image2.txt or image11.png should not.
My folder contained these files imag2gh.jpeg image11.png image1agb.jpg image1.png image2gh.jpg image2.txt image5.png image70.jpg image7bn.jpg Screenshot .png
If my command work it should only display image1agb.jpg image1.png image2gh.jpg image5.png image70.jpg image7bn.jpg
This is the command I used (ls -ad image[0-9][^0-9]*{.jpg,.png}) but I'm only getting this image1agb.jpg image2gh.jpg image7bn.jpg so I'm missing (image1.png image5.png)Kali Terminal and what I did
ls -ad image[0-9][!0-9]*{.jpg,.png}
Info
Character ranges like [0-9] are usually seen in RegEx statements and such. They won't work as shell globs (wildcards) like that.
Possible solution
Pipe output of command ls -a1
to standard input of the grep command (which does support RegEx).
Use a RegEx statement to make grep filter filenames.
ls -a1|grep "image"'[[:digit:]]\+[[:alpha:]]*\.\(png\|jpg\)'

Removing changing pattern from filenames in directory in Linux

I have a directory containing files following the following naming convention:
Label_0000_AA.gz
Label_0001_BB.gz
Label_0002_CC.gz
...
All I want to do is to rename these files so that the _#### number pattern is removed, resulting in:
Label_AA.gz
Label_BB.gz
Label_CC.gz
...
but only up to a certain number. E.g.: I may have 10000 files but might only want to remove the pattern in the first 3000. Would this be possible using something like bash?
If you don't have prename or rename -
(assuming the names are consistent)
for f in Label_[0-9][0-9][0-9][0-9]_[A-Z][A-Z].gz
do mv "$f" "${f//_[0-9][0-9][0-9][0-9]/}"
done
To just do a certain range -
for n in {0000..2999}
do for f in Label_${n}_??.gz
do mv $f ${f//_$n/}
done
done
You're sure there are not collisions?
If you can name the pattern you want to change/remove in a regex you can use the command prename:
prename 's/_[0-3][[:digit:]]{3}_/_/g' Label_*.gz
This regex would only remove numbers 0000-3999.
Using the flag -n does a "dry-run" and shows what it would do.
Edit: Thanks #KamilCuk to remind me about two renames. I made it clear and changed the name to prename.

bash handling of quotation marks in filename

I am trying to remove and replace quotation marks that are present in a file name. For example, I would like to change:
$ ls
abc"def"ghi"jkl"mno
to this
$ ls
abc:def:ghi:jkl:mno
In trying to solve this, I came across How to rename a bunch of files to eliminate quote marks, which is exactly what I want to do. However, it didn't work for my case. To figure out why, I tried creating a test file like this:
$ touch abba\"abba\"cde\"cde\"efef
With this file, the solutions I came across (such as mentioned above) worked. But why didn't it work for the first file?
One thing I discovered was that bash command completion sees them differently. If I type in
$ ls abb<tab>
bash will complete the filename like so:
$ abba\"abba\"cde\"cde\"efef
just as I created it. But for the original file, bash completion went like this:
$ ls abc<tab>
results in
$ abc"def"ghi"jkl"mno
So in the test case file, there is an escape of the quotation marks, and in the other case (the file I really want to rename), there is no escaping of the the quotation marks. I don't know how the original files were named.
Can anyone explain why bash sees these names differently, and how I would go about renaming my file?
Here is two ways to rename a file with "(quotation) mark,
option 1: With escape character \
mv abc\"cdf\"efg\"hij newFileName
option 2: By using '(single quote)
mv 'abc"cdf"efg"hij' newFileName
Note: using special charaters like :(colon) in file name might not be a good idea,
and regarding the auto completion, it usually fill the name with escape character, example
ls abc<tab> will complete the name to ls abc\"cdf\"efg\"hij
unless you start the name with a quote, example
ls 'abc<tab> will complete the name to ls 'abc"cdf"efg"hij'

change multiple files commandline

I have separated some tracks from mp3 mixes using mp3splt.
BASH: (mp3splt -c('**!!***use .cue file***!!**') [cuefile.cue] [nonstopmix.mp3] ~for anyone interested, is in the Ubu repos~)
And I ended up with these filenames: "Antares" - 01 - "Xibalba".mp3 which is not a format I prefer, now I've made it a little project to change them with a shell script but its more difficult than I anticipated.
I want to change the filename from:
"Antares" - 01 - "Xibalba".mp
to:
01-Antares_-_Xibalba.mp3
so far I've used :
for var in *.mp3; do mv $var {var/"/}; done
and I could repeat that until I'm through, delete the 0x number and add one but I'd like to do it more efficient.
Could anyone give me a pointer (!not a script!) ?
I'd still like to write it myself but there's so much options that I'm a bit lost.
so far I thought to use this program flow:
read all the filenames containing .mp3 and declare as variable $var
strip $var from quotes
select 0x number, append delimiter _ (0x_)
move 0x_ to the beginning of the string
select remaining ' - - ' and change to '-'
done
which bash programs to use? especially changing the 0x puzzles me cuz I need a loop which increments this number and test if it is present in the filename variable and then it has to be changed.
It is easy to do in python 2.x. You can use this logic in any language you want.
import string
a=raw_input('Enter the name of song')
a=a.replace('"', "")
a=a.replace('.mp', ' .mp3')
words = a.split()
print words[2]+'-'+words[0]+'_-_'+words[4]+words[5]
Logic:
I removed ", then make .mp to .mp3, then splitted the string, which created a list ( array ) and then printed the elements according to need.
Try doing this :
rename -n 's/"(\w+)"\s+-\s*(\d+)\s*-\s*"(\w+)"\.mp/$2-$1_-_$3.mp3/' *mp
from the shell prompt. It's very useful, you can put some perl tricks like I does in a substitution.
You can remove the -n (dry-run mode switch) when your tests become valids.
There are other tools with the same name which may or may not be able to do this, so be careful.
If you run the following command (linux)
$ file $(readlink -f $(type -p rename))
and you have a result like
.../rename: Perl script, ASCII text executable
then this seems to be the right tool =)
If not, to make it the default (usually already the case) on Debian and derivative like Ubuntu :
$ sudo update-alternatives --set rename /path/to/rename
Last but not least, this tool was originally written by Larry Wall, the Perl's dad.

linux batch rename directories and strip # character from name

i have a directory with a lot of subdirectories with a # infront of them:
#adhasdk
#ad18237
I want to rename them all and remove the # caracter
I tried to do:
rename -n `s/#//g` *
but didn't seem to work.
-bash: s/#//g: No such file or directory
Any ideas on this.
Thanks
Just use
$ rename 's/^#//' *
use -n just to check that what you think it would happen really happens.
In you example you have the clue about the wrong quotes used (backticks) in the error message
-bash: s/#//g: No such file or directory
bash is trying to execute a command named s/#//g.
No that using g (global) and not anchoring the regular expression you will replace any #, not just the one in the first position.
I don't know whether it's just a typo when you typed it here, but that "rename" command should work if:
you leave off the "-n" and
you quote the substitution with regular single-quotes and not back-quotes
The "-n" tells it to not really do anything. The back-quotes are just wrong (they mean something but not what you want here).
The problem is that you use backticks (`). You should use normal quotes:
rename -n 's/#//g' *
for DIR in \#*/
do
echo mv "$DIR" "${DIR/#\#/}"
done
I had to rename all folders inside a given folder. Each folder name had some text inside round braces. The following command removed the round braces from all folder names:
rename 's/(.+)//' *
Some distros doesn't support regexp in rename. You have to install prename. Even more, sometimes you can't install prename and you have to install gprename to have binary prename.
If you have 'prename' then just change backtick character " ` " to single quote and everything should work.
So the solution should be:
prename -n 's/#//g' *
or
prename -n 'y/#//' *

Resources