Can I do a negated wildcard on the command-line? [duplicate] - shell

This question already has answers here:
How can I use inverse or negative wildcards when pattern matching in a unix/linux shell?
(11 answers)
Closed 2 years ago.
On the command-line *tmp* will match all files with names containing "tmp". Is there a quick way to do the reverse, i.e. match all files with names that don't contain "tmp"?
I figured out how to get ls to do it (ls -I "*tmp*"), but that doesn't help if I want to use some other command rather than ls. Is there a general method?
I forgot to note: I'm using zsh.

It depends on your shell.
In ksh, you can use this:
!(*tmp*)
In bash, the same thing works if you first enable the feature with shopt -s extglob.
In zsh, you can enable the same syntax with setopt ksh_glob, but there's a conflict with another zsh feature that you have to disable with setopt no_bare_glob_qual before the above will actually work. Alternatively, you can just use zsh's native version via setopt extended_glob; the equivalent of the above expression then looks like this:
^*tmp*

When using bash, you can enable extglob with:
shopt -s extglob
then you can use:
!(*tmp*)
that will negate your wildcard condition.
you can finally disable extglob with shopt -u extglob.

Related

Remove file name extension in shell based on pattern [duplicate]

This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Closed 3 years ago.
I want to remove the file name extension from a variable based on the extension pattern. Currently, I am interested only to remove two extensions - exe and .cat. I can achieve this using multiple commands. But, wondering if there is a short cut available with a single command.
filename=testfile.exe
file=${testfile%.exe}
This will give me the output as "testfile" but if my filename now is:
filename=testfile.cat
I will have to again run:
file=${testfile%.cat}
I want a single command which will give me "testfile" as output.
I don't want to use file=${testfile%.*} as this will remove all the extensions of all other undesired types.
Thanks in Advance!
Updated to actually answer the question.
You will need to enable the extglob shell option which enables the #(pattern-list). This matches one of the given patterns. In the pattern below the '|' is an or operator and the '$' is a end of string match to avoid
Example Code:
$ shopt -s extglob # enables extglob option
$ shopt extglob # display current setting
extglob on
$ filename=testfile.cat.exe
$ file=${filename/#(.cat$|.exe$)/}
$ echo $file
testfile.cat
$ filename=testfile.cat.exe # What happens when you don't use $
$ echo ${filename/#(.cat|.exe)/}
testfile.exe
$

"${foo//#(...)/}" works only in command line but not in a script [duplicate]

This question already has an answer here:
Bash command runs in terminal but not in script file
(1 answer)
Closed 3 years ago.
I have the below command "${CHROMEDRIVER//#([\/])/}" is shell file and on running bash file.sh, it doesn't work:
#!/bin/bash
CHROMEDRIVER="75.0.3770.8/"
echo $CHROMEDRIVER
echo "${CHROMEDRIVER//#([\/])/}"
But, when I run the same cmd "${CHROMEDRIVER//#([\/])/}" in terminal, it works great
This command requires extglob support to be enabled. Presumably you're turning it on (with shopt -s extglob) in your dotfiles, but it's off-by-default in scripts.
See the bash-hackers' wiki on extended pattern language, describing the specific items that require the extglob flag to be enabled; #(...) is among them.
Thus, to fix your script without changing the pattern you're replacing:
#!/usr/bin/env bash
shopt -s extglob
CHROMEDRIVER="75.0.3770.8/"
echo "$CHROMEDRIVER"
echo "${CHROMEDRIVER//#([\/])/}"
...though insofar as your goal is just to strip a trailing slash, use "${CHROMEDRIVER%/}" instead, as described in the bash-hackers' wiki page on parameter expansion.

how to select all items in zsh?

in bash, we can say
dnf install <some-pkg>-*
or
rm -rf *.jpg
but when I'm using zsh, it doesn't do anything with the star (*) character!
how can I do those commands on zsh?!
These are two different uses of globs. With dnf, you expect dnf itself to expand the pattern against the available packages. With rm, you are expecting the shell to expand the pattern against the files in the current directory and passing the resulting names to rm.
In bash, the default behavior is for a pattern that doesn't match anything to be treated as a literal string. That's why the dnf example works in bash: when there are no local files matching <some-pkg>-*, the literal string is passed to zsh.
The solution is to quote strings that you intended to be treated literally, instead of relying on your shell's treatment of unmatched patterns. The following will work as intended in both shells:
dnf install "<some-pkg>.*"
rm -rf *.jpg
In both shells, you can change how unmatched patterns are treated. To make zsh act like the bash default, use
setopt NO_NOMATCH
To make bash behave like the zsh default, you could use
shopt -s failglob
Probably, the globbing is disabled on your zsh. Enable it using the below command in zsh:
setopt GLOB

How to match nothing if a file name glob has no matches [duplicate]

This question already has answers here:
How to skip the for loop when there are no matching files?
(2 answers)
Closed 3 years ago.
I want to loop over all files matching extension jpg or txt. I use:
for file in myDir/*.{jpg,txt}
do
echo "$file"
done
Problem: If the directory contains no jpg file at all, the loop will have one iteration with output myDir/*.jpg. I thought * will be replaced by an arbitrary file (and if no file exists it cannot be expanded). How can I avoid the unwanted iteration?
Use this to avoid the unwanted iteration:
shopt -s nullglob
From man bash:
nullglob: If set, bash allows patterns which match no files (see Pathname Expansion above) to expand to a null string, rather than themselves.
See: help shopt and shopt
This and a duplicate question both were in context of not just pathname-expansion, but also brace-expansion, and a duplicate asked for POSIX.
The compgen -G does bash --posix compatible pathname-expansion (no brace-expansion) and... you guessed it: yields nothing if there are no matches.
Therefore write a bash --posix function to do it. Brief outline: temporarily use set -f to first do brace-expansion (without pathname-expansion) via an echo, then apply compgen -G to each result for pathname-expansion. Full function left as an exercise.

for loop includes wild characters (*.c) if no files with extension .c. How do I get around? [duplicate]

This question already has answers here:
How to skip the for loop when there are no matching files?
(2 answers)
Closed 3 years ago.
I am using bash on UNIX (sparc 10)
for file in $SCPATH/$LIBNAME/*.{gob,c,cpp,h};
do
ln -s $file;
done;
The problem is: if there are no files with extension 'c', it will put ".c" in $file and ln -s will create a link to '.c'.
Is this a know issue? How can I get around it (besides the obvious 'if not *.c' hack).
you need to set nullglob before your loop
shopt -s nullglob
nullglob:
If set, bash allows patterns which match no files
(see Pathname Expansion above) to
expand to a null string, rather than
themselves.
When you're done and want to reset it to the original behavior, use:
shopt -u nullglob
As pointed out by Dennis Williamson in one of the comments.

Resources